From 5555e919c164eb5bffdb94e911c63c9f34836adb Mon Sep 17 00:00:00 2001 From: Petschko Date: Thu, 18 Feb 2021 22:43:45 +0100 Subject: [PATCH 1/8] Added way better support for CMD --- src/org/petschko/lib/File.java | 34 ++++ src/org/petschko/rpgmakermv/decrypt/App.java | 94 ++++------ src/org/petschko/rpgmakermv/decrypt/CMD.java | 155 ++++++++++++++++ .../rpgmakermv/decrypt/CMD_Command.java | 23 +++ .../rpgmakermv/decrypt/CMD_Decrypt.java | 135 ++++++++++++++ .../rpgmakermv/decrypt/CMD_DetectKey.java | 147 +++++++++++++++ .../rpgmakermv/decrypt/CMD_Encrypt.java | 131 ++++++++++++++ .../petschko/rpgmakermv/decrypt/CMD_Help.java | 44 +++++ .../petschko/rpgmakermv/decrypt/CMD_Open.java | 86 +++++++++ .../rpgmakermv/decrypt/CMD_Restore.java | 107 +++++++++++ .../decrypt/CMD_RestoreProject.java | 33 ++++ .../rpgmakermv/decrypt/Decrypter.java | 169 ++++++++++++++++-- .../petschko/rpgmakermv/decrypt/Finder.java | 2 +- src/org/petschko/rpgmakermv/decrypt/GUI.java | 2 +- .../rpgmakermv/decrypt/RPGProject.java | 162 +++++++++++++++-- 15 files changed, 1231 insertions(+), 93 deletions(-) create mode 100644 src/org/petschko/rpgmakermv/decrypt/CMD.java create mode 100644 src/org/petschko/rpgmakermv/decrypt/CMD_Command.java create mode 100644 src/org/petschko/rpgmakermv/decrypt/CMD_Decrypt.java create mode 100644 src/org/petschko/rpgmakermv/decrypt/CMD_DetectKey.java create mode 100644 src/org/petschko/rpgmakermv/decrypt/CMD_Encrypt.java create mode 100644 src/org/petschko/rpgmakermv/decrypt/CMD_Help.java create mode 100644 src/org/petschko/rpgmakermv/decrypt/CMD_Open.java create mode 100644 src/org/petschko/rpgmakermv/decrypt/CMD_Restore.java create mode 100644 src/org/petschko/rpgmakermv/decrypt/CMD_RestoreProject.java diff --git a/src/org/petschko/lib/File.java b/src/org/petschko/lib/File.java index d4ecd7a..4d916fc 100644 --- a/src/org/petschko/lib/File.java +++ b/src/org/petschko/lib/File.java @@ -517,6 +517,22 @@ public static boolean clearDirectory(@NotNull String directoryPath) { return File.deleteDirectoryOperation(directoryPath, true, false); } + /** + * Shows if this file can be encrypted + * + * @return - Can file be encrypted + */ + public boolean canBeEncrypted() { + switch(this.extension) { + case "png": + case "m4a": + case "ogg": + return true; + default: + return false; + } + } + /** * Returns the real Extension of the current fake extension * @@ -538,6 +554,24 @@ public String realExtByFakeExt() { } } + /** + * Returns the RPG-MV/MZ Extension of the current extension + * + * @return - Fake File-Extension + */ + public String fakeExtByRealExt(boolean isMV) { + switch(this.extension) { + case "png": + return isMV ? "rpgmvp" : "png_"; + case "m4a": + return isMV ? "rpgmvm" : "m4a_"; + case "ogg": + return isMV ? "rpgmvo" : "ogg_"; + default: + return this.extension; + } + } + /** * Shows if this file is an Image * diff --git a/src/org/petschko/rpgmakermv/decrypt/App.java b/src/org/petschko/rpgmakermv/decrypt/App.java index b768a7e..b027839 100644 --- a/src/org/petschko/rpgmakermv/decrypt/App.java +++ b/src/org/petschko/rpgmakermv/decrypt/App.java @@ -16,7 +16,7 @@ public class App { private static Boolean useGUI = true; private static GUI gui; - static String pathToProject; + private static CMD cmd; static String outputDir; static Preferences preferences; @@ -27,78 +27,47 @@ public class App { */ public static void main(String[] args) { // Check whats given from CMD - if(args.length > 0) - App.processArgs(args); + if(args.length > 0) { + useGUI = false; + cmd = new CMD(args); + } - if(App.useGUI) { + if(useGUI) { // Use GUI - App.preferences = new Preferences(Config.preferencesFile); - App.outputDir = App.preferences.getConfig(Preferences.lastOutputDir, Config.defaultOutputDir); - App.gui = new GUI(); + preferences = new Preferences(Config.preferencesFile); + outputDir = App.preferences.getConfig(Preferences.lastOutputDir, Config.defaultOutputDir); + gui = new GUI(); } else { // Use Command-Line Version - try { - RPGProject rpgProject = new RPGProject(App.pathToProject, false); - Decrypter decrypter = new Decrypter(); - - rpgProject.setOutputPath(App.outputDir); - decrypter.setIgnoreFakeHeader(true); - rpgProject.decryptFiles(decrypter); - } catch(Exception e) { - e.printStackTrace(); - } - - App.exitCMD(0); + cmd.runCMD(); } } /** - * Process Command-Line Arguments + * Shows the given message if no GUI is enabled * - * @param args - Optional Arguments from Command-Line + * @param msg - Message to display + * @param messageStatus - Status of the Message */ - private static void processArgs(String[] args) { - // Don't use GUI if using Command-Line - App.useGUI = false; - - // Show Welcome-Message - System.out.println(Config.programName + " - " + Config.version + " by " + Const.creator + " | Command-Line Version"); - - // Check if help is needed - if(args[0].equals("help") || args[0].equals("/?") || args[0].equals("--help")) { - App.printHelp(); - App.exitCMD(0); - } - - // Set Path to Project - App.pathToProject = args[0]; - App.showMessage("Set Project-Dir to: \"" + App.pathToProject + "\""); - - // Set Output-Dir - try { - App.outputDir = args[1]; - } catch(ArrayIndexOutOfBoundsException arEx) { - App.outputDir = Config.defaultOutputDir; + static void showMessage(String msg, int messageStatus) { + String status; + + switch(messageStatus) { + case CMD.STATUS_ERROR: + status = "[ERROR]: "; + break; + case CMD.STATUS_WARNING: + status = "[WARN]: "; + break; + case CMD.STATUS_OK: + status = "[SUCCESS]: "; + break; + default: + status = "[INFO]: "; } - App.showMessage("Set Output-Dir to: \"" + App.outputDir + "\""); - } - - /** - * Prints help for Command-Line usage - */ - private static void printHelp() { - System.out.println("Usage: java -jar \"RPG Maker MV Decrypter.jar\" [path to decrypt project] [(optional) output path]"); - } - - /** - * Exit the Program with a Message - * - * @param status - Exit-Status-Code - */ - private static void exitCMD(int status) { - System.out.println("Done."); - System.exit(status); + if(! App.useGUI) + System.out.println(status + msg); } /** @@ -107,8 +76,7 @@ private static void exitCMD(int status) { * @param msg - Message to display */ static void showMessage(String msg) { - if(! App.useGUI) - System.out.println(msg); + showMessage(msg, CMD.STATUS_INFO); } /** diff --git a/src/org/petschko/rpgmakermv/decrypt/CMD.java b/src/org/petschko/rpgmakermv/decrypt/CMD.java new file mode 100644 index 0000000..5f9b85a --- /dev/null +++ b/src/org/petschko/rpgmakermv/decrypt/CMD.java @@ -0,0 +1,155 @@ +package org.petschko.rpgmakermv.decrypt; + +import org.petschko.lib.Const; + +/** + * Author: Peter Dragicevic + * Authors-Website: https://petschko.org/ + * Date: 15.02.2021 + * Time: 22:11 + * + * Notes: CMD Class + */ +class CMD { + static final int STATUS_OK = 0; + static final int STATUS_INFO = 2; + static final int STATUS_WARNING = 1; + static final int STATUS_ERROR = -1; + static final String HELP_INDENT = " "; + private static final String CMD_HELP = "help"; + private static final String CMD_HELP_2 = "-help"; + private static final String CMD_HELP_3 = "--help"; + private static final String CMD_HELP_4 = "/?"; + private static final String CMD_OPEN_IMG = "open"; + private static final String CMD_DECRYPT = "decrypt"; + private static final String CMD_ENCRYPT = "encrypt"; + private static final String CMD_RESTORE = "restore"; + private static final String CMD_RESTORE_PROJECT = "restoreproject"; + private static final String CMD_GET_KEY = "key"; + private static final String CMD_GET_KEY_2 = "detect"; + private static final String CMD_GET_KEY_3 = "getkey"; + private static final String CMD_GET_KEY_4 = "detectkey"; + private static final String CMD_GET_KEY_5 = "keydetect"; + + private final String[] args; + + /** + * CMD Constructor + * + * @param args - CMD Args + */ + CMD(String[] args) { + this.args = args; + } + + /** + * Runs the Command + */ + void runCMD() { + // Show Welcome-Message + System.out.println("------------------------------------------------------------------------------"); + System.out.println(Config.programName + " - " + Config.version + " by " + Const.creator + " | Command-Line Version"); + System.out.println("------------------------------------------------------------------------------"); + + sanitizeArgs(); + processArgs(); + + exitCMD(0); + } + + /** + * Sanitize the first args + */ + private void sanitizeArgs() { + args[0] = args[0].toLowerCase().trim(); + args[0] = args[0].replaceAll("-", ""); + args[0] = args[0].replaceAll("/", ""); + + if(args.length >= 2) { + if(isHelpCmd(args[1].toLowerCase().trim())) + args[1] = args[1].toLowerCase().trim(); + } + } + + /** + * Process Command-Line Arguments + */ + private void processArgs() { + // Get the Sub command + String subCommand; + CMD_Command cmdHandler; + try { + subCommand = args[1].toLowerCase(); + } catch(ArrayIndexOutOfBoundsException arEx) { + subCommand = null; + } + + + switch(args[0]) { + case CMD_OPEN_IMG: + cmdHandler = new CMD_Open(); + break; + case CMD_DECRYPT: + cmdHandler = new CMD_Decrypt(); + break; + case CMD_ENCRYPT: + cmdHandler = new CMD_Encrypt(); + break; + case CMD_RESTORE: + cmdHandler = new CMD_Restore(); + break; + case CMD_RESTORE_PROJECT: + cmdHandler = new CMD_RestoreProject(); + break; + case CMD_GET_KEY: + case CMD_GET_KEY_2: + case CMD_GET_KEY_3: + case CMD_GET_KEY_4: + case CMD_GET_KEY_5: + cmdHandler = new CMD_DetectKey(); + break; + default: + boolean invalidCommand = false; + if(! isHelpCmd(args[0])) + invalidCommand = true; + + cmdHandler = new CMD_Help(); + + // Help is handles a bit different + if(invalidCommand) + cmdHandler.run(args); + else + cmdHandler.printHelp(); + + exitCMD(invalidCommand ? STATUS_WARNING : STATUS_OK); + return; + } + + if(isHelpCmd(subCommand)) + cmdHandler.printHelp(); + else + cmdHandler.run(args); + } + + /** + * Shows if the current command is a help command + * + * @return - Is the command a help command + */ + private boolean isHelpCmd(String command) { + if(command == null) + return false; + + return command.equals(CMD_HELP) || command.equals(CMD_HELP_2) || command.equals(CMD_HELP_3) || command.equals(CMD_HELP_4); + } + + /** + * Exit the Program with a Message + * + * @param status - Exit-Status-Code + */ + static void exitCMD(int status) { + App.showMessage("Done."); + System.exit(status); + } +} diff --git a/src/org/petschko/rpgmakermv/decrypt/CMD_Command.java b/src/org/petschko/rpgmakermv/decrypt/CMD_Command.java new file mode 100644 index 0000000..309046a --- /dev/null +++ b/src/org/petschko/rpgmakermv/decrypt/CMD_Command.java @@ -0,0 +1,23 @@ +package org.petschko.rpgmakermv.decrypt; + +/** + * Author: Peter Dragicevic + * Authors-Website: https://petschko.org/ + * Date: 15.02.2021 + * Time: 22:59 + * + * Notes: CMD_Command Class + */ +interface CMD_Command { + /** + * Runs the Command + * + * @param args - Command-Line commands + */ + void run(String[] args); + + /** + * Prints help for the command + */ + void printHelp(); +} diff --git a/src/org/petschko/rpgmakermv/decrypt/CMD_Decrypt.java b/src/org/petschko/rpgmakermv/decrypt/CMD_Decrypt.java new file mode 100644 index 0000000..8213ef0 --- /dev/null +++ b/src/org/petschko/rpgmakermv/decrypt/CMD_Decrypt.java @@ -0,0 +1,135 @@ +package org.petschko.rpgmakermv.decrypt; + +/** + * Author: Peter Dragicevic + * Authors-Website: https://petschko.org/ + * Date: 15.02.2021 + * Time: 23:05 + * + * Notes: CMD_Decrypt Class + */ +class CMD_Decrypt implements CMD_Command { + private String pathToProject; + private String outputDir; + + // Decrypter options + private String key = null; + private boolean verifyDir = false; + private boolean ignoreFakeHeader = true; + private int headerLen = Decrypter.defaultHeaderLen; + private String signature = Decrypter.defaultSignature; + private String version = Decrypter.defaultVersion; + private String remain = Decrypter.defaultRemain; + + /** + * Runs the Command + * + * @param args - Command-Line commands + */ + @Override + public void run(String[] args) { + if(args.length < 2) { + App.showMessage("To less arguments, see help", CMD.STATUS_WARNING); + App.showMessage(""); + this.printHelp(); + CMD.exitCMD(CMD.STATUS_WARNING); + return; + } + + // Set Path to Project + pathToProject = args[1]; + App.showMessage("Set Project-Dir to: \"" + pathToProject + "\""); + + // Set Output-Dir + try { + outputDir = args[2]; + } catch(ArrayIndexOutOfBoundsException arEx) { + outputDir = Config.defaultOutputDir; + } + + App.showMessage("Set Output-Dir to: \"" + outputDir + "\""); + + if(args.length >= 4) + verifyDir = Boolean.parseBoolean(args[3]); + if(args.length >= 5) + ignoreFakeHeader = Boolean.parseBoolean(args[4]); + if(args.length >= 6) { + if(! args[5].toLowerCase().equals("auto")) { + key = args[5].toLowerCase(); + } + } + if(args.length >= 7) { + headerLen = Integer.parseInt(args[6]); + + // Ensure headerLen is at least 1 else default + if(headerLen < 1) + headerLen = Decrypter.defaultHeaderLen; + } + if(args.length >= 8) + signature = args[7].trim().toLowerCase(); + if(args.length >= 9) + version = args[8].trim().toLowerCase(); + if(args.length >= 10) + remain = args[9].trim().toLowerCase(); + + handleFiles(); + } + + /** + * Handles Files to decrypt + */ + private void handleFiles() { + try { + RPGProject rpgProject = new RPGProject(pathToProject, verifyDir); + Decrypter decrypter; + + if(key == null) + decrypter = new Decrypter(); + else + decrypter = new Decrypter(key); + + rpgProject.setOutputPath(outputDir); + decrypter.setIgnoreFakeHeader(ignoreFakeHeader); + decrypter.setHeaderLen(headerLen); + decrypter.setSignature(signature); + decrypter.setVersion(version); + decrypter.setRemain(remain); + rpgProject.decryptFilesCmd(decrypter); + } catch(Exception e) { + e.printStackTrace(); + CMD.exitCMD(CMD.STATUS_ERROR); + } + } + + /** + * Prints help for the command + */ + @Override + public void printHelp() { + App.showMessage("Usage: java -jar \"RPG Maker MV Decrypter.jar\" decrypt [path to decrypt project] [(optional) output path] [(optional) verifyRpgDir - false|true]"); + App.showMessage(CMD.HELP_INDENT + " [(optional) ignoreFakeHeader - true|false] [(optional) key - auto|keyValue]"); + App.showMessage(CMD.HELP_INDENT + " [(optional) headerLen] [(optional) hsignature] [(optional) hversion] [(optional) hremain]"); + App.showMessage(""); + App.showMessage(CMD.HELP_INDENT + "Params: (Separate each param by a space, for paths use \"\" around the path)"); + App.showMessage(CMD.HELP_INDENT + " [path to decrypt project] - Path to the RPG-MV/MZ Project you want to decrypt"); + App.showMessage(CMD.HELP_INDENT + " (optional) [output path] - Path where the decrypted files go | Default: \"output\" (out dir in program dir)"); + App.showMessage(CMD.HELP_INDENT + " (optional) [verifyRpgDir (false|true)] - Verifies if its a RPG-MV/MZ dir | Default: false"); + App.showMessage(CMD.HELP_INDENT + " (optional) [ignoreFakeHeader (true|false)] - Ignored the Files MV/MZ-Header | Default: true"); + App.showMessage(CMD.HELP_INDENT + " (optional) [key (auto|keyValue)] - Decryption key or auto detection | Default: auto"); + App.showMessage(CMD.HELP_INDENT + "Header-Values: (usually REALLY not needed)"); + App.showMessage(CMD.HELP_INDENT + " (very optional) [headerLen] - Byte length of the Header | Default: " + this.headerLen); + App.showMessage(CMD.HELP_INDENT + " (very optional) [hsignature] - Signature of the Header | Default: " + this.signature); + App.showMessage(CMD.HELP_INDENT + " (very optional) [hversion] - Version of the Header | Default: " + this.version); + App.showMessage(CMD.HELP_INDENT + " (very optional) [hremain] - Remain of the Header | Default: " + this.remain); + App.showMessage(""); + App.showMessage(CMD.HELP_INDENT + "Examples of full commands with all params:"); + App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" decrypt \"C:\\my rpg mv game\\\" \"output\" false true auto"); + App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" decrypt \"C:\\my rpg mv game\\\" \"C:\\my rpg mv game\\\" true false d41d8cd98f00b204e9800998ecf8427e"); + App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" decrypt \"C:\\my rpg mv game\\\" \"C:\\my rpg mv game\\\" true false d41d8cd98f00b204e9800998ecf8427e 14 5250474d56000000 000301 00000000"); + App.showMessage(""); + App.showMessage(CMD.HELP_INDENT + "The first command extracts all files inside this programs output dir, dont check if its a RPG-MV/MZ dir, ignored if the header of the files is the RPG-MV/MZ header and auto-detects the key"); + App.showMessage(CMD.HELP_INDENT + "The second command extract all files inside the game directory, checks if RPG-MV/MZ dir and Header is valid with the key d41d8cd98f00b204e9800998ecf8427e"); + App.showMessage(CMD.HELP_INDENT + "The third command does the same as the 2nd, just shows how you can modify the values of the header"); + App.showMessage(""); + } +} diff --git a/src/org/petschko/rpgmakermv/decrypt/CMD_DetectKey.java b/src/org/petschko/rpgmakermv/decrypt/CMD_DetectKey.java new file mode 100644 index 0000000..26045ed --- /dev/null +++ b/src/org/petschko/rpgmakermv/decrypt/CMD_DetectKey.java @@ -0,0 +1,147 @@ +package org.petschko.rpgmakermv.decrypt; + +import java.io.BufferedReader; +import java.io.InputStreamReader; + +/** + * Author: Peter Dragicevic + * Authors-Website: https://petschko.org/ + * Date: 15.02.2021 + * Time: 23:35 + * + * Notes: CMD_DetectKey Class + */ +class CMD_DetectKey implements CMD_Command { + private String pathToProject; + private boolean skipImageQuestion = false; + + // Decoder options + private int headerLen = Decrypter.defaultHeaderLen; + + /** + * Runs the Command + * + * @param args - Command-Line commands + */ + @Override + public void run(String[] args) { + if(args.length < 2) { + App.showMessage("To less arguments, see help", CMD.STATUS_WARNING); + App.showMessage(""); + this.printHelp(); + CMD.exitCMD(CMD.STATUS_WARNING); + return; + } + + // Set Path to Project + pathToProject = args[1]; + App.showMessage("Set Project-Dir to: \"" + pathToProject + "\""); + + if(args.length >= 3) + skipImageQuestion = Boolean.parseBoolean(args[2]); + if(args.length >= 4) { + headerLen = Integer.parseInt(args[3]); + + // Ensure headerLen is at least 1 else default + if(headerLen < 1) + headerLen = Decrypter.defaultHeaderLen; + } + + detectsKey(); + } + + /** + * Detects the Key + */ + private void detectsKey() { + try { + RPGProject rpgProject = new RPGProject(pathToProject, false); + Decrypter decrypter = new Decrypter(); + decrypter.setHeaderLen(headerLen); + + // Ensure we may have an encrypted image + rpgProject.findEncryptedImg(); + + // Try the detection from JSON-File if found + if(rpgProject.getSystem() != null) { + decrypter.detectEncryptionKeyFromJson(rpgProject.getSystem(), rpgProject.getEncryptionKeyName()); + + if(decrypter.getDecryptCode() != null) { + showKey(decrypter.getDecryptCode()); + return; + } + } + + // Try the detection from image but ask before + if(rpgProject.getEncryptedImgFile() != null) { + boolean doImgSearch = true; + if(! skipImageQuestion) { + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + App.showMessage(""); + App.showMessage("Key not found so far (System.json)... Should I generate the Key from encrypted images?"); + App.showMessage("WARNING: Key from images may not work. Continue? (Y/n)"); + String s = reader.readLine().toLowerCase(); + + if(! (s.equals("") || s.equals("y") || s.equals("yes") || s.equals("true") || s.equals("1"))) + doImgSearch = false; + + App.showMessage("Ok!"); + } + + if(doImgSearch) { + App.showMessage("Searches for Key in Image..."); + decrypter.detectEncryptionKeyFromImage(rpgProject.getEncryptedImgFile()); + } else + App.showMessage("Skips searching for Key in Image..."); + + if(decrypter.getDecryptCode() != null) { + showKey(decrypter.getDecryptCode()); + return; + } + } + + App.showMessage("Key not found..."); + CMD.exitCMD(CMD.STATUS_OK); + } catch(Exception e) { + e.printStackTrace(); + CMD.exitCMD(CMD.STATUS_ERROR); + } + } + + /** + * Shows the Key + * + * @param key - Key + */ + private void showKey(String key) { + App.showMessage("---------------------------------------------------------", CMD.STATUS_OK); + App.showMessage("> The Key is: " + key, CMD.STATUS_OK); + App.showMessage("---------------------------------------------------------", CMD.STATUS_OK); + CMD.exitCMD(CMD.STATUS_OK); + } + + /** + * Prints help for the command + */ + @Override + public void printHelp() { + App.showMessage("Usage: java -jar \"RPG Maker MV Decrypter.jar\" key [path to project]"); + App.showMessage(CMD.HELP_INDENT + " [(optional) ask before image keysearch (true|false)] [(optional) headerLen]"); + App.showMessage(""); + App.showMessage(CMD.HELP_INDENT + "Params: (Separate each param by a space, for paths use \"\" around the path)"); + App.showMessage(CMD.HELP_INDENT + " [path to project] - Path to the RPG-MV/MZ Project were you want to get the key from"); + App.showMessage(CMD.HELP_INDENT + " (optional) [ask before image keysearch (true|false)] - Ask before search key in images | Default: true"); + App.showMessage(CMD.HELP_INDENT + "Header-Values: (usually REALLY not needed)"); + App.showMessage(CMD.HELP_INDENT + " (very optional) [headerLen] - Byte length of the Header | Default: " + this.headerLen); + App.showMessage(""); + App.showMessage(CMD.HELP_INDENT + "Examples of the full commands with all params:"); + App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" key \"C:\\my rpg mv game\\\" true"); + App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" key \"C:\\my rpg mv game\\\" false"); + App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" key \"C:\\my rpg mv game\\\" false 14"); + App.showMessage(""); + App.showMessage(CMD.HELP_INDENT + "The first command returns the key of the Project if found also asks before searching in images"); + App.showMessage(CMD.HELP_INDENT + "The second command returns the key of the Project if found but skips the image question"); + App.showMessage(CMD.HELP_INDENT + "The third command does the same as the 2nd, just shows how you can modify the header length"); + App.showMessage(""); + } +} diff --git a/src/org/petschko/rpgmakermv/decrypt/CMD_Encrypt.java b/src/org/petschko/rpgmakermv/decrypt/CMD_Encrypt.java new file mode 100644 index 0000000..c3fb8e9 --- /dev/null +++ b/src/org/petschko/rpgmakermv/decrypt/CMD_Encrypt.java @@ -0,0 +1,131 @@ +package org.petschko.rpgmakermv.decrypt; + +/** + * Author: Peter Dragicevic + * Authors-Website: https://petschko.org/ + * Date: 15.02.2021 + * Time: 23:35 + * + * Notes: CMD_Encrypt Class + */ +class CMD_Encrypt implements CMD_Command { + private String pathToProject; + private String outputDir; + + // Encrypter options + private boolean toMV = true; + private String key = null; + private int headerLen = Decrypter.defaultHeaderLen; + private String signature = Decrypter.defaultSignature; + private String version = Decrypter.defaultVersion; + private String remain = Decrypter.defaultRemain; + + /** + * Runs the Command + * + * @param args - Command-Line commands + */ + @Override + public void run(String[] args) { + if(args.length < 2) { + App.showMessage("To less arguments, see help", CMD.STATUS_WARNING); + App.showMessage(""); + this.printHelp(); + CMD.exitCMD(CMD.STATUS_WARNING); + return; + } + + // Set Path to Project + pathToProject = args[1]; + App.showMessage("Set Project-Dir to: \"" + pathToProject + "\""); + + // Set Output-Dir + try { + outputDir = args[2]; + } catch(ArrayIndexOutOfBoundsException arEx) { + outputDir = Config.defaultOutputDir; + } + + App.showMessage("Set Output-Dir to: \"" + outputDir + "\""); + + if(args.length >= 4) + toMV = Boolean.parseBoolean(args[3]); + if(args.length >= 5) { + if(! args[4].toLowerCase().equals("auto")) { + key = args[4].toLowerCase(); + } + } + if(args.length >= 6) { + headerLen = Integer.parseInt(args[5]); + + // Ensure headerLen is at least 1 else default + if(headerLen < 1) + headerLen = Decrypter.defaultHeaderLen; + } + if(args.length >= 7) + signature = args[6].trim().toLowerCase(); + if(args.length >= 8) + version = args[7].trim().toLowerCase(); + if(args.length >= 9) + remain = args[8].trim().toLowerCase(); + + handleFiles(); + } + + /** + * Handles Files to encrypt + */ + private void handleFiles() { + try { + RPGProject rpgProject = new RPGProject(pathToProject, false); + Decrypter encrypter; + + if(key == null) + encrypter = new Decrypter(); + else + encrypter = new Decrypter(key); + + rpgProject.setOutputPath(outputDir); + rpgProject.setMV(toMV); + encrypter.setHeaderLen(headerLen); + encrypter.setSignature(signature); + encrypter.setVersion(version); + encrypter.setRemain(remain); + rpgProject.encryptFilesCmd(encrypter); + } catch(Exception e) { + e.printStackTrace(); + CMD.exitCMD(CMD.STATUS_ERROR); + } + } + + /** + * Prints help for the command + */ + @Override + public void printHelp() { + App.showMessage("Usage: java -jar \"RPG Maker MV Decrypter.jar\" encrypt [path to encrypt files dir] [(optional) output path]"); + App.showMessage(CMD.HELP_INDENT + " [(optional) to MV - true|false] [(optional) key - auto|keyValue]"); + App.showMessage(CMD.HELP_INDENT + " [(optional) headerLen] [(optional) hsignature] [(optional) hversion] [(optional) hremain]"); + App.showMessage(""); + App.showMessage(CMD.HELP_INDENT + "Params: (Separate each param by a space, for paths use \"\" around the path)"); + App.showMessage(CMD.HELP_INDENT + " [path to encrypt files dir] - Path to the File-Dir/Project you want to encrypt"); + App.showMessage(CMD.HELP_INDENT + " (optional) [output path] - Path where the encrypted files go | Default: \"output\" (out dir in program dir)"); + App.showMessage(CMD.HELP_INDENT + " (optional) [to MV (true|false)] - Encrypts files for MV (MZ on false) | Default: true"); + App.showMessage(CMD.HELP_INDENT + " (optional) [key (auto|keyValue)] - Encryption key or auto detection | Default: auto"); + App.showMessage(CMD.HELP_INDENT + "Header-Values: (usually REALLY not needed)"); + App.showMessage(CMD.HELP_INDENT + " (very optional) [headerLen] - Byte length of the Header | Default: " + this.headerLen); + App.showMessage(CMD.HELP_INDENT + " (very optional) [hsignature] - Signature of the Header | Default: " + this.signature); + App.showMessage(CMD.HELP_INDENT + " (very optional) [hversion] - Version of the Header | Default: " + this.version); + App.showMessage(CMD.HELP_INDENT + " (very optional) [hremain] - Remain of the Header | Default: " + this.remain); + App.showMessage(""); + App.showMessage(CMD.HELP_INDENT + "Examples of full commands with all params:"); + App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" encrypt \"C:\\my rpg mv game\\\" \"output\" true auto"); + App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" encrypt \"C:\\my rpg mv game\\\" \"C:\\my rpg mv game\\\" true d41d8cd98f00b204e9800998ecf8427e"); + App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" encrypt \"C:\\my rpg mv game\\\" \"C:\\my rpg mv game\\\" true d41d8cd98f00b204e9800998ecf8427e 14 5250474d56000000 000301 00000000"); + App.showMessage(""); + App.showMessage(CMD.HELP_INDENT + "The first command encrypts all files to this programs output dir, as a MV-Files and auto-detects the key"); + App.showMessage(CMD.HELP_INDENT + "The second command encrypts all files inside the game directory, as MV-Files with the key d41d8cd98f00b204e9800998ecf8427e"); + App.showMessage(CMD.HELP_INDENT + "The third command does the same as the 2nd, just shows how you can modify the values of the header"); + App.showMessage(""); + } +} diff --git a/src/org/petschko/rpgmakermv/decrypt/CMD_Help.java b/src/org/petschko/rpgmakermv/decrypt/CMD_Help.java new file mode 100644 index 0000000..37efd8a --- /dev/null +++ b/src/org/petschko/rpgmakermv/decrypt/CMD_Help.java @@ -0,0 +1,44 @@ +package org.petschko.rpgmakermv.decrypt; + +/** + * Author: Peter Dragicevic + * Authors-Website: https://petschko.org/ + * Date: 15.02.2021 + * Time: 23:34 + * + * Notes: CMD_Help Class + */ +class CMD_Help implements CMD_Command { + /** + * Runs the Command + * + * @param args - Command-Line commands + */ + @Override + public void run(String[] args) { + App.showMessage("Invalid Command \"" + args[1] + "\"!", CMD.STATUS_WARNING); + App.showMessage(""); + printHelp(); + } + + /** + * Prints help for the command + */ + @Override + public void printHelp() { + App.showMessage("Usage: java -jar \"RPG Maker MV Decrypter.jar\" [command] [help|args...]"); + App.showMessage(""); + App.showMessage(CMD.HELP_INDENT + "Commands:"); + App.showMessage(CMD.HELP_INDENT + " help - Shows this message"); + App.showMessage(CMD.HELP_INDENT + " decrypt - Decrypts Files of a Game-Directory"); + App.showMessage(CMD.HELP_INDENT + " encrypt - (Re-)Encrypts resource Files"); + App.showMessage(CMD.HELP_INDENT + " restore - Restores images of a Game-Directory"); + //App.showMessage(CMD.HELP_INDENT + " restoreproject - Restores a RPG-MV/MZ Project (Makes it editable again)"); + App.showMessage(CMD.HELP_INDENT + " key - Detects the Key and Displays it"); + App.showMessage(CMD.HELP_INDENT + " open - Opens an encrypted Image"); + App.showMessage(""); + App.showMessage(CMD.HELP_INDENT + "Display detailed help for each command:"); + App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" [command] help"); + App.showMessage(""); + } +} diff --git a/src/org/petschko/rpgmakermv/decrypt/CMD_Open.java b/src/org/petschko/rpgmakermv/decrypt/CMD_Open.java new file mode 100644 index 0000000..9560d13 --- /dev/null +++ b/src/org/petschko/rpgmakermv/decrypt/CMD_Open.java @@ -0,0 +1,86 @@ +package org.petschko.rpgmakermv.decrypt; + +import java.awt.Desktop; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * Author: Peter Dragicevic + * Authors-Website: https://petschko.org/ + * Date: 15.02.2021 + * Time: 23:41 + * + * Notes: CMD_Open Class + */ +class CMD_Open implements CMD_Command { + private String pathToFile; + + /** + * Runs the Command + * + * @param args - Command-Line commands + */ + @Override + public void run(String[] args) { + /*if(args.length < 2) { + App.showMessage("To less arguments, see help", CMD.STATUS_WARNING); + App.showMessage(""); + this.printHelp(); + CMD.exitCMD(CMD.STATUS_WARNING); + return; + } + + // Set Path to Project + pathToFile = args[1]; + App.showMessage("Open file: \"" + pathToFile + "\"..."); + + openFile();*/ + printHelp(); + CMD.exitCMD(CMD.STATUS_WARNING); + } + + /** + * Opens a RPG-MV/MZ Image-File + */ + public void openFile() { + try { + // todo fix issue file is deleted before shown + + // Restore image + org.petschko.lib.File file = new org.petschko.lib.File(pathToFile); + Decrypter decrypter = new Decrypter(); + decrypter.decryptFile(file, true); + + // Write to tmp file + File tmpFile = File.createTempFile("temp", ".png"); + Path tmpFilePath = Paths.get(tmpFile.getAbsolutePath()); + Files.write(tmpFilePath, file.getContent()); + + // Open and set delete delete tmp file + Desktop.getDesktop().open(tmpFile); + tmpFile.deleteOnExit(); + } catch (Exception e) { + e.printStackTrace(); + CMD.exitCMD(CMD.STATUS_ERROR); + } + + CMD.exitCMD(CMD.STATUS_OK); + } + + /** + * Prints help for the command + */ + @Override + public void printHelp() { + App.showMessage("open -> !! NOT IMPLEMENTED YET !!", CMD.STATUS_WARNING); + App.showMessage(""); + App.showMessage("Usage: java -jar \"RPG Maker MV Decrypter.jar\" open [path to rpgmvp|png_ file]"); + App.showMessage(""); + App.showMessage(CMD.HELP_INDENT + "Params: (Separate each param by a space, for paths use \"\" around the path)"); + App.showMessage(CMD.HELP_INDENT + " [path to rpgmvp|png_ file] - Path to the rpgmvp/png_ file you want to open"); + App.showMessage(""); + } +} diff --git a/src/org/petschko/rpgmakermv/decrypt/CMD_Restore.java b/src/org/petschko/rpgmakermv/decrypt/CMD_Restore.java new file mode 100644 index 0000000..a321d42 --- /dev/null +++ b/src/org/petschko/rpgmakermv/decrypt/CMD_Restore.java @@ -0,0 +1,107 @@ +package org.petschko.rpgmakermv.decrypt; + +/** + * Author: Peter Dragicevic + * Authors-Website: https://petschko.org/ + * Date: 15.02.2021 + * Time: 23:37 + * + * Notes: CMD_Restore Class + */ +class CMD_Restore implements CMD_Command { + private String pathToProject; + private String outputDir; + + // Decoder options + private boolean verifyDir = false; + private boolean ignoreFakeHeader = true; + private int headerLen = Decrypter.defaultHeaderLen; + + /** + * Runs the Command + * + * @param args - Command-Line commands + */ + @Override + public void run(String[] args) { + if(args.length < 2) { + App.showMessage("To less arguments, see help", CMD.STATUS_WARNING); + App.showMessage(""); + this.printHelp(); + CMD.exitCMD(CMD.STATUS_WARNING); + return; + } + + // Set Path to Project + pathToProject = args[1]; + App.showMessage("Set Project-Dir to: \"" + pathToProject + "\""); + + // Set Output-Dir + try { + outputDir = args[2]; + } catch(ArrayIndexOutOfBoundsException arEx) { + outputDir = Config.defaultOutputDir; + } + + App.showMessage("Set Output-Dir to: \"" + outputDir + "\""); + + if(args.length >= 4) + verifyDir = Boolean.parseBoolean(args[3]); + if(args.length >= 5) + ignoreFakeHeader = Boolean.parseBoolean(args[4]); + if(args.length >= 6) { + headerLen = Integer.parseInt(args[5]); + + // Ensure headerLen is at least 1 else default + if(headerLen < 1) + headerLen = Decrypter.defaultHeaderLen; + } + + handleFiles(); + } + + /** + * Handles Files to restore + */ + private void handleFiles() { + try { + RPGProject rpgProject = new RPGProject(pathToProject, verifyDir); + Decrypter decrypter = new Decrypter(); + + rpgProject.setOutputPath(outputDir); + decrypter.setIgnoreFakeHeader(ignoreFakeHeader); + decrypter.setHeaderLen(headerLen); + rpgProject.decryptFilesCmd(decrypter, true); + } catch(Exception e) { + e.printStackTrace(); + CMD.exitCMD(CMD.STATUS_ERROR); + } + } + + /** + * Prints help for the command + */ + @Override + public void printHelp() { + App.showMessage("Usage: java -jar \"RPG Maker MV Decrypter.jar\" restore [path to project] [(optional) output path] [(optional) verifyRpgDir - false|true]"); + App.showMessage(CMD.HELP_INDENT + " [(optional) ignoreFakeHeader - true|false] [(optional) headerLen]"); + App.showMessage(""); + App.showMessage(CMD.HELP_INDENT + "Params: (Separate each param by a space, for paths use \"\" around the path)"); + App.showMessage(CMD.HELP_INDENT + " [path to project] - Path to the RPG-MV/MZ Project were you want to restore images"); + App.showMessage(CMD.HELP_INDENT + " (optional) [output path] - Path where the restored files go | Default: \"output\" (out dir in program dir)"); + App.showMessage(CMD.HELP_INDENT + " (optional) [verifyRpgDir (false|true)] - Verifies if its a RPG-MV/MZ dir | Default: false"); + App.showMessage(CMD.HELP_INDENT + " (optional) [ignoreFakeHeader (true|false)] - Ignored the Files MV/MZ-Header | Default: true"); + App.showMessage(CMD.HELP_INDENT + "Header-Values: (usually REALLY not needed)"); + App.showMessage(CMD.HELP_INDENT + " (very optional) [headerLen] - Byte length of the Header | Default: " + this.headerLen); + App.showMessage(""); + App.showMessage(CMD.HELP_INDENT + "Examples of full commands with all params:"); + App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" restore \"C:\\my rpg mv game\\\" \"output\" false true"); + App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" restore \"C:\\my rpg mv game\\\" \"C:\\my rpg mv game\\\" true false"); + App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" restore \"C:\\my rpg mv game\\\" \"C:\\my rpg mv game\\\" true false 14"); + App.showMessage(""); + App.showMessage(CMD.HELP_INDENT + "The first command restores all files inside this programs output dir, dont check if its a RPG-MV/MZ dir and ignores if the header of the files is the RPG-MV/MZ header"); + App.showMessage(CMD.HELP_INDENT + "The second command restores all files inside the game directory, it checks if RPG-MV/MZ dir and if Header is valid"); + App.showMessage(CMD.HELP_INDENT + "The third command does the same as the 2nd, just shows how you can modify the header length"); + App.showMessage(""); + } +} diff --git a/src/org/petschko/rpgmakermv/decrypt/CMD_RestoreProject.java b/src/org/petschko/rpgmakermv/decrypt/CMD_RestoreProject.java new file mode 100644 index 0000000..17099f0 --- /dev/null +++ b/src/org/petschko/rpgmakermv/decrypt/CMD_RestoreProject.java @@ -0,0 +1,33 @@ +package org.petschko.rpgmakermv.decrypt; + +/** + * Author: Peter Dragicevic + * Authors-Website: https://petschko.org/ + * Date: 15.02.2021 + * Time: 23:37 + * + * Notes: CMD_RestoreProject Class + */ +class CMD_RestoreProject implements CMD_Command { + /** + * Runs the Command + * + * @param args - Command-Line commands + */ + @Override + public void run(String[] args) { + printHelp(); + CMD.exitCMD(CMD.STATUS_WARNING); + } + + /** + * Prints help for the command + */ + @Override + public void printHelp() { + App.showMessage("restoreproject -> !! NOT IMPLEMENTED YET !!", CMD.STATUS_WARNING); + App.showMessage(""); + App.showMessage("Usage: java -jar \"RPG Maker MV Decrypter.jar\" restoreproject"); + App.showMessage(""); + } +} diff --git a/src/org/petschko/rpgmakermv/decrypt/Decrypter.java b/src/org/petschko/rpgmakermv/decrypt/Decrypter.java index bb630b5..605490b 100644 --- a/src/org/petschko/rpgmakermv/decrypt/Decrypter.java +++ b/src/org/petschko/rpgmakermv/decrypt/Decrypter.java @@ -5,6 +5,7 @@ import org.json.JSONObject; import org.petschko.lib.File; +import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.nio.file.FileSystemException; import java.util.ArrayList; @@ -20,8 +21,8 @@ * Notes: Decrypter class */ class Decrypter { - static final String pngHeader = "89504E470D0A1A0A0000000D49484452"; - static byte[] pngHeaderBytes = null; + private static final String pngHeader = "89504E470D0A1A0A0000000D49484452"; + private static byte[] pngHeaderBytes = null; static final int defaultHeaderLen = 16; static final String defaultSignature = "5250474d56000000"; @@ -30,6 +31,7 @@ class Decrypter { private String decryptCode = null; private String[] realDecryptCode = null; + private byte[] rpgHeaderBytes = null; private int headerLen = 16; private String signature = "5250474d56000000"; private String version = "000301"; @@ -90,6 +92,22 @@ private void setRealDecryptCode(@NotNull String[] realDecryptCode) { this.realDecryptCode = realDecryptCode; } + /** + * Returns the RPG-Header-Bytes (aka fake header) + * + * @return - RPG-Header-Bytes + */ + private byte[] getRpgHeaderBytes() { + if(this.rpgHeaderBytes == null) + this.generateRpgHeaderBytes(); + + return rpgHeaderBytes; + } + + private void setRpgHeaderBytes(byte[] rpgHeaderBytes) { + this.rpgHeaderBytes = rpgHeaderBytes; + } + /** * Returns the Byte-Length of the File-Header * @@ -202,13 +220,59 @@ private void calcRealDecryptionCode() throws NullPointerException { } /** - * Decrypts the File (Header) and removes the Encryption-Header + * (Re-)Encrypts the File and adds the File-Header * - * @param file - Encrypted File + * @param file - File which should be encrypted + * @param rpgMakerMv - Encrypt as RPG-Maker-MV File * @throws Exception - Various Exceptions */ - void decryptFile(File file) throws Exception { - decryptFile(file, false); + void encryptFile(File file, boolean rpgMakerMv) throws Exception { + try { + if(! file.load()) + throw new FileSystemException(file.getFilePath(), "", "Can't load File-Content..."); + } catch(Exception e) { + e.printStackTrace(); + + return; + } + + // Check if all required external stuff is here + if(this.getDecryptCode() == null) + throw new NullPointerException("Encryption-Code is not set!"); + if(file.getContent() == null) + throw new NullPointerException("File-Content is not loaded!"); + if(file.getContent().length < (this.getHeaderLen())) + throw new Exception("File is to short (<" + (this.getHeaderLen()) + " Bytes)"); + + // Get Content + byte[] content = file.getContent(); + + // Encrypt + if(content.length > 0) { + for(int i = 0; i < this.getHeaderLen(); i++) { + content[i] = (byte) (content[i] ^ (byte) Integer.parseInt(this.getRealDecryptCode()[i], 16)); + } + } + + // Add header and update File-Content + file.setContent(this.addFileHeader(content)); + file.changeExtension(file.fakeExtByRealExt(rpgMakerMv)); + } + + /** + * Adds the RPG-MV/MZ File-Header to the content + * + * @param content - Content where the header should be added + * @return - Header before content + */ + private byte[] addFileHeader(byte[] content) { + byte[] header = this.getRpgHeaderBytes(); + + ByteBuffer buffer = ByteBuffer.wrap(new byte[header.length + content.length]); + buffer.put(header); + buffer.put(content); + + return buffer.array(); } /** @@ -250,7 +314,6 @@ void decryptFile(File file, boolean restorePictures) throws Exception { // Remove Fake-Header from rest content = Decrypter.getByteArray(content, this.getHeaderLen()); - if(content.length > 0) { for(int i = 0; i < this.getHeaderLen(); i++) { if(restorePictures) // Restore Pictures @@ -273,14 +336,7 @@ void decryptFile(File file, boolean restorePictures) throws Exception { */ boolean checkFakeHeader(byte[] content) { byte[] header = Decrypter.getByteArray(content, 0, this.getHeaderLen()); - byte[] refBytes = new byte[this.getHeaderLen()]; - String refStr = this.getSignature() + this.getVersion() + this.getRemain(); - - // Generate reference bytes - for(int i = 0; i < this.getHeaderLen(); i++) { - int subStrStart = i * 2; - refBytes[i] = (byte) Integer.parseInt(refStr.substring(subStrStart, subStrStart + 2), 16); - } + byte[] refBytes = this.getRpgHeaderBytes(); // Verify header (Check if its an encrypted file) for(int i = 0; i < this.getHeaderLen(); i++) { @@ -292,7 +348,7 @@ boolean checkFakeHeader(byte[] content) { } /** - * Detect the Decryption-Code from the given File + * Detect the Decryption-Code from the given Json-File * * @param file - JSON-File with Decryption-Key * @param keyName - Key-Name of the Decryption-Key @@ -300,7 +356,7 @@ boolean checkFakeHeader(byte[] content) { * @throws NullPointerException - System-File is null * @throws FileSystemException - Can't load File */ - void detectEncryptionKey(File file, String keyName) throws JSONException, NullPointerException, FileSystemException { + void detectEncryptionKeyFromJson(File file, String keyName) throws JSONException, NullPointerException, FileSystemException { try { if(! file.load()) throw new FileSystemException(file.getFilePath(), "", "Can't load File-Content..."); @@ -326,9 +382,57 @@ void detectEncryptionKey(File file, String keyName) throws JSONException, NullPo key = jsonObj.getString(keyName); + App.showMessage("Key found :)!", CMD.STATUS_OK); this.setDecryptCode(key); } + /** + * Detects the Key from the given Encrypted-Image-File + * + * @param file - Encrypted-Image-File + */ + void detectEncryptionKeyFromImage(File file) throws Exception { + // Only encrypted images + if(! file.isImage() || ! file.isFileEncryptedExt()) + return; + + try { + if(! file.load()) + throw new FileSystemException(file.getFilePath(), "", "Can't load File-Content..."); + } catch(Exception e) { + e.printStackTrace(); + + return; + } + + // Check if all required external stuff is here + if(file.getContent() == null) + throw new NullPointerException("File-Content is not loaded!"); + if(file.getContent().length < (this.getHeaderLen() * 2)) + throw new Exception("File is to short (<" + (this.getHeaderLen() * 2) + " Bytes)"); + + // Get Content + byte[] content = file.getContent(); + byte[] keyBytes = new byte[this.getHeaderLen()]; + + // Check Header + if(! this.isIgnoreFakeHeader()) + if(! this.checkFakeHeader(content)) + throw new Exception("Header is Invalid!"); + + // Remove Fake-Header from rest + content = Decrypter.getByteArray(content, this.getHeaderLen()); + + if(content.length > 0) { + for(int i = 0; i < this.getHeaderLen(); i++) { + keyBytes[i] = (byte) (content[i] ^ this.getPNGHeaderByteArray()[i]); + } + } + + App.showMessage("Key found :) - Inside Image!", CMD.STATUS_OK); + this.setDecryptCode(bytesToHex(keyBytes)); + } + /** * Returns the PNG-Header Byte Array * @@ -349,6 +453,22 @@ private byte[] getPNGHeaderByteArray() { return pngHeaderBytes; } + /** + * Generates the RPG-Header byte-array (aka fake-Header) + */ + private void generateRpgHeaderBytes() { + byte[] refBytes = new byte[this.getHeaderLen()]; + String refStr = this.getSignature() + this.getVersion() + this.getRemain(); + + // Generate reference bytes + for(int i = 0; i < this.getHeaderLen(); i++) { + int subStrStart = i * 2; + refBytes[i] = (byte) Integer.parseInt(refStr.substring(subStrStart, subStrStart + 2), 16); + } + + this.rpgHeaderBytes = refBytes; + } + /** * Get a new Byte-Array with given start pos and length * @@ -391,4 +511,19 @@ private static byte[] getByteArray(byte[] byteArray, int startPos, int length) { private static byte[] getByteArray(byte[] byteArray, int startPos) { return getByteArray(byteArray, startPos, -1); } + + /** + * Converts bytes arrays to a hex string + * + * @param bytes - Byte-Array + * @return - Hex-String + */ + private static String bytesToHex(byte[] bytes) { + StringBuilder sb = new StringBuilder(); + + for(byte b : bytes) + sb.append(String.format("%02x", b)); + + return sb.toString(); + } } diff --git a/src/org/petschko/rpgmakermv/decrypt/Finder.java b/src/org/petschko/rpgmakermv/decrypt/Finder.java index b53d5e0..44f1314 100644 --- a/src/org/petschko/rpgmakermv/decrypt/Finder.java +++ b/src/org/petschko/rpgmakermv/decrypt/Finder.java @@ -68,7 +68,7 @@ static String testEncryptionKeyNames(File systemFile) { for(String keyName : keyNames) { try { - d.detectEncryptionKey(systemFile, keyName); + d.detectEncryptionKeyFromJson(systemFile, keyName); if(d.getDecryptCode() != null) { result = keyName; diff --git a/src/org/petschko/rpgmakermv/decrypt/GUI.java b/src/org/petschko/rpgmakermv/decrypt/GUI.java index 7d9dbbe..5fb77b5 100644 --- a/src/org/petschko/rpgmakermv/decrypt/GUI.java +++ b/src/org/petschko/rpgmakermv/decrypt/GUI.java @@ -321,7 +321,7 @@ protected Void doInBackground() throws Exception { if(decrypter.getDecryptCode() == null) { this.progressMonitor.setNote("Try to detect Encryption-Key..."); try { - decrypter.detectEncryptionKey(rpgProject.getSystem(), rpgProject.getEncryptionKeyName()); + decrypter.detectEncryptionKeyFromJson(rpgProject.getSystem(), rpgProject.getEncryptionKeyName()); } catch(FileSystemException fileSysEx) { // Can't load File fileSysEx.printStackTrace(); diff --git a/src/org/petschko/rpgmakermv/decrypt/RPGProject.java b/src/org/petschko/rpgmakermv/decrypt/RPGProject.java index 1246729..b5c307e 100644 --- a/src/org/petschko/rpgmakermv/decrypt/RPGProject.java +++ b/src/org/petschko/rpgmakermv/decrypt/RPGProject.java @@ -20,10 +20,13 @@ class RPGProject { private String path; private String outputPath = Config.defaultOutputDir; private File system = null; + private File encryptedImgFile = null; private String encryptionKeyName = "encryptionKey"; private boolean isEncrypted = true; + private boolean isMV = true; private ArrayList files = new ArrayList<>(); private ArrayList encryptedFiles = new ArrayList<>(); + private ArrayList resourceFiles = new ArrayList<>(); /** * RPGProject Constructor @@ -51,6 +54,8 @@ class RPGProject { if(this.isEncrypted()) this.findEncryptedFiles(); + else + this.findResourceFiles(); } /** @@ -107,6 +112,24 @@ void setSystem(@NotNull File system) { this.system = system; } + /** + * Returns the encrypted image file + * + * @return - encrypted image file or null if none found + */ + File getEncryptedImgFile() { + return encryptedImgFile; + } + + /** + * Sets the encrypted image file + * + * @param encryptedImgFile - encrypted image file + */ + private void setEncryptedImgFile(File encryptedImgFile) { + this.encryptedImgFile = encryptedImgFile; + } + /** * Returns the EncryptionKeyName * @@ -150,6 +173,24 @@ private void setEncrypted(boolean encrypted) { isEncrypted = encrypted; } + /** + * Returns if the Project is a MV Project + * + * @return - Is MV-Project + */ + boolean isMV() { + return isMV; + } + + /** + * Sets if the Project is an MV-Project + * + * @param isMV - Is MV-Project + */ + void setMV(boolean isMV) { + this.isMV = isMV; + } + /** * Returns the File List of the Project * @@ -168,6 +209,15 @@ ArrayList getEncryptedFiles() { return encryptedFiles; } + /** + * Returns the Resource-File List + * + * @return - Resource-File List + */ + public ArrayList getResourceFiles() { + return resourceFiles; + } + /** * Load all Files of the Project into an ArrayList */ @@ -191,7 +241,7 @@ private void checkIfEncrypted() { Decrypter d = new Decrypter(); try { - d.detectEncryptionKey(this.getSystem(), this.getEncryptionKeyName()); + d.detectEncryptionKeyFromJson(this.getSystem(), this.getEncryptionKeyName()); } catch(JSONException e) { this.setEncrypted(false); } catch(Exception ex) { @@ -212,15 +262,41 @@ private void checkIfEncrypted() { } /** - * Find all Encrypted-Files of the Project and save them into an ArrayList + * Finds an Encrypted image and assigns it to the class */ - private void findEncryptedFiles() { - if(! this.isEncrypted()) + public void findEncryptedImg() { + if(this.getEncryptedImgFile() != null) return; for(File file : this.getFiles()) { - if(file.isFileEncryptedExt()) + if(file.isFileEncryptedExt() && file.isImage()) { + this.setEncryptedImgFile(file); + return; + } + } + } + + /** + * Find all Encrypted-Files of the Project and save them into an ArrayList + */ + private void findEncryptedFiles() { + for(File file : this.getFiles()) { + if(file.isFileEncryptedExt()) { this.getEncryptedFiles().add(file); + + if(this.getEncryptedImgFile() == null && file.isImage()) + this.setEncryptedImgFile(file); + } + } + } + + /** + * Find all Resource-Files of the Project and save them into an ArrayList + */ + private void findResourceFiles() { + for(File file : this.getFiles()) { + if(file.canBeEncrypted()) + this.getResourceFiles().add(file); } } @@ -234,32 +310,96 @@ private void findSystemFile() { this.setSystem(system); } + /** + * Encrypts all Resource-Files + * + * @param encrypter - Decrypter (aka Encrypter) Object + * @throws Exception - Key not Found Exception + */ + void encryptFilesCmd(Decrypter encrypter) throws Exception { + // Check if Output-Dir exists + if(! File.existsDir(this.getOutputPath())) { + App.showMessage("Output-dir \"" + this.getOutputPath() + "\" doesn't exists!", CMD.STATUS_ERROR); + return; + } + + if(encrypter.getDecryptCode() == null) { + try { + encrypter.detectEncryptionKeyFromJson(this.getSystem(), this.getEncryptionKeyName()); + } catch(Exception e) { + throw new Exception(e); + } + } + + // Load resource files anyway + if(this.isEncrypted()) + this.findResourceFiles(); + + for(int i = 0; i < this.getResourceFiles().size(); i++) { + File currentFile = this.getResourceFiles().get(i); + + try { + App.showMessage("Encrypting: " + currentFile.getFilePath()); + + encrypter.encryptFile(currentFile, this.isMV()); + } catch(Exception e) { + e.printStackTrace(); + } finally { + this.saveFile(currentFile); + } + } + } + /** * Decrypts all Encrypted Files of the Project * * @param decrypter - Decrypter Object * @throws Exception - Key not Found Exception */ - void decryptFiles(Decrypter decrypter) throws Exception { + void decryptFilesCmd(Decrypter decrypter) throws Exception { + decryptFilesCmd(decrypter, false); + } + + /** + * Decrypts all Encrypted Files of the Project + * + * @param decrypter - Decrypter Object + * @param restoreImages - Restores images instead of decrypting + * @throws Exception - Key not Found Exception + */ + void decryptFilesCmd(Decrypter decrypter, boolean restoreImages) throws Exception { // Check if Output-Dir exists if(! File.existsDir(this.getOutputPath())) { - App.showMessage("Output-dir \"" + this.getOutputPath() + "\" doesn't exists!"); + App.showMessage("Output-dir \"" + this.getOutputPath() + "\" doesn't exists!", CMD.STATUS_ERROR); return; } if(decrypter.getDecryptCode() == null) { try { - decrypter.detectEncryptionKey(this.getSystem(), this.getEncryptionKeyName()); + decrypter.detectEncryptionKeyFromJson(this.getSystem(), this.getEncryptionKeyName()); } catch(Exception e) { throw new Exception(e); } } + // Ensure files are loaded + if(! this.isEncrypted()) + this.findEncryptedFiles(); + for(int i = 0; i < this.getEncryptedFiles().size(); i++) { File currentFile = this.getEncryptedFiles().get(i); + // Only images if restore images + if(restoreImages && (! currentFile.isImage() || ! currentFile.isFileEncryptedExt())) + continue; + try { - decrypter.decryptFile(currentFile); + if(restoreImages) + App.showMessage("Restoring Image" + currentFile.getFilePath()); + else + App.showMessage("Decrypting: " + currentFile.getFilePath()); + + decrypter.decryptFile(currentFile, restoreImages); } catch(Exception e) { e.printStackTrace(); } finally { @@ -301,10 +441,10 @@ void saveFile(File file, boolean overwriteExisting) { // Check if dir exists if not create it if(File.existsDir(newPath, true)) { file.changePathToFile(newPath); - App.showMessage("Save File to: " + file.getFilePath()); + App.showMessage("Save File to: " + file.getFilePath(), CMD.STATUS_OK); file.save(overwriteExisting); } else - App.showMessage("Can't create Directory for File: " + newPath + file.getFullFileName()); + App.showMessage("Can't create Directory for File: " + newPath + file.getFullFileName(), CMD.STATUS_ERROR); // Clean up Memory file.unloadContent(); From d6ae580bc9b6e9888a8ab0d695974b3e00d58f53 Mon Sep 17 00:00:00 2001 From: Petschko Date: Thu, 18 Feb 2021 22:48:35 +0100 Subject: [PATCH 2/8] Changed version --- src/org/petschko/rpgmakermv/decrypt/Config.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/petschko/rpgmakermv/decrypt/Config.java b/src/org/petschko/rpgmakermv/decrypt/Config.java index 6e5965d..df957fa 100644 --- a/src/org/petschko/rpgmakermv/decrypt/Config.java +++ b/src/org/petschko/rpgmakermv/decrypt/Config.java @@ -12,7 +12,7 @@ */ class Config { // Program Info - static final String versionNumber = "0.1.5.0"; + static final String versionNumber = "0.2.0"; static final String version = "v" + versionNumber + " Alpha"; static final String programName = "RPG-Maker MV Decrypter"; static final String projectPageURL = "https://github.com/Petschko/Java-RPG-Maker-MV-Decrypter"; From 10f1ea09161dcf17763c6be5264457cfab44b8a7 Mon Sep 17 00:00:00 2001 From: Petschko Date: Fri, 19 Feb 2021 13:58:49 +0100 Subject: [PATCH 3/8] Added MZ to program name --- src/org/petschko/rpgmakermv/decrypt/Config.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/petschko/rpgmakermv/decrypt/Config.java b/src/org/petschko/rpgmakermv/decrypt/Config.java index df957fa..840b962 100644 --- a/src/org/petschko/rpgmakermv/decrypt/Config.java +++ b/src/org/petschko/rpgmakermv/decrypt/Config.java @@ -14,7 +14,7 @@ class Config { // Program Info static final String versionNumber = "0.2.0"; static final String version = "v" + versionNumber + " Alpha"; - static final String programName = "RPG-Maker MV Decrypter"; + static final String programName = "RPG-Maker MV/MZ Decrypter"; static final String projectPageURL = "https://github.com/Petschko/Java-RPG-Maker-MV-Decrypter"; static final String projectBugReportURL = "https://github.com/Petschko/Java-RPG-Maker-MV-Decrypter/issues"; static final String projectLicenceURL = "https://github.com/Petschko/Java-RPG-Maker-MV-Decrypter/blob/master/LICENCE"; From 3da372c5a17f2ed4b28a88010770d057a55b944b Mon Sep 17 00:00:00 2001 From: Petschko Date: Fri, 19 Feb 2021 13:59:23 +0100 Subject: [PATCH 4/8] Formatted help commands --- .../rpgmakermv/decrypt/CMD_Decrypt.java | 29 +++++++++++-------- .../rpgmakermv/decrypt/CMD_DetectKey.java | 12 ++++---- .../rpgmakermv/decrypt/CMD_Encrypt.java | 27 +++++++++-------- .../petschko/rpgmakermv/decrypt/CMD_Help.java | 8 +++-- .../rpgmakermv/decrypt/CMD_Restore.java | 18 +++++++----- 5 files changed, 55 insertions(+), 39 deletions(-) diff --git a/src/org/petschko/rpgmakermv/decrypt/CMD_Decrypt.java b/src/org/petschko/rpgmakermv/decrypt/CMD_Decrypt.java index 8213ef0..5d61e1b 100644 --- a/src/org/petschko/rpgmakermv/decrypt/CMD_Decrypt.java +++ b/src/org/petschko/rpgmakermv/decrypt/CMD_Decrypt.java @@ -106,30 +106,35 @@ private void handleFiles() { */ @Override public void printHelp() { - App.showMessage("Usage: java -jar \"RPG Maker MV Decrypter.jar\" decrypt [path to decrypt project] [(optional) output path] [(optional) verifyRpgDir - false|true]"); - App.showMessage(CMD.HELP_INDENT + " [(optional) ignoreFakeHeader - true|false] [(optional) key - auto|keyValue]"); - App.showMessage(CMD.HELP_INDENT + " [(optional) headerLen] [(optional) hsignature] [(optional) hversion] [(optional) hremain]"); + App.showMessage("Decrypt Files"); + App.showMessage(""); + App.showMessage("Usage: java -jar \"RPG Maker MV Decrypter.jar\" decrypt [path to decrypt project] [(optional) output path] [(optional) verifyRpgDir - false|true] [(optional) ignoreFakeHeader - true|false] [(optional) key - auto|keyValue] [(optional) headerLen] [(optional) hsignature] [(optional) hversion] [(optional) hremain]"); App.showMessage(""); App.showMessage(CMD.HELP_INDENT + "Params: (Separate each param by a space, for paths use \"\" around the path)"); - App.showMessage(CMD.HELP_INDENT + " [path to decrypt project] - Path to the RPG-MV/MZ Project you want to decrypt"); - App.showMessage(CMD.HELP_INDENT + " (optional) [output path] - Path where the decrypted files go | Default: \"output\" (out dir in program dir)"); - App.showMessage(CMD.HELP_INDENT + " (optional) [verifyRpgDir (false|true)] - Verifies if its a RPG-MV/MZ dir | Default: false"); + App.showMessage(CMD.HELP_INDENT + " [path to decrypt project] - Path to the RPG-MV/MZ Project you want to decrypt"); + App.showMessage(CMD.HELP_INDENT + " (optional) [output path] - Path where the decrypted files go"); + App.showMessage(CMD.HELP_INDENT + " Default: \"output\" (out dir in program dir)"); + App.showMessage(CMD.HELP_INDENT + " (optional) [verifyRpgDir (false|true)]"); + App.showMessage(CMD.HELP_INDENT + " - Verifies if its a RPG-MV/MZ dir | Default: false"); App.showMessage(CMD.HELP_INDENT + " (optional) [ignoreFakeHeader (true|false)] - Ignored the Files MV/MZ-Header | Default: true"); App.showMessage(CMD.HELP_INDENT + " (optional) [key (auto|keyValue)] - Decryption key or auto detection | Default: auto"); + App.showMessage(""); App.showMessage(CMD.HELP_INDENT + "Header-Values: (usually REALLY not needed)"); - App.showMessage(CMD.HELP_INDENT + " (very optional) [headerLen] - Byte length of the Header | Default: " + this.headerLen); + App.showMessage(CMD.HELP_INDENT + " (very optional) [headerLen] - Byte length of the Header | Default: " + this.headerLen); App.showMessage(CMD.HELP_INDENT + " (very optional) [hsignature] - Signature of the Header | Default: " + this.signature); - App.showMessage(CMD.HELP_INDENT + " (very optional) [hversion] - Version of the Header | Default: " + this.version); - App.showMessage(CMD.HELP_INDENT + " (very optional) [hremain] - Remain of the Header | Default: " + this.remain); + App.showMessage(CMD.HELP_INDENT + " (very optional) [hversion] - Version of the Header | Default: " + this.version); + App.showMessage(CMD.HELP_INDENT + " (very optional) [hremain] - Remain of the Header | Default: " + this.remain); App.showMessage(""); App.showMessage(CMD.HELP_INDENT + "Examples of full commands with all params:"); App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" decrypt \"C:\\my rpg mv game\\\" \"output\" false true auto"); App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" decrypt \"C:\\my rpg mv game\\\" \"C:\\my rpg mv game\\\" true false d41d8cd98f00b204e9800998ecf8427e"); App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" decrypt \"C:\\my rpg mv game\\\" \"C:\\my rpg mv game\\\" true false d41d8cd98f00b204e9800998ecf8427e 14 5250474d56000000 000301 00000000"); App.showMessage(""); - App.showMessage(CMD.HELP_INDENT + "The first command extracts all files inside this programs output dir, dont check if its a RPG-MV/MZ dir, ignored if the header of the files is the RPG-MV/MZ header and auto-detects the key"); - App.showMessage(CMD.HELP_INDENT + "The second command extract all files inside the game directory, checks if RPG-MV/MZ dir and Header is valid with the key d41d8cd98f00b204e9800998ecf8427e"); - App.showMessage(CMD.HELP_INDENT + "The third command does the same as the 2nd, just shows how you can modify the values of the header"); + App.showMessage(CMD.HELP_INDENT + "- The first command extracts all files inside this programs output dir, dont check if its a RPG-MV/MZ dir,"); + App.showMessage(CMD.HELP_INDENT + " ignored if the header of the files is the RPG-MV/MZ header and auto-detects the key"); + App.showMessage(CMD.HELP_INDENT + "- The second command extract all files inside the game directory, checks if RPG-MV/MZ dir and Header is"); + App.showMessage(CMD.HELP_INDENT + " valid with the key d41d8cd98f00b204e9800998ecf8427e"); + App.showMessage(CMD.HELP_INDENT + "- The third command does the same as the 2nd, just shows how you can modify the values of the header"); App.showMessage(""); } } diff --git a/src/org/petschko/rpgmakermv/decrypt/CMD_DetectKey.java b/src/org/petschko/rpgmakermv/decrypt/CMD_DetectKey.java index 26045ed..f0798fa 100644 --- a/src/org/petschko/rpgmakermv/decrypt/CMD_DetectKey.java +++ b/src/org/petschko/rpgmakermv/decrypt/CMD_DetectKey.java @@ -125,12 +125,14 @@ private void showKey(String key) { */ @Override public void printHelp() { - App.showMessage("Usage: java -jar \"RPG Maker MV Decrypter.jar\" key [path to project]"); - App.showMessage(CMD.HELP_INDENT + " [(optional) ask before image keysearch (true|false)] [(optional) headerLen]"); + App.showMessage("Detects the En-/Decryption-Key"); + App.showMessage(""); + App.showMessage("Usage: java -jar \"RPG Maker MV Decrypter.jar\" key [path to project] [(optional) ask before image keysearch (true|false)] [(optional) headerLen]"); App.showMessage(""); App.showMessage(CMD.HELP_INDENT + "Params: (Separate each param by a space, for paths use \"\" around the path)"); App.showMessage(CMD.HELP_INDENT + " [path to project] - Path to the RPG-MV/MZ Project were you want to get the key from"); App.showMessage(CMD.HELP_INDENT + " (optional) [ask before image keysearch (true|false)] - Ask before search key in images | Default: true"); + App.showMessage(""); App.showMessage(CMD.HELP_INDENT + "Header-Values: (usually REALLY not needed)"); App.showMessage(CMD.HELP_INDENT + " (very optional) [headerLen] - Byte length of the Header | Default: " + this.headerLen); App.showMessage(""); @@ -139,9 +141,9 @@ public void printHelp() { App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" key \"C:\\my rpg mv game\\\" false"); App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" key \"C:\\my rpg mv game\\\" false 14"); App.showMessage(""); - App.showMessage(CMD.HELP_INDENT + "The first command returns the key of the Project if found also asks before searching in images"); - App.showMessage(CMD.HELP_INDENT + "The second command returns the key of the Project if found but skips the image question"); - App.showMessage(CMD.HELP_INDENT + "The third command does the same as the 2nd, just shows how you can modify the header length"); + App.showMessage(CMD.HELP_INDENT + "- The first command returns the key of the Project if found also asks before searching in images"); + App.showMessage(CMD.HELP_INDENT + "- The second command returns the key of the Project if found but skips the image question"); + App.showMessage(CMD.HELP_INDENT + "- The third command does the same as the 2nd, just shows how you can modify the header length"); App.showMessage(""); } } diff --git a/src/org/petschko/rpgmakermv/decrypt/CMD_Encrypt.java b/src/org/petschko/rpgmakermv/decrypt/CMD_Encrypt.java index c3fb8e9..a6b07a9 100644 --- a/src/org/petschko/rpgmakermv/decrypt/CMD_Encrypt.java +++ b/src/org/petschko/rpgmakermv/decrypt/CMD_Encrypt.java @@ -103,29 +103,32 @@ private void handleFiles() { */ @Override public void printHelp() { - App.showMessage("Usage: java -jar \"RPG Maker MV Decrypter.jar\" encrypt [path to encrypt files dir] [(optional) output path]"); - App.showMessage(CMD.HELP_INDENT + " [(optional) to MV - true|false] [(optional) key - auto|keyValue]"); - App.showMessage(CMD.HELP_INDENT + " [(optional) headerLen] [(optional) hsignature] [(optional) hversion] [(optional) hremain]"); + App.showMessage("Encrypts files back to MV/MZ"); + App.showMessage(""); + App.showMessage("Usage: java -jar \"RPG Maker MV Decrypter.jar\" encrypt [path to encrypt files dir] [(optional) output path] [(optional) to MV - true|false] [(optional) key - auto|keyValue] [(optional) headerLen] [(optional) hsignature] [(optional) hversion] [(optional) hremain]"); App.showMessage(""); App.showMessage(CMD.HELP_INDENT + "Params: (Separate each param by a space, for paths use \"\" around the path)"); - App.showMessage(CMD.HELP_INDENT + " [path to encrypt files dir] - Path to the File-Dir/Project you want to encrypt"); - App.showMessage(CMD.HELP_INDENT + " (optional) [output path] - Path where the encrypted files go | Default: \"output\" (out dir in program dir)"); - App.showMessage(CMD.HELP_INDENT + " (optional) [to MV (true|false)] - Encrypts files for MV (MZ on false) | Default: true"); + App.showMessage(CMD.HELP_INDENT + " [path to encrypt files dir] - Path to the File-Dir/Project you want to encrypt"); + App.showMessage(CMD.HELP_INDENT + " (optional) [output path] - Path where the encrypted files go"); + App.showMessage(CMD.HELP_INDENT + " Default: \"output\" (out dir in program dir)"); + App.showMessage(CMD.HELP_INDENT + " (optional) [to MV (true|false)] - Encrypts files for MV (MZ on false) | Default: true"); App.showMessage(CMD.HELP_INDENT + " (optional) [key (auto|keyValue)] - Encryption key or auto detection | Default: auto"); + App.showMessage(""); App.showMessage(CMD.HELP_INDENT + "Header-Values: (usually REALLY not needed)"); - App.showMessage(CMD.HELP_INDENT + " (very optional) [headerLen] - Byte length of the Header | Default: " + this.headerLen); + App.showMessage(CMD.HELP_INDENT + " (very optional) [headerLen] - Byte length of the Header | Default: " + this.headerLen); App.showMessage(CMD.HELP_INDENT + " (very optional) [hsignature] - Signature of the Header | Default: " + this.signature); - App.showMessage(CMD.HELP_INDENT + " (very optional) [hversion] - Version of the Header | Default: " + this.version); - App.showMessage(CMD.HELP_INDENT + " (very optional) [hremain] - Remain of the Header | Default: " + this.remain); + App.showMessage(CMD.HELP_INDENT + " (very optional) [hversion] - Version of the Header | Default: " + this.version); + App.showMessage(CMD.HELP_INDENT + " (very optional) [hremain] - Remain of the Header | Default: " + this.remain); App.showMessage(""); App.showMessage(CMD.HELP_INDENT + "Examples of full commands with all params:"); App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" encrypt \"C:\\my rpg mv game\\\" \"output\" true auto"); App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" encrypt \"C:\\my rpg mv game\\\" \"C:\\my rpg mv game\\\" true d41d8cd98f00b204e9800998ecf8427e"); App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" encrypt \"C:\\my rpg mv game\\\" \"C:\\my rpg mv game\\\" true d41d8cd98f00b204e9800998ecf8427e 14 5250474d56000000 000301 00000000"); App.showMessage(""); - App.showMessage(CMD.HELP_INDENT + "The first command encrypts all files to this programs output dir, as a MV-Files and auto-detects the key"); - App.showMessage(CMD.HELP_INDENT + "The second command encrypts all files inside the game directory, as MV-Files with the key d41d8cd98f00b204e9800998ecf8427e"); - App.showMessage(CMD.HELP_INDENT + "The third command does the same as the 2nd, just shows how you can modify the values of the header"); + App.showMessage(CMD.HELP_INDENT + "- The first command encrypts all files to this programs output dir, as a MV-Files and auto-detects the key"); + App.showMessage(CMD.HELP_INDENT + "- The second command encrypts all files inside the game directory, as MV-Files"); + App.showMessage(CMD.HELP_INDENT + " with the key d41d8cd98f00b204e9800998ecf8427e"); + App.showMessage(CMD.HELP_INDENT + "- The third command does the same as the 2nd, just shows how you can modify the values of the header"); App.showMessage(""); } } diff --git a/src/org/petschko/rpgmakermv/decrypt/CMD_Help.java b/src/org/petschko/rpgmakermv/decrypt/CMD_Help.java index 37efd8a..5c28cac 100644 --- a/src/org/petschko/rpgmakermv/decrypt/CMD_Help.java +++ b/src/org/petschko/rpgmakermv/decrypt/CMD_Help.java @@ -26,16 +26,18 @@ public void run(String[] args) { */ @Override public void printHelp() { + App.showMessage("Help:"); + App.showMessage(""); App.showMessage("Usage: java -jar \"RPG Maker MV Decrypter.jar\" [command] [help|args...]"); App.showMessage(""); App.showMessage(CMD.HELP_INDENT + "Commands:"); - App.showMessage(CMD.HELP_INDENT + " help - Shows this message"); + App.showMessage(CMD.HELP_INDENT + " help - Shows this message"); App.showMessage(CMD.HELP_INDENT + " decrypt - Decrypts Files of a Game-Directory"); App.showMessage(CMD.HELP_INDENT + " encrypt - (Re-)Encrypts resource Files"); App.showMessage(CMD.HELP_INDENT + " restore - Restores images of a Game-Directory"); //App.showMessage(CMD.HELP_INDENT + " restoreproject - Restores a RPG-MV/MZ Project (Makes it editable again)"); - App.showMessage(CMD.HELP_INDENT + " key - Detects the Key and Displays it"); - App.showMessage(CMD.HELP_INDENT + " open - Opens an encrypted Image"); + App.showMessage(CMD.HELP_INDENT + " key - Detects the Key and Displays it"); + //App.showMessage(CMD.HELP_INDENT + " open - Opens an encrypted Image"); App.showMessage(""); App.showMessage(CMD.HELP_INDENT + "Display detailed help for each command:"); App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" [command] help"); diff --git a/src/org/petschko/rpgmakermv/decrypt/CMD_Restore.java b/src/org/petschko/rpgmakermv/decrypt/CMD_Restore.java index a321d42..c9e7a54 100644 --- a/src/org/petschko/rpgmakermv/decrypt/CMD_Restore.java +++ b/src/org/petschko/rpgmakermv/decrypt/CMD_Restore.java @@ -83,14 +83,16 @@ private void handleFiles() { */ @Override public void printHelp() { - App.showMessage("Usage: java -jar \"RPG Maker MV Decrypter.jar\" restore [path to project] [(optional) output path] [(optional) verifyRpgDir - false|true]"); - App.showMessage(CMD.HELP_INDENT + " [(optional) ignoreFakeHeader - true|false] [(optional) headerLen]"); + App.showMessage("Restores images without needing the Key"); + App.showMessage(""); + App.showMessage("Usage: java -jar \"RPG Maker MV Decrypter.jar\" restore [path to project] [(optional) output path] [(optional) verifyRpgDir - false|true] [(optional) ignoreFakeHeader - true|false] [(optional) headerLen]"); App.showMessage(""); App.showMessage(CMD.HELP_INDENT + "Params: (Separate each param by a space, for paths use \"\" around the path)"); - App.showMessage(CMD.HELP_INDENT + " [path to project] - Path to the RPG-MV/MZ Project were you want to restore images"); + App.showMessage(CMD.HELP_INDENT + " [path to project] - Path to the RPG-MV/MZ Project were you want to restore images"); App.showMessage(CMD.HELP_INDENT + " (optional) [output path] - Path where the restored files go | Default: \"output\" (out dir in program dir)"); - App.showMessage(CMD.HELP_INDENT + " (optional) [verifyRpgDir (false|true)] - Verifies if its a RPG-MV/MZ dir | Default: false"); + App.showMessage(CMD.HELP_INDENT + " (optional) [verifyRpgDir (false|true)] - Verifies if its a RPG-MV/MZ dir | Default: false"); App.showMessage(CMD.HELP_INDENT + " (optional) [ignoreFakeHeader (true|false)] - Ignored the Files MV/MZ-Header | Default: true"); + App.showMessage(""); App.showMessage(CMD.HELP_INDENT + "Header-Values: (usually REALLY not needed)"); App.showMessage(CMD.HELP_INDENT + " (very optional) [headerLen] - Byte length of the Header | Default: " + this.headerLen); App.showMessage(""); @@ -99,9 +101,11 @@ public void printHelp() { App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" restore \"C:\\my rpg mv game\\\" \"C:\\my rpg mv game\\\" true false"); App.showMessage(CMD.HELP_INDENT + " java -jar \"RPG Maker MV Decrypter.jar\" restore \"C:\\my rpg mv game\\\" \"C:\\my rpg mv game\\\" true false 14"); App.showMessage(""); - App.showMessage(CMD.HELP_INDENT + "The first command restores all files inside this programs output dir, dont check if its a RPG-MV/MZ dir and ignores if the header of the files is the RPG-MV/MZ header"); - App.showMessage(CMD.HELP_INDENT + "The second command restores all files inside the game directory, it checks if RPG-MV/MZ dir and if Header is valid"); - App.showMessage(CMD.HELP_INDENT + "The third command does the same as the 2nd, just shows how you can modify the header length"); + App.showMessage(CMD.HELP_INDENT + "- The first command restores all files inside this programs output dir, dont check if its a"); + App.showMessage(CMD.HELP_INDENT + " RPG-MV/MZ dir and ignores if the header of the files is the RPG-MV/MZ header"); + App.showMessage(CMD.HELP_INDENT + "- The second command restores all files inside the game directory, it checks if RPG-MV/MZ dir and"); + App.showMessage(CMD.HELP_INDENT + " if Header is valid"); + App.showMessage(CMD.HELP_INDENT + "- The third command does the same as the 2nd, just shows how you can modify the header length"); App.showMessage(""); } } From 4892174a812e5d36bc77fd9a4fa38d95cc907122 Mon Sep 17 00:00:00 2001 From: Petschko Date: Fri, 19 Feb 2021 13:59:59 +0100 Subject: [PATCH 5/8] Added help alias (help [command]) --- src/org/petschko/rpgmakermv/decrypt/CMD.java | 47 +++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/org/petschko/rpgmakermv/decrypt/CMD.java b/src/org/petschko/rpgmakermv/decrypt/CMD.java index 5f9b85a..e7b7813 100644 --- a/src/org/petschko/rpgmakermv/decrypt/CMD.java +++ b/src/org/petschko/rpgmakermv/decrypt/CMD.java @@ -118,8 +118,13 @@ private void processArgs() { // Help is handles a bit different if(invalidCommand) cmdHandler.run(args); - else - cmdHandler.printHelp(); + else { + // Check if 2nd param is command so make this an alias too + if(subCommand != null) { + printHelpForCmd(subCommand); + } else + cmdHandler.printHelp(); + } exitCMD(invalidCommand ? STATUS_WARNING : STATUS_OK); return; @@ -143,6 +148,44 @@ private boolean isHelpCmd(String command) { return command.equals(CMD_HELP) || command.equals(CMD_HELP_2) || command.equals(CMD_HELP_3) || command.equals(CMD_HELP_4); } + /** + * Prints out the help for a specific command + * + * @param command - Command to print help of + */ + private void printHelpForCmd(String command) { + CMD_Command cmdCommand = new CMD_Help(); + + switch(command) { + case CMD_OPEN_IMG: + cmdCommand = new CMD_Open(); + break; + case CMD_DECRYPT: + cmdCommand = new CMD_Decrypt(); + break; + case CMD_ENCRYPT: + cmdCommand = new CMD_Encrypt(); + break; + case CMD_RESTORE: + cmdCommand = new CMD_Restore(); + break; + case CMD_RESTORE_PROJECT: + cmdCommand = new CMD_RestoreProject(); + break; + case CMD_GET_KEY: + case CMD_GET_KEY_2: + case CMD_GET_KEY_3: + case CMD_GET_KEY_4: + case CMD_GET_KEY_5: + cmdCommand = new CMD_DetectKey(); + break; + default: + // Void + } + + cmdCommand.printHelp(); + } + /** * Exit the Program with a Message * From 25503d8c1c0a9e6595a586a1103bdc95b6c7f731 Mon Sep 17 00:00:00 2001 From: Petschko Date: Fri, 19 Feb 2021 15:18:00 +0100 Subject: [PATCH 6/8] Updated README.md --- README.md | 89 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 71 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 327646e..62a09e4 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -# Petschko's RPG-Maker-MV File-Decrypter (Java-Version) +# Petschko's RPG-Maker-MV/MZ File-Decrypter (Java-Version) ## What's that? -This Project is used to decrypt RPG-Maker-MV-Resource-Files that are encrypted with the Build-In-Encryption of the RPG-Maker. +This Project is used to decrypt RPG-Maker-MV/MZ-Resource-Files that are encrypted with the Build-In-Encryption of the RPG-Maker. This Project is also able to decrypt whole RPG-Directories. It's similar to [my previous Project](https://github.com/Petschko/RPG-Maker-MV-Decrypter) but more comfortable. ### Which Files can be decrypted with this Program? -You can decrypt the Build-In-Encrypted Files from the RPG Maker MV. They usually have the extension `.rpgmvp`, `.rpgmvm` or `.rpgmvo` +You can decrypt the Build-In-Encrypted Files from the RPG Maker MV or MZ. They usually have the extension `.rpgmvp`, `.rpgmvm`, `.rpgmvo`, `.png_`, `.m4a_` or `.ogg_`. ## Requirements - Java 8 or higher @@ -17,7 +17,7 @@ You can decrypt the Build-In-Encrypted Files from the RPG Maker MV. They usually - Put it where ever you want (Don't forget to extract, if you downloaded the ZIP xP) ## How to use -### The "normal" way +### (Normal) With Graphical interface - Just double click on the JAR then the Program should start *(If not try the BAT-File - Windows only)* - Click on the Menu "File" then "Open" - Browse to the RPG-Maker MV Project which you want decrypt @@ -32,17 +32,66 @@ You can decrypt the Build-In-Encrypted Files from the RPG Maker MV. They usually - You can also check out the "Option"-Menu and check if the settings fit to you =) - You can find this "Manual" also within the "Info"-Menu -### Start with CMD - For the guys who like it :3 -- Go to the Directory where the JAR is -- Type in `java -jar "RPG Maker MV Decrypter.jar" Target-Directory(Req) Destination-Directory(Optional)` - - Example: `java -jar "RPG Maker MV Decrypter.jar" D:\games\Project1\` - - Other Example with Output-Dir: `java -jar "RPG Maker MV Decrypter.jar" D:\games\Project1\ .\myoutputdir` - - The Script will show you which Files are decrypted and where they are saved now - - The Script ends with "Done." Then you're done^^ +### Start with CMD - For automation / the guys who like it :3 -- Remember that IOS & Linux not have a Drive-Letter nor using the `\ ` as Directory-Separator use `/` instead when you're using Linux/IOS +In some case it can be useful to do stuff via the commandline. For example you want to mod a game and make it easy for the user to mod it (like they just need to start a .bat/.sh file which does the stuff) + +- To see the global help (with all sub-commands) type in `java -jar "RPG Maker MV Decrypter.jar" help` +- To see the detailed help of a specific command type in `java -jar "RPG Maker MV Decrypter.jar" [command] help` + - Example (display decrypt help): `java -jar "RPG Maker MV Decrypter.jar" decrypt help` + +#### Command explanation + +Before we start: +- You only need the params which are marked as __(Req.)__. All other params are optional. +- If a path contains a space use the quotations "" around: `"D:\games\Project with Space\"` +- Remember that IOS & Linux not have a Drive-Letter nor using the `\ ` as Directory-Separator; use `/` instead when you're using Linux/IOS - Important: If you just Type `java -jar "RPG Maker MV Decrypter.jar"` the Program will start with GUI -- You can also use the Help-Command with `java -jar "RPG Maker MV Decrypter.jar" --help` +- You can also use the Help-Command with `java -jar "RPG Maker MV Decrypter.jar" help` +- The Script ends with "Done." Then you're done. + +There exists commands you can use, these are explained here: +- __decrypt__ - Decrypts all Files (Help: `java -jar "RPG Maker MV Decrypter.jar" decrypt help`) + - The Script will decrypt all Files in the target dir + - Simple-Syntax: `java -jar "RPG Maker MV Decrypter.jar" decrypt [(Req.) target path] [output path] [verifyRpgDir (false|true)] [ignoreFakeHeader (true|false)]` + - Full-Syntax: `java -jar "RPG Maker MV Decrypter.jar" decrypt [(Req.) target path] [output path] [verifyRpgDir (false|true)] [ignoreFakeHeader (true|false)] [headerLen (number)] [hsignature] [hversion] [hremain]` + - Example 1: `java -jar "RPG Maker MV Decrypter.jar" decrypt D:\games\Project1\` + - Decrypts the game located in `D:\games\Project1\` and saves files to the output dir of this program + - Example 2: `java -jar "RPG Maker MV Decrypter.jar" decrypt D:\games\Project1\ D:\games\Project1\` + - Decrypts the game located in `D:\games\Project1\` and saves files in the game dir _(preserves structure)_ + - Example 3: `java -jar "RPG Maker MV Decrypter.jar" decrypt D:\games\Project1\ D:\games\Project1\ false true d41d8cd98f00b204e9800998ecf8427e` + - Like example 2, just using the Decryption key `d41d8cd98f00b204e9800998ecf8427e` + - You can do more things with `decrypt` please use the help to see more details +- __restore__ - Restores all PNG-Files without a Key (Help: `java -jar "RPG Maker MV Decrypter.jar" restore help`) + - The Script will restore all Files in the target dir _(no key needed)_ + - Simple-Syntax: `java -jar "RPG Maker MV Decrypter.jar" restore [(Req.) target path] [output path] [verifyRpgDir (false|true)] [ignoreFakeHeader (true|false)]` + - Full-Syntax: `java -jar "RPG Maker MV Decrypter.jar" restore [(Req.) target path] [output path] [verifyRpgDir (false|true)] [ignoreFakeHeader (true|false)] [headerLen (number)]` + - Example 1: `java -jar "RPG Maker MV Decrypter.jar" restore "C:\my rpg mv game\"` + - Restores all Images in `C:\my rpg mv game\` and saves files to the output dir of this program + - Example 2: `java -jar "RPG Maker MV Decrypter.jar" restore "C:\my rpg mv game\" "C:\my rpg mv game\"` + - Restores all Images in `C:\my rpg mv game\` and saves the restored images also to the game _(preserves structure)_ + - Example 3: `java -jar "RPG Maker MV Decrypter.jar" restore "C:\my rpg mv game\" "C:\my rpg mv game\" true false` + - Like Example 2, just adding that the Script should verify it's a RPG-MV/MZ dir and the MV/MZ-Header on the files is correct +- __encrypt__ - Encrypts all Files (Help: `java -jar "RPG Maker MV Decrypter.jar" ecrypt help`) + - This Script (Re-)Encrypts all Files in the target dir + - This Script NEEDs a Key (so it must be either in the Target-dir - `System.json`-File) or given as Parameter + - Simple-Syntax: `java -jar "RPG Maker MV Decrypter.jar" encrypt [(Req.) target path] [output path] [to MV (true|false)] [key (auto|keyValue)]` + - Full-Syntax: `java -jar "RPG Maker MV Decrypter.jar" encrypt [(Req.) target path] [output path] [to MV (true|false)] [key (auto|keyValue)] [headerLen (number)] [hsignature] [hversion] [hremain]` + - Example 1: `java -jar "RPG Maker MV Decrypter.jar" encrypt "C:\my rpg mv game\"` + - Encrypts all resource Files in `C:\my rpg mv game\` to the MV-Format with the detected Key + - Example 2: `java -jar "RPG Maker MV Decrypter.jar" encrypt "C:\my rpg mz game\" output false` + - Encrypts all resource File in `C:\my rpg mz game\` to the output dir of this program in the MZ-Format + - Example 3: `java -jar "RPG Maker MV Decrypter.jar" encrypt "C:\my rpg mz game\" output false d41d8cd98f00b204e9800998ecf8427e` + - Same as Example 2, just using a manual given Key: `d41d8cd98f00b204e9800998ecf8427e` + - You can do more things with `encrypt` please use the help to see more details +- __key__ - Detects and displays the Key (Help: `java -jar "RPG Maker MV Decrypter.jar" key help`) + - This Script shows the Key of the given directory/project + - Simple-Syntax: `java -jar "RPG Maker MV Decrypter.jar" key [(Req.) target path] [ask before image keysearch (true|false)]` + - Full-Syntax: `java -jar "RPG Maker MV Decrypter.jar" key [(Req.) target path] [ask before image keysearch (true|false)] [headerLen (number)]` + - Example 1: `java -jar "RPG Maker MV Decrypter.jar" key "C:\my rpg mv game\"` + - Will do a Key-Search in `C:\my rpg mv game\` and shows the Key if found, if not found it will ask if you want to generate it out of encrypted images + - Example 2: `java -jar "RPG Maker MV Decrypter.jar" key "C:\my rpg mv game\" false` + - Same as Example 1, just don't ask if search on images, it will always do automatically ## Motivation behind this As Art-Creator for the RPG-Maker by myself, it is sometimes hard to figure out, if somebody is using Resources from you *(and may violate the licence like giving no credit or using a Non-Commercial-Resource in a Commercial Game for example)*. @@ -53,12 +102,12 @@ Sad for me, more People use the build in Encryption from the RPG-Maker-MV, so th It can be also useful for Translators, eg when you want to make a Game available for different Regions. *(IMO you should ask the Creator of the Game first! - Sometimes it's not possible...)* -I'm also interested in Encryption in general, so this was a good base to learn, since the MV-Encryption is very weak - But it's great that it is simple, because it will not slow down weak machines! *(See below)* +I'm also interested in Encryption in general, so this was a good base to learn, since the MV-Encryption is very weak - But it's great that's simple, because it will not slow down weak machines! *(See below)* ### Why is the encryption of my Game useless in this case? Sometimes there is a nice Picture that you may save for yourself. You would even do with without this Decrypter by making a Screenshot (or record the Sound) >.< -So you see there is no need for encryption in RPG-Maker Games... If someone want to get the Files, he will able to get them. +So you see there is no need for encryption in RPG-Maker Games... If someone wants to get the Files, he will able to get them. You can't encrypt your Files 100%, because the Game has to decrypt them by itself, to display them... (Or Play) - And since you are able to play the Game offline, you have to provide the Decryption Method and the Key. ### But somebody will steal my assets @@ -70,9 +119,9 @@ So please DON'T steal stuff, as Artist I know how much time such stuff take, so This Java-Decrypter is better for whole Directories. It was easier to do it with Java, because you are very limited with JavaScript. (File-Access / Saving etc) Why Java? - Because I know this Language xP -**Advantages using this in comparision to my previous Project:** +**Advantages using this in comparison to my previous Project:** - Works with whole Directories - - It detect Encrypted Files by itself + - It detects Encrypted Files by itself - It Auto-Saves & Rename Decrypted-Files for you! - It keeps the Directory-Structure - You can specify an Output-Directory @@ -82,7 +131,7 @@ Why Java? - Because I know this Language xP - Will add a restore Project Function in the future - You can en/disable the verification of the 16Byte Fake-Header - If you disable the verification, it will just cut away the Fake-Header aka the first 16 Bytes of the File *(Useful when Decrypter-Signature changes)* - - If you enable the verification it will check if the Files-Header is correct (aka if its a real Encrypted File + - If you enable the verification it will check if the Files-Header is correct (aka if it's a real Encrypted File) ## Be Fair! You are **not allowed** to use the Decrypted Files (**if its not allowed by the origin Resource-Licence**). @@ -92,6 +141,10 @@ You can save them for **Personal-Use only**. If the origin Licence allow use you **If that's your Project** and you simply lost your Origin-Files, **you have the same rights**, to do stuff with them, **as before** =) +## Donate + +If you like this Project and want to thank me may consider to [Donate](https://www.paypal.me/petschko). I appreciate anything, which helps the Project (Pull-Requests, Bug Report etc), these are more worth than Donations but I'm happy for every amount as well. ^.^ + ## Contact - E-Mail me if you have questions (no bug reporting please): peter@petschko.org - You can also tell me you like it or not >.< Of course you can make improvement suggestions if you want From 68317108f3802a7699a7079acbe8680907799594 Mon Sep 17 00:00:00 2001 From: Petschko Date: Fri, 19 Feb 2021 15:49:36 +0100 Subject: [PATCH 7/8] Updated about icon --- src/org/petschko/icons/petschko_icon.png | Bin 30670 -> 70084 bytes .../rpgmakermv/decrypt/GUI_About.java | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/petschko/icons/petschko_icon.png b/src/org/petschko/icons/petschko_icon.png index 22b65edc8d5a993d75f55a01e20b1b672033ec83..969f0932adb002aff5e048842ea36f32fcbd2507 100644 GIT binary patch literal 70084 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

l@bVD<)IPKf~$`1XoXe@!1giD7z5piApsDJhWB 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 FV1oHuxu5_5 diff --git a/src/org/petschko/rpgmakermv/decrypt/GUI_About.java b/src/org/petschko/rpgmakermv/decrypt/GUI_About.java index d1b36ff..05d0849 100644 --- a/src/org/petschko/rpgmakermv/decrypt/GUI_About.java +++ b/src/org/petschko/rpgmakermv/decrypt/GUI_About.java @@ -153,7 +153,7 @@ protected JImageLabel aboutIcon() { JImageLabel imagePanel = new JImageLabel(Config.authorImage, true); // Ensure this size - imagePanel.setImageSize(200, 150); + imagePanel.setImageSize(200, 200); return imagePanel; } From 4bceed29c9964d9500e150ceb1263da55d6e5f9b Mon Sep 17 00:00:00 2001 From: Petschko Date: Fri, 19 Feb 2021 15:50:03 +0100 Subject: [PATCH 8/8] Moved detailed commandline instructions to own file --- README.md | 53 +----------------------------------------- cmd-instructions.md | 56 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 52 deletions(-) create mode 100644 cmd-instructions.md diff --git a/README.md b/README.md index 62a09e4..ca57320 100644 --- a/README.md +++ b/README.md @@ -40,58 +40,7 @@ In some case it can be useful to do stuff via the commandline. For example you w - To see the detailed help of a specific command type in `java -jar "RPG Maker MV Decrypter.jar" [command] help` - Example (display decrypt help): `java -jar "RPG Maker MV Decrypter.jar" decrypt help` -#### Command explanation - -Before we start: -- You only need the params which are marked as __(Req.)__. All other params are optional. -- If a path contains a space use the quotations "" around: `"D:\games\Project with Space\"` -- Remember that IOS & Linux not have a Drive-Letter nor using the `\ ` as Directory-Separator; use `/` instead when you're using Linux/IOS -- Important: If you just Type `java -jar "RPG Maker MV Decrypter.jar"` the Program will start with GUI -- You can also use the Help-Command with `java -jar "RPG Maker MV Decrypter.jar" help` -- The Script ends with "Done." Then you're done. - -There exists commands you can use, these are explained here: -- __decrypt__ - Decrypts all Files (Help: `java -jar "RPG Maker MV Decrypter.jar" decrypt help`) - - The Script will decrypt all Files in the target dir - - Simple-Syntax: `java -jar "RPG Maker MV Decrypter.jar" decrypt [(Req.) target path] [output path] [verifyRpgDir (false|true)] [ignoreFakeHeader (true|false)]` - - Full-Syntax: `java -jar "RPG Maker MV Decrypter.jar" decrypt [(Req.) target path] [output path] [verifyRpgDir (false|true)] [ignoreFakeHeader (true|false)] [headerLen (number)] [hsignature] [hversion] [hremain]` - - Example 1: `java -jar "RPG Maker MV Decrypter.jar" decrypt D:\games\Project1\` - - Decrypts the game located in `D:\games\Project1\` and saves files to the output dir of this program - - Example 2: `java -jar "RPG Maker MV Decrypter.jar" decrypt D:\games\Project1\ D:\games\Project1\` - - Decrypts the game located in `D:\games\Project1\` and saves files in the game dir _(preserves structure)_ - - Example 3: `java -jar "RPG Maker MV Decrypter.jar" decrypt D:\games\Project1\ D:\games\Project1\ false true d41d8cd98f00b204e9800998ecf8427e` - - Like example 2, just using the Decryption key `d41d8cd98f00b204e9800998ecf8427e` - - You can do more things with `decrypt` please use the help to see more details -- __restore__ - Restores all PNG-Files without a Key (Help: `java -jar "RPG Maker MV Decrypter.jar" restore help`) - - The Script will restore all Files in the target dir _(no key needed)_ - - Simple-Syntax: `java -jar "RPG Maker MV Decrypter.jar" restore [(Req.) target path] [output path] [verifyRpgDir (false|true)] [ignoreFakeHeader (true|false)]` - - Full-Syntax: `java -jar "RPG Maker MV Decrypter.jar" restore [(Req.) target path] [output path] [verifyRpgDir (false|true)] [ignoreFakeHeader (true|false)] [headerLen (number)]` - - Example 1: `java -jar "RPG Maker MV Decrypter.jar" restore "C:\my rpg mv game\"` - - Restores all Images in `C:\my rpg mv game\` and saves files to the output dir of this program - - Example 2: `java -jar "RPG Maker MV Decrypter.jar" restore "C:\my rpg mv game\" "C:\my rpg mv game\"` - - Restores all Images in `C:\my rpg mv game\` and saves the restored images also to the game _(preserves structure)_ - - Example 3: `java -jar "RPG Maker MV Decrypter.jar" restore "C:\my rpg mv game\" "C:\my rpg mv game\" true false` - - Like Example 2, just adding that the Script should verify it's a RPG-MV/MZ dir and the MV/MZ-Header on the files is correct -- __encrypt__ - Encrypts all Files (Help: `java -jar "RPG Maker MV Decrypter.jar" ecrypt help`) - - This Script (Re-)Encrypts all Files in the target dir - - This Script NEEDs a Key (so it must be either in the Target-dir - `System.json`-File) or given as Parameter - - Simple-Syntax: `java -jar "RPG Maker MV Decrypter.jar" encrypt [(Req.) target path] [output path] [to MV (true|false)] [key (auto|keyValue)]` - - Full-Syntax: `java -jar "RPG Maker MV Decrypter.jar" encrypt [(Req.) target path] [output path] [to MV (true|false)] [key (auto|keyValue)] [headerLen (number)] [hsignature] [hversion] [hremain]` - - Example 1: `java -jar "RPG Maker MV Decrypter.jar" encrypt "C:\my rpg mv game\"` - - Encrypts all resource Files in `C:\my rpg mv game\` to the MV-Format with the detected Key - - Example 2: `java -jar "RPG Maker MV Decrypter.jar" encrypt "C:\my rpg mz game\" output false` - - Encrypts all resource File in `C:\my rpg mz game\` to the output dir of this program in the MZ-Format - - Example 3: `java -jar "RPG Maker MV Decrypter.jar" encrypt "C:\my rpg mz game\" output false d41d8cd98f00b204e9800998ecf8427e` - - Same as Example 2, just using a manual given Key: `d41d8cd98f00b204e9800998ecf8427e` - - You can do more things with `encrypt` please use the help to see more details -- __key__ - Detects and displays the Key (Help: `java -jar "RPG Maker MV Decrypter.jar" key help`) - - This Script shows the Key of the given directory/project - - Simple-Syntax: `java -jar "RPG Maker MV Decrypter.jar" key [(Req.) target path] [ask before image keysearch (true|false)]` - - Full-Syntax: `java -jar "RPG Maker MV Decrypter.jar" key [(Req.) target path] [ask before image keysearch (true|false)] [headerLen (number)]` - - Example 1: `java -jar "RPG Maker MV Decrypter.jar" key "C:\my rpg mv game\"` - - Will do a Key-Search in `C:\my rpg mv game\` and shows the Key if found, if not found it will ask if you want to generate it out of encrypted images - - Example 2: `java -jar "RPG Maker MV Decrypter.jar" key "C:\my rpg mv game\" false` - - Same as Example 1, just don't ask if search on images, it will always do automatically +For details use and examples how to use it see [CMD Instructions](cmd-instructions.md) ## Motivation behind this As Art-Creator for the RPG-Maker by myself, it is sometimes hard to figure out, if somebody is using Resources from you *(and may violate the licence like giving no credit or using a Non-Commercial-Resource in a Commercial Game for example)*. diff --git a/cmd-instructions.md b/cmd-instructions.md new file mode 100644 index 0000000..4dd0852 --- /dev/null +++ b/cmd-instructions.md @@ -0,0 +1,56 @@ +I put the detailed Command-Line explanation here since not many people use it and its pretty "long" + +Everything what's explained here is also explained in the help-commands within the Program. + +# Command explanation + +Before we start: +- You only need the params which are marked as __(Req.)__. All other params are optional. +- If a path contains a space use the quotations "" around: `"D:\games\Project with Space\"` +- Remember that IOS & Linux not have a Drive-Letter nor using the `\ ` as Directory-Separator; use `/` instead when you're using Linux/IOS +- Important: If you just Type `java -jar "RPG Maker MV Decrypter.jar"` the Program will start with GUI +- You can also use the Help-Command with `java -jar "RPG Maker MV Decrypter.jar" help` +- The Script ends with "Done." Then you're done. + +There exists commands you can use, these are explained here: +- __decrypt__ - Decrypts all Files (Help: `java -jar "RPG Maker MV Decrypter.jar" decrypt help`) + - The Script will decrypt all Files in the target dir + - Simple-Syntax: `java -jar "RPG Maker MV Decrypter.jar" decrypt [(Req.) target path] [output path] [verifyRpgDir (false|true)] [ignoreFakeHeader (true|false)]` + - Full-Syntax: `java -jar "RPG Maker MV Decrypter.jar" decrypt [(Req.) target path] [output path] [verifyRpgDir (false|true)] [ignoreFakeHeader (true|false)] [headerLen (number)] [hsignature] [hversion] [hremain]` + - Example 1: `java -jar "RPG Maker MV Decrypter.jar" decrypt D:\games\Project1\` + - Decrypts the game located in `D:\games\Project1\` and saves files to the output dir of this program + - Example 2: `java -jar "RPG Maker MV Decrypter.jar" decrypt D:\games\Project1\ D:\games\Project1\` + - Decrypts the game located in `D:\games\Project1\` and saves files in the game dir _(preserves structure)_ + - Example 3: `java -jar "RPG Maker MV Decrypter.jar" decrypt D:\games\Project1\ D:\games\Project1\ false true d41d8cd98f00b204e9800998ecf8427e` + - Like example 2, just using the Decryption key `d41d8cd98f00b204e9800998ecf8427e` + - You can do more things with `decrypt` please use the help to see more details +- __restore__ - Restores all PNG-Files without a Key (Help: `java -jar "RPG Maker MV Decrypter.jar" restore help`) + - The Script will restore all Files in the target dir _(no key needed)_ + - Simple-Syntax: `java -jar "RPG Maker MV Decrypter.jar" restore [(Req.) target path] [output path] [verifyRpgDir (false|true)] [ignoreFakeHeader (true|false)]` + - Full-Syntax: `java -jar "RPG Maker MV Decrypter.jar" restore [(Req.) target path] [output path] [verifyRpgDir (false|true)] [ignoreFakeHeader (true|false)] [headerLen (number)]` + - Example 1: `java -jar "RPG Maker MV Decrypter.jar" restore "C:\my rpg mv game\"` + - Restores all Images in `C:\my rpg mv game\` and saves files to the output dir of this program + - Example 2: `java -jar "RPG Maker MV Decrypter.jar" restore "C:\my rpg mv game\" "C:\my rpg mv game\"` + - Restores all Images in `C:\my rpg mv game\` and saves the restored images also to the game _(preserves structure)_ + - Example 3: `java -jar "RPG Maker MV Decrypter.jar" restore "C:\my rpg mv game\" "C:\my rpg mv game\" true false` + - Like Example 2, just adding that the Script should verify it's a RPG-MV/MZ dir and the MV/MZ-Header on the files is correct +- __encrypt__ - Encrypts all Files (Help: `java -jar "RPG Maker MV Decrypter.jar" ecrypt help`) + - This Script (Re-)Encrypts all Files in the target dir + - This Script NEEDs a Key (so it must be either in the Target-dir - `System.json`-File) or given as Parameter + - Simple-Syntax: `java -jar "RPG Maker MV Decrypter.jar" encrypt [(Req.) target path] [output path] [to MV (true|false)] [key (auto|keyValue)]` + - Full-Syntax: `java -jar "RPG Maker MV Decrypter.jar" encrypt [(Req.) target path] [output path] [to MV (true|false)] [key (auto|keyValue)] [headerLen (number)] [hsignature] [hversion] [hremain]` + - Example 1: `java -jar "RPG Maker MV Decrypter.jar" encrypt "C:\my rpg mv game\"` + - Encrypts all resource Files in `C:\my rpg mv game\` to the MV-Format with the detected Key + - Example 2: `java -jar "RPG Maker MV Decrypter.jar" encrypt "C:\my rpg mz game\" output false` + - Encrypts all resource File in `C:\my rpg mz game\` to the output dir of this program in the MZ-Format + - Example 3: `java -jar "RPG Maker MV Decrypter.jar" encrypt "C:\my rpg mz game\" output false d41d8cd98f00b204e9800998ecf8427e` + - Same as Example 2, just using a manual given Key: `d41d8cd98f00b204e9800998ecf8427e` + - You can do more things with `encrypt` please use the help to see more details +- __key__ - Detects and displays the Key (Help: `java -jar "RPG Maker MV Decrypter.jar" key help`) + - This Script shows the Key of the given directory/project + - Simple-Syntax: `java -jar "RPG Maker MV Decrypter.jar" key [(Req.) target path] [ask before image keysearch (true|false)]` + - Full-Syntax: `java -jar "RPG Maker MV Decrypter.jar" key [(Req.) target path] [ask before image keysearch (true|false)] [headerLen (number)]` + - Example 1: `java -jar "RPG Maker MV Decrypter.jar" key "C:\my rpg mv game\"` + - Will do a Key-Search in `C:\my rpg mv game\` and shows the Key if found, if not found it will ask if you want to generate it out of encrypted images + - Example 2: `java -jar "RPG Maker MV Decrypter.jar" key "C:\my rpg mv game\" false` + - Same as Example 1, just don't ask if search on images, it will always do automatically