mirror of
https://github.com/revanced/ARSCLib.git
synced 2025-05-11 19:24:26 +02:00
make ApkFile as parent
This commit is contained in:
parent
76aa8ec7d7
commit
71437db825
@ -16,6 +16,7 @@
|
||||
package com.reandroid.apk;
|
||||
|
||||
import com.reandroid.archive.*;
|
||||
import com.reandroid.arsc.ApkFile;
|
||||
import com.reandroid.arsc.array.PackageArray;
|
||||
import com.reandroid.arsc.chunk.Chunk;
|
||||
import com.reandroid.arsc.chunk.PackageBlock;
|
||||
@ -34,12 +35,13 @@ import com.reandroid.xml.XMLDocument;
|
||||
import com.reandroid.xml.XMLException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
public class ApkModule {
|
||||
public class ApkModule implements ApkFile {
|
||||
private final String moduleName;
|
||||
private final APKArchive apkArchive;
|
||||
private boolean loadDefaultFramework = true;
|
||||
@ -91,9 +93,7 @@ public class ApkModule {
|
||||
return results;
|
||||
}
|
||||
public XMLDocument decodeXMLFile(String path) throws IOException, XMLException {
|
||||
InputSource inputSource = apkArchive.getInputSource(path);
|
||||
ResXmlDocument resXmlDocument = new ResXmlDocument();
|
||||
resXmlDocument.readBytes(inputSource.openStream());
|
||||
ResXmlDocument resXmlDocument = loadResXmlDocument(path);
|
||||
AndroidManifestBlock manifestBlock = getAndroidManifestBlock();
|
||||
int pkgId = manifestBlock.guessCurrentPackageId();
|
||||
return resXmlDocument.decodeToXml(getTableEntryStore(), pkgId);
|
||||
@ -124,10 +124,10 @@ public class ApkModule {
|
||||
AndroidManifestBlock manifestBlock;
|
||||
try {
|
||||
manifestBlock=getAndroidManifestBlock();
|
||||
} catch (IOException ignored) {
|
||||
return manifestBlock.getMainActivity()!=null;
|
||||
} catch (Exception ignored) {
|
||||
return false;
|
||||
}
|
||||
return manifestBlock.getMainActivity()!=null;
|
||||
}
|
||||
public String getModuleName(){
|
||||
return moduleName;
|
||||
@ -315,35 +315,61 @@ public class ApkModule {
|
||||
return mManifestBlock!=null
|
||||
|| getApkArchive().getInputSource(AndroidManifestBlock.FILE_NAME)!=null;
|
||||
}
|
||||
public AndroidManifestBlock getAndroidManifestBlock() throws IOException {
|
||||
public boolean hasTableBlock(){
|
||||
return mTableBlock!=null
|
||||
|| getApkArchive().getInputSource(TableBlock.FILE_NAME)!=null;
|
||||
}
|
||||
public AndroidManifestBlock getAndroidManifestBlock() {
|
||||
if(mManifestBlock!=null){
|
||||
return mManifestBlock;
|
||||
}
|
||||
APKArchive archive=getApkArchive();
|
||||
InputSource inputSource = archive.getInputSource(AndroidManifestBlock.FILE_NAME);
|
||||
if(inputSource==null){
|
||||
throw new IOException("Entry not found: "+AndroidManifestBlock.FILE_NAME);
|
||||
return null;
|
||||
}
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
inputStream = inputSource.openStream();
|
||||
AndroidManifestBlock manifestBlock=AndroidManifestBlock.load(inputStream);
|
||||
inputStream.close();
|
||||
BlockInputSource<AndroidManifestBlock> blockInputSource=new BlockInputSource<>(inputSource.getName(),manifestBlock);
|
||||
blockInputSource.setSort(inputSource.getSort());
|
||||
blockInputSource.setMethod(inputSource.getMethod());
|
||||
archive.add(blockInputSource);
|
||||
manifestBlock.setApkFile(this);
|
||||
mManifestBlock = manifestBlock;
|
||||
} catch (IOException exception) {
|
||||
throw new IllegalArgumentException(exception);
|
||||
}
|
||||
InputStream inputStream = inputSource.openStream();
|
||||
AndroidManifestBlock manifestBlock=AndroidManifestBlock.load(inputStream);
|
||||
inputStream.close();
|
||||
BlockInputSource<AndroidManifestBlock> blockInputSource=new BlockInputSource<>(inputSource.getName(),manifestBlock);
|
||||
blockInputSource.setSort(inputSource.getSort());
|
||||
blockInputSource.setMethod(inputSource.getMethod());
|
||||
archive.add(blockInputSource);
|
||||
mManifestBlock=manifestBlock;
|
||||
return mManifestBlock;
|
||||
}
|
||||
public boolean hasTableBlock(){
|
||||
return mTableBlock!=null
|
||||
|| getApkArchive().getInputSource(TableBlock.FILE_NAME)!=null;
|
||||
}
|
||||
public TableBlock getTableBlock() throws IOException {
|
||||
@Override
|
||||
public TableBlock getTableBlock() {
|
||||
if(mTableBlock==null){
|
||||
mTableBlock=loadTableBlock();
|
||||
if(!hasTableBlock()){
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
mTableBlock = loadTableBlock();
|
||||
} catch (IOException exception) {
|
||||
throw new IllegalArgumentException(exception);
|
||||
}
|
||||
}
|
||||
return mTableBlock;
|
||||
}
|
||||
@Override
|
||||
public ResXmlDocument loadResXmlDocument(String path) throws IOException{
|
||||
InputSource inputSource = getApkArchive().getInputSource(path);
|
||||
if(inputSource==null){
|
||||
throw new FileNotFoundException("No such file in apk: " + path);
|
||||
}
|
||||
ResXmlDocument resXmlDocument = new ResXmlDocument();
|
||||
resXmlDocument.setApkFile(this);
|
||||
resXmlDocument.readBytes(inputSource.openStream());
|
||||
return resXmlDocument;
|
||||
}
|
||||
|
||||
// If we need TableStringPool only, this loads pool without
|
||||
// loading packages and other chunk blocks for faster and less memory usage
|
||||
public TableStringPool getVolatileTableStringPool() throws IOException{
|
||||
@ -391,6 +417,7 @@ public class ApkModule {
|
||||
blockInputSource.setMethod(inputSource.getMethod());
|
||||
blockInputSource.setSort(inputSource.getSort());
|
||||
archive.add(blockInputSource);
|
||||
tableBlock.setApkFile(this);
|
||||
return tableBlock;
|
||||
}
|
||||
public APKArchive getApkArchive() {
|
||||
|
@ -124,14 +124,14 @@ import java.util.*;
|
||||
resFile.getInputSource().write(outputStream);
|
||||
outputStream.close();
|
||||
|
||||
addDecodedEntry(resFile.pickOne());
|
||||
addDecodedEntry(entry);
|
||||
}
|
||||
private void decodeResXml(EntryStore entryStore, File outDir, ResFile resFile)
|
||||
throws IOException, XMLException{
|
||||
Entry entry =resFile.pickOne();
|
||||
PackageBlock packageBlock= entry.getPackageBlock();
|
||||
ResXmlDocument resXmlDocument =new ResXmlDocument();
|
||||
resXmlDocument.readBytes(resFile.getInputSource().openStream());
|
||||
ResXmlDocument resXmlDocument = apkModule.loadResXmlDocument(
|
||||
resFile.getInputSource().getName());
|
||||
|
||||
File pkgDir=new File(outDir, getPackageDirName(packageBlock));
|
||||
File resDir=new File(pkgDir, ApkUtil.RES_DIR_NAME);
|
||||
|
@ -55,7 +55,8 @@ public class FrameworkApk extends ApkModule{
|
||||
archive.add(tableSource);
|
||||
archive.add(manifestSource);
|
||||
}
|
||||
public FrameworkTable getTableBlock() throws IOException {
|
||||
@Override
|
||||
public FrameworkTable getTableBlock() {
|
||||
return (FrameworkTable) super.getTableBlock();
|
||||
}
|
||||
@Override
|
||||
@ -67,6 +68,7 @@ public class FrameworkApk extends ApkModule{
|
||||
}
|
||||
InputStream inputStream = inputSource.openStream();
|
||||
FrameworkTable frameworkTable=FrameworkTable.load(inputStream);
|
||||
frameworkTable.setApkFile(this);
|
||||
if(hasAndroidManifestBlock()){
|
||||
optimizeTable(frameworkTable);
|
||||
}
|
||||
|
@ -129,19 +129,6 @@ public class ResFile {
|
||||
}
|
||||
return mBinXml;
|
||||
}
|
||||
public boolean dumpToJson(File rootDir) throws IOException {
|
||||
if(!isBinaryXml()){
|
||||
return false;
|
||||
}
|
||||
String fileName=getFilePath()+ApkUtil.JSON_FILE_EXTENSION;
|
||||
fileName=fileName.replace('/', File.separatorChar);
|
||||
File file=new File(rootDir, fileName);
|
||||
ResXmlDocument resXmlDocument =new ResXmlDocument();
|
||||
resXmlDocument.readBytes(getInputSource().openStream());
|
||||
JSONObject jsonObject= resXmlDocument.toJson();
|
||||
jsonObject.write(file);
|
||||
return true;
|
||||
}
|
||||
public File buildOutFile(File dir){
|
||||
String path=getFilePath();
|
||||
path=path.replace('/', File.separatorChar);
|
||||
|
28
src/main/java/com/reandroid/arsc/ApkFile.java
Normal file
28
src/main/java/com/reandroid/arsc/ApkFile.java
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.arsc;
|
||||
|
||||
import com.reandroid.arsc.chunk.TableBlock;
|
||||
import com.reandroid.arsc.chunk.xml.AndroidManifestBlock;
|
||||
import com.reandroid.arsc.chunk.xml.ResXmlDocument;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface ApkFile {
|
||||
AndroidManifestBlock getAndroidManifestBlock();
|
||||
TableBlock getTableBlock();
|
||||
ResXmlDocument loadResXmlDocument(String path) throws IOException;
|
||||
}
|
@ -1,7 +1,10 @@
|
||||
package com.reandroid.arsc.chunk;
|
||||
|
||||
import com.reandroid.arsc.ApkFile;
|
||||
import com.reandroid.arsc.pool.StringPool;
|
||||
|
||||
public interface MainChunk {
|
||||
public StringPool<?> getStringPool();
|
||||
StringPool<?> getStringPool();
|
||||
ApkFile getApkFile();
|
||||
void setApkFile(ApkFile apkFile);
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.reandroid.arsc.chunk;
|
||||
|
||||
import com.reandroid.arsc.ApkFile;
|
||||
import com.reandroid.arsc.BuildInfo;
|
||||
import com.reandroid.arsc.array.PackageArray;
|
||||
import com.reandroid.arsc.group.EntryGroup;
|
||||
@ -22,24 +23,23 @@ import com.reandroid.arsc.header.HeaderBlock;
|
||||
import com.reandroid.arsc.header.InfoHeader;
|
||||
import com.reandroid.arsc.header.TableHeader;
|
||||
import com.reandroid.arsc.io.BlockReader;
|
||||
import com.reandroid.arsc.pool.StringPool;
|
||||
import com.reandroid.arsc.pool.TableStringPool;
|
||||
import com.reandroid.arsc.value.StagedAliasEntry;
|
||||
import com.reandroid.common.EntryStore;
|
||||
import com.reandroid.common.Frameworks;
|
||||
import com.reandroid.json.JSONConvert;
|
||||
import com.reandroid.json.JSONArray;
|
||||
import com.reandroid.json.JSONObject;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
public class TableBlock extends Chunk<TableHeader>
|
||||
implements MainChunk, JSONConvert<JSONObject> {
|
||||
public class TableBlock extends Chunk<TableHeader>
|
||||
implements MainChunk, JSONConvert<JSONObject>, EntryStore {
|
||||
private final TableStringPool mTableStringPool;
|
||||
private final PackageArray mPackageArray;
|
||||
private final Set<TableBlock> mFrameWorks=new HashSet<>();
|
||||
private ApkFile mApkFile;
|
||||
public TableBlock() {
|
||||
super(new TableHeader(), 2);
|
||||
TableHeader header = getHeaderBlock();
|
||||
@ -61,6 +61,14 @@ public class TableBlock extends Chunk<TableHeader>
|
||||
public TableStringPool getStringPool() {
|
||||
return mTableStringPool;
|
||||
}
|
||||
@Override
|
||||
public ApkFile getApkFile(){
|
||||
return mApkFile;
|
||||
}
|
||||
@Override
|
||||
public void setApkFile(ApkFile apkFile){
|
||||
this.mApkFile = apkFile;
|
||||
}
|
||||
public TableStringPool getTableStringPool(){
|
||||
return mTableStringPool;
|
||||
}
|
||||
@ -132,16 +140,23 @@ public class TableBlock extends Chunk<TableHeader>
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append(super.toString());
|
||||
builder.append(", packages=");
|
||||
int pkgCount=mPackageArray.childesCount();
|
||||
builder.append(pkgCount);
|
||||
return builder.toString();
|
||||
}
|
||||
public EntryGroup search(int resourceId){
|
||||
if(resourceId==0){
|
||||
return null;
|
||||
}
|
||||
EntryGroup entryGroup = searchLocal(resourceId);
|
||||
if(entryGroup!=null){
|
||||
return entryGroup;
|
||||
}
|
||||
for(TableBlock tableBlock:getFrameWorks()){
|
||||
entryGroup = tableBlock.search(resourceId);
|
||||
if(entryGroup!=null){
|
||||
return entryGroup;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private EntryGroup searchLocal(int resourceId){
|
||||
if(resourceId==0){
|
||||
return null;
|
||||
}
|
||||
@ -156,14 +171,36 @@ public class TableBlock extends Chunk<TableHeader>
|
||||
return entryGroup;
|
||||
}
|
||||
}
|
||||
for(TableBlock tableBlock:getFrameWorks()){
|
||||
EntryGroup entryGroup = tableBlock.search(resourceId);
|
||||
if(entryGroup!=null){
|
||||
return entryGroup;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public Collection<EntryGroup> getEntryGroups(int resourceId) {
|
||||
List<EntryGroup> results = new ArrayList<>();
|
||||
EntryGroup entryGroup = searchLocal(resourceId);
|
||||
if(entryGroup!=null){
|
||||
results.add(entryGroup);
|
||||
}
|
||||
for(TableBlock framework:getFrameWorks()){
|
||||
results.addAll(framework.getEntryGroups(resourceId));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
@Override
|
||||
public EntryGroup getEntryGroup(int resourceId) {
|
||||
return search(resourceId);
|
||||
}
|
||||
@Override
|
||||
public Collection<PackageBlock> getPackageBlocks(int packageId) {
|
||||
List<PackageBlock> results=new ArrayList<>();
|
||||
PackageBlock packageBlock = getPackageBlockById(packageId);
|
||||
if(packageBlock!=null){
|
||||
results.add(packageBlock);
|
||||
}
|
||||
for(TableBlock tableBlock:getFrameWorks()){
|
||||
results.addAll(tableBlock.getPackageBlocks(packageId));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
public int searchResourceIdAlias(int resourceId){
|
||||
for(PackageBlock packageBlock:listPackages()){
|
||||
StagedAliasEntry stagedAliasEntry =
|
||||
@ -216,6 +253,16 @@ public class TableBlock extends Chunk<TableHeader>
|
||||
getPackageArray().merge(tableBlock.getPackageArray());
|
||||
refresh();
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append(super.toString());
|
||||
builder.append(", packages=");
|
||||
int pkgCount=mPackageArray.childesCount();
|
||||
builder.append(pkgCount);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static TableBlock loadWithAndroidFramework(InputStream inputStream) throws IOException{
|
||||
TableBlock tableBlock=load(inputStream);
|
||||
tableBlock.addFramework(Frameworks.getAndroid());
|
||||
|
@ -25,13 +25,17 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AndroidManifestBlock extends ResXmlDocument {
|
||||
private int mGuessedPackageId;
|
||||
public AndroidManifestBlock(){
|
||||
super();
|
||||
super.getStringPool().setUtf8(false);
|
||||
}
|
||||
// TODO: find a better way
|
||||
public byte guessCurrentPackageId(){
|
||||
return (byte) ((getIconResourceId()>>24) & 0xff);
|
||||
public int guessCurrentPackageId(){
|
||||
if(mGuessedPackageId == 0){
|
||||
mGuessedPackageId = ((getIconResourceId()>>24) & 0xff);
|
||||
}
|
||||
return mGuessedPackageId;
|
||||
}
|
||||
public int getIconResourceId(){
|
||||
ResXmlElement applicationElement = getApplicationElement();
|
||||
|
@ -15,10 +15,8 @@
|
||||
*/
|
||||
package com.reandroid.arsc.chunk.xml;
|
||||
|
||||
import com.reandroid.arsc.chunk.Chunk;
|
||||
import com.reandroid.arsc.chunk.ChunkType;
|
||||
import com.reandroid.arsc.chunk.MainChunk;
|
||||
import com.reandroid.arsc.chunk.ParentChunk;
|
||||
import com.reandroid.arsc.ApkFile;
|
||||
import com.reandroid.arsc.chunk.*;
|
||||
import com.reandroid.arsc.container.SingleBlockContainer;
|
||||
import com.reandroid.arsc.header.HeaderBlock;
|
||||
import com.reandroid.arsc.io.BlockReader;
|
||||
@ -45,6 +43,7 @@
|
||||
private final ResXmlIDMap mResXmlIDMap;
|
||||
private ResXmlElement mResXmlElement;
|
||||
private final SingleBlockContainer<ResXmlElement> mResXmlElementContainer;
|
||||
private ApkFile mApkFile;
|
||||
public ResXmlDocument() {
|
||||
super(new HeaderBlock(ChunkType.XML),3);
|
||||
this.mResXmlStringPool=new ResXmlStringPool(true);
|
||||
@ -169,6 +168,14 @@
|
||||
return mResXmlStringPool;
|
||||
}
|
||||
@Override
|
||||
public ApkFile getApkFile(){
|
||||
return mApkFile;
|
||||
}
|
||||
@Override
|
||||
public void setApkFile(ApkFile apkFile){
|
||||
this.mApkFile = apkFile;
|
||||
}
|
||||
@Override
|
||||
public StringPool<?> getSpecStringPool() {
|
||||
return null;
|
||||
}
|
||||
@ -224,6 +231,24 @@
|
||||
xmlElement.fromJson(json.optJSONObject(ResXmlDocument.NAME_element));
|
||||
refresh();
|
||||
}
|
||||
public XMLDocument decodeToXml() throws XMLException {
|
||||
ApkFile apkFile = getApkFile();
|
||||
if(apkFile == null){
|
||||
throw new XMLException("Null parent apk file");
|
||||
}
|
||||
int currentPackageId = 0;
|
||||
AndroidManifestBlock manifestBlock;
|
||||
if(this instanceof AndroidManifestBlock){
|
||||
manifestBlock = ((AndroidManifestBlock)this);
|
||||
}else {
|
||||
manifestBlock = apkFile.getAndroidManifestBlock();
|
||||
}
|
||||
if(manifestBlock!=null){
|
||||
currentPackageId = manifestBlock.guessCurrentPackageId();
|
||||
}
|
||||
TableBlock tableBlock = apkFile.getTableBlock();
|
||||
return decodeToXml(tableBlock, currentPackageId);
|
||||
}
|
||||
public XMLDocument decodeToXml(EntryStore entryStore, int currentPackageId) throws XMLException {
|
||||
XMLDocument xmlDocument = new XMLDocument();
|
||||
XMLElement xmlElement = getResXmlElement()
|
||||
|
Loading…
x
Reference in New Issue
Block a user