diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 8f5a1d7..ce5d24d --- a/README.md +++ b/README.md @@ -7,22 +7,38 @@ public static void example() throws IOException { File inFile=new File("resources.arsc"); - BlockReader blockReader=new BlockReader(inFile); TableBlock tableBlock=new TableBlock(); - tableBlock.readBytes(blockReader); + tableBlock.readBytes(inFile); - // edit tableBlock as desired + //edit tableBlock as desired, for example to change the package: + PackageBlock packageBlock=tableBlock.getPackageArray().get(0); + packageBlock.setPackageName("com.new.package.name"); + //refresh to recalculate offsets tableBlock.refresh(); + + //save the edited table + File outFile=new File("resources_out.arsc"); + tableBlock.writeBytes(outFile); + } - File outFile=new File("resources_out.arsc"); - OutputStream outputStream=new FileOutputStream(outFile, false); + public static void exampleManifest() throws IOException { + File inFile=new File("AndroidManifest.xml"); + + AndroidManifestBlock manifestBlock=new AndroidManifestBlock(); + manifestBlock.readBytes(file); + + //edit AndroidManifest as desired, for example to change the package: - tableBlock.writeBytes(outputStream); - - outputStream.flush(); - outputStream.close(); + manifestBlock.setPackageName("com.new.package.name"); + + //refresh to recalculate offsets + manifestBlock.refresh(); + + //save the edited table + File outFile=new File("AndroidManifest_out.xml"); + manifestBlock.writeBytes(outFile); } ``` diff --git a/build.gradle b/build.gradle old mode 100644 new mode 100755 index 66f27ae..0e23ca1 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'java-library' group 'com.reandroid.lib.arsc' -version '1.0.0' +version '1.0.1' java { sourceCompatibility JavaVersion.VERSION_1_8 @@ -18,12 +18,12 @@ if (JavaVersion.current().isJava8Compatible()) { } repositories { mavenCentral() + mavenLocal() } -dependencies { - +processResources { + filesMatching('lib.properties') { + expand('version': version) + } } -processResources.inputs.property('version', version) -processResources.expand('version': version) - diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar old mode 100644 new mode 100755 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties old mode 100644 new mode 100755 diff --git a/gradlew.bat b/gradlew.bat old mode 100644 new mode 100755 index 0f8d593..6d57edc --- a/gradlew.bat +++ b/gradlew.bat @@ -1,84 +1,84 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/BuildInfo.java b/src/main/java/com/reandroid/lib/arsc/BuildInfo.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/array/EntryBlockArray.java b/src/main/java/com/reandroid/lib/arsc/array/EntryBlockArray.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/array/LibraryInfoArray.java b/src/main/java/com/reandroid/lib/arsc/array/LibraryInfoArray.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/array/OffsetBlockArray.java b/src/main/java/com/reandroid/lib/arsc/array/OffsetBlockArray.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/array/PackageArray.java b/src/main/java/com/reandroid/lib/arsc/array/PackageArray.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/array/ResValueBagItemArray.java b/src/main/java/com/reandroid/lib/arsc/array/ResValueBagItemArray.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/array/ResXmlAttributeArray.java b/src/main/java/com/reandroid/lib/arsc/array/ResXmlAttributeArray.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/array/ResXmlChunkArray.java b/src/main/java/com/reandroid/lib/arsc/array/ResXmlChunkArray.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/array/ResXmlIDArray.java b/src/main/java/com/reandroid/lib/arsc/array/ResXmlIDArray.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/array/ResXmlStringArray.java b/src/main/java/com/reandroid/lib/arsc/array/ResXmlStringArray.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/array/SpecBlockArray.java b/src/main/java/com/reandroid/lib/arsc/array/SpecBlockArray.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/array/SpecStringArray.java b/src/main/java/com/reandroid/lib/arsc/array/SpecStringArray.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/array/SpecTypePairArray.java b/src/main/java/com/reandroid/lib/arsc/array/SpecTypePairArray.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/array/StringArray.java b/src/main/java/com/reandroid/lib/arsc/array/StringArray.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/array/StyleArray.java b/src/main/java/com/reandroid/lib/arsc/array/StyleArray.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/array/TableStringArray.java b/src/main/java/com/reandroid/lib/arsc/array/TableStringArray.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/array/TypeBlockArray.java b/src/main/java/com/reandroid/lib/arsc/array/TypeBlockArray.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/array/TypeStringArray.java b/src/main/java/com/reandroid/lib/arsc/array/TypeStringArray.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/base/Block.java b/src/main/java/com/reandroid/lib/arsc/base/Block.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/base/BlockArray.java b/src/main/java/com/reandroid/lib/arsc/base/BlockArray.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/base/BlockArrayCreator.java b/src/main/java/com/reandroid/lib/arsc/base/BlockArrayCreator.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/base/BlockContainer.java b/src/main/java/com/reandroid/lib/arsc/base/BlockContainer.java old mode 100644 new mode 100755 index b65082f..08df044 --- a/src/main/java/com/reandroid/lib/arsc/base/BlockContainer.java +++ b/src/main/java/com/reandroid/lib/arsc/base/BlockContainer.java @@ -9,6 +9,18 @@ public abstract class BlockContainer extends Block{ public BlockContainer(){ super(); } + + protected final BlockContainer getTopContainer(){ + Block parent=this; + BlockContainer result=this; + while (parent!=null){ + if(parent instanceof BlockContainer){ + result=(BlockContainer)parent; + } + parent=parent.getParent(); + } + return result; + } protected abstract void onRefreshed(); public final void refresh(){ if(isNull()){ diff --git a/src/main/java/com/reandroid/lib/arsc/base/BlockCounter.java b/src/main/java/com/reandroid/lib/arsc/base/BlockCounter.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/base/BlockCreator.java b/src/main/java/com/reandroid/lib/arsc/base/BlockCreator.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/BaseChunk.java b/src/main/java/com/reandroid/lib/arsc/chunk/BaseChunk.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/BaseTypeBlock.java b/src/main/java/com/reandroid/lib/arsc/chunk/BaseTypeBlock.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/ChunkType.java b/src/main/java/com/reandroid/lib/arsc/chunk/ChunkType.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/LibraryBlock.java b/src/main/java/com/reandroid/lib/arsc/chunk/LibraryBlock.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/PackageBlock.java b/src/main/java/com/reandroid/lib/arsc/chunk/PackageBlock.java old mode 100644 new mode 100755 index edf0b0a..d39810f --- a/src/main/java/com/reandroid/lib/arsc/chunk/PackageBlock.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/PackageBlock.java @@ -4,7 +4,6 @@ import com.reandroid.lib.arsc.array.SpecTypePairArray; import com.reandroid.lib.arsc.base.Block; import com.reandroid.lib.arsc.container.PackageLastBlocks; import com.reandroid.lib.arsc.container.SpecTypePair; -import com.reandroid.lib.arsc.decoder.ResourceNameProvider; import com.reandroid.lib.arsc.group.EntryGroup; import com.reandroid.lib.arsc.item.IntegerItem; import com.reandroid.lib.arsc.item.PackageName; @@ -12,15 +11,13 @@ import com.reandroid.lib.arsc.item.ReferenceItem; import com.reandroid.lib.arsc.pool.SpecStringPool; import com.reandroid.lib.arsc.pool.TableStringPool; import com.reandroid.lib.arsc.pool.TypeStringPool; -import com.reandroid.lib.arsc.value.BaseResValue; import com.reandroid.lib.arsc.value.EntryBlock; import com.reandroid.lib.arsc.value.LibraryInfo; -import com.reandroid.lib.arsc.value.ResValueBag; import java.util.*; -public class PackageBlock extends BaseChunk implements ResourceNameProvider { +public class PackageBlock extends BaseChunk { private final IntegerItem mPackageId; private final PackageName mPackageName; @@ -270,67 +267,6 @@ public class PackageBlock extends BaseChunk implements ResourceNameProvider { refreshKeyStrings(); } - @Override - public String getResourceFullName(int resId, boolean includePackageName) { - EntryGroup entryGroup=getEntryGroup(resId); - if(entryGroup==null){ - return null; - } - String type=entryGroup.getTypeName(); - if(type==null){ - return null; - } - String spec=entryGroup.getSpecName(); - if(spec==null){ - return null; - } - StringBuilder builder=new StringBuilder(); - builder.append('@'); - if(includePackageName){ - builder.append(getPackageName()); - builder.append(':'); - } - builder.append(type); - builder.append('/'); - builder.append(spec); - return builder.toString(); - } - - @Override - public String getResourceName(int resId, boolean includePackageName) { - EntryGroup entryGroup=getEntryGroup(resId); - if(entryGroup==null){ - return null; - } - String spec=entryGroup.getSpecName(); - if(spec==null){ - return null; - } - StringBuilder builder=new StringBuilder(); - if(includePackageName){ - builder.append(getPackageName()); - builder.append(':'); - } - builder.append(spec); - return builder.toString(); - } - @Override - public ResValueBag getAttributeBag(int resId){ - EntryGroup entryGroup=getEntryGroup(resId); - if(entryGroup==null){ - return null; - } - EntryBlock entryBlock=entryGroup.pickOne(); - if(entryBlock==null){ - return null; - } - BaseResValue resValue=entryBlock.getResValue(); - if(resValue instanceof ResValueBag){ - return (ResValueBag)resValue; - } - return null; - } - @Override public String toString(){ StringBuilder builder=new StringBuilder(); diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/SpecBlock.java b/src/main/java/com/reandroid/lib/arsc/chunk/SpecBlock.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/TableBlock.java b/src/main/java/com/reandroid/lib/arsc/chunk/TableBlock.java old mode 100644 new mode 100755 index 586c479..0495055 --- a/src/main/java/com/reandroid/lib/arsc/chunk/TableBlock.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/TableBlock.java @@ -1,14 +1,16 @@ package com.reandroid.lib.arsc.chunk; import com.reandroid.lib.arsc.array.PackageArray; -import com.reandroid.lib.arsc.decoder.ResourceNameProvider; +import com.reandroid.lib.arsc.chunk.xml.ResXmlBlock; +import com.reandroid.lib.arsc.header.HeaderBlock; +import com.reandroid.lib.arsc.io.BlockReader; import com.reandroid.lib.arsc.item.IntegerItem; import com.reandroid.lib.arsc.pool.TableStringPool; -import com.reandroid.lib.arsc.value.ResValueBag; +import java.io.*; import java.util.Collection; -public class TableBlock extends BaseChunk implements ResourceNameProvider { +public class TableBlock extends BaseChunk { private final IntegerItem mPackageCount; private final TableStringPool mTableStringPool; private final PackageArray mPackageArray; @@ -42,43 +44,34 @@ public class TableBlock extends BaseChunk implements ResourceNameProvider { protected void onChunkRefreshed() { refreshPackageCount(); } - @Override - public String getResourceFullName(int resId, boolean includePackageName) { - byte pkgId= (byte) ((resId>>24)&0xFF); - if(pkgId==0){ - return null; - } - PackageBlock packageBlock=getPackageBlockById(pkgId); - if(packageBlock!=null){ - return packageBlock.getResourceFullName(resId, includePackageName); - } - return null; - } @Override - public String getResourceName(int resId, boolean includePackageName) { - byte pkgId= (byte) ((resId>>24)&0xFF); - if(pkgId==0){ - return null; - } - PackageBlock packageBlock=getPackageBlockById(pkgId); - if(packageBlock!=null){ - return packageBlock.getResourceName(resId, includePackageName); - } - return null; + public int onWriteBytes(OutputStream stream) throws IOException{ + int result=super.onWriteBytes(stream); + stream.flush(); + stream.close(); + return result; } - @Override - public ResValueBag getAttributeBag(int resId){ - byte pkgId= (byte) ((resId>>24)&0xFF); - if(pkgId==0){ - return null; - } - PackageBlock packageBlock=getPackageBlockById(pkgId); - if(packageBlock!=null){ - return packageBlock.getAttributeBag(resId); - } - return null; + public void readBytes(File file) throws IOException{ + BlockReader reader=new BlockReader(file); + super.readBytes(reader); } + public void readBytes(InputStream inputStream) throws IOException{ + BlockReader reader=new BlockReader(inputStream); + super.readBytes(reader); + } + public final int writeBytes(File file) throws IOException{ + if(isNull()){ + throw new IOException("Can NOT save null block"); + } + File dir=file.getParentFile(); + if(dir!=null && !dir.exists()){ + dir.mkdirs(); + } + OutputStream outputStream=new FileOutputStream(file); + return super.writeBytes(outputStream); + } + @Override public String toString(){ StringBuilder builder=new StringBuilder(); @@ -89,4 +82,43 @@ public class TableBlock extends BaseChunk implements ResourceNameProvider { return builder.toString(); } + + public static boolean isResTableBlock(File file){ + if(file==null){ + return false; + } + try { + InputStream inputStream=new FileInputStream(file); + return isResTableBlock(inputStream); + } catch (FileNotFoundException ignored) { + return false; + } + } + public static boolean isResTableBlock(InputStream inputStream){ + try { + HeaderBlock headerBlock= BlockReader.readHeaderBlock(inputStream); + return isResTableBlock(headerBlock); + } catch (IOException ignored) { + return false; + } + } + public static boolean isResTableBlock(BlockReader blockReader){ + if(blockReader==null){ + return false; + } + try { + HeaderBlock headerBlock = blockReader.readHeaderBlock(); + return isResTableBlock(headerBlock); + } catch (IOException ignored) { + return false; + } + } + public static boolean isResTableBlock(HeaderBlock headerBlock){ + if(headerBlock==null){ + return false; + } + ChunkType chunkType=headerBlock.getChunkType(); + return chunkType==ChunkType.TABLE; + } + } diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/TypeBlock.java b/src/main/java/com/reandroid/lib/arsc/chunk/TypeBlock.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/xml/AndroidManifestBlock.java b/src/main/java/com/reandroid/lib/arsc/chunk/xml/AndroidManifestBlock.java new file mode 100644 index 0000000..67b98c0 --- /dev/null +++ b/src/main/java/com/reandroid/lib/arsc/chunk/xml/AndroidManifestBlock.java @@ -0,0 +1,166 @@ +package com.reandroid.lib.arsc.chunk.xml; + +import com.reandroid.lib.arsc.item.ResXmlString; +import com.reandroid.lib.arsc.pool.ResXmlStringPool; +import com.reandroid.lib.arsc.value.ValueType; + +import java.util.ArrayList; +import java.util.List; + +public class AndroidManifestBlock extends ResXmlBlock{ + public AndroidManifestBlock(){ + super(); + } + public List getPermissions(){ + List results=new ArrayList<>(); + ResXmlElement manifestElement=getManifestElement(); + if(manifestElement==null){ + return results; + } + List permissionList = manifestElement.searchElementsByTagName(TAG_uses_permission); + for(ResXmlElement permission:permissionList){ + ResXmlAttribute nameAttr = permission.searchAttributeByName(ATTR_android_name); + if(nameAttr==null){ + continue; + } + String val=nameAttr.getValueString(); + if(val!=null){ + results.add(val); + } + } + return results; + } + public String getPackageName(){ + return getManifestAttributeString(ATTR_PACKAGE); + } + public boolean setPackageName(String packageName){ + return setManifestAttributeString(ATTR_PACKAGE, packageName); + } + public Integer getCompileSdkVersion(){ + return getManifestAttributeInt(ATTR_compileSdkVersion); + } + public boolean setCompileSdkVersion(int val){ + return setManifestAttributeInt(ATTR_compileSdkVersion, val); + } + public String getCompileSdkVersionCodename(){ + return getManifestAttributeString(ATTR_compileSdkVersionCodename); + } + public boolean setCompileSdkVersionCodename(String val){ + return setManifestAttributeString(ATTR_compileSdkVersionCodename, val); + } + public Integer getVersionCode(){ + return getManifestAttributeInt(ATTR_versionCode); + } + public boolean setVersionCode(int val){ + return setManifestAttributeInt(ATTR_versionCode, val); + } + public String getVersionName(){ + return getManifestAttributeString(ATTR_versionName); + } + public boolean setVersionName(String packageName){ + return setManifestAttributeString(ATTR_versionName, packageName); + } + private String getManifestAttributeString(String name){ + ResXmlElement manifestElement=getManifestElement(); + if(manifestElement==null){ + return null; + } + ResXmlAttribute attribute= manifestElement.searchAttributeByName(name); + if(attribute==null){ + return null; + } + int raw=attribute.getRawValue(); + ResXmlStringPool pool = getStringPool(); + ResXmlString resXmlString = pool.get(raw); + if(resXmlString==null){ + return null; + } + return resXmlString.getHtml(); + } + private boolean setManifestAttributeString(String name, String value){ + ResXmlElement manifestElement=getManifestElement(); + if(manifestElement==null){ + return false; + } + ResXmlAttribute attribute= manifestElement.searchAttributeByName(name); + if(attribute==null){ + return false; + } + attribute.setValueType(ValueType.STRING); + ResXmlString resXmlString=attribute.setValueString(value); + return resXmlString!=null; + } + private boolean setManifestAttributeInt(String name, int value){ + ResXmlElement manifestElement=getManifestElement(); + if(manifestElement==null){ + return false; + } + ResXmlAttribute attribute= manifestElement.searchAttributeByName(name); + if(attribute==null){ + return false; + } + attribute.setValueType(ValueType.INT_DEC); + attribute.setValueString(String.valueOf(value)); + attribute.setRawValue(value); + return true; + } + private Integer getManifestAttributeInt(String name){ + ResXmlElement manifestElement=getManifestElement(); + if(manifestElement==null){ + return null; + } + ResXmlAttribute attribute= manifestElement.searchAttributeByName(name); + if(attribute==null){ + return null; + } + return attribute.getRawValue(); + } + private ResXmlElement getManifestElement(){ + ResXmlElement manifestElement=getResXmlElement(); + if(manifestElement==null){ + return null; + } + if(!TAG_MANIFEST.equals(manifestElement.getTag())){ + return null; + } + return manifestElement; + } + @Override + public String toString(){ + StringBuilder builder=new StringBuilder(); + builder.append(getClass().getSimpleName()); + builder.append("{"); + builder.append(ATTR_PACKAGE).append("=").append(getPackageName()); + builder.append(", ").append(ATTR_versionCode).append("=").append(getVersionCode()); + builder.append(", ").append(ATTR_versionName).append("=").append(getVersionName()); + builder.append(", ").append(ATTR_compileSdkVersion).append("=").append(getCompileSdkVersion()); + builder.append(", ").append(ATTR_compileSdkVersionCodename).append("=").append(getCompileSdkVersionCodename()); + + List allPermissions=getPermissions(); + builder.append(", PERMISSIONS["); + boolean appendOnce=false; + for(String permissions:allPermissions){ + if(appendOnce){ + builder.append(", "); + } + builder.append(permissions); + appendOnce=true; + } + builder.append("]"); + builder.append("}"); + return builder.toString(); + } + private static final String TAG_MANIFEST="manifest"; + private static final String TAG_uses_permission="uses-permission"; + + private static final String ATTR_compileSdkVersion="compileSdkVersion"; + private static final String ATTR_compileSdkVersionCodename="compileSdkVersionCodename"; + private static final String ATTR_installLocation="installLocation"; + private static final String ATTR_PACKAGE="package"; + private static final String ATTR_platformBuildVersionCode="platformBuildVersionCode"; + private static final String ATTR_platformBuildVersionName="platformBuildVersionName"; + private static final String ATTR_versionCode="versionCode"; + private static final String ATTR_versionName="versionName"; + + private static final String ATTR_android_name="name"; +} diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/xml/BaseXmlChunk.java b/src/main/java/com/reandroid/lib/arsc/chunk/xml/BaseXmlChunk.java old mode 100644 new mode 100755 index 7015020..e9283a5 --- a/src/main/java/com/reandroid/lib/arsc/chunk/xml/BaseXmlChunk.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/xml/BaseXmlChunk.java @@ -57,6 +57,15 @@ public class BaseXmlChunk extends BaseChunk { public int getStringReference(){ return mStringReference.get(); } + public ResXmlString setString(String str){ + ResXmlStringPool pool = getStringPool(); + if(pool==null){ + return null; + } + ResXmlString xmlString = pool.getOrCreate(str); + setStringReference(xmlString.getIndex()); + return xmlString; + } public ResXmlStringPool getStringPool(){ @@ -82,6 +91,13 @@ public class BaseXmlChunk extends BaseChunk { } return null; } + ResXmlString getOrCreateResXmlString(String str){ + ResXmlStringPool stringPool=getStringPool(); + if(stringPool!=null){ + return stringPool.getOrCreate(str); + } + return null; + } String getString(int ref){ ResXmlString xmlString=getResXmlString(ref); if(xmlString!=null){ @@ -96,6 +112,7 @@ public class BaseXmlChunk extends BaseChunk { public String getUri(){ return getString(getNamespaceReference()); } + public ResXmlElement getParentResXmlElement(){ Block parent=getParent(); while (parent!=null){ diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlAttribute.java b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlAttribute.java old mode 100644 new mode 100755 index 3bb60fa..011fe02 --- a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlAttribute.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlAttribute.java @@ -17,10 +17,10 @@ public class ResXmlAttribute extends FixedBlockContainer { private final IntegerItem mRawValue; public ResXmlAttribute() { super(7); - mNamespaceReference =new IntegerItem(); + mNamespaceReference =new IntegerItem(-1); mNameReference =new IntegerItem(); mValueStringReference =new IntegerItem(); - mNameType=new ShortItem(); + mNameType=new ShortItem((short) 0x0008); mReserved =new ByteItem(); mValueTypeByte=new ByteItem(); mRawValue=new IntegerItem(); @@ -75,6 +75,11 @@ public class ResXmlAttribute extends FixedBlockContainer { public ValueType getValueType(){ return ValueType.valueOf(getValueTypeByte()); } + public void setValueType(ValueType valueType){ + if(valueType!=null){ + setValueTypeByte(valueType.getByte()); + } + } public String getFullName(){ String name=getName(); if(name==null){ @@ -100,12 +105,86 @@ public class ResXmlAttribute extends FixedBlockContainer { } return startNamespace.getPrefix(); } + public ResXmlStartNamespace getStartNamespace(){ + ResXmlElement xmlElement=getParentResXmlElement(); + if(xmlElement==null){ + return null; + } + return xmlElement.getStartNamespaceByUriRef(getNamespaceReference()); + } public String getValueString(){ return getString(getValueStringReference()); } + ResXmlString setValueString(String str){ + ResXmlString resXmlString=getOrCreateResXmlString(str); + if(resXmlString==null){ + return null; + } + int ref=resXmlString.getIndex(); + setValueStringReference(ref); + if(getValueType()==ValueType.STRING){ + setRawValue(ref); + } + return resXmlString; + } public int getNameResourceID(){ return getResourceId(getNameReference()); } + public boolean setName(String name, int resourceId){ + ResXmlStringPool stringPool=getStringPool(); + if(stringPool==null){ + return false; + } + String old=getName(); + if(resourceId==0){ + if(name.equals(old)){ + return false; + } + ResXmlString resXmlString=stringPool.getOrCreate(name); + setNameReference(resXmlString.getIndex()); + return true; + } + ResXmlIDMap xmlIDMap=getResXmlIDMap(); + if(xmlIDMap==null){ + return false; + } + int oldId=getNameResourceID(); + if(oldId==resourceId){ + if(name.equals(old)){ + return false; + } + } + ResXmlID resXmlID=xmlIDMap.getByResId(resourceId); + if(resXmlID!=null){ + int ref=resXmlID.getIndex(); + ResXmlString idName=stringPool.get(ref); + if(idName != null){ + if(name.equals(idName.getHtml())){ + setNameReference(ref); + }else { + idName.set(name); + } + return true; + } + } + int stringsCount=stringPool.countStrings(); + int idCount=xmlIDMap.getResXmlIDArray().childesCount(); + if(idCount>stringsCount){ + xmlIDMap.addResourceId(idCount, resourceId);; + stringPool.getStringsArray().ensureSize(idCount+1); + ResXmlString resXmlString=stringPool.get(idCount); + resXmlString.set(name); + setNameReference(idCount); + return true; + } + xmlIDMap.addResourceId(stringsCount, resourceId); + stringPool.getStringsArray().ensureSize(stringsCount+1); + ResXmlString resXmlString=stringPool.get(stringsCount); + resXmlString.set(name); + setNameReference(stringsCount); + return true; + + } private int getResourceId(int ref){ if(ref<0){ return 0; @@ -139,6 +218,13 @@ public class ResXmlAttribute extends FixedBlockContainer { } return null; } + private ResXmlString getOrCreateResXmlString(String str){ + ResXmlStringPool stringPool=getStringPool(); + if(stringPool!=null){ + return stringPool.getOrCreate(str); + } + return null; + } private ResXmlStringPool getStringPool(){ ResXmlElement xmlElement=getParentResXmlElement(); if(xmlElement!=null){ @@ -153,7 +239,7 @@ public class ResXmlAttribute extends FixedBlockContainer { } return null; } - private ResXmlElement getParentResXmlElement(){ + public ResXmlElement getParentResXmlElement(){ Block parent=getParent(); while (parent!=null){ if(parent instanceof ResXmlElement){ diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlBlock.java b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlBlock.java old mode 100644 new mode 100755 index 51a9bd3..1e0ff56 --- a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlBlock.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlBlock.java @@ -2,20 +2,94 @@ package com.reandroid.lib.arsc.chunk.xml; import com.reandroid.lib.arsc.chunk.ChunkType; import com.reandroid.lib.arsc.chunk.BaseChunk; +import com.reandroid.lib.arsc.container.SingleBlockContainer; +import com.reandroid.lib.arsc.header.HeaderBlock; +import com.reandroid.lib.arsc.io.BlockReader; import com.reandroid.lib.arsc.pool.ResXmlStringPool; +import java.io.*; + public class ResXmlBlock extends BaseChunk { private final ResXmlStringPool mResXmlStringPool; private final ResXmlIDMap mResXmlIDMap; - private final ResXmlElement mResXmlElement; + private ResXmlElement mResXmlElement; + private final SingleBlockContainer mResXmlElementContainer; public ResXmlBlock() { super(ChunkType.XML,3); this.mResXmlStringPool=new ResXmlStringPool(true); this.mResXmlIDMap=new ResXmlIDMap(); this.mResXmlElement=new ResXmlElement(); + this.mResXmlElementContainer=new SingleBlockContainer<>(); + this.mResXmlElementContainer.setItem(mResXmlElement); addChild(mResXmlStringPool); addChild(mResXmlIDMap); - addChild(mResXmlElement); + addChild(mResXmlElementContainer); + } + @Override + public void onReadBytes(BlockReader reader) throws IOException { + HeaderBlock headerBlock=reader.readHeaderBlock(); + if(headerBlock==null){ + return; + } + BlockReader chunkReader=reader.create(reader.getPosition(), headerBlock.getChunkSize()); + ChunkType chunkType=headerBlock.getChunkType(); + if(chunkType==ChunkType.XML){ + getHeaderBlock().readBytes(chunkReader); + }else { + throw new IOException("Not ResXmlBlock: "+reader+", Header="+headerBlock); + } + while (chunkReader.isAvailable()){ + boolean readOk=readNext(chunkReader); + if(!readOk){ + break; + } + } + reader.offset(headerBlock.getChunkSize()); + chunkReader.close(); + onChunkLoaded(); + } + private boolean readNext(BlockReader reader) throws IOException { + if(!reader.isAvailable()){ + return false; + } + int position=reader.getPosition(); + HeaderBlock headerBlock=reader.readHeaderBlock(); + if(headerBlock==null){ + return false; + } + ChunkType chunkType=headerBlock.getChunkType(); + if(chunkType==ChunkType.STRING){ + mResXmlStringPool.readBytes(reader); + }else if(chunkType==ChunkType.XML_RESOURCE_MAP){ + mResXmlIDMap.readBytes(reader); + }else if(isElementChunk(chunkType)){ + mResXmlElementContainer.readBytes(reader); + return reader.isAvailable(); + }else { + throw new IOException("Unexpected chunk "+headerBlock); + } + return reader.isAvailable() && position!=reader.getPosition(); + } + private boolean isElementChunk(ChunkType chunkType){ + if(chunkType==ChunkType.XML_START_ELEMENT){ + return true; + } + if(chunkType==ChunkType.XML_END_ELEMENT){ + return true; + } + if(chunkType==ChunkType.XML_START_NAMESPACE){ + return true; + } + if(chunkType==ChunkType.XML_END_NAMESPACE){ + return true; + } + if(chunkType==ChunkType.XML_CDATA){ + return true; + } + if(chunkType==ChunkType.XML_LAST_CHUNK){ + return true; + } + return false; } public ResXmlStringPool getStringPool(){ return mResXmlStringPool; @@ -26,8 +100,76 @@ public class ResXmlBlock extends BaseChunk { public ResXmlElement getResXmlElement(){ return mResXmlElement; } + public void setResXmlElement(ResXmlElement resXmlElement){ + this.mResXmlElement=resXmlElement; + this.mResXmlElementContainer.setItem(resXmlElement); + } @Override protected void onChunkRefreshed() { } + @Override + public int onWriteBytes(OutputStream stream) throws IOException{ + int result=super.onWriteBytes(stream); + stream.flush(); + stream.close(); + return result; + } + public void readBytes(File file) throws IOException{ + BlockReader reader=new BlockReader(file); + super.readBytes(reader); + } + public void readBytes(InputStream inputStream) throws IOException{ + BlockReader reader=new BlockReader(inputStream); + super.readBytes(reader); + } + public final int writeBytes(File file) throws IOException{ + if(isNull()){ + throw new IOException("Can NOT save null block"); + } + File dir=file.getParentFile(); + if(dir!=null && !dir.exists()){ + dir.mkdirs(); + } + OutputStream outputStream=new FileOutputStream(file); + return super.writeBytes(outputStream); + } + + public static boolean isResXmlBlock(File file){ + if(file==null){ + return false; + } + try { + InputStream inputStream=new FileInputStream(file); + return isResXmlBlock(inputStream); + } catch (FileNotFoundException ignored) { + return false; + } + } + public static boolean isResXmlBlock(InputStream inputStream){ + try { + HeaderBlock headerBlock=BlockReader.readHeaderBlock(inputStream); + return isResXmlBlock(headerBlock); + } catch (IOException ignored) { + return false; + } + } + public static boolean isResXmlBlock(BlockReader blockReader){ + if(blockReader==null){ + return false; + } + try { + HeaderBlock headerBlock = blockReader.readHeaderBlock(); + return isResXmlBlock(headerBlock); + } catch (IOException ignored) { + return false; + } + } + public static boolean isResXmlBlock(HeaderBlock headerBlock){ + if(headerBlock==null){ + return false; + } + ChunkType chunkType=headerBlock.getChunkType(); + return chunkType==ChunkType.XML; + } } diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlElement.java b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlElement.java old mode 100644 new mode 100755 index 99747a8..fdef7e5 --- a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlElement.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlElement.java @@ -7,6 +7,7 @@ import com.reandroid.lib.arsc.container.FixedBlockContainer; import com.reandroid.lib.arsc.container.SingleBlockContainer; import com.reandroid.lib.arsc.header.HeaderBlock; import com.reandroid.lib.arsc.io.BlockReader; +import com.reandroid.lib.arsc.item.ResXmlString; import com.reandroid.lib.arsc.pool.ResXmlStringPool; @@ -38,6 +39,42 @@ public class ResXmlElement extends FixedBlockContainer { addChild(4, mEndElementContainer); addChild(5, mEndNamespaceList); } + public List searchElementsByTagName(String name){ + List results=new ArrayList<>(); + if(name==null){ + return results; + } + for(ResXmlElement child:listElements()){ + if(name.equals(child.getTag())||name.equals(child.getTagName())){ + results.add(child); + } + } + return results; + } + public ResXmlAttribute searchAttributeByName(String name){ + ResXmlStartElement startElement=getStartElement(); + if(startElement!=null){ + return startElement.searchAttributeByName(name); + } + return null; + } + public void setTag(String tag){ + ResXmlStringPool pool = getStringPool(); + if(pool==null){ + return; + } + ensureStartEndElement(); + ResXmlStartElement start=getStartElement(); + String prefix=null; + String name=tag; + int i=tag.lastIndexOf(':'); + if(i>=0){ + prefix=tag.substring(0,i); + i++; + name=tag.substring(i); + } + start.setName(name); + } public String getTagName(){ ResXmlStartElement startElement=getStartElement(); if(startElement!=null){ @@ -45,6 +82,27 @@ public class ResXmlElement extends FixedBlockContainer { } return null; } + public String getTag(){ + ResXmlStartElement startElement=getStartElement(); + if(startElement!=null){ + return startElement.getName(); + } + return null; + } + public String getTagUri(){ + ResXmlStartElement startElement=getStartElement(); + if(startElement!=null){ + return startElement.getUri(); + } + return null; + } + public String getTagPrefix(){ + ResXmlStartElement startElement=getStartElement(); + if(startElement!=null){ + return startElement.getPrefix(); + } + return null; + } public Collection listResXmlAttributes(){ ResXmlStartElement startElement=getStartElement(); if(startElement!=null){ @@ -182,6 +240,22 @@ public class ResXmlElement extends FixedBlockContainer { start.setResXmlEndElement(end); end.setResXmlStartElement(start); } + private void ensureStartEndElement(){ + ResXmlStartElement start=getStartElement(); + ResXmlEndElement end=getEndElement(); + if(start!=null && end!=null){ + return; + } + if(start==null){ + start=new ResXmlStartElement(); + setStartElement(start); + } + if(end==null){ + end=new ResXmlEndElement(); + setEndElement(end); + } + linkStartEndElement(); + } private void linkStartEndNameSpaces(){ if(!isNamespaceBalanced()){ return; @@ -227,8 +301,8 @@ public class ResXmlElement extends FixedBlockContainer { unBalancedFinish(reader); }else { readBytes(reader); + return; } - return; } linkStartEnd(); onFinishedRead(reader, headerBlock); @@ -245,7 +319,14 @@ public class ResXmlElement extends FixedBlockContainer { } private void onFinishedUnexpected(BlockReader reader) throws IOException{ - throw new IOException("Unexpected finish reading: "+reader.toString()); + StringBuilder builder=new StringBuilder(); + builder.append("Unexpected finish reading: reader=").append(reader.toString()); + HeaderBlock header = reader.readHeaderBlock(); + if(header!=null){ + builder.append(", next header="); + builder.append(header.toString()); + } + throw new IOException(builder.toString()); } private void onStartElement(BlockReader reader) throws IOException{ if(hasStartElement()){ @@ -298,9 +379,25 @@ public class ResXmlElement extends FixedBlockContainer { throw new IOException("Unbalanced namespace: start=" +mStartNamespaceList.size()+", end="+mEndNamespaceList.size()); } + if(!isElementBalanced()){ - throw new IOException("Unbalanced element: hasStart=" - +hasStartElement()+", hasEnd="+hasEndElement()); + // Should not happen unless corrupted file, auto corrected above + StringBuilder builder=new StringBuilder(); + builder.append("Unbalanced element: start="); + ResXmlStartElement startElement=getStartElement(); + if(startElement!=null){ + builder.append(startElement); + }else { + builder.append("null"); + } + builder.append(", end="); + ResXmlEndElement endElement=getEndElement(); + if(endElement!=null){ + builder.append(endElement); + }else { + builder.append("null"); + } + throw new IOException(builder.toString()); } } @Override @@ -324,4 +421,13 @@ public class ResXmlElement extends FixedBlockContainer { } return "NULL"; } + static ResXmlElement newResXmlElement(String tag){ + ResXmlElement resXmlElement=new ResXmlElement(); + ResXmlStartElement startElement=new ResXmlStartElement(); + resXmlElement.setStartElement(startElement); + ResXmlEndElement endElement=new ResXmlEndElement(); + resXmlElement.setEndElement(endElement); + resXmlElement.setTag(tag); + return resXmlElement; + } } diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlEndElement.java b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlEndElement.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlEndNamespace.java b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlEndNamespace.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlIDMap.java b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlIDMap.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlStartElement.java b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlStartElement.java old mode 100644 new mode 100755 index 3b9bd74..db34249 --- a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlStartElement.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlStartElement.java @@ -3,6 +3,7 @@ package com.reandroid.lib.arsc.chunk.xml; import com.reandroid.lib.arsc.chunk.ChunkType; import com.reandroid.lib.arsc.array.ResXmlAttributeArray; import com.reandroid.lib.arsc.item.IntegerItem; +import com.reandroid.lib.arsc.item.ResXmlString; import com.reandroid.lib.arsc.item.ShortItem; import java.util.Collection; @@ -31,6 +32,17 @@ public class ResXmlStartElement extends BaseXmlChunk { addChild(mClassAttribute); addChild(mAttributeArray); } + public ResXmlAttribute searchAttributeByName(String name){ + if(name==null){ + return null; + } + for(ResXmlAttribute attribute:listResXmlAttributes()){ + if(name.equals(attribute.getFullName()) || name.equals(attribute.getName())){ + return attribute; + } + } + return null; + } public String getTagName(){ String prefix=getPrefix(); String name=getName(); @@ -39,6 +51,9 @@ public class ResXmlStartElement extends BaseXmlChunk { } return prefix+":"+name; } + public void setName(String name){ + setString(name); + } public Collection listResXmlAttributes(){ return getResXmlAttributeArray().listItems(); } diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlStartNamespace.java b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlStartNamespace.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlText.java b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlText.java old mode 100644 new mode 100755 index eda1c6e..f9ca582 --- a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlText.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlText.java @@ -3,6 +3,7 @@ package com.reandroid.lib.arsc.chunk.xml; import com.reandroid.lib.arsc.chunk.ChunkType; import com.reandroid.lib.arsc.item.IntegerItem; import com.reandroid.lib.arsc.item.ResXmlString; +import com.reandroid.lib.arsc.pool.ResXmlStringPool; public class ResXmlText extends BaseXmlChunk { private final IntegerItem mReserved; @@ -24,6 +25,15 @@ public class ResXmlText extends BaseXmlChunk { public void setTextReference(int ref){ setNamespaceReference(ref); } + public void setText(String text){ + ResXmlStringPool stringPool=getStringPool(); + if(stringPool==null){ + return; + } + ResXmlString resXmlString = stringPool.getOrCreate(text); + int ref=resXmlString.getIndex(); + setTextReference(ref); + } @Override public String toString(){ String txt=getText(); diff --git a/src/main/java/com/reandroid/lib/arsc/container/BlockList.java b/src/main/java/com/reandroid/lib/arsc/container/BlockList.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/container/ExpandableBlockContainer.java b/src/main/java/com/reandroid/lib/arsc/container/ExpandableBlockContainer.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/container/FixedBlockContainer.java b/src/main/java/com/reandroid/lib/arsc/container/FixedBlockContainer.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/container/PackageLastBlocks.java b/src/main/java/com/reandroid/lib/arsc/container/PackageLastBlocks.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/container/ResValueContainer.java b/src/main/java/com/reandroid/lib/arsc/container/ResValueContainer.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/container/SingleBlockContainer.java b/src/main/java/com/reandroid/lib/arsc/container/SingleBlockContainer.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/container/SpecTypePair.java b/src/main/java/com/reandroid/lib/arsc/container/SpecTypePair.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/decoder/ResDecoder.java b/src/main/java/com/reandroid/lib/arsc/decoder/ResDecoder.java deleted file mode 100644 index 18e1cd9..0000000 --- a/src/main/java/com/reandroid/lib/arsc/decoder/ResDecoder.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.reandroid.lib.arsc.decoder; - -import com.reandroid.lib.arsc.base.Block; -import com.reandroid.lib.arsc.value.ResValueBag; -import com.reandroid.lib.arsc.value.ResValueBagItem; -import com.reandroid.lib.arsc.value.ValueType; - -public abstract class ResDecoder implements ResourceNameProvider{ - private final ResourceNameStore resourceNameStore; - public ResDecoder(ResourceNameStore store){ - this.resourceNameStore=store; - } - public String decodeAttribute(int attrResId, int rawValue){ - ResValueBag valueBag=getAttributeBag(attrResId); - if(valueBag==null){ - return null; - } - ResValueBagItem[] bagItems=valueBag.getBagItems(); - if(bagItems==null||bagItems.length==0){ - return null; - } - int len=bagItems.length; - ResValueBagItem firstAttrChild=bagItems[0]; - if(len==1){ - return null; - } - if(isFlagAttr(firstAttrChild)){ - for(int i=1;i resourceNameProviders; - public ResourceNameStore(){ - this.resourceNameProviders=new HashSet<>(); - } - public void addResourceNameProvider(ResourceNameProvider provider){ - if(provider!=null){ - resourceNameProviders.add(provider); - } - } - public Set getResourceNameProviders(){ - return resourceNameProviders; - } - @Override - public String getResourceFullName(int resId, boolean includePackageName) { - for(ResourceNameProvider provider:this.resourceNameProviders){ - String name=provider.getResourceFullName(resId, includePackageName); - if(name!=null){ - return name; - } - } - return null; - } - - @Override - public String getResourceName(int resId, boolean includePackageName) { - for(ResourceNameProvider provider:this.resourceNameProviders){ - String name=provider.getResourceName(resId, includePackageName); - if(name!=null){ - return name; - } - } - return null; - } - @Override - public ResValueBag getAttributeBag(int resId) { - for(ResourceNameProvider provider:this.resourceNameProviders){ - ResValueBag resValueBag=provider.getAttributeBag(resId); - if(resValueBag!=null){ - return resValueBag; - } - } - return null; - } -} diff --git a/src/main/java/com/reandroid/lib/arsc/decoder/ValueDecoder.java b/src/main/java/com/reandroid/lib/arsc/decoder/ValueDecoder.java old mode 100644 new mode 100755 index 362b388..423dade --- a/src/main/java/com/reandroid/lib/arsc/decoder/ValueDecoder.java +++ b/src/main/java/com/reandroid/lib/arsc/decoder/ValueDecoder.java @@ -1,8 +1,181 @@ package com.reandroid.lib.arsc.decoder; -import com.reandroid.lib.arsc.value.ValueType; +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.item.TableString; +import com.reandroid.lib.arsc.pool.TableStringPool; +import com.reandroid.lib.arsc.value.*; +import com.reandroid.lib.arsc.value.attribute.AttributeBag; +import com.reandroid.lib.common.EntryStore; + +import java.util.Collection; +import java.util.Iterator; public class ValueDecoder { + + public static String decodeAttributeName(EntryStore store, PackageBlock currentPackage, int resourceId){ + EntryGroup entryGroup=searchEntryGroup(store, currentPackage, resourceId); + if(entryGroup==null){ + return String.format("@0x%08x", resourceId); + } + EntryBlock entryBlock=entryGroup.pickOne(); + if(entryBlock==null){ + return String.format("@0x%08x", resourceId); + } + String prefix=null; + if(currentPackage!=null){ + String name=currentPackage.getPackageName(); + String other=entryBlock.getPackageBlock().getPackageName(); + if(!name.equals(other)){ + prefix=other+":"; + } + } + String name=entryGroup.getSpecName(); + if(prefix!=null){ + name=prefix+name; + } + return name; + } + public static String decodeAttribute(EntryStore store, int attrResId, int rawValue){ + AttributeBag attributeBag = getAttributeBag(store, attrResId); + if(attributeBag==null){ + return null; + } + return attributeBag.decodeAttributeValue(store, rawValue); + } + + + public static String decodeEntryValue(EntryStore store, PackageBlock currentPackage, ValueType valueType, int data){ + if(store==null || currentPackage==null){ + return null; + } + if(valueType==ValueType.STRING){ + return decodeIntEntryString(currentPackage, data); + } + boolean is_reference=false; + boolean is_attribute=false; + if(valueType==ValueType.REFERENCE){ + if(data==0){ + return "@null"; + } + is_reference=true; + } + if(valueType==ValueType.ATTRIBUTE){ + if(data==0){ + return "?null"; + } + is_attribute=true; + } + if(is_reference || is_attribute){ + String ref=buildReferenceValue(store, valueType, currentPackage.getPackageName(), data); + if(ref!=null){ + return ref; + } + char atOrQues=is_reference?'@':'?'; + ref=atOrQues+toHexResourceId(data); + return ref; + } + return decode(valueType, data); + } + public static String decodeIntEntry(EntryStore store, EntryBlock entryBlock){ + if(entryBlock==null){ + return null; + } + BaseResValue baseResValue = entryBlock.getResValue(); + if(!(baseResValue instanceof ResValueInt)){ + return null; + } + ResValueInt resValueInt=(ResValueInt)baseResValue; + return decodeIntEntry(store, resValueInt); + } + public static String decodeIntEntry(EntryStore store, ResValueInt resValueInt){ + if(resValueInt==null){ + return null; + } + EntryBlock parentEntry=resValueInt.getEntryBlock(); + if(parentEntry==null){ + return null; + } + ValueType valueType=resValueInt.getValueType(); + int data=resValueInt.getData(); + return decodeIntEntry(store, parentEntry, valueType, data); + } + public static String decodeIntEntry(EntryStore store, ResValueBagItem bagItem){ + if(bagItem==null){ + return null; + } + EntryBlock parentEntry=bagItem.getEntryBlock(); + if(parentEntry==null){ + return null; + } + ValueType valueType=bagItem.getValueType(); + int data=bagItem.getData(); + return decodeIntEntry(store, parentEntry, valueType, data); + } + public static String decodeIntEntry(EntryStore store, EntryBlock parentEntry, ValueType valueType, int data){ + if(valueType==ValueType.NULL){ + return "@empty"; + } + if(valueType==ValueType.STRING){ + return decodeIntEntryString(parentEntry, data); + } + boolean is_reference=false; + boolean is_attribute=false; + if(valueType==ValueType.REFERENCE){ + if(data==0){ + return "@null"; + } + is_reference=true; + } + if(valueType==ValueType.ATTRIBUTE){ + if(data==0){ + return "?null"; + } + is_attribute=true; + } + if(is_reference || is_attribute){ + String ref=buildReferenceValue(store, parentEntry, valueType, data); + if(ref!=null){ + return ref; + } + char atOrQues=is_reference?'@':'?'; + ref=atOrQues+toHexResourceId(data); + return ref; + } + return decode(valueType, data); + } + public static String buildReferenceValue(EntryStore store, EntryBlock entryBlock){ + if(entryBlock==null){ + return null; + } + BaseResValue baseResValue = entryBlock.getResValue(); + if(!(baseResValue instanceof ResValueInt)){ + return null; + } + ResValueInt resValueInt=(ResValueInt)baseResValue; + int resourceId=resValueInt.getData(); + ValueType valueType=resValueInt.getValueType(); + return buildReferenceValue(store, entryBlock, valueType, resourceId); + } + public static String decode(EntryStore entryStore, int currentPackageId, int nameResourceId, ValueType valueType, int rawVal){ + String currPackageName=getPackageName(entryStore, currentPackageId); + String result=buildReferenceValue(entryStore, valueType, currPackageName, rawVal); + if(result!=null){ + return result; + } + if(valueType==ValueType.STRING){ + // Should not happen the string could be in ResXmlBlock, but if you are lazy here it goes + return decodeString(entryStore, currentPackageId, rawVal); + } + if(valueType==ValueType.FIRST_INT||valueType==ValueType.INT_HEX){ + result=decodeAttribute(entryStore, nameResourceId, rawVal); + if(result!=null){ + return result; + } + } + return decode(valueType, rawVal); + } public static String decode(ValueType valueType, int data){ if(valueType==null){ return null; @@ -30,6 +203,229 @@ public class ValueDecoder { } return null; } + public static String buildReference(String currentPackageName, + String referredPackageName, + char atOrQues, + String typeName, + String resourceName){ + StringBuilder builder=new StringBuilder(); + if(atOrQues!=0){ + builder.append(atOrQues); + } + if(!isEqualString(currentPackageName, referredPackageName)){ + if(!isEmpty(currentPackageName) && !isEmpty(referredPackageName)){ + builder.append(referredPackageName); + if(!referredPackageName.endsWith(":")){ + builder.append(':'); + } + } + } + if(!isEmpty(typeName)){ + builder.append(typeName); + builder.append('/'); + } + builder.append(resourceName); + return builder.toString(); + } + + private static String buildReferenceValue(EntryStore store, EntryBlock entryBlock, ValueType valueType, int resourceId){ + if(entryBlock==null){ + return null; + } + EntryGroup value=searchEntryGroup(store, entryBlock, resourceId); + if(value==null){ + return null; + } + return buildReferenceValue(valueType, entryBlock, value); + } + private static String buildReferenceValue(ValueType valueType, EntryBlock entryBlock, EntryGroup value){ + char atOrQues; + if(valueType==ValueType.REFERENCE){ + atOrQues='@'; + }else if(valueType==ValueType.ATTRIBUTE){ + atOrQues='?'; + }else { + atOrQues=0; + } + String currentPackageName=getPackageName(entryBlock); + String referredPackageName=getPackageName(value); + String typeName=value.getTypeName(); + String name=value.getSpecName(); + return buildReference(currentPackageName, referredPackageName, atOrQues, typeName, name); + } + private static String buildReferenceValue(EntryStore entryStore, ValueType valueType, String currentPackageName, int resourceId){ + char atOrQues; + if(valueType==ValueType.REFERENCE){ + atOrQues='@'; + }else if(valueType==ValueType.ATTRIBUTE){ + atOrQues='?'; + }else { + return null; + } + EntryGroup value=null; + if(entryStore!=null){ + value=entryStore.getEntryGroup(resourceId); + } + if(value==null){ + return atOrQues+toHexResourceId(resourceId); + } + String referredPackageName=getPackageName(value); + String typeName=value.getTypeName(); + String name=value.getSpecName(); + return buildReference(currentPackageName, referredPackageName, atOrQues, typeName, name); + } + private static String getPackageName(EntryStore entryStore, int packageOrResourceId){ + if(entryStore==null || packageOrResourceId==0){ + return null; + } + int pkgId=(packageOrResourceId>>24)&0xFF; + if(pkgId==0){ + pkgId=packageOrResourceId; + } + Collection allPkg = entryStore.getPackageBlocks((byte) pkgId); + if(allPkg==null){ + return null; + } + for(PackageBlock packageBlock:allPkg){ + String name=packageBlock.getPackageName(); + if(name!=null){ + return name; + } + } + return null; + } + private static String getPackageName(EntryGroup entryGroup){ + if(entryGroup==null){ + return null; + } + return getPackageName(entryGroup.pickOne()); + } + private static String getPackageName(EntryBlock entryBlock){ + if(entryBlock==null){ + return null; + } + PackageBlock packageBlock=entryBlock.getPackageBlock(); + if(packageBlock==null){ + return null; + } + return packageBlock.getPackageName(); + } + private static EntryGroup searchEntryGroup(EntryStore store, EntryBlock entryBlock, int resourceId){ + EntryGroup entryGroup=searchEntryGroup(entryBlock, resourceId); + if(entryGroup!=null){ + return entryGroup; + } + if(store==null){ + return null; + } + return store.getEntryGroup(resourceId); + } + private static EntryGroup searchEntryGroup(EntryBlock entryBlock, int resourceId){ + if(entryBlock==null){ + return null; + } + PackageBlock packageBlock=entryBlock.getPackageBlock(); + if(packageBlock==null){ + return null; + } + TableBlock tableBlock=packageBlock.getTableBlock(); + if(tableBlock==null){ + return null; + } + for(PackageBlock pkg:tableBlock.listPackages()){ + EntryGroup entryGroup=pkg.getEntryGroup(resourceId); + if(entryGroup!=null){ + return entryGroup; + } + } + return null; + } + private static EntryGroup searchEntryGroup(EntryStore store, PackageBlock packageBlock, int resourceId){ + if(packageBlock!=null){ + TableBlock tableBlock=packageBlock.getTableBlock(); + if(tableBlock!=null){ + for(PackageBlock pkg:tableBlock.listPackages()){ + EntryGroup entryGroup=pkg.getEntryGroup(resourceId); + if(entryGroup!=null){ + return entryGroup; + } + } + } + } + if(store!=null){ + return store.getEntryGroup(resourceId); + } + return null; + } + private static String decodeIntEntryString(EntryBlock entryBlock, int data){ + if(entryBlock==null){ + return null; + } + PackageBlock packageBlock=entryBlock.getPackageBlock(); + if(packageBlock==null){ + return null; + } + TableBlock tableBlock=packageBlock.getTableBlock(); + if(tableBlock==null){ + return null; + } + TableStringPool pool = tableBlock.getTableStringPool(); + TableString tableString=pool.get(data); + if(tableString==null){ + return null; + } + return tableString.getHtml(); + } + private static String decodeString(EntryStore entryStore, int packageOrResourceId, int stringRef){ + if(entryStore==null||packageOrResourceId==0){ + return null; + } + int pkgId=(packageOrResourceId>>24)&0xFF; + if(pkgId==0){ + pkgId=packageOrResourceId; + } + Collection allPkg = entryStore.getPackageBlocks((byte) pkgId); + if(allPkg==null){ + return null; + } + TableString tableString=null; + for(PackageBlock packageBlock:allPkg){ + TableBlock tableBlock=packageBlock.getTableBlock(); + if(tableBlock==null){ + continue; + } + TableString ts=tableBlock.getTableStringPool().get(stringRef); + if(ts==null){ + continue; + } + if(tableString==null){ + tableString=ts; + }else { + // Duplicate result, could be from split apks + return null; + } + } + if(tableString!=null){ + return tableString.getHtml(); + } + return null; + } + private static String decodeIntEntryString(PackageBlock packageBlock, int data){ + if(packageBlock==null){ + return null; + } + TableBlock tableBlock=packageBlock.getTableBlock(); + if(tableBlock==null){ + return null; + } + TableStringPool pool = tableBlock.getTableStringPool(); + TableString tableString=pool.get(data); + if(tableString==null){ + return null; + } + return tableString.getHtml(); + } + private static String decodeHex(int rawVal){ return String.format("0x%x", rawVal); } @@ -99,6 +495,104 @@ public class ValueDecoder { } return 0; } + private static String getResourceName(EntryStore store, EntryBlock entryBlock, int resourceId){ + if(entryBlock!=null){ + EntryGroup group=searchEntryGroup(entryBlock, resourceId); + if(group!=null){ + String name=group.getSpecName(); + if(name!=null){ + return name; + } + } + } + if(store==null){ + return null; + } + Collection foundGroups = store.getEntryGroups(resourceId); + return pickResourceName(foundGroups); + } + private static String pickResourceName(Collection groups){ + if(groups==null){ + return null; + } + for(EntryGroup entryGroup:groups){ + String name=entryGroup.getSpecName(); + if(name!=null){ + return name; + } + } + return null; + } + private static AttributeBag getAttributeBag(EntryStore store, int resourceId){ + ResValueBag resValueBag=getAttributeValueBag(store, resourceId); + if(resValueBag==null){ + return null; + } + return AttributeBag.create(resValueBag); + } + private static ResValueBag getAttributeValueBag(EntryStore store, int resourceId){ + if(store==null){ + return null; + } + Collection foundGroups = store.getEntryGroups(resourceId); + ResValueBag best=null; + for(EntryGroup group:foundGroups){ + ResValueBag valueBag= getAttributeValueBag(group); + best=chooseBest(best, valueBag); + } + return best; + } + private static ResValueBag getAttributeValueBag(EntryGroup entryGroup){ + if(entryGroup==null){ + return null; + } + ResValueBag best=null; + Iterator iterator=entryGroup.iterator(true); + while (iterator.hasNext()){ + EntryBlock entryBlock=iterator.next(); + ResValueBag valueBag= getAttributeValueBag(entryBlock); + best=chooseBest(best, valueBag); + } + return best; + } + private static ResValueBag getAttributeValueBag(EntryBlock entryBlock){ + if(entryBlock==null){ + return null; + } + BaseResValue baseResValue = entryBlock.getResValue(); + if(baseResValue instanceof ResValueBag){ + return (ResValueBag) baseResValue; + } + return null; + } + private static ResValueBag chooseBest(ResValueBag valueBag1, ResValueBag valueBag2){ + if(valueBag1==null){ + return valueBag2; + } + if(valueBag2==null){ + return valueBag1; + } + if(valueBag2.getCount()>valueBag1.getCount()){ + return valueBag2; + } + return valueBag1; + } + private static String toHexResourceId(int resourceId){ + return String.format("0x%08x", resourceId); + } + private static boolean isEqualString(String str1, String str2){ + if(isEmpty(str1)){ + return isEmpty(str2); + } + return str1.equals(str2); + } + private static boolean isEmpty(String str){ + if(str==null){ + return true; + } + str=str.trim(); + return str.length()==0; + } private static final String[] DIMENSION_UNIT_STRS = new String[] { "px", "dip", "sp", "pt", "in", "mm" }; private static final float MANTISSA_MULT = 1.0f / (1 << 8); diff --git a/src/main/java/com/reandroid/lib/arsc/decoder/xml/ResXmlDecoder.java b/src/main/java/com/reandroid/lib/arsc/decoder/xml/ResXmlDecoder.java deleted file mode 100644 index c44519a..0000000 --- a/src/main/java/com/reandroid/lib/arsc/decoder/xml/ResXmlDecoder.java +++ /dev/null @@ -1,139 +0,0 @@ -package com.reandroid.lib.arsc.decoder.xml; - -import com.reandroid.lib.arsc.chunk.xml.ResXmlAttribute; -import com.reandroid.lib.arsc.chunk.xml.ResXmlBlock; -import com.reandroid.lib.arsc.chunk.xml.ResXmlElement; -import com.reandroid.lib.arsc.chunk.xml.ResXmlText; -import com.reandroid.lib.arsc.decoder.ResDecoder; -import com.reandroid.lib.arsc.decoder.ResourceNameStore; -import com.reandroid.lib.arsc.decoder.ValueDecoder; -import com.reandroid.lib.arsc.value.ValueType; - -import java.util.Collection; -import java.util.List; - -public class ResXmlDecoder extends ResDecoder { - private byte mCurrentPackageId; - public ResXmlDecoder(ResourceNameStore store){ - super(store); - } - private String decodeToString(ResXmlBlock resXmlBlock){ - StringBuilder builder=new StringBuilder(); - builder.append(""); - builder.append("\n"); - String body=decodeToString(resXmlBlock.getResXmlElement()); - builder.append(body); - return builder.toString(); - } - private String decodeToString(ResXmlElement resXmlElement){ - if(resXmlElement==null){ - return null; - } - StringBuilder builder=new StringBuilder(); - for(int i=0;i childElements=resXmlElement.listElements(); - if(!useEndTag){ - useEndTag=childElements.size()>0; - } - if(!useEndTag){ - builder.append("/>"); - return builder.toString(); - } - builder.append(">"); - if(text!=null){ - builder.append(text); - } - for(ResXmlElement child:childElements){ - builder.append("\n"); - String txtChild=decodeToString(child); - builder.append(txtChild); - } - if(childElements.size()>0){ - builder.append("\n"); - for(int i=0;i"); - return builder.toString(); - } - private String decodeAttributeValue(ResXmlAttribute attribute){ - ValueType valueType=attribute.getValueType(); - if(valueType==ValueType.FIRST_INT || valueType==ValueType.INT_HEX){ - int nameId=attribute.getNameResourceID(); - String val=decodeAttribute(nameId, attribute.getRawValue()); - if(val!=null){ - return val; - } - } - if(valueType==ValueType.REFERENCE){ - int id=attribute.getRawValue(); - byte pkgId= (byte) ((id>>24)&0xFF); - return getResourceFullName(id, pkgId!=getCurrentPackageId()); - } - if(valueType==ValueType.STRING){ - return attribute.getValueString(); - } - String val= ValueDecoder.decode(valueType, attribute.getRawValue()); - if(val!=null){ - return val; - } - return attribute.getRawValue()+"["+valueType+"]"; - } - private String decodeAttributeFullName(ResXmlAttribute attribute){ - StringBuilder builder=new StringBuilder(); - String prefix=getRealAttributeNamePrefix(attribute); - String name=getRealAttributeName(attribute); - if(prefix!=null&&!name.contains(":")){ - builder.append(prefix); - builder.append(':'); - } - builder.append(name); - return builder.toString(); - } - private String getRealAttributeNamePrefix(ResXmlAttribute attribute){ - /* TODO readjust wrong/stripped attribute name prefix prefix; */ - return attribute.getNamePrefix(); - } - private String getRealAttributeName(ResXmlAttribute attribute){ - int nameId=attribute.getNameResourceID(); - ResourceNameStore store=getResourceNameStore(); - byte pkgId= (byte) ((nameId>>24)&0xFF); - String name=store.getResourceName(nameId, getCurrentPackageId()!=pkgId); - if(name!=null){ - return name; - } - return attribute.getName(); - } - private byte getCurrentPackageId(){ - return mCurrentPackageId; - } - @Override - public String decode(byte currentPackageId, ResXmlBlock resXmlBlock) { - this.mCurrentPackageId=currentPackageId; - return decodeToString(resXmlBlock); - } -} diff --git a/src/main/java/com/reandroid/lib/arsc/group/EntryGroup.java b/src/main/java/com/reandroid/lib/arsc/group/EntryGroup.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/group/ItemGroup.java b/src/main/java/com/reandroid/lib/arsc/group/ItemGroup.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/group/StringGroup.java b/src/main/java/com/reandroid/lib/arsc/group/StringGroup.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/header/HeaderBlock.java b/src/main/java/com/reandroid/lib/arsc/header/HeaderBlock.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/io/BlockLoad.java b/src/main/java/com/reandroid/lib/arsc/io/BlockLoad.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/io/BlockReader.java b/src/main/java/com/reandroid/lib/arsc/io/BlockReader.java old mode 100644 new mode 100755 index 45e32d3..ba8d334 --- a/src/main/java/com/reandroid/lib/arsc/io/BlockReader.java +++ b/src/main/java/com/reandroid/lib/arsc/io/BlockReader.java @@ -324,4 +324,15 @@ public class BlockReader extends InputStream { System.arraycopy(arr2, 0, result, arr1.length, len); return result; } + public static HeaderBlock readHeaderBlock(File file) throws IOException{ + InputStream inputStream=new FileInputStream(file); + return readHeaderBlock(inputStream); + } + public static HeaderBlock readHeaderBlock(InputStream inputStream) throws IOException{ + byte[] buffer=new byte[8]; + inputStream.read(buffer); + inputStream.close(); + BlockReader reader=new BlockReader(buffer); + return reader.readHeaderBlock(); + } } diff --git a/src/main/java/com/reandroid/lib/arsc/item/BlockItem.java b/src/main/java/com/reandroid/lib/arsc/item/BlockItem.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/item/ByteArray.java b/src/main/java/com/reandroid/lib/arsc/item/ByteArray.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/item/ByteItem.java b/src/main/java/com/reandroid/lib/arsc/item/ByteItem.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/item/IntegerArray.java b/src/main/java/com/reandroid/lib/arsc/item/IntegerArray.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/item/IntegerItem.java b/src/main/java/com/reandroid/lib/arsc/item/IntegerItem.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/item/PackageName.java b/src/main/java/com/reandroid/lib/arsc/item/PackageName.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/item/ReferenceItem.java b/src/main/java/com/reandroid/lib/arsc/item/ReferenceItem.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/item/ResXmlID.java b/src/main/java/com/reandroid/lib/arsc/item/ResXmlID.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/item/ResXmlString.java b/src/main/java/com/reandroid/lib/arsc/item/ResXmlString.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/item/ShortItem.java b/src/main/java/com/reandroid/lib/arsc/item/ShortItem.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/item/SpecString.java b/src/main/java/com/reandroid/lib/arsc/item/SpecString.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/item/StringItem.java b/src/main/java/com/reandroid/lib/arsc/item/StringItem.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/item/StyleItem.java b/src/main/java/com/reandroid/lib/arsc/item/StyleItem.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/item/TableString.java b/src/main/java/com/reandroid/lib/arsc/item/TableString.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/item/TypeString.java b/src/main/java/com/reandroid/lib/arsc/item/TypeString.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/model/StyleSpanInfo.java b/src/main/java/com/reandroid/lib/arsc/model/StyleSpanInfo.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/pool/BaseStringPool.java b/src/main/java/com/reandroid/lib/arsc/pool/BaseStringPool.java old mode 100644 new mode 100755 index f78c0bd..d2e12aa --- a/src/main/java/com/reandroid/lib/arsc/pool/BaseStringPool.java +++ b/src/main/java/com/reandroid/lib/arsc/pool/BaseStringPool.java @@ -9,6 +9,7 @@ import com.reandroid.lib.arsc.group.StringGroup; import com.reandroid.lib.arsc.io.BlockLoad; import com.reandroid.lib.arsc.io.BlockReader; import com.reandroid.lib.arsc.item.*; +import com.reandroid.lib.arsc.pool.builder.StyleBuilder; import java.io.IOException; import java.util.*; @@ -63,6 +64,16 @@ public abstract class BaseStringPool extends BaseChunk imp mUniqueMap=new HashMap<>(); } + public void recreateStyles(){ + StyleArray styleArray = getStyleArray(); + //styleArray.clearChildes(); + StringArray stringArray=getStringsArray(); + for(T stringItem:stringArray.listItems()){ + if(StyleBuilder.hasStyle(stringItem)){ + StyleBuilder.buildStyle(stringItem); + } + } + } public List removeUnusedStrings(){ return getStringsArray().removeUnusedStrings(); } diff --git a/src/main/java/com/reandroid/lib/arsc/pool/PoolType.java b/src/main/java/com/reandroid/lib/arsc/pool/PoolType.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/pool/ResXmlStringPool.java b/src/main/java/com/reandroid/lib/arsc/pool/ResXmlStringPool.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/pool/SpecStringPool.java b/src/main/java/com/reandroid/lib/arsc/pool/SpecStringPool.java old mode 100644 new mode 100755 index 1b75f8e..df11e7a --- a/src/main/java/com/reandroid/lib/arsc/pool/SpecStringPool.java +++ b/src/main/java/com/reandroid/lib/arsc/pool/SpecStringPool.java @@ -15,4 +15,7 @@ public class SpecStringPool extends BaseStringPool { StringArray newInstance(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) { return new SpecStringArray(offsets, itemCount, itemStart, is_utf8); } + @Override + public void recreateStyles(){ + } } diff --git a/src/main/java/com/reandroid/lib/arsc/pool/TableStringPool.java b/src/main/java/com/reandroid/lib/arsc/pool/TableStringPool.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/pool/TypeStringPool.java b/src/main/java/com/reandroid/lib/arsc/pool/TypeStringPool.java old mode 100644 new mode 100755 index bbcb455..db1cdf7 --- a/src/main/java/com/reandroid/lib/arsc/pool/TypeStringPool.java +++ b/src/main/java/com/reandroid/lib/arsc/pool/TypeStringPool.java @@ -17,4 +17,7 @@ public class TypeStringPool extends BaseStringPool { StringArray newInstance(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) { return new TypeStringArray(offsets, itemCount, itemStart, is_utf8); } + @Override + public void recreateStyles(){ + } } diff --git a/src/main/java/com/reandroid/lib/arsc/pool/builder/SpannedText.java b/src/main/java/com/reandroid/lib/arsc/pool/builder/SpannedText.java new file mode 100755 index 0000000..52b1c02 --- /dev/null +++ b/src/main/java/com/reandroid/lib/arsc/pool/builder/SpannedText.java @@ -0,0 +1,16 @@ +package com.reandroid.lib.arsc.pool.builder; + +import java.util.List; + +public class SpannedText { + private String mLeftText; + private String mTag; + private String mText; + private String mRightText; + private List mChildes; + public SpannedText(){ + } + public void parse(int start, String text){ + int i=text.indexOf('<', start); + } +} diff --git a/src/main/java/com/reandroid/lib/arsc/pool/builder/StyleBuilder.java b/src/main/java/com/reandroid/lib/arsc/pool/builder/StyleBuilder.java new file mode 100755 index 0000000..8830362 --- /dev/null +++ b/src/main/java/com/reandroid/lib/arsc/pool/builder/StyleBuilder.java @@ -0,0 +1,29 @@ +package com.reandroid.lib.arsc.pool.builder; + +import com.reandroid.lib.arsc.item.StringItem; + +import java.util.regex.Pattern; + +public class StyleBuilder { + public static void buildStyle(StringItem stringItem){ + System.out.println(stringItem.toString()); + } + public static boolean hasStyle(StringItem stringItem){ + if(stringItem==null){ + return false; + } + return hasStyle(stringItem.getHtml()); + } + public static boolean hasStyle(String text){ + if(text==null){ + return false; + } + int i=text.indexOf('<'); + if(i<0){ + return false; + } + i=text.indexOf('>'); + return i>1; + } + private static final Pattern PATTERN_STYLE=Pattern.compile(""); +} diff --git a/src/main/java/com/reandroid/lib/arsc/util/FrameworkTable.java b/src/main/java/com/reandroid/lib/arsc/util/FrameworkTable.java old mode 100644 new mode 100755 index 4521fce..66c9cd6 --- a/src/main/java/com/reandroid/lib/arsc/util/FrameworkTable.java +++ b/src/main/java/com/reandroid/lib/arsc/util/FrameworkTable.java @@ -1,9 +1,11 @@ package com.reandroid.lib.arsc.util; +import com.reandroid.lib.arsc.chunk.ChunkType; import com.reandroid.lib.arsc.chunk.PackageBlock; import com.reandroid.lib.arsc.chunk.TableBlock; import com.reandroid.lib.arsc.chunk.TypeBlock; import com.reandroid.lib.arsc.group.EntryGroup; +import com.reandroid.lib.arsc.header.HeaderBlock; import com.reandroid.lib.arsc.io.BlockReader; import com.reandroid.lib.arsc.item.ReferenceItem; import com.reandroid.lib.arsc.item.TableString; @@ -15,9 +17,42 @@ import java.util.*; public class FrameworkTable extends TableBlock { + private String mFrameworkTitle; + private String mFrameworkName; + private String mFrameworkVersion; public FrameworkTable(){ super(); } + public String getFrameworkTitle(){ + if(mFrameworkTitle==null){ + mFrameworkTitle=loadProperty(PROP_TITLE); + } + return mFrameworkTitle; + } + public String getFrameworkName(){ + if(mFrameworkName==null){ + mFrameworkName=loadProperty(PROP_NAME); + } + return mFrameworkName; + } + public String getFrameworkVersion(){ + if(mFrameworkVersion==null){ + mFrameworkVersion=loadProperty(PROP_VERSION); + } + return mFrameworkVersion; + } + private void setFrameworkTitle(String value){ + mFrameworkTitle=null; + writeProperty(PROP_TITLE, value); + } + public void setFrameworkName(String value){ + mFrameworkName=null; + writeProperty(PROP_NAME, value); + } + public void setFrameworkVersion(String value){ + mFrameworkVersion=null; + writeProperty(PROP_VERSION, value); + } public int writeTable(File resourcesArscFile) throws IOException{ File dir=resourcesArscFile.getParentFile(); if(dir!=null && !dir.exists()){ @@ -49,7 +84,7 @@ public class FrameworkTable extends TableBlock { super.onReadBytes(reader); reader.close(); } - public void optimize(){ + public void optimize(String frameworkName, String frameworkVersion){ Map groupMap=scanAllEntryGroups(); for(EntryGroup group:groupMap.values()){ List entryBlockList=getEntriesToRemove(group); @@ -60,6 +95,9 @@ public class FrameworkTable extends TableBlock { pkg.refresh(); } optimizeTableString(); + setFrameworkTitle(TITLE_STRING); + setFrameworkName(frameworkName); + setFrameworkVersion(frameworkVersion); refresh(); } private void optimizeTableString(){ @@ -75,13 +113,14 @@ public class FrameworkTable extends TableBlock { } private void shrinkTableString(){ TableStringPool tableStringPool=getTableStringPool(); - TableString zero=tableStringPool.get(0); - zero.set("Framework string table"); + tableStringPool.getStringsArray().ensureSize(1); + TableString title=tableStringPool.get(0); + title.set(PROP_TITLE+":"+TITLE_STRING); for(TableString tableString:tableStringPool.getStringsArray().listItems()){ - if(tableString==zero){ + if(tableString==title){ continue; } - shrinkTableString(zero, tableString); + shrinkTableString(title, tableString); } tableStringPool.refresh(); } @@ -141,4 +180,106 @@ public class FrameworkTable extends TableBlock { } return results; } + private TableString writeProperty(String name, String value){ + if(!name.endsWith(":")){ + name=name+":"; + } + if(value==null){ + value=""; + } + if(!value.startsWith(name)){ + value=name+value; + } + TableString tableString=loadPropertyString(name); + if(tableString!=null){ + tableString.set(value); + }else { + TableStringPool tableStringPool=getTableStringPool(); + tableString=tableStringPool.getOrCreate(value); + } + return tableString; + } + private String loadProperty(String name){ + if(name==null){ + return null; + } + if(!name.endsWith(":")){ + name=name+":"; + } + TableString tableString=loadPropertyString(name); + if(tableString==null){ + return null; + } + String str=tableString.get().trim(); + return str.substring(name.length()); + } + private TableString loadPropertyString(String name){ + if(name==null){ + return null; + } + if(!name.endsWith(":")){ + name=name+":"; + } + TableStringPool tableStringPool=getTableStringPool(); + int max=PROP_COUNT; + for(int i=0;i allPkg = listPackages(); + builder.append("\n PACKAGES=").append(allPkg.size()); + for(PackageBlock packageBlock:allPkg){ + builder.append("\n "); + builder.append(String.format("0x%02x", packageBlock.getPackageId())); + builder.append(":").append(packageBlock.getPackageName()); + } + return builder.toString(); + } + private static final String TITLE_STRING="Framework table"; + private static final String PROP_TITLE="TITLE"; + private static final String PROP_NAME="NAME"; + private static final String PROP_VERSION="VERSION"; + private static final int PROP_COUNT=10; } diff --git a/src/main/java/com/reandroid/lib/arsc/value/BaseResValue.java b/src/main/java/com/reandroid/lib/arsc/value/BaseResValue.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/value/BaseResValueItem.java b/src/main/java/com/reandroid/lib/arsc/value/BaseResValueItem.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/value/EntryBlock.java b/src/main/java/com/reandroid/lib/arsc/value/EntryBlock.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/value/LibraryInfo.java b/src/main/java/com/reandroid/lib/arsc/value/LibraryInfo.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/value/ResConfig.java b/src/main/java/com/reandroid/lib/arsc/value/ResConfig.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/value/ResConfigHelper.java b/src/main/java/com/reandroid/lib/arsc/value/ResConfigHelper.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/value/ResValueBag.java b/src/main/java/com/reandroid/lib/arsc/value/ResValueBag.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/value/ResValueBagItem.java b/src/main/java/com/reandroid/lib/arsc/value/ResValueBagItem.java old mode 100644 new mode 100755 index 9026d7b..f80bc79 --- a/src/main/java/com/reandroid/lib/arsc/value/ResValueBagItem.java +++ b/src/main/java/com/reandroid/lib/arsc/value/ResValueBagItem.java @@ -1,6 +1,8 @@ package com.reandroid.lib.arsc.value; +import com.reandroid.lib.arsc.chunk.PackageBlock; import com.reandroid.lib.arsc.item.ReferenceItem; +import com.reandroid.lib.arsc.pool.SpecStringPool; public class ResValueBagItem extends BaseResValueItem{ @@ -68,14 +70,54 @@ public class ResValueBagItem extends BaseResValueItem{ if(data==old){ return; } + beforeDataValueChanged(); + setInt(OFFSET_DATA, data); + afterDataValueChanged(); + } + private void beforeDataValueChanged(){ if(getValueType()==ValueType.STRING){ removeTableReference(); } - setInt(OFFSET_DATA, data); + } + private void afterDataValueChanged(){ if(getValueType()==ValueType.STRING){ addTableReference(getTableStringReference()); } } + public short getIdHigh(){ + return getShort(OFFSET_ID+2); + } + public short getIdLow(){ + return getShort(OFFSET_ID); + } + public void setIdHigh(short val){ + setShort(OFFSET_ID+2, val); + } + public void setIdLow(short val){ + setShort(OFFSET_ID, val); + } + public short getDataHigh(){ + return getShort(OFFSET_DATA+2); + } + public short getDataLow(){ + return getShort(OFFSET_DATA); + } + public void setDataHigh(short val){ + if(val==getDataHigh()){ + return; + } + beforeDataValueChanged(); + setShort(OFFSET_DATA+2, val); + afterDataValueChanged(); + } + public void setDataLow(short val){ + if(val==getDataLow()){ + return; + } + beforeDataValueChanged(); + setShort(OFFSET_DATA+2, val); + afterDataValueChanged(); + } @Override public String toString(){ diff --git a/src/main/java/com/reandroid/lib/arsc/value/ResValueInt.java b/src/main/java/com/reandroid/lib/arsc/value/ResValueInt.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/value/ResValueItem.java b/src/main/java/com/reandroid/lib/arsc/value/ResValueItem.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/value/ValueType.java b/src/main/java/com/reandroid/lib/arsc/value/ValueType.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/reandroid/lib/arsc/value/attribute/AttributeBag.java b/src/main/java/com/reandroid/lib/arsc/value/attribute/AttributeBag.java new file mode 100755 index 0000000..0ce90e4 --- /dev/null +++ b/src/main/java/com/reandroid/lib/arsc/value/attribute/AttributeBag.java @@ -0,0 +1,194 @@ +package com.reandroid.lib.arsc.value.attribute; + +import com.reandroid.lib.arsc.value.EntryBlock; +import com.reandroid.lib.arsc.value.ResValueBag; +import com.reandroid.lib.common.EntryStore; + + +public class AttributeBag { + private final AttributeBagItem[] mBagItems; + public AttributeBag(AttributeBagItem[] bagItems){ + this.mBagItems=bagItems; + } + public String decodeAttributeValue(EntryStore entryStore, int attrValue){ + AttributeBagItem[] bagItems=searchValue(attrValue); + return AttributeBagItem.toString(entryStore, bagItems); + } + public String decodeValueType(){ + return AttributeValueType.toString(getValueTypes()); + } + public AttributeBagItem[] searchValue(int attrValue){ + if(isFlag()){ + return searchFlagValue(attrValue); + } + AttributeBagItem item=searchEnumValue(attrValue); + if(item!=null){ + return new AttributeBagItem[]{item}; + } + return null; + } + private AttributeBagItem searchEnumValue(int attrValue){ + AttributeBagItem[] bagItems= getBagItems(); + for(AttributeBagItem item:bagItems){ + if(item.isType()){ + continue; + } + int data=item.getData(); + if(attrValue==data){ + return item; + } + } + return null; + } + + private AttributeBagItem[] searchFlagValue(int attrValue){ + AttributeBagItem[] bagItems= getBagItems(); + int len=bagItems.length; + AttributeBagItem[] foundBags = new AttributeBagItem[len]; + for(int i=0;i=0) { + foundBags[index] = item; + } + } + return removeNull(foundBags); + } + + private int indexOf(AttributeBagItem[] foundFlag, int data) { + for (int i = 0; i < foundFlag.length; i++) { + AttributeBagItem item=foundFlag[i]; + if(item==null){ + return i; + } + int flag=item.getData(); + if(flag==0){ + return i; + } + if ((flag & data) == data) { + return -1; + } + if ((flag & data) == flag) { + return i; + } + } + return -1; + } + + private AttributeBagItem[] removeNull(AttributeBagItem[] bagItems){ + int count=countNonNull(bagItems); + if(count==0){ + return null; + } + AttributeBagItem[] results=new AttributeBagItem[count]; + int index=0; + int len=bagItems.length; + for(int i=0;i results=new ArrayList<>(); + AttributeValueType[] all=values(); + for(AttributeValueType vt:all){ + byte b=vt.mByte; + if(b==val){ + results.clear(); + results.add(vt); + break; + } + if((val&b)!=0 && vt!=ANY){ + results.add(vt); + } + } + return results.toArray(new AttributeValueType[0]); + } + public static AttributeValueType[] valuesOf(String valuesStr){ + if(valuesStr==null){ + return null; + } + String[] valueNames=valuesStr.split("[\\s|]+"); + List results=new ArrayList<>(); + for(String name:valueNames){ + AttributeValueType vt=fromName(name); + if(vt!=null){ + results.add(vt); + } + } + return results.toArray(new AttributeValueType[0]); + } + public static AttributeValueType fromName(String name){ + if(name==null){ + return null; + } + name=name.toUpperCase(); + AttributeValueType[] all=values(); + for(AttributeValueType vt:all){ + if(name.equals(vt.name())){ + return vt; + } + } + return null; + } +} diff --git a/src/main/java/com/reandroid/lib/arsc/value/plurals/PluralsQuantity.java b/src/main/java/com/reandroid/lib/arsc/value/plurals/PluralsQuantity.java new file mode 100755 index 0000000..ff5eb50 --- /dev/null +++ b/src/main/java/com/reandroid/lib/arsc/value/plurals/PluralsQuantity.java @@ -0,0 +1,45 @@ +package com.reandroid.lib.arsc.value.plurals; + + +public enum PluralsQuantity { + OTHER((short) 0x0004), + ZERO((short) 0x0005), + ONE((short) 0x0006), + TWO((short) 0x0007), + FEW((short) 0x0008), + MANY((short) 0x0009); + + private final short mId; + PluralsQuantity(short id) { + this.mId=id; + } + public short getId() { + return mId; + } + @Override + public String toString(){ + return name().toLowerCase(); + } + public static PluralsQuantity valueOf(short id){ + PluralsQuantity[] all=values(); + for(PluralsQuantity pq:all){ + if(id==pq.mId){ + return pq; + } + } + return null; + } + public static PluralsQuantity value(String name){ + if(name==null){ + return null; + } + name=name.toUpperCase(); + PluralsQuantity[] all=values(); + for(PluralsQuantity pq:all){ + if(name.equals(pq.name())){ + return pq; + } + } + return null; + } +} diff --git a/src/main/java/com/reandroid/lib/common/EntryStore.java b/src/main/java/com/reandroid/lib/common/EntryStore.java new file mode 100755 index 0000000..3160d2d --- /dev/null +++ b/src/main/java/com/reandroid/lib/common/EntryStore.java @@ -0,0 +1,18 @@ +package com.reandroid.lib.common; + +import com.reandroid.lib.arsc.chunk.PackageBlock; +import com.reandroid.lib.arsc.group.EntryGroup; +import com.reandroid.lib.arsc.item.TableString; + +import java.util.Collection; + +public interface EntryStore { + EntryGroup searchEntryGroup(String packageName, String type, String name); + Collection getEntryGroups(int resourceId); + + EntryGroup getEntryGroup(int resourceId); + + Collection getPackageBlocks(byte packageId); + + Collection getTableStrings(byte packageId, int stringReference); +} diff --git a/src/main/java/com/reandroid/lib/common/Frameworks.java b/src/main/java/com/reandroid/lib/common/Frameworks.java new file mode 100755 index 0000000..a91422e --- /dev/null +++ b/src/main/java/com/reandroid/lib/common/Frameworks.java @@ -0,0 +1,37 @@ +package com.reandroid.lib.common; + +import com.reandroid.lib.arsc.io.BlockReader; +import com.reandroid.lib.arsc.util.FrameworkTable; + +import java.io.IOException; +import java.io.InputStream; + +public class Frameworks { + private static FrameworkTable android_table; + private static boolean load_once; + public static FrameworkTable getAndroid(){ + if(android_table!=null || load_once){ + return android_table; + } + load_once=true; + FrameworkTable frameworkTable=null; + try { + frameworkTable = loadFramework(ANDROID_FRAMEWORK); + } catch (IOException e) { + } + android_table=frameworkTable; + return android_table; + } + private static FrameworkTable loadFramework(String name) throws IOException { + InputStream inputStream=Frameworks.class.getResourceAsStream(name); + if(inputStream==null){ + return null; + } + BlockReader reader=new BlockReader(inputStream); + FrameworkTable frameworkTable=new FrameworkTable(); + frameworkTable.readBytes(reader); + return frameworkTable; + } + + private static final String ANDROID_FRAMEWORK= "/fwk/android_resources_30.arsc"; +} diff --git a/src/main/java/com/reandroid/lib/common/ROArrayList.java b/src/main/java/com/reandroid/lib/common/ROArrayList.java new file mode 100755 index 0000000..968eae8 --- /dev/null +++ b/src/main/java/com/reandroid/lib/common/ROArrayList.java @@ -0,0 +1,21 @@ +package com.reandroid.lib.common; + +import java.util.AbstractList; + +public class ROArrayList extends AbstractList { + private final T[] elementData; + public ROArrayList(T[] elementData){ + this.elementData=elementData; + } + @Override + public T get(int i) { + return this.elementData[i]; + } + @Override + public int size() { + if(this.elementData==null){ + return 0; + } + return this.elementData.length; + } +} diff --git a/src/main/java/com/reandroid/lib/common/ROSingleList.java b/src/main/java/com/reandroid/lib/common/ROSingleList.java new file mode 100755 index 0000000..ee21c40 --- /dev/null +++ b/src/main/java/com/reandroid/lib/common/ROSingleList.java @@ -0,0 +1,25 @@ +package com.reandroid.lib.common; + +import java.util.AbstractList; + +public class ROSingleList extends AbstractList { + private final T item; + public ROSingleList(T item){ + this.item=item; + } + @Override + public T get(int i) { + if(i==0 && this.item!=null){ + return this.item; + } + throw new ArrayIndexOutOfBoundsException(getClass().getSimpleName()+": "+i); + } + + @Override + public int size() { + if(this.item==null){ + return 0; + } + return 1; + } +} diff --git a/src/main/java/com/reandroid/lib/common/TableEntryStore.java b/src/main/java/com/reandroid/lib/common/TableEntryStore.java new file mode 100755 index 0000000..6dd8cc9 --- /dev/null +++ b/src/main/java/com/reandroid/lib/common/TableEntryStore.java @@ -0,0 +1,111 @@ +package com.reandroid.lib.common; + +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.item.TableString; + +import java.util.*; + +public class TableEntryStore implements EntryStore{ + private final Map> mPackagesMap; + public TableEntryStore(){ + this.mPackagesMap = new HashMap<>(); + } + public void add(TableBlock tableBlock){ + if(tableBlock==null){ + return; + } + for(PackageBlock packageBlock:tableBlock.listPackages()){ + add(packageBlock); + } + } + public void add(PackageBlock packageBlock){ + if(packageBlock==null){ + return; + } + byte pkgId= (byte) packageBlock.getId(); + Set packageBlockSet=getOrCreate(pkgId); + packageBlockSet.add(packageBlock); + } + private Set getOrCreate(byte packageId){ + Byte id=packageId; + Set packageBlockSet=mPackagesMap.get(id); + if(packageBlockSet==null){ + packageBlockSet=new HashSet<>(); + mPackagesMap.put(id, packageBlockSet); + } + return packageBlockSet; + } + @Override + public EntryGroup searchEntryGroup(String packageName, String type, String name) { + return null; + } + private Set getTableBlocks(byte packageId) { + Set results=new HashSet<>(); + Set packageBlockSet = mPackagesMap.get(packageId); + if(packageBlockSet!=null){ + for(PackageBlock packageBlock:packageBlockSet){ + TableBlock tableBlock=packageBlock.getTableBlock(); + results.add(tableBlock); + } + } + return results; + } + + @Override + public Collection getEntryGroups(int resourceId) { + List results=new ArrayList<>(); + byte pkgId= (byte) ((resourceId>>24)&0xff); + Set packageBlockSet = mPackagesMap.get(pkgId); + if(packageBlockSet==null){ + return results; + } + for(PackageBlock packageBlock: packageBlockSet){ + EntryGroup group=packageBlock.getEntryGroup(resourceId); + if(group!=null){ + results.add(group); + } + } + return results; + } + + @Override + public EntryGroup getEntryGroup(int resourceId) { + byte pkgId= (byte) ((resourceId>>24)&0xff); + Set packageBlockSet = mPackagesMap.get(pkgId); + if(packageBlockSet==null){ + return null; + } + for(PackageBlock packageBlock: packageBlockSet){ + EntryGroup group=packageBlock.getEntryGroup(resourceId); + if(group!=null && group.pickOne()!=null){ + return group; + } + } + return null; + } + + @Override + public Collection getPackageBlocks(byte packageId) { + List results=new ArrayList<>(); + Set packageBlockSet = mPackagesMap.get(packageId); + if(packageBlockSet!=null){ + results.addAll(packageBlockSet); + } + return results; + } + + @Override + public Collection getTableStrings(byte packageId, int stringReference) { + List results=new ArrayList<>(); + Set tableBlockSet=getTableBlocks(packageId); + for(TableBlock tableBlock:tableBlockSet){ + TableString tableString=tableBlock.getTableStringPool().get(stringReference); + if(tableString!=null){ + results.add(tableString); + } + } + return results; + } +} diff --git a/src/main/resources/fwk/android_resources_30.arsc b/src/main/resources/fwk/android_resources_30.arsc new file mode 100755 index 0000000..bc18771 Binary files /dev/null and b/src/main/resources/fwk/android_resources_30.arsc differ diff --git a/src/main/resources/lib.properties b/src/main/resources/lib.properties old mode 100644 new mode 100755