├── .gitignore ├── LICENSE ├── NOTICE ├── NOTICE-smali ├── apktool-cli ├── pom.xml └── src │ └── main │ ├── assembly │ └── jar-with-deps.xml │ └── java │ └── brut │ └── apktool │ └── Main.java ├── apktool-lib ├── pom.xml └── src │ ├── main │ ├── java │ │ ├── android │ │ │ ├── content │ │ │ │ └── res │ │ │ │ │ └── XmlResourceParser.java │ │ │ └── util │ │ │ │ ├── AttributeSet.java │ │ │ │ └── TypedValue.java │ │ ├── brut │ │ │ └── androlib │ │ │ │ ├── Androlib.java │ │ │ │ ├── AndrolibException.java │ │ │ │ ├── ApkDecoder.java │ │ │ │ ├── ApktoolProperties.java │ │ │ │ ├── err │ │ │ │ ├── CantFind9PatchChunk.java │ │ │ │ ├── CantFindFrameworkResException.java │ │ │ │ ├── InFileNotFoundException.java │ │ │ │ ├── OutDirExistsException.java │ │ │ │ └── UndefinedResObject.java │ │ │ │ ├── java │ │ │ │ └── AndrolibJava.java │ │ │ │ ├── mod │ │ │ │ ├── IndentingWriter.java │ │ │ │ └── SmaliMod.java │ │ │ │ ├── res │ │ │ │ ├── AndrolibResources.java │ │ │ │ ├── ResSmaliUpdater.java │ │ │ │ ├── data │ │ │ │ │ ├── ResConfig.java │ │ │ │ │ ├── ResConfigFlags.java │ │ │ │ │ ├── ResID.java │ │ │ │ │ ├── ResPackage.java │ │ │ │ │ ├── ResResSpec.java │ │ │ │ │ ├── ResResource.java │ │ │ │ │ ├── ResTable.java │ │ │ │ │ ├── ResType.java │ │ │ │ │ ├── ResValuesFile.java │ │ │ │ │ └── value │ │ │ │ │ │ ├── ResArrayValue.java │ │ │ │ │ │ ├── ResAttr.java │ │ │ │ │ │ ├── ResBagValue.java │ │ │ │ │ │ ├── ResBoolValue.java │ │ │ │ │ │ ├── ResColorValue.java │ │ │ │ │ │ ├── ResDimenValue.java │ │ │ │ │ │ ├── ResEnumAttr.java │ │ │ │ │ │ ├── ResFileValue.java │ │ │ │ │ │ ├── ResFlagsAttr.java │ │ │ │ │ │ ├── ResFloatValue.java │ │ │ │ │ │ ├── ResFractionValue.java │ │ │ │ │ │ ├── ResIdValue.java │ │ │ │ │ │ ├── ResIntValue.java │ │ │ │ │ │ ├── ResPluralsValue.java │ │ │ │ │ │ ├── ResReferenceValue.java │ │ │ │ │ │ ├── ResScalarValue.java │ │ │ │ │ │ ├── ResStringValue.java │ │ │ │ │ │ ├── ResStyleValue.java │ │ │ │ │ │ ├── ResValue.java │ │ │ │ │ │ └── ResValueFactory.java │ │ │ │ ├── decoder │ │ │ │ │ ├── ARSCDecoder.java │ │ │ │ │ ├── AXmlResourceParser.java │ │ │ │ │ ├── Res9patchStreamDecoder.java │ │ │ │ │ ├── ResAttrDecoder.java │ │ │ │ │ ├── ResFileDecoder.java │ │ │ │ │ ├── ResRawStreamDecoder.java │ │ │ │ │ ├── ResStreamDecoder.java │ │ │ │ │ ├── ResStreamDecoderContainer.java │ │ │ │ │ ├── StringBlock.java │ │ │ │ │ └── XmlPullStreamDecoder.java │ │ │ │ ├── util │ │ │ │ │ ├── ExtFile.java │ │ │ │ │ ├── ExtMXSerializer.java │ │ │ │ │ └── ExtXmlSerializer.java │ │ │ │ └── xml │ │ │ │ │ ├── ResValuesXmlSerializable.java │ │ │ │ │ ├── ResXmlEncodable.java │ │ │ │ │ └── ResXmlEncoders.java │ │ │ │ └── src │ │ │ │ ├── DebugInjector.java │ │ │ │ ├── DexFileBuilder.java │ │ │ │ ├── SmaliBuilder.java │ │ │ │ ├── SmaliDecoder.java │ │ │ │ └── TypeName.java │ │ └── com │ │ │ └── mindprod │ │ │ └── ledatastream │ │ │ └── LEDataInputStream.java │ └── resources │ │ └── brut │ │ └── androlib │ │ ├── android-framework.jar │ │ └── apktool.properties │ └── test │ ├── java │ └── brut │ │ └── androlib │ │ ├── BuildAndDecodeTest.java │ │ └── TestUtils.java │ └── resources │ └── brut │ └── apktool │ └── testapp │ ├── AndroidManifest.xml │ ├── apktool.yml │ └── res │ ├── values-mcc001 │ ├── arrays.xml │ ├── bools.xml │ ├── colors.xml │ ├── dimens.xml │ ├── ids.xml │ ├── integers.xml │ └── strings.xml │ ├── values-mcc002 │ └── strings.xml │ ├── values-mcc003 │ ├── bools.xml │ ├── integers.xml │ └── strings.xml │ ├── values-mcc004-mnc4-en-rUS-sw100dp-w200dp-h300dp-xlarge-long-land-television-night-xhdpi-finger-keyssoft-12key-navhidden-dpad │ └── strings.xml │ ├── values │ └── strings.xml │ └── xml │ ├── literals.xml │ └── references.xml ├── pom.xml └── src └── templates └── apache2.0-header.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /nb* 2 | /target 3 | /apktool-lib/nb* 4 | /apktool-lib/target 5 | /apktool-cli/nb* 6 | /apktool-cli/target 7 | 8 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | apktool 2 | Copyright 2011 Ryszard Wiśniewski 3 | 4 | This product includes software developed by: 5 | 6 | * Ryszard Wiśniewski (brut.alll@gmail.com) 7 | * JesusFreke (http://code.google.com/p/smali/) 8 | * Dmitry Skiba (http://code.google.com/p/android4me/) 9 | * Tahseen Ur Rehman (http://code.google.com/p/radixtree/) 10 | * Android Open Source Project (http://source.android.com/) 11 | * The Apache Software Foundation (http://www.apache.org/) 12 | -------------------------------------------------------------------------------- /NOTICE-smali: -------------------------------------------------------------------------------- 1 | The majority of smali/baksmali is written and copyrighted by me (Ben Gruver) 2 | and released under the following license: 3 | 4 | ******************************************************************************* 5 | Copyright (c) 2010 Ben Gruver (JesusFreke) 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions 10 | are met: 11 | 1. Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | 2. Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in the 15 | documentation and/or other materials provided with the distribution. 16 | 3. The name of the author may not be used to endorse or promote products 17 | derived from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | ******************************************************************************* 30 | 31 | 32 | Various portions of the code are taken from the Android Open Source Project, 33 | and are used in accordance with the following license: 34 | 35 | ******************************************************************************* 36 | Copyright (C) 2007 The Android Open Source Project 37 | 38 | Licensed under the Apache License, Version 2.0 (the "License"); 39 | you may not use this file except in compliance with the License. 40 | You may obtain a copy of the License at 41 | 42 | http://www.apache.org/licenses/LICENSE-2.0 43 | 44 | Unless required by applicable law or agreed to in writing, software 45 | distributed under the License is distributed on an "AS IS" BASIS, 46 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 47 | See the License for the specific language governing permissions and 48 | limitations under the License. 49 | ******************************************************************************* 50 | 51 | 52 | The smali mojo plugin is (very loosely) based on an unknown mojo plugin with 53 | the following license: 54 | 55 | ******************************************************************************* 56 | Copyright 2001-2005 The Apache Software Foundation. 57 | 58 | Licensed under the Apache License, Version 2.0 (the "License"); 59 | you may not use this file except in compliance with the License. 60 | You may obtain a copy of the License at 61 | 62 | http://www.apache.org/licenses/LICENSE-2.0 63 | 64 | Unless required by applicable law or agreed to in writing, software 65 | distributed under the License is distributed on an "AS IS" BASIS, 66 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 67 | See the License for the specific language governing permissions and 68 | limitations under the License. 69 | ******************************************************************************* 70 | 71 | 72 | The RadixTree implementation in the "util" project is taken from 73 | http://code.google.com/p/radixtree/ (version .3), and is used with minor 74 | modifications in accordance with the following license: 75 | 76 | ******************************************************************************* 77 | The MIT License 78 | 79 | Copyright (c) 2008 Tahseen Ur Rehman 80 | 81 | Permission is hereby granted, free of charge, to any person obtaining a copy 82 | of this software and associated documentation files (the "Software"), to deal 83 | in the Software without restriction, including without limitation the rights 84 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 85 | copies of the Software, and to permit persons to whom the Software is 86 | furnished to do so, subject to the following conditions: 87 | 88 | The above copyright notice and this permission notice shall be included in 89 | all copies or substantial portions of the Software. 90 | 91 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 92 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 93 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 94 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 95 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 96 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 97 | THE SOFTWARE. 98 | ******************************************************************************* 99 | -------------------------------------------------------------------------------- /apktool-cli/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | brut.apktool 6 | apktool-cli 7 | 1.4.4-SNAPSHOT 8 | jar 9 | 10 | 11 | brut.apktool 12 | apktool-project 13 | 1.0-SNAPSHOT 14 | 15 | 16 | apktool cli 17 | 18 | 19 | ${project.parent.basedir} 20 | 21 | 22 | 23 | 24 | 25 | maven-assembly-plugin 26 | 27 | 28 | src/main/assembly/jar-with-deps.xml 29 | 30 | 31 | 32 | brut.apktool.Main 33 | 34 | 35 | 36 | 37 | 38 | package 39 | 40 | single 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | ${project.groupId} 51 | apktool-lib 52 | ${project.version} 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /apktool-cli/src/main/assembly/jar-with-deps.xml: -------------------------------------------------------------------------------- 1 | 18 | 19 | 22 | jar-with-deps 23 | 24 | jar 25 | 26 | false 27 | 28 | 29 | / 30 | true 31 | runtime 32 | 33 | brut.* 34 | xpp3:xpp3 35 | org.yaml:snakeyaml 36 | commons-io:* 37 | org.antlr:antlr-runtime 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /apktool-lib/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | brut.apktool 6 | apktool-lib 7 | 1.4.4-SNAPSHOT 8 | jar 9 | 10 | 11 | brut.apktool 12 | apktool-project 13 | 1.0-SNAPSHOT 14 | 15 | 16 | apktool library 17 | 18 | 19 | 1.3.0-brut6 20 | ${project.parent.basedir} 21 | 22 | 23 | 24 | 25 | 26 | src/main/resources 27 | true 28 | 29 | brut/androlib/apktool.properties 30 | 31 | 32 | 33 | src/main/resources 34 | 35 | brut/androlib/android-framework.jar 36 | 37 | 38 | 39 | 40 | 41 | com.mycila.maven-license-plugin 42 | maven-license-plugin 43 | 44 | 45 | src/main/java/com/mindprod/** 46 | src/main/java/android/** 47 | src/main/resources/brut/androlib/apktool.properties 48 | src/test/resources/brut/apktool/testapp/** 49 | 50 | 51 | 52 | 53 | pl.project13.maven 54 | git-commit-id-plugin 55 | 1.5 56 | 57 | 58 | 59 | revision 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | brut.j 70 | dir 71 | 1.0 72 | 73 | 74 | brut.j 75 | util 76 | 1.0 77 | 78 | 79 | brut.j 80 | common 81 | 1.0 82 | 83 | 84 | brut.apktool.smali 85 | smali 86 | ${smaliVersion} 87 | 88 | 89 | brut.apktool.smali 90 | baksmali 91 | ${smaliVersion} 92 | 93 | 94 | brut.apktool.smali 95 | util 96 | ${smaliVersion} 97 | 98 | 99 | org.yaml 100 | snakeyaml 101 | 1.7 102 | 103 | 104 | xpp3 105 | xpp3 106 | 1.1.4c 107 | 108 | 109 | junit 110 | junit 111 | 4.8.2 112 | 113 | 114 | xmlunit 115 | xmlunit 116 | 1.3 117 | 118 | 119 | 120 | 121 | 122 | sonatype-releases 123 | Sonatype Releases 124 | https://oss.sonatype.org/content/repositories/releases/ 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/android/content/res/XmlResourceParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.content.res; 18 | 19 | import org.xmlpull.v1.XmlPullParser; 20 | 21 | import android.util.AttributeSet; 22 | 23 | /** 24 | * The XML parsing interface returned for an XML resource. This is a standard 25 | * XmlPullParser interface, as well as an extended AttributeSet interface and 26 | * an additional close() method on this interface for the client to indicate 27 | * when it is done reading the resource. 28 | */ 29 | public interface XmlResourceParser extends XmlPullParser, AttributeSet { 30 | /** 31 | * Close this interface to the resource. Calls on the interface are no 32 | * longer value after this call. 33 | */ 34 | public void close(); 35 | } 36 | 37 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/android/util/AttributeSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008 Android4ME 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.util; 17 | 18 | /** 19 | * @author Dmitry Skiba 20 | * 21 | */ 22 | public interface AttributeSet { 23 | int getAttributeCount(); 24 | String getAttributeName(int index); 25 | String getAttributeValue(int index); 26 | String getPositionDescription(); 27 | int getAttributeNameResource(int index); 28 | int getAttributeListValue(int index,String options[],int defaultValue); 29 | boolean getAttributeBooleanValue(int index,boolean defaultValue); 30 | int getAttributeResourceValue(int index,int defaultValue); 31 | int getAttributeIntValue(int index,int defaultValue); 32 | int getAttributeUnsignedIntValue(int index,int defaultValue); 33 | float getAttributeFloatValue(int index,float defaultValue); 34 | String getIdAttribute(); 35 | String getClassAttribute(); 36 | int getIdAttributeResourceValue(int index); 37 | int getStyleAttribute(); 38 | String getAttributeValue(String namespace, String attribute); 39 | int getAttributeListValue(String namespace,String attribute,String options[],int defaultValue); 40 | boolean getAttributeBooleanValue(String namespace,String attribute,boolean defaultValue); 41 | int getAttributeResourceValue(String namespace,String attribute,int defaultValue); 42 | int getAttributeIntValue(String namespace,String attribute,int defaultValue); 43 | int getAttributeUnsignedIntValue(String namespace,String attribute,int defaultValue); 44 | float getAttributeFloatValue(String namespace,String attribute,float defaultValue); 45 | 46 | //TODO: remove 47 | int getAttributeValueType(int index); 48 | int getAttributeValueData(int index); 49 | } 50 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/android/util/TypedValue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.util; 18 | 19 | /** 20 | * Container for a dynamically typed data value. Primarily used with 21 | * {@link android.content.res.Resources} for holding resource values. 22 | */ 23 | public class TypedValue { 24 | /** The value contains no data. */ 25 | public static final int TYPE_NULL = 0x00; 26 | 27 | /** The data field holds a resource identifier. */ 28 | public static final int TYPE_REFERENCE = 0x01; 29 | /** The data field holds an attribute resource 30 | * identifier (referencing an attribute in the current theme 31 | * style, not a resource entry). */ 32 | public static final int TYPE_ATTRIBUTE = 0x02; 33 | /** The string field holds string data. In addition, if 34 | * data is non-zero then it is the string block 35 | * index of the string and assetCookie is the set of 36 | * assets the string came from. */ 37 | public static final int TYPE_STRING = 0x03; 38 | /** The data field holds an IEEE 754 floating point number. */ 39 | public static final int TYPE_FLOAT = 0x04; 40 | /** The data field holds a complex number encoding a 41 | * dimension value. */ 42 | public static final int TYPE_DIMENSION = 0x05; 43 | /** The data field holds a complex number encoding a fraction 44 | * of a container. */ 45 | public static final int TYPE_FRACTION = 0x06; 46 | 47 | /** Identifies the start of plain integer values. Any type value 48 | * from this to {@link #TYPE_LAST_INT} means the 49 | * data field holds a generic integer value. */ 50 | public static final int TYPE_FIRST_INT = 0x10; 51 | 52 | /** The data field holds a number that was 53 | * originally specified in decimal. */ 54 | public static final int TYPE_INT_DEC = 0x10; 55 | /** The data field holds a number that was 56 | * originally specified in hexadecimal (0xn). */ 57 | public static final int TYPE_INT_HEX = 0x11; 58 | /** The data field holds 0 or 1 that was originally 59 | * specified as "false" or "true". */ 60 | public static final int TYPE_INT_BOOLEAN = 0x12; 61 | 62 | /** Identifies the start of integer values that were specified as 63 | * color constants (starting with '#'). */ 64 | public static final int TYPE_FIRST_COLOR_INT = 0x1c; 65 | 66 | /** The data field holds a color that was originally 67 | * specified as #aarrggbb. */ 68 | public static final int TYPE_INT_COLOR_ARGB8 = 0x1c; 69 | /** The data field holds a color that was originally 70 | * specified as #rrggbb. */ 71 | public static final int TYPE_INT_COLOR_RGB8 = 0x1d; 72 | /** The data field holds a color that was originally 73 | * specified as #argb. */ 74 | public static final int TYPE_INT_COLOR_ARGB4 = 0x1e; 75 | /** The data field holds a color that was originally 76 | * specified as #rgb. */ 77 | public static final int TYPE_INT_COLOR_RGB4 = 0x1f; 78 | 79 | /** Identifies the end of integer values that were specified as color 80 | * constants. */ 81 | public static final int TYPE_LAST_COLOR_INT = 0x1f; 82 | 83 | /** Identifies the end of plain integer values. */ 84 | public static final int TYPE_LAST_INT = 0x1f; 85 | 86 | /* ------------------------------------------------------------ */ 87 | 88 | /** Complex data: bit location of unit information. */ 89 | public static final int COMPLEX_UNIT_SHIFT = 0; 90 | /** Complex data: mask to extract unit information (after shifting by 91 | * {@link #COMPLEX_UNIT_SHIFT}). This gives us 16 possible types, as 92 | * defined below. */ 93 | public static final int COMPLEX_UNIT_MASK = 0xf; 94 | 95 | /** {@link #TYPE_DIMENSION} complex unit: Value is raw pixels. */ 96 | public static final int COMPLEX_UNIT_PX = 0; 97 | /** {@link #TYPE_DIMENSION} complex unit: Value is Device Independent 98 | * Pixels. */ 99 | public static final int COMPLEX_UNIT_DIP = 1; 100 | /** {@link #TYPE_DIMENSION} complex unit: Value is a scaled pixel. */ 101 | public static final int COMPLEX_UNIT_SP = 2; 102 | /** {@link #TYPE_DIMENSION} complex unit: Value is in points. */ 103 | public static final int COMPLEX_UNIT_PT = 3; 104 | /** {@link #TYPE_DIMENSION} complex unit: Value is in inches. */ 105 | public static final int COMPLEX_UNIT_IN = 4; 106 | /** {@link #TYPE_DIMENSION} complex unit: Value is in millimeters. */ 107 | public static final int COMPLEX_UNIT_MM = 5; 108 | 109 | /** {@link #TYPE_FRACTION} complex unit: A basic fraction of the overall 110 | * size. */ 111 | public static final int COMPLEX_UNIT_FRACTION = 0; 112 | /** {@link #TYPE_FRACTION} complex unit: A fraction of the parent size. */ 113 | public static final int COMPLEX_UNIT_FRACTION_PARENT = 1; 114 | 115 | /** Complex data: where the radix information is, telling where the decimal 116 | * place appears in the mantissa. */ 117 | public static final int COMPLEX_RADIX_SHIFT = 4; 118 | /** Complex data: mask to extract radix information (after shifting by 119 | * {@link #COMPLEX_RADIX_SHIFT}). This give us 4 possible fixed point 120 | * representations as defined below. */ 121 | public static final int COMPLEX_RADIX_MASK = 0x3; 122 | 123 | /** Complex data: the mantissa is an integral number -- i.e., 0xnnnnnn.0 */ 124 | public static final int COMPLEX_RADIX_23p0 = 0; 125 | /** Complex data: the mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn */ 126 | public static final int COMPLEX_RADIX_16p7 = 1; 127 | /** Complex data: the mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn */ 128 | public static final int COMPLEX_RADIX_8p15 = 2; 129 | /** Complex data: the mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn */ 130 | public static final int COMPLEX_RADIX_0p23 = 3; 131 | 132 | /** Complex data: bit location of mantissa information. */ 133 | public static final int COMPLEX_MANTISSA_SHIFT = 8; 134 | /** Complex data: mask to extract mantissa information (after shifting by 135 | * {@link #COMPLEX_MANTISSA_SHIFT}). This gives us 23 bits of precision; 136 | * the top bit is the sign. */ 137 | public static final int COMPLEX_MANTISSA_MASK = 0xffffff; 138 | 139 | /* ------------------------------------------------------------ */ 140 | 141 | /** 142 | * If {@link #density} is equal to this value, then the density should be 143 | * treated as the system's default density value: {@link DisplayMetrics#DENSITY_DEFAULT}. 144 | */ 145 | public static final int DENSITY_DEFAULT = 0; 146 | 147 | /** 148 | * If {@link #density} is equal to this value, then there is no density 149 | * associated with the resource and it should not be scaled. 150 | */ 151 | public static final int DENSITY_NONE = 0xffff; 152 | 153 | /* ------------------------------------------------------------ */ 154 | 155 | /** The type held by this value, as defined by the constants here. 156 | * This tells you how to interpret the other fields in the object. */ 157 | public int type; 158 | 159 | private static final float MANTISSA_MULT = 160 | 1.0f / (1<>TypedValue.COMPLEX_RADIX_SHIFT) 181 | & TypedValue.COMPLEX_RADIX_MASK]; 182 | } 183 | 184 | private static final String[] DIMENSION_UNIT_STRS = new String[] { 185 | "px", "dip", "sp", "pt", "in", "mm" 186 | }; 187 | private static final String[] FRACTION_UNIT_STRS = new String[] { 188 | "%", "%p" 189 | }; 190 | 191 | /** 192 | * Perform type conversion as per {@link #coerceToString()} on an 193 | * explicitly supplied type and data. 194 | * 195 | * @param type The data type identifier. 196 | * @param data The data value. 197 | * 198 | * @return String The coerced string value. If the value is 199 | * null or the type is not known, null is returned. 200 | */ 201 | public static final String coerceToString(int type, int data) 202 | { 203 | switch (type) { 204 | case TYPE_NULL: 205 | return null; 206 | case TYPE_REFERENCE: 207 | return "@" + data; 208 | case TYPE_ATTRIBUTE: 209 | return "?" + data; 210 | case TYPE_FLOAT: 211 | return Float.toString(Float.intBitsToFloat(data)); 212 | case TYPE_DIMENSION: 213 | return Float.toString(complexToFloat(data)) + DIMENSION_UNIT_STRS[ 214 | (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK]; 215 | case TYPE_FRACTION: 216 | return Float.toString(complexToFloat(data)*100) + FRACTION_UNIT_STRS[ 217 | (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK]; 218 | case TYPE_INT_HEX: 219 | return "0x" + Integer.toHexString(data); 220 | case TYPE_INT_BOOLEAN: 221 | return data != 0 ? "true" : "false"; 222 | } 223 | 224 | if (type >= TYPE_FIRST_COLOR_INT && type <= TYPE_LAST_COLOR_INT) { 225 | return "#" + Integer.toHexString(data); 226 | } else if (type >= TYPE_FIRST_INT && type <= TYPE_LAST_INT) { 227 | return Integer.toString(data); 228 | } 229 | 230 | return null; 231 | } 232 | 233 | }; 234 | 235 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/AndrolibException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib; 18 | 19 | import brut.common.BrutException; 20 | 21 | /** 22 | * @author Ryszard Wiśniewski 23 | */ 24 | public class AndrolibException extends BrutException { 25 | public AndrolibException() { 26 | } 27 | 28 | public AndrolibException(String message) { 29 | super(message); 30 | } 31 | 32 | public AndrolibException(String message, Throwable cause) { 33 | super(message, cause); 34 | } 35 | 36 | public AndrolibException(Throwable cause) { 37 | super(cause); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/ApkDecoder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib; 18 | 19 | import brut.androlib.err.InFileNotFoundException; 20 | import brut.androlib.err.OutDirExistsException; 21 | import brut.androlib.res.AndrolibResources; 22 | import brut.androlib.res.data.ResPackage; 23 | import brut.androlib.res.data.ResTable; 24 | import brut.androlib.res.util.ExtFile; 25 | import brut.common.BrutException; 26 | import brut.directory.DirectoryException; 27 | import brut.util.OS; 28 | import java.io.File; 29 | import java.util.*; 30 | 31 | /** 32 | * @author Ryszard Wiśniewski 33 | */ 34 | public class ApkDecoder { 35 | public ApkDecoder() { 36 | this(new Androlib()); 37 | } 38 | 39 | public ApkDecoder(Androlib androlib) { 40 | mAndrolib = androlib; 41 | } 42 | 43 | public ApkDecoder(File apkFile) { 44 | this(apkFile, new Androlib()); 45 | } 46 | 47 | public ApkDecoder(File apkFile, Androlib androlib) { 48 | mAndrolib = androlib; 49 | setApkFile(apkFile); 50 | } 51 | 52 | public void setApkFile(File apkFile) { 53 | mApkFile = new ExtFile(apkFile); 54 | mResTable = null; 55 | } 56 | 57 | public void setOutDir(File outDir) throws AndrolibException { 58 | mOutDir = outDir; 59 | } 60 | 61 | public void decode() throws AndrolibException { 62 | File outDir = getOutDir(); 63 | 64 | if (! mForceDelete && outDir.exists()) { 65 | throw new OutDirExistsException(); 66 | } 67 | 68 | if (! mApkFile.isFile() || ! mApkFile.canRead() ) { 69 | throw new InFileNotFoundException(); 70 | } 71 | 72 | try { 73 | OS.rmdir(outDir); 74 | } catch (BrutException ex) { 75 | throw new AndrolibException(ex); 76 | } 77 | outDir.mkdirs(); 78 | 79 | if (hasSources()) { 80 | switch (mDecodeSources) { 81 | case DECODE_SOURCES_NONE: 82 | mAndrolib.decodeSourcesRaw(mApkFile, outDir, mDebug); 83 | break; 84 | case DECODE_SOURCES_SMALI: 85 | mAndrolib.decodeSourcesSmali(mApkFile, outDir, mDebug); 86 | break; 87 | case DECODE_SOURCES_JAVA: 88 | mAndrolib.decodeSourcesJava(mApkFile, outDir, mDebug); 89 | break; 90 | } 91 | } 92 | if (hasResources()) { 93 | switch (mDecodeResources) { 94 | case DECODE_RESOURCES_NONE: 95 | mAndrolib.decodeResourcesRaw(mApkFile, outDir); 96 | break; 97 | case DECODE_RESOURCES_FULL: 98 | mAndrolib.decodeResourcesFull(mApkFile, outDir, 99 | getResTable()); 100 | break; 101 | } 102 | } 103 | mAndrolib.decodeRawFiles(mApkFile, outDir); 104 | writeMetaFile(); 105 | } 106 | 107 | public void setDecodeSources(short mode) throws AndrolibException { 108 | if (mode != DECODE_SOURCES_NONE && mode != DECODE_SOURCES_SMALI 109 | && mode != DECODE_SOURCES_JAVA) { 110 | throw new AndrolibException("Invalid decode sources mode: " + mode); 111 | } 112 | mDecodeSources = mode; 113 | } 114 | 115 | public void setDecodeResources(short mode) throws AndrolibException { 116 | if (mode != DECODE_RESOURCES_NONE && mode != DECODE_RESOURCES_FULL) { 117 | throw new AndrolibException("Invalid decode resources mode"); 118 | } 119 | mDecodeResources = mode; 120 | } 121 | 122 | public void setDebugMode(boolean debug) { 123 | mDebug = debug; 124 | } 125 | 126 | public void setForceDelete(boolean forceDelete) { 127 | mForceDelete = forceDelete; 128 | } 129 | 130 | public void setFrameworkTag(String tag) throws AndrolibException { 131 | mFrameTag = tag; 132 | if (mResTable != null) { 133 | getResTable().setFrameTag(tag); 134 | } 135 | } 136 | 137 | public void setKeepBrokenResources(boolean keepBrokenResources) { 138 | mKeepBrokenResources = keepBrokenResources; 139 | } 140 | 141 | public ResTable getResTable() throws AndrolibException { 142 | if (mResTable == null) { 143 | if (! hasResources()) { 144 | throw new AndrolibException( 145 | "Apk doesn't containt resources.arsc file"); 146 | } 147 | AndrolibResources.sKeepBroken = mKeepBrokenResources; 148 | mResTable = mAndrolib.getResTable(mApkFile); 149 | mResTable.setFrameTag(mFrameTag); 150 | } 151 | return mResTable; 152 | } 153 | 154 | public boolean hasSources() throws AndrolibException { 155 | try { 156 | return mApkFile.getDirectory().containsFile("classes.dex"); 157 | } catch (DirectoryException ex) { 158 | throw new AndrolibException(ex); 159 | } 160 | } 161 | 162 | public boolean hasResources() throws AndrolibException { 163 | try { 164 | return mApkFile.getDirectory().containsFile("resources.arsc"); 165 | } catch (DirectoryException ex) { 166 | throw new AndrolibException(ex); 167 | } 168 | } 169 | 170 | public final static short DECODE_SOURCES_NONE = 0x0000; 171 | public final static short DECODE_SOURCES_SMALI = 0x0001; 172 | public final static short DECODE_SOURCES_JAVA = 0x0002; 173 | 174 | public final static short DECODE_RESOURCES_NONE = 0x0100; 175 | public final static short DECODE_RESOURCES_FULL = 0x0101; 176 | 177 | 178 | private File getOutDir() throws AndrolibException { 179 | if (mOutDir == null) { 180 | throw new AndrolibException("Out dir not set"); 181 | } 182 | return mOutDir; 183 | } 184 | 185 | private void writeMetaFile() throws AndrolibException { 186 | Map meta = new LinkedHashMap(); 187 | meta.put("version", Androlib.getVersion()); 188 | meta.put("apkFileName", mApkFile.getName()); 189 | 190 | if (mDecodeResources != DECODE_RESOURCES_NONE && hasResources()) { 191 | meta.put("isFrameworkApk", 192 | Boolean.valueOf(mAndrolib.isFrameworkApk(getResTable()))); 193 | putUsesFramework(meta); 194 | } 195 | 196 | mAndrolib.writeMetaFile(mOutDir, meta); 197 | } 198 | 199 | private void putUsesFramework(Map meta) 200 | throws AndrolibException { 201 | Set pkgs = getResTable().listFramePackages(); 202 | if (pkgs.isEmpty()) { 203 | return; 204 | } 205 | 206 | Integer[] ids = new Integer[pkgs.size()]; 207 | int i = 0; 208 | for (ResPackage pkg : pkgs) { 209 | ids[i++] = pkg.getId(); 210 | } 211 | Arrays.sort(ids); 212 | 213 | Map uses = new LinkedHashMap(); 214 | uses.put("ids", ids); 215 | 216 | if (mFrameTag != null) { 217 | uses.put("tag", mFrameTag); 218 | } 219 | 220 | meta.put("usesFramework", uses); 221 | } 222 | 223 | private final Androlib mAndrolib; 224 | 225 | private ExtFile mApkFile; 226 | private File mOutDir; 227 | private ResTable mResTable; 228 | private short mDecodeSources = DECODE_SOURCES_SMALI; 229 | private short mDecodeResources = DECODE_RESOURCES_FULL; 230 | private boolean mDebug = false; 231 | private boolean mForceDelete = false; 232 | private String mFrameTag; 233 | private boolean mKeepBrokenResources = false; 234 | } 235 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/ApktoolProperties.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib; 18 | 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.util.Properties; 22 | import java.util.logging.Logger; 23 | 24 | /** 25 | * @author Ryszard Wiśniewski 26 | */ 27 | public class ApktoolProperties { 28 | public static String get(String key) { 29 | return get().getProperty(key); 30 | } 31 | 32 | public static Properties get() { 33 | if (sProps == null) { 34 | loadProps(); 35 | } 36 | return sProps; 37 | } 38 | 39 | private static void loadProps() { 40 | InputStream in = ApktoolProperties.class 41 | .getResourceAsStream("apktool.properties"); 42 | sProps = new Properties(); 43 | try { 44 | sProps.load(in); 45 | in.close(); 46 | } catch (IOException ex) { 47 | LOGGER.warning("Can't load properties."); 48 | } 49 | } 50 | 51 | private static Properties sProps; 52 | 53 | private static final Logger LOGGER = 54 | Logger.getLogger(ApktoolProperties.class.getName()); 55 | } 56 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/err/CantFind9PatchChunk.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.err; 18 | 19 | import brut.androlib.AndrolibException; 20 | 21 | /** 22 | * @author Ryszard Wiśniewski 23 | */ 24 | public class CantFind9PatchChunk extends AndrolibException { 25 | 26 | public CantFind9PatchChunk(Throwable cause) { 27 | super(cause); 28 | } 29 | 30 | public CantFind9PatchChunk(String message, Throwable cause) { 31 | super(message, cause); 32 | } 33 | 34 | public CantFind9PatchChunk(String message) { 35 | super(message); 36 | } 37 | 38 | public CantFind9PatchChunk() { 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/err/CantFindFrameworkResException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.err; 18 | 19 | import brut.androlib.AndrolibException; 20 | 21 | /** 22 | * @author Ryszard Wiśniewski 23 | */ 24 | public class CantFindFrameworkResException extends AndrolibException { 25 | 26 | public CantFindFrameworkResException(Throwable cause, int id) { 27 | super(cause); 28 | mPkgId = id; 29 | } 30 | 31 | public CantFindFrameworkResException(int id) { 32 | mPkgId = id; 33 | } 34 | 35 | public int getPkgId() { 36 | return mPkgId; 37 | } 38 | 39 | private final int mPkgId; 40 | } 41 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/err/InFileNotFoundException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.err; 18 | 19 | import brut.androlib.AndrolibException; 20 | 21 | /** 22 | * @author Ryszard Wiśniewski 23 | */ 24 | public class InFileNotFoundException extends AndrolibException { 25 | 26 | public InFileNotFoundException(Throwable cause) { 27 | super(cause); 28 | } 29 | 30 | public InFileNotFoundException(String message, Throwable cause) { 31 | super(message, cause); 32 | } 33 | 34 | public InFileNotFoundException(String message) { 35 | super(message); 36 | } 37 | 38 | public InFileNotFoundException() { 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/err/OutDirExistsException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.err; 18 | 19 | import brut.androlib.AndrolibException; 20 | 21 | /** 22 | * @author Ryszard Wiśniewski 23 | */ 24 | public class OutDirExistsException extends AndrolibException { 25 | 26 | public OutDirExistsException(Throwable cause) { 27 | super(cause); 28 | } 29 | 30 | public OutDirExistsException(String message, Throwable cause) { 31 | super(message, cause); 32 | } 33 | 34 | public OutDirExistsException(String message) { 35 | super(message); 36 | } 37 | 38 | public OutDirExistsException() { 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/err/UndefinedResObject.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.err; 18 | 19 | import brut.androlib.AndrolibException; 20 | 21 | /** 22 | * @author Ryszard Wiśniewski 23 | */ 24 | public class UndefinedResObject extends AndrolibException { 25 | public UndefinedResObject(Throwable cause) { 26 | super(cause); 27 | } 28 | 29 | public UndefinedResObject(String message, Throwable cause) { 30 | super(message, cause); 31 | } 32 | 33 | public UndefinedResObject(String message) { 34 | super(message); 35 | } 36 | 37 | public UndefinedResObject() { 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/java/AndrolibJava.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.java; 18 | 19 | import brut.androlib.res.util.ExtFile; 20 | import java.io.File; 21 | 22 | /** 23 | * @author Ryszard Wiśniewski 24 | */ 25 | public class AndrolibJava { 26 | 27 | public void decode(ExtFile apkFile, File outDir) { 28 | throw new UnsupportedOperationException("Not yet implemented"); 29 | } 30 | 31 | public void build(File javaDir, File dex) { 32 | throw new UnsupportedOperationException("Not yet implemented"); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/mod/IndentingWriter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.mod; 18 | 19 | import java.io.Writer; 20 | 21 | /** 22 | * @author Ryszard Wiśniewski 23 | */ 24 | public class IndentingWriter extends org.jf.util.IndentingWriter { 25 | 26 | public IndentingWriter(Writer writer) { 27 | super(writer); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/mod/SmaliMod.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.mod; 18 | 19 | import java.io.*; 20 | import org.antlr.runtime.*; 21 | import org.antlr.runtime.tree.CommonTree; 22 | import org.antlr.runtime.tree.CommonTreeNodeStream; 23 | import org.jf.dexlib.DexFile; 24 | import org.jf.smali.*; 25 | 26 | /** 27 | * @author Ryszard Wiśniewski 28 | */ 29 | public class SmaliMod { 30 | 31 | public static boolean assembleSmaliFile(InputStream smaliStream, 32 | String name, DexFile dexFile, boolean verboseErrors, 33 | boolean oldLexer, boolean printTokens) 34 | throws IOException, RecognitionException { 35 | CommonTokenStream tokens; 36 | 37 | 38 | boolean lexerErrors = false; 39 | LexerErrorInterface lexer; 40 | 41 | if (oldLexer) { 42 | ANTLRInputStream input = new ANTLRInputStream(smaliStream, "UTF-8"); 43 | input.name = name; 44 | 45 | lexer = new smaliLexer(input); 46 | tokens = new CommonTokenStream((TokenSource)lexer); 47 | } else { 48 | InputStreamReader reader = 49 | new InputStreamReader(smaliStream, "UTF-8"); 50 | 51 | lexer = new smaliFlexLexer(reader); 52 | tokens = new CommonTokenStream((TokenSource)lexer); 53 | } 54 | 55 | if (printTokens) { 56 | tokens.getTokens(); 57 | 58 | for (int i=0; i 0 || lexer.getNumberOfSyntaxErrors() > 0) { 74 | return false; 75 | } 76 | 77 | CommonTree t = (CommonTree) result.getTree(); 78 | 79 | CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t); 80 | treeStream.setTokenStream(tokens); 81 | 82 | smaliTreeWalker dexGen = new smaliTreeWalker(treeStream); 83 | 84 | dexGen.dexFile = dexFile; 85 | dexGen.smali_file(); 86 | 87 | if (dexGen.getNumberOfSyntaxErrors() > 0) { 88 | return false; 89 | } 90 | 91 | return true; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/ResSmaliUpdater.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res; 18 | 19 | import brut.androlib.AndrolibException; 20 | import brut.androlib.err.UndefinedResObject; 21 | import brut.androlib.res.data.ResResSpec; 22 | import brut.androlib.res.data.ResTable; 23 | import brut.directory.Directory; 24 | import brut.directory.DirectoryException; 25 | import brut.directory.FileDirectory; 26 | import java.io.File; 27 | import java.io.IOException; 28 | import java.io.PrintWriter; 29 | import java.util.Iterator; 30 | import java.util.logging.Logger; 31 | import java.util.regex.Matcher; 32 | import java.util.regex.Pattern; 33 | import org.apache.commons.io.IOUtils; 34 | 35 | /** 36 | * @author Ryszard Wiśniewski 37 | */ 38 | public class ResSmaliUpdater { 39 | public void tagResIDs(ResTable resTable, File smaliDir) 40 | throws AndrolibException { 41 | Directory dir = null; 42 | try { 43 | dir = new FileDirectory(smaliDir); 44 | } catch (DirectoryException ex) { 45 | throw new AndrolibException( 46 | "Could not tag res IDs", ex); 47 | } 48 | for (String fileName : dir.getFiles(true)) { 49 | try { 50 | tagResIdsForFile(resTable, dir, fileName); 51 | } catch (IOException ex) { 52 | throw new AndrolibException( 53 | "Could not tag resIDs for file: " + fileName, ex); 54 | } catch (DirectoryException ex) { 55 | throw new AndrolibException( 56 | "Could not tag resIDs for file: " + fileName, ex); 57 | } catch (AndrolibException ex) { 58 | throw new AndrolibException( 59 | "Could not tag resIDs for file: " + fileName, ex); 60 | } 61 | } 62 | } 63 | 64 | public void updateResIDs(ResTable resTable, File smaliDir) 65 | throws AndrolibException { 66 | try { 67 | Directory dir = new FileDirectory(smaliDir); 68 | for (String fileName : dir.getFiles(true)) { 69 | Iterator it = 70 | IOUtils.readLines(dir.getFileInput(fileName)).iterator(); 71 | PrintWriter out = new PrintWriter(dir.getFileOutput(fileName)); 72 | while (it.hasNext()) { 73 | String line = it.next(); 74 | out.println(line); 75 | Matcher m1 = RES_NAME_PATTERN.matcher(line); 76 | if (! m1.matches()) { 77 | continue; 78 | } 79 | Matcher m2 = RES_ID_PATTERN.matcher(it.next()); 80 | if (! m2.matches()) { 81 | throw new AndrolibException(); 82 | } 83 | int resID = resTable.getPackage(m1.group(1)) 84 | .getType(m1.group(2)).getResSpec(m1.group(3)) 85 | .getId().id; 86 | if (m2.group(1) != null) { 87 | out.println(String.format( 88 | RES_ID_FORMAT_FIELD, m2.group(1), resID)); 89 | } else { 90 | out.println(String.format( 91 | RES_ID_FORMAT_CONST, m2.group(2), resID)); 92 | } 93 | } 94 | out.close(); 95 | } 96 | } catch (IOException ex) { 97 | throw new AndrolibException( 98 | "Could not tag res IDs for: " + smaliDir.getAbsolutePath(), ex); 99 | } catch (DirectoryException ex) { 100 | throw new AndrolibException( 101 | "Could not tag res IDs for: " + smaliDir.getAbsolutePath(), ex); 102 | } 103 | } 104 | 105 | private void tagResIdsForFile(ResTable resTable, Directory dir, 106 | String fileName) throws IOException, DirectoryException, 107 | AndrolibException { 108 | Iterator it = 109 | IOUtils.readLines(dir.getFileInput(fileName)).iterator(); 110 | PrintWriter out = new PrintWriter(dir.getFileOutput(fileName)); 111 | while (it.hasNext()) { 112 | String line = it.next(); 113 | if (RES_NAME_PATTERN.matcher(line).matches()) { 114 | out.println(line); 115 | out.println(it.next()); 116 | continue; 117 | } 118 | Matcher m = RES_ID_PATTERN.matcher(line); 119 | if (m.matches()) { 120 | int resID = parseResID(m.group(3)); 121 | if (resID != -1) { 122 | try { 123 | ResResSpec spec = resTable.getResSpec(resID); 124 | out.println(String.format( 125 | RES_NAME_FORMAT, spec.getFullName())); 126 | } catch (UndefinedResObject ex) { 127 | if (! R_FILE_PATTERN.matcher(fileName).matches()) { 128 | LOGGER.warning(String.format( 129 | "Undefined resource spec in %s: 0x%08x" 130 | , fileName, resID)); 131 | } 132 | } 133 | } 134 | } 135 | out.println(line); 136 | } 137 | out.close(); 138 | } 139 | 140 | private int parseResID(String resIDHex) { 141 | if (resIDHex.endsWith("ff")) { 142 | return -1; 143 | } 144 | int resID = Integer.valueOf(resIDHex, 16); 145 | if (resIDHex.length() == 4) { 146 | resID = resID << 16; 147 | } 148 | return resID; 149 | } 150 | 151 | private final static String RES_ID_FORMAT_FIELD = 152 | ".field %s:I = 0x%08x"; 153 | private final static String RES_ID_FORMAT_CONST = 154 | " const %s, 0x%08x"; 155 | private final static Pattern RES_ID_PATTERN = Pattern.compile( 156 | "^(?:\\.field (.+?):I =| const(?:|/(?:|high)16) ([pv]\\d+?),) 0x(7[a-f]0[1-9a-f](?:|[0-9a-f]{4}))$"); 157 | private final static String RES_NAME_FORMAT = 158 | "# APKTOOL/RES_NAME: %s"; 159 | private final static Pattern RES_NAME_PATTERN = Pattern.compile( 160 | "^# APKTOOL/RES_NAME: ([a-zA-Z0-9.]+):([a-z]+)/([a-zA-Z0-9._]+)$"); 161 | 162 | private final static Pattern R_FILE_PATTERN = Pattern.compile( 163 | ".*R\\$[a-z]+\\.smali$"); 164 | 165 | private final static Logger LOGGER = 166 | Logger.getLogger(ResSmaliUpdater.class.getName()); 167 | } 168 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/ResConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data; 18 | 19 | import brut.androlib.AndrolibException; 20 | import brut.androlib.err.UndefinedResObject; 21 | import java.util.*; 22 | 23 | /** 24 | * @author Ryszard Wiśniewski 25 | */ 26 | public class ResConfig { 27 | private final ResConfigFlags mFlags; 28 | private final Map mResources = 29 | new LinkedHashMap(); 30 | 31 | public ResConfig(ResConfigFlags flags) { 32 | this.mFlags = flags; 33 | } 34 | 35 | public Set listResources() { 36 | return new LinkedHashSet(mResources.values()); 37 | } 38 | 39 | public ResResource getResource(ResResSpec spec) throws AndrolibException { 40 | ResResource res = mResources.get(spec); 41 | if (res == null) { 42 | throw new UndefinedResObject(String.format( 43 | "resource: spec=%s, config=%s", spec, this)); 44 | } 45 | return res; 46 | } 47 | 48 | public Set listResSpecs() { 49 | return mResources.keySet(); 50 | } 51 | 52 | public ResConfigFlags getFlags() { 53 | return mFlags; 54 | } 55 | 56 | public void addResource(ResResource res) 57 | throws AndrolibException { 58 | addResource(res, false); 59 | } 60 | 61 | public void addResource(ResResource res, boolean overwrite) 62 | throws AndrolibException { 63 | ResResSpec spec = res.getResSpec(); 64 | if (mResources.put(spec, res) != null && ! overwrite) { 65 | throw new AndrolibException(String.format( 66 | "Multiple resources: spec=%s, config=%s", spec, this)); 67 | } 68 | } 69 | 70 | @Override 71 | public String toString() { 72 | return mFlags.toString(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/ResID.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data; 18 | 19 | /** 20 | * @author Ryszard Wiśniewski 21 | */ 22 | public class ResID { 23 | public final int package_; 24 | public final int type; 25 | public final int entry; 26 | 27 | public final int id; 28 | 29 | public ResID(int package_, int type, int entry) { 30 | this(package_, type, entry, (package_ << 24) + (type << 16) + entry); 31 | } 32 | 33 | public ResID(int id) { 34 | this(id >> 24, (id >> 16) & 0x000000ff, id & 0x0000ffff, id); 35 | } 36 | 37 | public ResID(int package_, int type, int entry, int id) { 38 | this.package_ = package_; 39 | this.type = type; 40 | this.entry = entry; 41 | this.id = id; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return String.format("0x%08x", id); 47 | } 48 | 49 | @Override 50 | public int hashCode() { 51 | int hash = 7; 52 | hash = 59 * hash + this.id; 53 | return hash; 54 | } 55 | 56 | @Override 57 | public boolean equals(Object obj) { 58 | if (obj == null) { 59 | return false; 60 | } 61 | if (getClass() != obj.getClass()) { 62 | return false; 63 | } 64 | final ResID other = (ResID) obj; 65 | if (this.id != other.id) { 66 | return false; 67 | } 68 | return true; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/ResPackage.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data; 18 | 19 | import brut.androlib.err.UndefinedResObject; 20 | import brut.androlib.AndrolibException; 21 | import brut.androlib.res.data.value.ResFileValue; 22 | import brut.androlib.res.data.value.ResValueFactory; 23 | import brut.androlib.res.xml.ResValuesXmlSerializable; 24 | import brut.util.Duo; 25 | import java.util.*; 26 | 27 | /** 28 | * @author Ryszard Wiśniewski 29 | */ 30 | public class ResPackage { 31 | private final ResTable mResTable; 32 | private final int mId; 33 | private final String mName; 34 | private final Map mResSpecs = 35 | new LinkedHashMap(); 36 | private final Map mConfigs = 37 | new LinkedHashMap(); 38 | private final Map mTypes = 39 | new LinkedHashMap(); 40 | private final Set mSynthesizedRes = new HashSet(); 41 | 42 | private ResValueFactory mValueFactory; 43 | 44 | public ResPackage(ResTable resTable, int id, String name) { 45 | this.mResTable = resTable; 46 | this.mId = id; 47 | this.mName = name; 48 | } 49 | 50 | public List listResSpecs() { 51 | return new ArrayList(mResSpecs.values()); 52 | } 53 | 54 | public boolean hasResSpec(ResID resID) { 55 | return mResSpecs.containsKey(resID); 56 | } 57 | 58 | public ResResSpec getResSpec(ResID resID) throws UndefinedResObject { 59 | ResResSpec spec = mResSpecs.get(resID); 60 | if (spec == null) { 61 | throw new UndefinedResObject("resource spec: " + resID.toString()); 62 | } 63 | return spec; 64 | } 65 | 66 | public List getConfigs() { 67 | return new ArrayList(mConfigs.values()); 68 | } 69 | 70 | public boolean hasConfig(ResConfigFlags flags) { 71 | return mConfigs.containsKey(flags); 72 | } 73 | 74 | public ResConfig getConfig(ResConfigFlags flags) throws AndrolibException { 75 | ResConfig config = mConfigs.get(flags); 76 | if (config == null) { 77 | throw new UndefinedResObject("config: " + flags); 78 | } 79 | return config; 80 | } 81 | 82 | public ResConfig getOrCreateConfig(ResConfigFlags flags) 83 | throws AndrolibException { 84 | ResConfig config = mConfigs.get(flags); 85 | if (config == null) { 86 | config = new ResConfig(flags); 87 | mConfigs.put(flags, config); 88 | } 89 | return config; 90 | } 91 | 92 | public List listTypes() { 93 | return new ArrayList(mTypes.values()); 94 | } 95 | 96 | public boolean hasType(String typeName) { 97 | return mTypes.containsKey(typeName); 98 | } 99 | 100 | public ResType getType(String typeName) throws AndrolibException { 101 | ResType type = mTypes.get(typeName); 102 | if (type == null) { 103 | throw new UndefinedResObject("type: " + typeName); 104 | } 105 | return type; 106 | } 107 | 108 | public Set listFiles() { 109 | Set ret = new HashSet(); 110 | for (ResResSpec spec : mResSpecs.values()) { 111 | for (ResResource res : spec.listResources()) { 112 | if (res.getValue() instanceof ResFileValue) { 113 | ret.add(res); 114 | } 115 | } 116 | } 117 | return ret; 118 | } 119 | 120 | public Collection listValuesFiles() { 121 | Map, ResValuesFile> ret = 122 | new HashMap, ResValuesFile>(); 123 | for (ResResSpec spec : mResSpecs.values()) { 124 | for (ResResource res : spec.listResources()) { 125 | if (res.getValue() instanceof ResValuesXmlSerializable) { 126 | ResType type = res.getResSpec().getType(); 127 | ResConfig config = res.getConfig(); 128 | Duo key = 129 | new Duo(type, config); 130 | ResValuesFile values = ret.get(key); 131 | if (values == null) { 132 | values = new ResValuesFile(this, type, config); 133 | ret.put(key, values); 134 | } 135 | values.addResource(res); 136 | } 137 | } 138 | } 139 | return ret.values(); 140 | } 141 | 142 | public ResTable getResTable() { 143 | return mResTable; 144 | } 145 | 146 | public int getId() { 147 | return mId; 148 | } 149 | 150 | public String getName() { 151 | return mName; 152 | } 153 | 154 | boolean isSynthesized(ResID resId) { 155 | return mSynthesizedRes.contains(resId); 156 | } 157 | 158 | public void addResSpec(ResResSpec spec) throws AndrolibException { 159 | if (mResSpecs.put(spec.getId(), spec) != null) { 160 | throw new AndrolibException("Multiple resource specs: " + spec); 161 | } 162 | } 163 | 164 | public void addConfig(ResConfig config) throws AndrolibException { 165 | if (mConfigs.put(config.getFlags(), config) != null) { 166 | throw new AndrolibException("Multiple configs: " + config); 167 | } 168 | } 169 | 170 | public void addType(ResType type) throws AndrolibException { 171 | if (mTypes.put(type.getName(), type) != null) { 172 | throw new AndrolibException("Multiple types: " + type); 173 | } 174 | } 175 | 176 | public void addResource(ResResource res) { 177 | } 178 | 179 | public void addSynthesizedRes(int resId) { 180 | mSynthesizedRes.add(new ResID(resId)); 181 | } 182 | 183 | @Override 184 | public String toString() { 185 | return mName; 186 | } 187 | 188 | @Override 189 | public boolean equals(Object obj) { 190 | if (obj == null) { 191 | return false; 192 | } 193 | if (getClass() != obj.getClass()) { 194 | return false; 195 | } 196 | final ResPackage other = (ResPackage) obj; 197 | if (this.mResTable != other.mResTable && (this.mResTable == null || !this.mResTable.equals(other.mResTable))) { 198 | return false; 199 | } 200 | if (this.mId != other.mId) { 201 | return false; 202 | } 203 | return true; 204 | } 205 | 206 | @Override 207 | public int hashCode() { 208 | int hash = 7; 209 | hash = 37 * hash + (this.mResTable != null ? this.mResTable.hashCode() : 0); 210 | hash = 37 * hash + this.mId; 211 | return hash; 212 | } 213 | 214 | public ResValueFactory getValueFactory() { 215 | if (mValueFactory == null) { 216 | mValueFactory = new ResValueFactory(this); 217 | } 218 | return mValueFactory; 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/ResResSpec.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data; 18 | 19 | import brut.androlib.AndrolibException; 20 | import brut.androlib.err.UndefinedResObject; 21 | import java.util.*; 22 | 23 | /** 24 | * @author Ryszard Wiśniewski 25 | */ 26 | public class ResResSpec { 27 | private final ResID mId; 28 | private final String mName; 29 | private final ResPackage mPackage; 30 | private final ResType mType; 31 | private final Map mResources = 32 | new LinkedHashMap(); 33 | 34 | public ResResSpec(ResID id, String name, ResPackage pkg, ResType type) { 35 | this.mId = id; 36 | this.mName = name; 37 | this.mPackage = pkg; 38 | this.mType = type; 39 | } 40 | 41 | public Set listResources() { 42 | return new LinkedHashSet(mResources.values()); 43 | } 44 | 45 | public ResResource getResource(ResConfig config) throws AndrolibException { 46 | return getResource(config.getFlags()); 47 | } 48 | 49 | public ResResource getResource(ResConfigFlags config) 50 | throws AndrolibException { 51 | ResResource res = mResources.get(config); 52 | if (res == null) { 53 | throw new UndefinedResObject(String.format( 54 | "resource: spec=%s, config=%s", this, config)); 55 | } 56 | return res; 57 | } 58 | 59 | public boolean hasResource(ResConfig config) { 60 | return hasResource(config.getFlags()); 61 | } 62 | 63 | private boolean hasResource(ResConfigFlags flags) { 64 | return mResources.containsKey(flags); 65 | } 66 | 67 | public ResResource getDefaultResource() throws AndrolibException { 68 | return getResource(new ResConfigFlags()); 69 | } 70 | 71 | public boolean hasDefaultResource() { 72 | return mResources.containsKey(new ResConfigFlags()); 73 | } 74 | 75 | public String getFullName() { 76 | return getFullName(false, false); 77 | } 78 | 79 | public String getFullName(ResPackage relativeToPackage, 80 | boolean excludeType) { 81 | return getFullName( 82 | getPackage().equals(relativeToPackage), excludeType); 83 | } 84 | 85 | public String getFullName(boolean excludePackage, boolean excludeType) { 86 | return 87 | (excludePackage ? "" : getPackage().getName() + ":") + 88 | (excludeType ? "" : getType().getName() + "/") + getName(); 89 | } 90 | 91 | public ResID getId() { 92 | return mId; 93 | } 94 | 95 | public String getName() { 96 | return mName; 97 | } 98 | 99 | public ResPackage getPackage() { 100 | return mPackage; 101 | } 102 | 103 | public ResType getType() { 104 | return mType; 105 | } 106 | 107 | public void addResource(ResResource res) 108 | throws AndrolibException { 109 | addResource(res, false); 110 | } 111 | 112 | public void addResource(ResResource res, boolean overwrite) 113 | throws AndrolibException { 114 | ResConfigFlags flags = res.getConfig().getFlags(); 115 | if (mResources.put(flags, res) != null && ! overwrite) { 116 | throw new AndrolibException(String.format( 117 | "Multiple resources: spec=%s, config=%s", this, flags)); 118 | } 119 | } 120 | 121 | @Override 122 | public String toString() { 123 | return mId.toString() + " " + mType.toString() + "/" + mName; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/ResResource.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data; 18 | 19 | import brut.androlib.AndrolibException; 20 | import brut.androlib.res.data.value.ResValue; 21 | 22 | /** 23 | * @author Ryszard Wiśniewski 24 | */ 25 | public class ResResource { 26 | private final ResConfig mConfig; 27 | private final ResResSpec mResSpec; 28 | private final ResValue mValue; 29 | 30 | public ResResource(ResConfig config, ResResSpec spec, 31 | ResValue value) { 32 | this.mConfig = config; 33 | this.mResSpec = spec; 34 | this.mValue = value; 35 | } 36 | 37 | public String getFilePath() { 38 | return mResSpec.getType().getName() + 39 | mConfig.getFlags().getQualifiers() + "/" + mResSpec.getName(); 40 | } 41 | 42 | public ResConfig getConfig() { 43 | return mConfig; 44 | } 45 | 46 | public ResResSpec getResSpec() { 47 | return mResSpec; 48 | } 49 | 50 | public ResValue getValue() { 51 | return mValue; 52 | } 53 | 54 | public void replace(ResValue value) throws AndrolibException { 55 | ResResource res = new ResResource(mConfig, mResSpec, value); 56 | mConfig.addResource(res, true); 57 | mResSpec.addResource(res, true); 58 | } 59 | 60 | @Override 61 | public String toString() { 62 | return getFilePath(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data; 18 | 19 | import brut.androlib.AndrolibException; 20 | import brut.androlib.err.UndefinedResObject; 21 | import brut.androlib.res.AndrolibResources; 22 | import brut.androlib.res.data.value.ResValue; 23 | import java.util.*; 24 | 25 | /** 26 | * @author Ryszard Wiśniewski 27 | */ 28 | public class ResTable { 29 | private final AndrolibResources mAndRes; 30 | 31 | private final Map mPackagesById = 32 | new HashMap(); 33 | private final Map mPackagesByName = 34 | new HashMap(); 35 | private final Set mMainPackages = 36 | new LinkedHashSet(); 37 | private final Set mFramePackages = 38 | new LinkedHashSet(); 39 | 40 | private String mFrameTag; 41 | 42 | public ResTable() { 43 | mAndRes = null; 44 | } 45 | 46 | public ResTable(AndrolibResources andRes) { 47 | mAndRes = andRes; 48 | } 49 | 50 | public ResResSpec getResSpec(int resID) throws AndrolibException { 51 | return getResSpec(new ResID(resID)); 52 | } 53 | 54 | public ResResSpec getResSpec(ResID resID) throws AndrolibException { 55 | return getPackage(resID.package_).getResSpec(resID); 56 | } 57 | 58 | public Set listMainPackages() { 59 | return mMainPackages; 60 | } 61 | 62 | public Set listFramePackages() { 63 | return mFramePackages; 64 | } 65 | 66 | public ResPackage getPackage(int id) throws AndrolibException { 67 | ResPackage pkg = mPackagesById.get(id); 68 | if (pkg != null) { 69 | return pkg; 70 | } 71 | if (mAndRes != null) { 72 | return mAndRes.loadFrameworkPkg(this, id, mFrameTag); 73 | } 74 | throw new UndefinedResObject(String.format("package: id=%d", id)); 75 | } 76 | 77 | public ResPackage getPackage(String name) throws AndrolibException { 78 | ResPackage pkg = mPackagesByName.get(name); 79 | if (pkg == null) { 80 | throw new UndefinedResObject("package: name=" + name); 81 | } 82 | return pkg; 83 | } 84 | 85 | public boolean hasPackage(int id) { 86 | return mPackagesById.containsKey(id); 87 | } 88 | 89 | public boolean hasPackage(String name) { 90 | return mPackagesByName.containsKey(name); 91 | } 92 | 93 | public ResValue getValue(String package_, String type, String name) 94 | throws AndrolibException { 95 | return getPackage(package_).getType(type).getResSpec(name) 96 | .getDefaultResource().getValue(); 97 | } 98 | 99 | public void addPackage(ResPackage pkg, boolean main) 100 | throws AndrolibException { 101 | Integer id = pkg.getId(); 102 | if (mPackagesById.containsKey(id)) { 103 | throw new AndrolibException( 104 | "Multiple packages: id=" + id.toString()); 105 | } 106 | String name = pkg.getName(); 107 | if (mPackagesByName.containsKey(name)) { 108 | throw new AndrolibException("Multiple packages: name=" + name); 109 | } 110 | 111 | mPackagesById.put(id, pkg); 112 | mPackagesByName.put(name, pkg); 113 | if (main) { 114 | mMainPackages.add(pkg); 115 | } else { 116 | mFramePackages.add(pkg); 117 | } 118 | } 119 | 120 | public void setFrameTag(String tag) { 121 | mFrameTag = tag; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/ResType.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data; 18 | 19 | import brut.androlib.AndrolibException; 20 | import brut.androlib.err.UndefinedResObject; 21 | import java.util.*; 22 | 23 | /** 24 | * @author Ryszard Wiśniewski 25 | */ 26 | public final class ResType { 27 | private final String mName; 28 | private final Map mResSpecs = 29 | new LinkedHashMap(); 30 | 31 | private final ResTable mResTable; 32 | private final ResPackage mPackage; 33 | 34 | public ResType(String name, ResTable resTable, 35 | ResPackage package_) { 36 | this.mName = name; 37 | this.mResTable = resTable; 38 | this.mPackage = package_; 39 | } 40 | 41 | public String getName() { 42 | return mName; 43 | } 44 | 45 | public Set listResSpecs() { 46 | return new LinkedHashSet(mResSpecs.values()); 47 | } 48 | 49 | public ResResSpec getResSpec(String name) throws AndrolibException { 50 | ResResSpec spec = mResSpecs.get(name); 51 | if (spec == null) { 52 | throw new UndefinedResObject(String.format( 53 | "resource spec: %s/%s", getName(), name)); 54 | } 55 | return spec; 56 | } 57 | 58 | public void addResSpec(ResResSpec spec) 59 | throws AndrolibException { 60 | if (mResSpecs.put(spec.getName(), spec) != null) { 61 | throw new AndrolibException(String.format( 62 | "Multiple res specs: %s/%s", getName(), spec.getName())); 63 | } 64 | } 65 | 66 | @Override 67 | public String toString() { 68 | return mName; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/ResValuesFile.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data; 18 | 19 | import java.util.LinkedHashSet; 20 | import java.util.Set; 21 | 22 | /** 23 | * @author Ryszard Wiśniewski 24 | */ 25 | public class ResValuesFile { 26 | private final ResPackage mPackage; 27 | private final ResType mType; 28 | private final ResConfig mConfig; 29 | private final Set mResources = 30 | new LinkedHashSet(); 31 | 32 | public ResValuesFile(ResPackage pkg, ResType type, ResConfig config) { 33 | this.mPackage = pkg; 34 | this.mType = type; 35 | this.mConfig = config; 36 | } 37 | 38 | public String getPath() { 39 | return "values" + mConfig.getFlags().getQualifiers() 40 | + "/" + mType.getName() 41 | + (mType.getName().endsWith("s") ? "" : "s") 42 | + ".xml"; 43 | } 44 | 45 | public Set listResources() { 46 | return mResources; 47 | } 48 | 49 | public ResType getType() { 50 | return mType; 51 | } 52 | 53 | public ResConfig getConfig() { 54 | return mConfig; 55 | } 56 | 57 | public boolean isSynthesized(ResResource res) { 58 | return mPackage.isSynthesized(res.getResSpec().getId()); 59 | } 60 | 61 | public void addResource(ResResource res) { 62 | mResources.add(res); 63 | } 64 | 65 | @Override 66 | public boolean equals(Object obj) { 67 | if (obj == null) { 68 | return false; 69 | } 70 | if (getClass() != obj.getClass()) { 71 | return false; 72 | } 73 | final ResValuesFile other = (ResValuesFile) obj; 74 | if (this.mType != other.mType && (this.mType == null || !this.mType.equals(other.mType))) { 75 | return false; 76 | } 77 | if (this.mConfig != other.mConfig && (this.mConfig == null || !this.mConfig.equals(other.mConfig))) { 78 | return false; 79 | } 80 | return true; 81 | } 82 | 83 | @Override 84 | public int hashCode() { 85 | int hash = 5; 86 | hash = 37 * hash + (this.mType != null ? this.mType.hashCode() : 0); 87 | hash = 37 * hash + (this.mConfig != null ? this.mConfig.hashCode() : 0); 88 | return hash; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data.value; 18 | 19 | import brut.androlib.res.xml.ResValuesXmlSerializable; 20 | import brut.androlib.AndrolibException; 21 | import brut.androlib.res.data.ResResource; 22 | import brut.util.Duo; 23 | import java.io.IOException; 24 | import org.xmlpull.v1.XmlSerializer; 25 | 26 | /** 27 | * @author Ryszard Wiśniewski 28 | */ 29 | public class ResArrayValue extends ResBagValue implements ResValuesXmlSerializable { 30 | ResArrayValue(ResReferenceValue parent, 31 | Duo[] items) { 32 | super(parent); 33 | 34 | mItems = new ResScalarValue[items.length]; 35 | for (int i = 0; i < items.length; i++) { 36 | mItems[i] = items[i].m2; 37 | } 38 | } 39 | 40 | public ResArrayValue(ResReferenceValue parent, ResScalarValue[] items) { 41 | super(parent); 42 | mItems = items; 43 | } 44 | 45 | @Override 46 | public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) 47 | throws IOException, AndrolibException { 48 | String type = getType(); 49 | type = (type == null ? "" : type + "-") + "array"; 50 | 51 | serializer.startTag(null, type); 52 | serializer.attribute(null, "name", res.getResSpec().getName()); 53 | for (int i = 0; i < mItems.length; i++) { 54 | serializer.startTag(null, "item"); 55 | serializer.text(mItems[i].encodeAsResXmlItemValue()); 56 | serializer.endTag(null, "item"); 57 | } 58 | serializer.endTag(null, type); 59 | } 60 | 61 | public String getType() { 62 | if (mItems.length == 0) { 63 | return null; 64 | } 65 | String type = mItems[0].getType(); 66 | if (! "string".equals(type) && ! "integer".equals(type)) { 67 | return null; 68 | } 69 | for (int i = 1; i < mItems.length; i++) { 70 | if (! type.equals(mItems[i].getType())) { 71 | return null; 72 | } 73 | } 74 | return type; 75 | } 76 | 77 | private final ResScalarValue[] mItems; 78 | 79 | 80 | public static final int BAG_KEY_ARRAY_START = 0x02000000; 81 | } 82 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/value/ResAttr.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data.value; 18 | 19 | import brut.androlib.res.xml.ResValuesXmlSerializable; 20 | import brut.androlib.AndrolibException; 21 | import brut.androlib.res.data.ResPackage; 22 | import brut.androlib.res.data.ResResource; 23 | import brut.util.Duo; 24 | import java.io.IOException; 25 | import org.xmlpull.v1.XmlSerializer; 26 | 27 | /** 28 | * @author Ryszard Wiśniewski 29 | */ 30 | public class ResAttr extends ResBagValue implements ResValuesXmlSerializable { 31 | ResAttr(ResReferenceValue parentVal, int type, Integer min, Integer max, 32 | Boolean l10n) { 33 | super(parentVal); 34 | mType = type; 35 | mMin = min; 36 | mMax = max; 37 | mL10n = l10n; 38 | } 39 | 40 | public String convertToResXmlFormat(ResScalarValue value) 41 | throws AndrolibException { 42 | return null; 43 | } 44 | 45 | @Override 46 | public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) 47 | throws IOException, AndrolibException { 48 | String type = getTypeAsString(); 49 | 50 | serializer.startTag(null, "attr"); 51 | serializer.attribute(null, "name", res.getResSpec().getName()); 52 | if (type != null) { 53 | serializer.attribute(null, "format", type); 54 | } 55 | if (mMin != null) { 56 | serializer.attribute(null, "min", mMin.toString()); 57 | } 58 | if (mMax != null) { 59 | serializer.attribute(null, "max", mMax.toString()); 60 | } 61 | if (mL10n != null && mL10n) { 62 | serializer.attribute(null, "localization", "suggested"); 63 | } 64 | serializeBody(serializer, res); 65 | serializer.endTag(null, "attr"); 66 | } 67 | 68 | 69 | public static ResAttr factory(ResReferenceValue parent, 70 | Duo[] items, ResValueFactory factory, 71 | ResPackage pkg) throws AndrolibException { 72 | 73 | int type = ((ResIntValue) items[0].m2).getValue(); 74 | int scalarType = type & 0xffff; 75 | Integer min = null, max = null; 76 | Boolean l10n = null; 77 | int i; 78 | for (i = 1; i < items.length; i++) { 79 | switch (items[i].m1) { 80 | case BAG_KEY_ATTR_MIN: 81 | min = ((ResIntValue) items[i].m2).getValue(); 82 | continue; 83 | case BAG_KEY_ATTR_MAX: 84 | max = ((ResIntValue) items[i].m2).getValue(); 85 | continue; 86 | case BAG_KEY_ATTR_L10N: 87 | l10n = ((ResIntValue) items[i].m2).getValue() != 0; 88 | continue; 89 | } 90 | break; 91 | } 92 | 93 | if (i == items.length) { 94 | return new ResAttr(parent, scalarType, min, max, l10n); 95 | } 96 | Duo[] attrItems = 97 | new Duo[items.length - i]; 98 | int j = 0; 99 | for (; i < items.length; i++) { 100 | int resId = items[i].m1; 101 | pkg.addSynthesizedRes(resId); 102 | attrItems[j++] = new Duo( 103 | factory.newReference(resId, null), (ResIntValue) items[i].m2); 104 | } 105 | switch (type & 0xff0000) { 106 | case TYPE_ENUM: 107 | return new ResEnumAttr( 108 | parent, scalarType, min, max, l10n, attrItems); 109 | case TYPE_FLAGS: 110 | return new ResFlagsAttr( 111 | parent, scalarType, min, max, l10n, attrItems); 112 | } 113 | 114 | throw new AndrolibException("Could not decode attr value"); 115 | } 116 | 117 | protected void serializeBody(XmlSerializer serializer, ResResource res) 118 | throws AndrolibException, IOException {} 119 | 120 | protected String getTypeAsString() { 121 | String s = ""; 122 | if ((mType & TYPE_REFERENCE) != 0) { 123 | s += "|reference"; 124 | } 125 | if ((mType & TYPE_STRING) != 0) { 126 | s += "|string"; 127 | } 128 | if ((mType & TYPE_INT) != 0) { 129 | s += "|integer"; 130 | } 131 | if ((mType & TYPE_BOOL) != 0) { 132 | s += "|boolean"; 133 | } 134 | if ((mType & TYPE_COLOR) != 0) { 135 | s += "|color"; 136 | } 137 | if ((mType & TYPE_FLOAT) != 0) { 138 | s += "|float"; 139 | } 140 | if ((mType & TYPE_DIMEN) != 0) { 141 | s += "|dimension"; 142 | } 143 | if ((mType & TYPE_FRACTION) != 0) { 144 | s += "|fraction"; 145 | } 146 | if (s.isEmpty()) { 147 | return null; 148 | } 149 | return s.substring(1); 150 | } 151 | 152 | private final int mType; 153 | private final Integer mMin; 154 | private final Integer mMax; 155 | private final Boolean mL10n; 156 | 157 | 158 | public static final int BAG_KEY_ATTR_TYPE = 0x01000000; 159 | private static final int BAG_KEY_ATTR_MIN = 0x01000001; 160 | private static final int BAG_KEY_ATTR_MAX = 0x01000002; 161 | private static final int BAG_KEY_ATTR_L10N = 0x01000003; 162 | 163 | private final static int TYPE_REFERENCE = 0x01; 164 | private final static int TYPE_STRING = 0x02; 165 | private final static int TYPE_INT = 0x04; 166 | private final static int TYPE_BOOL = 0x08; 167 | private final static int TYPE_COLOR = 0x10; 168 | private final static int TYPE_FLOAT = 0x20; 169 | private final static int TYPE_DIMEN = 0x40; 170 | private final static int TYPE_FRACTION = 0x80; 171 | private final static int TYPE_ANY_STRING = 0xee; 172 | 173 | private static final int TYPE_ENUM = 0x00010000; 174 | private static final int TYPE_FLAGS = 0x00020000; 175 | } 176 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/value/ResBagValue.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data.value; 18 | 19 | import brut.androlib.res.xml.ResValuesXmlSerializable; 20 | import brut.androlib.AndrolibException; 21 | import brut.androlib.res.data.ResResource; 22 | import brut.util.Duo; 23 | import java.io.IOException; 24 | import org.xmlpull.v1.XmlSerializer; 25 | 26 | /** 27 | * @author Ryszard Wiśniewski 28 | */ 29 | public class ResBagValue extends ResValue implements ResValuesXmlSerializable { 30 | protected final ResReferenceValue mParent; 31 | 32 | public ResBagValue(ResReferenceValue parent) { 33 | this.mParent = parent; 34 | } 35 | 36 | public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) 37 | throws IOException, AndrolibException { 38 | String type = res.getResSpec().getType().getName(); 39 | if ("style".equals(type)) { 40 | new ResStyleValue(mParent, new Duo[0], null) 41 | .serializeToResValuesXml(serializer, res); 42 | return; 43 | } 44 | if ("array".equals(type)) { 45 | new ResArrayValue(mParent, new Duo[0]) 46 | .serializeToResValuesXml(serializer, res); 47 | return; 48 | } 49 | if ("plurals".equals(type)) { 50 | new ResPluralsValue(mParent, new Duo[0]) 51 | .serializeToResValuesXml(serializer, res); 52 | return; 53 | } 54 | 55 | serializer.startTag(null, "item"); 56 | serializer.attribute(null, "type", type); 57 | serializer.attribute(null, "name", res.getResSpec().getName()); 58 | serializer.endTag(null, "item"); 59 | } 60 | 61 | public ResReferenceValue getParent() { 62 | return mParent; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/value/ResBoolValue.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data.value; 18 | 19 | /** 20 | * @author Ryszard Wiśniewski 21 | */ 22 | public class ResBoolValue extends ResScalarValue { 23 | private final boolean mValue; 24 | 25 | public ResBoolValue(boolean value, String rawValue) { 26 | super("bool", rawValue); 27 | this.mValue = value; 28 | } 29 | 30 | public boolean getValue() { 31 | return mValue; 32 | } 33 | 34 | protected String encodeAsResXml() { 35 | return mValue ? "true" : "false"; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/value/ResColorValue.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data.value; 18 | 19 | /** 20 | * @author Ryszard Wiśniewski 21 | */ 22 | public class ResColorValue extends ResIntValue { 23 | public ResColorValue(int value, String rawValue) { 24 | super(value, rawValue, "color"); 25 | } 26 | 27 | @Override 28 | protected String encodeAsResXml() { 29 | return String.format("#%08x", mValue); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/value/ResDimenValue.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data.value; 18 | 19 | import android.util.TypedValue; 20 | import brut.androlib.AndrolibException; 21 | 22 | /** 23 | * @author Ryszard Wiśniewski 24 | */ 25 | public class ResDimenValue extends ResIntValue { 26 | public ResDimenValue(int value, String rawValue) { 27 | super(value, rawValue, "dimen"); 28 | } 29 | 30 | @Override 31 | protected String encodeAsResXml() throws AndrolibException { 32 | return TypedValue.coerceToString(TypedValue.TYPE_DIMENSION, mValue); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/value/ResEnumAttr.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data.value; 18 | 19 | import brut.androlib.AndrolibException; 20 | import brut.androlib.res.data.ResResource; 21 | import brut.util.Duo; 22 | import java.io.IOException; 23 | import java.util.HashMap; 24 | import java.util.Map; 25 | import org.xmlpull.v1.XmlSerializer; 26 | 27 | /** 28 | * @author Ryszard Wiśniewski 29 | */ 30 | public class ResEnumAttr extends ResAttr { 31 | ResEnumAttr(ResReferenceValue parent, int type, Integer min, Integer max, 32 | Boolean l10n, Duo[] items) { 33 | super(parent, type, min, max, l10n); 34 | mItems = items; 35 | } 36 | 37 | @Override 38 | public String convertToResXmlFormat(ResScalarValue value) 39 | throws AndrolibException { 40 | if (value instanceof ResIntValue) { 41 | String ret = decodeValue(((ResIntValue) value).getValue()); 42 | if (ret != null) { 43 | return ret; 44 | } 45 | } 46 | return super.convertToResXmlFormat(value); 47 | } 48 | 49 | @Override 50 | protected void serializeBody(XmlSerializer serializer, ResResource res) 51 | throws AndrolibException, IOException { 52 | for (Duo duo : mItems) { 53 | int intVal = duo.m2.getValue(); 54 | 55 | serializer.startTag(null, "enum"); 56 | serializer.attribute(null, "name", duo.m1.getReferent().getName()); 57 | serializer.attribute(null, "value", String.valueOf(intVal)); 58 | serializer.endTag(null, "enum"); 59 | } 60 | } 61 | 62 | private String decodeValue(int value) throws AndrolibException { 63 | String value2 = mItemsCache.get(value); 64 | if (value2 == null) { 65 | ResReferenceValue ref = null; 66 | for (Duo duo : mItems) { 67 | if (duo.m2.getValue() == value) { 68 | ref = duo.m1; 69 | break; 70 | } 71 | } 72 | if (ref != null) { 73 | value2 = ref.getReferent().getName(); 74 | mItemsCache.put(value, value2); 75 | } 76 | } 77 | return value2; 78 | } 79 | 80 | 81 | private final Duo[] mItems; 82 | private final Map mItemsCache = 83 | new HashMap(); 84 | } 85 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/value/ResFileValue.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data.value; 18 | 19 | import brut.androlib.AndrolibException; 20 | 21 | /** 22 | * @author Ryszard Wiśniewski 23 | */ 24 | public class ResFileValue extends ResValue { 25 | private final String mPath; 26 | 27 | public ResFileValue(String path) { 28 | this.mPath = path; 29 | } 30 | 31 | public String getPath() { 32 | return mPath; 33 | } 34 | 35 | public String getStrippedPath() throws AndrolibException { 36 | if (! mPath.startsWith("res/")) { 37 | throw new AndrolibException( 38 | "File path does not start with \"res/\": " + mPath); 39 | } 40 | return mPath.substring(4); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/value/ResFlagsAttr.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data.value; 18 | 19 | import brut.androlib.AndrolibException; 20 | import brut.androlib.res.data.ResResource; 21 | import brut.util.Duo; 22 | import java.io.IOException; 23 | import java.util.Arrays; 24 | import java.util.Comparator; 25 | import org.xmlpull.v1.XmlSerializer; 26 | 27 | /** 28 | * @author Ryszard Wiśniewski 29 | */ 30 | public class ResFlagsAttr extends ResAttr { 31 | ResFlagsAttr(ResReferenceValue parent, int type, Integer min, Integer max, Boolean l10n, Duo[] items) { 32 | super(parent, type, min, max, l10n); 33 | 34 | mItems = new FlagItem[items.length]; 35 | for (int i = 0; i < items.length; i++) { 36 | mItems[i] = new FlagItem(items[i].m1, items[i].m2.getValue()); 37 | } 38 | } 39 | 40 | @Override 41 | public String convertToResXmlFormat(ResScalarValue value) 42 | throws AndrolibException { 43 | if (! (value instanceof ResIntValue)) { 44 | return super.convertToResXmlFormat(value); 45 | } 46 | loadFlags(); 47 | int intVal = ((ResIntValue) value).getValue(); 48 | 49 | if (intVal == 0) { 50 | return renderFlags(mZeroFlags); 51 | } 52 | 53 | FlagItem[] flagItems = new FlagItem[mFlags.length]; 54 | int[] flags = new int[mFlags.length]; 55 | int flagsCount = 0; 56 | for (int i = 0; i < mFlags.length; i++) { 57 | FlagItem flagItem = mFlags[i]; 58 | int flag = flagItem.flag; 59 | 60 | if ((intVal & flag) != flag) { 61 | continue; 62 | } 63 | 64 | if (! isSubpartOf(flag, flags)) { 65 | flags[flagsCount] = flag; 66 | flagItems[flagsCount++] = flagItem; 67 | } 68 | } 69 | return renderFlags(Arrays.copyOf(flagItems, flagsCount)); 70 | } 71 | 72 | @Override 73 | protected void serializeBody(XmlSerializer serializer, ResResource res) 74 | throws AndrolibException, IOException { 75 | for (int i = 0; i < mItems.length; i++) { 76 | FlagItem item = mItems[i]; 77 | 78 | serializer.startTag(null, "flag"); 79 | serializer.attribute(null, "name", item.getValue()); 80 | serializer.attribute(null, "value", 81 | String.format("0x%08x", item.flag)); 82 | serializer.endTag(null, "flag"); 83 | } 84 | } 85 | 86 | private boolean isSubpartOf(int flag, int[] flags) { 87 | for (int i = 0; i < flags.length; i++) { 88 | if ((flags[i] & flag) == flag) { 89 | return true; 90 | } 91 | } 92 | return false; 93 | } 94 | 95 | private String renderFlags(FlagItem[] flags) throws AndrolibException { 96 | String ret = ""; 97 | for (int i = 0; i < flags.length; i++) { 98 | ret += "|" + flags[i].getValue(); 99 | } 100 | if (ret.isEmpty()) { 101 | return ret; 102 | } 103 | return ret.substring(1); 104 | } 105 | 106 | private void loadFlags() { 107 | if (mFlags != null) { 108 | return; 109 | } 110 | 111 | FlagItem[] zeroFlags = new FlagItem[mItems.length]; 112 | int zeroFlagsCount = 0; 113 | FlagItem[] flags = new FlagItem[mItems.length]; 114 | int flagsCount = 0; 115 | 116 | for (int i = 0; i < mItems.length; i++) { 117 | FlagItem item = mItems[i]; 118 | if (item.flag == 0) { 119 | zeroFlags[zeroFlagsCount++] = item; 120 | } else { 121 | flags[flagsCount++] = item; 122 | } 123 | } 124 | 125 | mZeroFlags = Arrays.copyOf(zeroFlags, zeroFlagsCount); 126 | mFlags = Arrays.copyOf(flags, flagsCount); 127 | 128 | Arrays.sort(mFlags, new Comparator() { 129 | public int compare(FlagItem o1, FlagItem o2) { 130 | return Integer.valueOf(Integer.bitCount(o2.flag)) 131 | .compareTo(Integer.bitCount(o1.flag)); 132 | } 133 | }); 134 | } 135 | 136 | 137 | private final FlagItem[] mItems; 138 | 139 | private FlagItem[] mZeroFlags; 140 | private FlagItem[] mFlags; 141 | 142 | 143 | private static class FlagItem { 144 | public final ResReferenceValue ref; 145 | public final int flag; 146 | public String value; 147 | 148 | public FlagItem(ResReferenceValue ref, int flag) { 149 | this.ref = ref; 150 | this.flag = flag; 151 | } 152 | 153 | public String getValue() throws AndrolibException { 154 | if (value == null) { 155 | value = ref.getReferent().getName(); 156 | } 157 | return value; 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/value/ResFloatValue.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data.value; 18 | 19 | /** 20 | * @author Ryszard Wiśniewski 21 | */ 22 | public class ResFloatValue extends ResScalarValue { 23 | private final float mValue; 24 | 25 | public ResFloatValue(float value, String rawValue) { 26 | super("float", rawValue); 27 | this.mValue = value; 28 | } 29 | 30 | public float getValue() { 31 | return mValue; 32 | } 33 | 34 | protected String encodeAsResXml() { 35 | return String.valueOf(mValue); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/value/ResFractionValue.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data.value; 18 | 19 | import android.util.TypedValue; 20 | import brut.androlib.AndrolibException; 21 | 22 | /** 23 | * @author Ryszard Wiśniewski 24 | */ 25 | public class ResFractionValue extends ResIntValue { 26 | public ResFractionValue(int value, String rawValue) { 27 | super(value, rawValue, "fraction"); 28 | } 29 | 30 | @Override 31 | protected String encodeAsResXml() throws AndrolibException { 32 | return TypedValue.coerceToString(TypedValue.TYPE_FRACTION, mValue); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/value/ResIdValue.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data.value; 18 | 19 | import brut.androlib.res.xml.ResValuesXmlSerializable; 20 | import brut.androlib.AndrolibException; 21 | import brut.androlib.res.data.ResResource; 22 | import java.io.IOException; 23 | import org.xmlpull.v1.XmlSerializer; 24 | 25 | /** 26 | * @author Ryszard Wiśniewski 27 | */ 28 | public class ResIdValue extends ResValue implements ResValuesXmlSerializable { 29 | public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) throws IOException, AndrolibException { 30 | serializer.startTag(null, "item"); 31 | serializer.attribute(null, "type", res.getResSpec().getType().getName()); 32 | serializer.attribute(null, "name", res.getResSpec().getName()); 33 | serializer.endTag(null, "item"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/value/ResIntValue.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data.value; 18 | 19 | import brut.androlib.AndrolibException; 20 | 21 | /** 22 | * @author Ryszard Wiśniewski 23 | */ 24 | public class ResIntValue extends ResScalarValue { 25 | protected final int mValue; 26 | 27 | public ResIntValue(int value, String rawValue) { 28 | this(value, rawValue, "integer"); 29 | } 30 | 31 | public ResIntValue(int value, String rawValue, String type) { 32 | super(type, rawValue); 33 | this.mValue = value; 34 | } 35 | 36 | public int getValue() { 37 | return mValue; 38 | } 39 | 40 | protected String encodeAsResXml() throws AndrolibException { 41 | return String.valueOf(mValue); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/value/ResPluralsValue.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data.value; 18 | 19 | import brut.androlib.res.xml.ResValuesXmlSerializable; 20 | import brut.androlib.AndrolibException; 21 | import brut.androlib.res.data.ResResource; 22 | import brut.util.Duo; 23 | import java.io.IOException; 24 | import org.xmlpull.v1.XmlSerializer; 25 | 26 | /** 27 | * @author Ryszard Wiśniewski 28 | */ 29 | public class ResPluralsValue extends ResBagValue implements ResValuesXmlSerializable { 30 | ResPluralsValue(ResReferenceValue parent, 31 | Duo[] items) { 32 | super(parent); 33 | 34 | mItems = new ResScalarValue[6]; 35 | for (int i = 0; i < items.length; i++) { 36 | mItems[items[i].m1 - BAG_KEY_PLURALS_START] = 37 | (ResScalarValue) items[i].m2; 38 | } 39 | } 40 | 41 | @Override 42 | public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) 43 | throws IOException, AndrolibException { 44 | serializer.startTag(null, "plurals"); 45 | serializer.attribute(null, "name", res.getResSpec().getName()); 46 | for (int i = 0; i < mItems.length; i++) { 47 | ResScalarValue item = mItems[i]; 48 | if (item == null) { 49 | continue; 50 | } 51 | serializer.startTag(null, "item"); 52 | serializer.attribute(null, "quantity", QUANTITY_MAP[i]); 53 | serializer.text(item.encodeAsResXmlValue()); 54 | serializer.endTag(null, "item"); 55 | } 56 | serializer.endTag(null, "plurals"); 57 | } 58 | 59 | 60 | private final ResScalarValue[] mItems; 61 | 62 | 63 | public static final int BAG_KEY_PLURALS_START = 0x01000004; 64 | public static final int BAG_KEY_PLURALS_END = 0x01000009; 65 | private static final String[] QUANTITY_MAP = 66 | new String[] {"other", "zero", "one", "two", "few", "many"}; 67 | } 68 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/value/ResReferenceValue.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data.value; 18 | 19 | import brut.androlib.AndrolibException; 20 | import brut.androlib.res.data.ResPackage; 21 | import brut.androlib.res.data.ResResSpec; 22 | 23 | /** 24 | * @author Ryszard Wiśniewski 25 | */ 26 | public class ResReferenceValue extends ResIntValue { 27 | private final ResPackage mPackage; 28 | private final boolean mTheme; 29 | 30 | public ResReferenceValue(ResPackage package_, int value, String rawValue) { 31 | this(package_, value, rawValue, false); 32 | } 33 | 34 | public ResReferenceValue(ResPackage package_, int value, String rawValue, 35 | boolean theme) { 36 | super(value, rawValue, "reference"); 37 | mPackage = package_; 38 | mTheme = theme; 39 | } 40 | 41 | protected String encodeAsResXml() throws AndrolibException { 42 | if (isNull()) { 43 | return "@null"; 44 | } 45 | 46 | ResResSpec spec = getReferent(); 47 | boolean newId = 48 | spec.hasDefaultResource() && 49 | spec.getDefaultResource().getValue() instanceof ResIdValue; 50 | 51 | return 52 | (mTheme ? '?' : '@') + 53 | (newId ? "+" : "") + 54 | spec.getFullName(mPackage, 55 | mTheme && spec.getType().getName().equals("attr")); 56 | } 57 | 58 | public ResResSpec getReferent() throws AndrolibException { 59 | return mPackage.getResTable().getResSpec(getValue()); 60 | } 61 | 62 | public boolean isNull() { 63 | return mValue == 0; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data.value; 18 | 19 | import brut.androlib.res.xml.ResValuesXmlSerializable; 20 | import brut.androlib.res.xml.ResXmlEncodable; 21 | import brut.androlib.AndrolibException; 22 | import brut.androlib.res.data.ResResource; 23 | import java.io.IOException; 24 | import org.xmlpull.v1.XmlSerializer; 25 | 26 | /** 27 | * @author Ryszard Wiśniewski 28 | */ 29 | public abstract class ResScalarValue extends ResValue 30 | implements ResXmlEncodable, ResValuesXmlSerializable { 31 | protected final String mType; 32 | protected final String mRawValue; 33 | 34 | protected ResScalarValue(String type, String rawValue) { 35 | mType = type; 36 | mRawValue = rawValue; 37 | } 38 | 39 | public String encodeAsResXmlAttr() throws AndrolibException { 40 | if (mRawValue != null) { 41 | return mRawValue; 42 | } 43 | return encodeAsResXml(); 44 | } 45 | 46 | public String encodeAsResXmlItemValue() throws AndrolibException { 47 | return encodeAsResXmlValue(); 48 | } 49 | 50 | public String encodeAsResXmlValue() throws AndrolibException { 51 | if (mRawValue != null) { 52 | return mRawValue; 53 | } 54 | return encodeAsResXml(); 55 | } 56 | 57 | public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) 58 | throws IOException, AndrolibException { 59 | String type = res.getResSpec().getType().getName(); 60 | boolean item = ! "reference".equals(mType) && ! type.equals(mType); 61 | String tagName = item ? "item" : type; 62 | 63 | serializer.startTag(null, tagName); 64 | if (item) { 65 | serializer.attribute(null, "type", type); 66 | } 67 | serializer.attribute(null, "name", res.getResSpec().getName()); 68 | 69 | serializeExtraXmlAttrs(serializer, res); 70 | 71 | String body = encodeAsResXmlValue(); 72 | if (! body.isEmpty()) { 73 | serializer.ignorableWhitespace(body); 74 | } 75 | 76 | serializer.endTag(null, tagName); 77 | } 78 | 79 | public String getType() { 80 | return mType; 81 | } 82 | 83 | protected void serializeExtraXmlAttrs(XmlSerializer serializer, 84 | ResResource res) throws IOException { 85 | } 86 | 87 | protected abstract String encodeAsResXml() throws AndrolibException; 88 | } 89 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/value/ResStringValue.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data.value; 18 | 19 | import brut.androlib.AndrolibException; 20 | import brut.androlib.res.data.ResResource; 21 | import brut.androlib.res.xml.ResXmlEncoders; 22 | import java.io.IOException; 23 | import org.xmlpull.v1.XmlSerializer; 24 | 25 | /** 26 | * @author Ryszard Wiśniewski 27 | */ 28 | public class ResStringValue extends ResScalarValue { 29 | 30 | public ResStringValue(String value) { 31 | this(value, "string"); 32 | } 33 | 34 | public ResStringValue(String value, String type) { 35 | super(type, value); 36 | } 37 | 38 | @Override 39 | public String encodeAsResXmlAttr() { 40 | return ResXmlEncoders.encodeAsResXmlAttr(mRawValue); 41 | } 42 | 43 | @Override 44 | public String encodeAsResXmlItemValue() { 45 | return ResXmlEncoders.enumerateNonPositionalSubstitutions( 46 | ResXmlEncoders.encodeAsXmlValue(mRawValue)); 47 | } 48 | 49 | @Override 50 | public String encodeAsResXmlValue() { 51 | return ResXmlEncoders.encodeAsXmlValue(mRawValue); 52 | } 53 | 54 | @Override 55 | protected String encodeAsResXml() throws AndrolibException { 56 | throw new UnsupportedOperationException(); 57 | } 58 | 59 | @Override 60 | protected void serializeExtraXmlAttrs(XmlSerializer serializer, 61 | ResResource res) throws IOException { 62 | if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(mRawValue)) { 63 | serializer.attribute(null, "formatted", "false"); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/value/ResStyleValue.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data.value; 18 | 19 | import brut.androlib.res.xml.ResValuesXmlSerializable; 20 | import brut.androlib.AndrolibException; 21 | import brut.androlib.res.data.ResResSpec; 22 | import brut.androlib.res.data.ResResource; 23 | import brut.util.Duo; 24 | import java.io.IOException; 25 | import org.xmlpull.v1.XmlSerializer; 26 | 27 | /** 28 | * @author Ryszard Wiśniewski 29 | */ 30 | public class ResStyleValue extends ResBagValue implements ResValuesXmlSerializable { 31 | ResStyleValue(ResReferenceValue parent, 32 | Duo[] items, ResValueFactory factory) { 33 | super(parent); 34 | 35 | mItems = new Duo[items.length]; 36 | for (int i = 0; i < items.length; i++) { 37 | mItems[i] = new Duo( 38 | factory.newReference(items[i].m1, null), items[i].m2); 39 | } 40 | } 41 | 42 | @Override 43 | public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) 44 | throws IOException, AndrolibException { 45 | serializer.startTag(null, "style"); 46 | serializer.attribute(null, "name", res.getResSpec().getName()); 47 | if (! mParent.isNull()) { 48 | serializer.attribute(null, "parent", mParent.encodeAsResXmlAttr()); 49 | } 50 | for (int i = 0; i < mItems.length; i++) { 51 | ResResSpec spec = mItems[i].m1.getReferent(); 52 | ResAttr attr = (ResAttr) spec.getDefaultResource().getValue(); 53 | String value = attr.convertToResXmlFormat(mItems[i].m2); 54 | 55 | if (value == null) { 56 | value = mItems[i].m2.encodeAsResXmlValue(); 57 | } 58 | 59 | if (value == null) { 60 | continue; 61 | } 62 | 63 | serializer.startTag(null, "item"); 64 | serializer.attribute(null, "name", 65 | spec.getFullName(res.getResSpec().getPackage(), true)); 66 | serializer.text(value); 67 | serializer.endTag(null, "item"); 68 | } 69 | serializer.endTag(null, "style"); 70 | } 71 | 72 | 73 | private final Duo[] mItems; 74 | } 75 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/value/ResValue.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data.value; 18 | 19 | /** 20 | * @author Ryszard Wiśniewski 21 | */ 22 | public class ResValue { 23 | 24 | } 25 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.data.value; 18 | 19 | import android.util.TypedValue; 20 | import brut.androlib.AndrolibException; 21 | import brut.androlib.res.data.ResPackage; 22 | import brut.util.Duo; 23 | 24 | /** 25 | * @author Ryszard Wiśniewski 26 | */ 27 | public class ResValueFactory { 28 | private final ResPackage mPackage; 29 | 30 | public ResValueFactory(ResPackage pakage_) { 31 | this.mPackage = pakage_; 32 | } 33 | 34 | public ResScalarValue factory(int type, int value, String rawValue) 35 | throws AndrolibException { 36 | switch (type) { 37 | case TypedValue.TYPE_REFERENCE: 38 | return newReference(value, rawValue); 39 | case TypedValue.TYPE_ATTRIBUTE: 40 | return newReference(value, rawValue, true); 41 | case TypedValue.TYPE_STRING: 42 | return new ResStringValue(rawValue); 43 | case TypedValue.TYPE_FLOAT: 44 | return new ResFloatValue(Float.intBitsToFloat(value), rawValue); 45 | case TypedValue.TYPE_DIMENSION: 46 | return new ResDimenValue(value, rawValue); 47 | case TypedValue.TYPE_FRACTION: 48 | return new ResFractionValue(value, rawValue); 49 | case TypedValue.TYPE_INT_BOOLEAN: 50 | return new ResBoolValue(value != 0, rawValue); 51 | } 52 | 53 | if (type >= TypedValue.TYPE_FIRST_COLOR_INT 54 | && type <= TypedValue.TYPE_LAST_COLOR_INT) { 55 | return new ResColorValue(value, rawValue); 56 | } 57 | if (type >= TypedValue.TYPE_FIRST_INT 58 | && type <= TypedValue.TYPE_LAST_INT) { 59 | return new ResIntValue(value, rawValue); 60 | } 61 | 62 | throw new AndrolibException("Invalid value type: "+ type); 63 | } 64 | 65 | public ResValue factory(String value) { 66 | if (value.startsWith("res/")) { 67 | return new ResFileValue(value); 68 | } 69 | return new ResStringValue(value); 70 | } 71 | 72 | public ResBagValue bagFactory(int parent, 73 | Duo[] items) throws AndrolibException { 74 | ResReferenceValue parentVal = newReference(parent, null); 75 | 76 | if (items.length == 0) { 77 | return new ResBagValue(parentVal); 78 | } 79 | int key = items[0].m1; 80 | if (key == ResAttr.BAG_KEY_ATTR_TYPE) { 81 | return ResAttr.factory(parentVal, items, this, mPackage); 82 | } 83 | if (key == ResArrayValue.BAG_KEY_ARRAY_START) { 84 | return new ResArrayValue(parentVal, items); 85 | } 86 | if (key >= ResPluralsValue.BAG_KEY_PLURALS_START 87 | && key <= ResPluralsValue.BAG_KEY_PLURALS_END) { 88 | return new ResPluralsValue(parentVal, items); 89 | } 90 | return new ResStyleValue(parentVal, items, this); 91 | } 92 | 93 | public ResReferenceValue newReference(int resID, String rawValue) { 94 | return newReference(resID, rawValue, false); 95 | } 96 | 97 | public ResReferenceValue newReference(int resID, String rawValue, 98 | boolean theme) { 99 | return new ResReferenceValue(mPackage, resID, rawValue, theme); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/decoder/Res9patchStreamDecoder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.decoder; 18 | 19 | import brut.androlib.AndrolibException; 20 | import brut.androlib.err.CantFind9PatchChunk; 21 | import brut.util.ExtDataInput; 22 | import java.awt.image.BufferedImage; 23 | import java.io.*; 24 | import javax.imageio.ImageIO; 25 | import org.apache.commons.io.IOUtils; 26 | 27 | /** 28 | * @author Ryszard Wiśniewski 29 | */ 30 | public class Res9patchStreamDecoder implements ResStreamDecoder { 31 | public void decode(InputStream in, OutputStream out) 32 | throws AndrolibException { 33 | try { 34 | byte[] data = IOUtils.toByteArray(in); 35 | 36 | BufferedImage im = ImageIO.read(new ByteArrayInputStream(data)); 37 | int w = im.getWidth(), h = im.getHeight(); 38 | 39 | BufferedImage im2 = new BufferedImage( 40 | w + 2, h + 2, BufferedImage.TYPE_4BYTE_ABGR); 41 | if (im.getType() == BufferedImage.TYPE_4BYTE_ABGR) { 42 | im2.getRaster().setRect(1, 1, im.getRaster()); 43 | } else { 44 | im2.getGraphics().drawImage(im, 1, 1, null); 45 | } 46 | 47 | NinePatch np = getNinePatch(data); 48 | drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight); 49 | drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom); 50 | 51 | int[] xDivs = np.xDivs; 52 | for (int i = 0; i < xDivs.length; i += 2) { 53 | drawHLine(im2, 0, xDivs[i] + 1, xDivs[i + 1]); 54 | } 55 | 56 | int[] yDivs = np.yDivs; 57 | for (int i = 0; i < yDivs.length; i += 2) { 58 | drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]); 59 | } 60 | 61 | ImageIO.write(im2, "png", out); 62 | } catch (IOException ex) { 63 | throw new AndrolibException(ex); 64 | } 65 | } 66 | 67 | private NinePatch getNinePatch(byte[] data) 68 | throws AndrolibException, IOException { 69 | ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data)); 70 | find9patchChunk(di); 71 | return NinePatch.decode(di); 72 | } 73 | 74 | private void find9patchChunk(DataInput di) 75 | throws AndrolibException, IOException { 76 | di.skipBytes(8); 77 | while (true) { 78 | int size; 79 | try { 80 | size = di.readInt(); 81 | } catch (IOException ex) { 82 | throw new CantFind9PatchChunk("Cant find nine patch chunk", ex); 83 | } 84 | if (di.readInt() == NP_CHUNK_TYPE) { 85 | return; 86 | } 87 | di.skipBytes(size + 4); 88 | } 89 | } 90 | 91 | private void drawHLine(BufferedImage im, int y, int x1, int x2) { 92 | for (int x = x1; x <= x2; x++) { 93 | im.setRGB(x, y, NP_COLOR); 94 | } 95 | } 96 | 97 | private void drawVLine(BufferedImage im, int x, int y1, int y2) { 98 | for (int y = y1; y <= y2; y++) { 99 | im.setRGB(x, y, NP_COLOR); 100 | } 101 | } 102 | 103 | private static final int NP_CHUNK_TYPE = 0x6e705463; // npTc 104 | private static final int NP_COLOR = 0xff000000; 105 | 106 | 107 | private static class NinePatch { 108 | public final int padLeft, padRight, padTop, padBottom; 109 | public final int[] xDivs, yDivs; 110 | 111 | public NinePatch(int padLeft, int padRight, int padTop, int padBottom, 112 | int[] xDivs, int[] yDivs) { 113 | this.padLeft = padLeft; 114 | this.padRight = padRight; 115 | this.padTop = padTop; 116 | this.padBottom = padBottom; 117 | this.xDivs = xDivs; 118 | this.yDivs = yDivs; 119 | } 120 | 121 | public static NinePatch decode(ExtDataInput di) throws IOException { 122 | di.skipBytes(1); 123 | byte numXDivs = di.readByte(); 124 | byte numYDivs = di.readByte(); 125 | di.skipBytes(1); 126 | di.skipBytes(8); 127 | int padLeft = di.readInt(); 128 | int padRight = di.readInt(); 129 | int padTop = di.readInt(); 130 | int padBottom = di.readInt(); 131 | di.skipBytes(4); 132 | int[] xDivs = di.readIntArray(numXDivs); 133 | int[] yDivs = di.readIntArray(numYDivs); 134 | 135 | return new NinePatch(padLeft, padRight, padTop, padBottom, 136 | xDivs, yDivs); 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/decoder/ResAttrDecoder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.decoder; 18 | 19 | import brut.androlib.AndrolibException; 20 | import brut.androlib.res.data.ResPackage; 21 | import brut.androlib.res.data.value.ResAttr; 22 | import brut.androlib.res.data.value.ResScalarValue; 23 | 24 | /** 25 | * @author Ryszard Wiśniewski 26 | */ 27 | public class ResAttrDecoder { 28 | public String decode(int type, int value, String rawValue, int attrResId) 29 | throws AndrolibException { 30 | ResScalarValue resValue = mCurrentPackage.getValueFactory() 31 | .factory(type, value, rawValue); 32 | 33 | String decoded = null; 34 | if (attrResId != 0) { 35 | ResAttr attr = (ResAttr) getCurrentPackage().getResTable() 36 | .getResSpec(attrResId).getDefaultResource().getValue(); 37 | decoded = attr.convertToResXmlFormat(resValue); 38 | } 39 | 40 | return decoded != null ? decoded : resValue.encodeAsResXmlAttr(); 41 | } 42 | 43 | public ResPackage getCurrentPackage() throws AndrolibException { 44 | if (mCurrentPackage == null) { 45 | throw new AndrolibException("Current package not set"); 46 | } 47 | return mCurrentPackage; 48 | } 49 | 50 | public void setCurrentPackage(ResPackage currentPackage) { 51 | mCurrentPackage = currentPackage; 52 | } 53 | 54 | private ResPackage mCurrentPackage; 55 | } 56 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/decoder/ResFileDecoder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.decoder; 18 | 19 | import brut.androlib.AndrolibException; 20 | import brut.androlib.err.CantFind9PatchChunk; 21 | import brut.androlib.res.data.ResResource; 22 | import brut.androlib.res.data.value.ResBoolValue; 23 | import brut.androlib.res.data.value.ResFileValue; 24 | import brut.directory.Directory; 25 | import brut.directory.DirectoryException; 26 | import java.io.*; 27 | import java.util.logging.Level; 28 | import java.util.logging.Logger; 29 | 30 | /** 31 | * @author Ryszard Wiśniewski 32 | */ 33 | public class ResFileDecoder { 34 | private final ResStreamDecoderContainer mDecoders; 35 | 36 | public ResFileDecoder(ResStreamDecoderContainer decoders) { 37 | this.mDecoders = decoders; 38 | } 39 | 40 | public void decode(ResResource res, Directory inDir, Directory outDir) 41 | throws AndrolibException { 42 | 43 | ResFileValue fileValue = (ResFileValue) res.getValue(); 44 | String inFileName = fileValue.getStrippedPath(); 45 | String outResName = res.getFilePath(); 46 | String typeName = res.getResSpec().getType().getName(); 47 | 48 | String ext = null; 49 | String outFileName; 50 | int extPos = inFileName.lastIndexOf("."); 51 | if (extPos == -1) { 52 | outFileName = outResName; 53 | } else { 54 | ext = inFileName.substring(extPos); 55 | outFileName = outResName + ext; 56 | } 57 | 58 | try { 59 | if (typeName.equals("raw")) { 60 | decode(inDir, inFileName, outDir, outFileName, "raw"); 61 | return; 62 | } 63 | if (typeName.equals("drawable") || typeName.equals("mipmap")) { 64 | if (inFileName.toLowerCase().endsWith(".9.png")) { 65 | outFileName = outResName + ".9" + ext; 66 | 67 | try { 68 | decode( 69 | inDir, inFileName, outDir, outFileName, "9patch"); 70 | return; 71 | } catch (CantFind9PatchChunk ex) { 72 | LOGGER.log(Level.WARNING, String.format( 73 | "Cant find 9patch chunk in file: \"%s\". Renaming it to *.png.", 74 | inFileName 75 | ), ex); 76 | outDir.removeFile(outFileName); 77 | outFileName = outResName + ext; 78 | } 79 | } 80 | if (! ".xml".equals(ext)) { 81 | decode(inDir, inFileName, outDir, outFileName, "raw"); 82 | return; 83 | } 84 | } 85 | 86 | decode(inDir, inFileName, outDir, outFileName, "xml"); 87 | } catch (AndrolibException ex) { 88 | LOGGER.log(Level.SEVERE, String.format( 89 | "Could not decode file, replacing by FALSE value: %s", 90 | inFileName, outFileName), ex); 91 | res.replace(new ResBoolValue(false, null)); 92 | } 93 | } 94 | 95 | public void decode(Directory inDir, String inFileName, Directory outDir, 96 | String outFileName, String decoder) throws AndrolibException { 97 | try { 98 | InputStream in = inDir.getFileInput(inFileName); 99 | OutputStream out = outDir.getFileOutput(outFileName); 100 | mDecoders.decode(in, out, decoder); 101 | in.close(); 102 | out.close(); 103 | } catch (IOException ex) { 104 | throw new AndrolibException(ex); 105 | } catch (DirectoryException ex) { 106 | throw new AndrolibException(ex); 107 | } 108 | } 109 | 110 | private final static Logger LOGGER = 111 | Logger.getLogger(ResFileDecoder.class.getName()); 112 | } 113 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/decoder/ResRawStreamDecoder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.decoder; 18 | 19 | import brut.androlib.AndrolibException; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.io.OutputStream; 23 | import org.apache.commons.io.IOUtils; 24 | 25 | /** 26 | * @author Ryszard Wiśniewski 27 | */ 28 | public class ResRawStreamDecoder implements ResStreamDecoder { 29 | public void decode(InputStream in, OutputStream out) 30 | throws AndrolibException { 31 | try { 32 | IOUtils.copy(in, out); 33 | } catch (IOException ex) { 34 | throw new AndrolibException("Could not decode raw stream", ex); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.decoder; 18 | 19 | import brut.androlib.AndrolibException; 20 | import java.io.InputStream; 21 | import java.io.OutputStream; 22 | 23 | /** 24 | * @author Ryszard Wiśniewski 25 | */ 26 | public interface ResStreamDecoder { 27 | public void decode(InputStream in, OutputStream out) 28 | throws AndrolibException; 29 | } 30 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoderContainer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.decoder; 18 | 19 | import brut.androlib.AndrolibException; 20 | import java.io.InputStream; 21 | import java.io.OutputStream; 22 | import java.util.HashMap; 23 | import java.util.Map; 24 | 25 | /** 26 | * @author Ryszard Wiśniewski 27 | */ 28 | public class ResStreamDecoderContainer { 29 | private final Map mDecoders = 30 | new HashMap(); 31 | 32 | public void decode(InputStream in, OutputStream out, String decoderName) 33 | throws AndrolibException { 34 | getDecoder(decoderName).decode(in, out); 35 | } 36 | 37 | public ResStreamDecoder getDecoder(String name) throws AndrolibException { 38 | ResStreamDecoder decoder = mDecoders.get(name); 39 | if (decoder == null) { 40 | throw new AndrolibException("Undefined decoder: " + name); 41 | } 42 | return decoder; 43 | } 44 | 45 | public void setDecoder(String name, ResStreamDecoder decoder) { 46 | mDecoders.put(name, decoder); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.decoder; 18 | 19 | import brut.androlib.AndrolibException; 20 | import brut.androlib.res.util.ExtXmlSerializer; 21 | import java.io.*; 22 | import org.xmlpull.v1.*; 23 | import org.xmlpull.v1.wrapper.*; 24 | 25 | /** 26 | * @author Ryszard Wiśniewski 27 | */ 28 | public class XmlPullStreamDecoder implements ResStreamDecoder { 29 | public XmlPullStreamDecoder(XmlPullParser parser, 30 | ExtXmlSerializer serializer) { 31 | this.mParser = parser; 32 | this.mSerial = serializer; 33 | } 34 | 35 | public void decode(InputStream in, OutputStream out) 36 | throws AndrolibException { 37 | try { 38 | XmlPullWrapperFactory factory = XmlPullWrapperFactory.newInstance(); 39 | XmlPullParserWrapper par = factory.newPullParserWrapper(mParser); 40 | XmlSerializerWrapper ser = factory.newSerializerWrapper(mSerial); 41 | 42 | par.setInput(in, null); 43 | ser.setOutput(out, null); 44 | 45 | while (par.nextToken() != XmlPullParser.END_DOCUMENT) { 46 | ser.event(par); 47 | } 48 | mSerial.newLine(); 49 | ser.flush(); 50 | } catch (XmlPullParserException ex) { 51 | throw new AndrolibException("Could not decode XML", ex); 52 | } catch (IOException ex) { 53 | throw new AndrolibException("Could not decode XML", ex); 54 | } 55 | } 56 | 57 | private final XmlPullParser mParser; 58 | private final ExtXmlSerializer mSerial; 59 | } 60 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/util/ExtFile.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.util; 18 | 19 | import brut.directory.Directory; 20 | import brut.directory.DirectoryException; 21 | import brut.directory.FileDirectory; 22 | import brut.directory.ZipRODirectory; 23 | import java.io.File; 24 | import java.net.URI; 25 | 26 | /** 27 | * @author Ryszard Wiśniewski 28 | */ 29 | public class ExtFile extends File { 30 | public ExtFile(File file) { 31 | super(file.getPath()); 32 | } 33 | 34 | public ExtFile(URI uri) { 35 | super(uri); 36 | } 37 | 38 | public ExtFile(File parent, String child) { 39 | super(parent, child); 40 | } 41 | 42 | public ExtFile(String parent, String child) { 43 | super(parent, child); 44 | } 45 | 46 | public ExtFile(String pathname) { 47 | super(pathname); 48 | } 49 | 50 | public Directory getDirectory() throws DirectoryException { 51 | if (mDirectory == null) { 52 | if (isDirectory()) { 53 | mDirectory = new FileDirectory(this); 54 | } else { 55 | mDirectory = new ZipRODirectory(this); 56 | } 57 | } 58 | return mDirectory; 59 | } 60 | 61 | 62 | private Directory mDirectory; 63 | } 64 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/util/ExtMXSerializer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.util; 18 | 19 | import java.io.*; 20 | import org.xmlpull.mxp1_serializer.MXSerializer; 21 | 22 | /** 23 | * @author Ryszard Wiśniewski 24 | */ 25 | public class ExtMXSerializer extends MXSerializer implements ExtXmlSerializer { 26 | @Override 27 | public void startDocument(String encoding, Boolean standalone) throws 28 | IOException, IllegalArgumentException, IllegalStateException { 29 | super.startDocument(encoding != null ? encoding : mDefaultEncoding, 30 | standalone); 31 | this.newLine(); 32 | } 33 | 34 | @Override 35 | protected void writeAttributeValue(String value, Writer out) 36 | throws IOException { 37 | if (mIsDisabledAttrEscape) { 38 | out.write(value); 39 | return; 40 | } 41 | super.writeAttributeValue(value, out); 42 | } 43 | 44 | @Override 45 | public void setOutput(OutputStream os, String encoding) throws IOException { 46 | super.setOutput(os, encoding != null ? encoding : mDefaultEncoding); 47 | } 48 | 49 | @Override 50 | public Object getProperty(String name) throws IllegalArgumentException { 51 | if (PROPERTY_DEFAULT_ENCODING.equals(name)) { 52 | return mDefaultEncoding; 53 | } 54 | return super.getProperty(name); 55 | } 56 | 57 | @Override 58 | public void setProperty(String name, Object value) 59 | throws IllegalArgumentException, IllegalStateException { 60 | if (PROPERTY_DEFAULT_ENCODING.equals(name)) { 61 | mDefaultEncoding = (String) value; 62 | } else { 63 | super.setProperty(name, value); 64 | } 65 | } 66 | 67 | public ExtXmlSerializer newLine() throws IOException { 68 | super.out.write(lineSeparator); 69 | return this; 70 | } 71 | 72 | public void setDisabledAttrEscape(boolean disabled) { 73 | mIsDisabledAttrEscape = disabled; 74 | } 75 | 76 | private String mDefaultEncoding; 77 | private boolean mIsDisabledAttrEscape = false; 78 | } 79 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/util/ExtXmlSerializer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.util; 18 | 19 | import java.io.IOException; 20 | import org.xmlpull.v1.XmlSerializer; 21 | 22 | /** 23 | * @author Ryszard Wiśniewski 24 | */ 25 | public interface ExtXmlSerializer extends XmlSerializer { 26 | 27 | public ExtXmlSerializer newLine() throws IOException; 28 | public void setDisabledAttrEscape(boolean disabled); 29 | 30 | public static final String PROPERTY_SERIALIZER_INDENTATION = 31 | "http://xmlpull.org/v1/doc/properties.html#serializer-indentation"; 32 | public static final String PROPERTY_SERIALIZER_LINE_SEPARATOR = 33 | "http://xmlpull.org/v1/doc/properties.html#serializer-line-separator"; 34 | public static final String PROPERTY_DEFAULT_ENCODING = "DEFAULT_ENCODING"; 35 | } 36 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/xml/ResValuesXmlSerializable.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.xml; 18 | 19 | import brut.androlib.AndrolibException; 20 | import brut.androlib.res.data.ResResource; 21 | import java.io.IOException; 22 | import org.xmlpull.v1.XmlSerializer; 23 | 24 | /** 25 | * @author Ryszard Wiśniewski 26 | */ 27 | public interface ResValuesXmlSerializable { 28 | public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) 29 | throws IOException, AndrolibException; 30 | } 31 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncodable.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.xml; 18 | 19 | import brut.androlib.AndrolibException; 20 | 21 | /** 22 | * @author Ryszard Wiśniewski 23 | */ 24 | public interface ResXmlEncodable { 25 | public String encodeAsResXmlAttr() throws AndrolibException; 26 | public String encodeAsResXmlValue() throws AndrolibException; 27 | } 28 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncoders.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.res.xml; 18 | 19 | import java.awt.event.KeyEvent; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | /** 24 | * @author Ryszard Wiśniewski 25 | */ 26 | public final class ResXmlEncoders { 27 | 28 | public static String escapeXmlChars(String str) { 29 | return str.replace("&", "&").replace("<", "<"); 30 | } 31 | 32 | public static String encodeAsResXmlAttr(String str) { 33 | if (str.isEmpty()) { 34 | return str; 35 | } 36 | 37 | char[] chars = str.toCharArray(); 38 | StringBuilder out = new StringBuilder(str.length() + 10); 39 | 40 | switch (chars[0]) { 41 | case '#': 42 | case '@': 43 | case '?': 44 | out.append('\\'); 45 | } 46 | 47 | for (char c : chars) { 48 | switch (c) { 49 | case '\\': 50 | out.append('\\'); 51 | break; 52 | case '"': 53 | out.append("""); 54 | continue; 55 | case '\n': 56 | out.append("\\n"); 57 | continue; 58 | default: 59 | if (!isPrintableChar(c)) { 60 | out.append(String.format("\\u%04x", (int) c)); 61 | continue; 62 | } 63 | } 64 | out.append(c); 65 | } 66 | 67 | return out.toString(); 68 | } 69 | 70 | public static String encodeAsXmlValue(String str) { 71 | if (str.isEmpty()) { 72 | return str; 73 | } 74 | 75 | char[] chars = str.toCharArray(); 76 | StringBuilder out = new StringBuilder(str.length() + 10); 77 | 78 | switch (chars[0]) { 79 | case '#': 80 | case '@': 81 | case '?': 82 | out.append('\\'); 83 | } 84 | 85 | boolean isInStyleTag = false; 86 | int startPos = 0; 87 | boolean enclose = false; 88 | boolean wasSpace = true; 89 | for (char c : chars) { 90 | if (isInStyleTag) { 91 | if (c == '>') { 92 | isInStyleTag = false; 93 | startPos = out.length() + 1; 94 | enclose = false; 95 | } 96 | } else if (c == ' ') { 97 | if (wasSpace) { 98 | enclose = true; 99 | } 100 | wasSpace = true; 101 | } else { 102 | wasSpace = false; 103 | switch (c) { 104 | case '\\': 105 | out.append('\\'); 106 | break; 107 | case '\'': 108 | case '\n': 109 | enclose = true; 110 | break; 111 | case '"': 112 | out.append('\\'); 113 | break; 114 | case '<': 115 | isInStyleTag = true; 116 | if (enclose) { 117 | out.insert(startPos, '"').append('"'); 118 | } 119 | break; 120 | default: 121 | if (!isPrintableChar(c)) { 122 | out.append(String.format("\\u%04x", (int) c)); 123 | continue; 124 | } 125 | } 126 | } 127 | out.append(c); 128 | } 129 | 130 | if (enclose || wasSpace) { 131 | out.insert(startPos, '"').append('"'); 132 | } 133 | 134 | return out.toString(); 135 | } 136 | 137 | public static boolean hasMultipleNonPositionalSubstitutions(String str) { 138 | return findNonPositionalSubstitutions(str, 2).size() > 1; 139 | } 140 | 141 | public static String enumerateNonPositionalSubstitutions(String str) { 142 | List subs = findNonPositionalSubstitutions(str, -1); 143 | if (subs.size() < 2) { 144 | return str; 145 | } 146 | 147 | StringBuilder out = new StringBuilder(); 148 | int pos = 0; 149 | int count = 0; 150 | for (Integer sub : subs) { 151 | out.append(str.substring(pos, ++sub)).append(++count).append('$'); 152 | pos = sub; 153 | } 154 | out.append(str.substring(pos)); 155 | 156 | return out.toString(); 157 | } 158 | 159 | /** 160 | * It searches for "%", but not "%%" nor "%(\d)+\$" 161 | */ 162 | private static List findNonPositionalSubstitutions(String str, 163 | int max) { 164 | int pos = 0; 165 | int pos2 = 0; 166 | int count = 0; 167 | int length = str.length(); 168 | List ret = new ArrayList(); 169 | while((pos2 = (pos = str.indexOf('%', pos2)) + 1) != 0) { 170 | if (pos2 == length) { 171 | break; 172 | } 173 | char c = str.charAt(pos2++); 174 | if (c == '%') { 175 | continue; 176 | } 177 | if (c >= '0' && c <= '9' && pos2 < length) { 178 | do { 179 | c = str.charAt(pos2++); 180 | } while (c >= '0' && c <= '9' && pos2 < length); 181 | if (c == '$') { 182 | continue; 183 | } 184 | } 185 | 186 | ret.add(pos); 187 | if (max != -1 && ++count >= max) { 188 | break; 189 | } 190 | } 191 | 192 | return ret; 193 | } 194 | 195 | private static boolean isPrintableChar(char c) { 196 | Character.UnicodeBlock block = Character.UnicodeBlock.of(c); 197 | return !Character.isISOControl(c) 198 | && c != KeyEvent.CHAR_UNDEFINED 199 | && block != null 200 | && block != Character.UnicodeBlock.SPECIALS; 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/src/DebugInjector.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.src; 18 | 19 | import brut.androlib.AndrolibException; 20 | import java.util.*; 21 | import java.util.regex.Matcher; 22 | import java.util.regex.Pattern; 23 | import org.jf.dexlib.Code.Analysis.RegisterType; 24 | import org.jf.dexlib.Code.Opcode; 25 | 26 | /** 27 | * @author Ryszard Wiśniewski 28 | */ 29 | public class DebugInjector { 30 | 31 | public static void inject(ListIterator it, StringBuilder out) 32 | throws AndrolibException { 33 | new DebugInjector(it, out).inject(); 34 | } 35 | 36 | private DebugInjector(ListIterator it, StringBuilder out) { 37 | mIt = it; 38 | mOut = out; 39 | } 40 | 41 | private void inject() throws AndrolibException { 42 | String definition = nextAndAppend(); 43 | if ( 44 | definition.contains(" abstract ") || 45 | definition.contains(" native ") 46 | ) { 47 | nextAndAppend(); 48 | return; 49 | } 50 | injectParameters(definition); 51 | 52 | boolean end = false; 53 | while (!end) { 54 | end = step(); 55 | } 56 | } 57 | 58 | private void injectParameters(String definition) throws AndrolibException { 59 | int pos = definition.indexOf('('); 60 | if (pos == -1) { 61 | throw new AndrolibException(); 62 | } 63 | int pos2 = definition.indexOf(')', pos); 64 | if (pos2 == -1) { 65 | throw new AndrolibException(); 66 | } 67 | String params = definition.substring(pos + 1, pos2); 68 | 69 | int i = definition.contains(" static ") ? 0 : 1; 70 | int argc = TypeName.listFromInternalName(params).size() + i; 71 | while(i < argc) { 72 | mOut.append(".parameter \"p").append(i).append("\"\n"); 73 | i++; 74 | } 75 | } 76 | 77 | private boolean step() { 78 | String line = next(); 79 | if (line.isEmpty()) { 80 | return false; 81 | } 82 | 83 | switch (line.charAt(0)) { 84 | case '#': 85 | return processComment(line); 86 | case ':': 87 | append(line); 88 | return false; 89 | case '.': 90 | return processDirective(line); 91 | default: 92 | return processInstruction(line); 93 | } 94 | } 95 | 96 | private boolean processComment(String line) { 97 | if (mFirstInstruction) { 98 | return false; 99 | } 100 | 101 | Matcher m = REGISTER_INFO_PATTERN.matcher(line); 102 | 103 | while (m.find()) { 104 | String localName = m.group(1); 105 | String localType = null; 106 | switch (RegisterType.Category.valueOf(m.group(2))) { 107 | case Reference: 108 | case Null: 109 | case UninitRef: 110 | case UninitThis: 111 | localType = "Ljava/lang/Object;"; 112 | break; 113 | case Boolean: 114 | localType = "Z"; 115 | break; 116 | case Integer: 117 | case One: 118 | case Unknown: 119 | localType = "I"; 120 | break; 121 | case Uninit: 122 | case Conflicted: 123 | if (mInitializedRegisters.remove(localName)) { 124 | mOut.append(".end local ").append(localName) 125 | .append('\n'); 126 | } 127 | continue; 128 | case Short: 129 | case PosShort: 130 | localType = "S"; 131 | break; 132 | case Byte: 133 | case PosByte: 134 | localType = "B"; 135 | break; 136 | case Char: 137 | localType = "C"; 138 | break; 139 | case Float: 140 | localType = "F"; 141 | break; 142 | case LongHi: 143 | case LongLo: 144 | localType = "J"; 145 | break; 146 | case DoubleHi: 147 | case DoubleLo: 148 | localType = "D"; 149 | break; 150 | default: 151 | assert false; 152 | } 153 | 154 | mInitializedRegisters.add(localName); 155 | mOut.append(".local ").append(localName).append(", ") 156 | .append(localName).append(':').append(localType).append('\n'); 157 | } 158 | 159 | return false; 160 | } 161 | 162 | private boolean processDirective(String line) { 163 | String line2 = line.substring(1); 164 | if ( 165 | line2.startsWith("line ") || 166 | line2.equals("prologue") || 167 | line2.startsWith("parameter") || 168 | line2.startsWith("local ") || 169 | line2.startsWith("end local ") 170 | ) { 171 | return false; 172 | } 173 | 174 | append(line); 175 | if (line2.equals("end method")) { 176 | return true; 177 | } 178 | if ( 179 | line2.startsWith("annotation ") || 180 | line2.equals("sparse-switch") || 181 | line2.startsWith("packed-switch ") || 182 | line2.startsWith("array-data ") 183 | ) { 184 | while(true) { 185 | line2 = nextAndAppend(); 186 | if (line2.startsWith(".end ")) { 187 | break; 188 | } 189 | } 190 | } 191 | return false; 192 | } 193 | 194 | private boolean processInstruction(String line) { 195 | if (mFirstInstruction) { 196 | mOut.append(".prologue\n"); 197 | mFirstInstruction = false; 198 | } 199 | mOut.append(".line ").append(mIt.nextIndex()).append('\n') 200 | .append(line).append('\n'); 201 | 202 | return false; 203 | } 204 | 205 | private String next() { 206 | return mIt.next().trim(); 207 | } 208 | 209 | private String nextAndAppend() { 210 | String line = next(); 211 | append(line); 212 | return line; 213 | } 214 | 215 | private void append(String append) { 216 | mOut.append(append).append('\n'); 217 | } 218 | 219 | private final ListIterator mIt; 220 | private final StringBuilder mOut; 221 | 222 | private boolean mFirstInstruction = true; 223 | private final Set mInitializedRegisters = new HashSet(); 224 | 225 | private static final Pattern REGISTER_INFO_PATTERN = 226 | Pattern.compile("((?:p|v)\\d+)=\\(([^)]+)\\);"); 227 | } 228 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/src/DexFileBuilder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.src; 18 | 19 | import brut.androlib.AndrolibException; 20 | import brut.androlib.mod.SmaliMod; 21 | import java.io.*; 22 | import org.antlr.runtime.RecognitionException; 23 | import org.jf.dexlib.CodeItem; 24 | import org.jf.dexlib.DexFile; 25 | import org.jf.dexlib.Util.ByteArrayAnnotatedOutput; 26 | 27 | /** 28 | * @author Ryszard Wiśniewski 29 | */ 30 | public class DexFileBuilder { 31 | public void addSmaliFile(File smaliFile) throws AndrolibException { 32 | try { 33 | addSmaliFile(new FileInputStream(smaliFile), 34 | smaliFile.getAbsolutePath()); 35 | } catch (FileNotFoundException ex) { 36 | throw new AndrolibException(ex); 37 | } 38 | } 39 | 40 | public void addSmaliFile(InputStream smaliStream, String name) 41 | throws AndrolibException { 42 | try { 43 | if (! SmaliMod.assembleSmaliFile( 44 | smaliStream, name, mDexFile, false, false, false)) { 45 | throw new AndrolibException( 46 | "Could not smali file: " + smaliStream); 47 | } 48 | } catch (IOException ex) { 49 | throw new AndrolibException(ex); 50 | } catch (RecognitionException ex) { 51 | throw new AndrolibException(ex); 52 | } 53 | } 54 | 55 | public void writeTo(File dexFile) throws AndrolibException { 56 | try { 57 | OutputStream out = new FileOutputStream(dexFile); 58 | out.write(getAsByteArray()); 59 | out.close(); 60 | } catch (IOException ex) { 61 | throw new AndrolibException( 62 | "Could not write dex to file: " + dexFile, ex); 63 | } 64 | } 65 | 66 | public byte[] getAsByteArray() { 67 | mDexFile.place(); 68 | for (CodeItem codeItem: mDexFile.CodeItemsSection.getItems()) { 69 | codeItem.fixInstructions(true, true); 70 | } 71 | 72 | mDexFile.place(); 73 | 74 | ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(); 75 | mDexFile.writeTo(out); 76 | byte[] bytes = out.toByteArray(); 77 | 78 | DexFile.calcSignature(bytes); 79 | DexFile.calcChecksum(bytes); 80 | 81 | return bytes; 82 | } 83 | 84 | private final DexFile mDexFile = new DexFile(); 85 | } 86 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/src/SmaliBuilder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.src; 18 | 19 | import brut.androlib.AndrolibException; 20 | import brut.androlib.res.util.ExtFile; 21 | import brut.directory.DirectoryException; 22 | import java.io.*; 23 | import java.util.List; 24 | import java.util.ListIterator; 25 | import java.util.logging.Logger; 26 | import org.apache.commons.io.IOUtils; 27 | 28 | /** 29 | * @author Ryszard Wiśniewski 30 | */ 31 | public class SmaliBuilder { 32 | 33 | public static void build(ExtFile smaliDir, File dexFile, boolean debug) 34 | throws AndrolibException { 35 | new SmaliBuilder(smaliDir, dexFile, debug).build(); 36 | } 37 | 38 | private SmaliBuilder(ExtFile smaliDir, File dexFile, boolean debug) { 39 | mSmaliDir = smaliDir; 40 | mDexFile = dexFile; 41 | mDebug = debug; 42 | } 43 | 44 | private void build() throws AndrolibException { 45 | try { 46 | mDexBuilder = new DexFileBuilder(); 47 | for (String fileName : mSmaliDir.getDirectory().getFiles(true)) { 48 | buildFile(fileName); 49 | } 50 | mDexBuilder.writeTo(mDexFile); 51 | } catch (IOException ex) { 52 | throw new AndrolibException(ex); 53 | } catch (DirectoryException ex) { 54 | throw new AndrolibException(ex); 55 | } 56 | } 57 | 58 | private void buildFile(String fileName) throws AndrolibException, 59 | IOException { 60 | File inFile = new File(mSmaliDir, fileName); 61 | InputStream inStream = new FileInputStream(inFile); 62 | 63 | if (fileName.endsWith(".smali")) { 64 | mDexBuilder.addSmaliFile(inFile); 65 | return; 66 | } 67 | if (! fileName.endsWith(".java")) { 68 | LOGGER.warning("Unknown file type, ignoring: " + inFile); 69 | return; 70 | } 71 | 72 | StringBuilder out = new StringBuilder(); 73 | List lines = IOUtils.readLines(inStream); 74 | 75 | if (!mDebug) { 76 | final String[] linesArray = lines.toArray(new String[0]); 77 | for (int i = 2; i < linesArray.length - 2; i++) { 78 | out.append(linesArray[i]).append('\n'); 79 | } 80 | } else { 81 | lines.remove(lines.size() - 1); 82 | lines.remove(lines.size() - 1); 83 | ListIterator it = lines.listIterator(2); 84 | 85 | out.append(".source \"").append(inFile.getName()).append("\"\n"); 86 | while (it.hasNext()) { 87 | String line = it.next().trim(); 88 | if (line.isEmpty() || line.charAt(0) == '#' || 89 | line.startsWith(".source")) { 90 | continue; 91 | } 92 | if (line.startsWith(".method ")) { 93 | it.previous(); 94 | DebugInjector.inject(it, out); 95 | continue; 96 | } 97 | 98 | out.append(line).append('\n'); 99 | } 100 | } 101 | mDexBuilder.addSmaliFile( 102 | IOUtils.toInputStream(out.toString()), fileName); 103 | } 104 | 105 | private final ExtFile mSmaliDir; 106 | private final File mDexFile; 107 | private final boolean mDebug; 108 | 109 | private DexFileBuilder mDexBuilder; 110 | 111 | 112 | private final static Logger LOGGER = 113 | Logger.getLogger(SmaliBuilder.class.getName()); 114 | } 115 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.src; 18 | 19 | import brut.androlib.AndrolibException; 20 | import java.io.File; 21 | import java.io.IOException; 22 | import org.jf.baksmali.baksmali; 23 | import org.jf.baksmali.main; 24 | import org.jf.dexlib.DexFile; 25 | 26 | /** 27 | * @author Ryszard Wiśniewski 28 | */ 29 | public class SmaliDecoder { 30 | 31 | public static void decode(File apkFile, File outDir, boolean debug) 32 | throws AndrolibException { 33 | new SmaliDecoder(apkFile, outDir, debug).decode(); 34 | } 35 | 36 | private SmaliDecoder(File apkFile, File outDir, boolean debug) { 37 | mApkFile = apkFile; 38 | mOutDir = outDir; 39 | mDebug = debug; 40 | } 41 | 42 | private void decode() throws AndrolibException { 43 | try { 44 | baksmali.disassembleDexFile(mApkFile.getAbsolutePath(), 45 | new DexFile(mApkFile), false, mOutDir.getAbsolutePath(), null, 46 | null, null, false, true, true, true, false, false, 47 | mDebug ? main.DIFFPRE : 0, false, false, null); 48 | } catch (IOException ex) { 49 | throw new AndrolibException(ex); 50 | } 51 | } 52 | 53 | private final File mApkFile; 54 | private final File mOutDir; 55 | private final boolean mDebug; 56 | } 57 | -------------------------------------------------------------------------------- /apktool-lib/src/main/java/brut/androlib/src/TypeName.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib.src; 18 | 19 | import brut.androlib.AndrolibException; 20 | import brut.util.Duo; 21 | import java.io.File; 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | /** 26 | * @author Ryszard Wiśniewski 27 | */ 28 | public class TypeName { 29 | public final String package_; 30 | public final String type; 31 | public final String innerType; 32 | public final int array; 33 | 34 | public TypeName(String type, int array) { 35 | this(null, type, null, array); 36 | } 37 | 38 | public TypeName(String package_, String type, String innerType, int array) { 39 | this.package_ = package_; 40 | this.type = type; 41 | this.innerType = innerType; 42 | this.array = array; 43 | } 44 | 45 | public String getShortenedName() { 46 | return getName("java.lang".equals(package_), isFileOwner()); 47 | } 48 | 49 | public String getName() { 50 | return getName(false, false); 51 | } 52 | 53 | public String getName(boolean excludePackage, boolean separateInner) { 54 | String name = 55 | (package_ == null || excludePackage ? "" : package_ + '.') + 56 | type + 57 | (innerType != null ? (separateInner ? '$' : '.') + innerType : ""); 58 | for (int i = 0; i < array; i++) { 59 | name += "[]"; 60 | } 61 | return name; 62 | } 63 | 64 | public String getJavaFilePath() { 65 | return getFilePath(isFileOwner()) + ".java"; 66 | } 67 | 68 | public String getSmaliFilePath() { 69 | return getFilePath(true) + ".smali"; 70 | } 71 | 72 | public String getFilePath(boolean separateInner) { 73 | return package_.replace('.', File.separatorChar) + File.separatorChar 74 | + type + (separateInner && isInner() ? "$" + innerType : ""); 75 | } 76 | 77 | public boolean isInner() { 78 | return innerType != null; 79 | } 80 | 81 | public boolean isArray() { 82 | return array != 0; 83 | } 84 | 85 | public boolean isFileOwner() { 86 | if (mIsFileOwner == null) { 87 | mIsFileOwner = true; 88 | if (isInner()) { 89 | char c = innerType.charAt(0); 90 | if (c < '0' || c > '9') { 91 | mIsFileOwner = false; 92 | } 93 | } 94 | } 95 | return mIsFileOwner; 96 | } 97 | 98 | @Override 99 | public String toString() { 100 | return getName(); 101 | } 102 | 103 | public static TypeName fromInternalName(String internal) 104 | throws AndrolibException { 105 | Duo duo = fetchFromInternalName(internal); 106 | if (duo.m2 != internal.length()) { 107 | throw new AndrolibException( 108 | "Invalid internal name: " + internal); 109 | } 110 | return duo.m1; 111 | } 112 | 113 | public static List listFromInternalName(String internal) 114 | throws AndrolibException { 115 | List types = new ArrayList(); 116 | while (! internal.isEmpty()) { 117 | Duo duo = fetchFromInternalName(internal); 118 | types.add(duo.m1); 119 | internal = internal.substring(duo.m2); 120 | } 121 | return types; 122 | } 123 | 124 | public static Duo fetchFromInternalName(String internal) 125 | throws AndrolibException { 126 | String origInternal = internal; 127 | int array = 0; 128 | 129 | boolean isArray = false; 130 | do { 131 | if (internal.isEmpty()) { 132 | throw new AndrolibException( 133 | "Invalid internal name: " + origInternal); 134 | } 135 | isArray = internal.charAt(0) == '['; 136 | if (isArray) { 137 | array++; 138 | internal = internal.substring(1); 139 | } 140 | } while (isArray); 141 | 142 | int length = array + 1; 143 | String package_ = null; 144 | String type = null; 145 | String innerType = null; 146 | switch (internal.charAt(0)) { 147 | case 'B': 148 | type = "byte"; 149 | break; 150 | case 'C': 151 | type = "char"; 152 | break; 153 | case 'D': 154 | type = "double"; 155 | break; 156 | case 'F': 157 | type = "float"; 158 | break; 159 | case 'I': 160 | type = "int"; 161 | break; 162 | case 'J': 163 | type = "long"; 164 | break; 165 | case 'S': 166 | type = "short"; 167 | break; 168 | case 'Z': 169 | type = "boolean"; 170 | break; 171 | case 'V': 172 | type = "void"; 173 | break; 174 | case 'L': 175 | int pos = internal.indexOf(';'); 176 | if (pos == -1) { 177 | throw new AndrolibException( 178 | "Invalid internal name: " + origInternal); 179 | } 180 | length += pos; 181 | internal = internal.substring(1, pos); 182 | 183 | pos = internal.lastIndexOf('/'); 184 | if (pos == -1) { 185 | package_ = ""; 186 | type = internal; 187 | } else { 188 | package_ = internal.substring(0, pos).replace('/', '.'); 189 | type = internal.substring(pos + 1); 190 | } 191 | 192 | pos = type.indexOf('$'); 193 | if (pos != -1) { 194 | innerType = type.substring(pos + 1); 195 | type = type.substring(0, pos); 196 | } 197 | break; 198 | default: 199 | throw new AndrolibException( 200 | "Invalid internal name: " + origInternal); 201 | } 202 | 203 | return new Duo( 204 | new TypeName(package_, type, innerType, array), length); 205 | } 206 | 207 | 208 | private Boolean mIsFileOwner; 209 | } 210 | -------------------------------------------------------------------------------- /apktool-lib/src/main/resources/brut/androlib/android-framework.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiCode/brut.apktool/10ccf153a24afc666818231b621bebca9d3e1686/apktool-lib/src/main/resources/brut/androlib/android-framework.jar -------------------------------------------------------------------------------- /apktool-lib/src/main/resources/brut/androlib/apktool.properties: -------------------------------------------------------------------------------- 1 | version = ${project.version} 2 | git.commit.id.abbrev = ${git.commit.id.abbrev} 3 | -------------------------------------------------------------------------------- /apktool-lib/src/test/java/brut/androlib/BuildAndDecodeTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib; 18 | 19 | import brut.androlib.res.util.ExtFile; 20 | import brut.common.BrutException; 21 | import brut.util.OS; 22 | import java.io.*; 23 | import java.util.logging.Logger; 24 | import org.custommonkey.xmlunit.*; 25 | import org.junit.*; 26 | import static org.junit.Assert.*; 27 | import org.xml.sax.SAXException; 28 | 29 | 30 | /** 31 | * @author Ryszard Wiśniewski 32 | */ 33 | public class BuildAndDecodeTest { 34 | 35 | @BeforeClass 36 | public static void beforeClass() throws BrutException { 37 | sTmpDir = new ExtFile(OS.createTempDirectory()); 38 | sTestOrigDir = new ExtFile(sTmpDir, "testapp-orig"); 39 | sTestNewDir = new ExtFile(sTmpDir, "testapp-new"); 40 | File testApk = new File(sTmpDir, "testapp.apk"); 41 | 42 | LOGGER.info("Unpacking testapp..."); 43 | TestUtils.copyResourceDir(BuildAndDecodeTest.class, 44 | "brut/apktool/testapp/", sTestOrigDir); 45 | 46 | LOGGER.info("Building testapp.apk..."); 47 | new Androlib().build(sTestOrigDir, testApk, false, false); 48 | 49 | LOGGER.info("Decoding testapp.apk..."); 50 | ApkDecoder apkDecoder = new ApkDecoder(testApk); 51 | apkDecoder.setOutDir(sTestNewDir); 52 | apkDecoder.decode(); 53 | } 54 | 55 | @AfterClass 56 | public static void afterClass() throws BrutException { 57 | OS.rmdir(sTmpDir); 58 | } 59 | 60 | @Test 61 | public void valuesArraysTest() throws BrutException { 62 | compareValuesFiles("values-mcc001/arrays.xml"); 63 | } 64 | 65 | @Test 66 | public void valuesBoolsTest() throws BrutException { 67 | compareValuesFiles("values-mcc001/bools.xml"); 68 | } 69 | 70 | @Test 71 | public void valuesColorsTest() throws BrutException { 72 | compareValuesFiles("values-mcc001/colors.xml"); 73 | } 74 | 75 | @Test 76 | public void valuesDimensTest() throws BrutException { 77 | compareValuesFiles("values-mcc001/dimens.xml"); 78 | } 79 | 80 | @Test 81 | public void valuesIdsTest() throws BrutException { 82 | compareValuesFiles("values-mcc001/ids.xml"); 83 | } 84 | 85 | @Test 86 | public void valuesIntegersTest() throws BrutException { 87 | compareValuesFiles("values-mcc001/integers.xml"); 88 | } 89 | 90 | @Test 91 | public void valuesStringsTest() throws BrutException { 92 | compareValuesFiles("values-mcc001/strings.xml"); 93 | } 94 | 95 | @Test 96 | public void valuesReferencesTest() throws BrutException { 97 | compareValuesFiles("values-mcc002/strings.xml"); 98 | } 99 | 100 | @Test 101 | public void crossTypeTest() throws BrutException { 102 | compareValuesFiles("values-mcc003/strings.xml"); 103 | compareValuesFiles("values-mcc003/integers.xml"); 104 | compareValuesFiles("values-mcc003/bools.xml"); 105 | } 106 | 107 | @Test 108 | public void xmlLiteralsTest() throws BrutException { 109 | compareXmlFiles("res/xml/literals.xml"); 110 | } 111 | 112 | @Test 113 | public void xmlReferencesTest() throws BrutException { 114 | compareXmlFiles("res/xml/references.xml"); 115 | } 116 | 117 | @Test 118 | public void qualifiersTest() throws BrutException { 119 | compareValuesFiles("values-mcc004-mnc4-en-rUS-sw100dp-w200dp-h300dp" + 120 | "-xlarge-long-land-television-night-xhdpi-finger-keyssoft-12key" + 121 | "-navhidden-dpad/strings.xml"); 122 | } 123 | 124 | private void compareValuesFiles(String path) throws BrutException { 125 | compareXmlFiles("res/" + path, 126 | new ElementNameAndAttributeQualifier("name")); 127 | } 128 | 129 | private void compareXmlFiles(String path) throws BrutException { 130 | compareXmlFiles(path, null); 131 | } 132 | 133 | private void compareXmlFiles(String path, 134 | ElementQualifier qualifier) throws BrutException { 135 | DetailedDiff diff; 136 | try { 137 | Reader control = new FileReader( 138 | new File(sTestOrigDir, path)); 139 | Reader test = new FileReader(new File(sTestNewDir, path)); 140 | 141 | diff = new DetailedDiff(new Diff(control, test)); 142 | } catch (SAXException ex) { 143 | throw new BrutException(ex); 144 | } catch (IOException ex) { 145 | throw new BrutException(ex); 146 | } 147 | 148 | if (qualifier != null) { 149 | diff.overrideElementQualifier(qualifier); 150 | } 151 | 152 | assertTrue(path + ": " + 153 | diff.getAllDifferences().toString(), diff.similar()); 154 | } 155 | 156 | private static ExtFile sTmpDir; 157 | private static ExtFile sTestOrigDir; 158 | private static ExtFile sTestNewDir; 159 | 160 | private final static Logger LOGGER = 161 | Logger.getLogger(BuildAndDecodeTest.class.getName()); 162 | } 163 | -------------------------------------------------------------------------------- /apktool-lib/src/test/java/brut/androlib/TestUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Ryszard Wiśniewski 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package brut.androlib; 18 | 19 | import brut.common.BrutException; 20 | import brut.directory.*; 21 | import java.io.*; 22 | import java.net.URL; 23 | import java.net.URLDecoder; 24 | import java.util.HashMap; 25 | import java.util.Map; 26 | import org.custommonkey.xmlunit.ElementQualifier; 27 | import org.w3c.dom.Element; 28 | import org.xmlpull.v1.*; 29 | 30 | /** 31 | * @author Ryszard Wiśniewski 32 | */ 33 | public abstract class TestUtils { 34 | 35 | public static Map parseStringsXml(File file) 36 | throws BrutException { 37 | try { 38 | XmlPullParser xpp = XmlPullParserFactory.newInstance().newPullParser(); 39 | xpp.setInput(new FileReader(file)); 40 | 41 | int eventType; 42 | String key = null; 43 | Map map = new HashMap(); 44 | while ((eventType = xpp.next()) != XmlPullParser.END_DOCUMENT) { 45 | switch (eventType) { 46 | case XmlPullParser.START_TAG: 47 | if ("string".equals(xpp.getName())) { 48 | int attrCount = xpp.getAttributeCount(); 49 | for (int i = 0; i < attrCount; i++) { 50 | if ("name".equals(xpp.getAttributeName(i))) { 51 | key = xpp.getAttributeValue(i); 52 | break; 53 | } 54 | } 55 | } 56 | break; 57 | case XmlPullParser.END_TAG: 58 | if ("string".equals(xpp.getName())) { 59 | key = null; 60 | } 61 | break; 62 | case XmlPullParser.TEXT: 63 | if (key != null) { 64 | map.put(key, xpp.getText()); 65 | } 66 | break; 67 | } 68 | } 69 | 70 | return map; 71 | } catch (IOException ex) { 72 | throw new BrutException(ex); 73 | } catch (XmlPullParserException ex) { 74 | throw new BrutException(ex); 75 | } 76 | } 77 | 78 | /* TODO: move to brut.util.Jar - it's not possible for now, because below 79 | * implementation uses brut.dir. I think I should merge all my projects to 80 | * single brut.common . 81 | */ 82 | public static void copyResourceDir(Class class_, String dirPath, File out) 83 | throws BrutException { 84 | if (! out.exists()) { 85 | out.mkdirs(); 86 | } 87 | copyResourceDir(class_, dirPath, new FileDirectory(out)); 88 | } 89 | 90 | public static void copyResourceDir(Class class_, String dirPath, 91 | Directory out) throws BrutException { 92 | if (class_ == null) { 93 | class_ = Class.class; 94 | } 95 | 96 | URL dirURL = class_.getClassLoader().getResource(dirPath); 97 | if (dirURL != null && dirURL.getProtocol().equals("file")) { 98 | DirUtil.copyToDir(new FileDirectory(dirURL.getFile()), out); 99 | return; 100 | } 101 | 102 | if (dirURL == null) { 103 | String className = class_.getName().replace(".", "/") + ".class"; 104 | dirURL = class_.getClassLoader().getResource(className); 105 | } 106 | 107 | 108 | if (dirURL.getProtocol().equals("jar")) { 109 | String jarPath; 110 | try { 111 | jarPath = URLDecoder.decode(dirURL.getPath().substring( 112 | 5, dirURL.getPath().indexOf("!")), "UTF-8"); 113 | } catch (UnsupportedEncodingException ex) { 114 | throw new BrutException(ex); 115 | } 116 | DirUtil.copyToDir(new FileDirectory(jarPath), out); 117 | } 118 | } 119 | 120 | 121 | public static class ResValueElementQualifier implements ElementQualifier { 122 | 123 | public boolean qualifyForComparison(Element control, Element test) { 124 | String controlType = control.getTagName(); 125 | if ("item".equals(controlType)) { 126 | controlType = control.getAttribute("type"); 127 | } 128 | 129 | String testType = test.getTagName(); 130 | if ("item".equals(testType)) { 131 | testType = test.getAttribute("type"); 132 | } 133 | 134 | return controlType.equals(testType) && control.getAttribute("name") 135 | .equals(test.getAttribute("name")); 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /apktool-lib/src/test/resources/brut/apktool/testapp/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /apktool-lib/src/test/resources/brut/apktool/testapp/apktool.yml: -------------------------------------------------------------------------------- 1 | version: 1.3.2 2 | apkFileName: testapp.apk 3 | isFrameworkApk: false 4 | usesFramework: 5 | ids: 6 | - 1 7 | -------------------------------------------------------------------------------- /apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TEST1 5 | TEST2 6 | TEST3 7 | %2$s foo %1$d 8 | 9 | 10 | -1 11 | 0 12 | 1 13 | 14 | 15 | 16 | true 17 | TEST 18 | 5 19 | 5.5 20 | 10.0sp 21 | #ff123456 22 | 23 | 24 | -------------------------------------------------------------------------------- /apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/bools.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | true 5 | 6 | -------------------------------------------------------------------------------- /apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ff123456 4 | 5 | -------------------------------------------------------------------------------- /apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10.0dip 4 | 10.0sp 5 | 10.0pt 6 | 10.0px 7 | 10.0mm 8 | 10.0in 9 | 10 | -------------------------------------------------------------------------------- /apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -1 4 | 0 5 | 1 6 | 7 | -------------------------------------------------------------------------------- /apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Lorem ipsum... 5 | \@ 6 | \? 7 | \#ff123456 8 | & 9 | "'" 10 | \" 11 | \u0005 12 | " foo bar " 13 | "foo 14 | bar" 15 | " foo"bar baz foo 16 | foobarbaz 17 | foobar"b 18 | az"foo 19 | %d of %d 20 | foo %d bar % 21 | %2$s foo %1$d 22 | %-e foo %,d 23 | %2$-e foo %1$,d 24 | %02d foo %01d 25 | %d foo %1 26 | %1% foo %2% 27 | 28 | -------------------------------------------------------------------------------- /apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc002/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | @string/test1 4 | @android:string/ok 5 | ?android:textStyle 6 | 7 | -------------------------------------------------------------------------------- /apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc003/bools.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | TEST 4 | 5 | -------------------------------------------------------------------------------- /apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc003/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | TEST 4 | 5 | -------------------------------------------------------------------------------- /apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc003/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | 5 5 | 6 | -------------------------------------------------------------------------------- /apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc004-mnc4-en-rUS-sw100dp-w200dp-h300dp-xlarge-long-land-television-night-xhdpi-finger-keyssoft-12key-navhidden-dpad/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /apktool-lib/src/test/resources/brut/apktool/testapp/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /apktool-lib/src/test/resources/brut/apktool/testapp/res/xml/literals.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | -------------------------------------------------------------------------------- /apktool-lib/src/test/resources/brut/apktool/testapp/res/xml/references.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | brut.apktool 6 | apktool-project 7 | 1.0-SNAPSHOT 8 | pom 9 | 10 | apktool 11 | http://github.com/brutall/brut.apktool 12 | 13 | 14 | UTF-8 15 | ${basedir} 16 | 17 | 18 | 19 | apktool-lib 20 | apktool-cli 21 | 22 | 23 | 24 | 25 | 26 | .. 27 | 28 | LICENSE 29 | NOTICE 30 | NOTICE-smali 31 | 32 | 33 | 34 | 35 | 36 | 37 | com.mycila.maven-license-plugin 38 | maven-license-plugin 39 | 40 |
${root.basedir}/src/templates/apache2.0-header.txt
41 | true 42 | 43 | .gitignore 44 | LICENSE 45 | NOTICE 46 | NOTICE-smali 47 | 48 |
49 | 50 | 51 | 52 | check 53 | 54 | 55 | 56 |
57 | 58 | org.apache.maven.plugins 59 | maven-compiler-plugin 60 | 61 | 6 62 | 6 63 | 64 | 65 |
66 | 67 | 68 | 69 | org.apache.maven.wagon 70 | wagon-webdav 71 | 1.0-beta-2 72 | 73 | 74 |
75 | 76 | 77 | 78 | android-apktool.googlecode.com 79 | http://android-apktool.googlecode.com/svn/m2-releases 80 | 81 | 82 | 83 | 84 | 85 | android-apktool.googlecode.com 86 | dav:https://android-apktool.googlecode.com/svn/m2-releases 87 | 88 | 89 | android-apktool.googlecode.com 90 | dav:https://android-apktool.googlecode.com/svn/m2-snapshots 91 | 92 | 93 |
94 | -------------------------------------------------------------------------------- /src/templates/apache2.0-header.txt: -------------------------------------------------------------------------------- 1 | Copyright 2011 Ryszard Wiśniewski 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | --------------------------------------------------------------------------------