/* * Copyright 2008 Android4ME * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 parser.arsc; import parser.axml.res.IntReader; import parser.axml.res.StringBlock; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * 该class用于解析文件, */ public class ARSCFile { private static final int ARSC_CHUNK_TYPE = 0x000C0002, PACKAGE_CHUNK_TYPE = 0x011c0200, ASSET_CHUNK_TYPE = 0x00000202, CONTENT_CHUNK_TYPE = 0x00000201; public StringBlock strings; public Pkge[] pkges; public ARSCFile(IntReader reader) throws IOException { int arsc_type = reader.readInt(); if (arsc_type != ARSC_CHUNK_TYPE) throw new IOException("Expected chunk of type 0x" + Integer.toHexString(ARSC_CHUNK_TYPE) + ", read 0x" + Integer.toHexString(arsc_type) + "."); /* size */ reader.readInt(); int pkgCount = reader.readInt(); try { strings = new StringBlock(reader); } catch (Exception e) { e.printStackTrace(); return; } pkges = new Pkge[pkgCount]; int pkgType = reader.readInt(); if (pkgType != PACKAGE_CHUNK_TYPE) throw new IOException("Expected chunk of type 0x" + Integer.toHexString(PACKAGE_CHUNK_TYPE) + ", read 0x" + Integer.toHexString(pkgType) + "."); for (int i = 0; i < pkgCount; i++) { pkges[i] = readPackage(reader); } } // ///////////////////////////////// data private static Pkge readPackage(IntReader reader) throws IOException { Pkge pkge = new Pkge(); /* size */ reader.skipInt(); pkge.id = reader.readInt(); { final int nameLength = 128; StringBuilder name = new StringBuilder(16); int i = 0; for (; i != nameLength; ) { ++i; int ch = reader.readShort(); if (ch == 0) { break; } name.append((char) ch); } reader.skip((nameLength - i) * 2); reader.skip((nameLength * 2) % 4); pkge.name = name.toString(); } /* signature? */ reader.skipInt(); int assetCount = reader.readInt(); /* idNamesOffset */ reader.skipInt(); /* idNamesCount */ reader.skipInt(); pkge.assetNames = new StringBlock(reader); pkge.resourceNames = new StringBlock(reader); pkge.assets = new Asset[assetCount]; int assetType = reader.readInt() & 0x0000ffff; for (int i = 0; i < assetCount; i++) { pkge.assets[i] = readAsset(reader, assetType); } return pkge; } private static Asset readAsset(IntReader reader, int type) throws IOException { if (type != ASSET_CHUNK_TYPE) throw new IOException("asset 类型不匹配!"); /* chunkSize */ reader.skipInt(); Asset asset = new Asset(); asset.id = reader.readInt(); int count = reader.readInt(); asset.flags = reader.readIntArray(count); asset.contents = readContents(reader); return asset; } // ///////////////////////////////// creator private static List<Content> readContents(IntReader reader) throws IOException { List<Content> contents = new ArrayList<Content>(); while (reader.available() > 0) { int type = reader.readInt() & 0x0000ffff; if (type == CONTENT_CHUNK_TYPE) { } else if (type == ASSET_CHUNK_TYPE || type == (PACKAGE_CHUNK_TYPE & 0x0000ffff)) { return contents; } else { throw new IOException("content 类型不匹配 type=" + Integer.toHexString(type)); } int chunkSize = reader.readInt(); Content content = new Content(); content.id = reader.readInt(); int offsetCount = reader.readInt(); int dataOffset = reader.readInt(); int size = reader.readInt(); /* skip Configuartion */ reader.skip(size - 4); content.offsets = reader.readIntArray(offsetCount); int dataSize = (chunkSize - dataOffset); if ((dataSize % 4) != 0) { throw new IOException("Content data size (" + dataSize + ") is not multiple of 4."); } content.data = reader.readIntArray(dataSize / 4); contents.add(content); } return contents; } public static class Pkge { public int id; public String name; public StringBlock resourceNames; public StringBlock assetNames; public Asset[] assets; } /** * 'id'-1 gives asset name index. 'flags' values are essentially * changingConfiguration values. 'flags.length' is total resource count in * asset. */ public static class Asset { public int id; public int[] flags; public List<Content> contents; } public static class Content { public Configuration configuration; public int id; public int[] offsets; public int[] data; } /** * int values are interpreted according to * android.content.res.Configuration. */ public static class Configuration { public String language; public String country; public int orientation; public int touchscreen; public int keyboard; public int keyboardHidden; public int navigation; public int screenWidth; public int screenHeight; } }