mirror of
https://github.com/revanced/ARSCLib.git
synced 2025-06-13 05:37:41 +02:00
1.0.9
This commit is contained in:
@ -26,6 +26,7 @@ import java.util.List;
|
||||
|
||||
public class ApkJsonEncoder {
|
||||
private APKArchive apkArchive;
|
||||
private APKLogger apkLogger;
|
||||
public ApkJsonEncoder(){
|
||||
}
|
||||
public ApkModule scanDirectory(File moduleDir){
|
||||
@ -36,6 +37,7 @@ public class ApkJsonEncoder {
|
||||
scanResJsonDirs(moduleDir);
|
||||
scanRootDirs(moduleDir);
|
||||
ApkModule module=new ApkModule(moduleName, apkArchive);
|
||||
module.setAPKLogger(apkLogger);
|
||||
loadUncompressed(module, moduleDir);
|
||||
applyResourceId(module, moduleDir);
|
||||
return module;
|
||||
@ -93,6 +95,7 @@ public class ApkJsonEncoder {
|
||||
return;
|
||||
}
|
||||
JsonManifestInputSource inputSource=JsonManifestInputSource.fromFile(moduleDir, file);
|
||||
inputSource.setAPKLogger(apkLogger);
|
||||
apkArchive.add(inputSource);
|
||||
}
|
||||
private void scanTable(File moduleDir) {
|
||||
@ -108,6 +111,7 @@ public class ApkJsonEncoder {
|
||||
return false;
|
||||
}
|
||||
SplitJsonTableInputSource inputSource=new SplitJsonTableInputSource(dir);
|
||||
inputSource.setAPKLogger(apkLogger);
|
||||
apkArchive.add(inputSource);
|
||||
return true;
|
||||
}
|
||||
@ -117,6 +121,7 @@ public class ApkJsonEncoder {
|
||||
return;
|
||||
}
|
||||
SingleJsonTableInputSource inputSource= SingleJsonTableInputSource.fromFile(moduleDir, file);
|
||||
inputSource.setAPKLogger(apkLogger);
|
||||
apkArchive.add(inputSource);
|
||||
}
|
||||
private File toJsonTableFile(File dir){
|
||||
@ -143,4 +148,23 @@ public class ApkJsonEncoder {
|
||||
private File toRootDir(File dir){
|
||||
return new File(dir, ApkUtil.ROOT_NAME);
|
||||
}
|
||||
|
||||
public void setAPKLogger(APKLogger logger) {
|
||||
this.apkLogger = logger;
|
||||
}
|
||||
private void logMessage(String msg) {
|
||||
if(apkLogger!=null){
|
||||
apkLogger.logMessage(msg);
|
||||
}
|
||||
}
|
||||
private void logError(String msg, Throwable tr) {
|
||||
if(apkLogger!=null){
|
||||
apkLogger.logError(msg, tr);
|
||||
}
|
||||
}
|
||||
private void logVerbose(String msg) {
|
||||
if(apkLogger!=null){
|
||||
apkLogger.logVerbose(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ import com.reandroid.lib.arsc.group.StringGroup;
|
||||
import com.reandroid.lib.arsc.item.TableString;
|
||||
import com.reandroid.lib.arsc.pool.TableStringPool;
|
||||
import com.reandroid.lib.arsc.value.EntryBlock;
|
||||
import sun.tools.jconsole.Tab;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -178,6 +177,9 @@ public class ApkModule {
|
||||
}
|
||||
for(TableString tableString:groupTableString.listItems()){
|
||||
List<EntryBlock> entryBlockList = tableString.listReferencedEntries();
|
||||
if(entryBlockList.size()==0){
|
||||
continue;
|
||||
}
|
||||
ResFile resFile=new ResFile(inputSource, entryBlockList);
|
||||
results.add(resFile);
|
||||
}
|
||||
|
81
src/main/java/com/reandroid/lib/apk/FileMagic.java
Normal file
81
src/main/java/com/reandroid/lib/apk/FileMagic.java
Normal file
@ -0,0 +1,81 @@
|
||||
package com.reandroid.lib.apk;
|
||||
|
||||
import com.reandroid.archive.InputSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class FileMagic {
|
||||
|
||||
public static String getExtensionFromMagic(InputSource inputSource) throws IOException {
|
||||
byte[] magic=readFileMagic(inputSource);
|
||||
if(magic==null){
|
||||
return null;
|
||||
}
|
||||
if(isPng(magic)){
|
||||
return ".png";
|
||||
}
|
||||
if(isJpeg(magic)){
|
||||
return ".jpg";
|
||||
}
|
||||
if(isWebp(magic)){
|
||||
return ".webp";
|
||||
}
|
||||
if(isTtf(magic)){
|
||||
return ".ttf";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isJpeg(byte[] magic){
|
||||
return compareMagic(MAGIC_JPG, magic);
|
||||
}
|
||||
private static boolean isPng(byte[] magic){
|
||||
return compareMagic(MAGIC_PNG, magic);
|
||||
}
|
||||
private static boolean isWebp(byte[] magic){
|
||||
return compareMagic(MAGIC_WEBP, magic);
|
||||
}
|
||||
private static boolean isTtf(byte[] magic){
|
||||
return compareMagic(MAGIC_TTF, magic);
|
||||
}
|
||||
private static boolean compareMagic(byte[] magic, byte[] readMagic){
|
||||
if(magic==null || readMagic==null){
|
||||
return false;
|
||||
}
|
||||
int max=magic.length;
|
||||
if(max>readMagic.length){
|
||||
max=readMagic.length;
|
||||
}
|
||||
if(max==0){
|
||||
return false;
|
||||
}
|
||||
for(int i=0;i<max;i++){
|
||||
int m=magic[i];
|
||||
if(m==-1){
|
||||
continue;
|
||||
}
|
||||
if(m != readMagic[i]){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private static byte[] readFileMagic(InputSource inputSource) throws IOException {
|
||||
InputStream inputStream=inputSource.openStream();
|
||||
byte[] magic=new byte[MAGIC_MAX_LENGTH];
|
||||
int count=inputStream.read(magic, 0, magic.length);
|
||||
inputStream.close();
|
||||
if(count<magic.length){
|
||||
return null;
|
||||
}
|
||||
return magic;
|
||||
}
|
||||
|
||||
private static final int MAGIC_MAX_LENGTH=16;
|
||||
private static final byte[] MAGIC_PNG=new byte[]{(byte) 137, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
|
||||
private static final byte[] MAGIC_JPG=new byte[]{-0x01, (byte) 0xd8, -0x01, (byte) 224, 0x00, 0x10, 0x4a, 0x46};
|
||||
private static final byte[] MAGIC_WEBP=new byte[]{0x52, 0x49, 0x46, 0x46, -0x01, -0x01, -0x01, 0x00, 0x57, 0x45, 0x42, 0x50, 0x56, 0x50, 0x38};
|
||||
private static final byte[] MAGIC_TTF=new byte[]{0x00, 0x01, 0x00, 0x00, 0x00, -0x01, -0x01, -0x01};
|
||||
|
||||
}
|
@ -26,6 +26,7 @@ import java.io.*;
|
||||
|
||||
public class JsonXmlInputSource extends InputSource {
|
||||
private final InputSource inputSource;
|
||||
private APKLogger apkLogger;
|
||||
public JsonXmlInputSource(InputSource inputSource) {
|
||||
super(inputSource.getAlias());
|
||||
this.inputSource=inputSource;
|
||||
@ -45,6 +46,7 @@ public class JsonXmlInputSource extends InputSource {
|
||||
return resXmlBlock.countBytes();
|
||||
}
|
||||
private ResXmlBlock getResXmlBlock() throws IOException{
|
||||
logVerbose("From json: "+getAlias());
|
||||
ResXmlBlock resXmlBlock=newInstance();
|
||||
InputStream inputStream=inputSource.openStream();
|
||||
try{
|
||||
@ -58,6 +60,25 @@ public class JsonXmlInputSource extends InputSource {
|
||||
ResXmlBlock newInstance(){
|
||||
return new ResXmlBlock();
|
||||
}
|
||||
void setAPKLogger(APKLogger logger) {
|
||||
this.apkLogger = logger;
|
||||
}
|
||||
void logMessage(String msg) {
|
||||
if(apkLogger!=null){
|
||||
apkLogger.logMessage(msg);
|
||||
}
|
||||
}
|
||||
private void logError(String msg, Throwable tr) {
|
||||
if(apkLogger!=null){
|
||||
apkLogger.logError(msg, tr);
|
||||
}
|
||||
}
|
||||
private void logVerbose(String msg) {
|
||||
if(apkLogger!=null){
|
||||
apkLogger.logVerbose(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static JsonXmlInputSource fromFile(File rootDir, File jsonFile){
|
||||
String path=ApkUtil.toArchiveResourcePath(rootDir, jsonFile);
|
||||
FileInputSource fileInputSource=new FileInputSource(jsonFile, path);
|
||||
|
@ -30,6 +30,8 @@ public class ResFile {
|
||||
private final InputSource inputSource;
|
||||
private boolean mBinXml;
|
||||
private boolean mBinXmlChecked;
|
||||
private String mFileExtension;
|
||||
private boolean mFileExtensionChecked;
|
||||
public ResFile(InputSource inputSource, List<EntryBlock> entryBlockList){
|
||||
this.inputSource=inputSource;
|
||||
this.entryBlockList=entryBlockList;
|
||||
@ -116,7 +118,7 @@ public class ResFile {
|
||||
return true;
|
||||
}
|
||||
public File buildOutFile(File dir){
|
||||
String path=buildPath();
|
||||
String path=getFilePath();
|
||||
path=path.replace('/', File.separatorChar);
|
||||
return new File(dir, path);
|
||||
}
|
||||
@ -135,15 +137,29 @@ public class ResFile {
|
||||
return builder.toString();
|
||||
}
|
||||
private String getFileExtension(){
|
||||
if(!mFileExtensionChecked){
|
||||
mFileExtensionChecked=true;
|
||||
mFileExtension=readFileExtension();
|
||||
}
|
||||
return mFileExtension;
|
||||
}
|
||||
private String readFileExtension(){
|
||||
if(isBinaryXml()){
|
||||
return ".xml";
|
||||
}
|
||||
String path=getFilePath();
|
||||
int i=path.lastIndexOf('.');
|
||||
if(i<0){
|
||||
return null;
|
||||
if(i>0){
|
||||
return path.substring(i);
|
||||
}
|
||||
return path.substring(0);
|
||||
try {
|
||||
String magicExt=FileMagic.getExtensionFromMagic(getInputSource());
|
||||
if(magicExt!=null){
|
||||
return magicExt;
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
|
@ -15,10 +15,10 @@
|
||||
*/
|
||||
package com.reandroid.lib.apk;
|
||||
|
||||
|
||||
import com.reandroid.lib.arsc.chunk.PackageBlock;
|
||||
import com.reandroid.lib.arsc.chunk.TableBlock;
|
||||
import com.reandroid.lib.arsc.group.EntryGroup;
|
||||
import com.reandroid.lib.arsc.pool.SpecStringPool;
|
||||
import com.reandroid.lib.json.JSONArray;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
@ -36,8 +36,11 @@ public class ResourceIds {
|
||||
public ResourceIds(){
|
||||
this(new Table());
|
||||
}
|
||||
public void applyTo(TableBlock tableBlock){
|
||||
mTable.applyTo(tableBlock);
|
||||
public Table getTable(){
|
||||
return mTable;
|
||||
}
|
||||
public int applyTo(TableBlock tableBlock){
|
||||
return mTable.applyTo(tableBlock);
|
||||
}
|
||||
public void fromJson(JSONObject jsonObject){
|
||||
mTable.fromJson(jsonObject);
|
||||
@ -91,14 +94,18 @@ public class ResourceIds {
|
||||
public Table(){
|
||||
this.packageMap = new HashMap<>();
|
||||
}
|
||||
public void applyTo(TableBlock tableBlock){
|
||||
public int applyTo(TableBlock tableBlock){
|
||||
int renameCount=0;
|
||||
for(PackageBlock packageBlock : tableBlock.listPackages()){
|
||||
Package pkg=getPackage((byte) packageBlock.getId());
|
||||
if(pkg!=null){
|
||||
pkg.applyTo(packageBlock);
|
||||
renameCount+=pkg.applyTo(packageBlock);
|
||||
}
|
||||
}
|
||||
tableBlock.refresh();
|
||||
if(renameCount>0){
|
||||
tableBlock.refresh();
|
||||
}
|
||||
return renameCount;
|
||||
}
|
||||
public void add(Package pkg){
|
||||
Package exist=this.packageMap.get(pkg.id);
|
||||
@ -241,7 +248,8 @@ public class ResourceIds {
|
||||
this.id = id;
|
||||
this.typeMap = new HashMap<>();
|
||||
}
|
||||
public void applyTo(PackageBlock packageBlock){
|
||||
public int applyTo(PackageBlock packageBlock){
|
||||
int renameCount=0;
|
||||
Map<Integer, EntryGroup> map = packageBlock.getEntriesGroupMap();
|
||||
for(Map.Entry<Integer, EntryGroup> entry:map.entrySet()){
|
||||
byte typeId=Table.toTypeId(entry.getKey());
|
||||
@ -250,8 +258,20 @@ public class ResourceIds {
|
||||
continue;
|
||||
}
|
||||
EntryGroup entryGroup=entry.getValue();
|
||||
type.applyTo(entryGroup);
|
||||
if(type.applyTo(entryGroup)){
|
||||
renameCount++;
|
||||
}
|
||||
}
|
||||
if(renameCount>0){
|
||||
cleanSpecStringPool(packageBlock);
|
||||
}
|
||||
return renameCount;
|
||||
}
|
||||
private void cleanSpecStringPool(PackageBlock packageBlock){
|
||||
SpecStringPool specStringPool = packageBlock.getSpecStringPool();
|
||||
specStringPool.refreshUniqueIdMap();
|
||||
specStringPool.removeUnusedStrings();
|
||||
packageBlock.refresh();
|
||||
}
|
||||
public void merge(Package pkg){
|
||||
if(pkg==this||pkg==null){
|
||||
@ -389,18 +409,30 @@ public class ResourceIds {
|
||||
public static class Type implements Comparable<Type>, Comparator<Type.Entry>{
|
||||
public final byte id;
|
||||
public String name;
|
||||
public String nameAlias;
|
||||
public Package mPackage;
|
||||
public final Map<Short, Entry> entryMap;
|
||||
public Type(byte id){
|
||||
this.id = id;
|
||||
this.entryMap = new HashMap<>();
|
||||
}
|
||||
public void applyTo(EntryGroup entryGroup){
|
||||
public boolean applyTo(EntryGroup entryGroup){
|
||||
boolean renamed=false;
|
||||
Entry entry=entryMap.get(entryGroup.getEntryId());
|
||||
if(entry!=null){
|
||||
entry.applyTo(entryGroup);
|
||||
if(entry.applyTo(entryGroup)){
|
||||
renamed=true;
|
||||
}
|
||||
}
|
||||
return renamed;
|
||||
}
|
||||
public String getName() {
|
||||
if(nameAlias!=null){
|
||||
return nameAlias;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public byte getId(){
|
||||
return id;
|
||||
}
|
||||
@ -417,13 +449,14 @@ public class ResourceIds {
|
||||
if(this.id!= type.id){
|
||||
throw new DuplicateException("Different type ids: "+id+"!="+type.id);
|
||||
}
|
||||
if(type.name!=null){
|
||||
this.name=type.name;
|
||||
String n=type.getName();
|
||||
if(n!=null){
|
||||
this.name=n;
|
||||
}
|
||||
for(Entry entry:type.entryMap.values()){
|
||||
Short entryId=entry.getEntryId();
|
||||
Entry existEntry=this.entryMap.get(entryId);
|
||||
if(existEntry != null && Objects.equals(existEntry.name, entry.name)){
|
||||
if(existEntry != null && Objects.equals(existEntry.getName(), entry.getName())){
|
||||
continue;
|
||||
}
|
||||
this.entryMap.remove(entryId);
|
||||
@ -447,13 +480,13 @@ public class ResourceIds {
|
||||
short key=entry.getEntryId();
|
||||
Entry exist=entryMap.get(key);
|
||||
if(exist!=null){
|
||||
if(Objects.equals(exist.name, entry.name)){
|
||||
if(Objects.equals(exist.getName(), entry.getName())){
|
||||
return;
|
||||
}
|
||||
throw new DuplicateException("Duplicate entry exist: "+exist+", entry: "+entry);
|
||||
}
|
||||
if(name == null){
|
||||
this.name = entry.typeName;
|
||||
if(getName() == null){
|
||||
this.name = entry.getTypeName();
|
||||
}
|
||||
entry.type=this;
|
||||
entryMap.put(key, entry);
|
||||
@ -461,8 +494,8 @@ public class ResourceIds {
|
||||
|
||||
public JSONObject toJson(){
|
||||
JSONObject jsonObject=new JSONObject();
|
||||
jsonObject.put("id", id);
|
||||
jsonObject.put("name", name);
|
||||
jsonObject.put("id", getId());
|
||||
jsonObject.put("name", getName());
|
||||
JSONArray jsonArray=new JSONArray();
|
||||
for(Entry entry: entryMap.values()){
|
||||
jsonArray.put(entry.toJson());
|
||||
@ -507,8 +540,9 @@ public class ResourceIds {
|
||||
public String toString(){
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append(getHexId());
|
||||
if(name !=null){
|
||||
builder.append(" ").append(name);
|
||||
String n=getName();
|
||||
if(n !=null){
|
||||
builder.append(" ").append(n);
|
||||
}
|
||||
builder.append(", entries=").append(entryMap.size());
|
||||
return builder.toString();
|
||||
@ -532,6 +566,7 @@ public class ResourceIds {
|
||||
public int resourceId;
|
||||
public String typeName;
|
||||
public String name;
|
||||
public String nameAlias;
|
||||
public Type type;
|
||||
public Entry(int resourceId, String typeName, String name){
|
||||
this.resourceId = resourceId;
|
||||
@ -541,12 +576,18 @@ public class ResourceIds {
|
||||
public Entry(int resourceId, String name){
|
||||
this(resourceId, null, name);
|
||||
}
|
||||
public void applyTo(EntryGroup entryGroup){
|
||||
entryGroup.renameSpec(this.name);
|
||||
public boolean applyTo(EntryGroup entryGroup){
|
||||
return entryGroup.renameSpec(this.getName());
|
||||
}
|
||||
public String getName() {
|
||||
if(nameAlias!=null){
|
||||
return nameAlias;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
public String getTypeName(){
|
||||
if(this.type!=null){
|
||||
return this.type.name;
|
||||
return this.type.getName();
|
||||
}
|
||||
return this.typeName;
|
||||
}
|
||||
@ -594,7 +635,7 @@ public class ResourceIds {
|
||||
public JSONObject toJson(){
|
||||
JSONObject jsonObject=new JSONObject();
|
||||
jsonObject.put("id", getResourceId());
|
||||
jsonObject.put("name", name);
|
||||
jsonObject.put("name", getName());
|
||||
return jsonObject;
|
||||
}
|
||||
public String toXml(){
|
||||
@ -618,9 +659,10 @@ public class ResourceIds {
|
||||
writer.append(tn);
|
||||
writer.append("\"");
|
||||
}
|
||||
if(name!=null){
|
||||
String n=getName();
|
||||
if(n!=null){
|
||||
writer.write(" name=\"");
|
||||
writer.append(name);
|
||||
writer.append(n);
|
||||
writer.append("\"");
|
||||
}
|
||||
writer.append("/>");
|
||||
|
@ -26,6 +26,7 @@ import java.io.*;
|
||||
public class SingleJsonTableInputSource extends InputSource {
|
||||
private final InputSource inputSource;
|
||||
private TableBlock mCache;
|
||||
private APKLogger apkLogger;
|
||||
public SingleJsonTableInputSource(InputSource inputSource) {
|
||||
super(inputSource.getAlias());
|
||||
this.inputSource=inputSource;
|
||||
@ -54,6 +55,7 @@ public class SingleJsonTableInputSource extends InputSource {
|
||||
if(mCache!=null){
|
||||
return mCache;
|
||||
}
|
||||
logMessage("Building resources table: "+inputSource.getAlias());
|
||||
TableBlock tableBlock=newInstance();
|
||||
InputStream inputStream=inputSource.openStream();
|
||||
try{
|
||||
@ -76,4 +78,22 @@ public class SingleJsonTableInputSource extends InputSource {
|
||||
FileInputSource fileInputSource=new FileInputSource(jsonFile, path);
|
||||
return new SingleJsonTableInputSource(fileInputSource);
|
||||
}
|
||||
void setAPKLogger(APKLogger logger) {
|
||||
this.apkLogger = logger;
|
||||
}
|
||||
private void logMessage(String msg) {
|
||||
if(apkLogger!=null){
|
||||
apkLogger.logMessage(msg);
|
||||
}
|
||||
}
|
||||
private void logError(String msg, Throwable tr) {
|
||||
if(apkLogger!=null){
|
||||
apkLogger.logError(msg, tr);
|
||||
}
|
||||
}
|
||||
private void logVerbose(String msg) {
|
||||
if(apkLogger!=null){
|
||||
apkLogger.logVerbose(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import java.io.*;
|
||||
public class SplitJsonTableInputSource extends InputSource {
|
||||
private final File dir;
|
||||
private TableBlock mCache;
|
||||
private APKLogger apkLogger;
|
||||
public SplitJsonTableInputSource(File dir) {
|
||||
super(TableBlock.FILE_NAME);
|
||||
this.dir=dir;
|
||||
@ -56,4 +57,22 @@ public class SplitJsonTableInputSource extends InputSource {
|
||||
mCache=tableBlock;
|
||||
return tableBlock;
|
||||
}
|
||||
void setAPKLogger(APKLogger logger) {
|
||||
this.apkLogger = logger;
|
||||
}
|
||||
void logMessage(String msg) {
|
||||
if(apkLogger!=null){
|
||||
apkLogger.logMessage(msg);
|
||||
}
|
||||
}
|
||||
private void logError(String msg, Throwable tr) {
|
||||
if(apkLogger!=null){
|
||||
apkLogger.logError(msg, tr);
|
||||
}
|
||||
}
|
||||
private void logVerbose(String msg) {
|
||||
if(apkLogger!=null){
|
||||
apkLogger.logVerbose(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,8 @@ import com.reandroid.lib.arsc.value.ResConfig;
|
||||
import com.reandroid.lib.json.JSONConvert;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@ -132,6 +134,20 @@ public class TypeBlock extends BaseTypeBlock
|
||||
mResConfig.refresh();
|
||||
super.onPreRefreshRefresh();
|
||||
}
|
||||
/*
|
||||
* method Block.addBytes is inefficient for large size byte array
|
||||
* so let's override here because this block is the largest
|
||||
*/
|
||||
@Override
|
||||
public byte[] getBytes(){
|
||||
ByteArrayOutputStream os=new ByteArrayOutputStream();
|
||||
try {
|
||||
writeBytes(os);
|
||||
os.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
return os.toByteArray();
|
||||
}
|
||||
@Override
|
||||
public JSONObject toJson() {
|
||||
JSONObject jsonObject=new JSONObject();
|
||||
|
@ -54,6 +54,20 @@ public class ResXmlBlock extends BaseChunk implements JSONConvert<JSONObject> {
|
||||
element.linkStringReferences();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* method Block.addBytes is inefficient for large size byte array
|
||||
* so let's override here because this block is the largest
|
||||
*/
|
||||
@Override
|
||||
public byte[] getBytes(){
|
||||
ByteArrayOutputStream os=new ByteArrayOutputStream();
|
||||
try {
|
||||
writeBytes(os);
|
||||
os.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
return os.toByteArray();
|
||||
}
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException {
|
||||
HeaderBlock headerBlock=reader.readHeaderBlock();
|
||||
|
@ -15,7 +15,9 @@
|
||||
*/
|
||||
package com.reandroid.lib.arsc.item;
|
||||
|
||||
public class SpecString extends StringItem {
|
||||
import java.util.List;
|
||||
|
||||
public class SpecString extends StringItem {
|
||||
public SpecString(boolean utf8) {
|
||||
super(utf8);
|
||||
}
|
||||
@ -24,4 +26,9 @@ public class SpecString extends StringItem {
|
||||
// Spec (resource name) don't have style unless to obfuscate/confuse other decompilers
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
List<ReferenceItem> refList = getReferencedList();
|
||||
return "USED BY="+refList.size()+"{"+super.toString()+"}";
|
||||
}
|
||||
}
|
||||
|
@ -47,5 +47,6 @@ public class TableStringPool extends BaseStringPool<TableString> {
|
||||
exist.set(coming.get());
|
||||
}
|
||||
getStyleArray().merge(stringPool.getStyleArray());
|
||||
refreshUniqueIdMap();
|
||||
}
|
||||
}
|
||||
|
@ -279,7 +279,11 @@ public class ResConfig extends FixedBlockContainer
|
||||
return Orientation.fromValue(getOrientationByte());
|
||||
}
|
||||
public void setOrientation(Orientation orientation){
|
||||
setOrientation(orientation.getByteValue());
|
||||
byte b=0;
|
||||
if(orientation!=null){
|
||||
b=orientation.getByteValue();
|
||||
}
|
||||
setOrientation(b);
|
||||
}
|
||||
public void setTouchscreen(byte b){
|
||||
if(getConfigSize()<SIZE_16){
|
||||
@ -300,7 +304,11 @@ public class ResConfig extends FixedBlockContainer
|
||||
return Touchscreen.fromValue(getTouchscreenByte());
|
||||
}
|
||||
public void setTouchscreen(Touchscreen touchscreen){
|
||||
setTouchscreen(touchscreen.getByteValue());
|
||||
byte b=0;
|
||||
if(touchscreen!=null){
|
||||
b=touchscreen.getByteValue();
|
||||
}
|
||||
setTouchscreen(b);
|
||||
}
|
||||
public void setDensity(short sh){
|
||||
if(getConfigSize()<SIZE_16){
|
||||
@ -342,7 +350,11 @@ public class ResConfig extends FixedBlockContainer
|
||||
return Keyboard.fromValue(getKeyboardByte());
|
||||
}
|
||||
public void setKeyboard(Keyboard keyboard){
|
||||
setKeyboard(keyboard.getByteValue());
|
||||
byte b=0;
|
||||
if(keyboard!=null){
|
||||
b=keyboard.getByteValue();
|
||||
}
|
||||
setKeyboard(b);
|
||||
}
|
||||
public void setNavigation(byte b){
|
||||
if(getConfigSize()<SIZE_28){
|
||||
@ -363,7 +375,11 @@ public class ResConfig extends FixedBlockContainer
|
||||
return Navigation.fromValue(getNavigationByte());
|
||||
}
|
||||
public void setNavigation(Navigation navigation){
|
||||
setNavigation(navigation.getByteValue());
|
||||
byte b=0;
|
||||
if(navigation!=null){
|
||||
b=navigation.getByteValue();
|
||||
}
|
||||
setNavigation(b);
|
||||
}
|
||||
public void setInputFlags(byte b){
|
||||
if(getConfigSize()<SIZE_28){
|
||||
@ -676,11 +692,11 @@ public class ResConfig extends FixedBlockContainer
|
||||
jsonObject.put(NAME_region, str);
|
||||
}
|
||||
Orientation orientation=getOrientation();
|
||||
if(orientation!=Orientation.ANY){
|
||||
if(orientation!=null){
|
||||
jsonObject.put(NAME_orientation, orientation.toString());
|
||||
}
|
||||
Touchscreen touchscreen=getTouchscreen();
|
||||
if(touchscreen!=Touchscreen.NONE){
|
||||
if(touchscreen!=null){
|
||||
jsonObject.put(NAME_touchscreen, touchscreen.toString());
|
||||
}
|
||||
str = getDensity();
|
||||
@ -688,11 +704,11 @@ public class ResConfig extends FixedBlockContainer
|
||||
jsonObject.put(NAME_density, str);
|
||||
}
|
||||
Keyboard keyboard = getKeyboard();
|
||||
if(keyboard!=Keyboard.NONE){
|
||||
if(keyboard!=null){
|
||||
jsonObject.put(NAME_keyboard, keyboard.toString());
|
||||
}
|
||||
Navigation navigation = getNavigation();
|
||||
if(navigation!=Navigation.NONE){
|
||||
if(navigation!=null){
|
||||
jsonObject.put(NAME_navigation, navigation.toString());
|
||||
}
|
||||
str = getInputFlags();
|
||||
@ -918,7 +934,6 @@ public class ResConfig extends FixedBlockContainer
|
||||
return result;
|
||||
}
|
||||
public enum Orientation{
|
||||
ANY((byte) 0),
|
||||
PORT((byte) 0x1),
|
||||
LAND((byte) 0x2),
|
||||
SQUARE((byte) 0x3);
|
||||
@ -939,11 +954,11 @@ public class ResConfig extends FixedBlockContainer
|
||||
return orientation;
|
||||
}
|
||||
}
|
||||
return ANY;
|
||||
return null;
|
||||
}
|
||||
public static Orientation fromName(String name){
|
||||
if(name==null){
|
||||
return ANY;
|
||||
return null;
|
||||
}
|
||||
name=name.trim().toUpperCase();
|
||||
for(Orientation orientation:values()){
|
||||
@ -951,11 +966,10 @@ public class ResConfig extends FixedBlockContainer
|
||||
return orientation;
|
||||
}
|
||||
}
|
||||
return ANY;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public enum Touchscreen{
|
||||
NONE((byte) 0x0),
|
||||
NOTOUCH((byte) 0x1),
|
||||
STYLUS((byte) 0x2),
|
||||
FINGER((byte) 0x3);
|
||||
@ -976,11 +990,11 @@ public class ResConfig extends FixedBlockContainer
|
||||
return touchscreen;
|
||||
}
|
||||
}
|
||||
return NONE;
|
||||
return null;
|
||||
}
|
||||
public static Touchscreen fromName(String name){
|
||||
if(name==null){
|
||||
return NONE;
|
||||
return null;
|
||||
}
|
||||
name=name.trim().toUpperCase();
|
||||
for(Touchscreen touchscreen:values()){
|
||||
@ -988,11 +1002,10 @@ public class ResConfig extends FixedBlockContainer
|
||||
return touchscreen;
|
||||
}
|
||||
}
|
||||
return NONE;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public enum Keyboard{
|
||||
NONE((byte) 0x0),
|
||||
NOKEYS((byte) 0x1),
|
||||
QWERTY((byte) 0x2),
|
||||
KEY12((byte) 0x3);
|
||||
@ -1016,11 +1029,11 @@ public class ResConfig extends FixedBlockContainer
|
||||
return keyboard;
|
||||
}
|
||||
}
|
||||
return NONE;
|
||||
return null;
|
||||
}
|
||||
public static Keyboard fromName(String name){
|
||||
if(name==null){
|
||||
return NONE;
|
||||
return null;
|
||||
}
|
||||
name=name.trim().toUpperCase();
|
||||
if(name.equals("12KEY")){
|
||||
@ -1031,11 +1044,10 @@ public class ResConfig extends FixedBlockContainer
|
||||
return keyboard;
|
||||
}
|
||||
}
|
||||
return NONE;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public enum Navigation{
|
||||
NONE((byte) 0),
|
||||
NONAV((byte) 0x1),
|
||||
DPAD((byte) 0x2),
|
||||
TRACKBALL((byte) 0x3),
|
||||
@ -1057,11 +1069,11 @@ public class ResConfig extends FixedBlockContainer
|
||||
return navigation;
|
||||
}
|
||||
}
|
||||
return NONE;
|
||||
return null;
|
||||
}
|
||||
public static Navigation fromName(String name){
|
||||
if(name==null){
|
||||
return NONE;
|
||||
return null;
|
||||
}
|
||||
name=name.trim().toUpperCase();
|
||||
for(Navigation navigation:values()){
|
||||
@ -1069,7 +1081,7 @@ public class ResConfig extends FixedBlockContainer
|
||||
return navigation;
|
||||
}
|
||||
}
|
||||
return NONE;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -388,42 +388,22 @@ public class ResConfigHelper {
|
||||
private static void encodeKeyboard(ResConfig resConfig, String[] split){
|
||||
byte keyboard=0;
|
||||
for(int i=0;i<split.length;i++){
|
||||
String s=split[i];
|
||||
if(s==null){
|
||||
ResConfig.Keyboard key = ResConfig.Keyboard.fromName(split[i]);
|
||||
if(key==null){
|
||||
continue;
|
||||
}
|
||||
switch (s) {
|
||||
case "nokeys":
|
||||
keyboard = KEYBOARD_NOKEYS;
|
||||
break;
|
||||
case "qwerty":
|
||||
keyboard = KEYBOARD_QWERTY;
|
||||
break;
|
||||
case "12key":
|
||||
keyboard = KEYBOARD_12KEY;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
keyboard=key.getByteValue();
|
||||
split[i]=null;
|
||||
break;
|
||||
}
|
||||
resConfig.setKeyboard(keyboard);
|
||||
}
|
||||
private static String decodeKeyboard(ResConfig resConfig){
|
||||
StringBuilder ret=new StringBuilder();
|
||||
switch (resConfig.getKeyboardByte()) {
|
||||
case KEYBOARD_NOKEYS:
|
||||
ret.append("-nokeys");
|
||||
break;
|
||||
case KEYBOARD_QWERTY:
|
||||
ret.append("-qwerty");
|
||||
break;
|
||||
case KEYBOARD_12KEY:
|
||||
ret.append("-12key");
|
||||
break;
|
||||
ResConfig.Keyboard keyboard=resConfig.getKeyboard();
|
||||
if(keyboard==null){
|
||||
return "";
|
||||
}
|
||||
return ret.toString();
|
||||
return "-"+keyboard.toString();
|
||||
}
|
||||
/*
|
||||
* Encodes density to value
|
||||
@ -554,39 +534,22 @@ public class ResConfigHelper {
|
||||
if(s==null){
|
||||
continue;
|
||||
}
|
||||
switch (s) {
|
||||
case "notouch":
|
||||
touchscreen = TOUCHSCREEN_NOTOUCH;
|
||||
break;
|
||||
case "stylus":
|
||||
touchscreen = TOUCHSCREEN_STYLUS;
|
||||
break;
|
||||
case "finger":
|
||||
touchscreen = TOUCHSCREEN_FINGER;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
ResConfig.Touchscreen touch= ResConfig.Touchscreen.fromName(s);
|
||||
if(touch==null){
|
||||
continue;
|
||||
}
|
||||
touchscreen=touch.getByteValue();
|
||||
split[i]=null;
|
||||
break;
|
||||
}
|
||||
resConfig.setTouchscreen(touchscreen);
|
||||
}
|
||||
private static String decodeTouchscreen(ResConfig resConfig){
|
||||
StringBuilder ret=new StringBuilder();
|
||||
byte touchscreen=resConfig.getTouchscreenByte();
|
||||
switch (touchscreen) {
|
||||
case TOUCHSCREEN_NOTOUCH:
|
||||
ret.append("-notouch");
|
||||
break;
|
||||
case TOUCHSCREEN_STYLUS:
|
||||
ret.append("-stylus");
|
||||
break;
|
||||
case TOUCHSCREEN_FINGER:
|
||||
ret.append("-finger");
|
||||
break;
|
||||
ResConfig.Touchscreen touchscreen=resConfig.getTouchscreen();
|
||||
if(touchscreen==null){
|
||||
return "";
|
||||
}
|
||||
return ret.toString();
|
||||
return "-"+touchscreen.toString();
|
||||
}
|
||||
private static void encodeUiMode(ResConfig resConfig, String[] split){
|
||||
int uiMode=0;
|
||||
@ -842,7 +805,7 @@ public class ResConfigHelper {
|
||||
private static String decodeOrientation(ResConfig resConfig){
|
||||
ResConfig.Orientation orientation=resConfig.getOrientation();
|
||||
if(orientation==null){
|
||||
return null;
|
||||
return "";
|
||||
}
|
||||
return "-"+orientation.toString();
|
||||
}
|
||||
@ -1384,29 +1347,17 @@ public class ResConfigHelper {
|
||||
public final static byte UI_MODE_TYPE_LARGEUI = 0x0e;
|
||||
public final static byte UI_MODE_TYPE_HUGEUI = 0x0f;
|
||||
|
||||
|
||||
public final static byte TOUCHSCREEN_ANY = 0;
|
||||
public final static byte TOUCHSCREEN_NOTOUCH = 1;
|
||||
public final static byte TOUCHSCREEN_STYLUS = 2;
|
||||
public final static byte TOUCHSCREEN_FINGER = 3;
|
||||
|
||||
public final static byte MASK_KEYSHIDDEN = 0x3;
|
||||
public final static byte KEYSHIDDEN_ANY = 0x0;
|
||||
public final static byte KEYSHIDDEN_NO = 0x1;
|
||||
public final static byte KEYSHIDDEN_YES = 0x2;
|
||||
public final static byte KEYSHIDDEN_SOFT = 0x3;
|
||||
|
||||
private final static byte KEYBOARD_ANY = 0;
|
||||
private final static byte KEYBOARD_NOKEYS = 1;
|
||||
private final static byte KEYBOARD_QWERTY = 2;
|
||||
private final static byte KEYBOARD_12KEY = 3;
|
||||
|
||||
public final static byte MASK_NAVHIDDEN = 0xc;
|
||||
public final static byte NAVHIDDEN_ANY = 0x0;
|
||||
public final static byte NAVHIDDEN_NO = 0x4;
|
||||
public final static byte NAVHIDDEN_YES = 0x8;
|
||||
|
||||
|
||||
private final static byte NAVIGATION_ANY = 0;
|
||||
private final static byte NAVIGATION_NONAV = 1;
|
||||
private final static byte NAVIGATION_DPAD = 2;
|
||||
|
Reference in New Issue
Block a user