mirror of
https://github.com/revanced/Apktool.git
synced 2025-01-07 18:45:58 +01:00
code cleanup of 2014
This commit is contained in:
parent
720c08608d
commit
086139a037
File diff suppressed because it is too large
Load Diff
@ -22,18 +22,18 @@ import brut.common.BrutException;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class AndrolibException extends BrutException {
|
public class AndrolibException extends BrutException {
|
||||||
public AndrolibException() {
|
public AndrolibException() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public AndrolibException(String message) {
|
public AndrolibException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AndrolibException(String message, Throwable cause) {
|
public AndrolibException(String message, Throwable cause) {
|
||||||
super(message, cause);
|
super(message, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AndrolibException(Throwable cause) {
|
public AndrolibException(Throwable cause) {
|
||||||
super(cause);
|
super(cause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,25 +107,25 @@ public class ApkDecoder {
|
|||||||
zef.close();
|
zef.close();
|
||||||
|
|
||||||
switch (mDecodeResources) {
|
switch (mDecodeResources) {
|
||||||
case DECODE_RESOURCES_NONE:
|
case DECODE_RESOURCES_NONE:
|
||||||
mAndrolib.decodeResourcesRaw(mApkFile, outDir);
|
mAndrolib.decodeResourcesRaw(mApkFile, outDir);
|
||||||
break;
|
break;
|
||||||
case DECODE_RESOURCES_FULL:
|
case DECODE_RESOURCES_FULL:
|
||||||
mAndrolib.decodeResourcesFull(mApkFile, outDir, getResTable());
|
mAndrolib.decodeResourcesFull(mApkFile, outDir, getResTable());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if there's no resources.asrc, decode the manifest without looking
|
// if there's no resources.asrc, decode the manifest without looking
|
||||||
// up attribute references
|
// up attribute references
|
||||||
if (hasManifest()) {
|
if (hasManifest()) {
|
||||||
switch (mDecodeResources) {
|
switch (mDecodeResources) {
|
||||||
case DECODE_RESOURCES_NONE:
|
case DECODE_RESOURCES_NONE:
|
||||||
mAndrolib.decodeManifestRaw(mApkFile, outDir);
|
mAndrolib.decodeManifestRaw(mApkFile, outDir);
|
||||||
break;
|
break;
|
||||||
case DECODE_RESOURCES_FULL:
|
case DECODE_RESOURCES_FULL:
|
||||||
mAndrolib.decodeManifestFull(mApkFile, outDir,
|
mAndrolib.decodeManifestFull(mApkFile, outDir,
|
||||||
getResTable());
|
getResTable());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,128 +36,128 @@ import org.apache.commons.io.IOUtils;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResSmaliUpdater {
|
public class ResSmaliUpdater {
|
||||||
public void tagResIDs(ResTable resTable, File smaliDir)
|
public void tagResIDs(ResTable resTable, File smaliDir)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
Directory dir = null;
|
Directory dir = null;
|
||||||
try {
|
try {
|
||||||
dir = new FileDirectory(smaliDir);
|
dir = new FileDirectory(smaliDir);
|
||||||
} catch (DirectoryException ex) {
|
} catch (DirectoryException ex) {
|
||||||
throw new AndrolibException("Could not tag res IDs", ex);
|
throw new AndrolibException("Could not tag res IDs", ex);
|
||||||
}
|
}
|
||||||
for (String fileName : dir.getFiles(true)) {
|
for (String fileName : dir.getFiles(true)) {
|
||||||
try {
|
try {
|
||||||
tagResIdsForFile(resTable, dir, fileName);
|
tagResIdsForFile(resTable, dir, fileName);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new AndrolibException("Could not tag resIDs for file: "
|
throw new AndrolibException("Could not tag resIDs for file: "
|
||||||
+ fileName, ex);
|
+ fileName, ex);
|
||||||
} catch (DirectoryException ex) {
|
} catch (DirectoryException ex) {
|
||||||
throw new AndrolibException("Could not tag resIDs for file: "
|
throw new AndrolibException("Could not tag resIDs for file: "
|
||||||
+ fileName, ex);
|
+ fileName, ex);
|
||||||
} catch (AndrolibException ex) {
|
} catch (AndrolibException ex) {
|
||||||
throw new AndrolibException("Could not tag resIDs for file: "
|
throw new AndrolibException("Could not tag resIDs for file: "
|
||||||
+ fileName, ex);
|
+ fileName, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateResIDs(ResTable resTable, File smaliDir)
|
public void updateResIDs(ResTable resTable, File smaliDir)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
try {
|
try {
|
||||||
Directory dir = new FileDirectory(smaliDir);
|
Directory dir = new FileDirectory(smaliDir);
|
||||||
for (String fileName : dir.getFiles(true)) {
|
for (String fileName : dir.getFiles(true)) {
|
||||||
Iterator<String> it = IOUtils.readLines(
|
Iterator<String> it = IOUtils.readLines(
|
||||||
dir.getFileInput(fileName)).iterator();
|
dir.getFileInput(fileName)).iterator();
|
||||||
PrintWriter out = new PrintWriter(dir.getFileOutput(fileName));
|
PrintWriter out = new PrintWriter(dir.getFileOutput(fileName));
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
String line = it.next();
|
String line = it.next();
|
||||||
out.println(line);
|
out.println(line);
|
||||||
Matcher m1 = RES_NAME_PATTERN.matcher(line);
|
Matcher m1 = RES_NAME_PATTERN.matcher(line);
|
||||||
if (!m1.matches()) {
|
if (!m1.matches()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Matcher m2 = RES_ID_PATTERN.matcher(it.next());
|
Matcher m2 = RES_ID_PATTERN.matcher(it.next());
|
||||||
if (!m2.matches()) {
|
if (!m2.matches()) {
|
||||||
throw new AndrolibException();
|
throw new AndrolibException();
|
||||||
}
|
}
|
||||||
int resID = resTable.getPackage(m1.group(1))
|
int resID = resTable.getPackage(m1.group(1))
|
||||||
.getType(m1.group(2)).getResSpec(m1.group(3))
|
.getType(m1.group(2)).getResSpec(m1.group(3))
|
||||||
.getId().id;
|
.getId().id;
|
||||||
if (m2.group(1) != null) {
|
if (m2.group(1) != null) {
|
||||||
out.println(String.format(RES_ID_FORMAT_FIELD,
|
out.println(String.format(RES_ID_FORMAT_FIELD,
|
||||||
m2.group(1), resID));
|
m2.group(1), resID));
|
||||||
} else {
|
} else {
|
||||||
out.println(String.format(RES_ID_FORMAT_CONST,
|
out.println(String.format(RES_ID_FORMAT_CONST,
|
||||||
m2.group(2), resID));
|
m2.group(2), resID));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.close();
|
out.close();
|
||||||
}
|
}
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new AndrolibException("Could not tag res IDs for: "
|
throw new AndrolibException("Could not tag res IDs for: "
|
||||||
+ smaliDir.getAbsolutePath(), ex);
|
+ smaliDir.getAbsolutePath(), ex);
|
||||||
} catch (DirectoryException ex) {
|
} catch (DirectoryException ex) {
|
||||||
throw new AndrolibException("Could not tag res IDs for: "
|
throw new AndrolibException("Could not tag res IDs for: "
|
||||||
+ smaliDir.getAbsolutePath(), ex);
|
+ smaliDir.getAbsolutePath(), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tagResIdsForFile(ResTable resTable, Directory dir,
|
private void tagResIdsForFile(ResTable resTable, Directory dir,
|
||||||
String fileName) throws IOException, DirectoryException,
|
String fileName) throws IOException, DirectoryException,
|
||||||
AndrolibException {
|
AndrolibException {
|
||||||
Iterator<String> it = IOUtils.readLines(dir.getFileInput(fileName))
|
Iterator<String> it = IOUtils.readLines(dir.getFileInput(fileName))
|
||||||
.iterator();
|
.iterator();
|
||||||
PrintWriter out = new PrintWriter(dir.getFileOutput(fileName));
|
PrintWriter out = new PrintWriter(dir.getFileOutput(fileName));
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
String line = it.next();
|
String line = it.next();
|
||||||
if (RES_NAME_PATTERN.matcher(line).matches()) {
|
if (RES_NAME_PATTERN.matcher(line).matches()) {
|
||||||
out.println(line);
|
out.println(line);
|
||||||
out.println(it.next());
|
out.println(it.next());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Matcher m = RES_ID_PATTERN.matcher(line);
|
Matcher m = RES_ID_PATTERN.matcher(line);
|
||||||
if (m.matches()) {
|
if (m.matches()) {
|
||||||
int resID = parseResID(m.group(3));
|
int resID = parseResID(m.group(3));
|
||||||
if (resID != -1) {
|
if (resID != -1) {
|
||||||
try {
|
try {
|
||||||
ResResSpec spec = resTable.getResSpec(resID);
|
ResResSpec spec = resTable.getResSpec(resID);
|
||||||
out.println(String.format(RES_NAME_FORMAT,
|
out.println(String.format(RES_NAME_FORMAT,
|
||||||
spec.getFullName()));
|
spec.getFullName()));
|
||||||
} catch (UndefinedResObject ex) {
|
} catch (UndefinedResObject ex) {
|
||||||
if (!R_FILE_PATTERN.matcher(fileName).matches()) {
|
if (!R_FILE_PATTERN.matcher(fileName).matches()) {
|
||||||
LOGGER.warning(String.format(
|
LOGGER.warning(String.format(
|
||||||
"Undefined resource spec in %s: 0x%08x",
|
"Undefined resource spec in %s: 0x%08x",
|
||||||
fileName, resID));
|
fileName, resID));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.println(line);
|
out.println(line);
|
||||||
}
|
}
|
||||||
out.close();
|
out.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int parseResID(String resIDHex) {
|
private int parseResID(String resIDHex) {
|
||||||
if (resIDHex.endsWith("ff")) {
|
if (resIDHex.endsWith("ff")) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
int resID = Integer.valueOf(resIDHex, 16);
|
int resID = Integer.valueOf(resIDHex, 16);
|
||||||
if (resIDHex.length() == 4) {
|
if (resIDHex.length() == 4) {
|
||||||
resID = resID << 16;
|
resID = resID << 16;
|
||||||
}
|
}
|
||||||
return resID;
|
return resID;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final static String RES_ID_FORMAT_FIELD = ".field %s:I = 0x%08x";
|
private final static String RES_ID_FORMAT_FIELD = ".field %s:I = 0x%08x";
|
||||||
private final static String RES_ID_FORMAT_CONST = " const %s, 0x%08x";
|
private final static String RES_ID_FORMAT_CONST = " const %s, 0x%08x";
|
||||||
private final static Pattern RES_ID_PATTERN = Pattern
|
private final static Pattern RES_ID_PATTERN = Pattern
|
||||||
.compile("^(?:\\.field (.+?):I =| const(?:|/(?:|high)16) ([pv]\\d+?),) 0x(7[a-f]0[1-9a-f](?:|[0-9a-f]{4}))$");
|
.compile("^(?:\\.field (.+?):I =| const(?:|/(?:|high)16) ([pv]\\d+?),) 0x(7[a-f]0[1-9a-f](?:|[0-9a-f]{4}))$");
|
||||||
private final static String RES_NAME_FORMAT = "# APKTOOL/RES_NAME: %s";
|
private final static String RES_NAME_FORMAT = "# APKTOOL/RES_NAME: %s";
|
||||||
private final static Pattern RES_NAME_PATTERN = Pattern
|
private final static Pattern RES_NAME_PATTERN = Pattern
|
||||||
.compile("^# APKTOOL/RES_NAME: ([a-zA-Z0-9.]+):([a-z]+)/([a-zA-Z0-9._]+)$");
|
.compile("^# APKTOOL/RES_NAME: ([a-zA-Z0-9.]+):([a-z]+)/([a-zA-Z0-9._]+)$");
|
||||||
|
|
||||||
private final static Pattern R_FILE_PATTERN = Pattern
|
private final static Pattern R_FILE_PATTERN = Pattern
|
||||||
.compile(".*R\\$[a-z]+\\.smali$");
|
.compile(".*R\\$[a-z]+\\.smali$");
|
||||||
|
|
||||||
private final static Logger LOGGER = Logger.getLogger(ResSmaliUpdater.class
|
private final static Logger LOGGER = Logger.getLogger(ResSmaliUpdater.class
|
||||||
.getName());
|
.getName());
|
||||||
}
|
}
|
||||||
|
@ -24,49 +24,49 @@ import java.util.*;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResConfig {
|
public class ResConfig {
|
||||||
private final ResConfigFlags mFlags;
|
private final ResConfigFlags mFlags;
|
||||||
private final Map<ResResSpec, ResResource> mResources = new LinkedHashMap<ResResSpec, ResResource>();
|
private final Map<ResResSpec, ResResource> mResources = new LinkedHashMap<ResResSpec, ResResource>();
|
||||||
|
|
||||||
public ResConfig(ResConfigFlags flags) {
|
public ResConfig(ResConfigFlags flags) {
|
||||||
this.mFlags = flags;
|
this.mFlags = flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<ResResource> listResources() {
|
public Set<ResResource> listResources() {
|
||||||
return new LinkedHashSet<ResResource>(mResources.values());
|
return new LinkedHashSet<ResResource>(mResources.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResResource getResource(ResResSpec spec) throws AndrolibException {
|
public ResResource getResource(ResResSpec spec) throws AndrolibException {
|
||||||
ResResource res = mResources.get(spec);
|
ResResource res = mResources.get(spec);
|
||||||
if (res == null) {
|
if (res == null) {
|
||||||
throw new UndefinedResObject(String.format(
|
throw new UndefinedResObject(String.format(
|
||||||
"resource: spec=%s, config=%s", spec, this));
|
"resource: spec=%s, config=%s", spec, this));
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<ResResSpec> listResSpecs() {
|
public Set<ResResSpec> listResSpecs() {
|
||||||
return mResources.keySet();
|
return mResources.keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResConfigFlags getFlags() {
|
public ResConfigFlags getFlags() {
|
||||||
return mFlags;
|
return mFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addResource(ResResource res) throws AndrolibException {
|
public void addResource(ResResource res) throws AndrolibException {
|
||||||
addResource(res, false);
|
addResource(res, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addResource(ResResource res, boolean overwrite)
|
public void addResource(ResResource res, boolean overwrite)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
ResResSpec spec = res.getResSpec();
|
ResResSpec spec = res.getResSpec();
|
||||||
if (mResources.put(spec, res) != null && !overwrite) {
|
if (mResources.put(spec, res) != null && !overwrite) {
|
||||||
throw new AndrolibException(String.format(
|
throw new AndrolibException(String.format(
|
||||||
"Multiple resources: spec=%s, config=%s", spec, this));
|
"Multiple resources: spec=%s, config=%s", spec, this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return mFlags.toString();
|
return mFlags.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,51 +20,51 @@ package brut.androlib.res.data;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResID {
|
public class ResID {
|
||||||
public final int package_;
|
public final int package_;
|
||||||
public final int type;
|
public final int type;
|
||||||
public final int entry;
|
public final int entry;
|
||||||
|
|
||||||
public final int id;
|
public final int id;
|
||||||
|
|
||||||
public ResID(int package_, int type, int entry) {
|
public ResID(int package_, int type, int entry) {
|
||||||
this(package_, type, entry, (package_ << 24) + (type << 16) + entry);
|
this(package_, type, entry, (package_ << 24) + (type << 16) + entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResID(int id) {
|
public ResID(int id) {
|
||||||
this(id >> 24, (id >> 16) & 0x000000ff, id & 0x0000ffff, id);
|
this(id >> 24, (id >> 16) & 0x000000ff, id & 0x0000ffff, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResID(int package_, int type, int entry, int id) {
|
public ResID(int package_, int type, int entry, int id) {
|
||||||
this.package_ = package_;
|
this.package_ = package_;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.entry = entry;
|
this.entry = entry;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("0x%08x", id);
|
return String.format("0x%08x", id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int hash = 17;
|
int hash = 17;
|
||||||
hash = 31 * hash + this.id;
|
hash = 31 * hash + this.id;
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (getClass() != obj.getClass()) {
|
if (getClass() != obj.getClass()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final ResID other = (ResID) obj;
|
final ResID other = (ResID) obj;
|
||||||
if (this.id != other.id) {
|
if (this.id != other.id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,200 +29,200 @@ import java.util.logging.Logger;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResPackage {
|
public class ResPackage {
|
||||||
private final ResTable mResTable;
|
private final ResTable mResTable;
|
||||||
private final int mId;
|
private final int mId;
|
||||||
private final String mName;
|
private final String mName;
|
||||||
private final Map<ResID, ResResSpec> mResSpecs = new LinkedHashMap<ResID, ResResSpec>();
|
private final Map<ResID, ResResSpec> mResSpecs = new LinkedHashMap<ResID, ResResSpec>();
|
||||||
private final Map<ResConfigFlags, ResConfig> mConfigs = new LinkedHashMap<ResConfigFlags, ResConfig>();
|
private final Map<ResConfigFlags, ResConfig> mConfigs = new LinkedHashMap<ResConfigFlags, ResConfig>();
|
||||||
private final Map<String, ResType> mTypes = new LinkedHashMap<String, ResType>();
|
private final Map<String, ResType> mTypes = new LinkedHashMap<String, ResType>();
|
||||||
private final Set<ResID> mSynthesizedRes = new HashSet<ResID>();
|
private final Set<ResID> mSynthesizedRes = new HashSet<ResID>();
|
||||||
|
|
||||||
private ResValueFactory mValueFactory;
|
private ResValueFactory mValueFactory;
|
||||||
|
|
||||||
public ResPackage(ResTable resTable, int id, String name) {
|
public ResPackage(ResTable resTable, int id, String name) {
|
||||||
this.mResTable = resTable;
|
this.mResTable = resTable;
|
||||||
this.mId = id;
|
this.mId = id;
|
||||||
this.mName = name;
|
this.mName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ResResSpec> listResSpecs() {
|
public List<ResResSpec> listResSpecs() {
|
||||||
return new ArrayList<ResResSpec>(mResSpecs.values());
|
return new ArrayList<ResResSpec>(mResSpecs.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasResSpec(ResID resID) {
|
public boolean hasResSpec(ResID resID) {
|
||||||
return mResSpecs.containsKey(resID);
|
return mResSpecs.containsKey(resID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResResSpec getResSpec(ResID resID) throws UndefinedResObject {
|
public ResResSpec getResSpec(ResID resID) throws UndefinedResObject {
|
||||||
ResResSpec spec = mResSpecs.get(resID);
|
ResResSpec spec = mResSpecs.get(resID);
|
||||||
if (spec == null) {
|
if (spec == null) {
|
||||||
throw new UndefinedResObject("resource spec: " + resID.toString());
|
throw new UndefinedResObject("resource spec: " + resID.toString());
|
||||||
}
|
}
|
||||||
return spec;
|
return spec;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ResConfig> getConfigs() {
|
public List<ResConfig> getConfigs() {
|
||||||
return new ArrayList<ResConfig>(mConfigs.values());
|
return new ArrayList<ResConfig>(mConfigs.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasConfig(ResConfigFlags flags) {
|
public boolean hasConfig(ResConfigFlags flags) {
|
||||||
return mConfigs.containsKey(flags);
|
return mConfigs.containsKey(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResConfig getConfig(ResConfigFlags flags) throws AndrolibException {
|
public ResConfig getConfig(ResConfigFlags flags) throws AndrolibException {
|
||||||
ResConfig config = mConfigs.get(flags);
|
ResConfig config = mConfigs.get(flags);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
throw new UndefinedResObject("config: " + flags);
|
throw new UndefinedResObject("config: " + flags);
|
||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getResSpecCount() {
|
public int getResSpecCount() {
|
||||||
return mResSpecs.size();
|
return mResSpecs.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResConfig getOrCreateConfig(ResConfigFlags flags)
|
public ResConfig getOrCreateConfig(ResConfigFlags flags)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
ResConfig config = mConfigs.get(flags);
|
ResConfig config = mConfigs.get(flags);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
config = new ResConfig(flags);
|
config = new ResConfig(flags);
|
||||||
mConfigs.put(flags, config);
|
mConfigs.put(flags, config);
|
||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ResType> listTypes() {
|
public List<ResType> listTypes() {
|
||||||
return new ArrayList<ResType>(mTypes.values());
|
return new ArrayList<ResType>(mTypes.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasType(String typeName) {
|
public boolean hasType(String typeName) {
|
||||||
return mTypes.containsKey(typeName);
|
return mTypes.containsKey(typeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResType getType(String typeName) throws AndrolibException {
|
public ResType getType(String typeName) throws AndrolibException {
|
||||||
ResType type = mTypes.get(typeName);
|
ResType type = mTypes.get(typeName);
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
throw new UndefinedResObject("type: " + typeName);
|
throw new UndefinedResObject("type: " + typeName);
|
||||||
}
|
}
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<ResResource> listFiles() {
|
public Set<ResResource> listFiles() {
|
||||||
Set<ResResource> ret = new HashSet<ResResource>();
|
Set<ResResource> ret = new HashSet<ResResource>();
|
||||||
for (ResResSpec spec : mResSpecs.values()) {
|
for (ResResSpec spec : mResSpecs.values()) {
|
||||||
for (ResResource res : spec.listResources()) {
|
for (ResResource res : spec.listResources()) {
|
||||||
if (res.getValue() instanceof ResFileValue) {
|
if (res.getValue() instanceof ResFileValue) {
|
||||||
ret.add(res);
|
ret.add(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<ResValuesFile> listValuesFiles() {
|
public Collection<ResValuesFile> listValuesFiles() {
|
||||||
Map<Duo<ResType, ResConfig>, ResValuesFile> ret = new HashMap<Duo<ResType, ResConfig>, ResValuesFile>();
|
Map<Duo<ResType, ResConfig>, ResValuesFile> ret = new HashMap<Duo<ResType, ResConfig>, ResValuesFile>();
|
||||||
for (ResResSpec spec : mResSpecs.values()) {
|
for (ResResSpec spec : mResSpecs.values()) {
|
||||||
for (ResResource res : spec.listResources()) {
|
for (ResResource res : spec.listResources()) {
|
||||||
if (res.getValue() instanceof ResValuesXmlSerializable) {
|
if (res.getValue() instanceof ResValuesXmlSerializable) {
|
||||||
ResType type = res.getResSpec().getType();
|
ResType type = res.getResSpec().getType();
|
||||||
ResConfig config = res.getConfig();
|
ResConfig config = res.getConfig();
|
||||||
Duo<ResType, ResConfig> key = new Duo<ResType, ResConfig>(
|
Duo<ResType, ResConfig> key = new Duo<ResType, ResConfig>(
|
||||||
type, config);
|
type, config);
|
||||||
ResValuesFile values = ret.get(key);
|
ResValuesFile values = ret.get(key);
|
||||||
if (values == null) {
|
if (values == null) {
|
||||||
values = new ResValuesFile(this, type, config);
|
values = new ResValuesFile(this, type, config);
|
||||||
ret.put(key, values);
|
ret.put(key, values);
|
||||||
}
|
}
|
||||||
values.addResource(res);
|
values.addResource(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret.values();
|
return ret.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResTable getResTable() {
|
public ResTable getResTable() {
|
||||||
return mResTable;
|
return mResTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getId() {
|
public int getId() {
|
||||||
return mId;
|
return mId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return mName;
|
return mName;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isSynthesized(ResID resId) {
|
boolean isSynthesized(ResID resId) {
|
||||||
return mSynthesizedRes.contains(resId);
|
return mSynthesizedRes.contains(resId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addResSpec(ResResSpec spec) throws AndrolibException {
|
public void addResSpec(ResResSpec spec) throws AndrolibException {
|
||||||
if (mResSpecs.put(spec.getId(), spec) != null) {
|
if (mResSpecs.put(spec.getId(), spec) != null) {
|
||||||
throw new AndrolibException("Multiple resource specs: " + spec);
|
throw new AndrolibException("Multiple resource specs: " + spec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addConfig(ResConfig config) throws AndrolibException {
|
public void addConfig(ResConfig config) throws AndrolibException {
|
||||||
if (mConfigs.put(config.getFlags(), config) != null) {
|
if (mConfigs.put(config.getFlags(), config) != null) {
|
||||||
throw new AndrolibException("Multiple configs: " + config);
|
throw new AndrolibException("Multiple configs: " + config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addType(ResType type) throws AndrolibException {
|
public void addType(ResType type) throws AndrolibException {
|
||||||
if (mTypes.containsKey(type.getName())) {
|
if (mTypes.containsKey(type.getName())) {
|
||||||
LOGGER.warning("Multiple types detected! " + type + " ignored!");
|
LOGGER.warning("Multiple types detected! " + type + " ignored!");
|
||||||
} else {
|
} else {
|
||||||
mTypes.put(type.getName(), type);
|
mTypes.put(type.getName(), type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addResource(ResResource res) {
|
public void addResource(ResResource res) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addSynthesizedRes(int resId) {
|
public void addSynthesizedRes(int resId) {
|
||||||
mSynthesizedRes.add(new ResID(resId));
|
mSynthesizedRes.add(new ResID(resId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return mName;
|
return mName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (getClass() != obj.getClass()) {
|
if (getClass() != obj.getClass()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final ResPackage other = (ResPackage) obj;
|
final ResPackage other = (ResPackage) obj;
|
||||||
if (this.mResTable != other.mResTable
|
if (this.mResTable != other.mResTable
|
||||||
&& (this.mResTable == null || !this.mResTable
|
&& (this.mResTable == null || !this.mResTable
|
||||||
.equals(other.mResTable))) {
|
.equals(other.mResTable))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (this.mId != other.mId) {
|
if (this.mId != other.mId) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int hash = 17;
|
int hash = 17;
|
||||||
hash = 31 * hash
|
hash = 31 * hash
|
||||||
+ (this.mResTable != null ? this.mResTable.hashCode() : 0);
|
+ (this.mResTable != null ? this.mResTable.hashCode() : 0);
|
||||||
hash = 31 * hash + this.mId;
|
hash = 31 * hash + this.mId;
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResValueFactory getValueFactory() {
|
public ResValueFactory getValueFactory() {
|
||||||
if (mValueFactory == null) {
|
if (mValueFactory == null) {
|
||||||
mValueFactory = new ResValueFactory(this);
|
mValueFactory = new ResValueFactory(this);
|
||||||
}
|
}
|
||||||
return mValueFactory;
|
return mValueFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final static Logger LOGGER = Logger
|
private final static Logger LOGGER = Logger
|
||||||
.getLogger(ResPackage.class.getName());
|
.getLogger(ResPackage.class.getName());
|
||||||
|
@ -25,97 +25,97 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResResSpec {
|
public class ResResSpec {
|
||||||
private final ResID mId;
|
private final ResID mId;
|
||||||
private final String mName;
|
private final String mName;
|
||||||
private final ResPackage mPackage;
|
private final ResPackage mPackage;
|
||||||
private final ResType mType;
|
private final ResType mType;
|
||||||
private final Map<ResConfigFlags, ResResource> mResources = new LinkedHashMap<ResConfigFlags, ResResource>();
|
private final Map<ResConfigFlags, ResResource> mResources = new LinkedHashMap<ResConfigFlags, ResResource>();
|
||||||
|
|
||||||
public ResResSpec(ResID id, String name, ResPackage pkg, ResType type) {
|
public ResResSpec(ResID id, String name, ResPackage pkg, ResType type) {
|
||||||
this.mId = id;
|
this.mId = id;
|
||||||
this.mName = name;
|
this.mName = name;
|
||||||
this.mPackage = pkg;
|
this.mPackage = pkg;
|
||||||
this.mType = type;
|
this.mType = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<ResResource> listResources() {
|
public Set<ResResource> listResources() {
|
||||||
return new LinkedHashSet<ResResource>(mResources.values());
|
return new LinkedHashSet<ResResource>(mResources.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResResource getResource(ResConfig config) throws AndrolibException {
|
public ResResource getResource(ResConfig config) throws AndrolibException {
|
||||||
return getResource(config.getFlags());
|
return getResource(config.getFlags());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResResource getResource(ResConfigFlags config)
|
public ResResource getResource(ResConfigFlags config)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
ResResource res = mResources.get(config);
|
ResResource res = mResources.get(config);
|
||||||
if (res == null) {
|
if (res == null) {
|
||||||
throw new UndefinedResObject(String.format(
|
throw new UndefinedResObject(String.format(
|
||||||
"resource: spec=%s, config=%s", this, config));
|
"resource: spec=%s, config=%s", this, config));
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasResource(ResConfig config) {
|
public boolean hasResource(ResConfig config) {
|
||||||
return hasResource(config.getFlags());
|
return hasResource(config.getFlags());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasResource(ResConfigFlags flags) {
|
private boolean hasResource(ResConfigFlags flags) {
|
||||||
return mResources.containsKey(flags);
|
return mResources.containsKey(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResResource getDefaultResource() throws AndrolibException {
|
public ResResource getDefaultResource() throws AndrolibException {
|
||||||
return getResource(new ResConfigFlags());
|
return getResource(new ResConfigFlags());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasDefaultResource() {
|
public boolean hasDefaultResource() {
|
||||||
return mResources.containsKey(new ResConfigFlags());
|
return mResources.containsKey(new ResConfigFlags());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFullName() {
|
public String getFullName() {
|
||||||
return getFullName(false, false);
|
return getFullName(false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFullName(ResPackage relativeToPackage, boolean excludeType) {
|
public String getFullName(ResPackage relativeToPackage, boolean excludeType) {
|
||||||
return getFullName(getPackage().equals(relativeToPackage), excludeType);
|
return getFullName(getPackage().equals(relativeToPackage), excludeType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFullName(boolean excludePackage, boolean excludeType) {
|
public String getFullName(boolean excludePackage, boolean excludeType) {
|
||||||
return (excludePackage ? "" : getPackage().getName() + ":")
|
return (excludePackage ? "" : getPackage().getName() + ":")
|
||||||
+ (excludeType ? "" : getType().getName() + "/") + getName();
|
+ (excludeType ? "" : getType().getName() + "/") + getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResID getId() {
|
public ResID getId() {
|
||||||
return mId;
|
return mId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return StringUtils.replace(mName, "\"", "q");
|
return StringUtils.replace(mName, "\"", "q");
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResPackage getPackage() {
|
public ResPackage getPackage() {
|
||||||
return mPackage;
|
return mPackage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResType getType() {
|
public ResType getType() {
|
||||||
return mType;
|
return mType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addResource(ResResource res) throws AndrolibException {
|
public void addResource(ResResource res) throws AndrolibException {
|
||||||
addResource(res, false);
|
addResource(res, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addResource(ResResource res, boolean overwrite)
|
public void addResource(ResResource res, boolean overwrite)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
ResConfigFlags flags = res.getConfig().getFlags();
|
ResConfigFlags flags = res.getConfig().getFlags();
|
||||||
if (mResources.put(flags, res) != null && !overwrite) {
|
if (mResources.put(flags, res) != null && !overwrite) {
|
||||||
throw new AndrolibException(String.format(
|
throw new AndrolibException(String.format(
|
||||||
"Multiple resources: spec=%s, config=%s", this, flags));
|
"Multiple resources: spec=%s, config=%s", this, flags));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return mId.toString() + " " + mType.toString() + "/" + mName;
|
return mId.toString() + " " + mType.toString() + "/" + mName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,41 +23,41 @@ import brut.androlib.res.data.value.ResValue;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResResource {
|
public class ResResource {
|
||||||
private final ResConfig mConfig;
|
private final ResConfig mConfig;
|
||||||
private final ResResSpec mResSpec;
|
private final ResResSpec mResSpec;
|
||||||
private final ResValue mValue;
|
private final ResValue mValue;
|
||||||
|
|
||||||
public ResResource(ResConfig config, ResResSpec spec, ResValue value) {
|
public ResResource(ResConfig config, ResResSpec spec, ResValue value) {
|
||||||
this.mConfig = config;
|
this.mConfig = config;
|
||||||
this.mResSpec = spec;
|
this.mResSpec = spec;
|
||||||
this.mValue = value;
|
this.mValue = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFilePath() {
|
public String getFilePath() {
|
||||||
return mResSpec.getType().getName()
|
return mResSpec.getType().getName()
|
||||||
+ mConfig.getFlags().getQualifiers() + "/" + mResSpec.getName();
|
+ mConfig.getFlags().getQualifiers() + "/" + mResSpec.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResConfig getConfig() {
|
public ResConfig getConfig() {
|
||||||
return mConfig;
|
return mConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResResSpec getResSpec() {
|
public ResResSpec getResSpec() {
|
||||||
return mResSpec;
|
return mResSpec;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResValue getValue() {
|
public ResValue getValue() {
|
||||||
return mValue;
|
return mValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void replace(ResValue value) throws AndrolibException {
|
public void replace(ResValue value) throws AndrolibException {
|
||||||
ResResource res = new ResResource(mConfig, mResSpec, value);
|
ResResource res = new ResResource(mConfig, mResSpec, value);
|
||||||
mConfig.addResource(res, true);
|
mConfig.addResource(res, true);
|
||||||
mResSpec.addResource(res, true);
|
mResSpec.addResource(res, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getFilePath();
|
return getFilePath();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,57 +26,57 @@ import java.util.*;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResTable {
|
public class ResTable {
|
||||||
private final AndrolibResources mAndRes;
|
private final AndrolibResources mAndRes;
|
||||||
|
|
||||||
private final Map<Integer, ResPackage> mPackagesById = new HashMap<Integer, ResPackage>();
|
private final Map<Integer, ResPackage> mPackagesById = new HashMap<Integer, ResPackage>();
|
||||||
private final Map<String, ResPackage> mPackagesByName = new HashMap<String, ResPackage>();
|
private final Map<String, ResPackage> mPackagesByName = new HashMap<String, ResPackage>();
|
||||||
private final Set<ResPackage> mMainPackages = new LinkedHashSet<ResPackage>();
|
private final Set<ResPackage> mMainPackages = new LinkedHashSet<ResPackage>();
|
||||||
private final Set<ResPackage> mFramePackages = new LinkedHashSet<ResPackage>();
|
private final Set<ResPackage> mFramePackages = new LinkedHashSet<ResPackage>();
|
||||||
|
|
||||||
private String mFrameTag;
|
private String mFrameTag;
|
||||||
private String mPackageRenamed;
|
private String mPackageRenamed;
|
||||||
private String mPackageOriginal;
|
private String mPackageOriginal;
|
||||||
private int mPackageId;
|
private int mPackageId;
|
||||||
private boolean mAnalysisMode = false;
|
private boolean mAnalysisMode = false;
|
||||||
|
|
||||||
private Map<String, String> mSdkInfo = new LinkedHashMap<String, String>();
|
private Map<String, String> mSdkInfo = new LinkedHashMap<String, String>();
|
||||||
private Map<String, String> mVersionInfo = new LinkedHashMap<String, String>();
|
private Map<String, String> mVersionInfo = new LinkedHashMap<String, String>();
|
||||||
private Map<String, String> mUnknownFiles = new LinkedHashMap<String, String>();
|
private Map<String, String> mUnknownFiles = new LinkedHashMap<String, String>();
|
||||||
|
|
||||||
public ResTable() {
|
public ResTable() {
|
||||||
mAndRes = null;
|
mAndRes = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResTable(AndrolibResources andRes) {
|
public ResTable(AndrolibResources andRes) {
|
||||||
mAndRes = andRes;
|
mAndRes = andRes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResResSpec getResSpec(int resID) throws AndrolibException {
|
public ResResSpec getResSpec(int resID) throws AndrolibException {
|
||||||
return getResSpec(new ResID(resID));
|
return getResSpec(new ResID(resID));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResResSpec getResSpec(ResID resID) throws AndrolibException {
|
public ResResSpec getResSpec(ResID resID) throws AndrolibException {
|
||||||
return getPackage(resID.package_).getResSpec(resID);
|
return getPackage(resID.package_).getResSpec(resID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<ResPackage> listMainPackages() {
|
public Set<ResPackage> listMainPackages() {
|
||||||
return mMainPackages;
|
return mMainPackages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<ResPackage> listFramePackages() {
|
public Set<ResPackage> listFramePackages() {
|
||||||
return mFramePackages;
|
return mFramePackages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResPackage getPackage(int id) throws AndrolibException {
|
public ResPackage getPackage(int id) throws AndrolibException {
|
||||||
ResPackage pkg = mPackagesById.get(id);
|
ResPackage pkg = mPackagesById.get(id);
|
||||||
if (pkg != null) {
|
if (pkg != null) {
|
||||||
return pkg;
|
return pkg;
|
||||||
}
|
}
|
||||||
if (mAndRes != null) {
|
if (mAndRes != null) {
|
||||||
return mAndRes.loadFrameworkPkg(this, id, mFrameTag);
|
return mAndRes.loadFrameworkPkg(this, id, mFrameTag);
|
||||||
}
|
}
|
||||||
throw new UndefinedResObject(String.format("package: id=%d", id));
|
throw new UndefinedResObject(String.format("package: id=%d", id));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResPackage getHighestSpecPackage() throws AndrolibException {
|
public ResPackage getHighestSpecPackage() throws AndrolibException {
|
||||||
int id = 0;
|
int id = 0;
|
||||||
@ -91,52 +91,52 @@ public class ResTable {
|
|||||||
return (id == 0) ? getPackage(1) : getPackage(id);
|
return (id == 0) ? getPackage(1) : getPackage(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResPackage getPackage(String name) throws AndrolibException {
|
public ResPackage getPackage(String name) throws AndrolibException {
|
||||||
ResPackage pkg = mPackagesByName.get(name);
|
ResPackage pkg = mPackagesByName.get(name);
|
||||||
if (pkg == null) {
|
if (pkg == null) {
|
||||||
throw new UndefinedResObject("package: name=" + name);
|
throw new UndefinedResObject("package: name=" + name);
|
||||||
}
|
}
|
||||||
return pkg;
|
return pkg;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasPackage(int id) {
|
public boolean hasPackage(int id) {
|
||||||
return mPackagesById.containsKey(id);
|
return mPackagesById.containsKey(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasPackage(String name) {
|
public boolean hasPackage(String name) {
|
||||||
return mPackagesByName.containsKey(name);
|
return mPackagesByName.containsKey(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResValue getValue(String package_, String type, String name)
|
public ResValue getValue(String package_, String type, String name)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
return getPackage(package_).getType(type).getResSpec(name)
|
return getPackage(package_).getType(type).getResSpec(name)
|
||||||
.getDefaultResource().getValue();
|
.getDefaultResource().getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addPackage(ResPackage pkg, boolean main)
|
public void addPackage(ResPackage pkg, boolean main)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
Integer id = pkg.getId();
|
Integer id = pkg.getId();
|
||||||
if (mPackagesById.containsKey(id)) {
|
if (mPackagesById.containsKey(id)) {
|
||||||
throw new AndrolibException("Multiple packages: id="
|
throw new AndrolibException("Multiple packages: id="
|
||||||
+ id.toString());
|
+ id.toString());
|
||||||
}
|
}
|
||||||
String name = pkg.getName();
|
String name = pkg.getName();
|
||||||
if (mPackagesByName.containsKey(name)) {
|
if (mPackagesByName.containsKey(name)) {
|
||||||
throw new AndrolibException("Multiple packages: name=" + name);
|
throw new AndrolibException("Multiple packages: name=" + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
mPackagesById.put(id, pkg);
|
mPackagesById.put(id, pkg);
|
||||||
mPackagesByName.put(name, pkg);
|
mPackagesByName.put(name, pkg);
|
||||||
if (main) {
|
if (main) {
|
||||||
mMainPackages.add(pkg);
|
mMainPackages.add(pkg);
|
||||||
} else {
|
} else {
|
||||||
mFramePackages.add(pkg);
|
mFramePackages.add(pkg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFrameTag(String tag) {
|
public void setFrameTag(String tag) {
|
||||||
mFrameTag = tag;
|
mFrameTag = tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAnalysisMode(boolean mode) {
|
public void setAnalysisMode(boolean mode) {
|
||||||
mAnalysisMode = mode;
|
mAnalysisMode = mode;
|
||||||
@ -153,30 +153,30 @@ public class ResTable {
|
|||||||
public void setPackageId(int id) {
|
public void setPackageId(int id) {
|
||||||
mPackageId = id;
|
mPackageId = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearSdkInfo() {
|
|
||||||
mSdkInfo.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addSdkInfo(String key, String value) {
|
public void clearSdkInfo() {
|
||||||
mSdkInfo.put(key, value);
|
mSdkInfo.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addVersionInfo(String key, String value) {
|
public void addSdkInfo(String key, String value) {
|
||||||
mVersionInfo.put(key, value);
|
mSdkInfo.put(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addVersionInfo(String key, String value) {
|
||||||
|
mVersionInfo.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
public void addUnknownFileInfo(String file, String value) {
|
public void addUnknownFileInfo(String file, String value) {
|
||||||
mUnknownFiles.put(file,value);
|
mUnknownFiles.put(file,value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, String> getVersionInfo() {
|
public Map<String, String> getVersionInfo() {
|
||||||
return mVersionInfo;
|
return mVersionInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, String> getSdkInfo() {
|
public Map<String, String> getSdkInfo() {
|
||||||
return mSdkInfo;
|
return mSdkInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getAnalysisMode() {
|
public boolean getAnalysisMode() {
|
||||||
return mAnalysisMode;
|
return mAnalysisMode;
|
||||||
|
@ -24,44 +24,44 @@ import java.util.*;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public final class ResType {
|
public final class ResType {
|
||||||
private final String mName;
|
private final String mName;
|
||||||
private final Map<String, ResResSpec> mResSpecs = new LinkedHashMap<String, ResResSpec>();
|
private final Map<String, ResResSpec> mResSpecs = new LinkedHashMap<String, ResResSpec>();
|
||||||
|
|
||||||
private final ResTable mResTable;
|
private final ResTable mResTable;
|
||||||
private final ResPackage mPackage;
|
private final ResPackage mPackage;
|
||||||
|
|
||||||
public ResType(String name, ResTable resTable, ResPackage package_) {
|
public ResType(String name, ResTable resTable, ResPackage package_) {
|
||||||
this.mName = name;
|
this.mName = name;
|
||||||
this.mResTable = resTable;
|
this.mResTable = resTable;
|
||||||
this.mPackage = package_;
|
this.mPackage = package_;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return mName;
|
return mName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<ResResSpec> listResSpecs() {
|
public Set<ResResSpec> listResSpecs() {
|
||||||
return new LinkedHashSet<ResResSpec>(mResSpecs.values());
|
return new LinkedHashSet<ResResSpec>(mResSpecs.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResResSpec getResSpec(String name) throws AndrolibException {
|
public ResResSpec getResSpec(String name) throws AndrolibException {
|
||||||
ResResSpec spec = mResSpecs.get(name);
|
ResResSpec spec = mResSpecs.get(name);
|
||||||
if (spec == null) {
|
if (spec == null) {
|
||||||
throw new UndefinedResObject(String.format("resource spec: %s/%s",
|
throw new UndefinedResObject(String.format("resource spec: %s/%s",
|
||||||
getName(), name));
|
getName(), name));
|
||||||
}
|
}
|
||||||
return spec;
|
return spec;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addResSpec(ResResSpec spec) throws AndrolibException {
|
public void addResSpec(ResResSpec spec) throws AndrolibException {
|
||||||
if (mResSpecs.put(spec.getName(), spec) != null) {
|
if (mResSpecs.put(spec.getName(), spec) != null) {
|
||||||
throw new AndrolibException(String.format(
|
throw new AndrolibException(String.format(
|
||||||
"Multiple res specs: %s/%s", getName(), spec.getName()));
|
"Multiple res specs: %s/%s", getName(), spec.getName()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return mName;
|
return mName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,68 +23,68 @@ import java.util.Set;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResValuesFile {
|
public class ResValuesFile {
|
||||||
private final ResPackage mPackage;
|
private final ResPackage mPackage;
|
||||||
private final ResType mType;
|
private final ResType mType;
|
||||||
private final ResConfig mConfig;
|
private final ResConfig mConfig;
|
||||||
private final Set<ResResource> mResources = new LinkedHashSet<ResResource>();
|
private final Set<ResResource> mResources = new LinkedHashSet<ResResource>();
|
||||||
|
|
||||||
public ResValuesFile(ResPackage pkg, ResType type, ResConfig config) {
|
public ResValuesFile(ResPackage pkg, ResType type, ResConfig config) {
|
||||||
this.mPackage = pkg;
|
this.mPackage = pkg;
|
||||||
this.mType = type;
|
this.mType = type;
|
||||||
this.mConfig = config;
|
this.mConfig = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPath() {
|
public String getPath() {
|
||||||
return "values" + mConfig.getFlags().getQualifiers() + "/"
|
return "values" + mConfig.getFlags().getQualifiers() + "/"
|
||||||
+ mType.getName() + (mType.getName().endsWith("s") ? "" : "s")
|
+ mType.getName() + (mType.getName().endsWith("s") ? "" : "s")
|
||||||
+ ".xml";
|
+ ".xml";
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<ResResource> listResources() {
|
public Set<ResResource> listResources() {
|
||||||
return mResources;
|
return mResources;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResType getType() {
|
public ResType getType() {
|
||||||
return mType;
|
return mType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResConfig getConfig() {
|
public ResConfig getConfig() {
|
||||||
return mConfig;
|
return mConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSynthesized(ResResource res) {
|
public boolean isSynthesized(ResResource res) {
|
||||||
return mPackage.isSynthesized(res.getResSpec().getId());
|
return mPackage.isSynthesized(res.getResSpec().getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addResource(ResResource res) {
|
public void addResource(ResResource res) {
|
||||||
mResources.add(res);
|
mResources.add(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (getClass() != obj.getClass()) {
|
if (getClass() != obj.getClass()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final ResValuesFile other = (ResValuesFile) obj;
|
final ResValuesFile other = (ResValuesFile) obj;
|
||||||
if (this.mType != other.mType
|
if (this.mType != other.mType
|
||||||
&& (this.mType == null || !this.mType.equals(other.mType))) {
|
&& (this.mType == null || !this.mType.equals(other.mType))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (this.mConfig != other.mConfig
|
if (this.mConfig != other.mConfig
|
||||||
&& (this.mConfig == null || !this.mConfig.equals(other.mConfig))) {
|
&& (this.mConfig == null || !this.mConfig.equals(other.mConfig))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int hash = 17;
|
int hash = 17;
|
||||||
hash = 31 * hash + (this.mType != null ? this.mType.hashCode() : 0);
|
hash = 31 * hash + (this.mType != null ? this.mType.hashCode() : 0);
|
||||||
hash = 31 * hash + (this.mConfig != null ? this.mConfig.hashCode() : 0);
|
hash = 31 * hash + (this.mConfig != null ? this.mConfig.hashCode() : 0);
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,148 +28,148 @@ import org.xmlpull.v1.XmlSerializer;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResAttr extends ResBagValue implements ResValuesXmlSerializable {
|
public class ResAttr extends ResBagValue implements ResValuesXmlSerializable {
|
||||||
ResAttr(ResReferenceValue parentVal, int type, Integer min, Integer max,
|
ResAttr(ResReferenceValue parentVal, int type, Integer min, Integer max,
|
||||||
Boolean l10n) {
|
Boolean l10n) {
|
||||||
super(parentVal);
|
super(parentVal);
|
||||||
mType = type;
|
mType = type;
|
||||||
mMin = min;
|
mMin = min;
|
||||||
mMax = max;
|
mMax = max;
|
||||||
mL10n = l10n;
|
mL10n = l10n;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String convertToResXmlFormat(ResScalarValue value)
|
public String convertToResXmlFormat(ResScalarValue value)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serializeToResValuesXml(XmlSerializer serializer,
|
public void serializeToResValuesXml(XmlSerializer serializer,
|
||||||
ResResource res) throws IOException, AndrolibException {
|
ResResource res) throws IOException, AndrolibException {
|
||||||
String type = getTypeAsString();
|
String type = getTypeAsString();
|
||||||
|
|
||||||
serializer.startTag(null, "attr");
|
serializer.startTag(null, "attr");
|
||||||
serializer.attribute(null, "name", res.getResSpec().getName());
|
serializer.attribute(null, "name", res.getResSpec().getName());
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
serializer.attribute(null, "format", type);
|
serializer.attribute(null, "format", type);
|
||||||
}
|
}
|
||||||
if (mMin != null) {
|
if (mMin != null) {
|
||||||
serializer.attribute(null, "min", mMin.toString());
|
serializer.attribute(null, "min", mMin.toString());
|
||||||
}
|
}
|
||||||
if (mMax != null) {
|
if (mMax != null) {
|
||||||
serializer.attribute(null, "max", mMax.toString());
|
serializer.attribute(null, "max", mMax.toString());
|
||||||
}
|
}
|
||||||
if (mL10n != null && mL10n) {
|
if (mL10n != null && mL10n) {
|
||||||
serializer.attribute(null, "localization", "suggested");
|
serializer.attribute(null, "localization", "suggested");
|
||||||
}
|
}
|
||||||
serializeBody(serializer, res);
|
serializeBody(serializer, res);
|
||||||
serializer.endTag(null, "attr");
|
serializer.endTag(null, "attr");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ResAttr factory(ResReferenceValue parent,
|
public static ResAttr factory(ResReferenceValue parent,
|
||||||
Duo<Integer, ResScalarValue>[] items, ResValueFactory factory,
|
Duo<Integer, ResScalarValue>[] items, ResValueFactory factory,
|
||||||
ResPackage pkg) throws AndrolibException {
|
ResPackage pkg) throws AndrolibException {
|
||||||
|
|
||||||
int type = ((ResIntValue) items[0].m2).getValue();
|
int type = ((ResIntValue) items[0].m2).getValue();
|
||||||
int scalarType = type & 0xffff;
|
int scalarType = type & 0xffff;
|
||||||
Integer min = null, max = null;
|
Integer min = null, max = null;
|
||||||
Boolean l10n = null;
|
Boolean l10n = null;
|
||||||
int i;
|
int i;
|
||||||
for (i = 1; i < items.length; i++) {
|
for (i = 1; i < items.length; i++) {
|
||||||
switch (items[i].m1) {
|
switch (items[i].m1) {
|
||||||
case BAG_KEY_ATTR_MIN:
|
case BAG_KEY_ATTR_MIN:
|
||||||
min = ((ResIntValue) items[i].m2).getValue();
|
min = ((ResIntValue) items[i].m2).getValue();
|
||||||
continue;
|
continue;
|
||||||
case BAG_KEY_ATTR_MAX:
|
case BAG_KEY_ATTR_MAX:
|
||||||
max = ((ResIntValue) items[i].m2).getValue();
|
max = ((ResIntValue) items[i].m2).getValue();
|
||||||
continue;
|
continue;
|
||||||
case BAG_KEY_ATTR_L10N:
|
case BAG_KEY_ATTR_L10N:
|
||||||
l10n = ((ResIntValue) items[i].m2).getValue() != 0;
|
l10n = ((ResIntValue) items[i].m2).getValue() != 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == items.length) {
|
if (i == items.length) {
|
||||||
return new ResAttr(parent, scalarType, min, max, l10n);
|
return new ResAttr(parent, scalarType, min, max, l10n);
|
||||||
}
|
}
|
||||||
Duo<ResReferenceValue, ResIntValue>[] attrItems = new Duo[items.length
|
Duo<ResReferenceValue, ResIntValue>[] attrItems = new Duo[items.length
|
||||||
- i];
|
- i];
|
||||||
int j = 0;
|
int j = 0;
|
||||||
for (; i < items.length; i++) {
|
for (; i < items.length; i++) {
|
||||||
int resId = items[i].m1;
|
int resId = items[i].m1;
|
||||||
pkg.addSynthesizedRes(resId);
|
pkg.addSynthesizedRes(resId);
|
||||||
attrItems[j++] = new Duo<ResReferenceValue, ResIntValue>(
|
attrItems[j++] = new Duo<ResReferenceValue, ResIntValue>(
|
||||||
factory.newReference(resId, null),
|
factory.newReference(resId, null),
|
||||||
(ResIntValue) items[i].m2);
|
(ResIntValue) items[i].m2);
|
||||||
}
|
}
|
||||||
switch (type & 0xff0000) {
|
switch (type & 0xff0000) {
|
||||||
case TYPE_ENUM:
|
case TYPE_ENUM:
|
||||||
return new ResEnumAttr(parent, scalarType, min, max, l10n,
|
return new ResEnumAttr(parent, scalarType, min, max, l10n,
|
||||||
attrItems);
|
attrItems);
|
||||||
case TYPE_FLAGS:
|
case TYPE_FLAGS:
|
||||||
return new ResFlagsAttr(parent, scalarType, min, max, l10n,
|
return new ResFlagsAttr(parent, scalarType, min, max, l10n,
|
||||||
attrItems);
|
attrItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new AndrolibException("Could not decode attr value");
|
throw new AndrolibException("Could not decode attr value");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void serializeBody(XmlSerializer serializer, ResResource res)
|
protected void serializeBody(XmlSerializer serializer, ResResource res)
|
||||||
throws AndrolibException, IOException {
|
throws AndrolibException, IOException {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getTypeAsString() {
|
protected String getTypeAsString() {
|
||||||
String s = "";
|
String s = "";
|
||||||
if ((mType & TYPE_REFERENCE) != 0) {
|
if ((mType & TYPE_REFERENCE) != 0) {
|
||||||
s += "|reference";
|
s += "|reference";
|
||||||
}
|
}
|
||||||
if ((mType & TYPE_STRING) != 0) {
|
if ((mType & TYPE_STRING) != 0) {
|
||||||
s += "|string";
|
s += "|string";
|
||||||
}
|
}
|
||||||
if ((mType & TYPE_INT) != 0) {
|
if ((mType & TYPE_INT) != 0) {
|
||||||
s += "|integer";
|
s += "|integer";
|
||||||
}
|
}
|
||||||
if ((mType & TYPE_BOOL) != 0) {
|
if ((mType & TYPE_BOOL) != 0) {
|
||||||
s += "|boolean";
|
s += "|boolean";
|
||||||
}
|
}
|
||||||
if ((mType & TYPE_COLOR) != 0) {
|
if ((mType & TYPE_COLOR) != 0) {
|
||||||
s += "|color";
|
s += "|color";
|
||||||
}
|
}
|
||||||
if ((mType & TYPE_FLOAT) != 0) {
|
if ((mType & TYPE_FLOAT) != 0) {
|
||||||
s += "|float";
|
s += "|float";
|
||||||
}
|
}
|
||||||
if ((mType & TYPE_DIMEN) != 0) {
|
if ((mType & TYPE_DIMEN) != 0) {
|
||||||
s += "|dimension";
|
s += "|dimension";
|
||||||
}
|
}
|
||||||
if ((mType & TYPE_FRACTION) != 0) {
|
if ((mType & TYPE_FRACTION) != 0) {
|
||||||
s += "|fraction";
|
s += "|fraction";
|
||||||
}
|
}
|
||||||
if (s.isEmpty()) {
|
if (s.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return s.substring(1);
|
return s.substring(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final int mType;
|
private final int mType;
|
||||||
private final Integer mMin;
|
private final Integer mMin;
|
||||||
private final Integer mMax;
|
private final Integer mMax;
|
||||||
private final Boolean mL10n;
|
private final Boolean mL10n;
|
||||||
|
|
||||||
public static final int BAG_KEY_ATTR_TYPE = 0x01000000;
|
public static final int BAG_KEY_ATTR_TYPE = 0x01000000;
|
||||||
private static final int BAG_KEY_ATTR_MIN = 0x01000001;
|
private static final int BAG_KEY_ATTR_MIN = 0x01000001;
|
||||||
private static final int BAG_KEY_ATTR_MAX = 0x01000002;
|
private static final int BAG_KEY_ATTR_MAX = 0x01000002;
|
||||||
private static final int BAG_KEY_ATTR_L10N = 0x01000003;
|
private static final int BAG_KEY_ATTR_L10N = 0x01000003;
|
||||||
|
|
||||||
private final static int TYPE_REFERENCE = 0x01;
|
private final static int TYPE_REFERENCE = 0x01;
|
||||||
private final static int TYPE_STRING = 0x02;
|
private final static int TYPE_STRING = 0x02;
|
||||||
private final static int TYPE_INT = 0x04;
|
private final static int TYPE_INT = 0x04;
|
||||||
private final static int TYPE_BOOL = 0x08;
|
private final static int TYPE_BOOL = 0x08;
|
||||||
private final static int TYPE_COLOR = 0x10;
|
private final static int TYPE_COLOR = 0x10;
|
||||||
private final static int TYPE_FLOAT = 0x20;
|
private final static int TYPE_FLOAT = 0x20;
|
||||||
private final static int TYPE_DIMEN = 0x40;
|
private final static int TYPE_DIMEN = 0x40;
|
||||||
private final static int TYPE_FRACTION = 0x80;
|
private final static int TYPE_FRACTION = 0x80;
|
||||||
private final static int TYPE_ANY_STRING = 0xee;
|
private final static int TYPE_ANY_STRING = 0xee;
|
||||||
|
|
||||||
private static final int TYPE_ENUM = 0x00010000;
|
private static final int TYPE_ENUM = 0x00010000;
|
||||||
private static final int TYPE_FLAGS = 0x00020000;
|
private static final int TYPE_FLAGS = 0x00020000;
|
||||||
}
|
}
|
||||||
|
@ -27,39 +27,39 @@ import org.xmlpull.v1.XmlSerializer;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResBagValue extends ResValue implements ResValuesXmlSerializable {
|
public class ResBagValue extends ResValue implements ResValuesXmlSerializable {
|
||||||
protected final ResReferenceValue mParent;
|
protected final ResReferenceValue mParent;
|
||||||
|
|
||||||
public ResBagValue(ResReferenceValue parent) {
|
public ResBagValue(ResReferenceValue parent) {
|
||||||
this.mParent = parent;
|
this.mParent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serializeToResValuesXml(XmlSerializer serializer,
|
public void serializeToResValuesXml(XmlSerializer serializer,
|
||||||
ResResource res) throws IOException, AndrolibException {
|
ResResource res) throws IOException, AndrolibException {
|
||||||
String type = res.getResSpec().getType().getName();
|
String type = res.getResSpec().getType().getName();
|
||||||
if ("style".equals(type)) {
|
if ("style".equals(type)) {
|
||||||
new ResStyleValue(mParent, new Duo[0], null)
|
new ResStyleValue(mParent, new Duo[0], null)
|
||||||
.serializeToResValuesXml(serializer, res);
|
.serializeToResValuesXml(serializer, res);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ("array".equals(type)) {
|
if ("array".equals(type)) {
|
||||||
new ResArrayValue(mParent, new Duo[0]).serializeToResValuesXml(
|
new ResArrayValue(mParent, new Duo[0]).serializeToResValuesXml(
|
||||||
serializer, res);
|
serializer, res);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ("plurals".equals(type)) {
|
if ("plurals".equals(type)) {
|
||||||
new ResPluralsValue(mParent, new Duo[0]).serializeToResValuesXml(
|
new ResPluralsValue(mParent, new Duo[0]).serializeToResValuesXml(
|
||||||
serializer, res);
|
serializer, res);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
serializer.startTag(null, "item");
|
serializer.startTag(null, "item");
|
||||||
serializer.attribute(null, "type", type);
|
serializer.attribute(null, "type", type);
|
||||||
serializer.attribute(null, "name", res.getResSpec().getName());
|
serializer.attribute(null, "name", res.getResSpec().getName());
|
||||||
serializer.endTag(null, "item");
|
serializer.endTag(null, "item");
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResReferenceValue getParent() {
|
public ResReferenceValue getParent() {
|
||||||
return mParent;
|
return mParent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,19 +20,19 @@ package brut.androlib.res.data.value;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResBoolValue extends ResScalarValue {
|
public class ResBoolValue extends ResScalarValue {
|
||||||
private final boolean mValue;
|
private final boolean mValue;
|
||||||
|
|
||||||
public ResBoolValue(boolean value, String rawValue) {
|
public ResBoolValue(boolean value, String rawValue) {
|
||||||
super("bool", rawValue);
|
super("bool", rawValue);
|
||||||
this.mValue = value;
|
this.mValue = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getValue() {
|
public boolean getValue() {
|
||||||
return mValue;
|
return mValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String encodeAsResXml() {
|
protected String encodeAsResXml() {
|
||||||
return mValue ? "true" : "false";
|
return mValue ? "true" : "false";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,12 @@ package brut.androlib.res.data.value;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResColorValue extends ResIntValue {
|
public class ResColorValue extends ResIntValue {
|
||||||
public ResColorValue(int value, String rawValue) {
|
public ResColorValue(int value, String rawValue) {
|
||||||
super(value, rawValue, "color");
|
super(value, rawValue, "color");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String encodeAsResXml() {
|
protected String encodeAsResXml() {
|
||||||
return String.format("#%08x", mValue);
|
return String.format("#%08x", mValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -23,12 +23,12 @@ import brut.androlib.AndrolibException;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResDimenValue extends ResIntValue {
|
public class ResDimenValue extends ResIntValue {
|
||||||
public ResDimenValue(int value, String rawValue) {
|
public ResDimenValue(int value, String rawValue) {
|
||||||
super(value, rawValue, "dimen");
|
super(value, rawValue, "dimen");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String encodeAsResXml() throws AndrolibException {
|
protected String encodeAsResXml() throws AndrolibException {
|
||||||
return TypedValue.coerceToString(TypedValue.TYPE_DIMENSION, mValue);
|
return TypedValue.coerceToString(TypedValue.TYPE_DIMENSION, mValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,55 +28,55 @@ import org.xmlpull.v1.XmlSerializer;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResEnumAttr extends ResAttr {
|
public class ResEnumAttr extends ResAttr {
|
||||||
ResEnumAttr(ResReferenceValue parent, int type, Integer min, Integer max,
|
ResEnumAttr(ResReferenceValue parent, int type, Integer min, Integer max,
|
||||||
Boolean l10n, Duo<ResReferenceValue, ResIntValue>[] items) {
|
Boolean l10n, Duo<ResReferenceValue, ResIntValue>[] items) {
|
||||||
super(parent, type, min, max, l10n);
|
super(parent, type, min, max, l10n);
|
||||||
mItems = items;
|
mItems = items;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String convertToResXmlFormat(ResScalarValue value)
|
public String convertToResXmlFormat(ResScalarValue value)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
if (value instanceof ResIntValue) {
|
if (value instanceof ResIntValue) {
|
||||||
String ret = decodeValue(((ResIntValue) value).getValue());
|
String ret = decodeValue(((ResIntValue) value).getValue());
|
||||||
if (ret != null) {
|
if (ret != null) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.convertToResXmlFormat(value);
|
return super.convertToResXmlFormat(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void serializeBody(XmlSerializer serializer, ResResource res)
|
protected void serializeBody(XmlSerializer serializer, ResResource res)
|
||||||
throws AndrolibException, IOException {
|
throws AndrolibException, IOException {
|
||||||
for (Duo<ResReferenceValue, ResIntValue> duo : mItems) {
|
for (Duo<ResReferenceValue, ResIntValue> duo : mItems) {
|
||||||
int intVal = duo.m2.getValue();
|
int intVal = duo.m2.getValue();
|
||||||
|
|
||||||
serializer.startTag(null, "enum");
|
serializer.startTag(null, "enum");
|
||||||
serializer.attribute(null, "name", duo.m1.getReferent().getName());
|
serializer.attribute(null, "name", duo.m1.getReferent().getName());
|
||||||
serializer.attribute(null, "value", String.valueOf(intVal));
|
serializer.attribute(null, "value", String.valueOf(intVal));
|
||||||
serializer.endTag(null, "enum");
|
serializer.endTag(null, "enum");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String decodeValue(int value) throws AndrolibException {
|
private String decodeValue(int value) throws AndrolibException {
|
||||||
String value2 = mItemsCache.get(value);
|
String value2 = mItemsCache.get(value);
|
||||||
if (value2 == null) {
|
if (value2 == null) {
|
||||||
ResReferenceValue ref = null;
|
ResReferenceValue ref = null;
|
||||||
for (Duo<ResReferenceValue, ResIntValue> duo : mItems) {
|
for (Duo<ResReferenceValue, ResIntValue> duo : mItems) {
|
||||||
if (duo.m2.getValue() == value) {
|
if (duo.m2.getValue() == value) {
|
||||||
ref = duo.m1;
|
ref = duo.m1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ref != null) {
|
if (ref != null) {
|
||||||
value2 = ref.getReferent().getName();
|
value2 = ref.getReferent().getName();
|
||||||
mItemsCache.put(value, value2);
|
mItemsCache.put(value, value2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return value2;
|
return value2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Duo<ResReferenceValue, ResIntValue>[] mItems;
|
private final Duo<ResReferenceValue, ResIntValue>[] mItems;
|
||||||
private final Map<Integer, String> mItemsCache = new HashMap<Integer, String>();
|
private final Map<Integer, String> mItemsCache = new HashMap<Integer, String>();
|
||||||
}
|
}
|
||||||
|
@ -22,21 +22,21 @@ import brut.androlib.AndrolibException;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResFileValue extends ResValue {
|
public class ResFileValue extends ResValue {
|
||||||
private final String mPath;
|
private final String mPath;
|
||||||
|
|
||||||
public ResFileValue(String path) {
|
public ResFileValue(String path) {
|
||||||
this.mPath = path;
|
this.mPath = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPath() {
|
public String getPath() {
|
||||||
return mPath;
|
return mPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getStrippedPath() throws AndrolibException {
|
public String getStrippedPath() throws AndrolibException {
|
||||||
if (!mPath.startsWith("res/")) {
|
if (!mPath.startsWith("res/")) {
|
||||||
throw new AndrolibException(
|
throw new AndrolibException(
|
||||||
"File path does not start with \"res/\": " + mPath);
|
"File path does not start with \"res/\": " + mPath);
|
||||||
}
|
}
|
||||||
return mPath.substring(4);
|
return mPath.substring(4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,133 +28,133 @@ import org.xmlpull.v1.XmlSerializer;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResFlagsAttr extends ResAttr {
|
public class ResFlagsAttr extends ResAttr {
|
||||||
ResFlagsAttr(ResReferenceValue parent, int type, Integer min, Integer max,
|
ResFlagsAttr(ResReferenceValue parent, int type, Integer min, Integer max,
|
||||||
Boolean l10n, Duo<ResReferenceValue, ResIntValue>[] items) {
|
Boolean l10n, Duo<ResReferenceValue, ResIntValue>[] items) {
|
||||||
super(parent, type, min, max, l10n);
|
super(parent, type, min, max, l10n);
|
||||||
|
|
||||||
mItems = new FlagItem[items.length];
|
mItems = new FlagItem[items.length];
|
||||||
for (int i = 0; i < items.length; i++) {
|
for (int i = 0; i < items.length; i++) {
|
||||||
mItems[i] = new FlagItem(items[i].m1, items[i].m2.getValue());
|
mItems[i] = new FlagItem(items[i].m1, items[i].m2.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String convertToResXmlFormat(ResScalarValue value)
|
public String convertToResXmlFormat(ResScalarValue value)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
if (!(value instanceof ResIntValue)) {
|
if (!(value instanceof ResIntValue)) {
|
||||||
return super.convertToResXmlFormat(value);
|
return super.convertToResXmlFormat(value);
|
||||||
}
|
}
|
||||||
loadFlags();
|
loadFlags();
|
||||||
int intVal = ((ResIntValue) value).getValue();
|
int intVal = ((ResIntValue) value).getValue();
|
||||||
|
|
||||||
if (intVal == 0) {
|
if (intVal == 0) {
|
||||||
return renderFlags(mZeroFlags);
|
return renderFlags(mZeroFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
FlagItem[] flagItems = new FlagItem[mFlags.length];
|
FlagItem[] flagItems = new FlagItem[mFlags.length];
|
||||||
int[] flags = new int[mFlags.length];
|
int[] flags = new int[mFlags.length];
|
||||||
int flagsCount = 0;
|
int flagsCount = 0;
|
||||||
for (int i = 0; i < mFlags.length; i++) {
|
for (int i = 0; i < mFlags.length; i++) {
|
||||||
FlagItem flagItem = mFlags[i];
|
FlagItem flagItem = mFlags[i];
|
||||||
int flag = flagItem.flag;
|
int flag = flagItem.flag;
|
||||||
|
|
||||||
if ((intVal & flag) != flag) {
|
if ((intVal & flag) != flag) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isSubpartOf(flag, flags)) {
|
if (!isSubpartOf(flag, flags)) {
|
||||||
flags[flagsCount] = flag;
|
flags[flagsCount] = flag;
|
||||||
flagItems[flagsCount++] = flagItem;
|
flagItems[flagsCount++] = flagItem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return renderFlags(Arrays.copyOf(flagItems, flagsCount));
|
return renderFlags(Arrays.copyOf(flagItems, flagsCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void serializeBody(XmlSerializer serializer, ResResource res)
|
protected void serializeBody(XmlSerializer serializer, ResResource res)
|
||||||
throws AndrolibException, IOException {
|
throws AndrolibException, IOException {
|
||||||
for (int i = 0; i < mItems.length; i++) {
|
for (int i = 0; i < mItems.length; i++) {
|
||||||
FlagItem item = mItems[i];
|
FlagItem item = mItems[i];
|
||||||
|
|
||||||
serializer.startTag(null, "flag");
|
serializer.startTag(null, "flag");
|
||||||
serializer.attribute(null, "name", item.getValue());
|
serializer.attribute(null, "name", item.getValue());
|
||||||
serializer.attribute(null, "value",
|
serializer.attribute(null, "value",
|
||||||
String.format("0x%08x", item.flag));
|
String.format("0x%08x", item.flag));
|
||||||
serializer.endTag(null, "flag");
|
serializer.endTag(null, "flag");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSubpartOf(int flag, int[] flags) {
|
private boolean isSubpartOf(int flag, int[] flags) {
|
||||||
for (int i = 0; i < flags.length; i++) {
|
for (int i = 0; i < flags.length; i++) {
|
||||||
if ((flags[i] & flag) == flag) {
|
if ((flags[i] & flag) == flag) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String renderFlags(FlagItem[] flags) throws AndrolibException {
|
private String renderFlags(FlagItem[] flags) throws AndrolibException {
|
||||||
String ret = "";
|
String ret = "";
|
||||||
for (int i = 0; i < flags.length; i++) {
|
for (int i = 0; i < flags.length; i++) {
|
||||||
ret += "|" + flags[i].getValue();
|
ret += "|" + flags[i].getValue();
|
||||||
}
|
}
|
||||||
if (ret.isEmpty()) {
|
if (ret.isEmpty()) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
return ret.substring(1);
|
return ret.substring(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadFlags() {
|
private void loadFlags() {
|
||||||
if (mFlags != null) {
|
if (mFlags != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FlagItem[] zeroFlags = new FlagItem[mItems.length];
|
FlagItem[] zeroFlags = new FlagItem[mItems.length];
|
||||||
int zeroFlagsCount = 0;
|
int zeroFlagsCount = 0;
|
||||||
FlagItem[] flags = new FlagItem[mItems.length];
|
FlagItem[] flags = new FlagItem[mItems.length];
|
||||||
int flagsCount = 0;
|
int flagsCount = 0;
|
||||||
|
|
||||||
for (int i = 0; i < mItems.length; i++) {
|
for (int i = 0; i < mItems.length; i++) {
|
||||||
FlagItem item = mItems[i];
|
FlagItem item = mItems[i];
|
||||||
if (item.flag == 0) {
|
if (item.flag == 0) {
|
||||||
zeroFlags[zeroFlagsCount++] = item;
|
zeroFlags[zeroFlagsCount++] = item;
|
||||||
} else {
|
} else {
|
||||||
flags[flagsCount++] = item;
|
flags[flagsCount++] = item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mZeroFlags = Arrays.copyOf(zeroFlags, zeroFlagsCount);
|
mZeroFlags = Arrays.copyOf(zeroFlags, zeroFlagsCount);
|
||||||
mFlags = Arrays.copyOf(flags, flagsCount);
|
mFlags = Arrays.copyOf(flags, flagsCount);
|
||||||
|
|
||||||
Arrays.sort(mFlags, new Comparator<FlagItem>() {
|
Arrays.sort(mFlags, new Comparator<FlagItem>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(FlagItem o1, FlagItem o2) {
|
public int compare(FlagItem o1, FlagItem o2) {
|
||||||
return Integer.valueOf(Integer.bitCount(o2.flag)).compareTo(
|
return Integer.valueOf(Integer.bitCount(o2.flag)).compareTo(
|
||||||
Integer.bitCount(o1.flag));
|
Integer.bitCount(o1.flag));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private final FlagItem[] mItems;
|
private final FlagItem[] mItems;
|
||||||
|
|
||||||
private FlagItem[] mZeroFlags;
|
private FlagItem[] mZeroFlags;
|
||||||
private FlagItem[] mFlags;
|
private FlagItem[] mFlags;
|
||||||
|
|
||||||
private static class FlagItem {
|
private static class FlagItem {
|
||||||
public final ResReferenceValue ref;
|
public final ResReferenceValue ref;
|
||||||
public final int flag;
|
public final int flag;
|
||||||
public String value;
|
public String value;
|
||||||
|
|
||||||
public FlagItem(ResReferenceValue ref, int flag) {
|
public FlagItem(ResReferenceValue ref, int flag) {
|
||||||
this.ref = ref;
|
this.ref = ref;
|
||||||
this.flag = flag;
|
this.flag = flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getValue() throws AndrolibException {
|
public String getValue() throws AndrolibException {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
value = ref.getReferent().getName();
|
value = ref.getReferent().getName();
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,19 +20,19 @@ package brut.androlib.res.data.value;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResFloatValue extends ResScalarValue {
|
public class ResFloatValue extends ResScalarValue {
|
||||||
private final float mValue;
|
private final float mValue;
|
||||||
|
|
||||||
public ResFloatValue(float value, String rawValue) {
|
public ResFloatValue(float value, String rawValue) {
|
||||||
super("float", rawValue);
|
super("float", rawValue);
|
||||||
this.mValue = value;
|
this.mValue = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getValue() {
|
public float getValue() {
|
||||||
return mValue;
|
return mValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String encodeAsResXml() {
|
protected String encodeAsResXml() {
|
||||||
return String.valueOf(mValue);
|
return String.valueOf(mValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,12 +23,12 @@ import brut.androlib.AndrolibException;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResFractionValue extends ResIntValue {
|
public class ResFractionValue extends ResIntValue {
|
||||||
public ResFractionValue(int value, String rawValue) {
|
public ResFractionValue(int value, String rawValue) {
|
||||||
super(value, rawValue, "fraction");
|
super(value, rawValue, "fraction");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String encodeAsResXml() throws AndrolibException {
|
protected String encodeAsResXml() throws AndrolibException {
|
||||||
return TypedValue.coerceToString(TypedValue.TYPE_FRACTION, mValue);
|
return TypedValue.coerceToString(TypedValue.TYPE_FRACTION, mValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,13 +26,13 @@ import org.xmlpull.v1.XmlSerializer;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResIdValue extends ResValue implements ResValuesXmlSerializable {
|
public class ResIdValue extends ResValue implements ResValuesXmlSerializable {
|
||||||
@Override
|
@Override
|
||||||
public void serializeToResValuesXml(XmlSerializer serializer,
|
public void serializeToResValuesXml(XmlSerializer serializer,
|
||||||
ResResource res) throws IOException, AndrolibException {
|
ResResource res) throws IOException, AndrolibException {
|
||||||
serializer.startTag(null, "item");
|
serializer.startTag(null, "item");
|
||||||
serializer
|
serializer
|
||||||
.attribute(null, "type", res.getResSpec().getType().getName());
|
.attribute(null, "type", res.getResSpec().getType().getName());
|
||||||
serializer.attribute(null, "name", res.getResSpec().getName());
|
serializer.attribute(null, "name", res.getResSpec().getName());
|
||||||
serializer.endTag(null, "item");
|
serializer.endTag(null, "item");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,25 +23,25 @@ import brut.androlib.AndrolibException;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResIntValue extends ResScalarValue {
|
public class ResIntValue extends ResScalarValue {
|
||||||
protected final int mValue;
|
protected final int mValue;
|
||||||
private int type;
|
private int type;
|
||||||
|
|
||||||
public ResIntValue(int value, String rawValue, int type) {
|
public ResIntValue(int value, String rawValue, int type) {
|
||||||
this(value, rawValue, "integer");
|
this(value, rawValue, "integer");
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResIntValue(int value, String rawValue, String type) {
|
public ResIntValue(int value, String rawValue, String type) {
|
||||||
super(type, rawValue);
|
super(type, rawValue);
|
||||||
this.mValue = value;
|
this.mValue = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getValue() {
|
public int getValue() {
|
||||||
return mValue;
|
return mValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String encodeAsResXml() throws AndrolibException {
|
protected String encodeAsResXml() throws AndrolibException {
|
||||||
return TypedValue.coerceToString(type, mValue);
|
return TypedValue.coerceToString(type, mValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -29,42 +29,42 @@ import org.xmlpull.v1.XmlSerializer;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResPluralsValue extends ResBagValue implements
|
public class ResPluralsValue extends ResBagValue implements
|
||||||
ResValuesXmlSerializable {
|
ResValuesXmlSerializable {
|
||||||
ResPluralsValue(ResReferenceValue parent,
|
ResPluralsValue(ResReferenceValue parent,
|
||||||
Duo<Integer, ResScalarValue>[] items) {
|
Duo<Integer, ResScalarValue>[] items) {
|
||||||
super(parent);
|
super(parent);
|
||||||
|
|
||||||
mItems = new ResScalarValue[6];
|
mItems = new ResScalarValue[6];
|
||||||
for (int i = 0; i < items.length; i++) {
|
for (int i = 0; i < items.length; i++) {
|
||||||
mItems[items[i].m1 - BAG_KEY_PLURALS_START] = items[i].m2;
|
mItems[items[i].m1 - BAG_KEY_PLURALS_START] = items[i].m2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serializeToResValuesXml(XmlSerializer serializer,
|
public void serializeToResValuesXml(XmlSerializer serializer,
|
||||||
ResResource res) throws IOException, AndrolibException {
|
ResResource res) throws IOException, AndrolibException {
|
||||||
serializer.startTag(null, "plurals");
|
serializer.startTag(null, "plurals");
|
||||||
serializer.attribute(null, "name", res.getResSpec().getName());
|
serializer.attribute(null, "name", res.getResSpec().getName());
|
||||||
for (int i = 0; i < mItems.length; i++) {
|
for (int i = 0; i < mItems.length; i++) {
|
||||||
ResScalarValue item = mItems[i];
|
ResScalarValue item = mItems[i];
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResScalarValue rawValue = item;
|
ResScalarValue rawValue = item;
|
||||||
|
|
||||||
serializer.startTag(null, "item");
|
serializer.startTag(null, "item");
|
||||||
serializer.attribute(null, "quantity", QUANTITY_MAP[i]);
|
serializer.attribute(null, "quantity", QUANTITY_MAP[i]);
|
||||||
serializer.text(ResXmlEncoders.enumerateNonPositionalSubstitutionsIfRequired(item.encodeAsResXmlValue()));
|
serializer.text(ResXmlEncoders.enumerateNonPositionalSubstitutionsIfRequired(item.encodeAsResXmlValue()));
|
||||||
serializer.endTag(null, "item");
|
serializer.endTag(null, "item");
|
||||||
}
|
}
|
||||||
serializer.endTag(null, "plurals");
|
serializer.endTag(null, "plurals");
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ResScalarValue[] mItems;
|
private final ResScalarValue[] mItems;
|
||||||
|
|
||||||
public static final int BAG_KEY_PLURALS_START = 0x01000004;
|
public static final int BAG_KEY_PLURALS_START = 0x01000004;
|
||||||
public static final int BAG_KEY_PLURALS_END = 0x01000009;
|
public static final int BAG_KEY_PLURALS_END = 0x01000009;
|
||||||
private static final String[] QUANTITY_MAP = new String[] { "other",
|
private static final String[] QUANTITY_MAP = new String[] { "other",
|
||||||
"zero", "one", "two", "few", "many" };
|
"zero", "one", "two", "few", "many" };
|
||||||
}
|
}
|
||||||
|
@ -24,43 +24,43 @@ import brut.androlib.res.data.ResResSpec;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResReferenceValue extends ResIntValue {
|
public class ResReferenceValue extends ResIntValue {
|
||||||
private final ResPackage mPackage;
|
private final ResPackage mPackage;
|
||||||
private final boolean mTheme;
|
private final boolean mTheme;
|
||||||
|
|
||||||
public ResReferenceValue(ResPackage package_, int value, String rawValue) {
|
public ResReferenceValue(ResPackage package_, int value, String rawValue) {
|
||||||
this(package_, value, rawValue, false);
|
this(package_, value, rawValue, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResReferenceValue(ResPackage package_, int value, String rawValue,
|
public ResReferenceValue(ResPackage package_, int value, String rawValue,
|
||||||
boolean theme) {
|
boolean theme) {
|
||||||
super(value, rawValue, "reference");
|
super(value, rawValue, "reference");
|
||||||
mPackage = package_;
|
mPackage = package_;
|
||||||
mTheme = theme;
|
mTheme = theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String encodeAsResXml() throws AndrolibException {
|
protected String encodeAsResXml() throws AndrolibException {
|
||||||
if (isNull()) {
|
if (isNull()) {
|
||||||
return "@null";
|
return "@null";
|
||||||
}
|
}
|
||||||
|
|
||||||
ResResSpec spec = getReferent();
|
ResResSpec spec = getReferent();
|
||||||
boolean newId = spec.hasDefaultResource()
|
boolean newId = spec.hasDefaultResource()
|
||||||
&& spec.getDefaultResource().getValue() instanceof ResIdValue;
|
&& spec.getDefaultResource().getValue() instanceof ResIdValue;
|
||||||
|
|
||||||
// generate the beginning to fix @android
|
// generate the beginning to fix @android
|
||||||
String mStart = (mTheme ? '?' : '@') + (newId ? "+" : "");
|
String mStart = (mTheme ? '?' : '@') + (newId ? "+" : "");
|
||||||
|
|
||||||
return mStart
|
return mStart
|
||||||
+ spec.getFullName(mPackage, mTheme
|
+ spec.getFullName(mPackage, mTheme
|
||||||
&& spec.getType().getName().equals("attr"));
|
&& spec.getType().getName().equals("attr"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResResSpec getReferent() throws AndrolibException {
|
public ResResSpec getReferent() throws AndrolibException {
|
||||||
return mPackage.getResTable().getResSpec(getValue());
|
return mPackage.getResTable().getResSpec(getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNull() {
|
public boolean isNull() {
|
||||||
return mValue == 0;
|
return mValue == 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,48 +28,48 @@ import org.xmlpull.v1.XmlSerializer;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public abstract class ResScalarValue extends ResValue implements
|
public abstract class ResScalarValue extends ResValue implements
|
||||||
ResXmlEncodable, ResValuesXmlSerializable {
|
ResXmlEncodable, ResValuesXmlSerializable {
|
||||||
protected final String mType;
|
protected final String mType;
|
||||||
protected final String mRawValue;
|
protected final String mRawValue;
|
||||||
|
|
||||||
protected ResScalarValue(String type, String rawValue) {
|
protected ResScalarValue(String type, String rawValue) {
|
||||||
mType = type;
|
mType = type;
|
||||||
mRawValue = rawValue;
|
mRawValue = rawValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String encodeAsResXmlAttr() throws AndrolibException {
|
public String encodeAsResXmlAttr() throws AndrolibException {
|
||||||
if (mRawValue != null) {
|
if (mRawValue != null) {
|
||||||
return mRawValue;
|
return mRawValue;
|
||||||
}
|
}
|
||||||
return encodeAsResXml();
|
return encodeAsResXml();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String encodeAsResXmlItemValue() throws AndrolibException {
|
public String encodeAsResXmlItemValue() throws AndrolibException {
|
||||||
return encodeAsResXmlValue();
|
return encodeAsResXmlValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String encodeAsResXmlValue() throws AndrolibException {
|
public String encodeAsResXmlValue() throws AndrolibException {
|
||||||
if (mRawValue != null) {
|
if (mRawValue != null) {
|
||||||
return mRawValue;
|
return mRawValue;
|
||||||
}
|
}
|
||||||
return encodeAsResXml();
|
return encodeAsResXml();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String encodeAsResXmlNonEscapedItemValue() throws AndrolibException {
|
public String encodeAsResXmlNonEscapedItemValue() throws AndrolibException {
|
||||||
return encodeAsResXmlValue().replace("&", "&").replace("<","<");
|
return encodeAsResXmlValue().replace("&", "&").replace("<","<");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serializeToResValuesXml(XmlSerializer serializer,
|
public void serializeToResValuesXml(XmlSerializer serializer,
|
||||||
ResResource res) throws IOException, AndrolibException {
|
ResResource res) throws IOException, AndrolibException {
|
||||||
String type = res.getResSpec().getType().getName();
|
String type = res.getResSpec().getType().getName();
|
||||||
boolean item = !"reference".equals(mType) && !type.equals(mType);
|
boolean item = !"reference".equals(mType) && !type.equals(mType);
|
||||||
|
|
||||||
String body = encodeAsResXmlValue();
|
String body = encodeAsResXmlValue();
|
||||||
|
|
||||||
// check for resource reference
|
// check for resource reference
|
||||||
if (!type.equalsIgnoreCase("color")) {
|
if (!type.equalsIgnoreCase("color")) {
|
||||||
if (body.contains("@")) {
|
if (body.contains("@")) {
|
||||||
if (!res.getFilePath().contains("string")) {
|
if (!res.getFilePath().contains("string")) {
|
||||||
@ -78,31 +78,31 @@ public abstract class ResScalarValue extends ResValue implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for using attrib as node or item
|
// check for using attrib as node or item
|
||||||
String tagName = item ? "item" : type;
|
String tagName = item ? "item" : type;
|
||||||
|
|
||||||
serializer.startTag(null, tagName);
|
serializer.startTag(null, tagName);
|
||||||
if (item) {
|
if (item) {
|
||||||
serializer.attribute(null, "type", type);
|
serializer.attribute(null, "type", type);
|
||||||
}
|
}
|
||||||
serializer.attribute(null, "name", res.getResSpec().getName());
|
serializer.attribute(null, "name", res.getResSpec().getName());
|
||||||
|
|
||||||
serializeExtraXmlAttrs(serializer, res);
|
serializeExtraXmlAttrs(serializer, res);
|
||||||
|
|
||||||
if (!body.isEmpty()) {
|
if (!body.isEmpty()) {
|
||||||
serializer.ignorableWhitespace(body);
|
serializer.ignorableWhitespace(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
serializer.endTag(null, tagName);
|
serializer.endTag(null, tagName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getType() {
|
public String getType() {
|
||||||
return mType;
|
return mType;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void serializeExtraXmlAttrs(XmlSerializer serializer,
|
protected void serializeExtraXmlAttrs(XmlSerializer serializer,
|
||||||
ResResource res) throws IOException {
|
ResResource res) throws IOException {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract String encodeAsResXml() throws AndrolibException;
|
protected abstract String encodeAsResXml() throws AndrolibException;
|
||||||
}
|
}
|
||||||
|
@ -27,41 +27,41 @@ import org.xmlpull.v1.XmlSerializer;
|
|||||||
*/
|
*/
|
||||||
public class ResStringValue extends ResScalarValue {
|
public class ResStringValue extends ResScalarValue {
|
||||||
|
|
||||||
public ResStringValue(String value) {
|
public ResStringValue(String value) {
|
||||||
this(value, "string");
|
this(value, "string");
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResStringValue(String value, String type) {
|
public ResStringValue(String value, String type) {
|
||||||
super(type, value);
|
super(type, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String encodeAsResXmlAttr() {
|
public String encodeAsResXmlAttr() {
|
||||||
return ResXmlEncoders.encodeAsResXmlAttr(mRawValue);
|
return ResXmlEncoders.encodeAsResXmlAttr(mRawValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String encodeAsResXmlItemValue() {
|
public String encodeAsResXmlItemValue() {
|
||||||
return ResXmlEncoders
|
return ResXmlEncoders
|
||||||
.enumerateNonPositionalSubstitutionsIfRequired(ResXmlEncoders
|
.enumerateNonPositionalSubstitutionsIfRequired(ResXmlEncoders
|
||||||
.encodeAsXmlValue(mRawValue));
|
.encodeAsXmlValue(mRawValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String encodeAsResXmlValue() {
|
public String encodeAsResXmlValue() {
|
||||||
return ResXmlEncoders.encodeAsXmlValue(mRawValue);
|
return ResXmlEncoders.encodeAsXmlValue(mRawValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String encodeAsResXml() throws AndrolibException {
|
protected String encodeAsResXml() throws AndrolibException {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void serializeExtraXmlAttrs(XmlSerializer serializer,
|
protected void serializeExtraXmlAttrs(XmlSerializer serializer,
|
||||||
ResResource res) throws IOException {
|
ResResource res) throws IOException {
|
||||||
if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(mRawValue)) {
|
if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(mRawValue)) {
|
||||||
serializer.attribute(null, "formatted", "false");
|
serializer.attribute(null, "formatted", "false");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,55 +28,55 @@ import org.xmlpull.v1.XmlSerializer;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResStyleValue extends ResBagValue implements
|
public class ResStyleValue extends ResBagValue implements
|
||||||
ResValuesXmlSerializable {
|
ResValuesXmlSerializable {
|
||||||
ResStyleValue(ResReferenceValue parent,
|
ResStyleValue(ResReferenceValue parent,
|
||||||
Duo<Integer, ResScalarValue>[] items, ResValueFactory factory) {
|
Duo<Integer, ResScalarValue>[] items, ResValueFactory factory) {
|
||||||
super(parent);
|
super(parent);
|
||||||
|
|
||||||
mItems = new Duo[items.length];
|
mItems = new Duo[items.length];
|
||||||
for (int i = 0; i < items.length; i++) {
|
for (int i = 0; i < items.length; i++) {
|
||||||
mItems[i] = new Duo<ResReferenceValue, ResScalarValue>(
|
mItems[i] = new Duo<ResReferenceValue, ResScalarValue>(
|
||||||
factory.newReference(items[i].m1, null), items[i].m2);
|
factory.newReference(items[i].m1, null), items[i].m2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serializeToResValuesXml(XmlSerializer serializer,
|
public void serializeToResValuesXml(XmlSerializer serializer,
|
||||||
ResResource res) throws IOException, AndrolibException {
|
ResResource res) throws IOException, AndrolibException {
|
||||||
serializer.startTag(null, "style");
|
serializer.startTag(null, "style");
|
||||||
serializer.attribute(null, "name", res.getResSpec().getName());
|
serializer.attribute(null, "name", res.getResSpec().getName());
|
||||||
if (!mParent.isNull()) {
|
if (!mParent.isNull()) {
|
||||||
serializer.attribute(null, "parent", mParent.encodeAsResXmlAttr());
|
serializer.attribute(null, "parent", mParent.encodeAsResXmlAttr());
|
||||||
} else if (res.getResSpec().getName().indexOf('.') != -1) {
|
} else if (res.getResSpec().getName().indexOf('.') != -1) {
|
||||||
serializer.attribute(null, "parent", "");
|
serializer.attribute(null, "parent", "");
|
||||||
}
|
}
|
||||||
for (int i = 0; i < mItems.length; i++) {
|
for (int i = 0; i < mItems.length; i++) {
|
||||||
ResResSpec spec = mItems[i].m1.getReferent();
|
ResResSpec spec = mItems[i].m1.getReferent();
|
||||||
|
|
||||||
// hacky-fix remove bad ReferenceVars
|
// hacky-fix remove bad ReferenceVars
|
||||||
if (spec.getDefaultResource().getValue().toString()
|
if (spec.getDefaultResource().getValue().toString()
|
||||||
.contains("ResReferenceValue@")) {
|
.contains("ResReferenceValue@")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ResAttr attr = (ResAttr) spec.getDefaultResource().getValue();
|
ResAttr attr = (ResAttr) spec.getDefaultResource().getValue();
|
||||||
String value = attr.convertToResXmlFormat(mItems[i].m2);
|
String value = attr.convertToResXmlFormat(mItems[i].m2);
|
||||||
|
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
value = mItems[i].m2.encodeAsResXmlValue();
|
value = mItems[i].m2.encodeAsResXmlValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
serializer.startTag(null, "item");
|
serializer.startTag(null, "item");
|
||||||
serializer.attribute(null, "name",
|
serializer.attribute(null, "name",
|
||||||
spec.getFullName(res.getResSpec().getPackage(), true));
|
spec.getFullName(res.getResSpec().getPackage(), true));
|
||||||
serializer.text(value);
|
serializer.text(value);
|
||||||
serializer.endTag(null, "item");
|
serializer.endTag(null, "item");
|
||||||
}
|
}
|
||||||
serializer.endTag(null, "style");
|
serializer.endTag(null, "style");
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Duo<ResReferenceValue, ResScalarValue>[] mItems;
|
private final Duo<ResReferenceValue, ResScalarValue>[] mItems;
|
||||||
}
|
}
|
||||||
|
@ -25,77 +25,77 @@ import brut.util.Duo;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResValueFactory {
|
public class ResValueFactory {
|
||||||
private final ResPackage mPackage;
|
private final ResPackage mPackage;
|
||||||
|
|
||||||
public ResValueFactory(ResPackage pakage_) {
|
public ResValueFactory(ResPackage pakage_) {
|
||||||
this.mPackage = pakage_;
|
this.mPackage = pakage_;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResScalarValue factory(int type, int value, String rawValue)
|
public ResScalarValue factory(int type, int value, String rawValue)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case TypedValue.TYPE_REFERENCE:
|
case TypedValue.TYPE_REFERENCE:
|
||||||
return newReference(value, rawValue);
|
return newReference(value, rawValue);
|
||||||
case TypedValue.TYPE_ATTRIBUTE:
|
case TypedValue.TYPE_ATTRIBUTE:
|
||||||
return newReference(value, rawValue, true);
|
return newReference(value, rawValue, true);
|
||||||
case TypedValue.TYPE_STRING:
|
case TypedValue.TYPE_STRING:
|
||||||
return new ResStringValue(rawValue);
|
return new ResStringValue(rawValue);
|
||||||
case TypedValue.TYPE_FLOAT:
|
case TypedValue.TYPE_FLOAT:
|
||||||
return new ResFloatValue(Float.intBitsToFloat(value), rawValue);
|
return new ResFloatValue(Float.intBitsToFloat(value), rawValue);
|
||||||
case TypedValue.TYPE_DIMENSION:
|
case TypedValue.TYPE_DIMENSION:
|
||||||
return new ResDimenValue(value, rawValue);
|
return new ResDimenValue(value, rawValue);
|
||||||
case TypedValue.TYPE_FRACTION:
|
case TypedValue.TYPE_FRACTION:
|
||||||
return new ResFractionValue(value, rawValue);
|
return new ResFractionValue(value, rawValue);
|
||||||
case TypedValue.TYPE_INT_BOOLEAN:
|
case TypedValue.TYPE_INT_BOOLEAN:
|
||||||
return new ResBoolValue(value != 0, rawValue);
|
return new ResBoolValue(value != 0, rawValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type >= TypedValue.TYPE_FIRST_COLOR_INT
|
if (type >= TypedValue.TYPE_FIRST_COLOR_INT
|
||||||
&& type <= TypedValue.TYPE_LAST_COLOR_INT) {
|
&& type <= TypedValue.TYPE_LAST_COLOR_INT) {
|
||||||
return new ResColorValue(value, rawValue);
|
return new ResColorValue(value, rawValue);
|
||||||
}
|
}
|
||||||
if (type >= TypedValue.TYPE_FIRST_INT
|
if (type >= TypedValue.TYPE_FIRST_INT
|
||||||
&& type <= TypedValue.TYPE_LAST_INT) {
|
&& type <= TypedValue.TYPE_LAST_INT) {
|
||||||
return new ResIntValue(value, rawValue, type);
|
return new ResIntValue(value, rawValue, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new AndrolibException("Invalid value type: " + type);
|
throw new AndrolibException("Invalid value type: " + type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResValue factory(String value) {
|
public ResValue factory(String value) {
|
||||||
if (value.startsWith("res/")) {
|
if (value.startsWith("res/")) {
|
||||||
return new ResFileValue(value);
|
return new ResFileValue(value);
|
||||||
}
|
}
|
||||||
return new ResStringValue(value);
|
return new ResStringValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResBagValue bagFactory(int parent,
|
public ResBagValue bagFactory(int parent,
|
||||||
Duo<Integer, ResScalarValue>[] items) throws AndrolibException {
|
Duo<Integer, ResScalarValue>[] items) throws AndrolibException {
|
||||||
ResReferenceValue parentVal = newReference(parent, null);
|
ResReferenceValue parentVal = newReference(parent, null);
|
||||||
|
|
||||||
if (items.length == 0) {
|
if (items.length == 0) {
|
||||||
return new ResBagValue(parentVal);
|
return new ResBagValue(parentVal);
|
||||||
}
|
}
|
||||||
int key = items[0].m1;
|
int key = items[0].m1;
|
||||||
if (key == ResAttr.BAG_KEY_ATTR_TYPE) {
|
if (key == ResAttr.BAG_KEY_ATTR_TYPE) {
|
||||||
return ResAttr.factory(parentVal, items, this, mPackage);
|
return ResAttr.factory(parentVal, items, this, mPackage);
|
||||||
}
|
}
|
||||||
if (key == ResArrayValue.BAG_KEY_ARRAY_START) {
|
if (key == ResArrayValue.BAG_KEY_ARRAY_START) {
|
||||||
return new ResArrayValue(parentVal, items);
|
return new ResArrayValue(parentVal, items);
|
||||||
}
|
}
|
||||||
if (key >= ResPluralsValue.BAG_KEY_PLURALS_START
|
if (key >= ResPluralsValue.BAG_KEY_PLURALS_START
|
||||||
&& key <= ResPluralsValue.BAG_KEY_PLURALS_END) {
|
&& key <= ResPluralsValue.BAG_KEY_PLURALS_END) {
|
||||||
return new ResPluralsValue(parentVal, items);
|
return new ResPluralsValue(parentVal, items);
|
||||||
}
|
}
|
||||||
return new ResStyleValue(parentVal, items, this);
|
return new ResStyleValue(parentVal, items, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResReferenceValue newReference(int resID, String rawValue) {
|
public ResReferenceValue newReference(int resID, String rawValue) {
|
||||||
return newReference(resID, rawValue, false);
|
return newReference(resID, rawValue, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResReferenceValue newReference(int resID, String rawValue,
|
public ResReferenceValue newReference(int resID, String rawValue,
|
||||||
boolean theme) {
|
boolean theme) {
|
||||||
return new ResReferenceValue(mPackage, resID, rawValue, theme);
|
return new ResReferenceValue(mPackage, resID, rawValue, theme);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -34,391 +34,391 @@ import org.apache.commons.io.input.CountingInputStream;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ARSCDecoder {
|
public class ARSCDecoder {
|
||||||
public static ARSCData decode(InputStream arscStream,
|
public static ARSCData decode(InputStream arscStream,
|
||||||
boolean findFlagsOffsets, boolean keepBroken)
|
boolean findFlagsOffsets, boolean keepBroken)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
return decode(arscStream, findFlagsOffsets, keepBroken, new ResTable());
|
return decode(arscStream, findFlagsOffsets, keepBroken, new ResTable());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ARSCData decode(InputStream arscStream,
|
public static ARSCData decode(InputStream arscStream,
|
||||||
boolean findFlagsOffsets, boolean keepBroken, ResTable resTable)
|
boolean findFlagsOffsets, boolean keepBroken, ResTable resTable)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
try {
|
try {
|
||||||
ARSCDecoder decoder = new ARSCDecoder(arscStream, resTable,
|
ARSCDecoder decoder = new ARSCDecoder(arscStream, resTable,
|
||||||
findFlagsOffsets, keepBroken);
|
findFlagsOffsets, keepBroken);
|
||||||
ResPackage[] pkgs = decoder.readTable();
|
ResPackage[] pkgs = decoder.readTable();
|
||||||
return new ARSCData(pkgs, decoder.mFlagsOffsets == null ? null
|
return new ARSCData(pkgs, decoder.mFlagsOffsets == null ? null
|
||||||
: decoder.mFlagsOffsets.toArray(new FlagsOffset[0]),
|
: decoder.mFlagsOffsets.toArray(new FlagsOffset[0]),
|
||||||
resTable);
|
resTable);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new AndrolibException("Could not decode arsc file", ex);
|
throw new AndrolibException("Could not decode arsc file", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ARSCDecoder(InputStream arscStream, ResTable resTable,
|
private ARSCDecoder(InputStream arscStream, ResTable resTable,
|
||||||
boolean storeFlagsOffsets, boolean keepBroken) {
|
boolean storeFlagsOffsets, boolean keepBroken) {
|
||||||
if (storeFlagsOffsets) {
|
if (storeFlagsOffsets) {
|
||||||
arscStream = mCountIn = new CountingInputStream(arscStream);
|
arscStream = mCountIn = new CountingInputStream(arscStream);
|
||||||
mFlagsOffsets = new ArrayList<FlagsOffset>();
|
mFlagsOffsets = new ArrayList<FlagsOffset>();
|
||||||
} else {
|
} else {
|
||||||
mCountIn = null;
|
mCountIn = null;
|
||||||
mFlagsOffsets = null;
|
mFlagsOffsets = null;
|
||||||
}
|
}
|
||||||
mIn = new ExtDataInput(new LEDataInputStream(arscStream));
|
mIn = new ExtDataInput(new LEDataInputStream(arscStream));
|
||||||
mResTable = resTable;
|
mResTable = resTable;
|
||||||
mKeepBroken = keepBroken;
|
mKeepBroken = keepBroken;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResPackage[] readTable() throws IOException, AndrolibException {
|
private ResPackage[] readTable() throws IOException, AndrolibException {
|
||||||
nextChunkCheckType(Header.TYPE_TABLE);
|
nextChunkCheckType(Header.TYPE_TABLE);
|
||||||
int packageCount = mIn.readInt();
|
int packageCount = mIn.readInt();
|
||||||
|
|
||||||
mTableStrings = StringBlock.read(mIn);
|
mTableStrings = StringBlock.read(mIn);
|
||||||
ResPackage[] packages = new ResPackage[packageCount];
|
ResPackage[] packages = new ResPackage[packageCount];
|
||||||
|
|
||||||
nextChunk();
|
nextChunk();
|
||||||
for (int i = 0; i < packageCount; i++) {
|
for (int i = 0; i < packageCount; i++) {
|
||||||
packages[i] = readPackage();
|
packages[i] = readPackage();
|
||||||
}
|
}
|
||||||
return packages;
|
return packages;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResPackage readPackage() throws IOException, AndrolibException {
|
private ResPackage readPackage() throws IOException, AndrolibException {
|
||||||
checkChunkType(Header.TYPE_PACKAGE);
|
checkChunkType(Header.TYPE_PACKAGE);
|
||||||
int id = (byte) mIn.readInt();
|
int id = (byte) mIn.readInt();
|
||||||
String name = mIn.readNulEndedString(128, true);
|
String name = mIn.readNulEndedString(128, true);
|
||||||
/* typeNameStrings */mIn.skipInt();
|
/* typeNameStrings */mIn.skipInt();
|
||||||
/* typeNameCount */mIn.skipInt();
|
/* typeNameCount */mIn.skipInt();
|
||||||
/* specNameStrings */mIn.skipInt();
|
/* specNameStrings */mIn.skipInt();
|
||||||
/* specNameCount */mIn.skipInt();
|
/* specNameCount */mIn.skipInt();
|
||||||
|
|
||||||
mTypeNames = StringBlock.read(mIn);
|
mTypeNames = StringBlock.read(mIn);
|
||||||
mSpecNames = StringBlock.read(mIn);
|
mSpecNames = StringBlock.read(mIn);
|
||||||
|
|
||||||
mResId = id << 24;
|
mResId = id << 24;
|
||||||
mPkg = new ResPackage(mResTable, id, name);
|
mPkg = new ResPackage(mResTable, id, name);
|
||||||
|
|
||||||
nextChunk();
|
nextChunk();
|
||||||
while (mHeader.type == Header.TYPE_TYPE) {
|
while (mHeader.type == Header.TYPE_TYPE) {
|
||||||
readType();
|
readType();
|
||||||
}
|
}
|
||||||
|
|
||||||
return mPkg;
|
return mPkg;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResType readType() throws AndrolibException, IOException {
|
private ResType readType() throws AndrolibException, IOException {
|
||||||
checkChunkType(Header.TYPE_TYPE);
|
checkChunkType(Header.TYPE_TYPE);
|
||||||
byte id = mIn.readByte();
|
byte id = mIn.readByte();
|
||||||
mIn.skipBytes(3);
|
mIn.skipBytes(3);
|
||||||
int entryCount = mIn.readInt();
|
int entryCount = mIn.readInt();
|
||||||
|
|
||||||
mMissingResSpecs = new boolean[entryCount];
|
mMissingResSpecs = new boolean[entryCount];
|
||||||
Arrays.fill(mMissingResSpecs, true);
|
Arrays.fill(mMissingResSpecs, true);
|
||||||
|
|
||||||
if (mFlagsOffsets != null) {
|
if (mFlagsOffsets != null) {
|
||||||
mFlagsOffsets.add(new FlagsOffset(mCountIn.getCount(), entryCount));
|
mFlagsOffsets.add(new FlagsOffset(mCountIn.getCount(), entryCount));
|
||||||
}
|
}
|
||||||
/* flags */mIn.skipBytes(entryCount * 4);
|
/* flags */mIn.skipBytes(entryCount * 4);
|
||||||
|
|
||||||
mResId = (0xff000000 & mResId) | id << 16;
|
mResId = (0xff000000 & mResId) | id << 16;
|
||||||
mType = new ResType(mTypeNames.getString(id - 1), mResTable, mPkg);
|
mType = new ResType(mTypeNames.getString(id - 1), mResTable, mPkg);
|
||||||
mPkg.addType(mType);
|
mPkg.addType(mType);
|
||||||
|
|
||||||
while (nextChunk().type == Header.TYPE_CONFIG) {
|
while (nextChunk().type == Header.TYPE_CONFIG) {
|
||||||
readConfig();
|
readConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
addMissingResSpecs();
|
addMissingResSpecs();
|
||||||
|
|
||||||
return mType;
|
return mType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResConfig readConfig() throws IOException, AndrolibException {
|
private ResConfig readConfig() throws IOException, AndrolibException {
|
||||||
checkChunkType(Header.TYPE_CONFIG);
|
checkChunkType(Header.TYPE_CONFIG);
|
||||||
/* typeId */mIn.skipInt();
|
/* typeId */mIn.skipInt();
|
||||||
int entryCount = mIn.readInt();
|
int entryCount = mIn.readInt();
|
||||||
/* entriesStart */mIn.skipInt();
|
/* entriesStart */mIn.skipInt();
|
||||||
|
|
||||||
ResConfigFlags flags = readConfigFlags();
|
ResConfigFlags flags = readConfigFlags();
|
||||||
int[] entryOffsets = mIn.readIntArray(entryCount);
|
int[] entryOffsets = mIn.readIntArray(entryCount);
|
||||||
|
|
||||||
if (flags.isInvalid) {
|
if (flags.isInvalid) {
|
||||||
String resName = mType.getName() + flags.getQualifiers();
|
String resName = mType.getName() + flags.getQualifiers();
|
||||||
if (mKeepBroken) {
|
if (mKeepBroken) {
|
||||||
LOGGER.warning("Invalid config flags detected: " + resName);
|
LOGGER.warning("Invalid config flags detected: " + resName);
|
||||||
} else {
|
} else {
|
||||||
LOGGER.warning("Invalid config flags detected. Dropping resources: "
|
LOGGER.warning("Invalid config flags detected. Dropping resources: "
|
||||||
+ resName);
|
+ resName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mConfig = flags.isInvalid && !mKeepBroken ? null : mPkg
|
mConfig = flags.isInvalid && !mKeepBroken ? null : mPkg
|
||||||
.getOrCreateConfig(flags);
|
.getOrCreateConfig(flags);
|
||||||
|
|
||||||
for (int i = 0; i < entryOffsets.length; i++) {
|
for (int i = 0; i < entryOffsets.length; i++) {
|
||||||
if (entryOffsets[i] != -1) {
|
if (entryOffsets[i] != -1) {
|
||||||
mMissingResSpecs[i] = false;
|
mMissingResSpecs[i] = false;
|
||||||
mResId = (mResId & 0xffff0000) | i;
|
mResId = (mResId & 0xffff0000) | i;
|
||||||
readEntry();
|
readEntry();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return mConfig;
|
return mConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readEntry() throws IOException, AndrolibException {
|
private void readEntry() throws IOException, AndrolibException {
|
||||||
/* size */mIn.skipBytes(2);
|
/* size */mIn.skipBytes(2);
|
||||||
short flags = mIn.readShort();
|
short flags = mIn.readShort();
|
||||||
int specNamesId = mIn.readInt();
|
int specNamesId = mIn.readInt();
|
||||||
|
|
||||||
ResValue value = (flags & ENTRY_FLAG_COMPLEX) == 0 ? readValue()
|
ResValue value = (flags & ENTRY_FLAG_COMPLEX) == 0 ? readValue()
|
||||||
: readComplexEntry();
|
: readComplexEntry();
|
||||||
|
|
||||||
if (mConfig == null) {
|
if (mConfig == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResID resId = new ResID(mResId);
|
ResID resId = new ResID(mResId);
|
||||||
ResResSpec spec;
|
ResResSpec spec;
|
||||||
if (mPkg.hasResSpec(resId)) {
|
if (mPkg.hasResSpec(resId)) {
|
||||||
spec = mPkg.getResSpec(resId);
|
spec = mPkg.getResSpec(resId);
|
||||||
} else {
|
} else {
|
||||||
spec = new ResResSpec(resId, mSpecNames.getString(specNamesId),
|
spec = new ResResSpec(resId, mSpecNames.getString(specNamesId),
|
||||||
mPkg, mType);
|
mPkg, mType);
|
||||||
mPkg.addResSpec(spec);
|
mPkg.addResSpec(spec);
|
||||||
mType.addResSpec(spec);
|
mType.addResSpec(spec);
|
||||||
}
|
}
|
||||||
ResResource res = new ResResource(mConfig, spec, value);
|
ResResource res = new ResResource(mConfig, spec, value);
|
||||||
|
|
||||||
mConfig.addResource(res);
|
mConfig.addResource(res);
|
||||||
spec.addResource(res);
|
spec.addResource(res);
|
||||||
mPkg.addResource(res);
|
mPkg.addResource(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResBagValue readComplexEntry() throws IOException,
|
private ResBagValue readComplexEntry() throws IOException,
|
||||||
AndrolibException {
|
AndrolibException {
|
||||||
int parent = mIn.readInt();
|
int parent = mIn.readInt();
|
||||||
int count = mIn.readInt();
|
int count = mIn.readInt();
|
||||||
|
|
||||||
ResValueFactory factory = mPkg.getValueFactory();
|
ResValueFactory factory = mPkg.getValueFactory();
|
||||||
Duo<Integer, ResScalarValue>[] items = new Duo[count];
|
Duo<Integer, ResScalarValue>[] items = new Duo[count];
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
items[i] = new Duo<Integer, ResScalarValue>(mIn.readInt(),
|
items[i] = new Duo<Integer, ResScalarValue>(mIn.readInt(),
|
||||||
(ResScalarValue) readValue());
|
(ResScalarValue) readValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
return factory.bagFactory(parent, items);
|
return factory.bagFactory(parent, items);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResValue readValue() throws IOException, AndrolibException {
|
private ResValue readValue() throws IOException, AndrolibException {
|
||||||
/* size */mIn.skipCheckShort((short) 8);
|
/* size */mIn.skipCheckShort((short) 8);
|
||||||
/* zero */mIn.skipCheckByte((byte) 0);
|
/* zero */mIn.skipCheckByte((byte) 0);
|
||||||
byte type = mIn.readByte();
|
byte type = mIn.readByte();
|
||||||
int data = mIn.readInt();
|
int data = mIn.readInt();
|
||||||
|
|
||||||
return type == TypedValue.TYPE_STRING ? mPkg.getValueFactory().factory(
|
return type == TypedValue.TYPE_STRING ? mPkg.getValueFactory().factory(
|
||||||
mTableStrings.getHTML(data)) : mPkg.getValueFactory().factory(
|
mTableStrings.getHTML(data)) : mPkg.getValueFactory().factory(
|
||||||
type, data, null);
|
type, data, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResConfigFlags readConfigFlags() throws IOException,
|
private ResConfigFlags readConfigFlags() throws IOException,
|
||||||
AndrolibException {
|
AndrolibException {
|
||||||
int size = mIn.readInt();
|
int size = mIn.readInt();
|
||||||
if (size < 28) {
|
if (size < 28) {
|
||||||
throw new AndrolibException("Config size < 28");
|
throw new AndrolibException("Config size < 28");
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isInvalid = false;
|
boolean isInvalid = false;
|
||||||
|
|
||||||
short mcc = mIn.readShort();
|
short mcc = mIn.readShort();
|
||||||
short mnc = mIn.readShort();
|
short mnc = mIn.readShort();
|
||||||
|
|
||||||
char[] language = new char[] { (char) mIn.readByte(),
|
char[] language = new char[] { (char) mIn.readByte(),
|
||||||
(char) mIn.readByte() };
|
(char) mIn.readByte() };
|
||||||
char[] country = new char[] { (char) mIn.readByte(),
|
char[] country = new char[] { (char) mIn.readByte(),
|
||||||
(char) mIn.readByte() };
|
(char) mIn.readByte() };
|
||||||
|
|
||||||
byte orientation = mIn.readByte();
|
byte orientation = mIn.readByte();
|
||||||
byte touchscreen = mIn.readByte();
|
byte touchscreen = mIn.readByte();
|
||||||
|
|
||||||
int density = mIn.readUnsignedShort();
|
int density = mIn.readUnsignedShort();
|
||||||
|
|
||||||
byte keyboard = mIn.readByte();
|
byte keyboard = mIn.readByte();
|
||||||
byte navigation = mIn.readByte();
|
byte navigation = mIn.readByte();
|
||||||
byte inputFlags = mIn.readByte();
|
byte inputFlags = mIn.readByte();
|
||||||
/* inputPad0 */mIn.skipBytes(1);
|
/* inputPad0 */mIn.skipBytes(1);
|
||||||
|
|
||||||
short screenWidth = mIn.readShort();
|
short screenWidth = mIn.readShort();
|
||||||
short screenHeight = mIn.readShort();
|
short screenHeight = mIn.readShort();
|
||||||
|
|
||||||
short sdkVersion = mIn.readShort();
|
short sdkVersion = mIn.readShort();
|
||||||
/* minorVersion, now must always be 0 */mIn.skipBytes(2);
|
/* minorVersion, now must always be 0 */mIn.skipBytes(2);
|
||||||
|
|
||||||
byte screenLayout = 0;
|
byte screenLayout = 0;
|
||||||
byte uiMode = 0;
|
byte uiMode = 0;
|
||||||
short smallestScreenWidthDp = 0;
|
short smallestScreenWidthDp = 0;
|
||||||
if (size >= 32) {
|
if (size >= 32) {
|
||||||
screenLayout = mIn.readByte();
|
screenLayout = mIn.readByte();
|
||||||
uiMode = mIn.readByte();
|
uiMode = mIn.readByte();
|
||||||
smallestScreenWidthDp = mIn.readShort();
|
smallestScreenWidthDp = mIn.readShort();
|
||||||
}
|
}
|
||||||
|
|
||||||
short screenWidthDp = 0;
|
short screenWidthDp = 0;
|
||||||
short screenHeightDp = 0;
|
short screenHeightDp = 0;
|
||||||
if (size >= 36) {
|
if (size >= 36) {
|
||||||
screenWidthDp = mIn.readShort();
|
screenWidthDp = mIn.readShort();
|
||||||
screenHeightDp = mIn.readShort();
|
screenHeightDp = mIn.readShort();
|
||||||
}
|
}
|
||||||
|
|
||||||
short layoutDirection = 0;
|
short layoutDirection = 0;
|
||||||
if (size >= 38) {
|
if (size >= 38) {
|
||||||
layoutDirection = mIn.readShort();
|
layoutDirection = mIn.readShort();
|
||||||
}
|
}
|
||||||
|
|
||||||
int exceedingSize = size - KNOWN_CONFIG_BYTES;
|
int exceedingSize = size - KNOWN_CONFIG_BYTES;
|
||||||
if (exceedingSize > 0) {
|
if (exceedingSize > 0) {
|
||||||
byte[] buf = new byte[exceedingSize];
|
byte[] buf = new byte[exceedingSize];
|
||||||
mIn.readFully(buf);
|
mIn.readFully(buf);
|
||||||
BigInteger exceedingBI = new BigInteger(1, buf);
|
BigInteger exceedingBI = new BigInteger(1, buf);
|
||||||
|
|
||||||
if (exceedingBI.equals(BigInteger.ZERO)) {
|
if (exceedingBI.equals(BigInteger.ZERO)) {
|
||||||
LOGGER.fine(String
|
LOGGER.fine(String
|
||||||
.format("Config flags size > %d, but exceeding bytes are all zero, so it should be ok.",
|
.format("Config flags size > %d, but exceeding bytes are all zero, so it should be ok.",
|
||||||
KNOWN_CONFIG_BYTES));
|
KNOWN_CONFIG_BYTES));
|
||||||
} else {
|
} else {
|
||||||
LOGGER.warning(String.format(
|
LOGGER.warning(String.format(
|
||||||
"Config flags size > %d. Exceeding bytes: 0x%X.",
|
"Config flags size > %d. Exceeding bytes: 0x%X.",
|
||||||
KNOWN_CONFIG_BYTES, exceedingBI));
|
KNOWN_CONFIG_BYTES, exceedingBI));
|
||||||
isInvalid = true;
|
isInvalid = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ResConfigFlags(mcc, mnc, language, country, layoutDirection,
|
return new ResConfigFlags(mcc, mnc, language, country, layoutDirection,
|
||||||
orientation, touchscreen, density, keyboard, navigation,
|
orientation, touchscreen, density, keyboard, navigation,
|
||||||
inputFlags, screenWidth, screenHeight, sdkVersion,
|
inputFlags, screenWidth, screenHeight, sdkVersion,
|
||||||
screenLayout, uiMode, smallestScreenWidthDp, screenWidthDp,
|
screenLayout, uiMode, smallestScreenWidthDp, screenWidthDp,
|
||||||
screenHeightDp, isInvalid);
|
screenHeightDp, isInvalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addMissingResSpecs() throws AndrolibException {
|
private void addMissingResSpecs() throws AndrolibException {
|
||||||
int resId = mResId & 0xffff0000;
|
int resId = mResId & 0xffff0000;
|
||||||
|
|
||||||
for (int i = 0; i < mMissingResSpecs.length; i++) {
|
for (int i = 0; i < mMissingResSpecs.length; i++) {
|
||||||
if (!mMissingResSpecs[i]) {
|
if (!mMissingResSpecs[i]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResResSpec spec = new ResResSpec(new ResID(resId | i),
|
ResResSpec spec = new ResResSpec(new ResID(resId | i),
|
||||||
String.format("APKTOOL_DUMMY_%04x", i), mPkg, mType);
|
String.format("APKTOOL_DUMMY_%04x", i), mPkg, mType);
|
||||||
mPkg.addResSpec(spec);
|
mPkg.addResSpec(spec);
|
||||||
mType.addResSpec(spec);
|
mType.addResSpec(spec);
|
||||||
|
|
||||||
if (mConfig == null) {
|
if (mConfig == null) {
|
||||||
mConfig = mPkg.getOrCreateConfig(new ResConfigFlags());
|
mConfig = mPkg.getOrCreateConfig(new ResConfigFlags());
|
||||||
}
|
}
|
||||||
|
|
||||||
ResValue value = new ResBoolValue(false, null);
|
ResValue value = new ResBoolValue(false, null);
|
||||||
ResResource res = new ResResource(mConfig, spec, value);
|
ResResource res = new ResResource(mConfig, spec, value);
|
||||||
|
|
||||||
mPkg.addResource(res);
|
mPkg.addResource(res);
|
||||||
mConfig.addResource(res);
|
mConfig.addResource(res);
|
||||||
spec.addResource(res);
|
spec.addResource(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Header nextChunk() throws IOException {
|
private Header nextChunk() throws IOException {
|
||||||
return mHeader = Header.read(mIn);
|
return mHeader = Header.read(mIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkChunkType(int expectedType) throws AndrolibException {
|
private void checkChunkType(int expectedType) throws AndrolibException {
|
||||||
if (mHeader.type != expectedType) {
|
if (mHeader.type != expectedType) {
|
||||||
throw new AndrolibException(String.format(
|
throw new AndrolibException(String.format(
|
||||||
"Invalid chunk type: expected=0x%08x, got=0x%08x",
|
"Invalid chunk type: expected=0x%08x, got=0x%08x",
|
||||||
expectedType, mHeader.type));
|
expectedType, mHeader.type));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void nextChunkCheckType(int expectedType) throws IOException,
|
private void nextChunkCheckType(int expectedType) throws IOException,
|
||||||
AndrolibException {
|
AndrolibException {
|
||||||
nextChunk();
|
nextChunk();
|
||||||
checkChunkType(expectedType);
|
checkChunkType(expectedType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ExtDataInput mIn;
|
private final ExtDataInput mIn;
|
||||||
private final ResTable mResTable;
|
private final ResTable mResTable;
|
||||||
private final CountingInputStream mCountIn;
|
private final CountingInputStream mCountIn;
|
||||||
private final List<FlagsOffset> mFlagsOffsets;
|
private final List<FlagsOffset> mFlagsOffsets;
|
||||||
private final boolean mKeepBroken;
|
private final boolean mKeepBroken;
|
||||||
|
|
||||||
private Header mHeader;
|
private Header mHeader;
|
||||||
private StringBlock mTableStrings;
|
private StringBlock mTableStrings;
|
||||||
private StringBlock mTypeNames;
|
private StringBlock mTypeNames;
|
||||||
private StringBlock mSpecNames;
|
private StringBlock mSpecNames;
|
||||||
private ResPackage mPkg;
|
private ResPackage mPkg;
|
||||||
private ResType mType;
|
private ResType mType;
|
||||||
private ResConfig mConfig;
|
private ResConfig mConfig;
|
||||||
private int mResId;
|
private int mResId;
|
||||||
private boolean[] mMissingResSpecs;
|
private boolean[] mMissingResSpecs;
|
||||||
|
|
||||||
private final static short ENTRY_FLAG_COMPLEX = 0x0001;
|
private final static short ENTRY_FLAG_COMPLEX = 0x0001;
|
||||||
|
|
||||||
public static class Header {
|
public static class Header {
|
||||||
public final short type;
|
public final short type;
|
||||||
public final int chunkSize;
|
public final int chunkSize;
|
||||||
|
|
||||||
public Header(short type, int size) {
|
public Header(short type, int size) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.chunkSize = size;
|
this.chunkSize = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Header read(ExtDataInput in) throws IOException {
|
public static Header read(ExtDataInput in) throws IOException {
|
||||||
short type;
|
short type;
|
||||||
try {
|
try {
|
||||||
type = in.readShort();
|
type = in.readShort();
|
||||||
} catch (EOFException ex) {
|
} catch (EOFException ex) {
|
||||||
return new Header(TYPE_NONE, 0);
|
return new Header(TYPE_NONE, 0);
|
||||||
}
|
}
|
||||||
in.skipBytes(2);
|
in.skipBytes(2);
|
||||||
return new Header(type, in.readInt());
|
return new Header(type, in.readInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
public final static short TYPE_NONE = -1, TYPE_TABLE = 0x0002,
|
public final static short TYPE_NONE = -1, TYPE_TABLE = 0x0002,
|
||||||
TYPE_PACKAGE = 0x0200, TYPE_TYPE = 0x0202,
|
TYPE_PACKAGE = 0x0200, TYPE_TYPE = 0x0202,
|
||||||
TYPE_CONFIG = 0x0201;
|
TYPE_CONFIG = 0x0201;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class FlagsOffset {
|
public static class FlagsOffset {
|
||||||
public final int offset;
|
public final int offset;
|
||||||
public final int count;
|
public final int count;
|
||||||
|
|
||||||
public FlagsOffset(int offset, int count) {
|
public FlagsOffset(int offset, int count) {
|
||||||
this.offset = offset;
|
this.offset = offset;
|
||||||
this.count = count;
|
this.count = count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(ARSCDecoder.class
|
private static final Logger LOGGER = Logger.getLogger(ARSCDecoder.class
|
||||||
.getName());
|
.getName());
|
||||||
private static final int KNOWN_CONFIG_BYTES = 38;
|
private static final int KNOWN_CONFIG_BYTES = 38;
|
||||||
|
|
||||||
public static class ARSCData {
|
public static class ARSCData {
|
||||||
|
|
||||||
public ARSCData(ResPackage[] packages, FlagsOffset[] flagsOffsets,
|
public ARSCData(ResPackage[] packages, FlagsOffset[] flagsOffsets,
|
||||||
ResTable resTable) {
|
ResTable resTable) {
|
||||||
mPackages = packages;
|
mPackages = packages;
|
||||||
mFlagsOffsets = flagsOffsets;
|
mFlagsOffsets = flagsOffsets;
|
||||||
mResTable = resTable;
|
mResTable = resTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FlagsOffset[] getFlagsOffsets() {
|
public FlagsOffset[] getFlagsOffsets() {
|
||||||
return mFlagsOffsets;
|
return mFlagsOffsets;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResPackage[] getPackages() {
|
public ResPackage[] getPackages() {
|
||||||
return mPackages;
|
return mPackages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResPackage getOnePackage() throws AndrolibException {
|
public ResPackage getOnePackage() throws AndrolibException {
|
||||||
if (mPackages.length <= 0) {
|
if (mPackages.length <= 0) {
|
||||||
throw new AndrolibException(
|
throw new AndrolibException(
|
||||||
"Arsc file contains zero packages");
|
"Arsc file contains zero packages");
|
||||||
@ -426,9 +426,9 @@ public class ARSCDecoder {
|
|||||||
int id = findPackageWithMostResSpecs();
|
int id = findPackageWithMostResSpecs();
|
||||||
LOGGER.warning("Arsc file contains multiple packages. Using package " + mPackages[id].getName() + " as default.");
|
LOGGER.warning("Arsc file contains multiple packages. Using package " + mPackages[id].getName() + " as default.");
|
||||||
return mPackages[id];
|
return mPackages[id];
|
||||||
}
|
}
|
||||||
return mPackages[0];
|
return mPackages[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
public int findPackageWithMostResSpecs() {
|
public int findPackageWithMostResSpecs() {
|
||||||
int count = -1;
|
int count = -1;
|
||||||
@ -448,12 +448,12 @@ public class ARSCDecoder {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResTable getResTable() {
|
public ResTable getResTable() {
|
||||||
return mResTable;
|
return mResTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ResPackage[] mPackages;
|
private final ResPackage[] mPackages;
|
||||||
private final FlagsOffset[] mFlagsOffsets;
|
private final FlagsOffset[] mFlagsOffsets;
|
||||||
private final ResTable mResTable;
|
private final ResTable mResTable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -30,113 +30,113 @@ import org.apache.commons.io.IOUtils;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class Res9patchStreamDecoder implements ResStreamDecoder {
|
public class Res9patchStreamDecoder implements ResStreamDecoder {
|
||||||
@Override
|
@Override
|
||||||
public void decode(InputStream in, OutputStream out)
|
public void decode(InputStream in, OutputStream out)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
try {
|
try {
|
||||||
byte[] data = IOUtils.toByteArray(in);
|
byte[] data = IOUtils.toByteArray(in);
|
||||||
|
|
||||||
BufferedImage im = ImageIO.read(new ByteArrayInputStream(data));
|
BufferedImage im = ImageIO.read(new ByteArrayInputStream(data));
|
||||||
int w = im.getWidth(), h = im.getHeight();
|
int w = im.getWidth(), h = im.getHeight();
|
||||||
|
|
||||||
ImageTypeSpecifier its = ImageTypeSpecifier.createFromRenderedImage( im );
|
ImageTypeSpecifier its = ImageTypeSpecifier.createFromRenderedImage( im );
|
||||||
BufferedImage im2 = its.createBufferedImage( w+2, h+2 );
|
BufferedImage im2 = its.createBufferedImage( w+2, h+2 );
|
||||||
|
|
||||||
im2.getRaster().setRect(1, 1, im.getRaster());
|
im2.getRaster().setRect(1, 1, im.getRaster());
|
||||||
NinePatch np = getNinePatch(data);
|
NinePatch np = getNinePatch(data);
|
||||||
drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight);
|
drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight);
|
||||||
drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom);
|
drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom);
|
||||||
|
|
||||||
int[] xDivs = np.xDivs;
|
int[] xDivs = np.xDivs;
|
||||||
for (int i = 0; i < xDivs.length; i += 2) {
|
for (int i = 0; i < xDivs.length; i += 2) {
|
||||||
drawHLine(im2, 0, xDivs[i] + 1, xDivs[i + 1]);
|
drawHLine(im2, 0, xDivs[i] + 1, xDivs[i + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
int[] yDivs = np.yDivs;
|
int[] yDivs = np.yDivs;
|
||||||
for (int i = 0; i < yDivs.length; i += 2) {
|
for (int i = 0; i < yDivs.length; i += 2) {
|
||||||
drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]);
|
drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageIO.write(im2, "png", out);
|
ImageIO.write(im2, "png", out);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new AndrolibException(ex);
|
throw new AndrolibException(ex);
|
||||||
} catch (NullPointerException ex) {
|
} catch (NullPointerException ex) {
|
||||||
// In my case this was triggered because a .png file was
|
// In my case this was triggered because a .png file was
|
||||||
// containing a html document instead of an image.
|
// containing a html document instead of an image.
|
||||||
// This could be more verbose and try to MIME ?
|
// This could be more verbose and try to MIME ?
|
||||||
throw new AndrolibException(ex);
|
throw new AndrolibException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private NinePatch getNinePatch(byte[] data) throws AndrolibException,
|
private NinePatch getNinePatch(byte[] data) throws AndrolibException,
|
||||||
IOException {
|
IOException {
|
||||||
ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data));
|
ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data));
|
||||||
find9patchChunk(di);
|
find9patchChunk(di);
|
||||||
return NinePatch.decode(di);
|
return NinePatch.decode(di);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void find9patchChunk(DataInput di) throws AndrolibException,
|
private void find9patchChunk(DataInput di) throws AndrolibException,
|
||||||
IOException {
|
IOException {
|
||||||
di.skipBytes(8);
|
di.skipBytes(8);
|
||||||
while (true) {
|
while (true) {
|
||||||
int size;
|
int size;
|
||||||
try {
|
try {
|
||||||
size = di.readInt();
|
size = di.readInt();
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new CantFind9PatchChunk("Cant find nine patch chunk", ex);
|
throw new CantFind9PatchChunk("Cant find nine patch chunk", ex);
|
||||||
}
|
}
|
||||||
if (di.readInt() == NP_CHUNK_TYPE) {
|
if (di.readInt() == NP_CHUNK_TYPE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
di.skipBytes(size + 4);
|
di.skipBytes(size + 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawHLine(BufferedImage im, int y, int x1, int x2) {
|
private void drawHLine(BufferedImage im, int y, int x1, int x2) {
|
||||||
for (int x = x1; x <= x2; x++) {
|
for (int x = x1; x <= x2; x++) {
|
||||||
im.setRGB(x, y, NP_COLOR);
|
im.setRGB(x, y, NP_COLOR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawVLine(BufferedImage im, int x, int y1, int y2) {
|
private void drawVLine(BufferedImage im, int x, int y1, int y2) {
|
||||||
for (int y = y1; y <= y2; y++) {
|
for (int y = y1; y <= y2; y++) {
|
||||||
im.setRGB(x, y, NP_COLOR);
|
im.setRGB(x, y, NP_COLOR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int NP_CHUNK_TYPE = 0x6e705463; // npTc
|
private static final int NP_CHUNK_TYPE = 0x6e705463; // npTc
|
||||||
private static final int NP_COLOR = 0xff000000;
|
private static final int NP_COLOR = 0xff000000;
|
||||||
|
|
||||||
private static class NinePatch {
|
private static class NinePatch {
|
||||||
public final int padLeft, padRight, padTop, padBottom;
|
public final int padLeft, padRight, padTop, padBottom;
|
||||||
public final int[] xDivs, yDivs;
|
public final int[] xDivs, yDivs;
|
||||||
|
|
||||||
public NinePatch(int padLeft, int padRight, int padTop, int padBottom,
|
public NinePatch(int padLeft, int padRight, int padTop, int padBottom,
|
||||||
int[] xDivs, int[] yDivs) {
|
int[] xDivs, int[] yDivs) {
|
||||||
this.padLeft = padLeft;
|
this.padLeft = padLeft;
|
||||||
this.padRight = padRight;
|
this.padRight = padRight;
|
||||||
this.padTop = padTop;
|
this.padTop = padTop;
|
||||||
this.padBottom = padBottom;
|
this.padBottom = padBottom;
|
||||||
this.xDivs = xDivs;
|
this.xDivs = xDivs;
|
||||||
this.yDivs = yDivs;
|
this.yDivs = yDivs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static NinePatch decode(ExtDataInput di) throws IOException {
|
public static NinePatch decode(ExtDataInput di) throws IOException {
|
||||||
di.skipBytes(1);
|
di.skipBytes(1);
|
||||||
byte numXDivs = di.readByte();
|
byte numXDivs = di.readByte();
|
||||||
byte numYDivs = di.readByte();
|
byte numYDivs = di.readByte();
|
||||||
di.skipBytes(1);
|
di.skipBytes(1);
|
||||||
di.skipBytes(8);
|
di.skipBytes(8);
|
||||||
int padLeft = di.readInt();
|
int padLeft = di.readInt();
|
||||||
int padRight = di.readInt();
|
int padRight = di.readInt();
|
||||||
int padTop = di.readInt();
|
int padTop = di.readInt();
|
||||||
int padBottom = di.readInt();
|
int padBottom = di.readInt();
|
||||||
di.skipBytes(4);
|
di.skipBytes(4);
|
||||||
int[] xDivs = di.readIntArray(numXDivs);
|
int[] xDivs = di.readIntArray(numXDivs);
|
||||||
int[] yDivs = di.readIntArray(numYDivs);
|
int[] yDivs = di.readIntArray(numYDivs);
|
||||||
|
|
||||||
return new NinePatch(padLeft, padRight, padTop, padBottom, xDivs,
|
return new NinePatch(padLeft, padRight, padTop, padBottom, xDivs,
|
||||||
yDivs);
|
yDivs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -25,31 +25,31 @@ import brut.androlib.res.data.value.ResScalarValue;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResAttrDecoder {
|
public class ResAttrDecoder {
|
||||||
public String decode(int type, int value, String rawValue, int attrResId)
|
public String decode(int type, int value, String rawValue, int attrResId)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
ResScalarValue resValue = mCurrentPackage.getValueFactory().factory(
|
ResScalarValue resValue = mCurrentPackage.getValueFactory().factory(
|
||||||
type, value, rawValue);
|
type, value, rawValue);
|
||||||
|
|
||||||
String decoded = null;
|
String decoded = null;
|
||||||
if (attrResId != 0) {
|
if (attrResId != 0) {
|
||||||
ResAttr attr = (ResAttr) getCurrentPackage().getResTable()
|
ResAttr attr = (ResAttr) getCurrentPackage().getResTable()
|
||||||
.getResSpec(attrResId).getDefaultResource().getValue();
|
.getResSpec(attrResId).getDefaultResource().getValue();
|
||||||
decoded = attr.convertToResXmlFormat(resValue);
|
decoded = attr.convertToResXmlFormat(resValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
return decoded != null ? decoded : resValue.encodeAsResXmlAttr();
|
return decoded != null ? decoded : resValue.encodeAsResXmlAttr();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResPackage getCurrentPackage() throws AndrolibException {
|
public ResPackage getCurrentPackage() throws AndrolibException {
|
||||||
if (mCurrentPackage == null) {
|
if (mCurrentPackage == null) {
|
||||||
throw new AndrolibException("Current package not set");
|
throw new AndrolibException("Current package not set");
|
||||||
}
|
}
|
||||||
return mCurrentPackage;
|
return mCurrentPackage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCurrentPackage(ResPackage currentPackage) {
|
public void setCurrentPackage(ResPackage currentPackage) {
|
||||||
mCurrentPackage = currentPackage;
|
mCurrentPackage = currentPackage;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResPackage mCurrentPackage;
|
private ResPackage mCurrentPackage;
|
||||||
}
|
}
|
||||||
|
@ -31,121 +31,121 @@ import java.util.logging.Logger;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResFileDecoder {
|
public class ResFileDecoder {
|
||||||
private final ResStreamDecoderContainer mDecoders;
|
private final ResStreamDecoderContainer mDecoders;
|
||||||
|
|
||||||
public ResFileDecoder(ResStreamDecoderContainer decoders) {
|
public ResFileDecoder(ResStreamDecoderContainer decoders) {
|
||||||
this.mDecoders = decoders;
|
this.mDecoders = decoders;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void decode(ResResource res, Directory inDir, Directory outDir)
|
public void decode(ResResource res, Directory inDir, Directory outDir)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
|
|
||||||
ResFileValue fileValue = (ResFileValue) res.getValue();
|
ResFileValue fileValue = (ResFileValue) res.getValue();
|
||||||
String inFileName = fileValue.getStrippedPath();
|
String inFileName = fileValue.getStrippedPath();
|
||||||
String outResName = res.getFilePath();
|
String outResName = res.getFilePath();
|
||||||
String typeName = res.getResSpec().getType().getName();
|
String typeName = res.getResSpec().getType().getName();
|
||||||
|
|
||||||
String ext = null;
|
String ext = null;
|
||||||
String outFileName;
|
String outFileName;
|
||||||
int extPos = inFileName.lastIndexOf(".");
|
int extPos = inFileName.lastIndexOf(".");
|
||||||
if (extPos == -1) {
|
if (extPos == -1) {
|
||||||
outFileName = outResName;
|
outFileName = outResName;
|
||||||
} else {
|
} else {
|
||||||
ext = inFileName.substring(extPos);
|
ext = inFileName.substring(extPos);
|
||||||
outFileName = outResName + ext;
|
outFileName = outResName + ext;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (typeName.equals("raw")) {
|
if (typeName.equals("raw")) {
|
||||||
decode(inDir, inFileName, outDir, outFileName, "raw");
|
decode(inDir, inFileName, outDir, outFileName, "raw");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (typeName.equals("drawable") || typeName.equals("mipmap")) {
|
if (typeName.equals("drawable") || typeName.equals("mipmap")) {
|
||||||
if (inFileName.toLowerCase().endsWith(".9.png")) {
|
if (inFileName.toLowerCase().endsWith(".9.png")) {
|
||||||
outFileName = outResName + ".9" + ext;
|
outFileName = outResName + ".9" + ext;
|
||||||
|
|
||||||
// check for htc .r.9.png
|
// check for htc .r.9.png
|
||||||
if (inFileName.toLowerCase().endsWith(".r.9.png")) {
|
if (inFileName.toLowerCase().endsWith(".r.9.png")) {
|
||||||
outFileName = outResName + ".r.9" + ext;
|
outFileName = outResName + ".r.9" + ext;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
decode(inDir, inFileName, outDir, outFileName, "9patch");
|
decode(inDir, inFileName, outDir, outFileName, "9patch");
|
||||||
return;
|
return;
|
||||||
} catch (CantFind9PatchChunk ex) {
|
} catch (CantFind9PatchChunk ex) {
|
||||||
LOGGER.log(
|
LOGGER.log(
|
||||||
Level.WARNING,
|
Level.WARNING,
|
||||||
String.format(
|
String.format(
|
||||||
"Cant find 9patch chunk in file: \"%s\". Renaming it to *.png.",
|
"Cant find 9patch chunk in file: \"%s\". Renaming it to *.png.",
|
||||||
inFileName), ex);
|
inFileName), ex);
|
||||||
outDir.removeFile(outFileName);
|
outDir.removeFile(outFileName);
|
||||||
outFileName = outResName + ext;
|
outFileName = outResName + ext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!".xml".equals(ext)) {
|
if (!".xml".equals(ext)) {
|
||||||
decode(inDir, inFileName, outDir, outFileName, "raw");
|
decode(inDir, inFileName, outDir, outFileName, "raw");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
decode(inDir, inFileName, outDir, outFileName, "xml");
|
decode(inDir, inFileName, outDir, outFileName, "xml");
|
||||||
} catch (AndrolibException ex) {
|
} catch (AndrolibException ex) {
|
||||||
LOGGER.log(Level.SEVERE, String.format(
|
LOGGER.log(Level.SEVERE, String.format(
|
||||||
"Could not decode file, replacing by FALSE value: %s",
|
"Could not decode file, replacing by FALSE value: %s",
|
||||||
inFileName, outFileName), ex);
|
inFileName, outFileName), ex);
|
||||||
res.replace(new ResBoolValue(false, null));
|
res.replace(new ResBoolValue(false, null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void decode(Directory inDir, String inFileName, Directory outDir,
|
public void decode(Directory inDir, String inFileName, Directory outDir,
|
||||||
String outFileName, String decoder) throws AndrolibException {
|
String outFileName, String decoder) throws AndrolibException {
|
||||||
InputStream in = null;
|
InputStream in = null;
|
||||||
OutputStream out = null;
|
OutputStream out = null;
|
||||||
try {
|
try {
|
||||||
in = inDir.getFileInput(inFileName);
|
in = inDir.getFileInput(inFileName);
|
||||||
out = outDir.getFileOutput(outFileName);
|
out = outDir.getFileOutput(outFileName);
|
||||||
mDecoders.decode(in, out, decoder);
|
mDecoders.decode(in, out, decoder);
|
||||||
} catch (DirectoryException ex) {
|
} catch (DirectoryException ex) {
|
||||||
throw new AndrolibException(ex);
|
throw new AndrolibException(ex);
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
if (in != null) {
|
if (in != null) {
|
||||||
in.close();
|
in.close();
|
||||||
}
|
}
|
||||||
if (out != null) {
|
if (out != null) {
|
||||||
out.close();
|
out.close();
|
||||||
}
|
}
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new AndrolibException(ex);
|
throw new AndrolibException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void decodeManifest(Directory inDir, String inFileName,
|
public void decodeManifest(Directory inDir, String inFileName,
|
||||||
Directory outDir, String outFileName) throws AndrolibException {
|
Directory outDir, String outFileName) throws AndrolibException {
|
||||||
InputStream in = null;
|
InputStream in = null;
|
||||||
OutputStream out = null;
|
OutputStream out = null;
|
||||||
try {
|
try {
|
||||||
in = inDir.getFileInput(inFileName);
|
in = inDir.getFileInput(inFileName);
|
||||||
out = outDir.getFileOutput(outFileName);
|
out = outDir.getFileOutput(outFileName);
|
||||||
((XmlPullStreamDecoder) mDecoders.getDecoder("xml"))
|
((XmlPullStreamDecoder) mDecoders.getDecoder("xml"))
|
||||||
.decodeManifest(in, out);
|
.decodeManifest(in, out);
|
||||||
} catch (DirectoryException ex) {
|
} catch (DirectoryException ex) {
|
||||||
throw new AndrolibException(ex);
|
throw new AndrolibException(ex);
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
if (in != null) {
|
if (in != null) {
|
||||||
in.close();
|
in.close();
|
||||||
}
|
}
|
||||||
if (out != null) {
|
if (out != null) {
|
||||||
out.close();
|
out.close();
|
||||||
}
|
}
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new AndrolibException(ex);
|
throw new AndrolibException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final static Logger LOGGER = Logger.getLogger(ResFileDecoder.class
|
private final static Logger LOGGER = Logger.getLogger(ResFileDecoder.class
|
||||||
.getName());
|
.getName());
|
||||||
}
|
}
|
||||||
|
@ -26,13 +26,13 @@ import org.apache.commons.io.IOUtils;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResRawStreamDecoder implements ResStreamDecoder {
|
public class ResRawStreamDecoder implements ResStreamDecoder {
|
||||||
@Override
|
@Override
|
||||||
public void decode(InputStream in, OutputStream out)
|
public void decode(InputStream in, OutputStream out)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
try {
|
try {
|
||||||
IOUtils.copy(in, out);
|
IOUtils.copy(in, out);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new AndrolibException("Could not decode raw stream", ex);
|
throw new AndrolibException("Could not decode raw stream", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,6 @@ import java.io.OutputStream;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public interface ResStreamDecoder {
|
public interface ResStreamDecoder {
|
||||||
public void decode(InputStream in, OutputStream out)
|
public void decode(InputStream in, OutputStream out)
|
||||||
throws AndrolibException;
|
throws AndrolibException;
|
||||||
}
|
}
|
||||||
|
@ -26,22 +26,22 @@ import java.util.Map;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResStreamDecoderContainer {
|
public class ResStreamDecoderContainer {
|
||||||
private final Map<String, ResStreamDecoder> mDecoders = new HashMap<String, ResStreamDecoder>();
|
private final Map<String, ResStreamDecoder> mDecoders = new HashMap<String, ResStreamDecoder>();
|
||||||
|
|
||||||
public void decode(InputStream in, OutputStream out, String decoderName)
|
public void decode(InputStream in, OutputStream out, String decoderName)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
getDecoder(decoderName).decode(in, out);
|
getDecoder(decoderName).decode(in, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResStreamDecoder getDecoder(String name) throws AndrolibException {
|
public ResStreamDecoder getDecoder(String name) throws AndrolibException {
|
||||||
ResStreamDecoder decoder = mDecoders.get(name);
|
ResStreamDecoder decoder = mDecoders.get(name);
|
||||||
if (decoder == null) {
|
if (decoder == null) {
|
||||||
throw new AndrolibException("Undefined decoder: " + name);
|
throw new AndrolibException("Undefined decoder: " + name);
|
||||||
}
|
}
|
||||||
return decoder;
|
return decoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDecoder(String name, ResStreamDecoder decoder) {
|
public void setDecoder(String name, ResStreamDecoder decoder) {
|
||||||
mDecoders.put(name, decoder);
|
mDecoders.put(name, decoder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,275 +28,275 @@ import java.util.logging.Logger;
|
|||||||
/**
|
/**
|
||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
* @author Dmitry Skiba
|
* @author Dmitry Skiba
|
||||||
*
|
*
|
||||||
* Block of strings, used in binary xml and arsc.
|
* Block of strings, used in binary xml and arsc.
|
||||||
*
|
*
|
||||||
* TODO: - implement get()
|
* TODO: - implement get()
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class StringBlock {
|
public class StringBlock {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads whole (including chunk type) string block from stream. Stream must
|
* Reads whole (including chunk type) string block from stream. Stream must
|
||||||
* be at the chunk type.
|
* be at the chunk type.
|
||||||
*/
|
*/
|
||||||
public static StringBlock read(ExtDataInput reader) throws IOException {
|
public static StringBlock read(ExtDataInput reader) throws IOException {
|
||||||
reader.skipCheckInt(CHUNK_TYPE);
|
reader.skipCheckInt(CHUNK_TYPE);
|
||||||
int chunkSize = reader.readInt();
|
int chunkSize = reader.readInt();
|
||||||
int stringCount = reader.readInt();
|
int stringCount = reader.readInt();
|
||||||
int styleOffsetCount = reader.readInt();
|
int styleOffsetCount = reader.readInt();
|
||||||
int flags = reader.readInt();
|
int flags = reader.readInt();
|
||||||
int stringsOffset = reader.readInt();
|
int stringsOffset = reader.readInt();
|
||||||
int stylesOffset = reader.readInt();
|
int stylesOffset = reader.readInt();
|
||||||
|
|
||||||
StringBlock block = new StringBlock();
|
StringBlock block = new StringBlock();
|
||||||
block.m_isUTF8 = (flags & UTF8_FLAG) != 0;
|
block.m_isUTF8 = (flags & UTF8_FLAG) != 0;
|
||||||
block.m_stringOffsets = reader.readIntArray(stringCount);
|
block.m_stringOffsets = reader.readIntArray(stringCount);
|
||||||
block.m_stringOwns = new int[stringCount];
|
block.m_stringOwns = new int[stringCount];
|
||||||
Arrays.fill(block.m_stringOwns, -1);
|
Arrays.fill(block.m_stringOwns, -1);
|
||||||
|
|
||||||
if (styleOffsetCount != 0) {
|
if (styleOffsetCount != 0) {
|
||||||
block.m_styleOffsets = reader.readIntArray(styleOffsetCount);
|
block.m_styleOffsets = reader.readIntArray(styleOffsetCount);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
int size = ((stylesOffset == 0) ? chunkSize : stylesOffset)
|
int size = ((stylesOffset == 0) ? chunkSize : stylesOffset)
|
||||||
- stringsOffset;
|
- stringsOffset;
|
||||||
if ((size % 4) != 0) {
|
if ((size % 4) != 0) {
|
||||||
throw new IOException("String data size is not multiple of 4 ("
|
throw new IOException("String data size is not multiple of 4 ("
|
||||||
+ size + ").");
|
+ size + ").");
|
||||||
}
|
}
|
||||||
block.m_strings = new byte[size];
|
block.m_strings = new byte[size];
|
||||||
reader.readFully(block.m_strings);
|
reader.readFully(block.m_strings);
|
||||||
}
|
}
|
||||||
if (stylesOffset != 0) {
|
if (stylesOffset != 0) {
|
||||||
int size = (chunkSize - stylesOffset);
|
int size = (chunkSize - stylesOffset);
|
||||||
if ((size % 4) != 0) {
|
if ((size % 4) != 0) {
|
||||||
throw new IOException("Style data size is not multiple of 4 ("
|
throw new IOException("Style data size is not multiple of 4 ("
|
||||||
+ size + ").");
|
+ size + ").");
|
||||||
}
|
}
|
||||||
block.m_styles = reader.readIntArray(size / 4);
|
block.m_styles = reader.readIntArray(size / 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns number of strings in block.
|
* Returns number of strings in block.
|
||||||
*/
|
*/
|
||||||
public int getCount() {
|
public int getCount() {
|
||||||
return m_stringOffsets != null ? m_stringOffsets.length : 0;
|
return m_stringOffsets != null ? m_stringOffsets.length : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns raw string (without any styling information) at specified index.
|
* Returns raw string (without any styling information) at specified index.
|
||||||
*/
|
*/
|
||||||
public String getString(int index) {
|
public String getString(int index) {
|
||||||
if (index < 0 || m_stringOffsets == null || index >= m_stringOffsets.length) {
|
if (index < 0 || m_stringOffsets == null || index >= m_stringOffsets.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
int offset = m_stringOffsets[index];
|
int offset = m_stringOffsets[index];
|
||||||
int length;
|
int length;
|
||||||
|
|
||||||
if (m_isUTF8) {
|
if (m_isUTF8) {
|
||||||
int[] val = getUtf8(m_strings, offset);
|
int[] val = getUtf8(m_strings, offset);
|
||||||
offset = val[0];
|
offset = val[0];
|
||||||
length = val[1];
|
length = val[1];
|
||||||
} else {
|
} else {
|
||||||
int[] val = getUtf16(m_strings, offset);
|
int[] val = getUtf16(m_strings, offset);
|
||||||
offset += val[0];
|
offset += val[0];
|
||||||
length = val[1];
|
length = val[1];
|
||||||
}
|
}
|
||||||
return decodeString(offset, length);
|
return decodeString(offset, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Not yet implemented.
|
* Not yet implemented.
|
||||||
*
|
*
|
||||||
* Returns string with style information (if any).
|
* Returns string with style information (if any).
|
||||||
*/
|
*/
|
||||||
public CharSequence get(int index) {
|
public CharSequence get(int index) {
|
||||||
return getString(index);
|
return getString(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns string with style tags (html-like).
|
* Returns string with style tags (html-like).
|
||||||
*/
|
*/
|
||||||
public String getHTML(int index) {
|
public String getHTML(int index) {
|
||||||
String raw = getString(index);
|
String raw = getString(index);
|
||||||
if (raw == null) {
|
if (raw == null) {
|
||||||
return raw;
|
return raw;
|
||||||
}
|
}
|
||||||
int[] style = getStyle(index);
|
int[] style = getStyle(index);
|
||||||
if (style == null) {
|
if (style == null) {
|
||||||
return ResXmlEncoders.escapeXmlChars(raw);
|
return ResXmlEncoders.escapeXmlChars(raw);
|
||||||
}
|
}
|
||||||
StringBuilder html = new StringBuilder(raw.length() + 32);
|
StringBuilder html = new StringBuilder(raw.length() + 32);
|
||||||
int[] opened = new int[style.length / 3];
|
int[] opened = new int[style.length / 3];
|
||||||
int offset = 0, depth = 0;
|
int offset = 0, depth = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
int i = -1, j;
|
int i = -1, j;
|
||||||
for (j = 0; j != style.length; j += 3) {
|
for (j = 0; j != style.length; j += 3) {
|
||||||
if (style[j + 1] == -1) {
|
if (style[j + 1] == -1) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (i == -1 || style[i + 1] > style[j + 1]) {
|
if (i == -1 || style[i + 1] > style[j + 1]) {
|
||||||
i = j;
|
i = j;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int start = ((i != -1) ? style[i + 1] : raw.length());
|
int start = ((i != -1) ? style[i + 1] : raw.length());
|
||||||
for (j = depth - 1; j >= 0; j--) {
|
for (j = depth - 1; j >= 0; j--) {
|
||||||
int last = opened[j];
|
int last = opened[j];
|
||||||
int end = style[last + 2];
|
int end = style[last + 2];
|
||||||
if (end >= start) {
|
if (end >= start) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (offset <= end) {
|
if (offset <= end) {
|
||||||
html.append(ResXmlEncoders.escapeXmlChars(raw.substring(
|
html.append(ResXmlEncoders.escapeXmlChars(raw.substring(
|
||||||
offset, end + 1)));
|
offset, end + 1)));
|
||||||
offset = end + 1;
|
offset = end + 1;
|
||||||
}
|
}
|
||||||
outputStyleTag(getString(style[last]), html, true);
|
outputStyleTag(getString(style[last]), html, true);
|
||||||
}
|
}
|
||||||
depth = j + 1;
|
depth = j + 1;
|
||||||
if (offset < start) {
|
if (offset < start) {
|
||||||
html.append(ResXmlEncoders.escapeXmlChars(raw.substring(offset,
|
html.append(ResXmlEncoders.escapeXmlChars(raw.substring(offset,
|
||||||
start)));
|
start)));
|
||||||
offset = start;
|
offset = start;
|
||||||
}
|
}
|
||||||
if (i == -1) {
|
if (i == -1) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
outputStyleTag(getString(style[i]), html, false);
|
outputStyleTag(getString(style[i]), html, false);
|
||||||
style[i + 1] = -1;
|
style[i + 1] = -1;
|
||||||
opened[depth++] = i;
|
opened[depth++] = i;
|
||||||
}
|
}
|
||||||
return html.toString();
|
return html.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void outputStyleTag(String tag, StringBuilder builder, boolean close) {
|
private void outputStyleTag(String tag, StringBuilder builder, boolean close) {
|
||||||
builder.append('<');
|
builder.append('<');
|
||||||
if (close) {
|
if (close) {
|
||||||
builder.append('/');
|
builder.append('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
int pos = tag.indexOf(';');
|
int pos = tag.indexOf(';');
|
||||||
if (pos == -1) {
|
if (pos == -1) {
|
||||||
builder.append(tag);
|
builder.append(tag);
|
||||||
} else {
|
} else {
|
||||||
builder.append(tag.substring(0, pos));
|
builder.append(tag.substring(0, pos));
|
||||||
if (!close) {
|
if (!close) {
|
||||||
boolean loop = true;
|
boolean loop = true;
|
||||||
while (loop) {
|
while (loop) {
|
||||||
int pos2 = tag.indexOf('=', pos + 1);
|
int pos2 = tag.indexOf('=', pos + 1);
|
||||||
builder.append(' ').append(tag.substring(pos + 1, pos2))
|
builder.append(' ').append(tag.substring(pos + 1, pos2))
|
||||||
.append("=\"");
|
.append("=\"");
|
||||||
pos = tag.indexOf(';', pos2 + 1);
|
pos = tag.indexOf(';', pos2 + 1);
|
||||||
|
|
||||||
String val;
|
String val;
|
||||||
if (pos != -1) {
|
if (pos != -1) {
|
||||||
val = tag.substring(pos2 + 1, pos);
|
val = tag.substring(pos2 + 1, pos);
|
||||||
} else {
|
} else {
|
||||||
loop = false;
|
loop = false;
|
||||||
val = tag.substring(pos2 + 1);
|
val = tag.substring(pos2 + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.append(ResXmlEncoders.escapeXmlChars(val)).append(
|
builder.append(ResXmlEncoders.escapeXmlChars(val)).append(
|
||||||
'"');
|
'"');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
builder.append('>');
|
builder.append('>');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds index of the string. Returns -1 if the string was not found.
|
* Finds index of the string. Returns -1 if the string was not found.
|
||||||
*/
|
*/
|
||||||
public int find(String string) {
|
public int find(String string) {
|
||||||
if (string == null) {
|
if (string == null) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
for (int i = 0; i != m_stringOffsets.length; ++i) {
|
for (int i = 0; i != m_stringOffsets.length; ++i) {
|
||||||
int offset = m_stringOffsets[i];
|
int offset = m_stringOffsets[i];
|
||||||
int length = getShort(m_strings, offset);
|
int length = getShort(m_strings, offset);
|
||||||
if (length != string.length()) {
|
if (length != string.length()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int j = 0;
|
int j = 0;
|
||||||
for (; j != length; ++j) {
|
for (; j != length; ++j) {
|
||||||
offset += 2;
|
offset += 2;
|
||||||
if (string.charAt(j) != getShort(m_strings, offset)) {
|
if (string.charAt(j) != getShort(m_strings, offset)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (j == length) {
|
if (j == length) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// /////////////////////////////////////////// implementation
|
// /////////////////////////////////////////// implementation
|
||||||
private StringBlock() {
|
private StringBlock() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns style information - array of int triplets, where in each triplet:
|
* Returns style information - array of int triplets, where in each triplet:
|
||||||
* * first int is index of tag name ('b','i', etc.) * second int is tag
|
* * first int is index of tag name ('b','i', etc.) * second int is tag
|
||||||
* start index in string * third int is tag end index in string
|
* start index in string * third int is tag end index in string
|
||||||
*/
|
*/
|
||||||
private int[] getStyle(int index) {
|
private int[] getStyle(int index) {
|
||||||
if (m_styleOffsets == null || m_styles == null
|
if (m_styleOffsets == null || m_styles == null
|
||||||
|| index >= m_styleOffsets.length) {
|
|| index >= m_styleOffsets.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
int offset = m_styleOffsets[index] / 4;
|
int offset = m_styleOffsets[index] / 4;
|
||||||
int style[];
|
int style[];
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (int i = offset; i < m_styles.length; ++i) {
|
for (int i = offset; i < m_styles.length; ++i) {
|
||||||
if (m_styles[i] == -1) {
|
if (m_styles[i] == -1) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
if (count == 0 || (count % 3) != 0) {
|
if (count == 0 || (count % 3) != 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
style = new int[count];
|
style = new int[count];
|
||||||
}
|
}
|
||||||
for (int i = offset, j = 0; i < m_styles.length;) {
|
for (int i = offset, j = 0; i < m_styles.length;) {
|
||||||
if (m_styles[i] == -1) {
|
if (m_styles[i] == -1) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
style[j++] = m_styles[i++];
|
style[j++] = m_styles[i++];
|
||||||
}
|
}
|
||||||
return style;
|
return style;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String decodeString(int offset, int length) {
|
private String decodeString(int offset, int length) {
|
||||||
try {
|
try {
|
||||||
return (m_isUTF8 ? UTF8_DECODER : UTF16LE_DECODER).decode(
|
return (m_isUTF8 ? UTF8_DECODER : UTF16LE_DECODER).decode(
|
||||||
ByteBuffer.wrap(m_strings, offset, length)).toString();
|
ByteBuffer.wrap(m_strings, offset, length)).toString();
|
||||||
} catch (CharacterCodingException ex) {
|
} catch (CharacterCodingException ex) {
|
||||||
LOGGER.log(Level.WARNING, null, ex);
|
LOGGER.log(Level.WARNING, null, ex);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int getShort(byte[] array, int offset) {
|
private static final int getShort(byte[] array, int offset) {
|
||||||
return (array[offset + 1] & 0xff) << 8 | array[offset] & 0xff;
|
return (array[offset + 1] & 0xff) << 8 | array[offset] & 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int getShort(int[] array, int offset) {
|
private static final int getShort(int[] array, int offset) {
|
||||||
int value = array[offset / 4];
|
int value = array[offset / 4];
|
||||||
if ((offset % 4) / 2 == 0) {
|
if ((offset % 4) / 2 == 0) {
|
||||||
return (value & 0xFFFF);
|
return (value & 0xFFFF);
|
||||||
} else {
|
} else {
|
||||||
return (value >>> 16);
|
return (value >>> 16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int[] getUtf8(byte[] array, int offset) {
|
private static final int[] getUtf8(byte[] array, int offset) {
|
||||||
int val = array[offset];
|
int val = array[offset];
|
||||||
int length;
|
int length;
|
||||||
|
|
||||||
@ -315,8 +315,8 @@ public class StringBlock {
|
|||||||
while (array[offset + length] != 0) {
|
while (array[offset + length] != 0) {
|
||||||
length++;
|
length++;
|
||||||
}
|
}
|
||||||
return new int[] { offset, length};
|
return new int[] { offset, length};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int[] getUtf16(byte[] array, int offset) {
|
private static final int[] getUtf16(byte[] array, int offset) {
|
||||||
int val = ((array[offset + 1] & 0xFF) << 8 | array[offset] & 0xFF);
|
int val = ((array[offset + 1] & 0xFF) << 8 | array[offset] & 0xFF);
|
||||||
@ -329,20 +329,20 @@ public class StringBlock {
|
|||||||
return new int[] {2, val * 2};
|
return new int[] {2, val * 2};
|
||||||
}
|
}
|
||||||
|
|
||||||
private int[] m_stringOffsets;
|
private int[] m_stringOffsets;
|
||||||
private byte[] m_strings;
|
private byte[] m_strings;
|
||||||
private int[] m_styleOffsets;
|
private int[] m_styleOffsets;
|
||||||
private int[] m_styles;
|
private int[] m_styles;
|
||||||
private boolean m_isUTF8;
|
private boolean m_isUTF8;
|
||||||
private int[] m_stringOwns;
|
private int[] m_stringOwns;
|
||||||
private final CharsetDecoder UTF16LE_DECODER = Charset.forName(
|
private final CharsetDecoder UTF16LE_DECODER = Charset.forName(
|
||||||
"UTF-16LE").newDecoder();
|
"UTF-16LE").newDecoder();
|
||||||
private final CharsetDecoder UTF8_DECODER = Charset.forName("UTF-8")
|
private final CharsetDecoder UTF8_DECODER = Charset.forName("UTF-8")
|
||||||
.newDecoder();
|
.newDecoder();
|
||||||
private static final Logger LOGGER = Logger.getLogger(StringBlock.class
|
private static final Logger LOGGER = Logger.getLogger(StringBlock.class
|
||||||
.getName());
|
.getName());
|
||||||
|
|
||||||
// ResChunk_header = header.type (0x0001) + header.headerSize (0x001C)
|
// ResChunk_header = header.type (0x0001) + header.headerSize (0x001C)
|
||||||
private static final int CHUNK_TYPE = 0x001C0001;
|
private static final int CHUNK_TYPE = 0x001C0001;
|
||||||
private static final int UTF8_FLAG = 0x00000100;
|
private static final int UTF8_FLAG = 0x00000100;
|
||||||
}
|
}
|
||||||
|
@ -36,56 +36,56 @@ import brut.androlib.res.util.ExtXmlSerializer;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class XmlPullStreamDecoder implements ResStreamDecoder {
|
public class XmlPullStreamDecoder implements ResStreamDecoder {
|
||||||
public XmlPullStreamDecoder(XmlPullParser parser,
|
public XmlPullStreamDecoder(XmlPullParser parser,
|
||||||
ExtXmlSerializer serializer) {
|
ExtXmlSerializer serializer) {
|
||||||
this.mParser = parser;
|
this.mParser = parser;
|
||||||
this.mSerial = serializer;
|
this.mSerial = serializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void decode(InputStream in, OutputStream out)
|
public void decode(InputStream in, OutputStream out)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
try {
|
try {
|
||||||
XmlPullWrapperFactory factory = XmlPullWrapperFactory.newInstance();
|
XmlPullWrapperFactory factory = XmlPullWrapperFactory.newInstance();
|
||||||
XmlPullParserWrapper par = factory.newPullParserWrapper(mParser);
|
XmlPullParserWrapper par = factory.newPullParserWrapper(mParser);
|
||||||
final ResTable resTable = ((AXmlResourceParser) mParser)
|
final ResTable resTable = ((AXmlResourceParser) mParser)
|
||||||
.getAttrDecoder().getCurrentPackage().getResTable();
|
.getAttrDecoder().getCurrentPackage().getResTable();
|
||||||
|
|
||||||
XmlSerializerWrapper ser = new StaticXmlSerializerWrapper(mSerial,
|
XmlSerializerWrapper ser = new StaticXmlSerializerWrapper(mSerial,
|
||||||
factory) {
|
factory) {
|
||||||
boolean hideSdkInfo = false;
|
boolean hideSdkInfo = false;
|
||||||
boolean hidePackageInfo = false;
|
boolean hidePackageInfo = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void event(XmlPullParser pp)
|
public void event(XmlPullParser pp)
|
||||||
throws XmlPullParserException, IOException {
|
throws XmlPullParserException, IOException {
|
||||||
int type = pp.getEventType();
|
int type = pp.getEventType();
|
||||||
|
|
||||||
if (type == XmlPullParser.START_TAG) {
|
if (type == XmlPullParser.START_TAG) {
|
||||||
if ("manifest".equalsIgnoreCase(pp.getName())) {
|
if ("manifest".equalsIgnoreCase(pp.getName())) {
|
||||||
try {
|
try {
|
||||||
hidePackageInfo = parseManifest(pp);
|
hidePackageInfo = parseManifest(pp);
|
||||||
} catch (AndrolibException e) {
|
} catch (AndrolibException e) {
|
||||||
}
|
}
|
||||||
} else if ("uses-sdk".equalsIgnoreCase(pp.getName())) {
|
} else if ("uses-sdk".equalsIgnoreCase(pp.getName())) {
|
||||||
try {
|
try {
|
||||||
hideSdkInfo = parseAttr(pp);
|
hideSdkInfo = parseAttr(pp);
|
||||||
if (hideSdkInfo) {
|
if (hideSdkInfo) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (AndrolibException e) {
|
} catch (AndrolibException e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (hideSdkInfo && type == XmlPullParser.END_TAG
|
} else if (hideSdkInfo && type == XmlPullParser.END_TAG
|
||||||
&& "uses-sdk".equalsIgnoreCase(pp.getName())) {
|
&& "uses-sdk".equalsIgnoreCase(pp.getName())) {
|
||||||
return;
|
return;
|
||||||
} else if (hidePackageInfo && type == XmlPullParser.END_TAG
|
} else if (hidePackageInfo && type == XmlPullParser.END_TAG
|
||||||
&& "manifest".equalsIgnoreCase(pp.getName())) {
|
&& "manifest".equalsIgnoreCase(pp.getName())) {
|
||||||
super.event(pp);
|
super.event(pp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
super.event(pp);
|
super.event(pp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean parseManifest(XmlPullParser pp)
|
private boolean parseManifest(XmlPullParser pp)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
@ -103,71 +103,71 @@ public class XmlPullStreamDecoder implements ResStreamDecoder {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean parseAttr(XmlPullParser pp)
|
private boolean parseAttr(XmlPullParser pp)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
ResTable restable = resTable;
|
ResTable restable = resTable;
|
||||||
for (int i = 0; i < pp.getAttributeCount(); i++) {
|
for (int i = 0; i < pp.getAttributeCount(); i++) {
|
||||||
final String a_ns = "http://schemas.android.com/apk/res/android";
|
final String a_ns = "http://schemas.android.com/apk/res/android";
|
||||||
String ns = pp.getAttributeNamespace(i);
|
String ns = pp.getAttributeNamespace(i);
|
||||||
|
|
||||||
if (a_ns.equalsIgnoreCase(ns)) {
|
if (a_ns.equalsIgnoreCase(ns)) {
|
||||||
String name = pp.getAttributeName(i);
|
String name = pp.getAttributeName(i);
|
||||||
String value = pp.getAttributeValue(i);
|
String value = pp.getAttributeValue(i);
|
||||||
if (name != null && value != null) {
|
if (name != null && value != null) {
|
||||||
if (name.equalsIgnoreCase("minSdkVersion")
|
if (name.equalsIgnoreCase("minSdkVersion")
|
||||||
|| name.equalsIgnoreCase("targetSdkVersion")
|
|| name.equalsIgnoreCase("targetSdkVersion")
|
||||||
|| name.equalsIgnoreCase("maxSdkVersion")) {
|
|| name.equalsIgnoreCase("maxSdkVersion")) {
|
||||||
restable.addSdkInfo(name, value);
|
restable.addSdkInfo(name, value);
|
||||||
} else {
|
} else {
|
||||||
restable.clearSdkInfo();
|
restable.clearSdkInfo();
|
||||||
return false;// Found unknown flags
|
return false;// Found unknown flags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
resTable.clearSdkInfo();
|
resTable.clearSdkInfo();
|
||||||
|
|
||||||
if (i >= pp.getAttributeCount()) {
|
if (i >= pp.getAttributeCount()) {
|
||||||
return false;// Found unknown flags
|
return false;// Found unknown flags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (resTable.getAnalysisMode() == true) {
|
if (resTable.getAnalysisMode() == true) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
par.setInput(in, null);
|
par.setInput(in, null);
|
||||||
ser.setOutput(out, null);
|
ser.setOutput(out, null);
|
||||||
|
|
||||||
while (par.nextToken() != XmlPullParser.END_DOCUMENT) {
|
while (par.nextToken() != XmlPullParser.END_DOCUMENT) {
|
||||||
ser.event(par);
|
ser.event(par);
|
||||||
}
|
}
|
||||||
ser.flush();
|
ser.flush();
|
||||||
} catch (XmlPullParserException ex) {
|
} catch (XmlPullParserException ex) {
|
||||||
throw new AndrolibException("Could not decode XML", ex);
|
throw new AndrolibException("Could not decode XML", ex);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new AndrolibException("Could not decode XML", ex);
|
throw new AndrolibException("Could not decode XML", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void decodeManifest(InputStream in, OutputStream out)
|
public void decodeManifest(InputStream in, OutputStream out)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
mOptimizeForManifest = true;
|
mOptimizeForManifest = true;
|
||||||
try {
|
try {
|
||||||
decode(in, out);
|
decode(in, out);
|
||||||
} finally {
|
} finally {
|
||||||
mOptimizeForManifest = false;
|
mOptimizeForManifest = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final XmlPullParser mParser;
|
private final XmlPullParser mParser;
|
||||||
private final ExtXmlSerializer mSerial;
|
private final ExtXmlSerializer mSerial;
|
||||||
|
|
||||||
private boolean mOptimizeForManifest = false;
|
private boolean mOptimizeForManifest = false;
|
||||||
|
|
||||||
private final static Logger LOGGER = Logger
|
private final static Logger LOGGER = Logger
|
||||||
.getLogger(XmlPullStreamDecoder.class.getName());
|
.getLogger(XmlPullStreamDecoder.class.getName());
|
||||||
}
|
}
|
||||||
|
@ -27,36 +27,36 @@ import java.net.URI;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ExtFile extends File {
|
public class ExtFile extends File {
|
||||||
public ExtFile(File file) {
|
public ExtFile(File file) {
|
||||||
super(file.getPath());
|
super(file.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExtFile(URI uri) {
|
public ExtFile(URI uri) {
|
||||||
super(uri);
|
super(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExtFile(File parent, String child) {
|
public ExtFile(File parent, String child) {
|
||||||
super(parent, child);
|
super(parent, child);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExtFile(String parent, String child) {
|
public ExtFile(String parent, String child) {
|
||||||
super(parent, child);
|
super(parent, child);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExtFile(String pathname) {
|
public ExtFile(String pathname) {
|
||||||
super(pathname);
|
super(pathname);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Directory getDirectory() throws DirectoryException {
|
public Directory getDirectory() throws DirectoryException {
|
||||||
if (mDirectory == null) {
|
if (mDirectory == null) {
|
||||||
if (isDirectory()) {
|
if (isDirectory()) {
|
||||||
mDirectory = new FileDirectory(this);
|
mDirectory = new FileDirectory(this);
|
||||||
} else {
|
} else {
|
||||||
mDirectory = new ZipRODirectory(this);
|
mDirectory = new ZipRODirectory(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mDirectory;
|
return mDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Directory mDirectory;
|
private Directory mDirectory;
|
||||||
}
|
}
|
||||||
|
@ -23,59 +23,59 @@ import org.xmlpull.mxp1_serializer.MXSerializer;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ExtMXSerializer extends MXSerializer implements ExtXmlSerializer {
|
public class ExtMXSerializer extends MXSerializer implements ExtXmlSerializer {
|
||||||
@Override
|
@Override
|
||||||
public void startDocument(String encoding, Boolean standalone)
|
public void startDocument(String encoding, Boolean standalone)
|
||||||
throws IOException, IllegalArgumentException, IllegalStateException {
|
throws IOException, IllegalArgumentException, IllegalStateException {
|
||||||
super.startDocument(encoding != null ? encoding : mDefaultEncoding,
|
super.startDocument(encoding != null ? encoding : mDefaultEncoding,
|
||||||
standalone);
|
standalone);
|
||||||
this.newLine();
|
this.newLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void writeAttributeValue(String value, Writer out)
|
protected void writeAttributeValue(String value, Writer out)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (mIsDisabledAttrEscape) {
|
if (mIsDisabledAttrEscape) {
|
||||||
out.write(value);
|
out.write(value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
super.writeAttributeValue(value, out);
|
super.writeAttributeValue(value, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setOutput(OutputStream os, String encoding) throws IOException {
|
public void setOutput(OutputStream os, String encoding) throws IOException {
|
||||||
super.setOutput(os, encoding != null ? encoding : mDefaultEncoding);
|
super.setOutput(os, encoding != null ? encoding : mDefaultEncoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getProperty(String name) throws IllegalArgumentException {
|
public Object getProperty(String name) throws IllegalArgumentException {
|
||||||
if (PROPERTY_DEFAULT_ENCODING.equals(name)) {
|
if (PROPERTY_DEFAULT_ENCODING.equals(name)) {
|
||||||
return mDefaultEncoding;
|
return mDefaultEncoding;
|
||||||
}
|
}
|
||||||
return super.getProperty(name);
|
return super.getProperty(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setProperty(String name, Object value)
|
public void setProperty(String name, Object value)
|
||||||
throws IllegalArgumentException, IllegalStateException {
|
throws IllegalArgumentException, IllegalStateException {
|
||||||
if (PROPERTY_DEFAULT_ENCODING.equals(name)) {
|
if (PROPERTY_DEFAULT_ENCODING.equals(name)) {
|
||||||
mDefaultEncoding = (String) value;
|
mDefaultEncoding = (String) value;
|
||||||
} else {
|
} else {
|
||||||
super.setProperty(name, value);
|
super.setProperty(name, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ExtXmlSerializer newLine() throws IOException {
|
public ExtXmlSerializer newLine() throws IOException {
|
||||||
super.out.write(lineSeparator);
|
super.out.write(lineSeparator);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setDisabledAttrEscape(boolean disabled) {
|
public void setDisabledAttrEscape(boolean disabled) {
|
||||||
mIsDisabledAttrEscape = disabled;
|
mIsDisabledAttrEscape = disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String mDefaultEncoding;
|
private String mDefaultEncoding;
|
||||||
private boolean mIsDisabledAttrEscape = false;
|
private boolean mIsDisabledAttrEscape = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,11 +24,11 @@ import org.xmlpull.v1.XmlSerializer;
|
|||||||
*/
|
*/
|
||||||
public interface ExtXmlSerializer extends XmlSerializer {
|
public interface ExtXmlSerializer extends XmlSerializer {
|
||||||
|
|
||||||
public ExtXmlSerializer newLine() throws IOException;
|
public ExtXmlSerializer newLine() throws IOException;
|
||||||
|
|
||||||
public void setDisabledAttrEscape(boolean disabled);
|
public void setDisabledAttrEscape(boolean disabled);
|
||||||
|
|
||||||
public static final String PROPERTY_SERIALIZER_INDENTATION = "http://xmlpull.org/v1/doc/properties.html#serializer-indentation";
|
public static final String PROPERTY_SERIALIZER_INDENTATION = "http://xmlpull.org/v1/doc/properties.html#serializer-indentation";
|
||||||
public static final String PROPERTY_SERIALIZER_LINE_SEPARATOR = "http://xmlpull.org/v1/doc/properties.html#serializer-line-separator";
|
public static final String PROPERTY_SERIALIZER_LINE_SEPARATOR = "http://xmlpull.org/v1/doc/properties.html#serializer-line-separator";
|
||||||
public static final String PROPERTY_DEFAULT_ENCODING = "DEFAULT_ENCODING";
|
public static final String PROPERTY_DEFAULT_ENCODING = "DEFAULT_ENCODING";
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,6 @@ import org.xmlpull.v1.XmlSerializer;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public interface ResValuesXmlSerializable {
|
public interface ResValuesXmlSerializable {
|
||||||
public void serializeToResValuesXml(XmlSerializer serializer,
|
public void serializeToResValuesXml(XmlSerializer serializer,
|
||||||
ResResource res) throws IOException, AndrolibException;
|
ResResource res) throws IOException, AndrolibException;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ import brut.androlib.AndrolibException;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public interface ResXmlEncodable {
|
public interface ResXmlEncodable {
|
||||||
public String encodeAsResXmlAttr() throws AndrolibException;
|
public String encodeAsResXmlAttr() throws AndrolibException;
|
||||||
|
|
||||||
public String encodeAsResXmlValue() throws AndrolibException;
|
public String encodeAsResXmlValue() throws AndrolibException;
|
||||||
}
|
}
|
||||||
|
@ -27,187 +27,187 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public final class ResXmlEncoders {
|
public final class ResXmlEncoders {
|
||||||
|
|
||||||
public static String escapeXmlChars(String str) {
|
public static String escapeXmlChars(String str) {
|
||||||
return str.replace("&", "&").replace("<", "<");
|
return str.replace("&", "&").replace("<", "<");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String encodeAsResXmlAttr(String str) {
|
public static String encodeAsResXmlAttr(String str) {
|
||||||
if (str.isEmpty()) {
|
if (str.isEmpty()) {
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
char[] chars = str.toCharArray();
|
char[] chars = str.toCharArray();
|
||||||
StringBuilder out = new StringBuilder(str.length() + 10);
|
StringBuilder out = new StringBuilder(str.length() + 10);
|
||||||
|
|
||||||
switch (chars[0]) {
|
switch (chars[0]) {
|
||||||
case '#':
|
case '#':
|
||||||
case '@':
|
case '@':
|
||||||
case '?':
|
case '?':
|
||||||
out.append('\\');
|
out.append('\\');
|
||||||
}
|
}
|
||||||
|
|
||||||
for (char c : chars) {
|
for (char c : chars) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '\\':
|
case '\\':
|
||||||
out.append('\\');
|
out.append('\\');
|
||||||
break;
|
break;
|
||||||
case '"':
|
case '"':
|
||||||
out.append(""");
|
out.append(""");
|
||||||
continue;
|
continue;
|
||||||
case '\n':
|
case '\n':
|
||||||
out.append("\\n");
|
out.append("\\n");
|
||||||
continue;
|
continue;
|
||||||
default:
|
default:
|
||||||
if (!isPrintableChar(c)) {
|
if (!isPrintableChar(c)) {
|
||||||
out.append(String.format("\\u%04x", (int) c));
|
out.append(String.format("\\u%04x", (int) c));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.append(c);
|
out.append(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
return out.toString();
|
return out.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String encodeAsXmlValue(String str) {
|
public static String encodeAsXmlValue(String str) {
|
||||||
if (str.isEmpty()) {
|
if (str.isEmpty()) {
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
char[] chars = str.toCharArray();
|
char[] chars = str.toCharArray();
|
||||||
StringBuilder out = new StringBuilder(str.length() + 10);
|
StringBuilder out = new StringBuilder(str.length() + 10);
|
||||||
|
|
||||||
switch (chars[0]) {
|
switch (chars[0]) {
|
||||||
case '#':
|
case '#':
|
||||||
case '@':
|
case '@':
|
||||||
case '?':
|
case '?':
|
||||||
out.append('\\');
|
out.append('\\');
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isInStyleTag = false;
|
boolean isInStyleTag = false;
|
||||||
int startPos = 0;
|
int startPos = 0;
|
||||||
boolean enclose = false;
|
boolean enclose = false;
|
||||||
boolean wasSpace = true;
|
boolean wasSpace = true;
|
||||||
for (char c : chars) {
|
for (char c : chars) {
|
||||||
if (isInStyleTag) {
|
if (isInStyleTag) {
|
||||||
if (c == '>') {
|
if (c == '>') {
|
||||||
isInStyleTag = false;
|
isInStyleTag = false;
|
||||||
startPos = out.length() + 1;
|
startPos = out.length() + 1;
|
||||||
enclose = false;
|
enclose = false;
|
||||||
}
|
}
|
||||||
} else if (c == ' ') {
|
} else if (c == ' ') {
|
||||||
if (wasSpace) {
|
if (wasSpace) {
|
||||||
enclose = true;
|
enclose = true;
|
||||||
}
|
}
|
||||||
wasSpace = true;
|
wasSpace = true;
|
||||||
} else {
|
} else {
|
||||||
wasSpace = false;
|
wasSpace = false;
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '\\':
|
case '\\':
|
||||||
out.append('\\');
|
out.append('\\');
|
||||||
break;
|
break;
|
||||||
case '\'':
|
case '\'':
|
||||||
case '\n':
|
case '\n':
|
||||||
enclose = true;
|
enclose = true;
|
||||||
break;
|
break;
|
||||||
case '"':
|
case '"':
|
||||||
out.append('\\');
|
out.append('\\');
|
||||||
break;
|
break;
|
||||||
case '<':
|
case '<':
|
||||||
isInStyleTag = true;
|
isInStyleTag = true;
|
||||||
if (enclose) {
|
if (enclose) {
|
||||||
out.insert(startPos, '"').append('"');
|
out.insert(startPos, '"').append('"');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (!isPrintableChar(c)) {
|
if (!isPrintableChar(c)) {
|
||||||
|
|
||||||
// lets not write trailing \u0000 if we are at end of string
|
// lets not write trailing \u0000 if we are at end of string
|
||||||
if ((out.length() + 1) == str.length() && c == '\u0000') {
|
if ((out.length() + 1) == str.length() && c == '\u0000') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
out.append(String.format("\\u%04x", (int) c));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
out.append(String.format("\\u%04x", (int) c));
|
}
|
||||||
continue;
|
}
|
||||||
}
|
out.append(c);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
out.append(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enclose || wasSpace) {
|
if (enclose || wasSpace) {
|
||||||
out.insert(startPos, '"').append('"');
|
out.insert(startPos, '"').append('"');
|
||||||
}
|
}
|
||||||
return out.toString();
|
return out.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasMultipleNonPositionalSubstitutions(String str) {
|
public static boolean hasMultipleNonPositionalSubstitutions(String str) {
|
||||||
Duo<List<Integer>, List<Integer>> tuple = findSubstitutions(str, 2);
|
Duo<List<Integer>, List<Integer>> tuple = findSubstitutions(str, 2);
|
||||||
return ! tuple.m1.isEmpty() && tuple.m1.size() + tuple.m2.size() > 1;
|
return ! tuple.m1.isEmpty() && tuple.m1.size() + tuple.m2.size() > 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String enumerateNonPositionalSubstitutionsIfRequired(String str) {
|
public static String enumerateNonPositionalSubstitutionsIfRequired(String str) {
|
||||||
Duo<List<Integer>, List<Integer>> tuple = findSubstitutions(str, 2);
|
Duo<List<Integer>, List<Integer>> tuple = findSubstitutions(str, 2);
|
||||||
if (tuple.m1.isEmpty() || tuple.m1.size() + tuple.m2.size() < 2) {
|
if (tuple.m1.isEmpty() || tuple.m1.size() + tuple.m2.size() < 2) {
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
List<Integer> subs = tuple.m1;
|
List<Integer> subs = tuple.m1;
|
||||||
|
|
||||||
StringBuilder out = new StringBuilder();
|
StringBuilder out = new StringBuilder();
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (Integer sub : subs) {
|
for (Integer sub : subs) {
|
||||||
out.append(str.substring(pos, ++sub)).append(++count).append('$');
|
out.append(str.substring(pos, ++sub)).append(++count).append('$');
|
||||||
pos = sub;
|
pos = sub;
|
||||||
}
|
}
|
||||||
out.append(str.substring(pos));
|
out.append(str.substring(pos));
|
||||||
|
|
||||||
return out.toString();
|
return out.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* It returns a tuple of:
|
* It returns a tuple of:
|
||||||
* - a list of offsets of non positional substitutions. non-pos is defined as any "%" which isn't "%%" nor "%\d+\$"
|
* - a list of offsets of non positional substitutions. non-pos is defined as any "%" which isn't "%%" nor "%\d+\$"
|
||||||
* - a list of offsets of positional substitutions
|
* - a list of offsets of positional substitutions
|
||||||
*/
|
*/
|
||||||
private static Duo<List<Integer>, List<Integer>> findSubstitutions(String str, int nonPosMax) {
|
private static Duo<List<Integer>, List<Integer>> findSubstitutions(String str, int nonPosMax) {
|
||||||
if (nonPosMax == -1) {
|
if (nonPosMax == -1) {
|
||||||
nonPosMax = Integer.MAX_VALUE;
|
nonPosMax = Integer.MAX_VALUE;
|
||||||
}
|
}
|
||||||
int pos;
|
int pos;
|
||||||
int pos2 = 0;
|
int pos2 = 0;
|
||||||
int length = str.length();
|
int length = str.length();
|
||||||
List<Integer> nonPositional = new ArrayList<>();
|
List<Integer> nonPositional = new ArrayList<>();
|
||||||
List<Integer> positional = new ArrayList<>();
|
List<Integer> positional = new ArrayList<>();
|
||||||
while ((pos = str.indexOf('%', pos2)) != -1) {
|
while ((pos = str.indexOf('%', pos2)) != -1) {
|
||||||
pos2 = pos + 1;
|
pos2 = pos + 1;
|
||||||
if (pos2 == length) {
|
if (pos2 == length) {
|
||||||
nonPositional.add(pos);
|
nonPositional.add(pos);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
char c = str.charAt(pos2++);
|
char c = str.charAt(pos2++);
|
||||||
if (c == '%') {
|
if (c == '%') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (c >= '0' && c <= '9' && pos2 < length) {
|
if (c >= '0' && c <= '9' && pos2 < length) {
|
||||||
while ((c = str.charAt(pos2++)) >= '0' && c <= '9' && pos2 < length);
|
while ((c = str.charAt(pos2++)) >= '0' && c <= '9' && pos2 < length);
|
||||||
if (c == '$') {
|
if (c == '$') {
|
||||||
positional.add(pos);
|
positional.add(pos);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nonPositional.add(pos);
|
nonPositional.add(pos);
|
||||||
if (nonPositional.size() >= nonPosMax) {
|
if (nonPositional.size() >= nonPosMax) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Duo<>(nonPositional, positional);
|
return new Duo<>(nonPositional, positional);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isPrintableChar(char c) {
|
private static boolean isPrintableChar(char c) {
|
||||||
Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
|
Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
|
||||||
return !Character.isISOControl(c) && c != KeyEvent.CHAR_UNDEFINED
|
return !Character.isISOControl(c) && c != KeyEvent.CHAR_UNDEFINED
|
||||||
&& block != null && block != Character.UnicodeBlock.SPECIALS;
|
&& block != null && block != Character.UnicodeBlock.SPECIALS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,30 +30,30 @@ public class DebugInjector {
|
|||||||
private int currParam;
|
private int currParam;
|
||||||
private int lastParam;
|
private int lastParam;
|
||||||
|
|
||||||
public static void inject(ListIterator<String> it, StringBuilder out)
|
public static void inject(ListIterator<String> it, StringBuilder out)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
new DebugInjector(it, out).inject();
|
new DebugInjector(it, out).inject();
|
||||||
}
|
}
|
||||||
|
|
||||||
private DebugInjector(ListIterator<String> it, StringBuilder out) {
|
private DebugInjector(ListIterator<String> it, StringBuilder out) {
|
||||||
mIt = it;
|
mIt = it;
|
||||||
mOut = out;
|
mOut = out;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void inject() throws AndrolibException {
|
private void inject() throws AndrolibException {
|
||||||
String definition = nextAndAppend();
|
String definition = nextAndAppend();
|
||||||
if (definition.contains(" abstract ")
|
if (definition.contains(" abstract ")
|
||||||
|| definition.contains(" native ")) {
|
|| definition.contains(" native ")) {
|
||||||
nextAndAppend();
|
nextAndAppend();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
parseParamsNumber(definition);
|
parseParamsNumber(definition);
|
||||||
|
|
||||||
boolean end = false;
|
boolean end = false;
|
||||||
while (!end) {
|
while (!end) {
|
||||||
end = step();
|
end = step();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseParamsNumber(String definition) throws AndrolibException {
|
private void parseParamsNumber(String definition) throws AndrolibException {
|
||||||
int pos = definition.indexOf('(');
|
int pos = definition.indexOf('(');
|
||||||
@ -78,34 +78,34 @@ public class DebugInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean step() {
|
private boolean step() {
|
||||||
String line = next();
|
String line = next();
|
||||||
if (line.isEmpty()) {
|
if (line.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (line.charAt(0)) {
|
switch (line.charAt(0)) {
|
||||||
case '#':
|
case '#':
|
||||||
return processComment(line);
|
return processComment(line);
|
||||||
case ':':
|
case ':':
|
||||||
append(line);
|
append(line);
|
||||||
return false;
|
return false;
|
||||||
case '.':
|
case '.':
|
||||||
return processDirective(line);
|
return processDirective(line);
|
||||||
default:
|
default:
|
||||||
if (! areParamsInjected) {
|
if (! areParamsInjected) {
|
||||||
injectRemainingParams();
|
injectRemainingParams();
|
||||||
}
|
}
|
||||||
return processInstruction(line);
|
return processInstruction(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean processComment(String line) {
|
private boolean processComment(String line) {
|
||||||
if (mFirstInstruction) {
|
if (mFirstInstruction) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Matcher m = REGISTER_INFO_PATTERN.matcher(line);
|
Matcher m = REGISTER_INFO_PATTERN.matcher(line);
|
||||||
|
|
||||||
while (m.find()) {
|
while (m.find()) {
|
||||||
String localName = m.group(1);
|
String localName = m.group(1);
|
||||||
@ -145,7 +145,7 @@ public class DebugInjector {
|
|||||||
break;
|
break;
|
||||||
case "Float":
|
case "Float":
|
||||||
localType = "F";
|
localType = "F";
|
||||||
break;
|
break;
|
||||||
case "LongHi":
|
case "LongHi":
|
||||||
case "LongLo":
|
case "LongLo":
|
||||||
localType = "J";
|
localType = "J";
|
||||||
@ -167,11 +167,11 @@ public class DebugInjector {
|
|||||||
.append('\n');
|
.append('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean processDirective(String line) {
|
private boolean processDirective(String line) {
|
||||||
String line2 = line.substring(1);
|
String line2 = line.substring(1);
|
||||||
if (line2.startsWith("line ") || line2.startsWith("local ") || line2.startsWith("end local ")) {
|
if (line2.startsWith("line ") || line2.startsWith("local ") || line2.startsWith("end local ")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -191,54 +191,54 @@ public class DebugInjector {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
append(line);
|
append(line);
|
||||||
if (line2.equals("end method")) {
|
if (line2.equals("end method")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (line2.startsWith("annotation ") || line2.equals("sparse-switch")
|
if (line2.startsWith("annotation ") || line2.equals("sparse-switch")
|
||||||
|| line2.startsWith("packed-switch ")
|
|| line2.startsWith("packed-switch ")
|
||||||
|| line2.startsWith("array-data ")) {
|
|| line2.startsWith("array-data ")) {
|
||||||
while (true) {
|
while (true) {
|
||||||
line2 = nextAndAppend();
|
line2 = nextAndAppend();
|
||||||
if (line2.startsWith(".end ")) {
|
if (line2.startsWith(".end ")) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean processInstruction(String line) {
|
private boolean processInstruction(String line) {
|
||||||
if (mFirstInstruction) {
|
if (mFirstInstruction) {
|
||||||
mOut.append(".prologue\n");
|
mOut.append(".prologue\n");
|
||||||
mFirstInstruction = false;
|
mFirstInstruction = false;
|
||||||
}
|
}
|
||||||
mOut.append(".line ").append(mIt.nextIndex()).append('\n').append(line)
|
mOut.append(".line ").append(mIt.nextIndex()).append('\n').append(line)
|
||||||
.append('\n');
|
.append('\n');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String next() {
|
private String next() {
|
||||||
return mIt.next().split("//", 2)[1].trim();
|
return mIt.next().split("//", 2)[1].trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String nextAndAppend() {
|
private String nextAndAppend() {
|
||||||
String line = next();
|
String line = next();
|
||||||
append(line);
|
append(line);
|
||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void append(String append) {
|
private void append(String append) {
|
||||||
mOut.append(append).append('\n');
|
mOut.append(append).append('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ListIterator<String> mIt;
|
private final ListIterator<String> mIt;
|
||||||
private final StringBuilder mOut;
|
private final StringBuilder mOut;
|
||||||
|
|
||||||
private boolean mFirstInstruction = true;
|
private boolean mFirstInstruction = true;
|
||||||
private final Set<String> mInitializedRegisters = new HashSet<String>();
|
private final Set<String> mInitializedRegisters = new HashSet<String>();
|
||||||
|
|
||||||
private static final Pattern REGISTER_INFO_PATTERN = Pattern
|
private static final Pattern REGISTER_INFO_PATTERN = Pattern
|
||||||
.compile("((?:p|v)\\d+)=\\(([^,)]+)([^)]*)\\);");
|
.compile("((?:p|v)\\d+)=\\(([^,)]+)([^)]*)\\);");
|
||||||
}
|
}
|
||||||
|
@ -36,37 +36,37 @@ import org.jf.dexlib2.writer.io.FileDataStore;
|
|||||||
*/
|
*/
|
||||||
public class SmaliBuilder {
|
public class SmaliBuilder {
|
||||||
|
|
||||||
public static void build(ExtFile smaliDir, File dexFile,
|
public static void build(ExtFile smaliDir, File dexFile,
|
||||||
HashMap<String, Boolean> flags) throws AndrolibException {
|
HashMap<String, Boolean> flags) throws AndrolibException {
|
||||||
new SmaliBuilder(smaliDir, dexFile, flags).build();
|
new SmaliBuilder(smaliDir, dexFile, flags).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SmaliBuilder(ExtFile smaliDir, File dexFile,
|
private SmaliBuilder(ExtFile smaliDir, File dexFile,
|
||||||
HashMap<String, Boolean> flags) {
|
HashMap<String, Boolean> flags) {
|
||||||
mSmaliDir = smaliDir;
|
mSmaliDir = smaliDir;
|
||||||
mDexFile = dexFile;
|
mDexFile = dexFile;
|
||||||
mFlags = flags;
|
mFlags = flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void build() throws AndrolibException {
|
private void build() throws AndrolibException {
|
||||||
try {
|
try {
|
||||||
DexBuilder dexBuilder = DexBuilder.makeDexBuilder();
|
DexBuilder dexBuilder = DexBuilder.makeDexBuilder();
|
||||||
|
|
||||||
for (String fileName : mSmaliDir.getDirectory().getFiles(true)) {
|
for (String fileName : mSmaliDir.getDirectory().getFiles(true)) {
|
||||||
buildFile(fileName, dexBuilder);
|
buildFile(fileName, dexBuilder);
|
||||||
}
|
}
|
||||||
dexBuilder.writeTo(new FileDataStore( new File(mDexFile.getAbsolutePath())));
|
dexBuilder.writeTo(new FileDataStore( new File(mDexFile.getAbsolutePath())));
|
||||||
} catch (IOException | DirectoryException ex) {
|
} catch (IOException | DirectoryException ex) {
|
||||||
throw new AndrolibException(ex);
|
throw new AndrolibException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildFile(String fileName, DexBuilder dexBuilder) throws AndrolibException,
|
private void buildFile(String fileName, DexBuilder dexBuilder) throws AndrolibException,
|
||||||
IOException {
|
IOException {
|
||||||
File inFile = new File(mSmaliDir, fileName);
|
File inFile = new File(mSmaliDir, fileName);
|
||||||
InputStream inStream = new FileInputStream(inFile);
|
InputStream inStream = new FileInputStream(inFile);
|
||||||
|
|
||||||
if (fileName.endsWith(".smali")) {
|
if (fileName.endsWith(".smali")) {
|
||||||
try {
|
try {
|
||||||
if (!SmaliMod.assembleSmaliFile(inFile,dexBuilder, false, false)) {
|
if (!SmaliMod.assembleSmaliFile(inFile,dexBuilder, false, false)) {
|
||||||
throw new AndrolibException("Could not smali file: " + fileName);
|
throw new AndrolibException("Could not smali file: " + fileName);
|
||||||
@ -74,41 +74,41 @@ public class SmaliBuilder {
|
|||||||
} catch (IOException | RecognitionException ex) {
|
} catch (IOException | RecognitionException ex) {
|
||||||
throw new AndrolibException(ex);
|
throw new AndrolibException(ex);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!fileName.endsWith(".java")) {
|
if (!fileName.endsWith(".java")) {
|
||||||
LOGGER.warning("Unknown file type, ignoring: " + inFile);
|
LOGGER.warning("Unknown file type, ignoring: " + inFile);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder out = new StringBuilder();
|
StringBuilder out = new StringBuilder();
|
||||||
List<String> lines = IOUtils.readLines(inStream);
|
List<String> lines = IOUtils.readLines(inStream);
|
||||||
|
|
||||||
if (!mFlags.get("debug")) {
|
if (!mFlags.get("debug")) {
|
||||||
final String[] linesArray = lines.toArray(new String[0]);
|
final String[] linesArray = lines.toArray(new String[0]);
|
||||||
for (int i = 1; i < linesArray.length - 1; i++) {
|
for (int i = 1; i < linesArray.length - 1; i++) {
|
||||||
out.append(linesArray[i].split("//", 2)[1]).append('\n');
|
out.append(linesArray[i].split("//", 2)[1]).append('\n');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
lines.remove(lines.size() - 1);
|
lines.remove(lines.size() - 1);
|
||||||
ListIterator<String> it = lines.listIterator(1);
|
ListIterator<String> it = lines.listIterator(1);
|
||||||
|
|
||||||
out.append(".source \"").append(inFile.getName()).append("\"\n");
|
out.append(".source \"").append(inFile.getName()).append("\"\n");
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
String line = it.next().split("//", 2)[1].trim();
|
String line = it.next().split("//", 2)[1].trim();
|
||||||
if (line.isEmpty() || line.charAt(0) == '#'
|
if (line.isEmpty() || line.charAt(0) == '#'
|
||||||
|| line.startsWith(".source")) {
|
|| line.startsWith(".source")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (line.startsWith(".method ")) {
|
if (line.startsWith(".method ")) {
|
||||||
it.previous();
|
it.previous();
|
||||||
DebugInjector.inject(it, out);
|
DebugInjector.inject(it, out);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
out.append(line).append('\n');
|
out.append(line).append('\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!SmaliMod.assembleSmaliFile(out.toString(),dexBuilder, false, false, inFile)) {
|
if (!SmaliMod.assembleSmaliFile(out.toString(),dexBuilder, false, false, inFile)) {
|
||||||
@ -117,12 +117,12 @@ public class SmaliBuilder {
|
|||||||
} catch (IOException | RecognitionException ex) {
|
} catch (IOException | RecognitionException ex) {
|
||||||
throw new AndrolibException(ex);
|
throw new AndrolibException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ExtFile mSmaliDir;
|
private final ExtFile mSmaliDir;
|
||||||
private final File mDexFile;
|
private final File mDexFile;
|
||||||
private final HashMap<String, Boolean> mFlags;
|
private final HashMap<String, Boolean> mFlags;
|
||||||
|
|
||||||
private final static Logger LOGGER = Logger.getLogger(SmaliBuilder.class
|
private final static Logger LOGGER = Logger.getLogger(SmaliBuilder.class
|
||||||
.getName());
|
.getName());
|
||||||
}
|
}
|
||||||
|
@ -41,23 +41,23 @@ import java.nio.file.attribute.BasicFileAttributes;
|
|||||||
*/
|
*/
|
||||||
public class SmaliDecoder {
|
public class SmaliDecoder {
|
||||||
|
|
||||||
public static void decode(File apkFile, File outDir, boolean debug, String debugLinePrefix,
|
public static void decode(File apkFile, File outDir, boolean debug, String debugLinePrefix,
|
||||||
boolean bakdeb, int api) throws AndrolibException {
|
boolean bakdeb, int api) throws AndrolibException {
|
||||||
new SmaliDecoder(apkFile, outDir, debug, debugLinePrefix, bakdeb, api).decode();
|
new SmaliDecoder(apkFile, outDir, debug, debugLinePrefix, bakdeb, api).decode();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SmaliDecoder(File apkFile, File outDir, boolean debug, String debugLinePrefix,
|
private SmaliDecoder(File apkFile, File outDir, boolean debug, String debugLinePrefix,
|
||||||
boolean bakdeb, int api) {
|
boolean bakdeb, int api) {
|
||||||
mApkFile = apkFile;
|
mApkFile = apkFile;
|
||||||
mOutDir = outDir.toPath();
|
mOutDir = outDir.toPath();
|
||||||
mDebug = debug;
|
mDebug = debug;
|
||||||
mDebugLinePrefix = debugLinePrefix;
|
mDebugLinePrefix = debugLinePrefix;
|
||||||
mBakDeb = bakdeb;
|
mBakDeb = bakdeb;
|
||||||
mApi = api;
|
mApi = api;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void decode() throws AndrolibException {
|
private void decode() throws AndrolibException {
|
||||||
try {
|
try {
|
||||||
ClassPath.dontLoadClassPath = mDebug;
|
ClassPath.dontLoadClassPath = mDebug;
|
||||||
|
|
||||||
baksmaliOptions options = new baksmaliOptions();
|
baksmaliOptions options = new baksmaliOptions();
|
||||||
@ -106,16 +106,16 @@ public class SmaliDecoder {
|
|||||||
if (mDebug) {
|
if (mDebug) {
|
||||||
Files.walkFileTree(mOutDir, new SmaliFileVisitor());
|
Files.walkFileTree(mOutDir, new SmaliFileVisitor());
|
||||||
}
|
}
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new AndrolibException(ex);
|
throw new AndrolibException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final File mApkFile;
|
private final File mApkFile;
|
||||||
private final Path mOutDir;
|
private final Path mOutDir;
|
||||||
private final boolean mDebug;
|
private final boolean mDebug;
|
||||||
private final String mDebugLinePrefix;
|
private final String mDebugLinePrefix;
|
||||||
private final boolean mBakDeb;
|
private final boolean mBakDeb;
|
||||||
private final int mApi;
|
private final int mApi;
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,99 +30,99 @@ import java.util.List;
|
|||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class TypeName {
|
public class TypeName {
|
||||||
public final String package_;
|
public final String package_;
|
||||||
public final String type;
|
public final String type;
|
||||||
public final String innerType;
|
public final String innerType;
|
||||||
public final int array;
|
public final int array;
|
||||||
|
|
||||||
public TypeName(String type, int array) {
|
public TypeName(String type, int array) {
|
||||||
this(null, type, null, array);
|
this(null, type, null, array);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TypeName(String package_, String type, String innerType, int array) {
|
public TypeName(String package_, String type, String innerType, int array) {
|
||||||
this.package_ = package_;
|
this.package_ = package_;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.innerType = innerType;
|
this.innerType = innerType;
|
||||||
this.array = array;
|
this.array = array;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getShortenedName() {
|
public String getShortenedName() {
|
||||||
return getName("java.lang".equals(package_), isFileOwner());
|
return getName("java.lang".equals(package_), isFileOwner());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return getName(false, false);
|
return getName(false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName(boolean excludePackage, boolean separateInner) {
|
public String getName(boolean excludePackage, boolean separateInner) {
|
||||||
String name = (package_ == null || excludePackage ? "" : package_ + '.')
|
String name = (package_ == null || excludePackage ? "" : package_ + '.')
|
||||||
+ type
|
+ type
|
||||||
+ (innerType != null ? (separateInner ? '$' : '.') + innerType
|
+ (innerType != null ? (separateInner ? '$' : '.') + innerType
|
||||||
: "");
|
: "");
|
||||||
for (int i = 0; i < array; i++) {
|
for (int i = 0; i < array; i++) {
|
||||||
name += "[]";
|
name += "[]";
|
||||||
}
|
}
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getJavaFilePath() {
|
public String getJavaFilePath() {
|
||||||
return getFilePath(isFileOwner()) + ".java";
|
return getFilePath(isFileOwner()) + ".java";
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSmaliFilePath() {
|
public String getSmaliFilePath() {
|
||||||
return getFilePath(true) + ".smali";
|
return getFilePath(true) + ".smali";
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFilePath(boolean separateInner) {
|
public String getFilePath(boolean separateInner) {
|
||||||
return package_.replace('.', File.separatorChar) + File.separatorChar
|
return package_.replace('.', File.separatorChar) + File.separatorChar
|
||||||
+ type + (separateInner && isInner() ? "$" + innerType : "");
|
+ type + (separateInner && isInner() ? "$" + innerType : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInner() {
|
public boolean isInner() {
|
||||||
return innerType != null;
|
return innerType != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isArray() {
|
public boolean isArray() {
|
||||||
return array != 0;
|
return array != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFileOwner() {
|
public boolean isFileOwner() {
|
||||||
if (mIsFileOwner == null) {
|
if (mIsFileOwner == null) {
|
||||||
mIsFileOwner = true;
|
mIsFileOwner = true;
|
||||||
if (isInner()) {
|
if (isInner()) {
|
||||||
char c = innerType.charAt(0);
|
char c = innerType.charAt(0);
|
||||||
if (c < '0' || c > '9') {
|
if (c < '0' || c > '9') {
|
||||||
mIsFileOwner = false;
|
mIsFileOwner = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mIsFileOwner;
|
return mIsFileOwner;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getName();
|
return getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TypeName fromInternalName(String internal)
|
public static TypeName fromInternalName(String internal)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
Duo<TypeName, Integer> duo = fetchFromInternalName(internal);
|
Duo<TypeName, Integer> duo = fetchFromInternalName(internal);
|
||||||
if (duo.m2 != internal.length()) {
|
if (duo.m2 != internal.length()) {
|
||||||
throw new AndrolibException("Invalid internal name: " + internal);
|
throw new AndrolibException("Invalid internal name: " + internal);
|
||||||
}
|
}
|
||||||
return duo.m1;
|
return duo.m1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<TypeName> listFromInternalName(String internal)
|
public static List<TypeName> listFromInternalName(String internal)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
List<TypeName> types = new ArrayList<TypeName>();
|
List<TypeName> types = new ArrayList<TypeName>();
|
||||||
while (!internal.isEmpty()) {
|
while (!internal.isEmpty()) {
|
||||||
Duo<TypeName, Integer> duo = fetchFromInternalName(internal);
|
Duo<TypeName, Integer> duo = fetchFromInternalName(internal);
|
||||||
types.add(duo.m1);
|
types.add(duo.m1);
|
||||||
internal = internal.substring(duo.m2);
|
internal = internal.substring(duo.m2);
|
||||||
}
|
}
|
||||||
return types;
|
return types;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TypeName fromPath(Path path) {
|
public static TypeName fromPath(Path path) {
|
||||||
List<String> parts = new ArrayList<>(path.getNameCount());
|
List<String> parts = new ArrayList<>(path.getNameCount());
|
||||||
@ -145,68 +145,68 @@ public class TypeName {
|
|||||||
return new TypeName(Joiner.on('.').join(parts), type, innerType, array);
|
return new TypeName(Joiner.on('.').join(parts), type, innerType, array);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Duo<TypeName, Integer> fetchFromInternalName(String internal)
|
public static Duo<TypeName, Integer> fetchFromInternalName(String internal)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
String origInternal = internal;
|
String origInternal = internal;
|
||||||
int array = 0;
|
int array = 0;
|
||||||
|
|
||||||
boolean isArray = false;
|
boolean isArray = false;
|
||||||
do {
|
do {
|
||||||
if (internal.isEmpty()) {
|
if (internal.isEmpty()) {
|
||||||
throw new AndrolibException("Invalid internal name: "
|
throw new AndrolibException("Invalid internal name: "
|
||||||
+ origInternal);
|
+ origInternal);
|
||||||
}
|
}
|
||||||
isArray = internal.charAt(0) == '[';
|
isArray = internal.charAt(0) == '[';
|
||||||
if (isArray) {
|
if (isArray) {
|
||||||
array++;
|
array++;
|
||||||
internal = internal.substring(1);
|
internal = internal.substring(1);
|
||||||
}
|
}
|
||||||
} while (isArray);
|
} while (isArray);
|
||||||
|
|
||||||
int length = array + 1;
|
int length = array + 1;
|
||||||
String type;
|
String type;
|
||||||
switch (internal.charAt(0)) {
|
switch (internal.charAt(0)) {
|
||||||
case 'B':
|
case 'B':
|
||||||
type = "byte";
|
type = "byte";
|
||||||
break;
|
break;
|
||||||
case 'C':
|
case 'C':
|
||||||
type = "char";
|
type = "char";
|
||||||
break;
|
break;
|
||||||
case 'D':
|
case 'D':
|
||||||
type = "double";
|
type = "double";
|
||||||
break;
|
break;
|
||||||
case 'F':
|
case 'F':
|
||||||
type = "float";
|
type = "float";
|
||||||
break;
|
break;
|
||||||
case 'I':
|
case 'I':
|
||||||
type = "int";
|
type = "int";
|
||||||
break;
|
break;
|
||||||
case 'J':
|
case 'J':
|
||||||
type = "long";
|
type = "long";
|
||||||
break;
|
break;
|
||||||
case 'S':
|
case 'S':
|
||||||
type = "short";
|
type = "short";
|
||||||
break;
|
break;
|
||||||
case 'Z':
|
case 'Z':
|
||||||
type = "boolean";
|
type = "boolean";
|
||||||
break;
|
break;
|
||||||
case 'V':
|
case 'V':
|
||||||
type = "void";
|
type = "void";
|
||||||
break;
|
break;
|
||||||
case 'L':
|
case 'L':
|
||||||
int pos = internal.indexOf(';');
|
int pos = internal.indexOf(';');
|
||||||
if (pos == -1) {
|
if (pos == -1) {
|
||||||
throw new AndrolibException("Invalid internal name: "
|
throw new AndrolibException("Invalid internal name: "
|
||||||
+ origInternal);
|
+ origInternal);
|
||||||
}
|
}
|
||||||
return new Duo<>(fromNameParts(Arrays.asList(internal.substring(1, pos).split("/")), array), length + pos);
|
return new Duo<>(fromNameParts(Arrays.asList(internal.substring(1, pos).split("/")), array), length + pos);
|
||||||
default:
|
default:
|
||||||
throw new AndrolibException("Invalid internal name: "
|
throw new AndrolibException("Invalid internal name: "
|
||||||
+ origInternal);
|
+ origInternal);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Duo<>(new TypeName(null, type, null, array), length);
|
return new Duo<>(new TypeName(null, type, null, array), length);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Boolean mIsFileOwner;
|
private Boolean mIsFileOwner;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user