diff --git a/build/app.js b/build/app.js index d556d88..2e1932e 100644 --- a/build/app.js +++ b/build/app.js @@ -27,7 +27,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _HKCheckCompletion_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./HKCheckCompletion.js */ \"./src/js/HKCheckCompletion.js\");\n/* \r\n Parts of the code thanks to bloodorca https://github.com/bloodorca/hollow (base64.js, functions.js) with slight modifications.\r\n The steps used there for decryption were taken from KayDeeTee https://github.com/KayDeeTee/Hollow-Knight-SaveManager\r\n Without these two people the existence of this tool wouldn't be possible :)\r\n*/\n// ---------------- Constants ----------------- //\n// AES JS for file decryption\nvar aesjs = __webpack_require__(/*! ./aes-js.js */ \"./src/js/aes-js.js\"); // For reading the text area after save decoding\n\n\n\nvar CSHARP_HEADER = [0, 1, 0, 0, 0, 255, 255, 255, 255, 1, 0, 0, 0, 0, 0, 0, 0, 6, 1, 0, 0, 0]; // 22 bytes\n\nvar BASE64_ARRAY = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\".split(\"\").map(function (c) {\n return c.charCodeAt(0);\n});\nvar BASE64_DECODE_TABLE = new Map(BASE64_ARRAY.map(function (ord, i) {\n return [ord, i];\n}));\nvar AES_KEY = new TextEncoder().encode('UKu52ePUBwetZ9wNX88o54dnfKRu0T1l'); // encodes a string to Uint8Array (prepare for AES JS)\n\nvar ECB_STREAM_CIPHER = new aesjs.ModeOfOperation.ecb(AES_KEY); // create a new AES stream cipher object using the encoded key\n// ---------------- Variables ----------------- //\n\nvar benchLSFBegin, benchLSFEnd, benchTotal; // ---------------- Functions ----------------- //\n\n/**\r\n * Main input tag file function. Selects the first file, reads it as an Array Buffer, starts the processing of the file when loaded.\r\n * Starts benchmarking.\r\n * @param {FileList} input FileList object containing a list of File objects. The FileList behaves like an array, so you can check its length property to get the number of selected files.\r\n */\n// eslint-disable-next-line no-unused-vars\n\nfunction LoadSaveFile() {\n var startTime = new Date();\n var inputFileList = this.files; // console.info(\"Input length: \" + input.files.length)\n // Cease further processing if user canceled the file input dialog\n\n if (inputFileList.length < 1) return false; // start benchmark\n\n benchLSFBegin = startTime; // Prepares a File object from the first file of the input files for reading as an Array Buffer\n\n var inputFileObject = inputFileList[0]; // Cleans the file list to avoid problems after subsequent use\n // document.getElementById(\"save-area-file\").value = \"\";\n // 1. read file\n // The ArrayBuffer object is used to represent a generic, fixed-length raw binary data buffer.\n // It is an array of bytes, often referred to in other languages as a \"byte array\".\n // new FileReader()\n\n var inputReader = new FileReader(); // readAsArrayBuffer(file)\n\n inputReader.readAsArrayBuffer(inputFileObject); // addEventListener(\"load\", function) - to decode the file when loaded\n\n inputReader.addEventListener(\"load\", ProcessFileObject);\n}\n/**\r\n * Reads the File object as an Array Buffer and does all other operations (decoding, decryption, conversion to string, pasting to text area).\r\n * Launches the HKReadTextArea() function automatically after pasting the string to text area\r\n */\n\n\nfunction ProcessFileObject() {\n var inputArrayBuffer = this.result;\n var decodedString; // 2. Decode file\n\n try {\n // Uint8Array(ArrayBuffer) uint8_t equivalent in C\n inputArrayBuffer = new Uint8Array(inputArrayBuffer); // ArrayBuffer.slice()\n // The slice() method copies up to, but not including, the byte indicated by the end parameter.\n\n inputArrayBuffer.slice(); // remove C# header and LengthPrefixedString header (ArrayBuffer)\n\n inputArrayBuffer = RemoveHeaders(inputArrayBuffer); // base64 Decoding (ArrayBuffer)\n\n inputArrayBuffer = Base64Decode(inputArrayBuffer); // AES decryption (ECB) removes pkcs7 padding (ArrayBuffer)\n\n inputArrayBuffer = AESDecryption(inputArrayBuffer); // Convert ArrayBuffer to string/text TextDecoder().decode(ArrayBuffer)\n\n decodedString = new TextDecoder().decode(inputArrayBuffer); // 4. Paste decoded string file to text area\n\n document.getElementById(\"save-area\").value = \"\";\n document.getElementById(\"save-area\").value = decodedString; // finish and show benchmark\n\n benchLSFEnd = new Date();\n console.info(\"LoadSaveFile() time (ms) =\", benchLSFEnd - benchLSFBegin); // after pasting the decoded string, launch the main analyzing function immediately\n\n (0,_HKCheckCompletion_js__WEBPACK_IMPORTED_MODULE_0__.HKReadTextArea)(\"save-area\"); // finish total and show benchmark\n\n benchTotal = new Date();\n console.info(\"Total time (ms) =\", benchTotal - benchLSFBegin); // alert(`Decoded String: ${decodedString}`);\n // alert(`Array Buffer: ${inputArrayBuffer}`);\n } catch (error) {\n alert(\"The file cannot be decoded. \".concat(error));\n console.info(\"The file cannot be decoded. \".concat(error));\n }\n}\n/**\r\n * Removes C# header, LengthPrefixedString header and byte 11 at the end of the Uint8 Array Buffer\r\n * @param {Uint8Array} buffer Uint8Array buffer for removing the header from\r\n * @param {Uint8Array} csHeader Uint8Array for the C# header length calculation\r\n * @returns {Uint8Array}\r\n */\n\n\nfunction RemoveHeaders(buffer) {\n var csHeader = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : CSHARP_HEADER;\n // Remove the fixed C# header and byte 11 at the end. \n buffer = buffer.subarray(csHeader.length, buffer.length - 1); // Remove LengthPrefixedString header\n\n var lengthCount = 0;\n\n for (var i = 0; i < 5; i++) {\n lengthCount++;\n\n if ((buffer[i] & 0x80) == 0) {\n break;\n }\n }\n\n return buffer.subarray(lengthCount);\n}\n/**\r\n * Decodes an Array Buffer using a Base64 decode table\r\n * @param {Uint8Array} buffer Uint8Array Buffer to decode\r\n * @returns {Uint8Array}\r\n */\n\n\nfunction Base64Decode(buffer) {\n buffer = new Uint8Array(buffer).slice();\n buffer = buffer.map(function (v) {\n return BASE64_DECODE_TABLE.get(v);\n }); // The indexOf() method returns the first index at which a given element can be found in the array, or -1 if it is not present.\n\n var p = buffer.indexOf(64);\n var end;\n\n if (p != -1) {\n end = p;\n } else {\n end = buffer.length;\n }\n\n buffer = buffer.subarray(0, end);\n var output = new Uint8Array(3 * buffer.length / 4);\n var continuous = Math.floor(buffer.length / 4) * 4;\n\n for (var i = 0; i < continuous; i += 4) {\n var k = 3 * i / 4;\n output[k] = buffer[i] << 2 | buffer[i + 1] >> 4;\n output[k + 1] = (buffer[i + 1] & 0x0F) << 4 | buffer[i + 2] >> 2;\n output[k + 2] = (buffer[i + 2] & 0x03) << 6 | buffer[i + 3];\n }\n\n if (buffer[continuous] != undefined) {\n var _k = 3 * continuous / 4;\n\n output[_k] = buffer[continuous] << 2 | buffer[continuous + 1] >> 4;\n\n if (buffer[continuous + 2] != undefined) {\n output[_k + 1] = (buffer[continuous + 1] & 0x0F) << 4 | buffer[continuous + 2] >> 2;\n }\n }\n\n return output;\n}\n/**\r\n * Decrypt an Uint8Array Buffer using aesjs (ECB) and a predefined AES key + remove pkcs7 padding\r\n * @param {Uint8Array} buffer Uint8Array buffer to decrypt\r\n * @returns {Uint8Array}\r\n */\n\n\nfunction AESDecryption(buffer) {\n var cipherObject = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ECB_STREAM_CIPHER;\n var output = cipherObject.decrypt(buffer);\n return output.subarray(0, -output[output.length - 1]);\n} // Make functions global so they can be used on click and change events (for Webpack)\n// window.LoadSaveFile = LoadSaveFile;\n// Assign actions (functions) to launch when a specific element is used\n\n\ndocument.getElementById(\"save-area-file\").addEventListener(\"change\", LoadSaveFile);\ndocument.getElementById(\"save-area-file\").addEventListener(\"click\", function (mouseEvent) {\n mouseEvent.target.value = \"\";\n});\n\n//# sourceURL=webpack://hollow-knight-completion-check/./src/js/LoadSaveFile.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _HKCheckCompletion_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./HKCheckCompletion.js */ \"./src/js/HKCheckCompletion.js\");\n/* \r\n Parts of the code thanks to bloodorca https://github.com/bloodorca/hollow (base64.js, functions.js) with slight modifications.\r\n The steps used there for decryption were taken from KayDeeTee https://github.com/KayDeeTee/Hollow-Knight-SaveManager\r\n Without these two people the existence of this tool wouldn't be possible :)\r\n*/\n// ---------------- Constants ----------------- //\n// AES JS for file decryption\nvar aesjs = __webpack_require__(/*! ./aes-js.js */ \"./src/js/aes-js.js\"); // For reading the text area after save decoding\n\n\n\nvar CSHARP_HEADER = [0, 1, 0, 0, 0, 255, 255, 255, 255, 1, 0, 0, 0, 0, 0, 0, 0, 6, 1, 0, 0, 0]; // 22 bytes\n\nvar BASE64_ARRAY = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\".split(\"\").map(function (c) {\n return c.charCodeAt(0);\n});\nvar BASE64_DECODE_TABLE = new Map(BASE64_ARRAY.map(function (ord, i) {\n return [ord, i];\n}));\nvar AES_KEY = new TextEncoder().encode('UKu52ePUBwetZ9wNX88o54dnfKRu0T1l'); // encodes a string to Uint8Array (prepare for AES JS)\n\nvar ECB_STREAM_CIPHER = new aesjs.ModeOfOperation.ecb(AES_KEY); // create a new AES stream cipher object using the encoded key\n// ---------------- Variables ----------------- //\n\nvar benchLSFBegin, benchLSFEnd, benchTotal; // ---------------- Functions ----------------- //\n\n/**\r\n * Main input tag file function. Selects the first file, reads it as an Array Buffer, starts the processing of the file when loaded.\r\n * Starts benchmarking.\r\n * @param {FileList} input FileList object containing a list of File objects. The FileList behaves like an array, so you can check its length property to get the number of selected files.\r\n */\n// eslint-disable-next-line no-unused-vars\n\nfunction LoadSaveFile(input, time) {\n var inputFileList = input.files; // console.info(\"Input length: \" + input.files.length)\n // Cease further processing if user canceled the file input dialog\n\n if (inputFileList.length < 1) return false; // start benchmark\n\n benchLSFBegin = time; // Prepares a File object from the first file of the input files for reading as an Array Buffer\n\n var inputFileObject = inputFileList[0]; // Cleans the file list to avoid problems after subsequent use\n // document.getElementById(\"save-area-file\").value = \"\";\n // 1. read file\n // The ArrayBuffer object is used to represent a generic, fixed-length raw binary data buffer.\n // It is an array of bytes, often referred to in other languages as a \"byte array\".\n // new FileReader()\n\n var inputReader = new FileReader(); // readAsArrayBuffer(file)\n\n inputReader.readAsArrayBuffer(inputFileObject); // addEventListener(\"load\", function) - to decode the file when loaded\n\n inputReader.addEventListener(\"load\", ProcessFileObject);\n}\n/**\r\n * Reads the File object as an Array Buffer and does all other operations (decoding, decryption, conversion to string, pasting to text area).\r\n * Launches the HKReadTextArea() function automatically after pasting the string to text area\r\n */\n\n\nfunction ProcessFileObject() {\n var inputArrayBuffer = this.result;\n var decodedString; // 2. Decode file\n\n try {\n // Uint8Array(ArrayBuffer) uint8_t equivalent in C\n inputArrayBuffer = new Uint8Array(inputArrayBuffer); // ArrayBuffer.slice()\n // The slice() method copies up to, but not including, the byte indicated by the end parameter.\n\n inputArrayBuffer.slice(); // remove C# header and LengthPrefixedString header (ArrayBuffer)\n\n inputArrayBuffer = RemoveHeaders(inputArrayBuffer); // base64 Decoding (ArrayBuffer)\n\n inputArrayBuffer = Base64Decode(inputArrayBuffer); // AES decryption (ECB) removes pkcs7 padding (ArrayBuffer)\n\n inputArrayBuffer = AESDecryption(inputArrayBuffer); // Convert ArrayBuffer to string/text TextDecoder().decode(ArrayBuffer)\n\n decodedString = new TextDecoder().decode(inputArrayBuffer); // 4. Paste decoded string file to text area\n\n document.getElementById(\"save-area\").value = \"\";\n document.getElementById(\"save-area\").value = decodedString; // finish and show benchmark\n\n benchLSFEnd = new Date();\n console.info(\"LoadSaveFile() time (ms) =\", benchLSFEnd - benchLSFBegin); // after pasting the decoded string, launch the main analyzing function immediately\n\n (0,_HKCheckCompletion_js__WEBPACK_IMPORTED_MODULE_0__.HKReadTextArea)(\"save-area\"); // finish total and show benchmark\n\n benchTotal = new Date();\n console.info(\"Total time (ms) =\", benchTotal - benchLSFBegin); // alert(`Decoded String: ${decodedString}`);\n // alert(`Array Buffer: ${inputArrayBuffer}`);\n } catch (error) {\n alert(\"The file cannot be decoded. \".concat(error));\n console.info(\"The file cannot be decoded. \".concat(error));\n }\n}\n/**\r\n * Removes C# header, LengthPrefixedString header and byte 11 at the end of the Uint8 Array Buffer\r\n * @param {Uint8Array} buffer Uint8Array buffer for removing the header from\r\n * @param {Uint8Array} csHeader Uint8Array for the C# header length calculation\r\n * @returns {Uint8Array}\r\n */\n\n\nfunction RemoveHeaders(buffer) {\n var csHeader = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : CSHARP_HEADER;\n // Remove the fixed C# header and byte 11 at the end. \n buffer = buffer.subarray(csHeader.length, buffer.length - 1); // Remove LengthPrefixedString header\n\n var lengthCount = 0;\n\n for (var i = 0; i < 5; i++) {\n lengthCount++;\n\n if ((buffer[i] & 0x80) == 0) {\n break;\n }\n }\n\n return buffer.subarray(lengthCount);\n}\n/**\r\n * Decodes an Array Buffer using a Base64 decode table\r\n * @param {Uint8Array} buffer Uint8Array Buffer to decode\r\n * @returns {Uint8Array}\r\n */\n\n\nfunction Base64Decode(buffer) {\n buffer = new Uint8Array(buffer).slice();\n buffer = buffer.map(function (v) {\n return BASE64_DECODE_TABLE.get(v);\n }); // The indexOf() method returns the first index at which a given element can be found in the array, or -1 if it is not present.\n\n var p = buffer.indexOf(64);\n var end;\n\n if (p != -1) {\n end = p;\n } else {\n end = buffer.length;\n }\n\n buffer = buffer.subarray(0, end);\n var output = new Uint8Array(3 * buffer.length / 4);\n var continuous = Math.floor(buffer.length / 4) * 4;\n\n for (var i = 0; i < continuous; i += 4) {\n var k = 3 * i / 4;\n output[k] = buffer[i] << 2 | buffer[i + 1] >> 4;\n output[k + 1] = (buffer[i + 1] & 0x0F) << 4 | buffer[i + 2] >> 2;\n output[k + 2] = (buffer[i + 2] & 0x03) << 6 | buffer[i + 3];\n }\n\n if (buffer[continuous] != undefined) {\n var _k = 3 * continuous / 4;\n\n output[_k] = buffer[continuous] << 2 | buffer[continuous + 1] >> 4;\n\n if (buffer[continuous + 2] != undefined) {\n output[_k + 1] = (buffer[continuous + 1] & 0x0F) << 4 | buffer[continuous + 2] >> 2;\n }\n }\n\n return output;\n}\n/**\r\n * Decrypt an Uint8Array Buffer using aesjs (ECB) and a predefined AES key + remove pkcs7 padding\r\n * @param {Uint8Array} buffer Uint8Array buffer to decrypt\r\n * @returns {Uint8Array}\r\n */\n\n\nfunction AESDecryption(buffer) {\n var cipherObject = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ECB_STREAM_CIPHER;\n var output = cipherObject.decrypt(buffer);\n return output.subarray(0, -output[output.length - 1]);\n} // Assign actions (functions) to launch when a specific element is used\n\n\ndocument.getElementById(\"save-area-file\").addEventListener(\"change\", function (event) {\n LoadSaveFile(event.target, new Date());\n});\ndocument.getElementById(\"save-area-file\").addEventListener(\"click\", function (mouseEvent) {\n mouseEvent.target.value = \"\";\n});\n\n//# sourceURL=webpack://hollow-knight-completion-check/./src/js/LoadSaveFile.js?"); /***/ }), diff --git a/src/js/LoadSaveFile.js b/src/js/LoadSaveFile.js index 9ccd096..0fda1f5 100644 --- a/src/js/LoadSaveFile.js +++ b/src/js/LoadSaveFile.js @@ -9,7 +9,9 @@ // AES JS for file decryption const aesjs = require("./aes-js.js"); // For reading the text area after save decoding -import { HKReadTextArea } from "./HKCheckCompletion.js"; +import { + HKReadTextArea +} from "./HKCheckCompletion.js"; const CSHARP_HEADER = [0, 1, 0, 0, 0, 255, 255, 255, 255, 1, 0, 0, 0, 0, 0, 0, 0, 6, 1, 0, 0, 0]; // 22 bytes @@ -31,17 +33,15 @@ let benchLSFBegin, benchLSFEnd, benchTotal; * @param {FileList} input FileList object containing a list of File objects. The FileList behaves like an array, so you can check its length property to get the number of selected files. */ // eslint-disable-next-line no-unused-vars -function LoadSaveFile() { +function LoadSaveFile(input, time) { - let startTime = new Date(); - - const inputFileList = this.files; + const inputFileList = input.files; // console.info("Input length: " + input.files.length) // Cease further processing if user canceled the file input dialog if (inputFileList.length < 1) return false; // start benchmark - benchLSFBegin = startTime; + benchLSFBegin = time; // Prepares a File object from the first file of the input files for reading as an Array Buffer let inputFileObject = inputFileList[0]; @@ -188,11 +188,10 @@ function AESDecryption(buffer, cipherObject = ECB_STREAM_CIPHER) { return output.subarray(0, -output[output.length - 1]); } -// Make functions global so they can be used on click and change events (for Webpack) -// window.LoadSaveFile = LoadSaveFile; - // Assign actions (functions) to launch when a specific element is used -document.getElementById("save-area-file").addEventListener("change", LoadSaveFile); +document.getElementById("save-area-file").addEventListener("change", (event) => { + LoadSaveFile(event.target, new Date()); +}); document.getElementById("save-area-file").addEventListener("click", (mouseEvent) => { mouseEvent.target.value = ""; }); \ No newline at end of file