
com.tencent.mm.arscutil.io.ArscReader Maven / Gradle / Ivy
/*
* Tencent is pleased to support the open source community by making wechat-matrix available.
* Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved.
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.tencent.mm.arscutil.io;
import com.tencent.matrix.javalib.util.Log;
import com.tencent.mm.arscutil.ArscUtil;
import com.tencent.mm.arscutil.data.ArscConstants;
import com.tencent.mm.arscutil.data.ResChunk;
import com.tencent.mm.arscutil.data.ResConfig;
import com.tencent.mm.arscutil.data.ResEntry;
import com.tencent.mm.arscutil.data.ResMapValue;
import com.tencent.mm.arscutil.data.ResPackage;
import com.tencent.mm.arscutil.data.ResStringBlock;
import com.tencent.mm.arscutil.data.ResTable;
import com.tencent.mm.arscutil.data.ResType;
import com.tencent.mm.arscutil.data.ResTypeSpec;
import com.tencent.mm.arscutil.data.ResValue;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
/**
* Created by jinqiuchen on 18/7/29.
*/
public class ArscReader {
private static final String TAG = "ArscUtil.ArscReader";
LittleEndianInputStream dataInput;
public ArscReader(String arscFile) throws FileNotFoundException {
dataInput = new LittleEndianInputStream(arscFile);
Log.i(TAG, "read From %s", arscFile);
}
public ResTable readResourceTable() throws IOException {
Log.d(TAG, "===========================");
long headStart = 0;
ResTable resTable = new ResTable();
resTable.setStart(headStart);
resTable.setType(dataInput.readShort());
Log.d(TAG, "table type %d", resTable.getType());
resTable.setHeadSize(dataInput.readShort());
Log.d(TAG, "head size %d", resTable.getHeadSize());
resTable.setChunkSize(dataInput.readInt());
Log.d(TAG, "chunk size %f KB", resTable.getChunkSize() / 1024.0f);
resTable.setPackageCount(dataInput.readInt());
Log.d(TAG, "package count %d", resTable.getPackageCount());
resTable.setHeadPaddingSize((int) (resTable.getHeadSize() + headStart - dataInput.getFilePointer()));
dataInput.seek(headStart + resTable.getHeadSize());
resTable.setGlobalStringPool(readStringBlock());
Log.d(TAG, "global string pool pos %d", dataInput.getFilePointer());
if (resTable.getPackageCount() > 0) {
ResPackage[] packages = new ResPackage[resTable.getPackageCount()];
for (int i = 0; i < resTable.getPackageCount(); i++) {
packages[i] = readPackage();
}
resTable.setPackages(packages);
}
resTable.setChunkPaddingSize((int) (resTable.getChunkSize() + headStart - dataInput.getFilePointer()));
dataInput.close();
return resTable;
}
private ResPackage readPackage() throws IOException {
Log.d(TAG, "===========================");
long headStart = dataInput.getFilePointer();
Log.d(TAG, "package start %d", headStart);
ResPackage resPackage = new ResPackage();
resPackage.setStart(headStart);
resPackage.setType(dataInput.readShort());
Log.d(TAG, "package type %d", resPackage.getType());
resPackage.setHeadSize(dataInput.readShort());
Log.d(TAG, "head size %d", resPackage.getHeadSize());
resPackage.setChunkSize(dataInput.readInt());
Log.d(TAG, "chunk size %d", resPackage.getChunkSize());
resPackage.setId(dataInput.readInt());
Log.d(TAG, "package id %d", resPackage.getId());
byte[] buffer = new byte[256];
dataInput.read(buffer);
resPackage.setName(buffer);
Log.d(TAG, "package name %s", ArscUtil.toUTF16String(buffer));
resPackage.setResTypePoolOffset(dataInput.readInt());
Log.d(TAG, "resType pool offset %d", resPackage.getResTypePoolOffset());
resPackage.setLastPublicType(dataInput.readInt());
Log.d(TAG, "lastPublicType index %d", resPackage.getLastPublicType());
resPackage.setResNamePoolOffset(dataInput.readInt());
Log.d(TAG, "resName pool offset %d", resPackage.getResNamePoolOffset());
resPackage.setLastPublicName(dataInput.readInt());
Log.d(TAG, "lastPublicName index %d", resPackage.getLastPublicName());
resPackage.setHeadPaddingSize((int) (resPackage.getHeadSize() + headStart - dataInput.getFilePointer()));
if (resPackage.getResTypePoolOffset() > 0) {
dataInput.seek(headStart + resPackage.getResTypePoolOffset());
ResStringBlock resTypePool = readStringBlock();
resPackage.setResTypePool(resTypePool);
}
if (resPackage.getResNamePoolOffset() > 0) {
dataInput.seek(headStart + resPackage.getResNamePoolOffset());
ResStringBlock resNamePool = readStringBlock();
resPackage.setResNamePool(resNamePool);
}
List resTypeList = new ArrayList();
while (dataInput.getFilePointer() < (resPackage.getStart() + resPackage.getChunkSize())) {
int type = dataInput.readShort();
if (type == ArscConstants.RES_TABLE_TYPE_SPEC_TYPE) {
dataInput.seek(dataInput.getFilePointer() - 2);
ResTypeSpec resTypeSpec = readResTypeSpec();
resTypeList.add(resTypeSpec);
} else if (type == ArscConstants.RES_TABLE_TYPE_TYPE) {
dataInput.seek(dataInput.getFilePointer() - 2);
ResType resType = readResType(resPackage);
resTypeList.add(resType);
}
}
resPackage.setResTypeArray(resTypeList);
resPackage.setChunkPaddingSize((int) (resPackage.getChunkSize() + headStart - dataInput.getFilePointer()));
dataInput.seek(resPackage.getStart() + resPackage.getChunkSize());
return resPackage;
}
private ResTypeSpec readResTypeSpec() throws IOException {
Log.d(TAG, "===========================");
long headStart = dataInput.getFilePointer();
ResTypeSpec resTypeSpec = new ResTypeSpec();
resTypeSpec.setStart(headStart);
resTypeSpec.setType(dataInput.readShort());
Log.d(TAG, "resTypeSpec type %d", resTypeSpec.getType());
resTypeSpec.setHeadSize(dataInput.readShort());
Log.d(TAG, "resTypeSpec header size %d", resTypeSpec.getHeadSize());
resTypeSpec.setChunkSize(dataInput.readInt());
Log.d(TAG, "resTypeSpec chunk size %d", resTypeSpec.getChunkSize());
resTypeSpec.setId(dataInput.readByte());
Log.d(TAG, "resTypeSpec type id %d", resTypeSpec.getId());
resTypeSpec.setReserved0(dataInput.readByte());
resTypeSpec.setReserved1(dataInput.readShort());
resTypeSpec.setEntryCount(dataInput.readInt());
Log.d(TAG, "resTypeSpec entry count %d", resTypeSpec.getEntryCount());
resTypeSpec.setHeadPaddingSize((int) (resTypeSpec.getHeadSize() + headStart - dataInput.getFilePointer()));
if (resTypeSpec.getChunkSize() - resTypeSpec.getHeadSize() > 0) {
byte[] buffer = new byte[resTypeSpec.getChunkSize() - resTypeSpec.getHeadSize()];
dataInput.read(buffer);
resTypeSpec.setConfigFlags(buffer);
}
resTypeSpec.setChunkPaddingSize((int) (resTypeSpec.getChunkSize() + headStart - dataInput.getFilePointer()));
dataInput.seek(headStart + resTypeSpec.getChunkSize());
return resTypeSpec;
}
private ResType readResType(ResPackage resPackage) throws IOException {
Log.d(TAG, "===========================");
long headStart = dataInput.getFilePointer();
ResType resType = new ResType();
resType.setStart(headStart);
resType.setType(dataInput.readShort());
Log.d(TAG, "resType type %d", resType.getType());
resType.setHeadSize(dataInput.readShort());
Log.d(TAG, "resType header size %d", resType.getHeadSize());
resType.setChunkSize(dataInput.readInt());
Log.d(TAG, "resType chunk size %d", resType.getChunkSize());
resType.setId(dataInput.readByte());
Log.d(TAG, "resType type id %d", resType.getId());
resType.setReserved0(dataInput.readByte());
resType.setReserved1(dataInput.readShort());
resType.setEntryCount(dataInput.readInt());
Log.d(TAG, "resType entry count %d", resType.getEntryCount());
resType.setEntryTableOffset(dataInput.readInt());
Log.d(TAG, "resType entryTable offset %d", resType.getEntryTableOffset());
resType.setResConfigFlags(readResConfig());
resType.setHeadPaddingSize((int) (resType.getHeadSize() + headStart - dataInput.getFilePointer()));
if (resType.getEntryCount() > 0) {
List resEntryOffsets = new ArrayList();
for (int i = 0; i < resType.getEntryCount(); i++) {
resEntryOffsets.add(dataInput.readInt());
}
resType.setEntryOffsets(resEntryOffsets);
}
dataInput.seek(headStart + resType.getEntryTableOffset());
List entryTable = new ArrayList();
for (int i = 0; i < resType.getEntryCount(); i++) {
if (resType.getEntryOffsets().get(i) != ArscConstants.NO_ENTRY_INDEX) {
entryTable.add(readResEntry(resPackage,
headStart + resType.getEntryTableOffset() + resType.getEntryOffsets().get(i)));
} else {
entryTable.add(null);
}
}
resType.setEntryTable(entryTable);
resType.setChunkPaddingSize((int) (resType.getChunkSize() + headStart - dataInput.getFilePointer()));
dataInput.seek(headStart + resType.getChunkSize());
return resType;
}
@SuppressWarnings("PMD")
private ResEntry readResEntry(ResPackage resPackage, long start) throws IOException {
//Log.d(TAG, "===========================");
dataInput.seek(start);
ResEntry resEntry = new ResEntry();
resEntry.setSize(dataInput.readShort());
//Log.d(TAG, "resEntry size %d", resEntry.getSize());
resEntry.setFlag(dataInput.readShort());
//Log.d(TAG, "resEntry flag %d", resEntry.getFlag());
resEntry.setStringPoolIndex(dataInput.readInt());
//Log.d(TAG, "entryName %s", ArscUtil.resolveStringPoolEntry(resPackage.getResNamePool().getStrings().get(resEntry.getStringPoolIndex()).array(), resPackage.getResNamePool().getCharSet()));
if ((resEntry.getFlag() & ArscConstants.RES_TABLE_ENTRY_FLAG_COMPLEX) == 0) {
resEntry.setResValue(readResValue());
} else {
resEntry.setParent(dataInput.readInt());
resEntry.setPairCount(dataInput.readInt());
if (resEntry.getPairCount() > 0) {
List mapValues = new ArrayList();
for (int i = 0; i < resEntry.getPairCount(); i++) {
mapValues.add(readResMapValue());
}
resEntry.setResMapValues(mapValues);
}
}
return resEntry;
}
private ResValue readResValue() throws IOException {
//Log.d(TAG,"===========================");
ResValue resValue = new ResValue();
resValue.setSize(dataInput.readShort());
if (resValue.getSize() > 2) {
byte[] content = new byte[resValue.getSize() - 2];
dataInput.read(content);
resValue.setContent(content);
}
return resValue;
}
private ResMapValue readResMapValue() throws IOException {
//Log.d(TAG, "===========================");
ResMapValue resValue = new ResMapValue();
resValue.setName(dataInput.readInt());
resValue.setResValue(readResValue());
return resValue;
}
private ResConfig readResConfig() throws IOException {
//Log.d(TAG, "===========================");
ResConfig config = new ResConfig();
config.setSize(dataInput.readInt());
//Log.d(TAG, "resConfig size %d", config.getSize());
if (config.getSize() > 4) {
byte[] buffer = new byte[config.getSize() - 4];
dataInput.read(buffer);
config.setContent(buffer);
}
return config;
}
private ResStringBlock readStringBlock() throws IOException {
Log.d(TAG, "===========================");
long headStart = dataInput.getFilePointer();
ResStringBlock stringPool = new ResStringBlock();
stringPool.setStart(headStart);
stringPool.setType(dataInput.readShort());
Log.d(TAG, "stringPool type %d", stringPool.getType());
stringPool.setHeadSize(dataInput.readShort());
Log.d(TAG, "stringPool head size %d", stringPool.getHeadSize());
stringPool.setChunkSize(dataInput.readInt());
Log.d(TAG, "stringPool chunk size %d", stringPool.getChunkSize());
stringPool.setStringCount(dataInput.readInt());
Log.d(TAG, "stringPool string count %d", stringPool.getStringCount());
stringPool.setStyleCount(dataInput.readInt());
Log.d(TAG, "stringPool style count %d", stringPool.getStyleCount());
stringPool.setFlag(dataInput.readInt());
Log.d(TAG, "stringPool flag %d", stringPool.getFlag());
stringPool.setStringStart(dataInput.readInt());
Log.d(TAG, "stringPool string start %d", stringPool.getStringStart());
stringPool.setStyleStart(dataInput.readInt());
Log.d(TAG, "stringPool style start %d", stringPool.getStyleStart());
stringPool.setHeadPaddingSize((int) (stringPool.getHeadSize() + headStart - dataInput.getFilePointer()));
dataInput.seek(headStart + stringPool.getHeadSize());
if (stringPool.getStringCount() > 0) {
List stringOffsets = new ArrayList();
for (int i = 0; i < stringPool.getStringCount(); i++) {
stringOffsets.add(dataInput.readInt());
}
stringPool.setStringOffsets(stringOffsets);
}
if (stringPool.getStyleCount() > 0) {
List styleOffsets = new ArrayList();
for (int i = 0; i < stringPool.getStyleCount(); i++) {
styleOffsets.add(dataInput.readInt());
}
stringPool.setStyleOffsets(styleOffsets);
}
dataInput.seek(headStart + stringPool.getStringStart());
if (stringPool.getStringCount() > 0) {
List strings = new ArrayList();
for (int i = 0; i < stringPool.getStringCount(); i++) {
byte[] buffer = null;
if (i < stringPool.getStringCount() - 1) {
buffer = new byte[stringPool.getStringOffsets().get(i + 1) - stringPool.getStringOffsets().get(i)];
} else {
if (stringPool.getStyleCount() > 0) {
buffer = new byte[stringPool.getStyleStart() - (stringPool.getStringOffsets().get(i) + stringPool.getStringStart())];
} else {
buffer = new byte[stringPool.getChunkSize() - stringPool.getStringStart() - stringPool.getStringOffsets().get(i)];
}
}
dataInput.read(buffer);
strings.add(ByteBuffer.allocate(buffer.length));
strings.get(i).order(ByteOrder.LITTLE_ENDIAN);
strings.get(i).clear();
strings.get(i).put(buffer);
}
stringPool.setStrings(strings);
}
if (stringPool.getStyleCount() > 0) {
byte[] styleBytes = new byte[stringPool.getChunkSize() - stringPool.getStyleStart()];
dataInput.read(styleBytes);
stringPool.setStyles(styleBytes);
}
stringPool.setChunkPaddingSize((int) (stringPool.getChunkSize() + headStart - dataInput.getFilePointer()));
dataInput.seek(headStart + stringPool.getChunkSize());
return stringPool;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy