diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..45cf84fd --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +./docker +./github +*.md diff --git a/.github/assets/sponsors/emerge-tools-vertical-black.svg b/.github/assets/sponsors/emerge-tools-vertical-black.svg new file mode 100644 index 00000000..7a27559f --- /dev/null +++ b/.github/assets/sponsors/emerge-tools-vertical-black.svg @@ -0,0 +1 @@ + diff --git a/.github/assets/sponsors/emerge-tools-vertical-white.svg b/.github/assets/sponsors/emerge-tools-vertical-white.svg new file mode 100644 index 00000000..310e2e32 --- /dev/null +++ b/.github/assets/sponsors/emerge-tools-vertical-white.svg @@ -0,0 +1 @@ + diff --git a/.github/assets/sponsors/sourcetoad-horizontal.svg b/.github/assets/sponsors/sourcetoad-horizontal.svg new file mode 100644 index 00000000..2c8c55e0 --- /dev/null +++ b/.github/assets/sponsors/sourcetoad-horizontal.svg @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.github/workflows/analyze.yml b/.github/workflows/analyze.yml index e646dce5..2fe6ed30 100644 --- a/.github/workflows/analyze.yml +++ b/.github/workflows/analyze.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Initialize CodeQL uses: github/codeql-action/init@v2 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 12247ab0..77d76a13 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,6 +10,7 @@ on: - 'brut.apktool/apktool-lib/src/main/resources/**' - 'brut.apktool/apktool-lib/src/test/**' - '.github/workflows/**' + - 'gradle/libs.versions.toml' - 'gradle/wrapper/**' - 'gradlew' - 'gradlew.bat' @@ -27,7 +28,7 @@ jobs: matrix: file: [ aapt_64, aapt2_64 ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Verify Executable run: ${{ env.BINARY_PATH }}/macosx/${{ matrix.file }} version - name: Output Static @@ -39,7 +40,7 @@ jobs: matrix: file: [ aapt, aapt_64, aapt2, aapt2_64 ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Verify Executable run: ${{ env.BINARY_PATH }}/linux/${{ matrix.file }} version - name: Output Static @@ -51,7 +52,7 @@ jobs: matrix: file: [ aapt.exe, aapt_64.exe, aapt2.exe, aapt2_64.exe ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Verify Executable run: ${{ env.BINARY_PATH }}/windows/${{ matrix.file }} version - name: Output Static @@ -70,13 +71,13 @@ jobs: os: [ ubuntu-latest, macOS-latest, windows-latest ] java: [ 8, 11, 17, 18, 19, 20 ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: distribution: 'zulu' java-version: ${{ matrix.java }} - name: Build and test - uses: gradle/gradle-build-action@v2.7.1 + uses: gradle/gradle-build-action@v2.9.0 with: arguments: build shadowJar proguard @@ -87,7 +88,7 @@ jobs: needs: - build-and-test-with-Java-8-and-later steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-java@v3 @@ -95,8 +96,9 @@ jobs: distribution: 'zulu' java-version: 17 - name: Build - uses: gradle/gradle-build-action@v2.7.1 + uses: gradle/gradle-build-action@v2.9.0 with: + dependency-graph: generate-and-submit arguments: build shadowJar proguard - name: Upload uses: actions/upload-artifact@v3 diff --git a/.github/workflows/docker-beta.yml b/.github/workflows/docker-beta.yml new file mode 100644 index 00000000..08ce80f4 --- /dev/null +++ b/.github/workflows/docker-beta.yml @@ -0,0 +1,47 @@ +name: Build and push docker (beta) + +on: + pull_request: + branches: [master] + types: [closed] + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ibotpeaches/apktool + +jobs: + + build_and_push: + name: Build and push + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v4 + + - name: Log in to the Container registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Get hash + id: hash + run: echo "APKTOOL_HASH=$(echo $GITHUB_SHA | cut -c 1-6)" >> $GITHUB_ENV + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Build and push Docker image + uses: docker/build-push-action@v4 + with: + context: . + platforms: linux/amd64 + file: ./docker/Dockerfile + push: true + tags: | + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.APKTOOL_HASH }} + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:beta + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:master diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml new file mode 100644 index 00000000..ed979bbc --- /dev/null +++ b/.github/workflows/docker-release.yml @@ -0,0 +1,50 @@ +name: Build and push docker (release) + +on: + release: + types: [published] + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ibotpeaches/apktool + +jobs: + build_and_push: + name: Build and push + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v4 + + - name: Log in to the Container registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Get hash + id: hash + run: echo "APKTOOL_HASH=$(echo $GITHUB_SHA | cut -c 1-6)" >> $GITHUB_ENV + + - name: Get release tag + id: tag + run: echo "APKTOOL_TAG=$(echo ${GITHUB_REF:-no-tag} |sed 's|refs/tags/||g')" >> $GITHUB_ENV + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Build and push Docker image + uses: docker/build-push-action@v4 + with: + context: . + platforms: linux/amd64 + file: ./docker/Dockerfile + push: true + tags: | + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.APKTOOL_HASH }} + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.APKTOOL_TAG }} + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:prod + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 3fad6652..592a77cb 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -16,7 +16,7 @@ jobs: validate-gradle-wrapper: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: distribution: 'zulu' diff --git a/INTERNAL.md b/INTERNAL.md index f8189920..13be348e 100644 --- a/INTERNAL.md +++ b/INTERNAL.md @@ -251,8 +251,8 @@ we aren't building the entire AOSP package, the initial build is to just see if We check out a certain tag or branch. Currently we use - * aapt2 - `master`. - * aapt1 - `master`. + * aapt2 - `android-14.0.0_r2`. + * aapt1 - `android-14.0.0_r2`. ### Including our modified `frameworks/base` package. diff --git a/README.md b/README.md index f0b88302..7d26c9f8 100644 --- a/README.md +++ b/README.md @@ -12,14 +12,6 @@ It is NOT intended for piracy and other non-legal uses. It could be used for loc - [Project Page](https://ibotpeaches.github.io/Apktool/) - [#apktool on libera.chat](https://web.libera.chat/) -#### Sponsored by - -* [Sourcetoad](https://www.sourcetoad.com/cool-tools/apktool/) - helping with a weekly sponsorship for continued improvement and maintenance of the project. - -#### IDE of Choice - -* [JetBrains IntelliJ](https://www.jetbrains.com/idea/) - #### Security Vulnerabilities If you discover a security vulnerability within Apktool, please send an e-mail to Connor Tumbleson at connor.tumbleson(at)gmail.com. All security vulnerabilities will be promptly addressed. @@ -29,9 +21,35 @@ If you discover a security vulnerability within Apktool, please send an e-mail t - [Downloads Mirror](https://connortumbleson.com/apktool/) - [How to Build](https://ibotpeaches.github.io/Apktool/build/) - [Documentation](https://ibotpeaches.github.io/Apktool/documentation/) +- [Use in Docker](./docker/README.md) - [Bug Reports](https://github.com/iBotPeaches/Apktool/issues) - [Changelog/Information](https://ibotpeaches.github.io/Apktool/changes/) - [XDA Post](https://forum.xda-developers.com/t/util-dec-2-2020-apktool-tool-for-reverse-engineering-apk-files.1755243/) - [Source (Github)](https://github.com/iBotPeaches/Apktool) - [Source (Bitbucket)](https://bitbucket.org/iBotPeaches/apktool/) + +## Sponsors + +Special thanks goes to the following sponsors: + +### Sourcetoad +[Sourcetoad](https://sourcetoad.com/) is an award-winning software and app development firm committed to the co-creation of technology solutions that solve complex business problems, delight users, and help our clients achieve their goals. + + + + + + + +### Emerge Tools + +[Emerge Tools](https://www.emergetools.com) is a suite of revolutionary products designed to supercharge mobile apps and the teams that build them. + + + + + + + + diff --git a/brut.apktool/apktool-cli/build.gradle.kts b/brut.apktool/apktool-cli/build.gradle.kts index 722e8924..f9070836 100644 --- a/brut.apktool/apktool-cli/build.gradle.kts +++ b/brut.apktool/apktool-cli/build.gradle.kts @@ -1,10 +1,9 @@ import proguard.gradle.ProGuardTask -val commonsCliVersion: String by rootProject.extra val apktoolVersion: String by rootProject.extra plugins { - id("com.github.johnrengelman.shadow") + alias(libs.plugins.shadow) application } @@ -14,12 +13,12 @@ plugins { buildscript { dependencies { // Proguard doesn't support plugin DSL - https://github.com/Guardsquare/proguard/issues/225 - classpath("com.guardsquare:proguard-gradle:7.3.2") + classpath(libs.proguard) } } dependencies { - implementation("commons-cli:commons-cli:$commonsCliVersion") + implementation(libs.commons.cli) implementation(project(":brut.apktool:apktool-lib")) } diff --git a/brut.apktool/apktool-cli/src/main/java/brut/apktool/Main.java b/brut.apktool/apktool-cli/src/main/java/brut/apktool/Main.java index 396406aa..e10ed961 100644 --- a/brut.apktool/apktool-cli/src/main/java/brut/apktool/Main.java +++ b/brut.apktool/apktool-cli/src/main/java/brut/apktool/Main.java @@ -55,7 +55,7 @@ public class Main { CommandLine commandLine; // load options - _Options(); + _options(); try { commandLine = parser.parse(allOptions, args, false); @@ -167,6 +167,34 @@ public class Main { if (cli.hasOption("m") || cli.hasOption("match-original")) { config.analysisMode = true; } + if (cli.hasOption("resm") || cli.hasOption("res-mode") || cli.hasOption("resolve-resources-mode")) { + String mode = cli.getOptionValue("resm"); + if (mode == null) { + mode = cli.getOptionValue("res-mode"); + } + if (mode == null) { + mode = cli.getOptionValue("resolve-resources-mode"); + } + + switch (mode) { + case "remove": + case "delete": + config.setDecodeResolveMode(Config.DECODE_RES_RESOLVE_REMOVE); + break; + case "dummy": + case "dummies": + config.setDecodeResolveMode(Config.DECODE_RES_RESOLVE_DUMMY); + break; + case "keep": + case "preserve": + config.setDecodeResolveMode(Config.DECODE_RES_RESOLVE_RETAIN); + break; + default: + System.err.println("Unknown resolve resources mode: " + mode); + System.err.println("Expect: 'remove', 'dummy' or 'keep'."); + System.exit(1); + } + } File outDir; if (cli.hasOption("o") || cli.hasOption("output")) { @@ -239,11 +267,13 @@ public class Main { if (cli.hasOption("nc") || cli.hasOption("no-crunch")) { config.noCrunch = true; } + if (cli.hasOption("use-aapt1")) { + config.useAapt2 = false; + } - // Temporary flag to enable the use of aapt2. This will transform in time to a use-aapt1 flag, which will be - // legacy and eventually removed. - if (cli.hasOption("use-aapt2")) { - config.useAapt2 = true; + if (cli.hasOption("use-aapt1") && cli.hasOption("use-aapt2")) { + System.err.println("You can only use one of --use-aapt1 or --use-aapt2."); + System.exit(1); } File outFile; @@ -300,9 +330,7 @@ public class Main { System.out.println(ApktoolProperties.getVersion()); } - private static void _Options() { - - // create options + private static void _options() { Option versionOption = Option.builder("version") .longOpt("version") .desc("Print the version.") @@ -409,6 +437,13 @@ public class Main { .desc("Skip changes detection and build all files.") .build(); + Option resolveResModeOption = Option.builder("resm") + .longOpt("resource-mode") + .desc("Sets the resolve resources mode. Possible values are: 'remove' (default), 'dummy' or 'keep'.") + .hasArg(true) + .argName("mode") + .build(); + Option aaptOption = Option.builder("a") .longOpt("aapt") .hasArg(true) @@ -416,9 +451,14 @@ public class Main { .desc("Load aapt from specified location.") .build(); + Option aapt1Option = Option.builder() + .longOpt("use-aapt1") + .desc("Use aapt binary instead of aapt2 during the build step.") + .build(); + Option aapt2Option = Option.builder() .longOpt("use-aapt2") - .desc("Use aapt2 binary instead of aapt1 during the build step.") + .desc("Use aapt2 binary instead of aapt during the build step.") .build(); Option originalOption = Option.builder("c") @@ -469,13 +509,14 @@ public class Main { decodeOptions.addOption(apiLevelOption); decodeOptions.addOption(noAssetOption); decodeOptions.addOption(forceManOption); + decodeOptions.addOption(resolveResModeOption); buildOptions.addOption(apiLevelOption); buildOptions.addOption(debugBuiOption); buildOptions.addOption(netSecConfOption); buildOptions.addOption(aaptOption); buildOptions.addOption(originalOption); - buildOptions.addOption(aapt2Option); + buildOptions.addOption(aapt1Option); buildOptions.addOption(noCrunchOption); } @@ -525,6 +566,7 @@ public class Main { allOptions.addOption(debugDecOption); allOptions.addOption(noDbgOption); allOptions.addOption(forceManOption); + allOptions.addOption(resolveResModeOption); allOptions.addOption(noAssetOption); allOptions.addOption(keepResOption); allOptions.addOption(debugBuiOption); @@ -533,6 +575,7 @@ public class Main { allOptions.addOption(originalOption); allOptions.addOption(verboseOption); allOptions.addOption(quietOption); + allOptions.addOption(aapt1Option); allOptions.addOption(aapt2Option); allOptions.addOption(noCrunchOption); allOptions.addOption(onlyMainClassesOption); @@ -547,7 +590,7 @@ public class Main { } private static void usage() { - _Options(); + _options(); HelpFormatter formatter = new HelpFormatter(); formatter.setWidth(120); @@ -592,11 +635,18 @@ public class Main { return; } - Handler handler = new Handler(){ + Handler handler = new Handler() { @Override public void publish(LogRecord record) { if (getFormatter() == null) { - setFormatter(new SimpleFormatter()); + setFormatter(new Formatter() { + @Override + public String format(LogRecord record) { + return record.getLevel().toString().charAt(0) + ": " + + record.getMessage() + + System.getProperty("line.separator"); + } + }); } try { @@ -616,6 +666,7 @@ public class Main { reportError(null, exception, ErrorManager.FORMAT_FAILURE); } } + @Override public void close() throws SecurityException {} @Override @@ -627,15 +678,6 @@ public class Main { if (verbosity == Verbosity.VERBOSE) { handler.setLevel(Level.ALL); logger.setLevel(Level.ALL); - } else { - handler.setFormatter(new Formatter() { - @Override - public String format(LogRecord record) { - return record.getLevel().toString().charAt(0) + ": " - + record.getMessage() - + System.getProperty("line.separator"); - } - }); } } diff --git a/brut.apktool/apktool-lib/build.gradle.kts b/brut.apktool/apktool-lib/build.gradle.kts index 349c5705..ab393934 100644 --- a/brut.apktool/apktool-lib/build.gradle.kts +++ b/brut.apktool/apktool-lib/build.gradle.kts @@ -1,13 +1,3 @@ -val baksmaliVersion: String by rootProject.extra -val smaliVersion: String by rootProject.extra -val xmlpullVersion: String by rootProject.extra -val guavaVersion: String by rootProject.extra -val commonsLangVersion: String by rootProject.extra -val commonsIoVersion: String by rootProject.extra -val commonsTextVersion: String by rootProject.extra -val junitVersion: String by rootProject.extra -val xmlunitVersion: String by rootProject.extra - val gitRevision: String by rootProject.extra val apktoolVersion: String by rootProject.extra @@ -53,16 +43,16 @@ dependencies { api(project(":brut.j.util")) api(project(":brut.j.common")) - implementation("com.android.tools.smali:smali-baksmali:$baksmaliVersion") - implementation("com.android.tools.smali:smali:$smaliVersion") - implementation("xpp3:xpp3:$xmlpullVersion") - implementation("com.google.guava:guava:$guavaVersion") - implementation("org.apache.commons:commons-lang3:$commonsLangVersion") - implementation("commons-io:commons-io:$commonsIoVersion") - implementation("org.apache.commons:commons-text:$commonsTextVersion") + implementation(libs.baksmali) + implementation(libs.smali) + implementation(libs.xmlpull) + implementation(libs.guava) + implementation(libs.commons.lang3) + implementation(libs.commons.io) + implementation(libs.commons.text) - testImplementation("junit:junit:$junitVersion") - testImplementation("org.xmlunit:xmlunit-legacy:$xmlunitVersion") + testImplementation(libs.junit) + testImplementation(libs.xmlunit) compileOnly(files(androidJarPath)) } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java index 31ad6b1b..492d52f6 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java @@ -92,6 +92,7 @@ public class ApkDecoder { } catch (BrutException ex) { throw new AndrolibException(ex); } + //noinspection ResultOfMethodCallIgnored outDir.mkdirs(); LOGGER.info("Using Apktool " + ApktoolProperties.getVersion() + " on " + mApkInfo.apkFileName); diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Config.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Config.java index 9aad41e1..2ffba37d 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Config.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Config.java @@ -23,6 +23,7 @@ import java.io.File; import java.util.logging.Logger; public class Config { + private static Config instance = null; private final static Logger LOGGER = Logger.getLogger(Config.class.getName()); public final static short DECODE_SOURCES_NONE = 0x0000; @@ -38,6 +39,10 @@ public class Config { public final static short DECODE_ASSETS_NONE = 0x0000; public final static short DECODE_ASSETS_FULL = 0x0001; + public final static short DECODE_RES_RESOLVE_REMOVE = 0x0000; + public final static short DECODE_RES_RESOLVE_DUMMY = 0x0001; + public final static short DECODE_RES_RESOLVE_RETAIN = 0x0002; + // Build options public boolean forceBuildAll = false; public boolean forceDeleteFramework = false; @@ -46,7 +51,7 @@ public class Config { public boolean verbose = false; public boolean copyOriginalFiles = false; public boolean updateFiles = false; - public boolean useAapt2 = false; + public boolean useAapt2 = true; public boolean noCrunch = false; public int forceApi = 0; @@ -55,6 +60,7 @@ public class Config { public short decodeResources = DECODE_RESOURCES_FULL; public short forceDecodeManifest = FORCE_DECODE_MANIFEST_NONE; public short decodeAssets = DECODE_ASSETS_FULL; + public short decodeResolveMode = DECODE_RES_RESOLVE_REMOVE; public int apiLevel = 0; public boolean analysisMode = false; public boolean forceDelete = false; @@ -72,8 +78,23 @@ public class Config { return this.useAapt2 || this.aaptVersion == 2; } - private Config() { + public boolean isDecodeResolveModeUsingDummies() { + return decodeResolveMode == DECODE_RES_RESOLVE_DUMMY; + } + public boolean isDecodeResolveModeRemoving() { + return decodeResolveMode == DECODE_RES_RESOLVE_REMOVE; + } + + private Config() { + instance = this; + } + + public static Config getInstance() { + if (instance == null) { + instance = new Config(); + } + return instance; } private void setDefaultFrameworkDirectory() { @@ -105,6 +126,13 @@ public class Config { decodeSources = mode; } + public void setDecodeResolveMode(short mode) throws AndrolibException { + if (mode != DECODE_RES_RESOLVE_REMOVE && mode != DECODE_RES_RESOLVE_DUMMY && mode != DECODE_RES_RESOLVE_RETAIN) { + throw new AndrolibException("Invalid decode resources mode"); + } + decodeResolveMode = mode; + } + public void setDecodeResources(short mode) throws AndrolibException { if (mode != DECODE_RESOURCES_NONE && mode != DECODE_RESOURCES_FULL) { throw new AndrolibException("Invalid decode resources mode"); diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/ApkInfo.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/ApkInfo.java index 99faee09..e7866360 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/ApkInfo.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/ApkInfo.java @@ -191,6 +191,7 @@ public class ApkInfo implements YamlSerializable { return ResConfigFlags.SDK_TIRAMISU; case "UPSIDEDOWNCAKE": case "UPSIDE_DOWN_CAKE": + return ResConfigFlags.SDK_UPSIDEDOWN_CAKE; case "VANILLAICECREAM": case "VANILLA_ICE_CREAM": return ResConfigFlags.SDK_DEVELOPMENT; diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java index 2c4ae0f8..1d27b77e 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java @@ -32,6 +32,7 @@ public class ResConfigFlags { public final byte keyboard; public final byte navigation; public final byte inputFlags; + public final byte grammaticalInflection; public final short screenWidth; public final short screenHeight; @@ -70,6 +71,7 @@ public class ResConfigFlags { keyboard = KEYBOARD_ANY; navigation = NAVIGATION_ANY; inputFlags = KEYSHIDDEN_ANY | NAVHIDDEN_ANY; + grammaticalInflection = GRAMMATICAL_GENDER_ANY; screenWidth = 0; screenHeight = 0; sdkVersion = 0; @@ -91,7 +93,7 @@ public class ResConfigFlags { public ResConfigFlags(short mcc, short mnc, char[] language, char[] region, byte orientation, byte touchscreen, int density, byte keyboard, byte navigation, - byte inputFlags, short screenWidth, short screenHeight, + byte inputFlags, byte grammaticalInflection, short screenWidth, short screenHeight, short sdkVersion, byte screenLayout, byte uiMode, short smallestScreenWidthDp, short screenWidthDp, short screenHeightDp, char[] localeScript, char[] localeVariant, @@ -149,6 +151,7 @@ public class ResConfigFlags { this.keyboard = keyboard; this.navigation = navigation; this.inputFlags = inputFlags; + this.grammaticalInflection = grammaticalInflection; this.screenWidth = screenWidth; this.screenHeight = screenHeight; this.sdkVersion = sdkVersion; @@ -198,6 +201,18 @@ public class ResConfigFlags { } ret.append(getLocaleString()); + switch (grammaticalInflection) { + case GRAMMATICAL_GENDER_NEUTER: + ret.append("-neuter"); + break; + case GRAMMATICAL_GENDER_FEMININE: + ret.append("-feminine"); + break; + case GRAMMATICAL_GENDER_MASCULINE: + ret.append("-masculine"); + break; + } + switch (screenLayout & MASK_LAYOUTDIR) { case SCREENLAYOUT_LAYOUTDIR_RTL: ret.append("-ldrtl"); @@ -421,6 +436,9 @@ public class ResConfigFlags { } private short getNaturalSdkVersionRequirement() { + if (grammaticalInflection != 0) { + return SDK_UPSIDEDOWN_CAKE; + } if ((uiMode & MASK_UI_MODE_TYPE) == UI_MODE_TYPE_VR_HEADSET || (colorMode & COLOR_WIDE_MASK) != 0 || ((colorMode & COLOR_HDR_MASK) != 0)) { return SDK_OREO; } @@ -550,6 +568,7 @@ public class ResConfigFlags { public final static byte SDK_S = 31; public final static byte SDK_S_V2 = 32; public final static byte SDK_TIRAMISU = 33; + public final static byte SDK_UPSIDEDOWN_CAKE = 34; // AOSP has this as 10,000 for dev purposes. // platform_frameworks_base/commit/c7a1109a1fe0771d4c9b572dcf178e2779fc4f2d @@ -590,6 +609,11 @@ public class ResConfigFlags { public final static short SCREENLAYOUT_ROUND_NO = 0x1; public final static short SCREENLAYOUT_ROUND_YES = 0x2; + public final static byte GRAMMATICAL_GENDER_ANY = 0; + public final static byte GRAMMATICAL_GENDER_NEUTER = 1; + public final static byte GRAMMATICAL_GENDER_FEMININE = 2; + public final static byte GRAMMATICAL_GENDER_MASCULINE = 3; + public final static byte KEYBOARD_ANY = 0; public final static byte KEYBOARD_NOKEYS = 1; public final static byte KEYBOARD_QWERTY = 2; diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResSpec.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResSpec.java index 338a4e9c..c12b8240 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResSpec.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResSpec.java @@ -105,6 +105,10 @@ public class ResResSpec { return mType; } + public boolean isDummyResSpec() { + return getName().startsWith("APKTOOL_DUMMY_"); + } + public void addResource(ResResource res) throws AndrolibException { addResource(res, false); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java index f4341d18..1c29ef4f 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java @@ -70,6 +70,10 @@ public class ResTable { return mConfig.analysisMode; } + public Config getConfig() { + return mConfig; + } + public boolean isMainPkgLoaded() { return mMainPkgLoaded; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTypeSpec.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTypeSpec.java index 72a97d50..f1b0cd24 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTypeSpec.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTypeSpec.java @@ -23,9 +23,11 @@ import java.util.*; public final class ResTypeSpec { public static final String RES_TYPE_NAME_ARRAY = "array"; - public static final String RES_TYPE_NAME_PLURALS = "plurals"; - public static final String RES_TYPE_NAME_STYLES = "style"; public static final String RES_TYPE_NAME_ATTR = "attr"; + public static final String RES_TYPE_NAME_ATTR_PRIVATE = "^attr-private"; + public static final String RES_TYPE_NAME_PLURALS = "plurals"; + public static final String RES_TYPE_NAME_STRING = "string"; + public static final String RES_TYPE_NAME_STYLES = "style"; private final String mName; private final Map mResSpecs = new LinkedHashMap<>(); @@ -46,7 +48,7 @@ public final class ResTypeSpec { } public boolean isString() { - return mName.equalsIgnoreCase("string"); + return mName.equalsIgnoreCase(RES_TYPE_NAME_STRING); } public ResResSpec getResSpec(String name) throws AndrolibException { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/arsc/FlagItem.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/arsc/FlagItem.java new file mode 100644 index 00000000..4c2dfb4a --- /dev/null +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/arsc/FlagItem.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2010 Ryszard Wiśniewski + * Copyright (C) 2010 Connor Tumbleson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package brut.androlib.res.data.arsc; + +import brut.androlib.exceptions.AndrolibException; +import brut.androlib.res.data.value.ResReferenceValue; + +public class FlagItem { + public final ResReferenceValue ref; + public final int flag; + public String value; + + public FlagItem(ResReferenceValue ref, int flag) { + this.ref = ref; + this.flag = flag; + } + + public String getValue() throws AndrolibException { + if (value == null) { + if (ref.referentIsNull()) { + return String.format("APKTOOL_MISSING_0x%08x", ref.getRawIntValue()); + } + value = ref.getReferent().getName(); + } + return value; + } +} diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java index 732d2e2d..2b1e2046 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java @@ -91,6 +91,4 @@ public class ResArrayValue extends ResBagValue implements ResValuesXmlSerializab private final ResScalarValue[] mItems; private final String[] AllowedArrayTypes = {"string", "integer"}; - - public static final int BAG_KEY_ARRAY_START = 0x02000000; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResAttr.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResAttr.java index f47b2243..982a78c5 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResAttr.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResAttr.java @@ -26,8 +26,7 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; public class ResAttr extends ResBagValue implements ResValuesXmlSerializable { - ResAttr(ResReferenceValue parentVal, int type, Integer min, Integer max, - Boolean l10n) { + ResAttr(ResReferenceValue parentVal, int type, Integer min, Integer max, Boolean l10n) { super(parentVal); mType = type; mMin = min; @@ -64,38 +63,39 @@ public class ResAttr extends ResBagValue implements ResValuesXmlSerializable { public static ResAttr factory(ResReferenceValue parent, Duo[] items, ResValueFactory factory, ResPackage pkg) throws AndrolibException { - - int type = ((ResIntValue) items[0].m2).getValue(); - int scalarType = type & 0xffff; Integer min = null, max = null; Boolean l10n = null; int i; for (i = 1; i < items.length; i++) { switch (items[i].m1) { case BAG_KEY_ATTR_MIN: - min = ((ResIntValue) items[i].m2).getValue(); + min = (items[i].m2).getRawIntValue(); continue; case BAG_KEY_ATTR_MAX: - max = ((ResIntValue) items[i].m2).getValue(); + max = (items[i].m2).getRawIntValue(); continue; case BAG_KEY_ATTR_L10N: - l10n = ((ResIntValue) items[i].m2).getValue() != 0; + l10n = (items[i].m2).getRawIntValue() != 0; continue; } break; } + // #2806 - Make sure we handle int-based values and not just ResIntValue + int rawValue = items[0].m2.getRawIntValue(); + int scalarType = rawValue & 0xffff; + if (i == items.length) { return new ResAttr(parent, scalarType, min, max, l10n); } - Duo[] attrItems = new Duo[items.length - i]; + Duo[] attrItems = new Duo[items.length - i]; int j = 0; for (; i < items.length; i++) { int resId = items[i].m1; pkg.addSynthesizedRes(resId); - attrItems[j++] = new Duo<>(factory.newReference(resId, null), (ResIntValue) items[i].m2); + attrItems[j++] = new Duo<>(factory.newReference(resId, null), items[i].m2); } - switch (type & 0xff0000) { + switch (rawValue & 0xff0000) { case TYPE_ENUM: return new ResEnumAttr(parent, scalarType, min, max, l10n, attrItems); case TYPE_FLAGS: @@ -145,7 +145,6 @@ public class ResAttr extends ResBagValue implements ResValuesXmlSerializable { private final Integer mMax; private final Boolean mL10n; - public static final int BAG_KEY_ATTR_TYPE = 0x01000000; private static final int BAG_KEY_ATTR_MIN = 0x01000001; private static final int BAG_KEY_ATTR_MAX = 0x01000002; private static final int BAG_KEY_ATTR_L10N = 0x01000003; diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBagValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBagValue.java index faa36f81..ea77c39e 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBagValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBagValue.java @@ -36,18 +36,15 @@ public class ResBagValue extends ResValue implements ResValuesXmlSerializable { ResResource res) throws IOException, AndrolibException { String type = res.getResSpec().getType().getName(); if ("style".equals(type)) { - new ResStyleValue(mParent, new Duo[0], null) - .serializeToResValuesXml(serializer, res); + new ResStyleValue(mParent, new Duo[0], null).serializeToResValuesXml(serializer, res); return; } if ("array".equals(type)) { - new ResArrayValue(mParent, new Duo[0]).serializeToResValuesXml( - serializer, res); + new ResArrayValue(mParent, new Duo[0]).serializeToResValuesXml(serializer, res); return; } if ("plurals".equals(type)) { - new ResPluralsValue(mParent, new Duo[0]).serializeToResValuesXml( - serializer, res); + new ResPluralsValue(mParent, new Duo[0]).serializeToResValuesXml(serializer, res); return; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEnumAttr.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEnumAttr.java index 1cffc3b6..d8ffced4 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEnumAttr.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEnumAttr.java @@ -25,10 +25,11 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.HashMap; import java.util.Map; +import java.util.logging.Logger; public class ResEnumAttr extends ResAttr { ResEnumAttr(ResReferenceValue parent, int type, Integer min, Integer max, - Boolean l10n, Duo[] items) { + Boolean l10n, Duo[] items) { super(parent, type, min, max, l10n); mItems = items; } @@ -46,15 +47,21 @@ public class ResEnumAttr extends ResAttr { } @Override - protected void serializeBody(XmlSerializer serializer, ResResource res) - throws AndrolibException, IOException { - for (Duo duo : mItems) { - int intVal = duo.m2.getValue(); + protected void serializeBody(XmlSerializer serializer, ResResource res) throws AndrolibException, IOException { + for (Duo duo : mItems) { + int intVal = duo.m2.getRawIntValue(); + + // #2836 - Support skipping items if the resource cannot be identified. ResResSpec m1Referent = duo.m1.getReferent(); + if (m1Referent == null && shouldRemoveUnknownRes()) { + LOGGER.fine(String.format("null enum reference: m1=0x%08x(%s), m2=0x%08x(%s)", + duo.m1.getRawIntValue(), duo.m1.getType(), duo.m2.getRawIntValue(), duo.m2.getType())); + continue; + } serializer.startTag(null, "enum"); serializer.attribute(null, "name", - m1Referent != null ? m1Referent.getName() : "@null" + m1Referent != null ? m1Referent.getName() : String.format("APKTOOL_MISSING_0x%08x", duo.m1.getRawIntValue()) ); serializer.attribute(null, "value", String.valueOf(intVal)); serializer.endTag(null, "enum"); @@ -65,8 +72,8 @@ public class ResEnumAttr extends ResAttr { String value2 = mItemsCache.get(value); if (value2 == null) { ResReferenceValue ref = null; - for (Duo duo : mItems) { - if (duo.m2.getValue() == value) { + for (Duo duo : mItems) { + if (duo.m2.getRawIntValue() == value) { ref = duo.m1; break; } @@ -79,6 +86,8 @@ public class ResEnumAttr extends ResAttr { return value2; } - private final Duo[] mItems; + private final Duo[] mItems; private final Map mItemsCache = new HashMap<>(); + + private static final Logger LOGGER = Logger.getLogger(ResEnumAttr.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFlagsAttr.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFlagsAttr.java index d69ea509..c37519f6 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFlagsAttr.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFlagsAttr.java @@ -17,28 +17,31 @@ package brut.androlib.res.data.value; import brut.androlib.exceptions.AndrolibException; +import brut.androlib.res.data.ResResSpec; import brut.androlib.res.data.ResResource; +import brut.androlib.res.data.arsc.FlagItem; import brut.util.Duo; import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.Arrays; +import java.util.logging.Logger; public class ResFlagsAttr extends ResAttr { ResFlagsAttr(ResReferenceValue parent, int type, Integer min, Integer max, - Boolean l10n, Duo[] items) { + Boolean l10n, Duo[] items) { super(parent, type, min, max, l10n); mItems = new FlagItem[items.length]; for (int i = 0; i < items.length; i++) { - mItems[i] = new FlagItem(items[i].m1, items[i].m2.getValue()); + mItems[i] = new FlagItem(items[i].m1, items[i].m2.getRawIntValue()); } } @Override public String convertToResXmlFormat(ResScalarValue value) throws AndrolibException { - if(value instanceof ResReferenceValue) { + if (value instanceof ResReferenceValue) { return value.encodeAsResXml(); } if (!(value instanceof ResIntValue)) { @@ -70,13 +73,19 @@ public class ResFlagsAttr extends ResAttr { } @Override - protected void serializeBody(XmlSerializer serializer, ResResource res) - throws AndrolibException, IOException { + protected void serializeBody(XmlSerializer serializer, ResResource res) throws AndrolibException, IOException { for (FlagItem item : mItems) { + ResResSpec referent = item.ref.getReferent(); + + // #2836 - Support skipping items if the resource cannot be identified. + if (referent == null && shouldRemoveUnknownRes()) { + LOGGER.fine(String.format("null flag reference: 0x%08x(%s)", item.ref.getValue(), item.ref.getType())); + continue; + } + serializer.startTag(null, "flag"); serializer.attribute(null, "name", item.getValue()); - serializer.attribute(null, "value", - String.format("0x%08x", item.flag)); + serializer.attribute(null, "value", String.format("0x%08x", item.flag)); serializer.endTag(null, "flag"); } } @@ -130,24 +139,5 @@ public class ResFlagsAttr extends ResAttr { private FlagItem[] mZeroFlags; private FlagItem[] mFlags; - private static class FlagItem { - public final ResReferenceValue ref; - public final int flag; - public String value; - - public FlagItem(ResReferenceValue ref, int flag) { - this.ref = ref; - this.flag = flag; - } - - public String getValue() throws AndrolibException { - if (value == null) { - if (ref.referentIsNull()) { - return "@null"; - } - value = ref.getReferent().getName(); - } - return value; - } - } + private static final Logger LOGGER = Logger.getLogger(ResFlagsAttr.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResPluralsValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResPluralsValue.java index 50b3cda5..34e90a42 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResPluralsValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResPluralsValue.java @@ -25,10 +25,8 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; -public class ResPluralsValue extends ResBagValue implements - ResValuesXmlSerializable { - ResPluralsValue(ResReferenceValue parent, - Duo[] items) { +public class ResPluralsValue extends ResBagValue implements ResValuesXmlSerializable { + ResPluralsValue(ResReferenceValue parent, Duo[] items) { super(parent); mItems = new ResScalarValue[6]; @@ -59,6 +57,5 @@ public class ResPluralsValue extends ResBagValue implements private final ResScalarValue[] mItems; public static final int BAG_KEY_PLURALS_START = 0x01000004; - public static final int BAG_KEY_PLURALS_END = 0x01000009; private static final String[] QUANTITY_MAP = new String[] { "other", "zero", "one", "two", "few", "many" }; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java index 21978f77..30ee763b 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java @@ -81,6 +81,11 @@ public abstract class ResScalarValue extends ResIntBasedValue implements } } + // Dummy attributes should be with type attribute + if (res.getResSpec().isDummyResSpec()) { + item = true; + } + // Android does not allow values (false) for ids.xml anymore // https://issuetracker.google.com/issues/80475496 // But it decodes as a ResBoolean, which makes no sense. So force it to empty diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStringValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStringValue.java index e9ccc684..0cbb49b4 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStringValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStringValue.java @@ -25,7 +25,6 @@ import java.io.IOException; import java.util.regex.Pattern; public class ResStringValue extends ResScalarValue { - public ResStringValue(String value, int rawValue) { this(value, rawValue, "string"); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStyleValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStyleValue.java index ab8dc271..397431bf 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStyleValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStyleValue.java @@ -26,10 +26,8 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.logging.Logger; -public class ResStyleValue extends ResBagValue implements - ResValuesXmlSerializable { - ResStyleValue(ResReferenceValue parent, - Duo[] items, ResValueFactory factory) { +public class ResStyleValue extends ResBagValue implements ResValuesXmlSerializable { + ResStyleValue(ResReferenceValue parent, Duo[] items, ResValueFactory factory) { super(parent); mItems = new Duo[items.length]; @@ -53,7 +51,7 @@ public class ResStyleValue extends ResBagValue implements ResResSpec spec = mItem.m1.getReferent(); if (spec == null) { - LOGGER.fine(String.format("null reference: m1=0x%08x(%s), m2=0x%08x(%s)", + LOGGER.fine(String.format("null style reference: m1=0x%08x(%s), m2=0x%08x(%s)", mItem.m1.getRawIntValue(), mItem.m1.getType(), mItem.m2.getRawIntValue(), mItem.m2.getType())); continue; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValue.java index 1d12f497..dec2394e 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValue.java @@ -16,6 +16,10 @@ */ package brut.androlib.res.data.value; -public class ResValue { +import brut.androlib.Config; +public class ResValue { + public boolean shouldRemoveUnknownRes() { + return Config.getInstance().isDecodeResolveModeRemoving(); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java index 99e6811c..052621e0 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java @@ -80,39 +80,29 @@ public class ResValueFactory { return new ResStringValue(value, rawValue); } - public ResBagValue bagFactory(int parent, Duo[] items, ResTypeSpec resTypeSpec) throws AndrolibException { + public ResBagValue bagFactory(int parent, Duo[] items, ResTypeSpec resTypeSpec) + throws AndrolibException { ResReferenceValue parentVal = newReference(parent, null); if (items.length == 0) { return new ResBagValue(parentVal); } - int key = items[0].m1; - if (key == ResAttr.BAG_KEY_ATTR_TYPE) { - return ResAttr.factory(parentVal, items, this, mPackage); - } - String resTypeName = resTypeSpec.getName(); - // Android O Preview added an unknown enum for c. This is hardcoded as 0 for now. - if (ResTypeSpec.RES_TYPE_NAME_ARRAY.equals(resTypeName) - || key == ResArrayValue.BAG_KEY_ARRAY_START || key == 0) { - return new ResArrayValue(parentVal, items); + switch (resTypeName) { + case ResTypeSpec.RES_TYPE_NAME_ATTR: + case ResTypeSpec.RES_TYPE_NAME_ATTR_PRIVATE: + return ResAttr.factory(parentVal, items, this, mPackage); + case ResTypeSpec.RES_TYPE_NAME_ARRAY: + return new ResArrayValue(parentVal, items); + case ResTypeSpec.RES_TYPE_NAME_PLURALS: + return new ResPluralsValue(parentVal, items); + default: + if (resTypeName.startsWith(ResTypeSpec.RES_TYPE_NAME_STYLES)) { + return new ResStyleValue(parentVal, items, this); + } + throw new AndrolibException("unsupported res type name for bags. Found: " + resTypeName); } - - if (ResTypeSpec.RES_TYPE_NAME_PLURALS.equals(resTypeName) || - (key >= ResPluralsValue.BAG_KEY_PLURALS_START && key <= ResPluralsValue.BAG_KEY_PLURALS_END)) { - return new ResPluralsValue(parentVal, items); - } - - if (ResTypeSpec.RES_TYPE_NAME_ATTR.equals(resTypeName)) { - return new ResAttr(parentVal, 0, null, null, null); - } - - if (resTypeName.startsWith(ResTypeSpec.RES_TYPE_NAME_STYLES)) { - return new ResStyleValue(parentVal, items, this); - } - - throw new AndrolibException("unsupported res type name for bags. Found: " + resTypeName); } public ResReferenceValue newReference(int resID, String rawValue) { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java index 38c20897..75d2fe62 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java @@ -59,11 +59,11 @@ public class ARSCDecoder { mIn = new ExtCountingDataInput(new LittleEndianDataInputStream(arscStream)); mResTable = resTable; mKeepBroken = keepBroken; + mMissingResSpecMap = new LinkedHashMap<>(); } private ResPackage[] readResourceTable() throws IOException, AndrolibException { Set pkgs = new LinkedHashSet<>(); - ResTypeSpec typeSpec; int chunkNumber = 1; @@ -118,6 +118,10 @@ public class ARSCDecoder { } } + if (mResTable.getConfig().isDecodeResolveModeUsingDummies() && mPkg != null && mPkg.getResSpecCount() > 0) { + addMissingResSpecs(); + } + return pkgs.toArray(new ResPackage[0]); } @@ -207,7 +211,7 @@ public class ARSCDecoder { mHeader.checkForUnreadHeader(mIn); for (int i = 0; i < count; i++) { - LOGGER.fine(String.format("Skipping staged alias stagedId (%h) finalId: %h", mIn.readInt(), mIn.readInt())); + LOGGER.fine(String.format("Staged alias: 0x%08x -> 0x%08x", mIn.readInt(), mIn.readInt())); } } @@ -256,10 +260,16 @@ public class ARSCDecoder { private ResType readTableType() throws IOException, AndrolibException { checkChunkType(ARSCHeader.XML_TYPE_TYPE); int typeId = mIn.readUnsignedByte() - mTypeIdOffset; + + // #3311 - Some older applications have no TYPE_SPEC chunks, but still define TYPE chunks. if (mResTypeSpecs.containsKey(typeId)) { - mResId = (0xff000000 & mResId) | mResTypeSpecs.get(typeId).getId() << 16; mTypeSpec = mResTypeSpecs.get(typeId); + } else { + mTypeSpec = new ResTypeSpec(mTypeNames.getString(typeId - 1), typeId); + addTypeSpec(mTypeSpec); + mPkg.addType(mTypeSpec); } + mResId = (0xff000000 & mResId) | mTypeSpec.getId() << 16; int typeFlags = mIn.readByte(); mIn.skipBytes(2); // reserved @@ -270,16 +280,21 @@ public class ARSCDecoder { mHeader.checkForUnreadHeader(mIn); + boolean isOffset16 = (typeFlags & TABLE_TYPE_FLAG_OFFSET16) != 0; + boolean isSparse = (typeFlags & TABLE_TYPE_FLAG_SPARSE) != 0; + // Be sure we don't poison mResTable by marking the application as sparse // Only flag the ResTable as sparse if the main package is not loaded. - if ((typeFlags & 0x01) != 0 && !mResTable.isMainPkgLoaded()) { + if (isSparse && !mResTable.isMainPkgLoaded()) { mResTable.setSparseResources(true); } HashMap entryOffsetMap = new LinkedHashMap<>(); for (int i = 0; i < entryCount; i++) { - if ((typeFlags & 0x01) != 0) { + if (isSparse) { entryOffsetMap.put(mIn.readUnsignedShort(), mIn.readUnsignedShort()); + } else if (isOffset16) { + entryOffsetMap.put(i, mIn.readUnsignedShort()); } else { entryOffsetMap.put(i, mIn.readInt()); } @@ -295,11 +310,13 @@ public class ARSCDecoder { } mType = flags.isInvalid && !mKeepBroken ? null : mPkg.getOrCreateConfig(flags); + int noEntry = isOffset16 ? NO_ENTRY_OFFSET16 : NO_ENTRY; for (int i : entryOffsetMap.keySet()) { mResId = (mResId & 0xffff0000) | i; int offset = entryOffsetMap.get(i); - if (offset == NO_ENTRY) { + if (offset == noEntry) { + mMissingResSpecMap.put(mResId, typeId); continue; } @@ -315,6 +332,8 @@ public class ARSCDecoder { EntryData entryData = readEntryData(); if (entryData != null) { readEntry(entryData); + } else { + mMissingResSpecMap.put(mResId, typeId); } } @@ -329,17 +348,35 @@ public class ARSCDecoder { private EntryData readEntryData() throws IOException, AndrolibException { short size = mIn.readShort(); - if (size < 0) { - throw new AndrolibException("Entry size is under 0 bytes."); + short flags = mIn.readShort(); + + boolean isComplex = (flags & ENTRY_FLAG_COMPLEX) != 0; + boolean isCompact = (flags & ENTRY_FLAG_COMPACT) != 0; + + if (size < 0 && !isCompact) { + throw new AndrolibException("Entry size is under 0 bytes and not compactly packed."); } - short flags = mIn.readShort(); int specNamesId = mIn.readInt(); - if (specNamesId == NO_ENTRY) { + if (specNamesId == NO_ENTRY && !isCompact) { return null; } - ResValue value = (flags & ENTRY_FLAG_COMPLEX) == 0 ? readValue() : readComplexEntry(); + // #3366 - In a compactly packed entry, the key index is the size & type is higher 8 bits on flags. + // We assume a size of 8 bytes for compact entries and the specNamesId is the data itself encoded. + ResValue value; + if (isCompact) { + byte type = (byte) ((flags >> 8) & 0xFF); + value = readCompactValue(type, specNamesId); + + // To keep code below happy - we know if compact that the size has the key index encoded. + specNamesId = size; + } else if (isComplex) { + value = readComplexEntry(); + } else { + value = readValue(); + } + // #2824 - In some applications the res entries are duplicated with the 2nd being malformed. // AOSP skips this, so we will do the same. if (value == null) { @@ -402,6 +439,12 @@ public class ARSCDecoder { resId = mIn.readInt(); resValue = readValue(); + // #2824 - In some applications the res entries are duplicated with the 2nd being malformed. + // AOSP skips this, so we will do the same. + if (resValue == null) { + continue; + } + if (!(resValue instanceof ResScalarValue)) { resValue = new ResStringValue(resValue.toString(), resValue.getRawIntValue()); } @@ -411,6 +454,12 @@ public class ARSCDecoder { return factory.bagFactory(parent, items, mTypeSpec); } + private ResIntBasedValue readCompactValue(byte type, int data) throws AndrolibException { + return type == TypedValue.TYPE_STRING + ? mPkg.getValueFactory().factory(mTableStrings.getHTML(data), data) + : mPkg.getValueFactory().factory(type, data, null); + } + private ResIntBasedValue readValue() throws IOException, AndrolibException { int size = mIn.readShort(); if (size < 8) { @@ -464,11 +513,12 @@ public class ARSCDecoder { byte keyboard = 0; byte navigation = 0; byte inputFlags = 0; + byte grammaticalInflection = 0; if (size >= 20) { keyboard = mIn.readByte(); navigation = mIn.readByte(); inputFlags = mIn.readByte(); - mIn.skipBytes(1); // inputPad0 + grammaticalInflection = mIn.readByte(); read = 20; } @@ -526,6 +576,7 @@ public class ARSCDecoder { } int exceedingKnownSize = size - KNOWN_CONFIG_BYTES; + if (exceedingKnownSize > 0) { byte[] buf = new byte[exceedingKnownSize]; read += exceedingKnownSize; @@ -550,7 +601,7 @@ public class ARSCDecoder { return new ResConfigFlags(mcc, mnc, language, country, orientation, touchscreen, density, keyboard, navigation, - inputFlags, screenWidth, screenHeight, sdkVersion, + inputFlags, grammaticalInflection, screenWidth, screenHeight, sdkVersion, screenLayout, uiMode, smallestScreenWidthDp, screenWidthDp, screenHeightDp, localeScript, localeVariant, screenLayout2, colorMode, localeNumberingSystem, isInvalid, size); @@ -589,6 +640,30 @@ public class ARSCDecoder { mResTypeSpecs.put(resTypeSpec.getId(), resTypeSpec); } + private void addMissingResSpecs() throws AndrolibException { + for (int resId : mMissingResSpecMap.keySet()) { + int typeId = mMissingResSpecMap.get(resId); + String resName = "APKTOOL_DUMMY_" + Integer.toHexString(resId); + ResID id = new ResID(resId); + ResResSpec spec = new ResResSpec(id, resName, mPkg, mResTypeSpecs.get(typeId)); + + // If we already have this resID don't add it again. + if (! mPkg.hasResSpec(id)) { + mPkg.addResSpec(spec); + spec.getType().addResSpec(spec); + ResType resType = mPkg.getOrCreateConfig(new ResConfigFlags()); + + // We are going to make dummy attributes a null reference (@null) now instead of a boolean false. + // This is because aapt2 is stricter when it comes to what we can put in an application. + ResValue value = new ResReferenceValue(mPkg, 0, ""); + + ResResource res = new ResResource(resType, spec, value); + resType.addResource(res); + spec.addResource(res); + } + } + } + private ARSCHeader nextChunk() throws IOException { return mHeader = ARSCHeader.read(mIn); } @@ -614,15 +689,21 @@ public class ARSCDecoder { private ResType mType; private int mResId; private int mTypeIdOffset = 0; + private final HashMap mMissingResSpecMap; private final HashMap mResTypeSpecs = new HashMap<>(); private final static short ENTRY_FLAG_COMPLEX = 0x0001; private final static short ENTRY_FLAG_PUBLIC = 0x0002; private final static short ENTRY_FLAG_WEAK = 0x0004; + private final static short ENTRY_FLAG_COMPACT = 0x0008; + + private final static short TABLE_TYPE_FLAG_SPARSE = 0x01; + private final static short TABLE_TYPE_FLAG_OFFSET16 = 0x02; private static final int KNOWN_CONFIG_BYTES = 64; private static final int NO_ENTRY = 0xFFFFFFFF; + private static final int NO_ENTRY_OFFSET16 = 0xFFFF; private static final Logger LOGGER = Logger.getLogger(ARSCDecoder.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlPatcher.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlPatcher.java index 53cdf52c..0acd9328 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlPatcher.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlPatcher.java @@ -365,7 +365,7 @@ public final class ResXmlPatcher { * @throws SAXException * @throws ParserConfigurationException */ - private static Document loadDocument(File file) + public static Document loadDocument(File file) throws IOException, SAXException, ParserConfigurationException { DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); diff --git a/brut.apktool/apktool-lib/src/main/resources/brut/androlib/android-framework.jar b/brut.apktool/apktool-lib/src/main/resources/brut/androlib/android-framework.jar index 02854940..76974147 100644 Binary files a/brut.apktool/apktool-lib/src/main/resources/brut/androlib/android-framework.jar and b/brut.apktool/apktool-lib/src/main/resources/brut/androlib/android-framework.jar differ diff --git a/brut.apktool/apktool-lib/src/main/resources/prebuilt/linux/aapt b/brut.apktool/apktool-lib/src/main/resources/prebuilt/linux/aapt index 7036ca4e..ef14966a 100755 Binary files a/brut.apktool/apktool-lib/src/main/resources/prebuilt/linux/aapt and b/brut.apktool/apktool-lib/src/main/resources/prebuilt/linux/aapt differ diff --git a/brut.apktool/apktool-lib/src/main/resources/prebuilt/linux/aapt2 b/brut.apktool/apktool-lib/src/main/resources/prebuilt/linux/aapt2 index cc74c93a..4ba1e9fc 100755 Binary files a/brut.apktool/apktool-lib/src/main/resources/prebuilt/linux/aapt2 and b/brut.apktool/apktool-lib/src/main/resources/prebuilt/linux/aapt2 differ diff --git a/brut.apktool/apktool-lib/src/main/resources/prebuilt/linux/aapt2_64 b/brut.apktool/apktool-lib/src/main/resources/prebuilt/linux/aapt2_64 index 8705475f..a27b4b45 100755 Binary files a/brut.apktool/apktool-lib/src/main/resources/prebuilt/linux/aapt2_64 and b/brut.apktool/apktool-lib/src/main/resources/prebuilt/linux/aapt2_64 differ diff --git a/brut.apktool/apktool-lib/src/main/resources/prebuilt/linux/aapt_64 b/brut.apktool/apktool-lib/src/main/resources/prebuilt/linux/aapt_64 index eb11fef5..6129b00a 100755 Binary files a/brut.apktool/apktool-lib/src/main/resources/prebuilt/linux/aapt_64 and b/brut.apktool/apktool-lib/src/main/resources/prebuilt/linux/aapt_64 differ diff --git a/brut.apktool/apktool-lib/src/main/resources/prebuilt/macosx/aapt2_64 b/brut.apktool/apktool-lib/src/main/resources/prebuilt/macosx/aapt2_64 index a06b16f9..c9747ae5 100755 Binary files a/brut.apktool/apktool-lib/src/main/resources/prebuilt/macosx/aapt2_64 and b/brut.apktool/apktool-lib/src/main/resources/prebuilt/macosx/aapt2_64 differ diff --git a/brut.apktool/apktool-lib/src/main/resources/prebuilt/macosx/aapt_64 b/brut.apktool/apktool-lib/src/main/resources/prebuilt/macosx/aapt_64 index e5eaa78b..8c2b9ee4 100755 Binary files a/brut.apktool/apktool-lib/src/main/resources/prebuilt/macosx/aapt_64 and b/brut.apktool/apktool-lib/src/main/resources/prebuilt/macosx/aapt_64 differ diff --git a/brut.apktool/apktool-lib/src/main/resources/prebuilt/windows/aapt.exe b/brut.apktool/apktool-lib/src/main/resources/prebuilt/windows/aapt.exe index a45deb20..c50197f4 100755 Binary files a/brut.apktool/apktool-lib/src/main/resources/prebuilt/windows/aapt.exe and b/brut.apktool/apktool-lib/src/main/resources/prebuilt/windows/aapt.exe differ diff --git a/brut.apktool/apktool-lib/src/main/resources/prebuilt/windows/aapt2.exe b/brut.apktool/apktool-lib/src/main/resources/prebuilt/windows/aapt2.exe index 21a2b115..f8b25f07 100755 Binary files a/brut.apktool/apktool-lib/src/main/resources/prebuilt/windows/aapt2.exe and b/brut.apktool/apktool-lib/src/main/resources/prebuilt/windows/aapt2.exe differ diff --git a/brut.apktool/apktool-lib/src/main/resources/prebuilt/windows/aapt2_64.exe b/brut.apktool/apktool-lib/src/main/resources/prebuilt/windows/aapt2_64.exe index 8eb97245..0c8690e4 100755 Binary files a/brut.apktool/apktool-lib/src/main/resources/prebuilt/windows/aapt2_64.exe and b/brut.apktool/apktool-lib/src/main/resources/prebuilt/windows/aapt2_64.exe differ diff --git a/brut.apktool/apktool-lib/src/main/resources/prebuilt/windows/aapt_64.exe b/brut.apktool/apktool-lib/src/main/resources/prebuilt/windows/aapt_64.exe index 9f667d55..3c9e54b7 100755 Binary files a/brut.apktool/apktool-lib/src/main/resources/prebuilt/windows/aapt_64.exe and b/brut.apktool/apktool-lib/src/main/resources/prebuilt/windows/aapt_64.exe differ diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/BaseTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/BaseTest.java index acc8785c..12d3a351 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/BaseTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/BaseTest.java @@ -22,6 +22,7 @@ import brut.directory.ExtFile; import brut.directory.FileDirectory; import org.custommonkey.xmlunit.*; import org.w3c.dom.Document; +import org.w3c.dom.Element; import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; @@ -155,6 +156,17 @@ public class BaseTest { } } + protected static int getStringEntryCount(Document doc, String key) { + int count = 0; + Element resources = doc.getDocumentElement(); + for (int i = 0; i < resources.getChildNodes().getLength(); i++) { + if (resources.getChildNodes().item(i).getNodeName().equals(key)) { + count++; + } + } + return count; + } + protected static ExtFile sTmpDir; protected static ExtFile sTestOrigDir; protected static ExtFile sTestNewDir; diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/TestUtils.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/TestUtils.java index b2b9d2a5..855326ff 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/TestUtils.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/TestUtils.java @@ -18,15 +18,19 @@ package brut.androlib; import brut.androlib.exceptions.AndrolibException; import brut.androlib.res.Framework; +import brut.androlib.res.xml.ResXmlPatcher; import brut.common.BrutException; import brut.directory.DirUtil; import brut.directory.Directory; import brut.directory.FileDirectory; import brut.util.OS; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; +import javax.xml.parsers.ParserConfigurationException; import java.io.*; import java.net.URL; import java.net.URLDecoder; @@ -77,6 +81,14 @@ public abstract class TestUtils { } } + public static Document getDocumentFromFile(File file) throws BrutException { + try { + return ResXmlPatcher.loadDocument(file); + } catch (ParserConfigurationException | SAXException | IOException ex) { + throw new BrutException(ex); + } + } + public static void copyResourceDir(Class class_, String dirPath, File out) throws BrutException { if (!out.exists()) { out.mkdirs(); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoNotSparseTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoNotSparseTest.java index fbbc5b2d..e13deccd 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoNotSparseTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoNotSparseTest.java @@ -47,6 +47,7 @@ public class AndroidOreoNotSparseTest extends BaseTest { LOGGER.info("Building not_sparse.apk..."); Config config = Config.getDefaultConfig(); + config.useAapt2 = false; new ApkBuilder(config, sTestNewDir).build(testApk); } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoSparseTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoSparseTest.java index 0acfe7aa..94619167 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoSparseTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoSparseTest.java @@ -47,6 +47,7 @@ public class AndroidOreoSparseTest extends BaseTest { LOGGER.info("Building sparse.apk..."); Config config = Config.getDefaultConfig(); + config.useAapt2 = false; new ApkBuilder(config, sTestNewDir).build(testApk); } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/BuildAndDecodeJarTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/BuildAndDecodeJarTest.java index ef858865..2de35510 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/BuildAndDecodeJarTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/BuildAndDecodeJarTest.java @@ -16,10 +16,7 @@ */ package brut.androlib.aapt1; -import brut.androlib.ApkBuilder; -import brut.androlib.ApkDecoder; -import brut.androlib.BaseTest; -import brut.androlib.TestUtils; +import brut.androlib.*; import brut.directory.ExtFile; import brut.common.BrutException; import brut.util.OS; @@ -44,7 +41,9 @@ public class BuildAndDecodeJarTest extends BaseTest { LOGGER.info("Building testjar.jar..."); File testJar = new File(sTmpDir, "testjar.jar"); - new ApkBuilder(sTestOrigDir).build(testJar); + Config config = Config.getDefaultConfig(); + config.useAapt2 = false; + new ApkBuilder(config, sTestOrigDir).build(testJar); LOGGER.info("Decoding testjar.jar..."); ApkDecoder apkDecoder = new ApkDecoder(testJar); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/BuildAndDecodeTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/BuildAndDecodeTest.java index 99117e21..316b9644 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/BuildAndDecodeTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/BuildAndDecodeTest.java @@ -16,10 +16,7 @@ */ package brut.androlib.aapt1; -import brut.androlib.ApkBuilder; -import brut.androlib.ApkDecoder; -import brut.androlib.BaseTest; -import brut.androlib.TestUtils; +import brut.androlib.*; import brut.androlib.apk.ApkInfo; import brut.common.BrutException; import brut.directory.ExtFile; @@ -52,7 +49,9 @@ public class BuildAndDecodeTest extends BaseTest { LOGGER.info("Building testapp.apk..."); File testApk = new File(sTmpDir, "testapp.apk"); - new ApkBuilder(sTestOrigDir).build(testApk); + Config config = Config.getDefaultConfig(); + config.useAapt2 = false; + new ApkBuilder(config, sTestOrigDir).build(testApk); LOGGER.info("Decoding testapp.apk..."); ApkDecoder apkDecoder = new ApkDecoder(testApk); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/DebugTagRetainedTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/DebugTagRetainedTest.java index 1ae1d68c..9c63aa29 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/DebugTagRetainedTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/DebugTagRetainedTest.java @@ -48,6 +48,7 @@ public class DebugTagRetainedTest extends BaseTest { LOGGER.info("Building issue1235.apk..."); Config config = Config.getDefaultConfig(); + config.useAapt2 = false; config.debugMode = true; File testApk = new File(sTmpDir, "issue1235.apk"); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/DefaultBaksmaliVariableTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/DefaultBaksmaliVariableTest.java index 2173efcf..ccdd9b1c 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/DefaultBaksmaliVariableTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/DefaultBaksmaliVariableTest.java @@ -16,10 +16,7 @@ */ package brut.androlib.aapt1; -import brut.androlib.ApkBuilder; -import brut.androlib.ApkDecoder; -import brut.androlib.BaseTest; -import brut.androlib.TestUtils; +import brut.androlib.*; import brut.common.BrutException; import brut.directory.ExtFile; import brut.util.OS; @@ -46,7 +43,9 @@ public class DefaultBaksmaliVariableTest extends BaseTest { LOGGER.info("Building issue1481.jar..."); File testJar = new File(sTmpDir, "issue1481.jar"); - new ApkBuilder(sTestOrigDir).build(testJar); + Config config = Config.getDefaultConfig(); + config.useAapt2 = false; + new ApkBuilder(config, sTestOrigDir).build(testJar); LOGGER.info("Decoding issue1481.jar..."); ApkDecoder apkDecoder = new ApkDecoder(testJar); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/EmptyResourcesArscTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/EmptyResourcesArscTest.java index fc37fff9..240d3040 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/EmptyResourcesArscTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/EmptyResourcesArscTest.java @@ -50,6 +50,7 @@ public class EmptyResourcesArscTest { LOGGER.info("Building issue1730.apk..."); Config config = Config.getDefaultConfig(); + config.useAapt2 = false; new ApkBuilder(config, sTestNewDir).build(testApk); } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ExternalEntityTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/ExternalEntityTest.java similarity index 93% rename from brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ExternalEntityTest.java rename to brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/ExternalEntityTest.java index 60232eca..4c673958 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ExternalEntityTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/ExternalEntityTest.java @@ -14,12 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package brut.androlib.decode; +package brut.androlib.aapt1; -import brut.androlib.ApkBuilder; -import brut.androlib.ApkDecoder; -import brut.androlib.BaseTest; -import brut.androlib.TestUtils; +import brut.androlib.*; import brut.directory.ExtFile; import brut.common.BrutException; import brut.util.OS; @@ -43,7 +40,9 @@ public class ExternalEntityTest extends BaseTest { LOGGER.info("Building doctype.apk..."); File testApk = new File(sTestOrigDir, "doctype.apk"); - new ApkBuilder(sTestOrigDir).build(testApk); + Config config = Config.getDefaultConfig(); + config.useAapt2 = false; + new ApkBuilder(config, sTestOrigDir).build(testApk); LOGGER.info("Decoding doctype.apk..."); ApkDecoder apkDecoder = new ApkDecoder(testApk); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/LargeIntsInManifestTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/LargeIntsInManifestTest.java index 3481dc23..c8e4574c 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/LargeIntsInManifestTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/LargeIntsInManifestTest.java @@ -16,10 +16,7 @@ */ package brut.androlib.aapt1; -import brut.androlib.ApkBuilder; -import brut.androlib.ApkDecoder; -import brut.androlib.BaseTest; -import brut.androlib.TestUtils; +import brut.androlib.*; import brut.directory.ExtFile; import brut.common.BrutException; import brut.util.OS; @@ -54,8 +51,10 @@ public class LargeIntsInManifestTest extends BaseTest { apkDecoder.decode(outDir); // build issue767 + Config config = Config.getDefaultConfig(); + config.useAapt2 = false; ExtFile testApk = new ExtFile(sTmpDir, apk + ".out"); - new ApkBuilder(testApk).build(null); + new ApkBuilder(config, testApk).build(null); String newApk = apk + ".out" + File.separator + "dist" + File.separator + apk; // decode issue767 again diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/ProviderAttributeTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/ProviderAttributeTest.java index abc724dd..35a9ba9a 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/ProviderAttributeTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/ProviderAttributeTest.java @@ -16,10 +16,7 @@ */ package brut.androlib.aapt1; -import brut.androlib.ApkBuilder; -import brut.androlib.ApkDecoder; -import brut.androlib.BaseTest; -import brut.androlib.TestUtils; +import brut.androlib.*; import brut.directory.ExtFile; import brut.common.BrutException; import brut.util.OS; @@ -62,7 +59,9 @@ public class ProviderAttributeTest extends BaseTest { // build issue636 ExtFile testApk = new ExtFile(sTmpDir, apk + ".out"); - new ApkBuilder(testApk).build(null); + Config config = Config.getDefaultConfig(); + config.useAapt2 = false; + new ApkBuilder(config, testApk).build(null); String newApk = apk + ".out" + File.separator + "dist" + File.separator + apk; assertTrue(fileExists(newApk)); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/SharedLibraryTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/SharedLibraryTest.java index bc59d963..bc7b44f2 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/SharedLibraryTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/SharedLibraryTest.java @@ -81,6 +81,7 @@ public class SharedLibraryTest extends BaseTest { Config config = Config.getDefaultConfig(); config.frameworkDirectory = sTmpDir.getAbsolutePath(); config.frameworkTag = "shared"; + config.useAapt2 = false; // install library/framework new Framework(config).installFramework(new File(sTmpDir + File.separator + library)); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/UnknownCompressionTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/UnknownCompressionTest.java index dda7c3c5..5e087db2 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/UnknownCompressionTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/UnknownCompressionTest.java @@ -42,6 +42,7 @@ public class UnknownCompressionTest extends BaseTest { String apk = "deflated_unknowns.apk"; Config config = Config.getDefaultConfig(); config.frameworkDirectory = sTmpDir.getAbsolutePath(); + config.useAapt2 = false; sTestOrigDir = new ExtFile(sTmpDir, apk); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/BuildAndDecodeTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/BuildAndDecodeTest.java index d5720829..a0aa8048 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/BuildAndDecodeTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/BuildAndDecodeTest.java @@ -86,6 +86,12 @@ public class BuildAndDecodeTest extends BaseTest { compareValuesFiles("values-b+iw+660/strings.xml"); } + @Test + public void valuesGrammaticalGenderTest() throws BrutException { + compareValuesFiles("values-neuter/strings.xml"); + compareValuesFiles("values-feminine/strings.xml"); + } + @Test public void valuesBcp47LanguageScriptRegionVariantTest() throws BrutException { compareValuesFiles("values-b+ast+Latn+IT+AREVELA/strings.xml"); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/CompactResourceTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/CompactResourceTest.java new file mode 100644 index 00000000..5b4ad679 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/CompactResourceTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2010 Ryszard Wiśniewski + * Copyright (C) 2010 Connor Tumbleson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package brut.androlib.decode; + +import brut.androlib.*; +import brut.directory.ExtFile; +import brut.common.BrutException; +import brut.util.OS; +import java.io.File; +import java.io.IOException; + +import org.junit.*; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +import javax.xml.parsers.ParserConfigurationException; + +import static org.junit.Assert.*; + +public class CompactResourceTest extends BaseTest { + + @BeforeClass + public static void beforeClass() throws Exception { + TestUtils.cleanFrameworkFile(); + sTmpDir = new ExtFile(OS.createTempDirectory()); + TestUtils.copyResourceDir(CompactResourceTest.class, "decode/issue3366/", sTmpDir); + } + + @AfterClass + public static void afterClass() throws BrutException { + OS.rmdir(sTmpDir); + } + + @Test + public void checkIfDecodeSucceeds() throws BrutException, IOException, ParserConfigurationException, SAXException { + String apk = "issue3366.apk"; + File testApk = new File(sTmpDir, apk); + + // decode issue3366.apk + ApkDecoder apkDecoder = new ApkDecoder(testApk); + sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + ".out"); + + File outDir = new File(sTmpDir + File.separator + apk + ".out"); + apkDecoder.decode(outDir); + + Document doc = loadDocument(new File(sTestOrigDir + "/res/values/strings.xml")); + assertEquals(1002, getStringEntryCount(doc, "string")); + + Config config = Config.getDefaultConfig(); + LOGGER.info("Building duplicatedex.apk..."); + new ApkBuilder(config, sTestOrigDir).build(testApk); + } +} diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ResourceModeTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ResourceModeTest.java new file mode 100644 index 00000000..1fbe855d --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ResourceModeTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2010 Ryszard Wiśniewski + * Copyright (C) 2010 Connor Tumbleson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package brut.androlib.decode; + +import brut.androlib.ApkDecoder; +import brut.androlib.BaseTest; +import brut.androlib.Config; +import brut.androlib.TestUtils; +import brut.directory.ExtFile; +import brut.common.BrutException; +import brut.util.OS; +import java.io.File; +import java.io.IOException; + +import org.junit.*; +import org.w3c.dom.Document; + +import static org.junit.Assert.*; + +public class ResourceModeTest extends BaseTest { + + @BeforeClass + public static void beforeClass() throws Exception { + TestUtils.cleanFrameworkFile(); + sTmpDir = new ExtFile(OS.createTempDirectory()); + TestUtils.copyResourceDir(ResourceModeTest.class, "decode/issue2836/", sTmpDir); + } + + @AfterClass + public static void afterClass() throws BrutException { + OS.rmdir(sTmpDir); + } + + @Test + public void checkDecodingModeAsRemove() throws BrutException, IOException { + String apk = "issue2836.apk"; + + Config config = Config.getDefaultConfig(); + config.setDecodeResolveMode(Config.DECODE_RES_RESOLVE_REMOVE); + + // decode issue2836.apk + ApkDecoder apkDecoder = new ApkDecoder(config, new File(sTmpDir + File.separator + apk)); + sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + "remove.out"); + + File outDir = new File(sTmpDir + File.separator + apk + "remove.out"); + apkDecoder.decode(outDir); + + File stringsXml = new File(sTestOrigDir,"res/values/strings.xml"); + assertTrue(stringsXml.isFile()); + + File attrXml = new File(sTestOrigDir,"res/values/attrs.xml"); + Document attrDocument = TestUtils.getDocumentFromFile(attrXml); + assertEquals(3, attrDocument.getElementsByTagName("enum").getLength()); + + File colorXml = new File(sTestOrigDir,"res/values/colors.xml"); + Document colorDocument = TestUtils.getDocumentFromFile(colorXml); + assertEquals(8, colorDocument.getElementsByTagName("color").getLength()); + assertEquals(0, colorDocument.getElementsByTagName("item").getLength()); + + File publicXml = new File(sTestOrigDir,"res/values/public.xml"); + Document publicDocument = TestUtils.getDocumentFromFile(publicXml); + assertEquals(21, publicDocument.getElementsByTagName("public").getLength()); + } + + @Test + public void checkDecodingModeAsDummies() throws BrutException, IOException { + String apk = "issue2836.apk"; + + Config config = Config.getDefaultConfig(); + config.setDecodeResolveMode(Config.DECODE_RES_RESOLVE_DUMMY); + + // decode issue2836.apk + ApkDecoder apkDecoder = new ApkDecoder(config, new File(sTmpDir + File.separator + apk)); + sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + "dummies.out"); + + File outDir = new File(sTmpDir + File.separator + apk + "dummies.out"); + apkDecoder.decode(outDir); + + File stringsXml = new File(sTestOrigDir,"res/values/strings.xml"); + assertTrue(stringsXml.isFile()); + + File attrXml = new File(sTestOrigDir,"res/values/attrs.xml"); + Document attrDocument = TestUtils.getDocumentFromFile(attrXml); + assertEquals(4, attrDocument.getElementsByTagName("enum").getLength()); + + File colorXml = new File(sTestOrigDir,"res/values/colors.xml"); + Document colorDocument = TestUtils.getDocumentFromFile(colorXml); + assertEquals(8, colorDocument.getElementsByTagName("color").getLength()); + assertEquals(1, colorDocument.getElementsByTagName("item").getLength()); + + File publicXml = new File(sTestOrigDir,"res/values/public.xml"); + Document publicDocument = TestUtils.getDocumentFromFile(publicXml); + assertEquals(22, publicDocument.getElementsByTagName("public").getLength()); + } + + @Test + public void checkDecodingModeAsLeave() throws BrutException, IOException { + String apk = "issue2836.apk"; + + Config config = Config.getDefaultConfig(); + config.setDecodeResolveMode(Config.DECODE_RES_RESOLVE_RETAIN); + + // decode issue2836.apk + ApkDecoder apkDecoder = new ApkDecoder(config, new File(sTmpDir + File.separator + apk)); + sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + "leave.out"); + + File outDir = new File(sTmpDir + File.separator + apk + "leave.out"); + apkDecoder.decode(outDir); + + File stringsXml = new File(sTestOrigDir,"res/values/strings.xml"); + assertTrue(stringsXml.isFile()); + + File attrXml = new File(sTestOrigDir,"res/values/attrs.xml"); + Document attrDocument = TestUtils.getDocumentFromFile(attrXml); + assertEquals(4, attrDocument.getElementsByTagName("enum").getLength()); + + File colorXml = new File(sTestOrigDir,"res/values/colors.xml"); + Document colorDocument = TestUtils.getDocumentFromFile(colorXml); + assertEquals(8, colorDocument.getElementsByTagName("color").getLength()); + assertEquals(0, colorDocument.getElementsByTagName("item").getLength()); + + File publicXml = new File(sTestOrigDir,"res/values/public.xml"); + Document publicDocument = TestUtils.getDocumentFromFile(publicXml); + assertEquals(21, publicDocument.getElementsByTagName("public").getLength()); + } +} diff --git a/brut.apktool/apktool-lib/src/test/resources/aapt1/testapp/res/values-mcc001/arrays.xml b/brut.apktool/apktool-lib/src/test/resources/aapt1/testapp/res/values-mcc001/arrays.xml index 141babbe..697a79d5 100644 --- a/brut.apktool/apktool-lib/src/test/resources/aapt1/testapp/res/values-mcc001/arrays.xml +++ b/brut.apktool/apktool-lib/src/test/resources/aapt1/testapp/res/values-mcc001/arrays.xml @@ -35,4 +35,7 @@ res/ view/ + + MIICXAIBAAKBgQCjcGqTkOq0CR3rTx0ZSQSIdTrDrFAYl29611xN8aVgMQIWtDB/lD0W5TpKPuU9iaiG/sSn/VYt6EzN7Sr332jj7cyl2WrrHI6ujRswNy4HojMuqtfab5FFDpRmCuvl35fge18OvoQTJELhhJ1EvJ5KUeZiuJ3u3YyMnxxXzLuKbQIDAQABAoGAPrNDz7TKtaLBvaIuMaMXgBopHyQd3jFKbT/tg2Fu5kYm3PrnmCoQfZYXFKCoZUFIS/G1FBVWWGpD/MQ9tbYZkKpwuH+t2rGndMnLXiTC296/s9uix7gsjnT4Naci5N6EN9pVUBwQmGrYUTHFc58ThtelSiPARX7LSU2ibtJSv8ECQQDWBRrrAYmbCUN7ra0DFT6SppaDtvvuKtb+mUeKbg0B8U4y4wCIK5GH8EyQSwUWcXnNBO05rlUPbifsDLv/u82lAkEAw39sTJ0KmJJyaChqvqAJ8guulKlgucQJ0Et9ppZyet9iVwNKX/aW9UlwGBMQdafQ36nd1QMEA8AbAw4D+hw/KQJBANJbHDUGQtk2hrSmZNoV5HXB9Uiq7v4N71k5ER8XwgM5yVGs2tX8dMM3RhnBEtQXXs9LW1uJZSOQcv7JGXNnhN0CQBZenzrJAWxh3XtznHtBfsHWelyCYRIAj4rpCHCmaGUM6IjCVKFUawOYKp5mmAyObkUZf8ue87emJLEdynC1CLkCQHduNjP1hemAGWrd6v8BHhE3kKtcK6KHsPvJR5dOfzbdHAqVePERhISfN6cwZt5p8B3/JUwSR8el66DF7Jm57BM= + diff --git a/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/res/values-feminine/strings.xml b/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/res/values-feminine/strings.xml new file mode 100644 index 00000000..df52143f --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/res/values-feminine/strings.xml @@ -0,0 +1,4 @@ + + + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/res/values-neuter/strings.xml b/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/res/values-neuter/strings.xml new file mode 100644 index 00000000..df52143f --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/res/values-neuter/strings.xml @@ -0,0 +1,4 @@ + + + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/resources/decode/issue2836/issue2836.apk b/brut.apktool/apktool-lib/src/test/resources/decode/issue2836/issue2836.apk new file mode 100644 index 00000000..367304fd Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/decode/issue2836/issue2836.apk differ diff --git a/brut.apktool/apktool-lib/src/test/resources/decode/issue3366/issue3366.apk b/brut.apktool/apktool-lib/src/test/resources/decode/issue3366/issue3366.apk new file mode 100644 index 00000000..0bccfb8b Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/decode/issue3366/issue3366.apk differ diff --git a/brut.j.dir/build.gradle.kts b/brut.j.dir/build.gradle.kts index 2792b209..d4865c80 100644 --- a/brut.j.dir/build.gradle.kts +++ b/brut.j.dir/build.gradle.kts @@ -1,7 +1,5 @@ -val commonsIoVersion: String by rootProject.extra - dependencies { implementation(project(":brut.j.common")) implementation(project(":brut.j.util")) - implementation("commons-io:commons-io:$commonsIoVersion") + implementation(libs.commons.io) } diff --git a/brut.j.dir/src/main/java/brut/directory/AbstractDirectory.java b/brut.j.dir/src/main/java/brut/directory/AbstractDirectory.java index ef7ddf50..ecd5da77 100644 --- a/brut.j.dir/src/main/java/brut/directory/AbstractDirectory.java +++ b/brut.j.dir/src/main/java/brut/directory/AbstractDirectory.java @@ -267,8 +267,7 @@ public abstract class AbstractDirectory implements Directory { protected abstract AbstractDirectory createDirLocal(String name) throws DirectoryException; protected abstract void removeFileLocal(String name); - - private class ParsedPath { + private static class ParsedPath { public final String dir; public final String subpath; public ParsedPath(String dir, String subpath) { @@ -277,7 +276,7 @@ public abstract class AbstractDirectory implements Directory { } } - private class SubPath { + private static class SubPath { public final AbstractDirectory dir; public final String path; diff --git a/brut.j.util/build.gradle.kts b/brut.j.util/build.gradle.kts index b717cd3b..1f3a9234 100644 --- a/brut.j.util/build.gradle.kts +++ b/brut.j.util/build.gradle.kts @@ -1,8 +1,5 @@ -val commonsIoVersion: String by rootProject.extra -val guavaVersion: String by rootProject.extra - dependencies { implementation(project(":brut.j.common")) - implementation("commons-io:commons-io:$commonsIoVersion") - implementation("com.google.guava:guava:$guavaVersion") + implementation(libs.commons.io) + implementation(libs.guava) } diff --git a/build.gradle.kts b/build.gradle.kts index ae8ad636..268ff87c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,18 +1,7 @@ import java.io.ByteArrayOutputStream -val baksmaliVersion by extra("3.0.3") -val commonsCliVersion by extra("1.5.0") -val commonsIoVersion by extra("2.13.0") -val commonsLangVersion by extra("3.13.0") -val commonsTextVersion by extra("1.10.0") -val guavaVersion by extra("32.0.1-jre") -val junitVersion by extra("4.13.2") -val smaliVersion by extra("3.0.3") -val xmlpullVersion by extra("1.1.4c") -val xmlunitVersion by extra("2.9.1") - -val version = "2.8.2" -val suffix = "6" +val version = "2.9.1" +val suffix = "SNAPSHOT" // Strings embedded into the build. var gitRevision by extra("") @@ -66,17 +55,12 @@ if ("release" !in gradle.startParameter.taskNames) { } plugins { - id("com.github.johnrengelman.shadow") version "8.1.1" + alias(libs.plugins.shadow) `java-library` `maven-publish` signing } -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - tasks.withType { options.compilerArgs.add("-Xlint:-options") options.compilerArgs.add("--release 8") @@ -95,6 +79,11 @@ subprojects { apply(plugin = "java") apply(plugin = "java-library") + java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + val mavenProjects = arrayOf("apktool-lib", "apktool-cli", "brut.j.common", "brut.j.util", "brut.j.dir") if (project.name in mavenProjects) { diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 00000000..a4bc12ae --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,62 @@ +# BUILDER +# ===================================================== +FROM ibm-semeru-runtimes:open-17-jdk-jammy AS builder + +COPY . /build +WORKDIR /build +RUN \ + # Apktool + ./gradlew build shadowJar proguard + +RUN \ + # Relocate for easier copying + JAR=$(find /build/brut.apktool/apktool-cli/build/libs/apktool-*.jar |grep -v cli-all) &&\ + mv ${JAR} /build/apktool.jar + +# BASE +# ===================================================== +FROM ibm-semeru-runtimes:open-17-jre-jammy AS base + +# Latest version as of 2023.10.01 +# Ref: https://developer.android.com/studio/index.html#command-line-tools-only +ARG COMMAND_LINE_TOOLS_VERSION=10406996 + +ARG ANDROID_SDK_ROOT=/opt/android-sdk + +COPY --from=builder /build/apktool.jar /usr/local/bin/apktool.jar +COPY --from=builder /build/scripts/linux/apktool /usr/local/bin/apktool + +RUN \ + # Update + apt-get update &&\ + \ + # Apktool final setup + chmod +x /usr/local/bin/apktool /usr/local/bin/apktool.jar &&\ + \ + # Android SDK + apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + zipalign \ + git \ + openssl \ + wget \ + unzip \ + sdkmanager &&\ + curl -o /tmp/tools.zip https://dl.google.com/android/repository/commandlinetools-linux-${COMMAND_LINE_TOOLS_VERSION}_latest.zip &&\ + mkdir -p ${ANDROID_SDK_ROOT}/cmdline-tools &&\ + unzip -q /tmp/tools.zip -d ${ANDROID_SDK_ROOT}/cmdline-tools &&\ + mv ${ANDROID_SDK_ROOT}/cmdline-tools/cmdline-tools ${ANDROID_SDK_ROOT}/cmdline-tools/latest &&\ + rm -v /tmp/tools.zip &&\ + mkdir -p /root/.android/ && touch /root/.android/repositories.cfg &&\ + yes | sdkmanager --licenses &&\ + export BUILD_TOOLS_VERSION=$(sdkmanager --list |grep build-tools |grep -v rc |awk '{print $1}' |sed 's/build-tools;//g' |sort |tail -n1) &&\ + sdkmanager --install "build-tools;${BUILD_TOOLS_VERSION}" &&\ + ln -s ${ANDROID_SDK_ROOT}/build-tools/${BUILD_TOOLS_VERSION} /opt/bin &&\ + \ + # Cleanup + rm -rf /var/lib/apt/lists/* + +ENV PATH ${PATH}:/opt/bin + +CMD ["/usr/local/bin/apktool"] \ No newline at end of file diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 00000000..f0bdbdf1 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,27 @@ +# Apktool in Docker +We provide an easy way to leverage `apktool`, along with common Android tools such as `zipalign` and `apksigner`, all from within Docker. + +## Building the Docker image +To use the image, pull or build with the included Dockerfile: +```bash +docker pull ghcr.io/ibotpeaches/apktool:latest +# OR +docker build -t apktool:local . +``` + +## Using the Docker image +The best way to use the image is to create aliases to run the internal commands. Replace `ghcr.io/ibotpeaches/apktool:latest` with `apktool:local` if you have built the image locally. +```bash +alias apktool="docker run --rm -ti --name=apktool -v \"${PWD}:${PWD}\" -w \"${PWD}\" ghcr.io/ibotpeaches/apktool:latest apktool" +alias zipalign="docker run --rm -ti --name=zipalign -v \"${PWD}:${PWD}\" -w \"${PWD}\" ghcr.io/ibotpeaches/apktool:latest zipalign" +alias apksigner="docker run --rm -ti --name=apksigner -v \"${PWD}:${PWD}\" -w \"${PWD}\" ghcr.io/ibotpeaches/apktool:latest apksigner" +``` + +## Running the commands +You can then utilize these commands as you would if they were natively installed: +```bash +apktool d My.apk -o MyFolder +apktool b MyFolder -o MyNew.apk +zipalign -p -f 4 MyNew.apk MyNewAligned.apk +apksigner sign --ks My.keystore MyNewAligned.apk +``` diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 00000000..611f22ae --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,29 @@ +[versions] +baksmali = "3.0.3" +commons_io = "2.14.0" +commons_cli = "1.5.0" +commons_lang3 = "3.13.0" +commons_text = "1.10.0" +guava = "32.0.1-jre" +junit = "4.13.2" +proguard = "7.3.2" +shadow = "8.1.1" +smali = "3.0.3" +xmlpull = "1.1.4c" +xmlunit = "2.9.1" + +[libraries] +baksmali = { module = "com.android.tools.smali:smali-baksmali", version.ref = "baksmali" } +commons_cli = { module = "commons-cli:commons-cli", version.ref = "commons_cli"} +commons_io = { module = "commons-io:commons-io", version.ref = "commons_io" } +commons_lang3 = { module = "org.apache.commons:commons-lang3", version.ref = "commons_lang3" } +commons_text = { module = "org.apache.commons:commons-text", version.ref = "commons_text" } +guava = { module = "com.google.guava:guava", version.ref = "guava" } +junit = { module = "junit:junit", version.ref = "junit" } +proguard = { module = "com.guardsquare:proguard-gradle", version.ref = "proguard" } +smali = { module = "com.android.tools.smali:smali", version.ref = "smali" } +xmlpull = { module = "xpp3:xpp3", version.ref = "xmlpull" } +xmlunit = { module = "org.xmlunit:xmlunit-legacy", version.ref = "xmlunit" } + +[plugins] +shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" } diff --git a/scripts/windows/apktool.bat b/scripts/windows/apktool.bat index 08adf635..ee2d4da3 100755 --- a/scripts/windows/apktool.bat +++ b/scripts/windows/apktool.bat @@ -39,4 +39,4 @@ if "%ATTR:~0,1%"=="-" if "%~x1"==".apk" ( "%java_exe%" -jar -Xmx1024M -Duser.language=en -Dfile.encoding=UTF8 -Djdk.util.zip.disableZip64ExtraFieldValidation=true -Djdk.nio.zipfs.allowDotZipEntry=true "%~dp0%BASENAME%%max%.jar" %fastCommand% %* rem Pause when ran non interactively -for /f "tokens=2" %%# in ("%cmdcmdline%") do if /i "%%#" equ "/c" pause +for %%i in (%cmdcmdline%) do if /i "%%~i"=="/c" pause & exit /b diff --git a/settings.gradle.kts b/settings.gradle.kts index 9fffe0ae..db82a26b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,2 +1,8 @@ rootProject.name = "apktool-cli" include("brut.j.common", "brut.j.util", "brut.j.dir", "brut.apktool:apktool-lib", "brut.apktool:apktool-cli") + +dependencyResolutionManagement { + versionCatalogs { + create("libs") {} + } +}