From 8b7788e92ebb72d87ff56908ed31fbc9d216351d Mon Sep 17 00:00:00 2001 From: REAndroid Date: Thu, 11 Apr 2024 17:36:22 +0200 Subject: [PATCH] Fix raw decoding error for apk without resources REAndroid/APKEditor#99 --- .../java/com/reandroid/apk/ApkModule.java | 16 +++----- .../com/reandroid/apk/ApkModuleEncoder.java | 17 ++++++-- .../reandroid/apk/ApkModuleJsonEncoder.java | 5 +-- .../reandroid/apk/ApkModuleRawEncoder.java | 36 ++++++++++++++-- .../apk/SingleJsonTableInputSource.java | 41 +++++++------------ .../apk/SplitJsonTableInputSource.java | 36 +++++++--------- 6 files changed, 82 insertions(+), 69 deletions(-) diff --git a/src/main/java/com/reandroid/apk/ApkModule.java b/src/main/java/com/reandroid/apk/ApkModule.java index 44f3f2a06..03cb474ac 100644 --- a/src/main/java/com/reandroid/apk/ApkModule.java +++ b/src/main/java/com/reandroid/apk/ApkModule.java @@ -23,8 +23,6 @@ import com.reandroid.archive.writer.ApkStreamWriter; import com.reandroid.arsc.ApkFile; import com.reandroid.arsc.array.PackageArray; -import com.reandroid.arsc.base.Block; -import com.reandroid.arsc.chunk.Chunk; import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.chunk.TableBlock; import com.reandroid.arsc.chunk.TypeBlock; @@ -351,7 +349,7 @@ private Integer safeParseInteger(String versionString) { } } - public void setPreferredFramework(Integer version) throws IOException { + public void setPreferredFramework(Integer version) { if(version != null && version.equals(preferredFramework)){ return; } @@ -719,6 +717,7 @@ public void setManifest(AndroidManifestBlock manifestBlock){ ZipEntryMap archive = getZipEntryMap(); if(manifestBlock==null){ mManifestBlock = null; + mManifestOriginalSource = null; archive.remove(AndroidManifestBlock.FILE_NAME); return; } @@ -734,6 +733,7 @@ public void setTableBlock(TableBlock tableBlock){ ZipEntryMap archive = getZipEntryMap(); if(tableBlock == null){ mTableBlock = null; + mTableOriginalSource = null; archive.remove(TableBlock.FILE_NAME); return; } @@ -964,15 +964,11 @@ public TableStringPool getVolatileTableStringPool() throws IOException{ } TableBlock loadTableBlock() throws IOException { InputSource inputSource = getInputSource(TableBlock.FILE_NAME); - if(inputSource==null){ + if(inputSource == null){ throw new IOException("Entry not found: "+TableBlock.FILE_NAME); } TableBlock tableBlock; - if(inputSource instanceof SplitJsonTableInputSource){ - tableBlock=((SplitJsonTableInputSource)inputSource).getTableBlock(); - }else if(inputSource instanceof SingleJsonTableInputSource){ - tableBlock=((SingleJsonTableInputSource)inputSource).getTableBlock(); - }else if(inputSource instanceof BlockInputSource){ + if(inputSource instanceof BlockInputSource){ tableBlock = (TableBlock) ((BlockInputSource) inputSource).getBlock(); }else { setTableOriginalSource(inputSource); @@ -983,7 +979,7 @@ TableBlock loadTableBlock() throws IOException { BlockInputSource blockInputSource=new BlockInputSource<>(inputSource.getName(), tableBlock); blockInputSource.setMethod(inputSource.getMethod()); blockInputSource.setSort(inputSource.getSort()); - zipEntryMap.add(blockInputSource); + getZipEntryMap().add(blockInputSource); tableBlock.setApkFile(this); return tableBlock; } diff --git a/src/main/java/com/reandroid/apk/ApkModuleEncoder.java b/src/main/java/com/reandroid/apk/ApkModuleEncoder.java index 2dac50e50..cca693e77 100644 --- a/src/main/java/com/reandroid/apk/ApkModuleEncoder.java +++ b/src/main/java/com/reandroid/apk/ApkModuleEncoder.java @@ -49,7 +49,8 @@ public void scanDirectory(File mainDirectory) throws IOException{ sortFiles(); refreshTable(); dropEmptyManifest(); - droNullTableBlock(); + dropNullTableBlock(); + onScanDirectoryComplete(); } public void encodeBinaryManifest(File mainDirectory){ File file = new File(mainDirectory, AndroidManifestBlock.FILE_NAME_BIN); @@ -65,6 +66,8 @@ public void encodeBinaryManifest(File mainDirectory){ public abstract ApkModule getApkModule(); + void onScanDirectoryComplete(){ + } void refreshTable(){ logMessage("Refreshing resource table ..."); getApkModule().refreshTable(); @@ -83,15 +86,21 @@ private void dropEmptyManifest(){ logMessage("Removed empty: " + AndroidManifest.FILE_NAME); } } - private void droNullTableBlock(){ + private void dropNullTableBlock(){ ApkModule apkModule = getApkModule(); if(!apkModule.hasTableBlock()){ return; } - TableBlock tableBlock = apkModule.getTableBlock(); + TableBlock loadedTableBlock = apkModule.getLoadedTableBlock(); + TableBlock tableBlock = loadedTableBlock; + if(tableBlock == null){ + tableBlock = apkModule.getTableBlock(false); + } if(tableBlock.isEmpty() && tableBlock.isNull()){ apkModule.setTableBlock(null); - logMessage("Removed null: " + TableBlock.FILE_NAME); + logMessage("Removed empty: " + TableBlock.FILE_NAME); + }else if(loadedTableBlock == null){ + apkModule.discardTableBlockChanges(); } } private void sortFiles(){ diff --git a/src/main/java/com/reandroid/apk/ApkModuleJsonEncoder.java b/src/main/java/com/reandroid/apk/ApkModuleJsonEncoder.java index 73165172a..7bf92ab30 100644 --- a/src/main/java/com/reandroid/apk/ApkModuleJsonEncoder.java +++ b/src/main/java/com/reandroid/apk/ApkModuleJsonEncoder.java @@ -22,7 +22,8 @@ import java.io.IOException; import java.util.List; -public class ApkModuleJsonEncoder extends ApkModuleEncoder{ +public class ApkModuleJsonEncoder extends ApkModuleEncoder { + private ApkModule apkModule; public ApkModuleJsonEncoder(){ super(); @@ -89,7 +90,6 @@ private boolean scanTableSplitJson(File mainDirectory) { return false; } SplitJsonTableInputSource inputSource = new SplitJsonTableInputSource(resourcesDir); - inputSource.setApkLogger(getApkLogger()); getApkModule().add(inputSource); return true; } @@ -101,7 +101,6 @@ private boolean scanTableSingleJson(File mainDirectory) { } SingleJsonTableInputSource inputSource = SingleJsonTableInputSource .fromFile(resourcesDir, file); - inputSource.setApkLogger(getApkLogger()); getApkModule().add(inputSource); return true; } diff --git a/src/main/java/com/reandroid/apk/ApkModuleRawEncoder.java b/src/main/java/com/reandroid/apk/ApkModuleRawEncoder.java index c8a8e4a0a..8a5a9104a 100644 --- a/src/main/java/com/reandroid/apk/ApkModuleRawEncoder.java +++ b/src/main/java/com/reandroid/apk/ApkModuleRawEncoder.java @@ -24,12 +24,23 @@ import java.io.File; public class ApkModuleRawEncoder extends ApkModuleEncoder { + private final ApkModule apkModule; + private boolean mKeepOriginal; + public ApkModuleRawEncoder(){ ZipEntryMap zipEntryMap = new ZipEntryMap(); String name = "encoded_raw" + String.valueOf(zipEntryMap.hashCode()).substring(1); this.apkModule = new ApkModule(name, zipEntryMap); } + + public void setKeepOriginal(boolean keepOriginal) { + this.mKeepOriginal = keepOriginal; + } + public boolean isKeepOriginal() { + return mKeepOriginal; + } + @Override public void buildResources(File mainDirectory) { addTableBlock(mainDirectory); @@ -44,13 +55,17 @@ public void encodeBinaryManifest(File mainDirectory){ if(!file.isFile()){ file = new File(mainDirectory, AndroidManifestBlock.FILE_NAME); if(!file.isFile() || !AndroidManifestBlock.isResXmlBlock(file)){ - logMessage("Warn: File not found: " + AndroidManifestBlock.FILE_NAME_BIN); + logMessage("WARN: Missing file " + AndroidManifestBlock.FILE_NAME_BIN); return; } } logMessage("Loaded binary manifest: " + file.getName()); FileInputSource inputSource = new FileInputSource(file, AndroidManifestBlock.FILE_NAME); - getApkModule().add(inputSource); + ApkModule apkModule = getApkModule(); + apkModule.add(inputSource); + if(isKeepOriginal()){ + apkModule.discardManifestChanges(); + } } private void addTableBlock(File mainDirectory){ File file = new File(mainDirectory, TableBlock.FILE_NAME); @@ -58,13 +73,26 @@ private void addTableBlock(File mainDirectory){ logMessage("Warn: File not found: " + TableBlock.FILE_NAME); return; } + getApkModule().setLoadDefaultFramework(false); InputSource inputSource = new FileInputSource(file, TableBlock.FILE_NAME); - getApkModule().add(inputSource); + ApkModule apkModule = getApkModule(); + apkModule.add(inputSource); + if(isKeepOriginal()){ + apkModule.discardManifestChanges(); + } } @Override void refreshTable(){ - if(getApkModule().getLoadedTableBlock() != null){ + if(!isKeepOriginal() && getApkModule().getLoadedTableBlock() != null){ super.refreshTable(); } } + @Override + void onScanDirectoryComplete() { + if(isKeepOriginal()){ + ApkModule apkModule = getApkModule(); + apkModule.discardTableBlockChanges(); + apkModule.discardManifestChanges(); + } + } } diff --git a/src/main/java/com/reandroid/apk/SingleJsonTableInputSource.java b/src/main/java/com/reandroid/apk/SingleJsonTableInputSource.java index ca94425f6..e959d3f79 100644 --- a/src/main/java/com/reandroid/apk/SingleJsonTableInputSource.java +++ b/src/main/java/com/reandroid/apk/SingleJsonTableInputSource.java @@ -15,6 +15,7 @@ */ package com.reandroid.apk; +import com.reandroid.archive.BlockInputSource; import com.reandroid.archive.FileInputSource; import com.reandroid.archive.InputSource; import com.reandroid.arsc.chunk.TableBlock; @@ -23,14 +24,24 @@ import java.io.*; -public class SingleJsonTableInputSource extends InputSource { +public class SingleJsonTableInputSource extends BlockInputSource { + private final InputSource inputSource; private TableBlock mCache; - private APKLogger apkLogger; + public SingleJsonTableInputSource(InputSource inputSource) { - super(TableBlock.FILE_NAME); + super(TableBlock.FILE_NAME, null); this.inputSource = inputSource; } + + @Override + public TableBlock getBlock() { + try { + return getTableBlock(); + } catch (IOException exception) { + throw new RuntimeException(exception); + } + } @Override public long write(OutputStream outputStream) throws IOException { return getTableBlock().writeBytes(outputStream); @@ -55,8 +66,7 @@ public TableBlock getTableBlock() throws IOException{ if(mCache != null){ return mCache; } - logMessage("Building resources table: " + inputSource.getAlias()); - TableBlock tableBlock=newInstance(); + TableBlock tableBlock = new TableBlock(); InputStream inputStream = inputSource.openStream(); try{ JsonStringPoolBuilder poolBuilder = new JsonStringPoolBuilder(); @@ -70,30 +80,9 @@ public TableBlock getTableBlock() throws IOException{ mCache = tableBlock; return tableBlock; } - TableBlock newInstance(){ - return new TableBlock(); - } public static SingleJsonTableInputSource fromFile(File rootDir, File jsonFile){ String path = ApkUtil.jsonToArchiveResourcePath(rootDir, jsonFile); FileInputSource fileInputSource = new FileInputSource(jsonFile, path); return new SingleJsonTableInputSource(fileInputSource); } - void setApkLogger(APKLogger logger) { - this.apkLogger = logger; - } - private void logMessage(String msg) { - if(apkLogger!=null){ - apkLogger.logMessage(msg); - } - } - private void logError(String msg, Throwable tr) { - if(apkLogger!=null){ - apkLogger.logError(msg, tr); - } - } - private void logVerbose(String msg) { - if(apkLogger!=null){ - apkLogger.logVerbose(msg); - } - } } diff --git a/src/main/java/com/reandroid/apk/SplitJsonTableInputSource.java b/src/main/java/com/reandroid/apk/SplitJsonTableInputSource.java index 0c4f7fbab..7e450210b 100644 --- a/src/main/java/com/reandroid/apk/SplitJsonTableInputSource.java +++ b/src/main/java/com/reandroid/apk/SplitJsonTableInputSource.java @@ -15,19 +15,29 @@ */ package com.reandroid.apk; -import com.reandroid.archive.InputSource; +import com.reandroid.archive.BlockInputSource; import com.reandroid.arsc.chunk.TableBlock; import java.io.*; -public class SplitJsonTableInputSource extends InputSource { +public class SplitJsonTableInputSource extends BlockInputSource { private final File resourcesDirectory; private TableBlock mCache; - private APKLogger apkLogger; + public SplitJsonTableInputSource(File resourcesDirectory) { - super(TableBlock.FILE_NAME); + super(TableBlock.FILE_NAME, null); this.resourcesDirectory =resourcesDirectory; } + + @Override + public TableBlock getBlock() { + try { + return getTableBlock(); + } catch (IOException exception) { + throw new RuntimeException(exception); + } + } + @Override public long write(OutputStream outputStream) throws IOException { return getTableBlock().writeBytes(outputStream); @@ -57,22 +67,4 @@ public TableBlock getTableBlock() throws IOException { mCache = tableBlock; return tableBlock; } - public void setApkLogger(APKLogger logger) { - this.apkLogger = logger; - } - void logMessage(String msg) { - if(apkLogger!=null){ - apkLogger.logMessage(msg); - } - } - private void logError(String msg, Throwable tr) { - if(apkLogger!=null){ - apkLogger.logError(msg, tr); - } - } - private void logVerbose(String msg) { - if(apkLogger!=null){ - apkLogger.logVerbose(msg); - } - } }