diff --git a/.github/workflows/gradle_build.yml b/.github/workflows/gradle_build.yml index 1a220dc4..dab4ddc7 100644 --- a/.github/workflows/gradle_build.yml +++ b/.github/workflows/gradle_build.yml @@ -5,6 +5,7 @@ on: branches: [ "master" ] pull_request: branches: [ "master" ] + workflow_dispatch: # Allows manual execution from the GitHub interface permissions: contents: read @@ -21,6 +22,7 @@ jobs: with: java-version: '8' distribution: 'temurin' + cache: 'gradle' - uses: gradle/actions/setup-gradle@v4 - name: Build with Gradle run: ./gradlew fatJar diff --git a/src/main/java/com/reandroid/apkeditor/Main.java b/src/main/java/com/reandroid/apkeditor/Main.java index 35a98566..8c8e2add 100644 --- a/src/main/java/com/reandroid/apkeditor/Main.java +++ b/src/main/java/com/reandroid/apkeditor/Main.java @@ -42,110 +42,127 @@ RefactorOptions.class, ProtectorOptions.class, InfoOptions.class - } -) -public class Main { - - private boolean mEmptyOption; - private Options mOptions; - private int mExitCode; + } +) - private Main() { +public class Main { + private static final int EXIT_SUCCESS = 0; + private static final int EXIT_ERROR = 1; + private static final int EXIT_HELP = 2; + + private Options currentOptions; + private int exitCode = EXIT_HELP; + private boolean emptyOption; - } public static void main(String[] args) { - int result = execute(args); - System.exit(result); + System.exit(execute(args)); } - /** - * If you are running inside java application, use this method to - * avoid unwanted System.exit() - * - * Returns 0 - executed successfully - * Returns 1 - error - * Returns 2 - non executing commands like help, version - * - * */ public static int execute(String[] args) { - Main main = new Main(); - return main.run(args); + return new Main().run(args); } + @OtherOption( - names = {"-h", "-help"}, alternates = {"--help"}, - description = "Displays this help and exit" + names = {"-h", "--help"}, + description = "Display this help and exit" ) - void onMainHelp() { - mExitCode = 2; - CommandHelpBuilder builder = new CommandHelpBuilder( - ResourceStrings.INSTANCE, Main.class); - builder.setFooters("", "help_main_footer", " -h", ""); - System.err.println(builder.build()); + private void displayHelp() { + exitCode = EXIT_HELP; + System.err.println(buildHelpText()); } + @OtherOption( - names = {"-v", "-version"}, alternates = {"--version"}, - description = "Displays version" + names = {"-v", "--version"}, + description = "Display version information" ) - void onPrintVersion() { - mExitCode = 2; - System.err.println(APKEditor.getName() + - " version " + APKEditor.getVersion() + - ", " + ARSCLib.getName() + - " version " + ARSCLib.getVersion()); + private void displayVersion() { + exitCode = EXIT_HELP; + System.out.println(buildVersionText()); } + + // Option selection handle @OnOptionSelected - void onOption(Object option, boolean emptyArgs) { - this.mOptions = (Options) option; - this.mEmptyOption = emptyArgs; + private void onOptionSelected(Object option, boolean emptyArgs) { + this.currentOptions = (Options) option; + this.emptyOption = emptyArgs; } private int run(String[] args) { - mOptions = null; - mEmptyOption = false; - mExitCode = 2; - CommandParser parser = new CommandParser(Main.class); + resetState(); + try { + CommandParser parser = new CommandParser(Main.class); parser.parse(this, args); + + if (currentOptions == null) { + return exitCode; + } + + if (emptyOption) { + throw new CommandException("empty_command_option_exception"); + } + + processSelectedOption(); + } catch (CommandException e) { - System.err.flush(); - System.err.println(e.getMessage(ResourceStrings.INSTANCE)); - return mExitCode; - } - if(mOptions == null) { - return mExitCode; - } - if(mEmptyOption) { - System.err.println(ResourceStrings.INSTANCE.getString( - "empty_command_option_exception")); - return mExitCode; - } - try { - mOptions.validate(); - } catch (CommandException e) { - System.err.flush(); - System.err.println(e.getMessage(ResourceStrings.INSTANCE)); - return mExitCode; + handleCommandException(e); + } catch (EncodeException | XmlEncodeException e) { + handleEncodeException(e); + } catch (Exception e) { + handleUnexpectedException(e); } - if(mOptions.help) { - System.err.println(mOptions.getHelp()); - return mExitCode; - } - mExitCode = 1; - try { - mOptions.runCommand(); - mExitCode = 0; - } catch (CommandException ex1) { - System.err.flush(); - System.err.println(ex1.getMessage(ResourceStrings.INSTANCE)); - } catch (EncodeException | XmlEncodeException ex) { - System.err.flush(); - System.err.println("\nERROR:\n" + ex.getMessage()); - } catch (Exception exception) { - System.err.flush(); - System.err.println("\nERROR:"); - exception.printStackTrace(System.err); + + return exitCode; + } + + + private void resetState() { + currentOptions = null; + exitCode = EXIT_HELP; + emptyOption = false; + } + + private void processSelectedOption() throws Exception { + currentOptions.validate(); + + if (currentOptions.help) { + System.err.println(currentOptions.getHelp()); + return; } - return mExitCode; + + exitCode = EXIT_ERROR; + currentOptions.runCommand(); + exitCode = EXIT_SUCCESS; + } + + private String buildHelpText() { + CommandHelpBuilder builder = new CommandHelpBuilder( + ResourceStrings.INSTANCE, Main.class); + builder.setFooters("", "help_main_footer", " -h", ""); + return builder.build(); + } + + private String buildVersionText() { + return String.format("%s version %s, %s version %s", + APKEditor.getName(), APKEditor.getVersion(), + ARSCLib.getName(), ARSCLib.getVersion()); + } + + private void handleCommandException(CommandException e) { + System.err.flush(); + System.err.println(e.getMessage(ResourceStrings.INSTANCE)); + } + + private void handleEncodeException(Exception e) { + System.err.flush(); + System.err.println("\nERROR:\n" + e.getMessage()); + } + + private void handleUnexpectedException(Exception e) { + System.err.flush(); + System.err.println("\nUNEXPECTED ERROR:"); + e.printStackTrace(System.err); } -} +} + diff --git a/src/main/resources/strings/strings-es.propertie b/src/main/resources/strings/strings-es.propertie new file mode 100644 index 00000000..e7e35b49 --- /dev/null +++ b/src/main/resources/strings/strings-es.propertie @@ -0,0 +1,130 @@ + +# strings-es.properties +################################################################################################ +# Localized strings used by APKEditor +# File naming: +# strings-[language]-[country(optional)].properties +# default: +# strings.properties +#. e.g. : strings-en-US.properties, strings-en.properties +# Content: The content is as per specification of java.util.Properties +# Encoding: utf-8 only. If utf-8 can not handle, use escaped hex encoding. +# format: +# name={VALUE} +# name: A unique string of characters starting with a-z , digits 0-9 and underscore '_' +# {VALUE}: the value +#Comment: New line starting with '#' character +#NOTES: +#1 - Optionally, keep the list alphabetically sorted by name. +#2 - If the name and {VALUE} is the same as default, the entry can be ignored +############################################################################################### +# Authors: github.com/REAndroid, +################################################################################################ + +app_version=Muestra la información de la versión y sale +app_help=Muestra esta ayuda y sale +build_description=Compila binarios de Android a partir de json/xml/raw. +build_example_1=[Básico]\n java -jar APKEditor.jar b -i path/input_directory +build_example_2=[Especificar Salida]\n java -jar APKEditor.jar b -i path/input_directory -o path/output.apk +build_example_3=[Restaurar firma]\n java -jar APKEditor.jar b -t sig -i path/input.apk -sig path/signatures_dir +build_example_4=[Especificar el marco]\n java -jar APKEditor.jar b -i path/input_directory -framework framework-res.apk -framework platforms/android-32/android.jar +build_no_cache=Ignora los archivos .dex almacenados en caché compilados y vuelve a compilar los archivos smali. +build_types=Tipos de compilación, por defecto los tipos de compilación se determinan mediante el análisis rápido de los archivos del directorio de entrada. Los valores son: +clean_meta=Limpia el directorio META-INF junto con el bloque de firma. +decode_description=Decodifica el binario de los recursos de Android en json/xml/raw legible. +decode_example_1=[Básico]\njava -jar APKEditor.jar d -i path/input.apk +decode_example_2=[Especificar salida]\njava -jar APKEditor.jar d -i path/input.apk -o path/output.apk +decode_example_3=[Especificar el tipo de decodificación]\njava -jar APKEditor.jar d -t xml -i path/input.apk +decode_example_4=[Especificar archivo(s) de marco]\njava -jar APKEditor.jar d -i path/input.apk -framework framework-res.apk -framework platforms/android-32/android.jar +decode_example_5=[Decodificar el bloque de firma apk]\njava -jar APKEditor.jar d -t sig -i path/input.apk -sig path/signatures_dir +decode_load_dex=Número de archivos dex para cargar a la vez.\nSi el conteo de archivos dex del apk es mayor que este valor, entonces el decodificador carga un dex a la vez.\n Aplica solo cuando -dex-lib está configurado como interno.\n Por defecto = 3\n Ver abajo. +decode_note_1=[interno] Constructor Dex:\n Soporta completamente archivos dex hasta 042.\n Máxima compresión de archivos dex.\n Construye con orden de sección dex similar a r8/dx.\n Edición conveniente de marcadores dex, ver archivo smali/classes/dex-file.json\n Comentarios smali adicionales útiles: ej. jerarquía de clase/método.\n Soporta espacios en blanco en nombres simples de clase como se introdujo en dex 041+ +decode_note_2=[-load-dex] Para imprimir la jerarquía correcta de clase/método, es necesario cargar todos los archivos dex a la vez. Esto puede resultar en alto consumo de memoria y podría fallar con "OutOfMemoryError", por lo que debes limitar el número de archivos dex a cargar a la vez. Puedes solucionar este problema con el argumento de memoria -Xmx ej. java -Xmx8g -jar APKEditor.jar ... +decode_types=Tipos de decodificación: +decode_usage=d [Opciones, banderas] +dump_dex_markers=Vuelca marcadores dex (aplica solo en modo smali). +duplicate_option_exception=Opción duplicada '%s' +dex_lib=Librería Dex a usar:\n 1) internal : Usa librería interna, soporta versiones dex hasta 042.\n 2) jf : Usa librería de JesusFreke/smali, soporta versiones dex 035 e inferiores.\n Por defecto = jf\n *ADVERTENCIA: El valor por defecto será reemplazado por "internal" en versiones futuras.\n Ver abajo. +empty_command_args_exception=Comando vacío, ejecuta con -h para obtener ayuda +empty_command_option_exception=Opciones vacías, ejecuta con -h para obtener ayuda +force_delete=Forzar eliminación de ruta de salida. +framework_version_number=Número de versión de marco preferido +help_description=Muestra esta ayuda y sale. +help_main_footer=Para obtener ayuda sobre cada comando ejecuta con: +info_activities=Imprime el nombre de la clase de actividad principal. En modo verbose, imprime todas las actividades declaradas incluyendo . +info_app_class_name=Nombre de la clase de aplicación. +info_app_icon=Ruta/valor del icono de la aplicación. En modo verbose, imprime todas las configuraciones. +info_app_icon_round=Ruta/valor del icono redondo de la aplicación. En modo verbose, imprime todas las configuraciones. +info_app_name=Nombre de la aplicación. En modo verbose, imprime todas las configuraciones. +info_app_version_code=Código de versión de la aplicación. +info_app_version_name=Nombre de versión de la aplicación. +info_description=Imprime información del apk. +info_dex=Imprime información dex. +info_example_1=[Básico]\n java -jar APKEditor.jar info -i file.apk +info_example_2=[Especificar salida y tipo]\n java -jar APKEditor.jar info -i path/input.apk -t json -v -o info_file.json +info_example_3=[Imprimir solo tipo específico]\n java -jar APKEditor.jar info -i path/input.apk -resources -filter-type drawable +info_filter_type=Imprime solo los nombres de tipo de recurso especificados\n Aplica solo cuando se usa la bandera '-resources'.\n Puede ser múltiple. +info_invalid_output_extension=¡Extensión de archivo inválida! Se esperaba '%s', '%s' +info_configurations=Imprime las configuraciones en el APK. +info_languages=Imprime los idiomas en el APK. +info_locales=Imprime las localizaciones en el APK. +info_list_files=Lista archivos dentro del apk. +info_list_xml_files=Lista archivos xml compilados dentro del apk. +info_min_sdk_version=Versión mínima de SDK. +info_package_name=Nombre del paquete (ID de aplicación) del manifiesto y en modo verbose, imprime los paquetes de la tabla de recursos. +info_permissions=Permisos. +info_print_types=Tipos/formatos a imprimir: +info_res=Imprime entradas de recursos especificadas por cualquiera de:\n 1) ID de recurso hexadecimal o decimal.\n 2) Nombre completo del recurso ej. @string/app_name.\n Puede ser múltiple. +info_resources=Imprime todos los recursos +info_signatures=Imprime información de firma. +info_signatures_base64=Imprime información de firma con certificados en base64. +info_strings=Imprime el contenido del grupo de cadenas de la tabla de recursos en el APK. +info_target_sdk_version=Versión objetivo de SDK. +info_verbose_mode=Modo verbose. +info_xml_tree=Imprime los xmls compilados en los assets dados.\n Puede ser múltiple +info_xml_strings=Imprime las cadenas de los assets xml compilados dados.\n Puede ser múltiple +input_path=Ruta de entrada. +invalid_sig_parameter_combination=¡Combinación de parámetros inválida!\nDirectorio de firmas proporcionado pero falta: -t sig +invalid_type_format=<%s> inválido cadena '%s' +keep_original_res=Mantiene las rutas originales de archivos res/:\n Aplica solo al decodificar a xml\n Todos los archivos res/ se colocarán en el directorio \n Las rutas relativas estarán vinculadas a values/xml +merge_description=Fusiona archivos apk divididos desde un directorio o archivos apk comprimidos como XAPK, APKM, APKS... +merge_example_1=[Básico]\n java -jar APKEditor.jar m -i path/input -o path/output.apk +missing_input_file=Falta archivo de entrada. +missing_sig_directory=Falta directorio de firmas. +missing_value_exception=Falta valor para '%s' +no_dex_debug=Elimina toda la información de depuración de smali/dex. +no_such_directory=No existe el directorio: '%s' +no_such_file=No existe el archivo: '%s'. +no_such_file_or_directory=No existe el archivo o directorio: '%s' +output_path=Ruta de salida. Opcional, si no se proporciona se generará un nuevo archivo en el mismo directorio que la entrada +path_already_exists=La ruta ya existe: '%s' +path_is_directory_expect_file=La ruta es un directorio, se esperaba archivo: '%s' +path_is_file_expect_directory=La ruta es un archivo, se esperaba directorio: '%s' +path_of_framework=Ruta del archivo de marco (puede ser múltiple). +protect_confuse_zip=Confunde la estructura zip. Cuando está activado:\n Las apps pueden fallar si acceden directamente a archivos apk ej. Class.getResourceAsStream().\n Algunos escáneres apk pueden marcarlo como "Zip malformado" +protect_description=Protege/Ofusca archivos de recursos del apk. Usando técnicas únicas de ofuscación. +protect_dic_dir_name=Ruta a un archivo de texto que contiene una lista de nombres de directorios separados por nueva línea. +protect_dic_file_name=Ruta a un archivo de texto que contiene una lista de nombres de archivos separados por nueva línea. +protect_example_1=[Básico]\n java -jar APKEditor.jar p -i path/input.apk -o path/output.apk +protect_keep_type=Mantener nombres de tipos de recursos específicos (ej. drawable), por defecto solo mantiene el tipo de recurso .\n Puede ser múltiple +protect_skip_manifest=No proteger el manifiesto. +raw_dex=Copiar archivos dex crudos / omitir smali. +res_dir_name=Establece el nombre del directorio raíz de archivos de recursos. ej. para ofuscación para mover archivos de 'res/' a 'r/' o viceversa. +refactor_description=Refactoriza nombres de recursos ofuscados +refactor_example_1=[Básico]\n java -jar APKEditor.jar x -i path/input.apk -o path/output.apk +refactor_fix_types=Corrige nombres de tipos de recursos basados en usos y valores +refactor_public_xml=Ruta del archivo xml de IDs de recursos (public.xml)\nCarga nombres y aplica a recursos desde archivo 'public.xml' +signatures_path=Ruta del directorio de firmas. +split_json=Divide resources.arsc en múltiples partes según entradas de tipo (usa esto para archivos grandes) +title_commands=Comandos: +title_app_description=Editor de archivos de recursos binarios de Android +title_example=Ejemplo: +title_flags=Banderas: +title_options=Opciones: +title_other_options=Otras opciones: +title_notes=Notas: +title_usage=Uso: +unknown_command_exception=Comando desconocido: '%s' +unknown_option_exception=Opción desconocida: '%s' +validate_modules=Valida para el mismo número de versión de base.apk como archivos apk divididos. +validate_resources_dir=Valida el nombre del directorio de recursos\n(ej. si la ruta de un archivo de recurso de diseño es 'res/abc.png', entonces se moverá a 'res/drawable/abc.png)'