mirror of
https://github.com/revanced/ARSCLib.git
synced 2025-04-30 14:24:25 +02:00
V1.0.4
This commit is contained in:
parent
661073d9b4
commit
b75cb1f163
95
README.md
95
README.md
@ -2,10 +2,93 @@
|
|||||||
## Android binary resources read/write library
|
## Android binary resources read/write library
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import com.reandroid.lib.arsc.chunk.TableBlock;
|
import com.reandroid.lib.arsc.chunk.TableBlock;
|
||||||
import com.reandroid.lib.arsc.io.BlockReader;
|
import com.reandroid.lib.arsc.chunk.PackageBlock;
|
||||||
|
import com.reandroid.lib.arsc.chunk.xml.AndroidManifestBlock;
|
||||||
|
import com.reandroid.lib.arsc.chunk.xml.ResXmlElement;
|
||||||
|
import com.reandroid.lib.arsc.chunk.xml.ResXmlAttribute;
|
||||||
|
|
||||||
public static void example() throws IOException{
|
public static void exampleManifest() throws IOException{
|
||||||
|
File inFile=new File("AndroidManifest.xml");
|
||||||
|
|
||||||
|
// *** Loading AndroidManifest ***
|
||||||
|
AndroidManifestBlock manifestBlock = AndroidManifestBlock.load(inFile);
|
||||||
|
|
||||||
|
System.out.println("Package name: "+manifestBlock.getPackageName());
|
||||||
|
|
||||||
|
List<String> usesPermissionList = manifestBlock.getUsesPermissions();
|
||||||
|
for(String usesPermission:usesPermissionList){
|
||||||
|
System.out.println("Uses permission: "+usesPermission);
|
||||||
|
}
|
||||||
|
|
||||||
|
// *** Modifying AndroidManifest ***
|
||||||
|
// Change package name
|
||||||
|
manifestBlock.setPackageName("com.new.package-name");
|
||||||
|
// Add uses-permission
|
||||||
|
manifestBlock.addUsesPermission("android.permission.WRITE_EXTERNAL_STORAGE");
|
||||||
|
// Modify version code
|
||||||
|
manifestBlock.setVersionCode(904);
|
||||||
|
// Modify version name
|
||||||
|
manifestBlock.setVersionName("9.0.4");
|
||||||
|
|
||||||
|
// Modify xml attribute
|
||||||
|
List<ResXmlElement> activityList = manifestBlock.listActivities();
|
||||||
|
for(ResXmlElement activityElement:activityList){
|
||||||
|
ResXmlAttribute attributeName = activityElement.searchAttributeByResourceId(AndroidManifestBlock.ID_name);
|
||||||
|
System.out.println("Old activity name: "+attributeName.getValueAsString());
|
||||||
|
attributeName.setValueAsString("com.app.MyActivity");
|
||||||
|
System.out.println("New activity name: "+attributeName.getValueAsString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh to re-calculate offsets
|
||||||
|
manifestBlock.refresh();
|
||||||
|
// Save
|
||||||
|
File outFile=new File("AndroidManifest_out.xml");
|
||||||
|
manifestBlock.writeBytes(outFile);
|
||||||
|
|
||||||
|
System.out.println("Saved: "+outFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
public static void exampleResourceTable() throws IOException{
|
||||||
|
File inFile=new File("resources.arsc");
|
||||||
|
|
||||||
|
// *** Loading resource table ***
|
||||||
|
TableBlock tableBlock=TableBlock.load(inFile);
|
||||||
|
|
||||||
|
Collection<PackageBlock> packageBlockList=tableBlock.listPackages();
|
||||||
|
System.out.println("Packages count = "+packageBlockList.size());
|
||||||
|
for(PackageBlock packageBlock:packageBlockList){
|
||||||
|
System.out.println("Package id = "+packageBlock.getId()
|
||||||
|
+", name = "+packageBlock.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// *** Modify resource table
|
||||||
|
// Change package name
|
||||||
|
for(PackageBlock packageBlock:packageBlockList){
|
||||||
|
String name = packageBlock.getName();
|
||||||
|
String newName = name + ".new-name";
|
||||||
|
packageBlock.setName(newName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh to re-calculate offsets
|
||||||
|
tableBlock.refresh();
|
||||||
|
// Save
|
||||||
|
File outFile=new File("resources_out.arsc");
|
||||||
|
tableBlock.writeBytes(outFile);
|
||||||
|
|
||||||
|
System.out.println("Saved: "+outFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
public static void exampleLoadApk() throws IOException{
|
||||||
File inFile=new File("test.apk");
|
File inFile=new File("test.apk");
|
||||||
File outDir=new File("test_out");
|
File outDir=new File("test_out");
|
||||||
|
|
||||||
@ -15,7 +98,7 @@
|
|||||||
outDir=decoder.writeToDirectory(outDir);
|
outDir=decoder.writeToDirectory(outDir);
|
||||||
System.out.println("Decoded to: "+outDir);
|
System.out.println("Decoded to: "+outDir);
|
||||||
|
|
||||||
// You can do any logical modification on any json files
|
// You can do any logical modification on any json files here
|
||||||
|
|
||||||
// To convert back json to apk
|
// To convert back json to apk
|
||||||
|
|
||||||
@ -26,6 +109,8 @@
|
|||||||
encodedModule.writeApk(outApk);
|
encodedModule.writeApk(outApk);
|
||||||
|
|
||||||
System.out.println("Created apk: "+outApk);
|
System.out.println("Created apk: "+outApk);
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
[Check implementation on APKEditor](https://github.com/REAndroid/APKEditor)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
apply plugin: 'java-library'
|
apply plugin: 'java-library'
|
||||||
|
|
||||||
group 'com.reandroid.lib.arsc'
|
group 'com.reandroid.lib.arsc'
|
||||||
version '1.0.3'
|
version '1.0.4'
|
||||||
|
|
||||||
java {
|
java {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
7
src/main/java/com/reandroid/lib/apk/APKLogger.java
Normal file
7
src/main/java/com/reandroid/lib/apk/APKLogger.java
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package com.reandroid.lib.apk;
|
||||||
|
|
||||||
|
public interface APKLogger {
|
||||||
|
void logMessage(String msg);
|
||||||
|
void logError(String msg, Throwable tr);
|
||||||
|
void logVerbose(String msg);
|
||||||
|
}
|
@ -14,6 +14,7 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
|
||||||
public class ApkModule {
|
public class ApkModule {
|
||||||
private final String moduleName;
|
private final String moduleName;
|
||||||
@ -22,6 +23,7 @@ public class ApkModule {
|
|||||||
private TableBlock mTableBlock;
|
private TableBlock mTableBlock;
|
||||||
private AndroidManifestBlock mManifestBlock;
|
private AndroidManifestBlock mManifestBlock;
|
||||||
private final UncompressedFiles mUncompressedFiles;
|
private final UncompressedFiles mUncompressedFiles;
|
||||||
|
private APKLogger apkLogger;
|
||||||
ApkModule(String moduleName, APKArchive apkArchive){
|
ApkModule(String moduleName, APKArchive apkArchive){
|
||||||
this.moduleName=moduleName;
|
this.moduleName=moduleName;
|
||||||
this.apkArchive=apkArchive;
|
this.apkArchive=apkArchive;
|
||||||
@ -72,6 +74,34 @@ public class ApkModule {
|
|||||||
public void removeDir(String dirName){
|
public void removeDir(String dirName){
|
||||||
getApkArchive().removeDir(dirName);
|
getApkArchive().removeDir(dirName);
|
||||||
}
|
}
|
||||||
|
public void validateResourcesDir() throws IOException {
|
||||||
|
List<ResFile> resFileList = listResFiles();
|
||||||
|
Set<String> existPaths=new HashSet<>();
|
||||||
|
List<InputSource> sourceList = getApkArchive().listInputSources();
|
||||||
|
for(InputSource inputSource:sourceList){
|
||||||
|
existPaths.add(inputSource.getAlias());
|
||||||
|
}
|
||||||
|
for(ResFile resFile:resFileList){
|
||||||
|
String path=resFile.getFilePath();
|
||||||
|
String pathNew=resFile.validateTypeDirectoryName();
|
||||||
|
if(pathNew==null || pathNew.equals(path)){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(existPaths.contains(pathNew)){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
existPaths.remove(path);
|
||||||
|
existPaths.add(pathNew);
|
||||||
|
resFile.setFilePath(pathNew);
|
||||||
|
if(resFile.getInputSource().getMethod() == ZipEntry.STORED){
|
||||||
|
getUncompressedFiles().replacePath(path, pathNew);
|
||||||
|
}
|
||||||
|
logVerbose("Dir validated: '"+path+"' -> '"+pathNew+"'");
|
||||||
|
}
|
||||||
|
TableStringPool stringPool= getTableBlock().getTableStringPool();
|
||||||
|
stringPool.refreshUniqueIdMap();
|
||||||
|
getTableBlock().refresh();
|
||||||
|
}
|
||||||
public void setResourcesRootDir(String dirName) throws IOException {
|
public void setResourcesRootDir(String dirName) throws IOException {
|
||||||
List<ResFile> resFileList = listResFiles();
|
List<ResFile> resFileList = listResFiles();
|
||||||
Set<String> existPaths=new HashSet<>();
|
Set<String> existPaths=new HashSet<>();
|
||||||
@ -88,9 +118,14 @@ public class ApkModule {
|
|||||||
existPaths.remove(path);
|
existPaths.remove(path);
|
||||||
existPaths.add(pathNew);
|
existPaths.add(pathNew);
|
||||||
resFile.setFilePath(pathNew);
|
resFile.setFilePath(pathNew);
|
||||||
|
if(resFile.getInputSource().getMethod() == ZipEntry.STORED){
|
||||||
|
getUncompressedFiles().replacePath(path, pathNew);
|
||||||
|
}
|
||||||
|
logVerbose("Root changed: '"+path+"' -> '"+pathNew+"'");
|
||||||
}
|
}
|
||||||
TableStringPool stringPool= getTableBlock().getTableStringPool();
|
TableStringPool stringPool= getTableBlock().getTableStringPool();
|
||||||
stringPool.refreshUniqueIdMap();
|
stringPool.refreshUniqueIdMap();
|
||||||
|
getTableBlock().refresh();
|
||||||
}
|
}
|
||||||
public List<ResFile> listResFiles() throws IOException {
|
public List<ResFile> listResFiles() throws IOException {
|
||||||
List<ResFile> results=new ArrayList<>();
|
List<ResFile> results=new ArrayList<>();
|
||||||
@ -123,7 +158,7 @@ public class ApkModule {
|
|||||||
if(pkg==null){
|
if(pkg==null){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return pkg.getPackageName();
|
return pkg.getName();
|
||||||
}
|
}
|
||||||
public void setPackageName(String name) throws IOException {
|
public void setPackageName(String name) throws IOException {
|
||||||
String old=getPackageName();
|
String old=getPackageName();
|
||||||
@ -137,13 +172,13 @@ public class ApkModule {
|
|||||||
PackageArray pkgArray = tableBlock.getPackageArray();
|
PackageArray pkgArray = tableBlock.getPackageArray();
|
||||||
for(PackageBlock pkg:pkgArray.listItems()){
|
for(PackageBlock pkg:pkgArray.listItems()){
|
||||||
if(pkgArray.childesCount()==1){
|
if(pkgArray.childesCount()==1){
|
||||||
pkg.setPackageName(name);
|
pkg.setName(name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String pkgName=pkg.getPackageName();
|
String pkgName=pkg.getName();
|
||||||
if(pkgName.startsWith(old)){
|
if(pkgName.startsWith(old)){
|
||||||
pkgName=pkgName.replace(old, name);
|
pkgName=pkgName.replace(old, name);
|
||||||
pkg.setPackageName(pkgName);
|
pkg.setName(pkgName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -211,6 +246,24 @@ public class ApkModule {
|
|||||||
this.loadDefaultFramework = loadDefaultFramework;
|
this.loadDefaultFramework = loadDefaultFramework;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAPKLogger(APKLogger logger) {
|
||||||
|
this.apkLogger = logger;
|
||||||
|
}
|
||||||
|
private void logMessage(String msg) {
|
||||||
|
if(apkLogger!=null){
|
||||||
|
apkLogger.logMessage(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void logError(String msg, Throwable tr) {
|
||||||
|
if(apkLogger!=null){
|
||||||
|
apkLogger.logError(msg, tr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void logVerbose(String msg) {
|
||||||
|
if(apkLogger!=null){
|
||||||
|
apkLogger.logVerbose(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
public static ApkModule loadApkFile(File apkFile) throws IOException {
|
public static ApkModule loadApkFile(File apkFile) throws IOException {
|
||||||
return loadApkFile(apkFile, ApkUtil.DEF_MODULE_NAME);
|
return loadApkFile(apkFile, ApkUtil.DEF_MODULE_NAME);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.reandroid.lib.apk;
|
package com.reandroid.lib.apk;
|
||||||
|
|
||||||
import com.reandroid.archive.InputSource;
|
import com.reandroid.archive.InputSource;
|
||||||
|
import com.reandroid.lib.arsc.chunk.TypeBlock;
|
||||||
import com.reandroid.lib.arsc.chunk.xml.ResXmlBlock;
|
import com.reandroid.lib.arsc.chunk.xml.ResXmlBlock;
|
||||||
import com.reandroid.lib.arsc.value.EntryBlock;
|
import com.reandroid.lib.arsc.value.EntryBlock;
|
||||||
import com.reandroid.lib.json.JSONObject;
|
import com.reandroid.lib.json.JSONObject;
|
||||||
@ -18,6 +19,51 @@ public class ResFile {
|
|||||||
this.inputSource=inputSource;
|
this.inputSource=inputSource;
|
||||||
this.entryBlockList=entryBlockList;
|
this.entryBlockList=entryBlockList;
|
||||||
}
|
}
|
||||||
|
public String validateTypeDirectoryName(){
|
||||||
|
EntryBlock entryBlock=pickOne();
|
||||||
|
if(entryBlock==null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String path=getFilePath();
|
||||||
|
String root="";
|
||||||
|
int i=path.indexOf('/');
|
||||||
|
if(i>0){
|
||||||
|
i++;
|
||||||
|
root=path.substring(0, i);
|
||||||
|
path=path.substring(i);
|
||||||
|
}
|
||||||
|
String name=path;
|
||||||
|
i=path.lastIndexOf('/');
|
||||||
|
if(i>0){
|
||||||
|
i++;
|
||||||
|
name=path.substring(i);
|
||||||
|
}
|
||||||
|
TypeBlock typeBlock=entryBlock.getTypeBlock();
|
||||||
|
String typeName=typeBlock.getTypeName()+typeBlock.getResConfig().getQualifiers();
|
||||||
|
return root+typeName+"/"+name;
|
||||||
|
}
|
||||||
|
private EntryBlock pickOne(){
|
||||||
|
List<EntryBlock> entryList = entryBlockList;
|
||||||
|
if(entryList.size()==0){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for(EntryBlock entryBlock:entryList){
|
||||||
|
if(!entryBlock.isNull() && entryBlock.isDefault()){
|
||||||
|
return entryBlock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(EntryBlock entryBlock:entryList){
|
||||||
|
if(!entryBlock.isNull()){
|
||||||
|
return entryBlock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(EntryBlock entryBlock:entryList){
|
||||||
|
if(entryBlock.isDefault()){
|
||||||
|
return entryBlock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entryList.get(0);
|
||||||
|
}
|
||||||
public String getFilePath(){
|
public String getFilePath(){
|
||||||
return getInputSource().getAlias();
|
return getInputSource().getAlias();
|
||||||
}
|
}
|
||||||
|
@ -243,7 +243,7 @@ public class ResourceIds {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(pkg.id!=this.id){
|
if(pkg.id!=this.id){
|
||||||
throw new IllegalArgumentException("Different package id: "+this.id+"!="+pkg.id);
|
throw new DuplicateException("Different package id: "+this.id+"!="+pkg.id);
|
||||||
}
|
}
|
||||||
if(pkg.name!=null){
|
if(pkg.name!=null){
|
||||||
this.name = pkg.name;
|
this.name = pkg.name;
|
||||||
@ -277,7 +277,7 @@ public class ResourceIds {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(entry.getPackageId()!=this.id){
|
if(entry.getPackageId()!=this.id){
|
||||||
throw new IllegalArgumentException("Different package id: "+entry);
|
throw new DuplicateException("Different package id: "+entry);
|
||||||
}
|
}
|
||||||
byte typeId=entry.getTypeId();
|
byte typeId=entry.getTypeId();
|
||||||
Type type=typeMap.get(typeId);
|
Type type=typeMap.get(typeId);
|
||||||
@ -400,7 +400,7 @@ public class ResourceIds {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(this.id!= type.id){
|
if(this.id!= type.id){
|
||||||
throw new IllegalArgumentException("Different type ids: "+id+"!="+type.id);
|
throw new DuplicateException("Different type ids: "+id+"!="+type.id);
|
||||||
}
|
}
|
||||||
if(type.name!=null){
|
if(type.name!=null){
|
||||||
this.name=type.name;
|
this.name=type.name;
|
||||||
@ -427,7 +427,7 @@ public class ResourceIds {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(entry.getTypeId()!=this.id){
|
if(entry.getTypeId()!=this.id){
|
||||||
throw new IllegalArgumentException("Different type id: "+entry);
|
throw new DuplicateException("Different type id: "+entry);
|
||||||
}
|
}
|
||||||
short key=entry.getEntryId();
|
short key=entry.getEntryId();
|
||||||
Entry exist=entryMap.get(key);
|
Entry exist=entryMap.get(key);
|
||||||
@ -435,7 +435,7 @@ public class ResourceIds {
|
|||||||
if(Objects.equals(exist.name, entry.name)){
|
if(Objects.equals(exist.name, entry.name)){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException("Duplicate entry exist: "+exist+", entry: "+entry);
|
throw new DuplicateException("Duplicate entry exist: "+exist+", entry: "+entry);
|
||||||
}
|
}
|
||||||
if(name == null){
|
if(name == null){
|
||||||
this.name = entry.typeName;
|
this.name = entry.typeName;
|
||||||
@ -652,10 +652,10 @@ public class ResourceIds {
|
|||||||
matcher= pattern.matcher(element);
|
matcher= pattern.matcher(element);
|
||||||
}
|
}
|
||||||
if(id==0){
|
if(id==0){
|
||||||
throw new IllegalArgumentException("Missing id: "+xmlElement);
|
throw new DuplicateException("Missing id: "+xmlElement);
|
||||||
}
|
}
|
||||||
if(name==null){
|
if(name==null){
|
||||||
throw new IllegalArgumentException("Missing name: "+xmlElement);
|
throw new DuplicateException("Missing name: "+xmlElement);
|
||||||
}
|
}
|
||||||
return new Entry(id, type, name);
|
return new Entry(id, type, name);
|
||||||
}
|
}
|
||||||
@ -684,6 +684,17 @@ public class ResourceIds {
|
|||||||
| (entryId & 0xffff);
|
| (entryId & 0xffff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public static class DuplicateException extends IllegalArgumentException{
|
||||||
|
public DuplicateException(String message){
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
public DuplicateException(String message, final Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
public DuplicateException(Throwable cause) {
|
||||||
|
super(cause.getMessage(), cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static final String JSON_FILE_NAME ="resource-ids.json";
|
public static final String JSON_FILE_NAME ="resource-ids.json";
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ public class TableBlockJson {
|
|||||||
File infoFile=new File(pkgDir, PackageBlock.JSON_FILE_NAME);
|
File infoFile=new File(pkgDir, PackageBlock.JSON_FILE_NAME);
|
||||||
JSONObject jsonObject=new JSONObject();
|
JSONObject jsonObject=new JSONObject();
|
||||||
jsonObject.put(PackageBlock.NAME_package_id, packageBlock.getId());
|
jsonObject.put(PackageBlock.NAME_package_id, packageBlock.getId());
|
||||||
jsonObject.put(PackageBlock.NAME_package_name, packageBlock.getPackageName());
|
jsonObject.put(PackageBlock.NAME_package_name, packageBlock.getName());
|
||||||
jsonObject.write(infoFile);
|
jsonObject.write(infoFile);
|
||||||
for(SpecTypePair specTypePair: packageBlock.listAllSpecTypePair()){
|
for(SpecTypePair specTypePair: packageBlock.listAllSpecTypePair()){
|
||||||
for(TypeBlock typeBlock:specTypePair.getTypeBlockArray().listItems()){
|
for(TypeBlock typeBlock:specTypePair.getTypeBlockArray().listItems()){
|
||||||
@ -52,7 +52,7 @@ public class TableBlockJson {
|
|||||||
private String getDirName(PackageBlock packageBlock){
|
private String getDirName(PackageBlock packageBlock){
|
||||||
StringBuilder builder=new StringBuilder();
|
StringBuilder builder=new StringBuilder();
|
||||||
builder.append(String.format("0x%02x", packageBlock.getId()));
|
builder.append(String.format("0x%02x", packageBlock.getId()));
|
||||||
String name= packageBlock.getPackageName();
|
String name= packageBlock.getName();
|
||||||
if(name!=null){
|
if(name!=null){
|
||||||
builder.append('-');
|
builder.append('-');
|
||||||
builder.append(name);
|
builder.append(name);
|
||||||
|
@ -70,6 +70,10 @@ public class UncompressedFiles implements JSONConvert<JSONObject> {
|
|||||||
return mExtensionList.contains(extension.substring(1));
|
return mExtensionList.contains(extension.substring(1));
|
||||||
}
|
}
|
||||||
public boolean containsPath(String path){
|
public boolean containsPath(String path){
|
||||||
|
path=sanitizePath(path);
|
||||||
|
if(path==null){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return mPathList.contains(path);
|
return mPathList.contains(path);
|
||||||
}
|
}
|
||||||
public void addPath(ZipArchive zipArchive){
|
public void addPath(ZipArchive zipArchive){
|
||||||
@ -84,15 +88,31 @@ public class UncompressedFiles implements JSONConvert<JSONObject> {
|
|||||||
addPath(inputSource.getAlias());
|
addPath(inputSource.getAlias());
|
||||||
}
|
}
|
||||||
public void addPath(String path){
|
public void addPath(String path){
|
||||||
if(path==null || path.length()==0){
|
path=sanitizePath(path);
|
||||||
|
if(path==null){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
path=path.replace(File.separatorChar, '/').trim();
|
|
||||||
while (path.startsWith("/")){
|
|
||||||
path=path.substring(1);
|
|
||||||
}
|
|
||||||
mPathList.add(path);
|
mPathList.add(path);
|
||||||
}
|
}
|
||||||
|
public void removePath(String path){
|
||||||
|
path=sanitizePath(path);
|
||||||
|
if(path==null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mPathList.remove(path);
|
||||||
|
}
|
||||||
|
public void replacePath(String path, String rep){
|
||||||
|
path=sanitizePath(path);
|
||||||
|
rep=sanitizePath(rep);
|
||||||
|
if(path==null||rep==null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!mPathList.contains(path)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mPathList.remove(path);
|
||||||
|
mPathList.add(rep);
|
||||||
|
}
|
||||||
public void addCommonExtensions(){
|
public void addCommonExtensions(){
|
||||||
for(String ext:COMMON_EXTENSIONS){
|
for(String ext:COMMON_EXTENSIONS){
|
||||||
addExtension(ext);
|
addExtension(ext);
|
||||||
@ -146,6 +166,19 @@ public class UncompressedFiles implements JSONConvert<JSONObject> {
|
|||||||
JSONObject jsonObject=new JSONObject(new FileInputStream(jsonFile));
|
JSONObject jsonObject=new JSONObject(new FileInputStream(jsonFile));
|
||||||
fromJson(jsonObject);
|
fromJson(jsonObject);
|
||||||
}
|
}
|
||||||
|
private static String sanitizePath(String path){
|
||||||
|
if(path==null || path.length()==0){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
path=path.replace(File.separatorChar, '/').trim();
|
||||||
|
while (path.startsWith("/")){
|
||||||
|
path=path.substring(1);
|
||||||
|
}
|
||||||
|
if(path.length()==0){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
public static final String JSON_FILE = "uncompressed-files.json";
|
public static final String JSON_FILE = "uncompressed-files.json";
|
||||||
public static final String NAME_paths = "paths";
|
public static final String NAME_paths = "paths";
|
||||||
public static final String NAME_extensions = "extensions";
|
public static final String NAME_extensions = "extensions";
|
||||||
|
@ -113,8 +113,6 @@ public class PackageBlock extends BaseChunk implements BlockLoad, JSONConvert<JS
|
|||||||
public void setName(String name){
|
public void setName(String name){
|
||||||
mPackageName.set(name);
|
mPackageName.set(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public TableBlock getTableBlock(){
|
public TableBlock getTableBlock(){
|
||||||
Block parent=getParent();
|
Block parent=getParent();
|
||||||
while(parent!=null){
|
while(parent!=null){
|
||||||
@ -125,13 +123,6 @@ public class PackageBlock extends BaseChunk implements BlockLoad, JSONConvert<JS
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
public String getPackageName(){
|
|
||||||
return mPackageName.get();
|
|
||||||
}
|
|
||||||
public void setPackageName(String name){
|
|
||||||
mPackageName.set(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TypeStringPool getTypeStringPool(){
|
public TypeStringPool getTypeStringPool(){
|
||||||
return mTypeStringPool;
|
return mTypeStringPool;
|
||||||
}
|
}
|
||||||
|
@ -143,6 +143,9 @@ public class TableBlock extends BaseChunk implements JSONConvert<JSONObject> {
|
|||||||
tableBlock.addFramework(Frameworks.getAndroid());
|
tableBlock.addFramework(Frameworks.getAndroid());
|
||||||
return tableBlock;
|
return tableBlock;
|
||||||
}
|
}
|
||||||
|
public static TableBlock load(File file) throws IOException{
|
||||||
|
return load(new FileInputStream(file));
|
||||||
|
}
|
||||||
public static TableBlock load(InputStream inputStream) throws IOException{
|
public static TableBlock load(InputStream inputStream) throws IOException{
|
||||||
TableBlock tableBlock=new TableBlock();
|
TableBlock tableBlock=new TableBlock();
|
||||||
tableBlock.readBytes(inputStream);
|
tableBlock.readBytes(inputStream);
|
||||||
|
@ -248,6 +248,16 @@ public class AndroidManifestBlock extends ResXmlBlock{
|
|||||||
builder.append("}");
|
builder.append("}");
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
public static boolean isAndroidManifestBlock(ResXmlBlock xmlBlock){
|
||||||
|
if(xmlBlock==null){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ResXmlElement root = xmlBlock.getResXmlElement();
|
||||||
|
if(root==null){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return TAG_manifest.equals(root.getTag());
|
||||||
|
}
|
||||||
public static AndroidManifestBlock load(File file) throws IOException {
|
public static AndroidManifestBlock load(File file) throws IOException {
|
||||||
return load(new FileInputStream(file));
|
return load(new FileInputStream(file));
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ public class ResXmlAttribute extends FixedBlockContainer
|
|||||||
super(7);
|
super(7);
|
||||||
mNamespaceReference =new IntegerItem(-1);
|
mNamespaceReference =new IntegerItem(-1);
|
||||||
mNameReference =new IntegerItem();
|
mNameReference =new IntegerItem();
|
||||||
mValueStringReference =new IntegerItem();
|
mValueStringReference =new IntegerItem(-1);
|
||||||
mNameType=new ShortItem((short) 0x0008);
|
mNameType=new ShortItem((short) 0x0008);
|
||||||
mReserved =new ByteItem();
|
mReserved =new ByteItem();
|
||||||
mValueTypeByte=new ByteItem();
|
mValueTypeByte=new ByteItem();
|
||||||
@ -42,7 +42,7 @@ public class ResXmlAttribute extends FixedBlockContainer
|
|||||||
int getNamespaceReference(){
|
int getNamespaceReference(){
|
||||||
return mNamespaceReference.get();
|
return mNamespaceReference.get();
|
||||||
}
|
}
|
||||||
void setNamespaceReference(int ref){
|
public void setNamespaceReference(int ref){
|
||||||
mNamespaceReference.set(ref);
|
mNamespaceReference.set(ref);
|
||||||
}
|
}
|
||||||
int getNameReference(){
|
int getNameReference(){
|
||||||
@ -112,7 +112,7 @@ public class ResXmlAttribute extends FixedBlockContainer
|
|||||||
}
|
}
|
||||||
return startNamespace.getPrefix();
|
return startNamespace.getPrefix();
|
||||||
}
|
}
|
||||||
ResXmlStartNamespace getStartNamespace(){
|
public ResXmlStartNamespace getStartNamespace(){
|
||||||
ResXmlElement xmlElement=getParentResXmlElement();
|
ResXmlElement xmlElement=getParentResXmlElement();
|
||||||
if(xmlElement==null){
|
if(xmlElement==null){
|
||||||
return null;
|
return null;
|
||||||
@ -131,6 +131,7 @@ public class ResXmlAttribute extends FixedBlockContainer
|
|||||||
setValueStringReference(ref);
|
setValueStringReference(ref);
|
||||||
if(getValueType()==ValueType.STRING){
|
if(getValueType()==ValueType.STRING){
|
||||||
setRawValue(ref);
|
setRawValue(ref);
|
||||||
|
setValueStringReference(ref);
|
||||||
}
|
}
|
||||||
return resXmlString;
|
return resXmlString;
|
||||||
}
|
}
|
||||||
@ -280,7 +281,7 @@ public class ResXmlAttribute extends FixedBlockContainer
|
|||||||
}
|
}
|
||||||
public void setValueAsBoolean(boolean val){
|
public void setValueAsBoolean(boolean val){
|
||||||
setValueType(ValueType.INT_BOOLEAN);
|
setValueType(ValueType.INT_BOOLEAN);
|
||||||
int ref=val?0xffff:0;
|
int ref=val?0xffffffff:0;
|
||||||
setRawValue(ref);
|
setRawValue(ref);
|
||||||
setValueStringReference(-1);
|
setValueStringReference(-1);
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,7 @@ public class ResXmlElement extends FixedBlockContainer implements JSONConvert<JS
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
public Collection<ResXmlAttribute> listResXmlAttributes(){
|
public Collection<ResXmlAttribute> listAttributes(){
|
||||||
ResXmlStartElement startElement=getStartElement();
|
ResXmlStartElement startElement=getStartElement();
|
||||||
if(startElement!=null){
|
if(startElement!=null){
|
||||||
return startElement.listResXmlAttributes();
|
return startElement.listResXmlAttributes();
|
||||||
|
@ -31,6 +31,14 @@ public class ResXmlStartElement extends BaseXmlChunk {
|
|||||||
addChild(mClassAttribute);
|
addChild(mClassAttribute);
|
||||||
addChild(mAttributeArray);
|
addChild(mAttributeArray);
|
||||||
}
|
}
|
||||||
|
public ResXmlAttribute getAttribute(int resourceId){
|
||||||
|
for(ResXmlAttribute attribute:listResXmlAttributes()){
|
||||||
|
if(resourceId==attribute.getNameResourceID()){
|
||||||
|
return attribute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
public ResXmlAttribute getAttribute(String uri, String name){
|
public ResXmlAttribute getAttribute(String uri, String name){
|
||||||
if(name==null){
|
if(name==null){
|
||||||
return null;
|
return null;
|
||||||
|
@ -25,8 +25,8 @@ public class ValueDecoder {
|
|||||||
}
|
}
|
||||||
String prefix=null;
|
String prefix=null;
|
||||||
if(currentPackage!=null){
|
if(currentPackage!=null){
|
||||||
String name=currentPackage.getPackageName();
|
String name=currentPackage.getName();
|
||||||
String other=entryBlock.getPackageBlock().getPackageName();
|
String other=entryBlock.getPackageBlock().getName();
|
||||||
if(!name.equals(other)){
|
if(!name.equals(other)){
|
||||||
prefix=other+":";
|
prefix=other+":";
|
||||||
}
|
}
|
||||||
@ -67,7 +67,7 @@ public class ValueDecoder {
|
|||||||
is_attribute=true;
|
is_attribute=true;
|
||||||
}
|
}
|
||||||
if(is_reference || is_attribute){
|
if(is_reference || is_attribute){
|
||||||
String ref=buildReferenceValue(store, valueType, currentPackage.getPackageName(), data);
|
String ref=buildReferenceValue(store, valueType, currentPackage.getName(), data);
|
||||||
if(ref!=null){
|
if(ref!=null){
|
||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
@ -286,7 +286,7 @@ public class ValueDecoder {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
for(PackageBlock packageBlock:allPkg){
|
for(PackageBlock packageBlock:allPkg){
|
||||||
String name=packageBlock.getPackageName();
|
String name=packageBlock.getName();
|
||||||
if(name!=null){
|
if(name!=null){
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
@ -307,7 +307,7 @@ public class ValueDecoder {
|
|||||||
if(packageBlock==null){
|
if(packageBlock==null){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return packageBlock.getPackageName();
|
return packageBlock.getName();
|
||||||
}
|
}
|
||||||
private static EntryGroup searchEntryGroup(EntryStore store, EntryBlock entryBlock, int resourceId){
|
private static EntryGroup searchEntryGroup(EntryStore store, EntryBlock entryBlock, int resourceId){
|
||||||
EntryGroup entryGroup=searchEntryGroup(entryBlock, resourceId);
|
EntryGroup entryGroup=searchEntryGroup(entryBlock, resourceId);
|
||||||
|
@ -3,6 +3,7 @@ package com.reandroid.lib.arsc.io;
|
|||||||
import com.reandroid.lib.arsc.header.HeaderBlock;
|
import com.reandroid.lib.arsc.header.HeaderBlock;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
|
|
||||||
public class BlockReader extends InputStream {
|
public class BlockReader extends InputStream {
|
||||||
@ -317,6 +318,9 @@ public class BlockReader extends InputStream {
|
|||||||
while((len=in.read(buff))>0){
|
while((len=in.read(buff))>0){
|
||||||
result=add(result, buff, len);
|
result=add(result, buff, len);
|
||||||
}
|
}
|
||||||
|
if(!(in instanceof ZipInputStream)){
|
||||||
|
in.close();
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
private static byte[] add(byte[] arr1, byte[] arr2, int len){
|
private static byte[] add(byte[] arr1, byte[] arr2, int len){
|
||||||
@ -331,7 +335,7 @@ public class BlockReader extends InputStream {
|
|||||||
}
|
}
|
||||||
public static HeaderBlock readHeaderBlock(InputStream inputStream) throws IOException{
|
public static HeaderBlock readHeaderBlock(InputStream inputStream) throws IOException{
|
||||||
byte[] buffer=new byte[8];
|
byte[] buffer=new byte[8];
|
||||||
inputStream.read(buffer);
|
inputStream.read(buffer, 0, 8);
|
||||||
inputStream.close();
|
inputStream.close();
|
||||||
BlockReader reader=new BlockReader(buffer);
|
BlockReader reader=new BlockReader(buffer);
|
||||||
return reader.readHeaderBlock();
|
return reader.readHeaderBlock();
|
||||||
|
@ -273,7 +273,7 @@ public class FrameworkTable extends TableBlock {
|
|||||||
for(PackageBlock packageBlock:allPkg){
|
for(PackageBlock packageBlock:allPkg){
|
||||||
builder.append("\n ");
|
builder.append("\n ");
|
||||||
builder.append(String.format("0x%02x", packageBlock.getId()));
|
builder.append(String.format("0x%02x", packageBlock.getId()));
|
||||||
builder.append(":").append(packageBlock.getPackageName());
|
builder.append(":").append(packageBlock.getName());
|
||||||
}
|
}
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
@ -333,7 +333,7 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
|
|||||||
if(packageBlock==null){
|
if(packageBlock==null){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return packageBlock.getPackageName();
|
return packageBlock.getName();
|
||||||
}
|
}
|
||||||
public SpecString getSpecString(){
|
public SpecString getSpecString(){
|
||||||
PackageBlock packageBlock=getPackageBlock();
|
PackageBlock packageBlock=getPackageBlock();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user