├── .gitignore ├── App ├── css │ └── style.css ├── img │ ├── 404.png │ ├── 404_BG.png │ ├── banner.jpg │ ├── logo.ico │ ├── logo.png │ └── svg │ │ ├── app-menu.svg │ │ ├── list-filled.svg │ │ └── view-list.svg ├── index.htm ├── js │ ├── TMS.js │ ├── design.js │ ├── emumanager.js │ ├── filemanager.js │ ├── gamelist.js │ ├── language.js │ ├── main.js │ ├── paramSfoDatabase.js │ ├── paramSfoParser.js │ ├── settings.js │ ├── tools.js │ └── updateEmu.js └── node_modules │ ├── memoryjs │ ├── .eslintrc.js │ ├── LICENSE.md │ ├── README.md │ ├── assets │ │ └── logo.png │ ├── binding.gyp │ ├── build │ │ ├── Release │ │ │ ├── memoryjs.lib │ │ │ ├── memoryjs.node │ │ │ ├── memoryjs.pdb │ │ │ └── obj │ │ │ │ └── memoryjs │ │ │ │ ├── debugger.obj │ │ │ │ ├── functions.obj │ │ │ │ ├── memory.obj │ │ │ │ ├── memoryjs.obj │ │ │ │ ├── memoryjs.tlog │ │ │ │ ├── clang-cl.1528.write.1.tlog │ │ │ │ ├── clang-cl.command.1.tlog │ │ │ │ ├── clang-cl.read.1.tlog │ │ │ │ ├── lld-link.command.1.tlog │ │ │ │ ├── lld-link.read.1.tlog │ │ │ │ ├── lld-link.write.1.tlog │ │ │ │ ├── memoryjs.lastbuildstate │ │ │ │ └── memoryjs.write.1u.tlog │ │ │ │ ├── module.obj │ │ │ │ ├── pattern.obj │ │ │ │ ├── process.obj │ │ │ │ └── win_delay_load_hook.obj │ │ ├── binding.sln │ │ ├── config.gypi │ │ ├── memoryjs.vcxproj │ │ └── memoryjs.vcxproj.filters │ ├── examples │ │ ├── buffers.js │ │ ├── debugging.js │ │ ├── general.js │ │ └── vectors.js │ ├── index.js │ ├── lib │ │ ├── debugger.cc │ │ ├── debugger.h │ │ ├── dll.h │ │ ├── functions.cc │ │ ├── functions.h │ │ ├── memory.cc │ │ ├── memory.h │ │ ├── memoryjs.cc │ │ ├── memoryjs.h │ │ ├── module.cc │ │ ├── module.h │ │ ├── pattern.cc │ │ ├── pattern.h │ │ ├── process.cc │ │ └── process.h │ ├── package.json │ ├── scripts │ │ ├── debug.js │ │ └── install.js │ ├── src │ │ ├── consts.js │ │ ├── debugger.js │ │ └── utils.js │ └── test │ │ ├── allocationTest.js │ │ ├── debuggerTest.js │ │ ├── functionTest.js │ │ ├── memoryTest.js │ │ ├── project.sln │ │ ├── protectionTest.js │ │ ├── queryTest.js │ │ ├── src │ │ ├── MemoryTest.cpp │ │ ├── functionTest.cpp │ │ └── protectionTest.cpp │ │ └── vcxproj │ │ ├── FunctionTest.vcxproj │ │ ├── MemoryTest.vcxproj │ │ └── ProtectionTest.vcxproj │ └── node-stream-zip │ ├── LICENSE │ ├── README.md │ ├── node_stream_zip.d.ts │ ├── node_stream_zip.js │ └── package.json ├── LICENSE ├── Lang ├── Ar-ar.json ├── about-translations.md ├── fr-fr.json ├── it-it.json ├── ja-ja.json ├── nl-nl.json ├── pt-br.json ├── ru-ru.json ├── tr-tr.json ├── uk-ua.json └── zh-s.json ├── Nwjs └── .gitkeep ├── README.md ├── install.sh ├── launcher.bat ├── launcher.sh ├── package.json ├── update.bat └── update.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Paths 2 | Games/ 3 | Nwjs/ 4 | Emu/ 5 | 6 | # Paths generated by fpPS4 7 | shader_dump/ 8 | savedata/ 9 | 10 | # Launcher settings 11 | Settings.json 12 | 13 | # Misc. Files 14 | *.tar.gz 15 | *.info 16 | *.nexe 17 | *.dump 18 | *.sprx 19 | *.exe 20 | *.dll 21 | *.pak 22 | *.zip 23 | *.pkg 24 | *.log 25 | *.dat 26 | *.sh -------------------------------------------------------------------------------- /App/img/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themitosan/fpPS4-Temmie-s-Launcher/cc971d1c4ea3d37ad89536f5fd52706a70939d66/App/img/404.png -------------------------------------------------------------------------------- /App/img/404_BG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themitosan/fpPS4-Temmie-s-Launcher/cc971d1c4ea3d37ad89536f5fd52706a70939d66/App/img/404_BG.png -------------------------------------------------------------------------------- /App/img/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themitosan/fpPS4-Temmie-s-Launcher/cc971d1c4ea3d37ad89536f5fd52706a70939d66/App/img/banner.jpg -------------------------------------------------------------------------------- /App/img/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themitosan/fpPS4-Temmie-s-Launcher/cc971d1c4ea3d37ad89536f5fd52706a70939d66/App/img/logo.ico -------------------------------------------------------------------------------- /App/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themitosan/fpPS4-Temmie-s-Launcher/cc971d1c4ea3d37ad89536f5fd52706a70939d66/App/img/logo.png -------------------------------------------------------------------------------- /App/img/svg/app-menu.svg: -------------------------------------------------------------------------------- 1 | app-menuCreated with sketchtool. -------------------------------------------------------------------------------- /App/img/svg/list-filled.svg: -------------------------------------------------------------------------------- 1 | list-filled -------------------------------------------------------------------------------- /App/img/svg/view-list.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /App/js/emumanager.js: -------------------------------------------------------------------------------- 1 | /* 2 | ****************************************************************************** 3 | fpPS4 Temmie's Launcher 4 | emumanager.js 5 | 6 | This file contains all functions / variables about running main project 7 | executable, game module checks and updating fpPS4 executable. 8 | ****************************************************************************** 9 | */ 10 | 11 | temp_EMUMANAGER = { 12 | 13 | // Emulator is running 14 | emuRunning: !1, 15 | 16 | // Update functions 17 | update: temp_EMU_UPDATE, 18 | 19 | // Run emu 20 | runGame: function(){ 21 | 22 | // Get selected game details and check if user selected a game 23 | const mainGameData = APP.gameList.list[APP.gameList.selectedGame]; 24 | if (mainGameData !== void 0){ 25 | 26 | // Reset Error List and clear log on emu running (if needed) 27 | APP.emuManager.emuErrorList = []; 28 | if (APP.settings.data.clearLogOnEmuLoad === !0 && APP.emuManager.emuRunCounter !== 0){ 29 | APP.clearLog(!0); 30 | } 31 | 32 | // If (by some reason) main emu still running, close it and set main variables 33 | this.killEmu(!0); 34 | var ebootPath = mainGameData.exe, 35 | emuArgs = ['-e', ebootPath], 36 | hList = APP.design.hackList; 37 | 38 | // Check if patches are available 39 | if (APP.gameList.cGameSettings.usePatch === !0 && APP.design.gamePatchLoaded === !0){ 40 | emuArgs.push('-p'); 41 | emuArgs.push(`\"${APP.gameList.cGameSettings.patchLocation}\"`); 42 | } 43 | 44 | // Get enabled hacks 45 | hList.forEach(function(hackName){ 46 | if (document.getElementById(`CHECK_${hackName}`).checked === !0){ 47 | emuArgs.push('-h'); 48 | emuArgs.push(hackName); 49 | } 50 | }); 51 | 52 | // Push gamepad mode 53 | const padMode = document.getElementById('FPPS4_OPTIONS_SELECT_GAMEPAD_MODE').value; 54 | if (padMode === 'sdl2' && APP.gameList.checkSdl2() === !1){ 55 | emuArgs.push('-pad "keyboard"'); 56 | } else { 57 | emuArgs.push(`-pad "${padMode}"`); 58 | } 59 | 60 | // If current gameppad mode is SDL2 and change led bar color is enabled, push led args 61 | if (padMode === 'sdl2' && APP.settings.data.sdlEnableGamepadLed === !0){ 62 | emuArgs.push(`-led $${APP.gameList.cGameSettings.gPadLedColor}`); 63 | } 64 | 65 | // Add fullscreen flag if it's enabled 66 | if (APP.settings.data.enableEmuFullscreen === !0){ 67 | emuArgs.push('-w'); 68 | } 69 | 70 | // Log emu location and args and run fpPS4 71 | APP.log(APP.lang.getVariable('runEmuArgs', [emuArgs.toString().replace(RegExp(',', 'gi'), ' '), APP.settings.data.emuPath])); 72 | APP.runfpPS4(APP.settings.data.emuPath, emuArgs); 73 | 74 | // Update main GUI 75 | APP.design.update(); 76 | APP.design.toggleDisplayMode({ 77 | appPath: mainGameData.exe, 78 | appIcon: mainGameData.icon, 79 | appName: mainGameData.name, 80 | paramSfo: mainGameData.paramSfo, 81 | appStatus: APP.lang.getVariable('emuStatusRunning') 82 | }); 83 | 84 | // Save game settings 85 | APP.gameList.saveGameSettings(); 86 | 87 | } 88 | 89 | }, 90 | 91 | // Stop fpPS4 92 | killEmu: function(){ 93 | 94 | // Update status 95 | if (this.emuRunning === !0){ 96 | document.getElementById('LABEL_GAME_DETAILS_STATUS').innerHTML = APP.lang.getVariable('killEmuStatus'); 97 | TMS.css('DIV_GAME_DETAILS', {'display': 'flex'}); 98 | } 99 | 100 | // Kill process and set emu running var to false 101 | APP.getProcessInfo(APP.path.parse(APP.settings.data.emuPath).base, function(pData){ 102 | process.kill(pData.th32ProcessID); 103 | this.emuRunning = !1; 104 | }); 105 | 106 | } 107 | 108 | } -------------------------------------------------------------------------------- /App/js/filemanager.js: -------------------------------------------------------------------------------- 1 | /* 2 | ****************************************************************************** 3 | fpPS4 Temmie's Launcher 4 | filemanager.js 5 | 6 | This file contains all functions for loading files and paths 7 | ****************************************************************************** 8 | */ 9 | 10 | temp_FILEMANAGER = { 11 | 12 | // Select path 13 | selectPath: function(postAction){ 14 | 15 | if (postAction !== void 0 && typeof postAction === 'function'){ 16 | 17 | document.getElementById('APP_FOLDER_LOADER').onchange = function(){ 18 | 19 | const cFile = document.getElementById('APP_FOLDER_LOADER').files[0]; 20 | 21 | if (cFile.path !== null && cFile.path !== void 0 && cFile.path !== ''){ 22 | document.getElementById('APP_FOLDER_LOADER').accept = ''; 23 | document.getElementById('APP_FOLDER_LOADER').value = ''; 24 | postAction(APP.tools.fixPath(cFile.path)); 25 | } 26 | 27 | } 28 | 29 | TMS.triggerClick('APP_FOLDER_LOADER'); 30 | 31 | } 32 | 33 | }, 34 | 35 | // Select file 36 | selectFile: function(ext, postAction){ 37 | 38 | if (ext !== void 0 && postAction !== void 0 && typeof postAction === 'function'){ 39 | 40 | if (ext === ''){ 41 | ext = '*.*'; 42 | } 43 | 44 | document.getElementById('APP_FILE_LOADER').value = ''; 45 | document.getElementById('APP_FILE_LOADER').files = null; 46 | document.getElementById('APP_FILE_LOADER').accept = ext; 47 | TMS.triggerClick('APP_FILE_LOADER'); 48 | 49 | // Start read 50 | document.getElementById('APP_FILE_LOADER').onchange = function(){ 51 | postAction(APP.tools.fixPath(document.getElementById('APP_FILE_LOADER').files[0].path)); 52 | } 53 | 54 | } 55 | 56 | }, 57 | 58 | // Save file 59 | saveFile: function(fileName, ext, mode, content, postAction){ 60 | 61 | // Fix extension 62 | if (ext === '' || typeof ext !== 'string'){ 63 | ext = '*.*'; 64 | } 65 | 66 | // Set file info 67 | document.getElementById('APP_FILE_SAVE').accept = ext; 68 | document.getElementById('APP_FILE_SAVE').nwsaveas = fileName; 69 | document.getElementById('APP_FILE_SAVE').onchange = function(){ 70 | 71 | var location = document.getElementById('APP_FILE_SAVE').value; 72 | if (location.replace(fileName, '') !== ''){ 73 | 74 | // Try writing file 75 | try { 76 | 77 | APP.fs.writeFileSync(location, content, mode); 78 | if (postAction !== void 0 && typeof postAction === 'function'){ 79 | postAction(APP.tools.fixPath(location)); 80 | } 81 | 82 | } catch (err) { 83 | console.error(err); 84 | APP.log(APP.lang.getVariable('errorSaveFile', [err])); 85 | } 86 | 87 | } 88 | 89 | // Reset DOM 90 | document.getElementById('APP_FILE_SAVE').value = ''; 91 | document.getElementById('APP_FILE_SAVE').accept = ''; 92 | 93 | } 94 | 95 | // Open save dialog 96 | TMS.triggerClick('APP_FILE_SAVE'); 97 | 98 | }, 99 | 100 | // Open game folder 101 | openDir: function(path){ 102 | 103 | // Switch platform 104 | switch (APP.os.platform()){ 105 | 106 | case 'win32': 107 | APP.childProcess.exec(`start "" "${path}"`); 108 | break; 109 | 110 | case 'linux': 111 | APP.childProcess.exec(`browse "${path}"`); 112 | break; 113 | 114 | } 115 | 116 | } 117 | 118 | } -------------------------------------------------------------------------------- /App/js/language.js: -------------------------------------------------------------------------------- 1 | /* 2 | ****************************************************************************** 3 | fpPS4 Temmie's Launcher 4 | language.js 5 | 6 | This file is a database with all labels and strings for different 7 | languages 8 | ****************************************************************************** 9 | */ 10 | 11 | temp_LANGUAGE = { 12 | 13 | // Get variable string 14 | getVariable: function(name, list){ 15 | 16 | // Fix settings 17 | if (Object.keys(this.selected).length === 0){ 18 | this.selected = this.english; 19 | } 20 | 21 | // Declare main var 22 | var lPatch = [], 23 | res = this.selected.variables[name]; 24 | 25 | // If variable is not found or an empty string, get English instead 26 | if (res === void 0 || res === ''){ 27 | res = this.english.variables[name]; 28 | } 29 | 30 | // If list is undefined, set patch list as a empty array 31 | if (list !== void 0){ 32 | lPatch = list; 33 | } 34 | 35 | // Apply variables and return string 36 | lPatch.forEach(function(fix, entry){ 37 | res = res.replace(`%VARIABLE_${entry}%`, fix); 38 | }); 39 | return res; 40 | 41 | }, 42 | 43 | /* 44 | Database 45 | */ 46 | 47 | // English (Default) 48 | english: { 49 | 50 | "lang": "English (Default)", 51 | 52 | "variables": { 53 | 54 | "labelEnableHack": "Enable", 55 | "emuStatusRunning": "Running", 56 | "logWindowTitle": "Running fpPS4", 57 | "killEmuStatus": "Main process closed - close the fpPS4 log window to go back", 58 | "logCleared": "INFO - Previous log was cleared!", 59 | "about": "fpPS4 Temmie\'s Launcher - Version: %VARIABLE_0%\n\nCreated by TheMitoSan [Previously known as TemmieHeartz]\n(https://twitter.com/themitosan)\n\nfpPS4 is created by red-prig\n(https://github.com/red-prig/fpPS4)\n\nPlugin memoryjs is created by Rob--\n(https://github.com/rob--/memoryjs)\n\nPlugin node-stream-zip is created by antelle\n(https://github.com/antelle/node-stream-zip)\n\nSVG icons were obtained from https://www.svgrepo.com/", 60 | "mainLog": 'fpPS4 Temmie\'s Launcher - Version: %VARIABLE_0%\nRunning on nw.js version %VARIABLE_1% [%VARIABLE_2%]', 61 | "settingsErrorCreatePath": "ERROR - Unable to create path!\n(%VARIABLE_0%)\n%VARIABLE_1%", 62 | "settingsErrorfpPS4NotFound": "ERROR - Unable to locate the main fpPS4 executable!\nMake sure to select it in Settings or insert it in the \"Emu\" folder and click OK.", 63 | "settingsConfirmRemoveAllGameSettings": "WARNING - This option will remove all saved settings from your game list.\nDo you want to continue?", 64 | "settingsRemovedGameSettings": "INFO - ( %VARIABLE_0% ) Settings file was removed successfully!", 65 | "settingsConfirmRemoveGameSettings": "WARNING - This action will delete all saved settings for %VARIABLE_0%\n\nDo you want to continue?", 66 | "settingsRemoveGameSettingsError": 'ERROR - ( %VARIABLE_0% ) Unable to delete settings file!\nReason: %VARIABLE_1%', 67 | "settingsRemoveGameSettings404": 'WARNING - ( %VARIABLE_0% ) Unable to find the settings file for this app / game!', 68 | "infoProcessComplete": "INFO - Process complete!\nCheck the log for more details", 69 | "infoSettingsUpdated": "INFO - Settings file was updated successfully!", 70 | "settingsLoadError": "ERROR - Unable to load the settings file!\n %VARIABLE_0%", 71 | "settingsSaveError": "ERROR - Unable to save the settings file!\n %VARIABLE_0%", 72 | "runEmuArgs": "\nINFO - Running fpPS4 with args: %VARIABLE_0%\nEmu location: %VARIABLE_1%", 73 | "closeEmuStatus": "INFO - %VARIABLE_0% was closed, returning code %VARIABLE_1%", 74 | "removedLibModules": "INFO - All the previously imported modules using this launcher were removed since it can be harmful to your game dumps.", 75 | "removeLibModule": "INFO - ( %VARIABLE_0% ) Removing module: %VARIABLE_1%", 76 | "removeModuleError": "ERROR - Unable to remove modules!\nReason: %VARIABLE_0%", 77 | "updateGameSettings": "INFO - ( %VARIABLE_0% ) Settings file was updated successfully!", 78 | "updateGameSettingsError": "ERROR - Unable to update the settings file for %VARIABLE_0% at %VARIABLE_1%!\nReason: %VARIABLE_2%", 79 | "skipUpdateGameSettings": "INFO - ( %VARIABLE_0% ) Skipped updating the settings file since it has no changes!", 80 | "errorSaveFile": "ERROR - Unable to save the file!\nReason: %VARIABLE_0%", 81 | "saveSucessfullPath": "INFO - Save successful!\nPath: %VARIABLE_0%", 82 | "createdSettingsFile": "INFO - Settings file was created successfully for %VARIABLE_0%", 83 | "errorCreateSettingsFile": "ERROR - Unable to create the settings file for %VARIABLE_0% at %VARIABLE_1%!\nReason: %VARIABLE_2%", 84 | "patchLoadedSucessfully": "INFO - Patch loaded successfully!\nName: %VARIABLE_0%\nType: %VARIABLE_1%", 85 | "patchLoadErrorMismatch": "ERROR - This isn\'t a patch or it isn't made for this app / game!\nPatch ID: %VARIABLE_0%\nSelected app / game: %VARIABLE_1%", 86 | "patchLoadErrorParamSfo404": "ERROR - Unable to find the PARAM.SFO for this patch!", 87 | "gameListLoadWarnPlayGo": "WARNING - Unable to locate the playgo-chunk.dat for %VARIABLE_0%!\nIf this isn\'t a homebrew, check if this app / game was dumped properly.", 88 | "gameListLoadWarnParamSfo": "WARNING - Unable to locate the PARAM.SFO for %VARIABLE_0%!\nIf this isn\'t a homebrew, check if this app / game was dumped properly.", 89 | "gameListDoubleIdError": "WARNING - Unable to add %VARIABLE_0% to game list because another app / game with the same title ID already exists! ( %VARIABLE_1% )", 90 | "gameListNoGameFound": "INFO - No apps / games were detected on current path ( %VARIABLE_0% )", 91 | "gameListSearch404": "Unable to find", 92 | "checkDumpPlayGoOnApp": "INFO - ( %VARIABLE_0% ) playgo-chunk.dat was found inside sce_sys/app - a new copy was created in sce_sys.", 93 | "gameListLoadSuccessful": "INFO - Game list was loaded successfully! ( %VARIABLE_0% entries found )", 94 | "gameListVersion": "Version", 95 | "selectGameLoadPatchErrorParamSfo": "ERROR - Unable to read the PARAM.SFO from this patch!\n%VARIABLE_0%", 96 | "path": "Path", 97 | "gamelistGamePath404": "ERROR - Unable to find the selected app / game path!\n%VARIABLE_0%", 98 | "updateEmuFetchActionsError": "ERROR - Unable to fetch GitHub actions data!", 99 | "updateEmuIsLatestVersion": "INFO - You are already using the latest fpPS4 version available!\nCommit ID (SHA): %VARIABLE_0%", 100 | "updateEmuShaAvailable": "INFO - A new fpPS4 update is available!\n\nLocal version: %VARIABLE_0%\nUpstream version: %VARIABLE_1%\n\nDo you want to update?", 101 | "updateEmuShaUnavailable": "INFO - This Launcher detected that you didn\'t updated fpPS4 yet (or the fpPS4 executable was not found!)\n\nYou can fix this by running the fpPS4 updater process.\nDo you want to proceed?", 102 | "updateEmuDownloadFailed": "ERROR - Unable to download the fpPS4 update!\nResponse status: %VARIABLE_0% - OK: %VARIABLE_1%", 103 | "updateEmuProcessComplete": "INFO - Update complete! - New fpPS4 version (Commit ID / SHA): %VARIABLE_0%", 104 | "updateEmu-1-4": "Downloading fpPS4 update ()", 105 | "updateEmu-2-4": "Extracting update", 106 | "updateEmu-3-4": "Removing leftover files", 107 | "updateEmu-4-4": "Update complete!", 108 | "settingsLogEmuSha": "INFO - fpPS4 version: %VARIABLE_0%", 109 | "dumpStatus_OK": "Fine", 110 | "dumpStatus_WARN": "Missing files", 111 | "dumpStatus_HB": "Homebrew", 112 | "updateEmuWorkflow404": "ERROR - (Updater) Unable to load the workflow list from the fpPS4 GitHub!", 113 | "updater_noWorkflowListAvailable": "No workflow list available", 114 | "Sdl2NotFound": "SDL2.dll is not found in the Emu folder, please install it to use SDL2.", 115 | "errorListUnableLocateGamePath": "ERROR - Unable to locate \"%VARIABLE_0%\" settings path! In order to prevent issues, the game list will be reloaded.\nPath: %VARIABLE_1%", 116 | "updateEmuSettingsWorkflow404": "ERROR - (Updater) Unable to find (%VARIABLE_0%) on the fpPS4 workflow list! %VARIABLE_1% will be used as a fallback.", 117 | "nonWindowsOsWarn": "WARN - You are running fpPS4 Temmie's Launcher on a non-windows operating system!\n\nIn order to run fpPS4, you will need Wine installed on your OS.\n\nBe aware that running fpPS4 through tools like Wine can result in more glitches and a degraded performance / experience.", 118 | "cGameCompatStatus_BOOTS": "Boots", 119 | "cGameCompatStatus_MENUS": "Menus", 120 | "cGameCompatStatus_INGAME": "In-Game", 121 | "cGameCompatStatus_UNKNOWN": "Unknown", 122 | "cGameCompatStatus_NOTHING": "Nothing", 123 | "cGameCompatStatus_PLAYABLE": "Playable", 124 | "warnUnableFindGameCompatDb": "WARN - Unable to find the compatibility status for \"%VARIABLE_0%\" (%VARIABLE_1%) on the fpPS4 database!", 125 | "warnUserOffline": "WARN - You are offline! Some features (like the game compatibility status and the fpPS4 updater) will not be available until you reconnect to the internet." 126 | }, 127 | 128 | "title": { 129 | "DIV_selectedGameStatus_dump": "Green: All files are present\nYellow: Some files are missing - check the log for more details\nCyan: Executable is a .elf file", 130 | "DIV_selectedGameStatus_compat": "Playable: You can play this title from start to finish.\nIn-game: You can play parts / segments of this title, but you can't finish it.\nMenus: This title boots into the main menu, but you can't play the main game.\nBoots: This title starts loading the game, but fails at some point.\nNothing: This title doesn't do anything.\nUnknown: There is no data about this title on the fpPS4 database." 131 | } 132 | 133 | }, 134 | 135 | // Selected lang 136 | selected: {} 137 | 138 | } -------------------------------------------------------------------------------- /App/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | ****************************************************************************** 3 | fpPS4 Temmie's launcher 4 | main.js 5 | 6 | This file contains all modules and required functions to initialize 7 | launcher. 8 | ****************************************************************************** 9 | */ 10 | 11 | const APP = { 12 | 13 | // Load nwjs / node.js modules 14 | loadModules: function(){ 15 | 16 | try{ 17 | 18 | // Require global modules 19 | APP.fs = require('fs'); 20 | APP.os = require('os'); 21 | APP.win = nw.Window.get(); 22 | APP.path = require('path'); 23 | APP.https = require('https'); 24 | APP.childProcess = require('child_process'); 25 | APP.packageJson = require('../package.json'); 26 | APP.streamZip = require('App/node_modules/node-stream-zip'); 27 | 28 | // If current OS is windows, load memoryjs 29 | if (APP.os.platform() === 'win32'){ 30 | APP.memoryjs = require('App/node_modules/memoryjs'); 31 | } 32 | 33 | } catch(e) { 34 | console.error(e); 35 | window.alert(`ERROR - Unable to load node modules!\n${e}`); 36 | } 37 | 38 | }, 39 | 40 | // App version 41 | title: '', 42 | version: '', 43 | appVersion: void 0, 44 | 45 | // Internet connection 46 | webConnection: !1, 47 | 48 | // Import app modules 49 | tools: temp_TOOLS, 50 | lang: temp_LANGUAGE, 51 | design: temp_DESIGN, 52 | gameList: temp_GAMELIST, 53 | settings: temp_SETTINGS, 54 | emuManager: temp_EMUMANAGER, 55 | fileManager: temp_FILEMANAGER, 56 | paramSfo: temp_PARAMSFO_PARSER, 57 | 58 | // Log function and variables 59 | logData: '', 60 | logLine: '', 61 | log: function(text){ 62 | 63 | if (text !== '' && text !== void 0){ 64 | 65 | // Delclare main vars 66 | var canLog = !0, 67 | previousLog = APP.logData, 68 | newLog = `${previousLog}\n${text}`; 69 | 70 | // Fix log with white line 71 | if (previousLog == ''){ 72 | newLog = text; 73 | } 74 | if (previousLog.slice(previousLog.length - 1, previousLog.length) === '\n'){ 75 | newLog = previousLog + text; 76 | } 77 | 78 | // Fix duplicate lines 79 | if (APP.logLine === text){ 80 | canLog = !1; 81 | } 82 | 83 | // Check if can append log 84 | if (canLog === !0){ 85 | 86 | // Set current line, append log and scroll log view 87 | APP.logLine = text; 88 | document.getElementById('APP_LOG').value = newLog; 89 | APP.logData = newLog; 90 | document.getElementById('APP_LOG').scrollTop = document.getElementById('APP_LOG').scrollHeight; 91 | 92 | } 93 | 94 | } 95 | 96 | }, 97 | 98 | // Clear Log 99 | clearLog: function(){ 100 | 101 | // Reset log 102 | APP.logData = APP.appVersion; 103 | document.getElementById('APP_LOG').value = APP.appVersion; 104 | APP.log(APP.lang.getVariable('logCleared')); 105 | 106 | }, 107 | 108 | // DEBUG: Process fpPS4 output data 109 | processStdOutput: function(data, type){ 110 | 111 | const logSplit = data.split('\n'); 112 | logSplit.forEach(function(logLine){ 113 | 114 | if (logLine !== '' && logLine !== '\r'){ 115 | console[type](logLine); 116 | } 117 | 118 | }); 119 | 120 | }, 121 | 122 | // Run fpPS4 123 | execProcess: void 0, 124 | runfpPS4: function(exe, args){ 125 | 126 | if (exe !== void 0 && exe !== ''){ 127 | 128 | /* 129 | Change context path to current emu folder 130 | This will allow fpPS4 create all required folders (savedata, shader_dump, tmp) on it's current location. 131 | */ 132 | process.chdir(APP.path.parse(exe).dir); 133 | 134 | // Run external window 135 | if (APP.settings.data.debugTestLog === !1){ 136 | 137 | // Window state 138 | var winMode, 139 | pressAnyKey = '', 140 | emuExecPath = APP.path.parse(APP.settings.data.emuPath).base, 141 | cmdWinTitle = `"${APP.lang.getVariable('logWindowTitle')} - ${APP.gameList.selectedGame}"`; 142 | 143 | // Switch cmd window mode 144 | switch (APP.settings.data.logExternalWindowStartMode){ 145 | 146 | case 'normal': 147 | winMode = ''; 148 | break; 149 | 150 | case 'max': 151 | winMode = '/MAX'; 152 | break; 153 | 154 | case 'min': 155 | winMode = '/MIN'; 156 | break; 157 | 158 | } 159 | 160 | // Ask user to press any key 161 | if (APP.settings.data.logExternalWindowPrompt === !0){ 162 | pressAnyKey = '^& pause'; 163 | } 164 | 165 | // Transform args into string 166 | var gPath = `"${args[args.indexOf('-e') + 1]}"`, 167 | parseArgs = args.toString().replace(RegExp(',', 'gi'), ' ').replace(args[args.indexOf('-e') + 1], gPath), 168 | execLine = `start ${cmdWinTitle} ${winMode} cmd /C ${emuExecPath} ${parseArgs} ${pressAnyKey}`; 169 | 170 | // Check if needs to change exec line and run process 171 | if (APP.os.platform() !== 'win32'){ 172 | execLine = `wine wineconsole "Z:${APP.settings.data.emuPath}" ${parseArgs}` 173 | } 174 | APP.execProcess = APP.childProcess.exec(execLine); 175 | 176 | } else { 177 | 178 | /* 179 | Debug 180 | */ 181 | console.clear(); 182 | APP.execProcess = APP.childProcess.spawn(exe, args, { detached: !0 }); 183 | 184 | } 185 | 186 | // Set emu running and stream as string (UTF-8) 187 | APP.emuManager.emuRunning = !0; 188 | APP.execProcess.stdout.setEncoding('utf-8'); 189 | APP.execProcess.stderr.setEncoding('utf-8'); 190 | 191 | // Log on stdout and stderr 192 | APP.execProcess.stdout.on('data', function(data){ 193 | APP.processStdOutput(data, 'info'); 194 | }); 195 | APP.execProcess.stderr.on('data', function(data){ 196 | APP.processStdOutput(data, 'error'); 197 | }); 198 | 199 | // Log on close 200 | APP.execProcess.on('close', function(code){ 201 | 202 | // Reset chdir 203 | process.chdir(APP.settings.data.nwPath); 204 | APP.emuManager.emuRunning = !1; 205 | 206 | // Update GUI 207 | APP.design.update(); 208 | APP.design.toggleDisplayMode({ 209 | appStatus: 'idle' 210 | }); 211 | 212 | // Log exit code and save log if APP.settings.data.saveLogOnEmuClose is true 213 | APP.log(APP.lang.getVariable('closeEmuStatus', [APP.path.parse(exe).base, code])); 214 | if (APP.settings.data.saveLogOnEmuClose === !0){ 215 | APP.clearLog(); 216 | } 217 | 218 | // Scroll game list to last selected game 219 | if (APP.gameList.selectedGame !== ''){ 220 | TMS.css(`GAME_ENTRY_${APP.gameList.selectedGame}`, {'animation': '0.8s hintGameFocus'}); 221 | TMS.focus('INPUT_gameListSearch', 100); 222 | 223 | setTimeout(function(){ 224 | APP.design.selectGame(APP.gameList.selectedGame); 225 | TMS.scrollCenter(`GAME_ENTRY_${APP.gameList.selectedGame}`); 226 | }, 100); 227 | 228 | } 229 | 230 | // Return exit code 231 | return code; 232 | 233 | }); 234 | 235 | } 236 | 237 | }, 238 | 239 | // MemoryJS - Get Process Info 240 | getProcessInfo: function(processName, postAction){ 241 | 242 | // Check if current os is windows 243 | if (APP.os.platform() === 'win32'){ 244 | 245 | // Get process list and start seek 246 | var res, pList = this.memoryjs.getProcesses(); 247 | Object.keys(pList).forEach(function(pName){ 248 | 249 | if (pList[pName].szExeFile.toLowerCase() === processName.toLowerCase()){ 250 | res = pList[pName]; 251 | } 252 | 253 | }); 254 | 255 | // If found and post-action function is present, execute it! 256 | if (postAction !== void 0 && res !== void 0){ 257 | postAction(res); 258 | } 259 | 260 | } 261 | 262 | }, 263 | 264 | // Check current operating system 265 | checkCurrentOs: function(){ 266 | 267 | // Check if needs to display warn 268 | if (APP.os.platform() !== 'win32' && APP.settings.data.nonWindowsOsWarn === !1){ 269 | window.alert(APP.lang.getVariable('nonWindowsOsWarn')); 270 | APP.log(APP.lang.getVariable('nonWindowsOsWarn')); 271 | APP.settings.data.nonWindowsOsWarn = !0; 272 | APP.settings.save(); 273 | APP.emuManager.update.check({forceUpdate: !0, silent: !0}); 274 | } 275 | 276 | }, 277 | 278 | // Check current user internet connection 279 | startOnlineCheck: function(){ 280 | 281 | // Create update connection function 282 | const updateConnectionStatus = function(){ 283 | APP.webConnection = navigator.onLine; 284 | }; 285 | 286 | // Set current connection status and crerate event listeners 287 | APP.webConnection = navigator.onLine; 288 | window.addEventListener('online', function(){ 289 | updateConnectionStatus(); 290 | document.getElementById('BTN_UPDATE_FPPS4').disabled = !1; 291 | }); 292 | window.addEventListener('offline', function(){ 293 | updateConnectionStatus(); 294 | APP.log(APP.lang.getVariable('warnUserOffline')); 295 | document.getElementById('BTN_UPDATE_FPPS4').disabled = !0; 296 | }); 297 | 298 | }, 299 | 300 | // About screen 301 | about: function(){ 302 | window.alert(this.lang.getVariable('about', [this.version])); 303 | }, 304 | 305 | // Reload app 306 | reload: function(){ 307 | location.reload(); 308 | } 309 | 310 | } 311 | 312 | // Delete modules 313 | delete temp_TOOLS; 314 | delete temp_DESIGN; 315 | delete temp_SETTINGS; 316 | delete temp_GAMELIST; 317 | delete temp_LANGUAGE; 318 | delete temp_EMUMANAGER; 319 | delete temp_EMU_UPDATE; 320 | delete temp_FILEMANAGER; 321 | delete temp_PARAMSFO_PARSER; 322 | 323 | // Start 324 | window.onload = function(){ 325 | 326 | try { 327 | 328 | // Load nwjs / node.js modules and start loding settings ( 1 / 2 ) 329 | APP.loadModules(); 330 | APP.settings.load(); 331 | APP.settings.loadLang(); 332 | 333 | // App title 334 | APP.version = APP.packageJson.version; 335 | APP.title = `${APP.packageJson.name} - Ver. ${APP.version} [${process.versions['nw-flavor'].toUpperCase()}]`; 336 | document.title = APP.title; 337 | 338 | // App Log 339 | APP.appVersion = APP.lang.getVariable('mainLog', [APP.version, process.versions.nw, process.versions['nw-flavor'].toUpperCase()]); 340 | APP.log(APP.appVersion); 341 | 342 | // Start connection check, load remaining settings, kill fpPS4 process if is active and check currert OS 343 | APP.settings.checkPaths(); 344 | APP.design.renderSettings(); 345 | APP.emuManager.killEmu(!0); 346 | APP.startOnlineCheck(); 347 | APP.checkCurrentOs(); 348 | 349 | // Rener hack list, gamepad modes and focus input search field 350 | APP.design.renderHacklist(); 351 | TMS.focus('INPUT_gameListSearch'); 352 | 353 | // Load game list, Get all available workflows from updater and check if fpPS4 have any update (silently) 354 | APP.gameList.load(); 355 | APP.emuManager.update.getWorkflows(); 356 | if (APP.emuManager.update.skipLoadingCheck === !1){ 357 | APP.emuManager.update.check({ silent: !0 }); 358 | } 359 | 360 | } catch (err) { 361 | 362 | // Log error 363 | console.error(err); 364 | window.alert(`ERROR - Unable to start main application!\n\nReason:\n${err}\n\nTo know more, hit F12 and go to console tab to see more details.`); 365 | 366 | } 367 | 368 | } -------------------------------------------------------------------------------- /App/js/paramSfoDatabase.js: -------------------------------------------------------------------------------- 1 | /* 2 | ****************************************************************************** 3 | fpPS4 Temmie's Launcher 4 | paramSfoDatabase.js 5 | 6 | This is a simple database used to identify extracted content from PARAM.SFO 7 | files. 8 | 9 | Article used as reference: 10 | https://www.psdevwiki.com/ps4/Param.sfo 11 | ****************************************************************************** 12 | */ 13 | 14 | temp_PARAMSFO_DATABASE = { 15 | 16 | DB_APP_TYPE: { 17 | 0: 'Not specified', 18 | 1: 'Paid Standalone Full App', 19 | 2: 'Upgradable App', 20 | 3: 'Demo App', 21 | 4: 'Freemium App' 22 | }, 23 | 24 | DB_CATEGORY: { 25 | 'ac': 'Additional Content', 26 | 'bd': 'Blu-ray Disc?', 27 | 'gc': 'Game Content', 28 | 'gd': 'Game Digital Application', 29 | 'gda': 'System Application', 30 | 'gdb': 'Unknown', 31 | 'gdc': 'Non-Game Big Application', 32 | 'gdd': 'BG Application', 33 | 'gde': 'Non-Game Mini App / Video Service Native App', 34 | 'gdk': 'Video Service Web App', 35 | 'gdl': 'PS Cloud Beta App', 36 | 'gdO': 'PS2 Classic', 37 | 'gp': 'Game Application Patch', 38 | 'gpc': 'Non-Game Big App Patch', 39 | 'gpd': 'BG Application patch', 40 | 'gpe': 'Non-Game Mini App Patch / Video Service Native App Patch', 41 | 'gpk': 'Video Service Web App Patch', 42 | 'gpl': 'PS Cloud Beta App Patch', 43 | 'sd': 'Save Data', 44 | 'la': 'License Area', 45 | 'wda': 'Unknown' 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /App/js/paramSfoParser.js: -------------------------------------------------------------------------------- 1 | /* 2 | ****************************************************************************** 3 | fpPS4 Temmie's Launcher 4 | paramSfoParser.js 5 | 6 | This file is responsible for holding all funcions / database for reading 7 | PARAM.SFO files! 8 | 9 | Many thanks to Control eXecute (@notzecoxao) for this challenge! 10 | 11 | Article used as reference: 12 | https://www.psdevwiki.com/ps4/Param.sfo 13 | ****************************************************************************** 14 | */ 15 | 16 | temp_PARAMSFO_PARSER = { 17 | 18 | // PARAM.SFO Key Database 19 | database: temp_PARAMSFO_DATABASE, 20 | 21 | /* 22 | Read PARAM.SFO files! 23 | This function returns all data as object. 24 | 25 | Info: Since all hex data is being read as String, here is a simple explanation: 26 | To read 0x04 bytes, Slice current string from starting point (0x00) to selection length (0x04) using it's hex value converted to int * 2 27 | 28 | parseInt 29 | (Start) 0x00 ---------> 0 30 | (Length) 0x04 ---------> (4 * 2) = 8; 31 | 32 | JS: sfoHex.slice(0, 8); ---> 00 50 53 46 (" PSF") 33 | */ 34 | parse: function(fLocation){ 35 | 36 | // Read file as hex (String) 37 | const sfoHex = APP.fs.readFileSync(fLocation, 'hex'); 38 | 39 | var sfoMetadata = {}, 40 | 41 | // SFO Header 42 | sfoHeader = { 43 | 44 | magic: APP.tools.parseEndian(sfoHex.slice(0, 8)), // (0x04) " PSF" Magic 45 | version: APP.tools.parseEndian(sfoHex.slice(8, 16)), // (0x04) File Version (01 01) 46 | keyTableStart: APP.tools.parseEndian(sfoHex.slice(16, 24)), // (0x04) Key table start offset 47 | dataTableStart: APP.tools.parseEndian(sfoHex.slice(24, 32)), // (0x04) Data table start offset 48 | totalIndexEntries: APP.tools.parseEndian(sfoHex.slice(32, 40)) // (0x04) Total entries in index table 49 | 50 | } 51 | 52 | /* 53 | Create key list [APP_TYPE, TITLE_ID...] 54 | */ 55 | var listAttrRaw = sfoHex.slice((parseInt(sfoHeader.keyTableStart, 16) * 2), (parseInt(sfoHeader.dataTableStart, 16) * 2)), 56 | listAttrArray = APP.tools.convertHexToUft8(listAttrRaw).split('\x00'); 57 | 58 | // Remove blank entries [I'm not feeling 100% secure about this method but... Here we go!] 59 | while (listAttrArray.indexOf('') !== -1){ 60 | listAttrArray.splice(listAttrArray.indexOf(''), 1); 61 | } 62 | 63 | /* 64 | Get reading mode for current entry [Key table offset, param_fmt...] 65 | */ 66 | 67 | // Set variables 68 | var readMode = {}, 69 | readerLocation = 0, 70 | hexStartLocation = sfoHex.slice(40); 71 | 72 | // Get key table data info 73 | listAttrArray.forEach(function(cAttr){ 74 | 75 | // Slice current data and set current read mode 76 | const cReadingMode = hexStartLocation.slice(readerLocation, parseInt(readerLocation + 32)); 77 | readMode[cAttr] = { 78 | 79 | keyTableOffset: cReadingMode.slice(0, 4), // Key table offset 80 | param_fmt: cReadingMode.slice(4, 8), // param_fmt (type of data) 81 | paramLength: cReadingMode.slice(8, 16), // Parameter length 82 | paramMaxLength: cReadingMode.slice(16, 24), // Parameter Max Length 83 | dataOffset: cReadingMode.slice(24, 32) // Data Offset 84 | 85 | } 86 | 87 | // Update position for next location 88 | readerLocation = parseInt(readerLocation + 32); 89 | 90 | }); 91 | 92 | /* 93 | Set metadata info 94 | */ 95 | 96 | // Set location to data table start create first slice 97 | var pointerLocation = 0, 98 | dataTableSlice = sfoHex.slice(parseInt(sfoHeader.dataTableStart, 16) * 2); 99 | 100 | // Process list 101 | listAttrArray.forEach(function(cAttr, cIndex){ 102 | 103 | // Get hex file starting from current location 104 | var keyData = '', 105 | convertUft8 = !1, 106 | stopLocation = parseInt(pointerLocation + 8); // Default: int32 107 | 108 | /* 109 | Check param length 110 | 111 | If not 32 bits unsigned (0x0404), use paramMaxLength converted to int minus 1 multiplied by 2 112 | ...and (of course) convert it to utf-8! 113 | 114 | JS: length = ((parseInt(paramMaxLength, 16) - 1) * 2) 115 | */ 116 | if (readMode[cAttr].param_fmt !== '0404'){ 117 | convertUft8 = !0; 118 | stopLocation = (pointerLocation + ((parseInt(APP.tools.parseEndian(readMode[cAttr].paramLength), 16) - 1)) * 2); 119 | } 120 | 121 | // Get attr data 122 | keyData = dataTableSlice.slice(pointerLocation, stopLocation); 123 | 124 | // Set key value to attr 125 | if (convertUft8 === !0){ 126 | sfoMetadata[cAttr] = APP.tools.convertHexToUft8(keyData); 127 | } else { 128 | sfoMetadata[cAttr] = APP.tools.parseEndian(keyData); 129 | } 130 | 131 | // Update reader location 132 | if (listAttrArray[(cIndex + 1)] !== void 0){ 133 | pointerLocation = (parseInt(APP.tools.parseEndian(readMode[listAttrArray[(cIndex + 1)]].dataOffset), 16) * 2); 134 | } 135 | 136 | }); 137 | 138 | // End 139 | return sfoMetadata; 140 | 141 | } 142 | 143 | } 144 | 145 | delete temp_PARAMSFO_DATABASE; -------------------------------------------------------------------------------- /App/js/settings.js: -------------------------------------------------------------------------------- 1 | /* 2 | ****************************************************************************** 3 | fpPS4 Temmie's Launcher 4 | settings.js 5 | 6 | This file is contains all functions / variables related to settings menu 7 | and Launcher's look, behavior and more. 8 | ****************************************************************************** 9 | */ 10 | 11 | temp_SETTINGS = { 12 | 13 | // Settings list 14 | data: { 15 | 16 | /* 17 | General 18 | */ 19 | 20 | // App Version 21 | launcherVersion: '', 22 | 23 | // Language 24 | appLanguage: 'english', 25 | 26 | // Paths 27 | nwPath: '', 28 | emuPath: '', 29 | gamePath: '', 30 | 31 | // Run fpPS4 on fullscreen 32 | enableEmuFullscreen: !1, 33 | 34 | // Enable / disable change led color 35 | sdlEnableGamepadLed: !0, 36 | 37 | // Enable / disable PARAM.SFO support 38 | enableParamSfo: !0, 39 | 40 | // Log External window 41 | logExternalWindowPrompt: !0, 42 | logExternalWindowStartMode: 'normal', 43 | 44 | /* 45 | GUI 46 | */ 47 | 48 | // Zoom scale 49 | guiZoomScale: 1, 50 | 51 | // Game list 52 | showBgOnEntry: !0, 53 | showPathEntry: !0, 54 | gameListMode: 'compact', 55 | 56 | // Emu running 57 | showPathRunning: !0, 58 | showGuiMetadata: !0, 59 | 60 | // Game search mode (appName or titleId) 61 | gameSearchMode: 'appName', 62 | searchCaseSensitive: !1, 63 | 64 | // Background Opacity 65 | bgEmuOpacity: 0.6, 66 | bgListOpacity: 0.7, 67 | 68 | // Background Blur 69 | bgEmuBlur: 6, 70 | bgListBlur: 2, 71 | 72 | // (Grid) 73 | gridIconSize: 116, 74 | gridBorderRadius: 8, 75 | 76 | // Enable compat status check 77 | enableCompatStatusCheck: !0, 78 | 79 | /* 80 | fpPS4 Update 81 | */ 82 | latestCommitSha: '', 83 | enableEmuUpdates: !0, 84 | fpps4BranchName: 'trunk', 85 | fpps4selectedCI: 'Main CI', 86 | 87 | /* 88 | Warning messages 89 | */ 90 | nonWindowsOsWarn: !1, 91 | 92 | /* 93 | Debug 94 | */ 95 | debugTestLog: !1 96 | 97 | }, 98 | 99 | // Load settings 100 | load: function(){ 101 | 102 | // Get launcher main dir before settings load 103 | var updateSettings = !1, 104 | nwPath = APP.tools.fixPath(nw.__dirname), 105 | settingsPath = `${nwPath}/Settings.json`; 106 | 107 | // Create save 108 | if (APP.fs.existsSync(settingsPath) === !1){ 109 | APP.settings.save(); 110 | } 111 | 112 | try { 113 | 114 | // Read settings file and check for obsolete keys 115 | var loadSettings = JSON.parse(APP.fs.readFileSync(settingsPath, 'utf8')); 116 | Object.keys(loadSettings).forEach(function(cSettings){ 117 | 118 | if (APP.settings.data[cSettings] === void 0){ 119 | delete loadSettings[cSettings]; 120 | updateSettings = !0; 121 | } 122 | 123 | }); 124 | 125 | // Fix new settings data 126 | Object.keys(this.data).forEach(function(cSettings){ 127 | 128 | if (loadSettings[cSettings] === void 0){ 129 | loadSettings[cSettings] = APP.settings.data[cSettings]; 130 | updateSettings = !0; 131 | } 132 | 133 | }); 134 | 135 | // Load settings and check if needs to update settings file 136 | this.data = loadSettings; 137 | if (updateSettings === !0){ 138 | APP.log(APP.lang.getVariable('infoSettingsUpdated')); 139 | APP.settings.save(); 140 | } 141 | 142 | // Fix path 143 | this.data.nwPath = APP.tools.fixPath(nw.__dirname); 144 | 145 | } catch (err) { 146 | 147 | console.error(APP.lang.getVariable('settingsLoadError', [err])); 148 | 149 | } 150 | 151 | }, 152 | 153 | // Save settings 154 | save: function(){ 155 | 156 | // Get launcher main dir before settings load and include current launcher version on settings 157 | const nwPath = APP.tools.fixPath(nw.__dirname); 158 | this.data.launcherVersion = APP.packageJson.version; 159 | 160 | try { 161 | APP.fs.writeFileSync(`${nwPath}/Settings.json`, JSON.stringify(this.data), 'utf-8'); 162 | } catch (err) { 163 | console.error(APP.lang.getVariable('settingsSaveError', [err])); 164 | } 165 | 166 | }, 167 | 168 | // Load selected language 169 | loadLang: function(){ 170 | 171 | try { 172 | 173 | // Get lang data 174 | var cLang = this.data.appLanguage, 175 | fileLocation = `${APP.settings.data.nwPath}/Lang/${cLang}.json`; 176 | 177 | // Check if lang file exists and if lang isn't english 178 | if (cLang !== 'english' && APP.fs.existsSync(fileLocation) === !0){ 179 | 180 | // Get selected lang 181 | var getLangFile = APP.fs.readFileSync(fileLocation, 'utf-8'); 182 | APP.lang.selected = JSON.parse(getLangFile); 183 | 184 | } else { 185 | 186 | // Set english as default lang 187 | APP.lang.selected = APP.lang.english; 188 | 189 | } 190 | 191 | // Update GUI 192 | APP.design.updateLang(); 193 | 194 | } catch (err) { 195 | 196 | console.error(err); 197 | 198 | } 199 | 200 | }, 201 | 202 | // Check paths 203 | checkPaths: function(){ 204 | 205 | // Create main vars 206 | var logMessage = '', 207 | mainPath = this.data.nwPath, 208 | pathList = ['/Emu', '/Games', '/Lang']; 209 | 210 | // Try create required paths 211 | pathList.forEach(function(cPath){ 212 | 213 | if (APP.fs.existsSync(mainPath + cPath) !== !0){ 214 | 215 | try { 216 | APP.fs.mkdirSync(mainPath + cPath); 217 | } catch (err) { 218 | APP.log(APP.lang.getVariable(settingsErrorCreatePath, [mainPath + cPath, err])); 219 | } 220 | 221 | } 222 | 223 | }); 224 | 225 | // Set Games / Emu paths and check if both exists 226 | if (this.data.gamePath === '' && APP.fs.existsSync(this.data.gamePath) === !1){ 227 | APP.settings.data.gamePath = `${mainPath}/Games`; 228 | } 229 | 230 | // fpPS4 path 231 | if (this.data.emuPath === '' || APP.fs.existsSync(this.data.emuPath) === !1){ 232 | APP.settings.data.emuPath = `${mainPath}/Emu/fpPS4.exe`; 233 | } 234 | 235 | // If fpPS4 is not found, reset latest commit sha and request update 236 | if (APP.fs.existsSync(this.data.emuPath) !== !0){ 237 | 238 | // Set flag to skip update check on window.onload 239 | APP.emuManager.update.skipLoadingCheck = !0; 240 | this.data.latestCommitSha = ''; 241 | APP.emuManager.update.check(); 242 | 243 | } 244 | 245 | // If latestCommitSha isn't empty, log it 246 | if (this.data.latestCommitSha !== ''){ 247 | APP.log(APP.lang.getVariable('settingsLogEmuSha', [APP.settings.data.latestCommitSha.slice(0, 7)])); 248 | } 249 | APP.log(logMessage); 250 | 251 | }, 252 | 253 | // Select path 254 | selectPath: function(data){ 255 | 256 | APP.fileManager.selectPath(function(newGamePath){ 257 | document.getElementById(data.label).innerHTML = newGamePath; 258 | APP.settings.data[data.settings] = newGamePath; 259 | APP.settings.save(); 260 | APP.gameList.load(); 261 | }); 262 | 263 | }, 264 | 265 | // Select file 266 | selectFile: function(data){ 267 | 268 | APP.fileManager.selectFile(data.extension, function(newEmuPath){ 269 | document.getElementById(data.label).innerHTML = newEmuPath; 270 | APP.settings.data[data.settings] = newEmuPath; 271 | APP.settings.save(); 272 | APP.gameList.load(); 273 | }); 274 | 275 | }, 276 | 277 | // Set display mode from buttons 278 | setDisplayMode: function(cMode){ 279 | 280 | if (cMode !== void 0){ 281 | 282 | // Update display mode and clear previous search 283 | this.data.gameListMode = cMode; 284 | document.getElementById('INPUT_gameListSearch').value = ''; 285 | 286 | // Render GUI 287 | APP.design.renderSettings(!0); 288 | APP.design.renderGameList({displayLog: !1}); 289 | 290 | } 291 | 292 | }, 293 | 294 | // Reset all game settings 295 | resetAllGameSettings: function(){ 296 | 297 | // Confirm action 298 | const conf = window.confirm(APP.lang.getVariable('settingsConfirmRemoveAllGameSettings')); 299 | if (conf === !0){ 300 | 301 | // Reset search form 302 | document.getElementById('INPUT_gameListSearch').value = ''; 303 | 304 | // Get game list 305 | var cMessage = '', 306 | gList = Object.keys(APP.gameList.list); 307 | 308 | // Check if user has games and apps 309 | if (gList.length !== 0){ 310 | 311 | // Process game list 312 | gList.forEach(function(cGame){ 313 | 314 | // Check if settings file exists 315 | if (APP.fs.existsSync(`${APP.path.parse(APP.gameList.list[cGame].exe).dir}/launcherSettings.json`) === !0){ 316 | 317 | try { 318 | 319 | APP.fs.unlinkSync(`${APP.path.parse(APP.gameList.list[cGame].exe).dir}/launcherSettings.json`); 320 | cMessage = APP.lang.getVariable('settingsRemovedGameSettings', [APP.gameList.list[cGame].name]); 321 | 322 | } catch (err) { 323 | 324 | cMessage = APP.lang.getVariable('settingsRemoveGameSettingsError', [APP.gameList.list[cGame].name, err]); 325 | console.error(err); 326 | 327 | } 328 | 329 | } else { 330 | 331 | // Unable to find settings file 332 | cMessage = APP.lang.getVariable('settingsRemoveGameSettings404', [APP.gameList.list[cGame].name]); 333 | 334 | } 335 | 336 | // Log status 337 | APP.log(cMessage); 338 | 339 | }); 340 | 341 | // Process complete 342 | window.alert(APP.lang.getVariable('infoProcessComplete')); 343 | 344 | } 345 | 346 | } 347 | 348 | } 349 | 350 | } -------------------------------------------------------------------------------- /App/js/tools.js: -------------------------------------------------------------------------------- 1 | /* 2 | ****************************************************************************** 3 | fpPS4 Temmie's Launcher 4 | tools.js 5 | 6 | This file contains most tools for converting hex data 7 | ****************************************************************************** 8 | */ 9 | 10 | temp_TOOLS = { 11 | 12 | // Solve Hex 13 | solveHex: function(hex){ 14 | 15 | if (hex !== void 0){ 16 | return hex.toLowerCase().replace(RegExp(' ', 'gi'), ''); 17 | } 18 | 19 | }, 20 | 21 | // Unsolve Hex 22 | unsolveHex: function(hex){ 23 | 24 | if (hex !== void 0){ 25 | return hex.toUpperCase().match(/.{2,2}/g).toString().replace(RegExp(',', 'gi'), ' ') 26 | } 27 | 28 | }, 29 | 30 | // Parse endian values 31 | parseEndian: function(hex){ 32 | 33 | if (hex !== void 0){ 34 | return hex.match(/.{2,2}/g).reverse().toString().replace(RegExp(',', 'gi'), ''); 35 | } 36 | 37 | }, 38 | 39 | // Convert Hex values to UTF-8 string 40 | convertHexToUft8: function(hex){ 41 | 42 | var textValue = ''; 43 | if (hex !== void 0 && hex !== ''){ 44 | textValue = decodeURIComponent(`%${hex.match(/.{2,2}/g).join('%')}`); 45 | } 46 | 47 | return textValue; 48 | 49 | }, 50 | 51 | // Parse percentage 52 | parsePercentage: function(current, maximum){ 53 | 54 | var res = 0; 55 | if (current !== void 0 && maximum !== void 0){ 56 | res = Math.floor((current / maximum) * 100); 57 | } 58 | 59 | return res; 60 | 61 | }, 62 | 63 | // Process checkbox status 64 | processCheckbox: function(domName){ 65 | 66 | var res = !1, 67 | domId = document.getElementById(domName).checked; 68 | 69 | if (domId === !1){ 70 | res = !0; 71 | } 72 | 73 | document.getElementById(domName).checked = res; 74 | 75 | }, 76 | 77 | // Fix paths 78 | fixPath: function(path){ 79 | 80 | if (path !== void 0 && path !== ''){ 81 | return path.replace(RegExp('\\\\', 'gi'), '/'); 82 | } 83 | 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /App/js/updateEmu.js: -------------------------------------------------------------------------------- 1 | /* 2 | ****************************************************************************** 3 | fpPS4 Temmie's Launcher 4 | updateEmu.js 5 | 6 | This file is responsible for feching latest data from red-prig fpPS4 actions 7 | and update. 8 | ****************************************************************************** 9 | */ 10 | 11 | temp_EMU_UPDATE = { 12 | 13 | // Skip main loading call 14 | skipLoadingCheck: !1, 15 | 16 | // Log fetch error 17 | logFetchError: function(msg){ 18 | const errMsg = APP.lang.getVariable(msg); 19 | document.getElementById('BTN_UPDATE_FPPS4').disabled = ''; 20 | console.error(errMsg); 21 | APP.log(errMsg); 22 | }, 23 | 24 | // Fetch data from url 25 | fetchData: async function(url, callback){ 26 | 27 | // If url was provided 28 | if (url !== void 0 && APP.webConnection === !0 && typeof callback === 'function'){ 29 | 30 | // Get error message and fetch data 31 | fetch(url).then(function(resp){ 32 | 33 | // Check if fetch status is ok 34 | if (resp.ok === !0){ 35 | 36 | resp.json().then(function(jsonData){ 37 | callback(jsonData); 38 | }); 39 | 40 | } else { 41 | APP.emuManager.update.logFetchError('updateEmuFetchActionsError'); 42 | } 43 | 44 | }, function(err){ 45 | APP.emuManager.update.logFetchError('updateEmuFetchActionsError'); 46 | console.error(err); 47 | }); 48 | 49 | } 50 | 51 | }, 52 | 53 | // Get all available workflows 54 | getWorkflows: function(){ 55 | 56 | // Process workflows 57 | const processWorkflows = function(data){ 58 | 59 | // Create variables and check if data was provided 60 | var htmlTemp = ``; 61 | if (data !== void 0){ 62 | 63 | // Reset html temp and process workflow list 64 | htmlTemp = ''; 65 | data.workflows.forEach(function(cData){ 66 | htmlTemp = `${htmlTemp}`; 67 | }); 68 | 69 | } 70 | 71 | // Append HTML 72 | document.getElementById('SELECT_settingsUpdaterCurrentCI').innerHTML = htmlTemp; 73 | document.getElementById('SELECT_settingsUpdaterCurrentCI').value = APP.settings.data.fpps4selectedCI; 74 | 75 | } 76 | 77 | // Fetch data 78 | fetch('https://api.github.com/repos/red-prig/fpPS4/actions/workflows').then(function(resp){ 79 | 80 | // Check if fetch status is ok 81 | if (resp.ok === !0){ 82 | 83 | resp.json().then(function(jsonData){ 84 | processWorkflows(jsonData); 85 | }); 86 | 87 | } else { 88 | APP.emuManager.update.logFetchError('updateEmuFetchActionsError'); 89 | } 90 | 91 | }, function(err){ 92 | APP.emuManager.update.logFetchError('updateEmuFetchActionsError'); 93 | console.error(err); 94 | }); 95 | 96 | }, 97 | 98 | /* 99 | Fetch latest github actions 100 | 101 | options: Object 102 | jsonData: Object - GitHub actions list (json) 103 | forceUpdate: Boolean - Skip checks and download latest version available 104 | silent: Boolean - Don't show message if user already have latest version 105 | */ 106 | check: function(options){ 107 | 108 | // Process options 109 | if (options === void 0){ 110 | options = { forceUpdate: !1, silent: !1 }; 111 | } 112 | var fetchData = this.fetchData, 113 | workflowLink = 'https://api.github.com/repos/red-prig/fpPS4/actions/workflows', 114 | optionsList = ['forceUpdate', 'silent'].forEach(function(optId){ 115 | if (options[optId] === void 0){ 116 | options[optId] = !1; 117 | } 118 | }); 119 | 120 | // If Emu updates is available, has internet and fpPS4 isn't running 121 | if (APP.settings.data.enableEmuUpdates === !0 && navigator.onLine === !0 && APP.emuManager.emuRunning === !1){ 122 | 123 | // Disable check for updates emu and fetch workflow list 124 | document.getElementById('BTN_UPDATE_FPPS4').disabled = 'disabled'; 125 | fetchData(workflowLink, function(data){ 126 | 127 | // Set json and declare variables 128 | options['wList'] = data; 129 | var sWorkflow, 130 | wList = options.wList.workflows; 131 | 132 | // Fix empty ci 133 | if (APP.settings.data.fpps4selectedCI === ''){ 134 | APP.settings.data.fpps4selectedCI = 'Main CI'; 135 | } 136 | 137 | // Check if workflow list has items 138 | if (wList.length !== 0){ 139 | 140 | // Seek selected ci 141 | for (var i = 0; i < wList.length; i++){ 142 | if (wList[i].name === APP.settings.data.fpps4selectedCI){ 143 | sWorkflow = i; 144 | break; 145 | } 146 | } 147 | 148 | // Check if workflow was found. If not, use first available! 149 | if (sWorkflow === void 0){ 150 | sWorkflow = 0; 151 | APP.log(APP.lang.getVariable('updateEmuSettingsWorkflow404', [APP.settings.data.fpps4selectedCI, wList[sWorkflow]])); 152 | } 153 | fetchData(`${workflowLink}/${wList[sWorkflow].id}/runs`, function(data){ 154 | options['runs'] = data; 155 | APP.emuManager.update.processActions(options); 156 | }); 157 | 158 | } else { 159 | const errMsg = APP.lang.getVariable('updateEmuWorkflow404'); 160 | console.error(errMsg); 161 | APP.log(errMsg); 162 | } 163 | 164 | }); 165 | 166 | } 167 | 168 | }, 169 | 170 | // Process github actions data 171 | processActions: function(options){ 172 | 173 | // Check if data was provided 174 | if (options !== void 0){ 175 | 176 | // Variables 177 | var winConf, 178 | msgData = '', 179 | artifactData, 180 | canPrompt = !0, 181 | canUpdate = !1, 182 | msgMode = 'confirm', 183 | settingsData = APP.settings.data; 184 | 185 | // Seek for latest success run 186 | for (var i = 0; i < options.runs.workflow_runs.length; i++){ 187 | 188 | // Get current run data, check if status is completed (with a success build) and if is from same branch 189 | const cRun = options.runs.workflow_runs[i]; 190 | if (cRun.status === 'completed' && cRun.conclusion === 'success' && cRun.head_branch === settingsData.fpps4BranchName){ 191 | 192 | // Set canUpdate on and run info 193 | canUpdate = !0; 194 | artifactData = { 195 | artifact: cRun.id, 196 | sha: cRun.head_sha 197 | } 198 | break; 199 | } 200 | 201 | } 202 | 203 | // If found valid run 204 | if (artifactData !== void 0){ 205 | 206 | // Check if current version is latest commit (or force update is on) 207 | if (settingsData.latestCommitSha !== artifactData.sha || options.forceUpdate === !0){ 208 | 209 | // Set default update message 210 | msgData = APP.lang.getVariable('updateEmuShaAvailable', [settingsData.latestCommitSha.slice(0, 7), artifactData.sha.slice(0, 7)]); 211 | 212 | // If user didn't updated yet using launcher or executable was not found 213 | if (settingsData.latestCommitSha === '' || APP.fs.existsSync(settingsData.emuPath) === !1){ 214 | msgData = APP.lang.getVariable('updateEmuShaUnavailable'); 215 | } 216 | 217 | } else { 218 | 219 | // If silent is active 220 | if (options.silent === !0){ 221 | canPrompt = !1; 222 | } 223 | 224 | // User already have latest version 225 | if (settingsData.latestCommitSha === artifactData.sha && APP.fs.existsSync(settingsData.emuPath) === !0){ 226 | 227 | // Set message mode to alert and get message for latest version 228 | msgMode = 'alert'; 229 | msgData = APP.lang.getVariable('updateEmuIsLatestVersion', [settingsData.latestCommitSha.slice(0, 7)]); 230 | 231 | } 232 | 233 | } 234 | 235 | } 236 | 237 | // Check if can update 238 | if (canUpdate === !0 && canPrompt === !0){ 239 | winConf = window[msgMode](msgData); 240 | } 241 | 242 | // If can update and user confirms action or can update and force update is on 243 | if (msgMode === 'confirm' && winConf === !0 || canUpdate === !0 && options.forceUpdate === !0){ 244 | this.getZipFile(artifactData); 245 | } 246 | 247 | } 248 | 249 | // Enable updater button again 250 | document.getElementById('BTN_UPDATE_FPPS4').disabled = ''; 251 | 252 | }, 253 | 254 | /* 255 | Get zip from specific github action run 256 | 257 | Since GitHub requires a token to be able to download artifacts, nightly.links service will be used instead. 258 | https://nightly.link 259 | */ 260 | getZipFile: function(actionsData){ 261 | 262 | // If (by some reason) fpPS4 is running - close it! 263 | APP.emuManager.killEmu(); 264 | 265 | // Display GUI and start download 266 | APP.design.toggleEmuUpdateGUI('show'); 267 | APP.design.updateProgressbarStatus(25, APP.lang.getVariable('updateEmu-1-4', [actionsData.sha.slice(0, 7)])); 268 | fetch(`https://nightly.link/red-prig/fpPS4/actions/runs/${actionsData.artifact}/fpPS4.zip`).then(function(resp){ 269 | 270 | if (resp.ok === !0){ 271 | 272 | APP.https.get(resp.url, function(data){ 273 | 274 | const 275 | fPath = `${APP.settings.data.nwPath}/Emu/fpPS4.zip`, 276 | writeStream = APP.fs.createWriteStream(fPath); 277 | 278 | data.pipe(writeStream); 279 | writeStream.on('finish', function(){ 280 | 281 | // Close writestream and extract emu executable 282 | writeStream.close(); 283 | APP.emuManager.update.extractZip({ 284 | actions: actionsData, 285 | path: fPath 286 | }); 287 | 288 | }); 289 | 290 | }); 291 | 292 | } else { 293 | 294 | console.error(resp); 295 | APP.log(APP.lang.getVariable('updateEmuDownloadFailed', [resp.status, resp.ok])); 296 | 297 | } 298 | 299 | }); 300 | 301 | }, 302 | 303 | // Extract zip 304 | extractZip: function(data){ 305 | 306 | // Update status, open and extract zip file 307 | APP.design.updateProgressbarStatus(50, APP.lang.getVariable('updateEmu-2-4')); 308 | const updateFile = new APP.streamZip.async({ file: data.path }); 309 | updateFile.extract(null, `${APP.path.parse(data.path).dir}/`, function(err){ 310 | if (err){ 311 | console.error(err); 312 | } 313 | }).then(function(){ 314 | 315 | // Close zip and finish process 316 | updateFile.close(); 317 | APP.emuManager.update.finish(data); 318 | 319 | }); 320 | 321 | }, 322 | 323 | // Finish process 324 | finish: function(data){ 325 | 326 | // Update status, remove download file and update settings 327 | APP.design.updateProgressbarStatus(75, APP.lang.getVariable('updateEmu-3-4')); 328 | APP.fs.unlinkSync(data.path); 329 | APP.settings.data.latestCommitSha = data.actions.sha; 330 | APP.settings.data.emuPath = `${APP.path.parse(data.path).dir}/fpPS4.exe`; 331 | 332 | // Save settings and update progressbar 333 | APP.settings.save(); 334 | const processCompleteMsg = APP.lang.getVariable('updateEmuProcessComplete', [data.actions.sha.slice(0, 7)]); 335 | APP.design.updateProgressbarStatus(100, APP.lang.getVariable('updateEmu-4-4')); 336 | 337 | // Timing out just to update GUI 338 | setTimeout(function(){ 339 | 340 | // Display message and hide update gui 341 | APP.log(processCompleteMsg); 342 | window.alert(processCompleteMsg); 343 | APP.design.toggleEmuUpdateGUI('hide'); 344 | 345 | }, 410); 346 | 347 | } 348 | 349 | } -------------------------------------------------------------------------------- /App/node_modules/memoryjs/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parserOptions: { 4 | sourceType: 'module' 5 | }, 6 | extends: 'airbnb-base', 7 | rules: { 8 | 'linebreak-style': 0, 9 | 'import/no-unresolved': 0, 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Robert Valentyne 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themitosan/fpPS4-Temmie-s-Launcher/cc971d1c4ea3d37ad89536f5fd52706a70939d66/App/node_modules/memoryjs/assets/logo.png -------------------------------------------------------------------------------- /App/node_modules/memoryjs/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "memoryjs", 5 | "include_dirs" : [ 6 | " 2 | 3 | 4 | 5 | {739DB09A-CC57-A953-A6CF-F64FA08E4FA7} 6 | 7 | 8 | {2739B19F-16DF-601C-060A-FF86F6A40045} 9 | 10 | 11 | {739DB09A-CC57-A953-A6CF-F64FA08E4FA7} 12 | 13 | 14 | {2739B19F-16DF-601C-060A-FF86F6A40045} 15 | 16 | 17 | {739DB09A-CC57-A953-A6CF-F64FA08E4FA7} 18 | 19 | 20 | {2739B19F-16DF-601C-060A-FF86F6A40045} 21 | 22 | 23 | {739DB09A-CC57-A953-A6CF-F64FA08E4FA7} 24 | 25 | 26 | {2739B19F-16DF-601C-060A-FF86F6A40045} 27 | 28 | 29 | {739DB09A-CC57-A953-A6CF-F64FA08E4FA7} 30 | 31 | 32 | {2739B19F-16DF-601C-060A-FF86F6A40045} 33 | 34 | 35 | {739DB09A-CC57-A953-A6CF-F64FA08E4FA7} 36 | 37 | 38 | {2739B19F-16DF-601C-060A-FF86F6A40045} 39 | 40 | 41 | {739DB09A-CC57-A953-A6CF-F64FA08E4FA7} 42 | 43 | 44 | {2739B19F-16DF-601C-060A-FF86F6A40045} 45 | 46 | 47 | {7B735499-E5DD-1C2B-6C26-70023832A1CF} 48 | 49 | 50 | {E9F714C1-DA89-54E2-60CF-39FEB20BF756} 51 | 52 | 53 | {7A03FE82-505F-C884-9686-48DDAE2FE7B2} 54 | 55 | 56 | {F852EB63-437C-846A-220F-8D9ED6DAEC1D} 57 | 58 | 59 | {D51E5808-912B-5C70-4BB7-475D1DBFA067} 60 | 61 | 62 | {741E0E76-39B2-B1AB-9FA1-F1A20B16F295} 63 | 64 | 65 | {56DF7A98-063D-FB9D-485C-089023B4C16A} 66 | 67 | 68 | {3F3DA212-C923-AD55-6808-5CDB089DD269} 69 | 70 | 71 | {8CDEE807-BC53-E450-C8B8-4DEBB66742D4} 72 | 73 | 74 | {739DB09A-CC57-A953-A6CF-F64FA08E4FA7} 75 | 76 | 77 | 78 | 79 | ..\lib 80 | 81 | 82 | ..\lib 83 | 84 | 85 | ..\lib 86 | 87 | 88 | ..\lib 89 | 90 | 91 | ..\lib 92 | 93 | 94 | ..\lib 95 | 96 | 97 | ..\lib 98 | 99 | 100 | C:\Users\TemmieHeartz\AppData\Roaming\npm\node_modules\nw-gyp\src 101 | 102 | 103 | .. 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/examples/buffers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This example uses the following structs defined in C++: 3 | struct vector { 4 | float x, y, z; 5 | }; 6 | 7 | struct player { 8 | vector position; 9 | double health; 10 | std::string name; 11 | float distance; 12 | bool alive; 13 | }; 14 | */ 15 | 16 | // https://github.com/LordVonAdel/structron 17 | const Struct = require('structron'); 18 | const memoryjs = require('../index'); 19 | 20 | const processObject = memoryjs.openProcess('Testing.exe'); 21 | const structAddress = 0x000000DEADBEEF; 22 | 23 | // -- Step 1: define the structures 24 | 25 | // Custom double consumer/producer (since it's not yet implemented in `structron`) 26 | const double = { 27 | read(buffer, offset) { 28 | return buffer.readDoubleLE(offset); 29 | }, 30 | write(value, context, offset) { 31 | context.buffer.writeDoubleLE(value, offset); 32 | }, 33 | SIZE: 8, 34 | }; 35 | 36 | // Use string consumer/producer provided by the library (custom implementation for `std::string`), 37 | // pass process handle and base address of structure so the library can read/write the string, 38 | // also requires passing the platform architecture to determine the structure of `std::string` 39 | const string = memoryjs.STRUCTRON_TYPE_STRING(processObject.handle, structAddress, '64'); 40 | 41 | // Define vector structure 42 | const Vector = new Struct() 43 | .addMember(Struct.TYPES.FLOAT, 'x') // 4 bytes 44 | .addMember(Struct.TYPES.FLOAT, 'y') // 4 bytes 45 | .addMember(Struct.TYPES.FLOAT, 'z'); // 4 bytes 46 | 47 | // Define player structure 48 | const Player = new Struct() 49 | .addMember(Vector, 'position') // 12 bytes 50 | .addMember(Struct.TYPES.SKIP(4), 'unused') // compiler padding to put member on 8 byte boundary 51 | .addMember(double, 'health') // 8 bytes 52 | .addMember(string, 'name') // 32 bytes (in 64bit process, 24 bytes in 32bit process) 53 | .addMember(Struct.TYPES.FLOAT, 'distance') // 4 bytes 54 | .addMember(Struct.TYPES.BYTE, 'alive'); // 1 byte 55 | 56 | // -- Step 2: create object to write to memory 57 | const object = { 58 | position: { 59 | x: 1.23, y: 4.56, z: 7.89, 60 | }, 61 | health: 80.12, 62 | name: 'Example Name 1234567890', 63 | distance: 4.20, 64 | alive: false, 65 | }; 66 | 67 | // -- Step 3: create buffer from object and write to memory 68 | let context = Player.write(object); 69 | memoryjs.writeBuffer(processObject.handle, structAddress, context.buffer); 70 | 71 | // -- Step 4: read buffer from memory and parse 72 | const buffer = memoryjs.readBuffer(processObject.handle, structAddress, context.buffer.length); 73 | 74 | context = Player.readContext(buffer); 75 | 76 | if (!context.hasErrors()) { 77 | console.log(context.data); 78 | } 79 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/examples/debugging.js: -------------------------------------------------------------------------------- 1 | const memoryjs = require('../index'); 2 | const processName = 'Testing Things.exe'; 3 | 4 | const processObject = memoryjs.openProcess(processName); 5 | const processId = processObject.th32ProcessID; 6 | 7 | // Address of variable 8 | const address = 0xEFFBF0; 9 | 10 | // When should we breakpoint? On read, write or execute 11 | const trigger = memoryjs.TRIGGER_ACCESS; 12 | const dataType = memoryjs.INT; 13 | 14 | // Whether to end the process once debugging has finished 15 | const killOnDetatch = false; 16 | 17 | /** 18 | * Example 1: Using the `Debugger` wrapper class. 19 | * The library contanis a wrapper class for hardware debugging. 20 | * It works by simply registering a hardware breakpoint and 21 | * then listening for all debug events that are emitted when 22 | * a breakpoint occurs. 23 | */ 24 | 25 | const hardwareDebugger = memoryjs.Debugger; 26 | 27 | // Attach the debugger to the process 28 | hardwareDebugger.attach(processId, killOnDetatch); 29 | 30 | const registerUsed = hardwareDebugger.setHardwareBreakpoint(processId, address, trigger, dataType); 31 | 32 | // `debugEvent` event emission catches debug events from all registers 33 | hardwareDebugger.on('debugEvent', ({ register, event }) => { 34 | console.log(`Hardware Register ${register} breakpoint`); 35 | console.log(event); 36 | }); 37 | 38 | // You can listen to debug events from specific hardware registers 39 | // by listening to whatever register was returned from `setHardwareBreakpoint` 40 | hardwareDebugger.on(registerUsed, (event) => { 41 | console.log(event); 42 | }); 43 | 44 | // Don't forget to call `hardwareDebugger.detatch()` when you're done! 45 | 46 | /** 47 | * Example 2: Manually using the exposed functions 48 | * There are a few steps involved when not using the wrapper: 49 | * 50 | * 1. Attatch the debugger 51 | * 2. Set your hardware breakpoints by manually referencing 52 | * which register you want to set. It's important you keep 53 | * track of which hardware registers you use as there are only 4 54 | * meaning only 4 breakpoints can be set. 55 | * You also need to manually reference the size of the data type. 56 | * 3. Constantly call `awaitDebugEvent` to wait for debug events 57 | * 4. When a debug event occurs, call `handleDebugEvent` 58 | * 5. Don't forget to detatch the debugger via `memoryjs.detatch(processId)` 59 | */ 60 | 61 | memoryjs.attachDebugger(processId, killOnDetatch); 62 | 63 | // There are 4 hardware registers: 64 | // `memoryjs.DR0` through `memoryjs.DR3` 65 | const registerToUse = memoryjs.DR0; 66 | 67 | // Our `address` references an integer variable. An integer 68 | // is 4 bytes therefore we pass `4` to the `size` parameter. 69 | const size = 4; 70 | memoryjs.setHardwareBreakpoint(processId, address, registerToUse, trigger, size); 71 | 72 | // How long to wait for the debug event before timing out 73 | const timeout = 100; 74 | 75 | // The interval duration must be the same or larger than the `timeout` value. 76 | // `awaitDebugEvent` works by waiting a certain amount of time before timing out, 77 | // therefore we only want to call the method again when we're sure the previous 78 | // call has already timed out. 79 | setInterval(() => { 80 | // `debugEvent` can be null if no event occurred 81 | const debugEvent = memoryjs.awaitDebugEvent(registerToUse, timeout); 82 | 83 | // If a breakpoint occurred, handle it 84 | if (debugEvent) { 85 | memoryjs.handleDebugEvent(debugEvent.processId, debugEvent.threadId); 86 | } 87 | }, timeout); 88 | 89 | // Don't forget to detatch the debugger! 90 | // memoryjs.detatchDebugger(processId); 91 | 92 | memoryjs.closeProcess(processObject.handle); 93 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/examples/general.js: -------------------------------------------------------------------------------- 1 | const memoryjs = require('./index'); 2 | const processName = 'csgo.exe'; 3 | let clientModule; 4 | const offset = 0x00A9D44C; 5 | 6 | // open a process (sync) 7 | const processObject = memoryjs.openProcess(processName); 8 | 9 | // open a process (async) 10 | memoryjs.openProcess(processName, (error, processObject) => { 11 | console.log(JSON.stringify(processObject, null, 3)); 12 | 13 | if (process.szExeFile) { 14 | console.log('Successfully opened handle on', processName); 15 | 16 | memoryjs.closeProcess(processObject.handle); 17 | console.log('Closed handle on', processName); 18 | } else { 19 | console.log('Unable to open handle on', processName); 20 | } 21 | }); 22 | 23 | // get all processes (sync) 24 | const processes = memoryjs.getProcesses(); 25 | console.log('\nGetting all processes (sync)\n---\n'); 26 | processes.forEach(({ szExeFile }) => console.log(szExeFile)); 27 | 28 | // get all processes (async) 29 | console.log('\nGetting all processes (async)\n---\n'); 30 | memoryjs.getProcesses((error, processes) => { 31 | processes.forEach(({ szExeFile }) => console.log(szExeFile)); 32 | }); 33 | 34 | /* process = 35 | { cntThreads: 47, 36 | szExeFile: "csgo.exe", 37 | th32ProcessID: 10316, 38 | th32ParentProcessID: 7804, 39 | pcPriClassBase: 8 } */ 40 | 41 | // get all modules (sync) 42 | console.log('\nGetting all modules (sync)\n---\n'); 43 | const modules = memoryjs.getModules(processObject.th32ProcessID); 44 | modules.forEach(({ szExeFile }) => console.log(szExeFile)); 45 | 46 | // get all modules (async) 47 | console.log('\nGetting all modules (async)\n---\n'); 48 | memoryjs.getModules(processObject.th32ProcessID, (error, modules) => { 49 | modules.forEach(({ szExeFile }) => console.log(szExeFile)); 50 | }); 51 | 52 | // find a module associated with a process (sync) 53 | console.log('\nFinding module "client.dll" (sync)\n---\n'); 54 | console.log(memoryjs.findModule('client.dll', processObject.th32ProcessID)); 55 | 56 | // find a module associated with a process (async) 57 | console.log('\nFinding module "client.dll" (async)\n---\n'); 58 | memoryjs.findModule('client.dll', processObject.th32ProcessID, (error, module) => { 59 | console.log(module.szModule); 60 | clientModule = module; 61 | }); 62 | 63 | /* module = 64 | { modBaseAddr: 468123648, 65 | modBaseSize: 80302080, 66 | szExePath: 'c:\\program files (x86)\\steam\\steamapps\\common\\counter-strike global offensive\\csgo\\bin\\client.dll', 67 | szModule: 'client.dll', 68 | th32ProcessID: 10316 } */ 69 | 70 | const address = clientModule.modBaseAddr + offset; 71 | 72 | // read memory (sync) 73 | console.log(`value of 0x${address.toString(16)}: ${memoryjs.readMemory(processObject.handle, address, memoryjs.INT)}`); 74 | 75 | // read memory (async) 76 | memoryjs.readMemory(processObject.handle, address, memoryjs.INT, (error, result) => { 77 | console.log(`value of 0x${address.toString(16)}: ${result}`); 78 | }); 79 | 80 | // write memory 81 | memoryjs.writeMemory(processObject.handle, address, 1, memoryjs.INT); 82 | 83 | // pattern reading 84 | const signature = 'A3 ? ? ? ? C7 05 ? ? ? ? ? ? ? ? E8 ? ? ? ? 59 C3 6A'; 85 | const signatureTypes = memoryjs.READ | memoryjs.SUBTRACT; 86 | const patternOffset = 0x1; 87 | const addressOffset = 0x10; 88 | const dwLocalPlayer = memoryjs.findPattern(processObject.handle, clientModule.szModule, signature, signatureTypes, patternOffset, addressOffset); 89 | console.log(`value of dwLocalPlayer: 0x${dwLocalPlayer.toString(16)}`); 90 | 91 | // inject dll 92 | memoryjs.injectDll(processObject.handle, 'C:\\TestDLL.dll', (error, success) => { 93 | console.log(`injected TestDLL.dll into process: ${success} ${error}`); 94 | }); 95 | 96 | // unload dll (by name) 97 | memoryjs.unloadDll(processObject.handle, 'TestDLL.dll', (error, success) => { 98 | console.log(`unloaded TestDLL.dll from process: ${success} ${error}`); 99 | }); 100 | 101 | // unload dll (by module base address) 102 | // const testDll = memoryjs.findModule('TestDLL.dll', processObject.th32ProcessID); 103 | // const success = memoryjs.unloadDll(processObject.handle, testDll.modBaseAddr); 104 | // console.log(`unloaded TestDLL.dll from process: ${success}`); 105 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/examples/vectors.js: -------------------------------------------------------------------------------- 1 | const memoryjs = require('./index'); 2 | const processName = 'Test Project.exe'; 3 | 4 | const processObject = memoryjs.openProcess(processName); 5 | console.log(JSON.stringify(processObject, null, 3)); 6 | 7 | const modules = memoryjs.getModules(processObject.th32ProcessID); 8 | modules.forEach(module => console.log(module)); 9 | 10 | /* How to read a "string" 11 | * C++ code: 12 | * ``` 13 | * std::string str = "this is a sample string"; 14 | * std::cout << "Address: " << (DWORD) str.c_str() << ", value: " << str.c_str() << std::endl; 15 | * ``` 16 | * This will create a string and the address of the string and the value of the string. 17 | */ 18 | 19 | const str = memoryjs.readMemory(0x69085bc0, memoryjs.STRING); 20 | console.log(str); // "this is a sample string"; 21 | 22 | /* How to read and write vectors 23 | * C++ code: 24 | * ``` 25 | * struct Vector3 { float x, y, z; }; 26 | * struct Vector4 { float w, x, y, z; }; 27 | * Vector3 vec3 = { 1.0f, 2.0f, 3.0f }; 28 | * Vector4 vec4 = { 1.0f, 2.0f, 3.0f, 4.0f }; 29 | * std::cout << "[Vec3] Address: " << &vec3 << std::endl; 30 | * std::cout << "[Vec4] Address: " << &vec4 << std::endl; 31 | */ 32 | 33 | let vec3 = { 34 | x: 0, y: 0, z: 0, 35 | }; 36 | memoryjs.writeMemory(0x000001, vec3, memoryjs.VEC3); 37 | vec3 = memoryjs.readMemory(0x000001, memoryjs.VEC3); // { x, y, z } 38 | console.log(vec3); 39 | 40 | let vec4 = { 41 | w: 0, x: 0, y: 0, z: 0, 42 | }; 43 | memoryjs.writeMemory(0x000002, vec4, memoryjs.VEC4); 44 | vec4 = memoryjs.readMemory(0x000002, memoryjs.VEC4); // { w, x, y, z } 45 | console.log(vec4); 46 | 47 | memoryjs.closeProcess(processObject.handle); 48 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const memoryjs = require('./build/Release/memoryjs'); 3 | const Debugger = require('./src/debugger'); 4 | const constants = require('./src/consts'); 5 | const { STRUCTRON_TYPE_STRING } = require('./src/utils'); 6 | 7 | function openProcess(processIdentifier, callback) { 8 | if (arguments.length === 1) { 9 | return memoryjs.openProcess(processIdentifier); 10 | } 11 | 12 | return memoryjs.openProcess(processIdentifier, callback); 13 | } 14 | 15 | function closeProcess(handle) { 16 | return memoryjs.closeProcess(handle); 17 | } 18 | 19 | function getProcesses(callback) { 20 | if (arguments.length === 0) { 21 | return memoryjs.getProcesses(); 22 | } 23 | 24 | return memoryjs.getProcesses(callback); 25 | } 26 | 27 | function findModule(moduleName, processId, callback) { 28 | if (arguments.length === 2) { 29 | return memoryjs.findModule(moduleName, processId); 30 | } 31 | 32 | return memoryjs.findModule(moduleName, processId, callback); 33 | } 34 | 35 | function getModules(processId, callback) { 36 | if (arguments.length === 1) { 37 | return memoryjs.getModules(processId); 38 | } 39 | 40 | return memoryjs.getModules(processId, callback); 41 | } 42 | 43 | function readMemory(handle, address, dataType, callback) { 44 | if (dataType.toLowerCase().endsWith('_be')) { 45 | return readMemoryBE(handle, address, dataType, callback); 46 | } 47 | 48 | if (arguments.length === 3) { 49 | return memoryjs.readMemory(handle, address, dataType.toLowerCase()); 50 | } 51 | 52 | return memoryjs.readMemory(handle, address, dataType.toLowerCase(), callback); 53 | } 54 | 55 | function readMemoryBE(handle, address, dataType, callback) { 56 | let value = null; 57 | 58 | switch (dataType) { 59 | case constants.INT64_BE: 60 | value = readBuffer(handle, address, 8).readBigInt64BE(); 61 | break; 62 | 63 | case constants.UINT64_BE: 64 | value = readBuffer(handle, address, 8).readBigUInt64BE(); 65 | break; 66 | 67 | case constants.INT32_BE: 68 | case constants.INT_BE: 69 | case constants.LONG_BE: 70 | value = readBuffer(handle, address, 4).readInt32BE(); 71 | break; 72 | 73 | case constants.UINT32_BE: 74 | case constants.UINT_BE: 75 | case constants.ULONG_BE: 76 | value = readBuffer(handle, address, 4).readUInt32BE(); 77 | break; 78 | 79 | case constants.INT16_BE: 80 | case constants.SHORT_BE: 81 | value = readBuffer(handle, address, 2).readInt16BE(); 82 | break; 83 | 84 | case constants.UINT16_BE: 85 | case constants.USHORT_BE: 86 | value = readBuffer(handle, address, 2).readUInt16BE(); 87 | break; 88 | 89 | case constants.FLOAT_BE: 90 | value = readBuffer(handle, address, 4).readFloatBE(); 91 | break; 92 | 93 | case constants.DOUBLE_BE: 94 | value = readBuffer(handle, address, 8).readDoubleBE(); 95 | break; 96 | } 97 | 98 | if (typeof callback !== 'function') { 99 | if (value === null) { 100 | throw new Error('Invalid data type argument!'); 101 | } 102 | 103 | return value; 104 | } 105 | 106 | callback(value === null ? 'Invalid data type argument!' : '', value); 107 | } 108 | 109 | function readBuffer(handle, address, size, callback) { 110 | if (arguments.length === 3) { 111 | return memoryjs.readBuffer(handle, address, size); 112 | } 113 | 114 | return memoryjs.readBuffer(handle, address, size, callback); 115 | } 116 | 117 | function writeMemory(handle, address, value, dataType) { 118 | let dataValue = value; 119 | if (dataType === constants.STR || dataType === constants.STRING) { 120 | dataValue += '\0'; // add terminator 121 | } 122 | 123 | const bigintTypes = [constants.INT64, constants.INT64_BE, constants.UINT64, constants.UINT64_BE]; 124 | if (bigintTypes.indexOf(dataType) != -1 && typeof value !== 'bigint') { 125 | throw new Error(`${dataType.toUpperCase()} expects type BigInt`); 126 | } 127 | 128 | if (dataType.endsWith('_be')) { 129 | return writeMemoryBE(handle, address, dataValue, dataType); 130 | } 131 | 132 | return memoryjs.writeMemory(handle, address, dataValue, dataType.toLowerCase()); 133 | } 134 | 135 | function writeMemoryBE(handle, address, value, dataType) { 136 | let buffer = null; 137 | 138 | switch (dataType) { 139 | case constants.INT64_BE: 140 | if (typeof value !== 'bigint') { 141 | throw new Error('INT64_BE expects type BigInt'); 142 | } 143 | buffer = Buffer.alloc(8); 144 | buffer.writeBigInt64BE(value); 145 | break; 146 | 147 | case constants.UINT64_BE: 148 | if (typeof value !== 'bigint') { 149 | throw new Error('UINT64_BE expects type BigInt'); 150 | } 151 | buffer = Buffer.alloc(8); 152 | buffer.writeBigUInt64BE(value); 153 | break; 154 | 155 | case constants.INT32_BE: 156 | case constants.INT_BE: 157 | case constants.LONG_BE: 158 | buffer = Buffer.alloc(4); 159 | buffer.writeInt32BE(value); 160 | break; 161 | 162 | case constants.UINT32_BE: 163 | case constants.UINT_BE: 164 | case constants.ULONG_BE: 165 | buffer = Buffer.alloc(4); 166 | buffer.writeUInt32BE(value); 167 | break; 168 | 169 | case constants.INT16_BE: 170 | case constants.SHORT_BE: 171 | buffer = Buffer.alloc(2); 172 | buffer.writeInt16BE(value); 173 | break; 174 | 175 | case constants.UINT16_BE: 176 | case constants.USHORT_BE: 177 | buffer = Buffer.alloc(2); 178 | buffer.writeUInt16BE(value); 179 | break; 180 | 181 | case constants.FLOAT_BE: 182 | buffer = Buffer.alloc(4); 183 | buffer.writeFloatBE(value); 184 | break; 185 | 186 | case constants.DOUBLE_BE: 187 | buffer = Buffer.alloc(8); 188 | buffer.writeDoubleBE(value); 189 | break; 190 | } 191 | 192 | if (buffer == null) { 193 | throw new Error('Invalid data type argument!'); 194 | } 195 | 196 | writeBuffer(handle, address, buffer); 197 | } 198 | 199 | function writeBuffer(handle, address, buffer) { 200 | return memoryjs.writeBuffer(handle, address, buffer); 201 | } 202 | 203 | function findPattern() { 204 | const findPattern = ['number', 'string', 'number', 'number'].toString(); 205 | const findPatternByModule = ['number', 'string', 'string', 'number', 'number'].toString(); 206 | const findPatternByAddress = ['number', 'number', 'string', 'number', 'number'].toString(); 207 | 208 | const args = Array.from(arguments).map(arg => typeof arg); 209 | 210 | if (args.slice(0, 4).toString() === findPattern) { 211 | if (args.length === 4 || (args.length === 5 && args[4] === 'function')) { 212 | return memoryjs.findPattern(...arguments); 213 | } 214 | } 215 | 216 | if (args.slice(0, 5).toString() === findPatternByModule) { 217 | if (args.length === 5 || (args.length === 6 && args[5] === 'function')) { 218 | return memoryjs.findPatternByModule(...arguments); 219 | } 220 | } 221 | 222 | if (args.slice(0, 5).toString() === findPatternByAddress) { 223 | if (args.length === 5 || (args.length === 6 && args[5] === 'function')) { 224 | return memoryjs.findPatternByAddress(...arguments); 225 | } 226 | } 227 | 228 | throw new Error('invalid arguments!'); 229 | } 230 | 231 | function callFunction(handle, args, returnType, address, callback) { 232 | if (arguments.length === 4) { 233 | return memoryjs.callFunction(handle, args, returnType, address); 234 | } 235 | 236 | return memoryjs.callFunction(handle, args, returnType, address, callback); 237 | } 238 | 239 | function virtualAllocEx(handle, address, size, allocationType, protection, callback) { 240 | if (arguments.length === 5) { 241 | return memoryjs.virtualAllocEx(handle, address, size, allocationType, protection); 242 | } 243 | 244 | return memoryjs.virtualAllocEx(handle, address, size, allocationType, protection, callback); 245 | } 246 | 247 | function virtualProtectEx(handle, address, size, protection, callback) { 248 | if (arguments.length === 4) { 249 | return memoryjs.virtualProtectEx(handle, address, size, protection); 250 | } 251 | 252 | return memoryjs.virtualProtectEx(handle, address, size, protection, callback); 253 | } 254 | 255 | function getRegions(handle, getOffsets, callback) { 256 | if (arguments.length === 1) { 257 | return memoryjs.getRegions(handle); 258 | } 259 | 260 | return memoryjs.getRegions(handle, callback); 261 | } 262 | 263 | function virtualQueryEx(handle, address, callback) { 264 | if (arguments.length === 2) { 265 | return memoryjs.virtualQueryEx(handle, address); 266 | } 267 | 268 | return memoryjs.virtualQueryEx(handle, address, callback); 269 | } 270 | 271 | function injectDll(handle, dllPath, callback) { 272 | if (!dllPath.endsWith('.dll')) { 273 | throw new Error("Given path is invalid: file is not of type 'dll'."); 274 | } 275 | 276 | if (!fs.existsSync(dllPath)) { 277 | throw new Error('Given path is invaild: file does not exist.'); 278 | } 279 | 280 | if (arguments.length === 2) { 281 | return memoryjs.injectDll(handle, dllPath); 282 | } 283 | 284 | return memoryjs.injectDll(handle, dllPath, callback); 285 | } 286 | 287 | function unloadDll(handle, module, callback) { 288 | if (arguments.length === 2) { 289 | return memoryjs.unloadDll(handle, module); 290 | } 291 | 292 | return memoryjs.unloadDll(handle, module, callback); 293 | } 294 | 295 | const library = { 296 | openProcess, 297 | closeProcess, 298 | getProcesses, 299 | findModule, 300 | getModules, 301 | readMemory, 302 | readBuffer, 303 | writeMemory, 304 | writeBuffer, 305 | findPattern, 306 | callFunction, 307 | virtualAllocEx, 308 | virtualProtectEx, 309 | getRegions, 310 | virtualQueryEx, 311 | injectDll, 312 | unloadDll, 313 | attachDebugger: memoryjs.attachDebugger, 314 | detatchDebugger: memoryjs.detatchDebugger, 315 | awaitDebugEvent: memoryjs.awaitDebugEvent, 316 | handleDebugEvent: memoryjs.handleDebugEvent, 317 | setHardwareBreakpoint: memoryjs.setHardwareBreakpoint, 318 | removeHardwareBreakpoint: memoryjs.removeHardwareBreakpoint, 319 | Debugger: new Debugger(memoryjs), 320 | }; 321 | 322 | module.exports = { 323 | ...constants, 324 | ...library, 325 | STRUCTRON_TYPE_STRING: STRUCTRON_TYPE_STRING(library), 326 | }; 327 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/lib/debugger.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Hardware debugger for memory.js 3 | * A lot of the hardware debugging code is based on ReClass.NET 4 | * https://github.com/ReClassNET/ReClass.NET 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "debugger.h" 12 | #include "module.h" 13 | 14 | bool debugger::attach(DWORD processId, bool killOnDetatch) { 15 | if (DebugActiveProcess(processId) == 0) { 16 | return false; 17 | } 18 | 19 | DebugSetProcessKillOnExit(killOnDetatch); 20 | return true; 21 | } 22 | 23 | bool debugger::detatch(DWORD processId) { 24 | return DebugActiveProcessStop(processId) != 0; 25 | } 26 | 27 | bool debugger::setHardwareBreakpoint(DWORD processId, DWORD64 address, Register reg, int trigger, int size) { 28 | char* errorMessage = ""; 29 | std::vector threads = module::getThreads(0, &errorMessage); 30 | 31 | if (strcmp(errorMessage, "")) { 32 | return false; 33 | } 34 | 35 | for (std::vector::size_type i = 0; i != threads.size(); i++) { 36 | if (threads[i].th32OwnerProcessID != processId) { 37 | continue; 38 | } 39 | 40 | HANDLE threadHandle = OpenThread(THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT, false, threads[i].th32ThreadID); 41 | 42 | if (threadHandle == 0) { 43 | continue; 44 | } 45 | 46 | SuspendThread(threadHandle); 47 | 48 | CONTEXT context = { 0 }; 49 | context.ContextFlags = CONTEXT_DEBUG_REGISTERS; 50 | GetThreadContext(threadHandle, &context); 51 | 52 | DebugRegister7 dr7; 53 | dr7.Value = context.Dr7; 54 | 55 | if (reg == Register::DR0) { 56 | context.Dr0 = address; 57 | dr7.G0 = true; 58 | dr7.RW0 = trigger; 59 | dr7.Len0 = size; 60 | } 61 | 62 | if (reg == Register::DR1) { 63 | context.Dr1 = address; 64 | dr7.G1 = true; 65 | dr7.RW1 = trigger; 66 | dr7.Len1 = size; 67 | } 68 | 69 | if (reg == Register::DR2) { 70 | context.Dr2 = address; 71 | dr7.G2 = true; 72 | dr7.RW2 = trigger; 73 | dr7.Len2 = size; 74 | } 75 | 76 | if (reg == Register::DR3) { 77 | context.Dr3 = address; 78 | dr7.G3 = true; 79 | dr7.RW3 = trigger; 80 | dr7.Len3 = size; 81 | } 82 | 83 | context.Dr7 = dr7.Value; 84 | 85 | SetThreadContext(threadHandle, &context); 86 | ResumeThread(threadHandle); 87 | CloseHandle(threadHandle); 88 | 89 | return true; 90 | } 91 | 92 | return false; 93 | } 94 | 95 | bool debugger::awaitDebugEvent(DWORD millisTimeout, DebugEvent *info) { 96 | DEBUG_EVENT debugEvent = {}; 97 | 98 | if (WaitForDebugEvent(&debugEvent, millisTimeout) == 0) { 99 | return false; 100 | } 101 | 102 | if (debugEvent.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) { 103 | CloseHandle(debugEvent.u.CreateProcessInfo.hFile); 104 | } 105 | 106 | if (debugEvent.dwDebugEventCode == LOAD_DLL_DEBUG_EVENT) { 107 | CloseHandle(debugEvent.u.LoadDll.hFile); 108 | } 109 | 110 | if (debugEvent.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) { 111 | EXCEPTION_DEBUG_INFO exception = debugEvent.u.Exception; 112 | 113 | info->processId = debugEvent.dwProcessId; 114 | info->threadId = debugEvent.dwThreadId; 115 | info->exceptionAddress = exception.ExceptionRecord.ExceptionAddress; 116 | info->exceptionCode = exception.ExceptionRecord.ExceptionCode; 117 | info->exceptionFlags = exception.ExceptionRecord.ExceptionFlags; 118 | 119 | HANDLE handle = OpenThread(THREAD_GET_CONTEXT, false, debugEvent.dwThreadId); 120 | 121 | if (handle == 0) { 122 | return false; 123 | } 124 | 125 | CONTEXT context = {}; 126 | context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_DEBUG_REGISTERS; 127 | GetThreadContext(handle, &context); 128 | 129 | DebugRegister6 dr6; 130 | dr6.Value = context.Dr6; 131 | 132 | if (dr6.DR0) { 133 | info->hardwareRegister = Register::DR0; 134 | } 135 | 136 | if (dr6.DR1) { 137 | info->hardwareRegister = Register::DR1; 138 | } 139 | 140 | if (dr6.DR2) { 141 | info->hardwareRegister = Register::DR2; 142 | } 143 | 144 | if (dr6.DR3) { 145 | info->hardwareRegister = Register::DR3; 146 | } 147 | 148 | if (!dr6.DR0 && !dr6.DR1 && !dr6.DR2 && !dr6.DR3) { 149 | info->hardwareRegister = Register::Invalid; 150 | } 151 | 152 | CloseHandle(handle); 153 | } else { 154 | ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_CONTINUE); 155 | } 156 | 157 | return true; 158 | } 159 | 160 | bool debugger::handleDebugEvent(DWORD processId, DWORD threadId) { 161 | return ContinueDebugEvent(processId, threadId, DBG_CONTINUE); 162 | // if (status == DebugContinueStatus::Handled) { 163 | // return ContinueDebugEvent(processId, threadId, DBG_CONTINUE) != 0; 164 | // } 165 | 166 | // if (status == DebugContinueStatus::NotHandled) { 167 | // return ContinueDebugEvent(processId, threadId, DBG_EXCEPTION_NOT_HANDLED) != 0; 168 | // } 169 | } -------------------------------------------------------------------------------- /App/node_modules/memoryjs/lib/debugger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef debugger_H 3 | #define debugger_H 4 | #define WIN32_LEAN_AND_MEAN 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | enum class Register { 11 | Invalid = -0x1, 12 | DR0 = 0x0, 13 | DR1 = 0x1, 14 | DR2 = 0x2, 15 | DR3 = 0x3 16 | }; 17 | 18 | struct DebugRegister6 { 19 | union { 20 | uintptr_t Value; 21 | struct { 22 | unsigned DR0 : 1; 23 | unsigned DR1 : 1; 24 | unsigned DR2 : 1; 25 | unsigned DR3 : 1; 26 | unsigned Reserved : 9; 27 | unsigned BD : 1; 28 | unsigned BS : 1; 29 | unsigned BT : 1; 30 | }; 31 | }; 32 | }; 33 | 34 | struct DebugRegister7 { 35 | union { 36 | uintptr_t Value; 37 | struct { 38 | unsigned G0 : 1; 39 | unsigned L0 : 1; 40 | unsigned G1 : 1; 41 | unsigned L1 : 1; 42 | unsigned G2 : 1; 43 | unsigned L2 : 1; 44 | unsigned G3 : 1; 45 | unsigned L3 : 1; 46 | unsigned GE : 1; 47 | unsigned LE : 1; 48 | unsigned Reserved : 6; 49 | unsigned RW0 : 2; 50 | unsigned Len0 : 2; 51 | unsigned RW1 : 2; 52 | unsigned Len1 : 2; 53 | unsigned RW2 : 2; 54 | unsigned Len2 : 2; 55 | unsigned RW3 : 2; 56 | unsigned Len3 : 2; 57 | }; 58 | }; 59 | }; 60 | 61 | struct DebugEvent { 62 | DWORD processId; 63 | DWORD threadId; 64 | DWORD exceptionCode; 65 | DWORD exceptionFlags; 66 | void* exceptionAddress; 67 | Register hardwareRegister; 68 | }; 69 | 70 | namespace debugger { 71 | bool attach(DWORD processId, bool killOnDetatch); 72 | bool detatch(DWORD processId); 73 | bool setHardwareBreakpoint(DWORD processId, DWORD64 address, Register reg, int trigger, int size); 74 | bool awaitDebugEvent(DWORD millisTimeout, DebugEvent *info); 75 | bool handleDebugEvent(DWORD processId, DWORD threadId); 76 | } 77 | 78 | #endif 79 | #pragma once 80 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/lib/dll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef DLL_H 3 | #define DLL_H 4 | #define WIN32_LEAN_AND_MEAN 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace dll { 12 | bool inject(HANDLE handle, std::string dllPath, char** errorMessage, LPDWORD moduleHandle) { 13 | // allocate space in target process memory for DLL path 14 | LPVOID targetProcessPath = VirtualAllocEx(handle, NULL, dllPath.length() + 1, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 15 | 16 | if (targetProcessPath == NULL) { 17 | *errorMessage = "unable to allocate memory in target process"; 18 | return false; 19 | } 20 | 21 | // write DLL path to reserved memory space 22 | if (WriteProcessMemory(handle, targetProcessPath, dllPath.c_str(), dllPath.length() + 1, 0) == 0) { 23 | *errorMessage = "unable to to write dll path to target process"; 24 | VirtualFreeEx(handle, targetProcessPath, 0, MEM_RELEASE); 25 | return false; 26 | } 27 | 28 | HMODULE kernel32 = LoadLibrary("kernel32"); 29 | 30 | if (kernel32 == 0) { 31 | VirtualFreeEx(handle, targetProcessPath, 0, MEM_RELEASE); 32 | *errorMessage = "unable to load kernel32"; 33 | return false; 34 | } 35 | 36 | // call LoadLibrary from target process 37 | LPTHREAD_START_ROUTINE loadLibraryAddress = (LPTHREAD_START_ROUTINE) GetProcAddress(kernel32, "LoadLibraryA"); 38 | HANDLE thread = CreateRemoteThread(handle, NULL, NULL, loadLibraryAddress, targetProcessPath, NULL, NULL); 39 | 40 | if (thread == NULL) { 41 | *errorMessage = "unable to call LoadLibrary from target process"; 42 | VirtualFreeEx(handle, targetProcessPath, 0, MEM_RELEASE); 43 | return false; 44 | } 45 | 46 | WaitForSingleObject(thread, INFINITE); 47 | GetExitCodeThread(thread, moduleHandle); 48 | 49 | // free memory reserved in target process 50 | VirtualFreeEx(handle, targetProcessPath, 0, MEM_RELEASE); 51 | CloseHandle(thread); 52 | 53 | return *moduleHandle > 0; 54 | } 55 | 56 | bool unload(HANDLE handle, char** errorMessage, HMODULE moduleHandle) { 57 | HMODULE kernel32 = LoadLibrary("kernel32"); 58 | 59 | if (kernel32 == 0) { 60 | *errorMessage = "unable to load kernel32"; 61 | return false; 62 | } 63 | 64 | // call FreeLibrary from target process 65 | LPTHREAD_START_ROUTINE freeLibraryAddress = (LPTHREAD_START_ROUTINE) GetProcAddress(kernel32, "FreeLibrary"); 66 | HANDLE thread = CreateRemoteThread(handle, NULL, NULL, freeLibraryAddress, (void*)moduleHandle, NULL, NULL); 67 | 68 | if (thread == NULL) { 69 | *errorMessage = "unable to call FreeLibrary from target process"; 70 | return false; 71 | } 72 | 73 | WaitForSingleObject(thread, INFINITE); 74 | DWORD exitCode = -1; 75 | GetExitCodeThread(thread, &exitCode); 76 | CloseHandle(thread); 77 | 78 | return exitCode != 0; 79 | } 80 | } 81 | 82 | #endif 83 | #pragma once 84 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/lib/functions.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "functions.h" 6 | 7 | char functions::readChar(HANDLE hProcess, DWORD64 address) { 8 | char value; 9 | ReadProcessMemory(hProcess, (LPVOID)address, &value, sizeof(char), NULL); 10 | return value; 11 | } 12 | 13 | LPVOID functions::reserveString(HANDLE hProcess, const char* value, SIZE_T size) { 14 | LPVOID memoryAddress = VirtualAllocEx(hProcess, NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); 15 | WriteProcessMemory(hProcess, memoryAddress, value, size, NULL); 16 | return memoryAddress; 17 | } 18 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/lib/functions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef FUNCTIONS_H 3 | #define FUNCTIONS_H 4 | #define WIN32_LEAN_AND_MEAN 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | struct Call { 11 | int returnValue; 12 | std::string returnString; 13 | DWORD exitCode; 14 | }; 15 | 16 | namespace functions { 17 | enum class Type { 18 | T_VOID = 0x0, 19 | T_STRING = 0x1, 20 | T_CHAR = 0x2, 21 | T_BOOL = 0x3, 22 | T_INT = 0x4, 23 | T_DOUBLE = 0x5, 24 | T_FLOAT = 0x6 25 | }; 26 | 27 | struct Arg { 28 | Type type; 29 | LPVOID value; 30 | }; 31 | 32 | LPVOID reserveString(HANDLE hProcess, const char* value, SIZE_T size); 33 | char readChar(HANDLE hProcess, DWORD64 address); 34 | 35 | template 36 | Call call(HANDLE pHandle, std::vector args, Type returnType, DWORD64 address, char** errorMessage) { 37 | std::vector argShellcode; 38 | 39 | std::reverse(args.begin(), args.end()); 40 | 41 | for (auto &arg : args) { 42 | // 0x68: PUSH imm16/imm32 43 | // 0x6A: PUSH imm8 44 | 45 | if (arg.type == Type::T_INT || arg.type == Type::T_FLOAT) { 46 | argShellcode.push_back(0x68); 47 | int value = *static_cast(arg.value); 48 | 49 | // Little endian representation 50 | for (int i = 0; i < 4; i++) { 51 | int shifted = (value >> (i * 8)) & 0xFF; 52 | argShellcode.push_back(shifted); 53 | } 54 | 55 | continue; 56 | } 57 | 58 | if (arg.type == Type::T_STRING) { 59 | argShellcode.push_back(0x68); 60 | std::string value = *static_cast(arg.value); 61 | LPVOID address = functions::reserveString(pHandle, value.c_str(), value.length()); 62 | 63 | // Little endian representation 64 | for (int i = 0; i < 4; i++) { 65 | int shifted = ((int)address >> (i * 8)) & 0xFF; 66 | argShellcode.push_back(shifted); 67 | } 68 | 69 | continue; 70 | } 71 | 72 | argShellcode.push_back(0x6A); 73 | unsigned char value = *static_cast(arg.value); 74 | argShellcode.push_back(value); 75 | } 76 | 77 | // 83: ADD r/m16/32 imm8 78 | std::vector callShellcode = { 79 | // call 0x00000000 80 | 0xE8, 0x00, 0x00, 0x00, 0x00, 81 | // add esp, [arg count * 4] 82 | 0x83, 0xC4, (unsigned char)(args.size() * 0x4), 83 | }; 84 | 85 | LPVOID returnValuePointer = 0; 86 | if (returnType != Type::T_VOID) { 87 | // We will reserve memory for where we want to store the result, 88 | // and move the return value to this address. 89 | returnValuePointer = VirtualAllocEx(pHandle, NULL, sizeof(returnDataType), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); 90 | 91 | if (returnType == Type::T_FLOAT) { 92 | // fstp DWORD PTR [0x12345678] 93 | // when `call` is executed, if return type is float, it's stored 94 | // in fpu registers (st0 through st7). we can use the `fst` 95 | // instruction to move st0 to a place in memory 96 | // D9 FSTP m32real ST Store Floating Point Value and Pop 97 | // D9 = for m32 98 | callShellcode.push_back(0xD9); 99 | callShellcode.push_back(0x1C); 100 | callShellcode.push_back(0x25); 101 | } else if (returnType == Type::T_DOUBLE) { 102 | // fstp QWORD PTR [0x12345678] 103 | // DD FSTP m64real ST Store Floating Point Value and Pop 104 | // DD = for m64 105 | callShellcode.push_back(0xDD); 106 | callShellcode.push_back(0x1C); 107 | callShellcode.push_back(0x25); 108 | } else { 109 | // mov [0x1234], eax 110 | // A3: MOVE moffs16/32 eAX 111 | // Call routines places return value inside EAX 112 | callShellcode.push_back(0xA3); 113 | } 114 | 115 | for (int i = 0; i < 4; i++) { 116 | int shifted = ((DWORD)returnValuePointer >> (i * 8)) & 0xFF; 117 | callShellcode.push_back(shifted); 118 | } 119 | } 120 | 121 | // C3: return 122 | callShellcode.push_back(0xC3); 123 | 124 | // concatenate the arg shellcode with the calling shellcode 125 | std::vector shellcode; 126 | shellcode.reserve(argShellcode.size() + callShellcode.size()); 127 | shellcode.insert(shellcode.end(), argShellcode.begin(), argShellcode.end()); 128 | shellcode.insert(shellcode.end(), callShellcode.begin(), callShellcode.end()); 129 | 130 | // 5 = 0xE8 (call) + 4 empty bytes for where the address will go 131 | int addessShellcodeOffset = argShellcode.size() + 5; 132 | 133 | // Allocate space for the shellcode 134 | SIZE_T size = shellcode.size() * sizeof(unsigned char); 135 | LPVOID pShellcode = VirtualAllocEx(pHandle, NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); 136 | 137 | // `call` opcode takes relative address, so calculate the relative address 138 | // taking into account where the shellcode will be written in memory 139 | DWORD64 relative = address - (uintptr_t)pShellcode - addessShellcodeOffset; 140 | 141 | // Write the relative address to the shellcode 142 | for (int i = 0; i < 4; i++) { 143 | int shifted = (relative >> (i * 8)) & 0xFF; 144 | 145 | // argShellcode.size() will offset to the `0xE8` opcode, add 1 to offset that instruction 146 | shellcode.at(argShellcode.size() + 1 + i) = shifted; 147 | } 148 | 149 | // Write the shellcode 150 | WriteProcessMemory(pHandle, pShellcode, shellcode.data(), size, NULL); 151 | 152 | // Execute the shellcode 153 | HANDLE thread = CreateRemoteThread(pHandle, NULL, NULL, (LPTHREAD_START_ROUTINE)pShellcode, NULL, NULL, NULL); 154 | 155 | Call data = { 0, "", (DWORD) -1 }; 156 | 157 | if (thread == NULL) { 158 | *errorMessage = "unable to create remote thread."; 159 | return data; 160 | } 161 | 162 | WaitForSingleObject(thread, INFINITE); 163 | GetExitCodeThread(thread, &data.exitCode); 164 | 165 | if (returnType != Type::T_VOID && returnType != Type::T_STRING) { 166 | ReadProcessMemory(pHandle, (LPVOID)returnValuePointer, &data.returnValue, sizeof(int), NULL); 167 | VirtualFreeEx(pHandle, returnValuePointer, 0, MEM_RELEASE); 168 | } 169 | 170 | if (returnType == Type::T_STRING) { 171 | // String is stored in memory somewhere 172 | // When returning a string, the address of the string is placed in EAX. 173 | // So we read the current returnValuePointer address to get the actual address of the string 174 | ReadProcessMemory(pHandle, (LPVOID)returnValuePointer, &returnValuePointer, sizeof(int), NULL); 175 | 176 | std::vector chars; 177 | int offset = 0x0; 178 | while (true) { 179 | char c = functions::readChar(pHandle, (DWORD64)returnValuePointer + offset); 180 | chars.push_back(c); 181 | 182 | // break at 1 million chars 183 | if (offset == (sizeof(char) * 1000000)) { 184 | chars.clear(); 185 | break; 186 | } 187 | 188 | // break at terminator (end of string) 189 | if (c == '\0') { 190 | break; 191 | } 192 | 193 | // go to next char 194 | offset += sizeof(char); 195 | } 196 | 197 | std::string str(chars.begin(), chars.end()); 198 | // TODO: pass str as LPVOID and cast back to string 199 | data.returnString = str; 200 | } 201 | 202 | VirtualFreeEx(pHandle, pShellcode, 0, MEM_RELEASE); 203 | 204 | return data; 205 | } 206 | } 207 | 208 | #endif 209 | #pragma once 210 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/lib/memory.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "memory.h" 6 | 7 | memory::memory() {} 8 | memory::~memory() {} 9 | 10 | std::vector memory::getRegions(HANDLE hProcess) { 11 | std::vector regions; 12 | 13 | MEMORY_BASIC_INFORMATION region; 14 | DWORD64 address; 15 | 16 | for (address = 0; VirtualQueryEx(hProcess, (LPVOID)address, ®ion, sizeof(region)) == sizeof(region); address += region.RegionSize) { 17 | regions.push_back(region); 18 | } 19 | 20 | return regions; 21 | } -------------------------------------------------------------------------------- /App/node_modules/memoryjs/lib/memory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef MEMORY_H 3 | #define MEMORY_H 4 | #define WIN32_LEAN_AND_MEAN 5 | 6 | #include 7 | #include 8 | 9 | class memory { 10 | public: 11 | memory(); 12 | ~memory(); 13 | std::vector getRegions(HANDLE hProcess); 14 | 15 | template 16 | dataType readMemory(HANDLE hProcess, DWORD64 address) { 17 | dataType cRead; 18 | ReadProcessMemory(hProcess, (LPVOID)address, &cRead, sizeof(dataType), NULL); 19 | return cRead; 20 | } 21 | 22 | BOOL readBuffer(HANDLE hProcess, DWORD64 address, SIZE_T size, char* dstBuffer) { 23 | return ReadProcessMemory(hProcess, (LPVOID)address, dstBuffer, size, NULL); 24 | } 25 | 26 | char readChar(HANDLE hProcess, DWORD64 address) { 27 | char value; 28 | ReadProcessMemory(hProcess, (LPVOID)address, &value, sizeof(char), NULL); 29 | return value; 30 | } 31 | 32 | BOOL readString(HANDLE hProcess, DWORD64 address, std::string* pString) { 33 | int length = 0; 34 | int BATCH_SIZE = 256; 35 | char* data = (char*) malloc(sizeof(char) * BATCH_SIZE); 36 | while (length <= BATCH_SIZE * 4096) { 37 | BOOL success = readBuffer(hProcess, address + length, BATCH_SIZE, data); 38 | 39 | if (success == 0) { 40 | free(data); 41 | break; 42 | } 43 | 44 | for (const char* ptr = data; ptr - data < BATCH_SIZE; ++ptr) { 45 | if (*ptr == '\0') { 46 | length += ptr - data + 1; 47 | 48 | char* buffer = (char*) malloc(length); 49 | readBuffer(hProcess, address, length, buffer); 50 | 51 | *pString = std::string(buffer); 52 | 53 | free(data); 54 | free(buffer); 55 | 56 | return TRUE; 57 | } 58 | } 59 | 60 | length += BATCH_SIZE; 61 | } 62 | 63 | return FALSE; 64 | } 65 | 66 | template 67 | void writeMemory(HANDLE hProcess, DWORD64 address, dataType value) { 68 | WriteProcessMemory(hProcess, (LPVOID)address, &value, sizeof(dataType), NULL); 69 | } 70 | 71 | template 72 | void writeMemory(HANDLE hProcess, DWORD64 address, dataType value, SIZE_T size) { 73 | LPVOID buffer = value; 74 | 75 | if (typeid(dataType) != typeid(char*)) { 76 | buffer = &value; 77 | } 78 | 79 | WriteProcessMemory(hProcess, (LPVOID)address, buffer, size, NULL); 80 | } 81 | 82 | // Write String, Method 1: Utf8Value is converted to string, get pointer and length from string 83 | // template <> 84 | // void writeMemory(HANDLE hProcess, DWORD address, std::string value) { 85 | // WriteProcessMemory(hProcess, (LPVOID)address, value.c_str(), value.length(), NULL); 86 | // } 87 | 88 | // Write String, Method 2: get pointer and length from Utf8Value directly 89 | void writeMemory(HANDLE hProcess, DWORD64 address, char* value, SIZE_T size) { 90 | WriteProcessMemory(hProcess, (LPVOID)address, value, size, NULL); 91 | } 92 | }; 93 | #endif 94 | #pragma once -------------------------------------------------------------------------------- /App/node_modules/memoryjs/lib/memoryjs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef MEMORYJS_H 3 | #define MEMORYJS_H 4 | #define WIN32_LEAN_AND_MEAN 5 | 6 | #include 7 | 8 | class memoryjs { 9 | 10 | public: 11 | memoryjs(); 12 | ~memoryjs(); 13 | }; 14 | #endif 15 | #pragma once 16 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/lib/module.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "module.h" 6 | #include "process.h" 7 | #include "memoryjs.h" 8 | 9 | DWORD64 module::getBaseAddress(const char* processName, DWORD processId) { 10 | char* errorMessage = ""; 11 | MODULEENTRY32 baseModule = module::findModule(processName, processId, &errorMessage); 12 | return (DWORD64)baseModule.modBaseAddr; 13 | } 14 | 15 | MODULEENTRY32 module::findModule(const char* moduleName, DWORD processId, char** errorMessage) { 16 | MODULEENTRY32 module; 17 | bool found = false; 18 | 19 | std::vector moduleEntries = getModules(processId, errorMessage); 20 | 21 | // Loop over every module 22 | for (std::vector::size_type i = 0; i != moduleEntries.size(); i++) { 23 | // Check to see if this is the module we want. 24 | if (!strcmp(moduleEntries[i].szModule, moduleName)) { 25 | // module is returned and moduleEntry is used internally for reading/writing to memory 26 | module = moduleEntries[i]; 27 | found = true; 28 | break; 29 | } 30 | } 31 | 32 | if (!found) { 33 | *errorMessage = "unable to find module"; 34 | } 35 | 36 | return module; 37 | } 38 | 39 | std::vector module::getModules(DWORD processId, char** errorMessage) { 40 | // Take a snapshot of all modules inside a given process. 41 | HANDLE hModuleSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, processId); 42 | MODULEENTRY32 mEntry; 43 | 44 | if (hModuleSnapshot == INVALID_HANDLE_VALUE) { 45 | *errorMessage = "method failed to take snapshot of the modules"; 46 | } 47 | 48 | // Before use, set the structure size. 49 | mEntry.dwSize = sizeof(mEntry); 50 | 51 | // Exit if unable to find the first module. 52 | if (!Module32First(hModuleSnapshot, &mEntry)) { 53 | CloseHandle(hModuleSnapshot); 54 | *errorMessage = "method failed to retrieve the first module"; 55 | } 56 | 57 | std::vector modules; 58 | 59 | // Loop through modules. 60 | do { 61 | // Add the module to the vector 62 | modules.push_back(mEntry); 63 | } while (Module32Next(hModuleSnapshot, &mEntry)); 64 | 65 | CloseHandle(hModuleSnapshot); 66 | 67 | return modules; 68 | } 69 | 70 | std::vector module::getThreads(DWORD processId, char** errorMessage) { 71 | HANDLE hThreadSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, processId); 72 | THREADENTRY32 mEntry; 73 | 74 | if (hThreadSnapshot == INVALID_HANDLE_VALUE) { 75 | *errorMessage = "method failed to take snapshot of the threads"; 76 | } 77 | 78 | mEntry.dwSize = sizeof(mEntry); 79 | 80 | if(!Thread32First(hThreadSnapshot, &mEntry)) { 81 | CloseHandle(hThreadSnapshot); 82 | *errorMessage = "method failed to retrieve the first thread"; 83 | } 84 | 85 | std::vector threads; 86 | 87 | do { 88 | threads.push_back(mEntry); 89 | } while (Thread32Next(hThreadSnapshot, &mEntry)); 90 | 91 | CloseHandle(hThreadSnapshot); 92 | 93 | return threads; 94 | } 95 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/lib/module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef MODULE_H 3 | #define MODULE_H 4 | #define WIN32_LEAN_AND_MEAN 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace module { 11 | DWORD64 getBaseAddress(const char* processName, DWORD processId); 12 | MODULEENTRY32 findModule(const char* moduleName, DWORD processId, char** errorMessage); 13 | std::vector getModules(DWORD processId, char** errorMessage); 14 | std::vector getThreads(DWORD processId, char** errorMessage); 15 | 16 | }; 17 | #endif 18 | #pragma once 19 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/lib/pattern.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "pattern.h" 6 | #include "memoryjs.h" 7 | #include "process.h" 8 | #include "memory.h" 9 | 10 | #define INRANGE(x,a,b) (x >= a && x <= b) 11 | #define getBits( x ) (INRANGE(x,'0','9') ? (x - '0') : ((x&(~0x20)) - 'A' + 0xa)) 12 | #define getByte( x ) (getBits(x[0]) << 4 | getBits(x[1])) 13 | 14 | pattern::pattern() {} 15 | pattern::~pattern() {} 16 | 17 | bool pattern::search(HANDLE handle, std::vector regions, DWORD64 searchAddress, const char* pattern, short flags, uint32_t patternOffset, uintptr_t* pAddress) { 18 | for (std::vector::size_type i = 0; i != regions.size(); i++) { 19 | uintptr_t baseAddress = (uintptr_t) regions[i].BaseAddress; 20 | DWORD baseSize = regions[i].RegionSize; 21 | 22 | // if `searchAddress` has been set, only pattern match if the address lies inside of this region 23 | if (searchAddress != 0 && (searchAddress < baseAddress || searchAddress > (baseAddress + baseSize))) { 24 | continue; 25 | } 26 | 27 | // read memory region to pattern match inside 28 | std::vector regionBytes = std::vector(baseSize); 29 | ReadProcessMemory(handle, (LPVOID)baseAddress, ®ionBytes[0], baseSize, nullptr); 30 | unsigned char* byteBase = const_cast(®ionBytes.at(0)); 31 | 32 | if (findPattern(handle, baseAddress, byteBase, baseSize, pattern, flags, patternOffset, pAddress)) { 33 | return true; 34 | } 35 | } 36 | 37 | return false; 38 | } 39 | 40 | bool pattern::search(HANDLE handle, std::vector modules, DWORD64 searchAddress, const char* pattern, short flags, uint32_t patternOffset, uintptr_t* pAddress) { 41 | for (std::vector::size_type i = 0; i != modules.size(); i++) { 42 | uintptr_t baseAddress = (uintptr_t) modules[i].modBaseAddr; 43 | DWORD baseSize = modules[i].modBaseSize; 44 | 45 | // if `searchAddress` has been set, only pattern match if the address lies inside of this module 46 | if (searchAddress != 0 && (searchAddress < baseAddress || searchAddress > (baseAddress + baseSize))) { 47 | continue; 48 | } 49 | 50 | // read memory region occupied by the module to pattern match inside 51 | std::vector moduleBytes = std::vector(baseSize); 52 | ReadProcessMemory(handle, (LPVOID)baseAddress, &moduleBytes[0], baseSize, nullptr); 53 | unsigned char* byteBase = const_cast(&moduleBytes.at(0)); 54 | 55 | if (findPattern(handle, baseAddress, byteBase, baseSize, pattern, flags, patternOffset, pAddress)) { 56 | return true; 57 | } 58 | } 59 | 60 | return false; 61 | } 62 | 63 | /* based off Y3t1y3t's implementation */ 64 | bool pattern::findPattern(HANDLE handle, uintptr_t memoryBase, unsigned char* byteBase, DWORD memorySize, const char* pattern, short flags, uint32_t patternOffset, uintptr_t* pAddress) { 65 | // uintptr_t moduleBase = (uintptr_t)module.hModule; 66 | auto maxOffset = memorySize - 0x1000; 67 | 68 | for (uintptr_t offset = 0; offset < maxOffset; ++offset) { 69 | if (compareBytes(byteBase + offset, pattern)) { 70 | uintptr_t address = memoryBase + offset + patternOffset; 71 | 72 | if (flags & ST_READ) { 73 | ReadProcessMemory(handle, LPCVOID(address), &address, sizeof(uintptr_t), nullptr); 74 | } 75 | 76 | if (flags & ST_SUBTRACT) { 77 | address -= memoryBase; 78 | } 79 | 80 | *pAddress = address; 81 | 82 | return true; 83 | } 84 | } 85 | 86 | return false; 87 | }; 88 | 89 | bool pattern::compareBytes(const unsigned char* bytes, const char* pattern) { 90 | for (; *pattern; *pattern != ' ' ? ++bytes : bytes, ++pattern) { 91 | if (*pattern == ' ' || *pattern == '?') { 92 | continue; 93 | } 94 | 95 | if (*bytes != getByte(pattern)) { 96 | return false; 97 | } 98 | 99 | ++pattern; 100 | } 101 | 102 | return true; 103 | } -------------------------------------------------------------------------------- /App/node_modules/memoryjs/lib/pattern.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef PATTERN_H 3 | #define PATTERN_H 4 | #define WIN32_LEAN_AND_MEAN 5 | 6 | #include 7 | #include 8 | 9 | class pattern { 10 | public: 11 | pattern(); 12 | ~pattern(); 13 | 14 | // Signature/pattern types 15 | enum { 16 | // normal: normal 17 | // read: read memory at pattern 18 | // subtract: subtract module base 19 | ST_NORMAL = 0x0, 20 | ST_READ = 0x1, 21 | ST_SUBTRACT = 0x2 22 | }; 23 | 24 | bool search(HANDLE handle, std::vector regions, DWORD64 searchAddress, const char* pattern, short flags, uint32_t patternOffset, uintptr_t* pAddress); 25 | bool search(HANDLE handle, std::vector modules, DWORD64 searchAddress, const char* pattern, short flags, uint32_t patternOffset, uintptr_t* pAddress); 26 | bool findPattern(HANDLE handle, uintptr_t memoryBase, unsigned char* module, DWORD memorySize, const char* pattern, short flags, uint32_t patternOffset, uintptr_t* pAddress); 27 | bool compareBytes(const unsigned char* bytes, const char* pattern); 28 | }; 29 | 30 | #endif 31 | #pragma once 32 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/lib/process.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "process.h" 6 | #include "memoryjs.h" 7 | 8 | process::process() {} 9 | process::~process() {} 10 | 11 | using v8::Exception; 12 | using v8::Isolate; 13 | using v8::String; 14 | 15 | process::Pair process::openProcess(const char* processName, char** errorMessage){ 16 | PROCESSENTRY32 process; 17 | HANDLE handle = NULL; 18 | 19 | // A list of processes (PROCESSENTRY32) 20 | std::vector processes = getProcesses(errorMessage); 21 | 22 | for (std::vector::size_type i = 0; i != processes.size(); i++) { 23 | // Check to see if this is the process we want. 24 | if (!strcmp(processes[i].szExeFile, processName)) { 25 | handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processes[i].th32ProcessID); 26 | process = processes[i]; 27 | break; 28 | } 29 | } 30 | 31 | if (handle == NULL) { 32 | *errorMessage = "unable to find process"; 33 | } 34 | 35 | return { 36 | handle, 37 | process, 38 | }; 39 | } 40 | 41 | process::Pair process::openProcess(DWORD processId, char** errorMessage) { 42 | PROCESSENTRY32 process; 43 | HANDLE handle = NULL; 44 | 45 | // A list of processes (PROCESSENTRY32) 46 | std::vector processes = getProcesses(errorMessage); 47 | 48 | for (std::vector::size_type i = 0; i != processes.size(); i++) { 49 | // Check to see if this is the process we want. 50 | if (processId == processes[i].th32ProcessID) { 51 | handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processes[i].th32ProcessID); 52 | process = processes[i]; 53 | break; 54 | } 55 | } 56 | 57 | if (handle == NULL) { 58 | *errorMessage = "unable to find process"; 59 | } 60 | 61 | return { 62 | handle, 63 | process, 64 | }; 65 | } 66 | 67 | void process::closeProcess(HANDLE hProcess){ 68 | CloseHandle(hProcess); 69 | } 70 | 71 | std::vector process::getProcesses(char** errorMessage) { 72 | // Take a snapshot of all processes. 73 | HANDLE hProcessSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); 74 | PROCESSENTRY32 pEntry; 75 | 76 | if (hProcessSnapshot == INVALID_HANDLE_VALUE) { 77 | *errorMessage = "method failed to take snapshot of the process"; 78 | } 79 | 80 | // Before use, set the structure size. 81 | pEntry.dwSize = sizeof(pEntry); 82 | 83 | // Exit if unable to find the first process. 84 | if (!Process32First(hProcessSnapshot, &pEntry)) { 85 | CloseHandle(hProcessSnapshot); 86 | *errorMessage = "method failed to retrieve the first process"; 87 | } 88 | 89 | std::vector processes; 90 | 91 | // Loop through processes. 92 | do { 93 | // Add the process to the vector 94 | processes.push_back(pEntry); 95 | } while (Process32Next(hProcessSnapshot, &pEntry)); 96 | 97 | CloseHandle(hProcessSnapshot); 98 | return processes; 99 | } 100 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/lib/process.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef PROCESS_H 3 | #define PROCESS_H 4 | #define WIN32_LEAN_AND_MEAN 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | class process { 11 | public: 12 | struct Pair { 13 | HANDLE handle; 14 | PROCESSENTRY32 process; 15 | }; 16 | 17 | process(); 18 | ~process(); 19 | 20 | Pair openProcess(const char* processName, char** errorMessage); 21 | Pair openProcess(DWORD processId, char** errorMessage); 22 | void closeProcess(HANDLE hProcess); 23 | std::vector getProcesses(char** errorMessage); 24 | }; 25 | 26 | #endif 27 | #pragma once 28 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_from": "memoryjs", 3 | "_id": "memoryjs@3.5.1", 4 | "_inBundle": false, 5 | "_integrity": "sha512-xSdgCVwjtpGr6GvC0h2gYnhno/8/cFFGa3PiDE4D3MlutWEtBO8LEYSB4sk5kfxZ2t6alHT+H76QJfWBoZWnlQ==", 6 | "_location": "/memoryjs", 7 | "_phantomChildren": {}, 8 | "_requested": { 9 | "type": "tag", 10 | "registry": true, 11 | "raw": "memoryjs", 12 | "name": "memoryjs", 13 | "escapedName": "memoryjs", 14 | "rawSpec": "", 15 | "saveSpec": null, 16 | "fetchSpec": "latest" 17 | }, 18 | "_requiredBy": [ 19 | "#USER", 20 | "/" 21 | ], 22 | "_resolved": "https://registry.npmjs.org/memoryjs/-/memoryjs-3.5.1.tgz", 23 | "_shasum": "9156412cf18ad4ee0f6e57aa9753cc593884ff89", 24 | "_spec": "memoryjs", 25 | "_where": "", 26 | "author": { 27 | "name": "Rob--" 28 | }, 29 | "bugs": { 30 | "url": "https://github.com/Rob--/memoryjs/issues" 31 | }, 32 | "bundleDependencies": false, 33 | "dependencies": { 34 | "eslint": "^8.5.0", 35 | "eslint-config-airbnb-base": "^12.1.0", 36 | "node-addon-api": "^3.2.1" 37 | }, 38 | "deprecated": false, 39 | "description": "Node add-on for memory reading and writing!", 40 | "gypfile": true, 41 | "homepage": "https://github.com/Rob--/memoryjs#readme", 42 | "keywords": [ 43 | "memory", 44 | "reading", 45 | "writing", 46 | "management", 47 | "addon" 48 | ], 49 | "license": "MIT", 50 | "main": "index.js", 51 | "name": "memoryjs", 52 | "repository": { 53 | "type": "git", 54 | "url": "git+https://github.com/Rob--/memoryjs.git" 55 | }, 56 | "scripts": { 57 | "build": "node ./scripts/install.js", 58 | "build32": "node-gyp clean configure build --arch=ia32", 59 | "build64": "node-gyp clean configure build --arch=x64", 60 | "buildtest": "cd test && MSBuild.exe project.sln //p:Configuration=Release", 61 | "debug": "node ./scripts/debug.js", 62 | "debug32": "node-gyp configure rebuild --debug --arch=xia32", 63 | "debug64": "node-gyp configure rebuild --debug --arch=x64", 64 | "install": "npm run build", 65 | "test": "echo \"Error: no test specified\" && exit 1" 66 | }, 67 | "version": "3.5.1" 68 | } 69 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/scripts/debug.js: -------------------------------------------------------------------------------- 1 | /** 2 | * [npm debug script] 3 | * Purpose is to automatically detect Node process architecture and run the 4 | * corresponding script to build the library for debugging. 5 | * Defaults to `node-gyp rebuild` if unable to detect the architecture. 6 | */ 7 | 8 | /* eslint-disable no-console */ 9 | const { spawn } = require('child_process'); 10 | 11 | function run(script) { 12 | console.log(`Node architecture is ${process.arch}: running "${script}"`); 13 | 14 | const program = script.split(' ')[0]; 15 | const args = script.split(' ').slice(1); 16 | 17 | // inherit stdio to print colour (helpful for warnings/errors readability) 18 | const child = spawn(program, args, { stdio: 'inherit' }); 19 | 20 | child.on('close', code => console.log(`Script "${script}" exited with ${code}`)); 21 | } 22 | 23 | const buildScripts = { 24 | x64: 'run debug64', 25 | ia32: 'run debug32', 26 | }; 27 | 28 | if (Object.prototype.hasOwnProperty.call(buildScripts, process.arch)) { 29 | // on Windows, npm is actually `npm.cmd` 30 | const npm = /^win/.test(process.platform) ? 'npm.cmd' : 'npm'; 31 | run(`${npm} ${buildScripts[process.arch]}`); 32 | } else { 33 | console.log('Unfamiliar architecture detected, this library is probably not compatible with your OS.'); 34 | run('node-gyp --debug configure rebuild'); 35 | } 36 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/scripts/install.js: -------------------------------------------------------------------------------- 1 | /** 2 | * [npm install script] 3 | * Purpose is to automatically detect Node process architecture and run the 4 | * corresponding script to build the library to target the appropriate architecture. 5 | * Defaults to `node-gyp rebuild` if unable to detect the architecture. 6 | */ 7 | 8 | /* eslint-disable no-console */ 9 | const { spawn } = require('child_process'); 10 | 11 | function run(script) { 12 | console.log(`Node architecture is ${process.arch}: running "${script}"`); 13 | 14 | const program = script.split(' ')[0]; 15 | const args = script.split(' ').slice(1); 16 | 17 | // inherit stdio to print colour (helpful for warnings/errors readability) 18 | const child = spawn(program, args, { stdio: 'inherit' }); 19 | 20 | child.on('close', code => console.log(`Script "${script}" exited with ${code}`)); 21 | } 22 | 23 | const buildScripts = { 24 | x64: 'run build64', 25 | ia32: 'run build32', 26 | }; 27 | 28 | if (Object.prototype.hasOwnProperty.call(buildScripts, process.arch)) { 29 | // on Windows, npm is actually `npm.cmd` 30 | const npm = /^win/.test(process.platform) ? 'npm.cmd' : 'npm'; 31 | run(`${npm} ${buildScripts[process.arch]}`); 32 | } else { 33 | console.log('Unfamiliar architecture detected, this library is probably not compatible with your OS.'); 34 | run('node-gyp rebuild'); 35 | } 36 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/src/consts.js: -------------------------------------------------------------------------------- 1 | const dataTypes = { 2 | standard: { 3 | BYTE: 'byte', 4 | UBYTE: 'ubyte', 5 | CHAR: 'char', 6 | UCHAR: 'uchar', 7 | INT8: 'int8', 8 | UINT8: 'uint8', 9 | INT16: 'int16', 10 | INT16_BE: 'int16_be', 11 | UINT16: 'uint16', 12 | UINT16_BE: 'uint16_be', 13 | SHORT: 'short', 14 | SHORT_BE: 'short_be', 15 | USHORT: 'ushort', 16 | USHORT_BE: 'ushort_be', 17 | LONG: 'long', 18 | LONG_BE: 'long_be', 19 | ULONG: 'ulong', 20 | ULONG_BE: 'ulong_be', 21 | INT: 'int', 22 | INT_BE: 'int_be', 23 | UINT: 'uint', 24 | UINT_BE: 'uint_be', 25 | INT32: 'int32', 26 | INT32_BE: 'int32_be', 27 | UINT32: 'uint32', 28 | UINT32_BE: 'uint32_be', 29 | INT64: 'int64', 30 | INT64_BE: 'int64_be', 31 | UINT64: 'uint64', 32 | UINT64_BE: 'uint64_be', 33 | WORD: 'word', 34 | DWORD: 'dword', 35 | FLOAT: 'float', 36 | FLOAT_BE: 'float_be', 37 | DOUBLE: 'double', 38 | DOUBLE_BE: 'double_be', 39 | BOOL: 'bool', 40 | BOOLEAN: 'boolean', 41 | PTR: 'ptr', 42 | POINTER: 'pointer', 43 | UPTR: 'uptr', 44 | UPOINTER: 'upointer', 45 | STR: 'str', 46 | STRING: 'string', 47 | VEC3: 'vec3', 48 | VECTOR3: 'vector3', 49 | VEC4: 'vec4', 50 | VECTOR4: 'vector4', 51 | }, 52 | function: { 53 | T_VOID: 0x0, 54 | T_STRING: 0x1, 55 | T_CHAR: 0x2, 56 | T_BOOL: 0x3, 57 | T_INT: 0x4, 58 | T_DOUBLE: 0x5, 59 | T_FLOAT: 0x6, 60 | }, 61 | }; 62 | 63 | const signatureTypes = { 64 | NORMAL: 0x0, 65 | READ: 0x1, 66 | SUBTRACT: 0x2, 67 | }; 68 | 69 | const memoryFlags = { 70 | // see: https://docs.microsoft.com/en-gb/windows/desktop/Memory/memory-protection-constants 71 | access: { 72 | PAGE_NOACCESS: 0x01, 73 | PAGE_READONLY: 0x02, 74 | PAGE_READWRITE: 0x04, 75 | PAGE_WRITECOPY: 0x08, 76 | PAGE_EXECUTE: 0x10, 77 | PAGE_EXECUTE_READ: 0x20, 78 | PAGE_EXECUTE_READWRITE: 0x40, 79 | PAGE_EXECUTE_WRITECOPY: 0x80, 80 | PAGE_GUARD: 0x100, 81 | PAGE_NOCACHE: 0x200, 82 | PAGE_WRITECOMBINE: 0x400, 83 | PAGE_ENCLAVE_UNVALIDATED: 0x20000000, 84 | PAGE_TARGETS_NO_UPDATE: 0x40000000, 85 | PAGE_TARGETS_INVALID: 0x40000000, 86 | PAGE_ENCLAVE_THREAD_CONTROL: 0x80000000, 87 | }, 88 | 89 | // see: https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex#parameters 90 | allocation: { 91 | MEM_COMMIT: 0x00001000, 92 | MEM_RESERVE: 0x00002000, 93 | MEM_RESET: 0x00080000, 94 | MEM_TOP_DOWN: 0x00100000, 95 | MEM_RESET_UNDO: 0x1000000, 96 | MEM_LARGE_PAGES: 0x20000000, 97 | MEM_PHYSICAL: 0x00400000, 98 | }, 99 | 100 | // see: https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_memory_basic_information 101 | page: { 102 | MEM_PRIVATE: 0x20000, 103 | MEM_MAPPED: 0x40000, 104 | MEM_IMAGE: 0x1000000, 105 | }, 106 | }; 107 | 108 | const hardwareDebug = { 109 | registers: { 110 | DR0: 0x0, 111 | DR1: 0x1, 112 | DR2: 0x2, 113 | DR3: 0x3, 114 | }, 115 | breakpointTriggerTypes: { 116 | TRIGGER_EXECUTE: 0x0, 117 | TRIGGER_ACCESS: 0x3, 118 | TRIGGER_WRITE: 0x1, 119 | }, 120 | }; 121 | 122 | module.exports = { 123 | // data type constants 124 | ...dataTypes.standard, 125 | ...dataTypes.function, 126 | 127 | // pattern scanning flags 128 | ...signatureTypes, 129 | 130 | // memory flags 131 | ...memoryFlags.access, 132 | ...memoryFlags.allocation, 133 | ...memoryFlags.page, 134 | 135 | // debugger consts 136 | ...hardwareDebug.registers, 137 | ...hardwareDebug.breakpointTriggerTypes, 138 | }; 139 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/src/debugger.js: -------------------------------------------------------------------------------- 1 | const EventEmitter = require('events'); 2 | 3 | const lengths = { 4 | byte: 1, 5 | int: 4, 6 | int32: 4, 7 | uint32: 4, 8 | int64: 8, 9 | uint64: 8, 10 | dword: 4, 11 | short: 2, 12 | long: 8, 13 | float: 4, 14 | double: 8, 15 | bool: 1, 16 | boolean: 1, 17 | ptr: 4, 18 | pointer: 4, 19 | // str: 0, 20 | // string: 0, 21 | // vec3: 0, 22 | // vector3: 0, 23 | // vec4: 0, 24 | // vector4: 0, 25 | }; 26 | 27 | // Tracks used and unused registers 28 | class Registers { 29 | constructor() { 30 | this.registers = Object.freeze({ 31 | DR0: 0x0, 32 | DR1: 0x1, 33 | DR2: 0x2, 34 | DR3: 0x3, 35 | }); 36 | 37 | this.used = []; 38 | } 39 | 40 | getRegister() { 41 | const unused = Object 42 | .values(this.registers) 43 | .filter(r => !this.used.includes(r)); 44 | 45 | return unused[0]; 46 | } 47 | 48 | busy(register) { 49 | this.used.push(register); 50 | } 51 | 52 | unbusy(register) { 53 | this.used.splice(this.used.indexOf(register), 1); 54 | } 55 | } 56 | 57 | class Debugger extends EventEmitter { 58 | constructor(memoryjs) { 59 | super(); 60 | this.memoryjs = memoryjs; 61 | this.registers = new Registers(); 62 | this.attached = false; 63 | this.intervals = []; 64 | } 65 | 66 | attach(processId, killOnDetatch = false) { 67 | const success = this.memoryjs.attachDebugger(processId, killOnDetatch); 68 | 69 | if (success) { 70 | this.attached = true; 71 | } 72 | 73 | return success; 74 | } 75 | 76 | detatch(processId) { 77 | this.intervals.map(({ id }) => clearInterval(id)); 78 | return this.memoryjs.detatchDebugger(processId); 79 | } 80 | 81 | removeHardwareBreakpoint(processId, register) { 82 | const success = this.memoryjs.removeHardwareBreakpoint(processId, register); 83 | 84 | if (success) { 85 | this.registers.unbusy(register); 86 | } 87 | 88 | // Find the register's corresponding interval and delete it 89 | this.intervals.forEach(({ register: r, id }) => { 90 | if (r === register) { 91 | clearInterval(id); 92 | } 93 | }); 94 | 95 | return success; 96 | } 97 | 98 | setHardwareBreakpoint(processId, address, trigger, dataType) { 99 | let size = lengths[dataType]; 100 | 101 | // If we are breakpointing a string, we need to determine the length of it 102 | if (dataType === 'str' || dataType === 'string') { 103 | const { handle } = this.memoryjs.openProcess(processId); 104 | const value = this.memoryjs.readMemory(handle, address, this.memoryjs.STRING); 105 | 106 | size = value.length; 107 | 108 | this.memoryjs.closeProcess(handle); 109 | } 110 | 111 | // Obtain an available register 112 | const register = this.registers.getRegister(); 113 | const success = this.memoryjs 114 | .setHardwareBreakpoint(processId, address, register, trigger, size); 115 | 116 | // If the breakpoint was set, mark this register as busy 117 | if (success) { 118 | this.registers.busy(register); 119 | this.monitor(register); 120 | } 121 | 122 | return register; 123 | } 124 | 125 | monitor(register, timeout = 100) { 126 | const id = setInterval(() => { 127 | const debugEvent = this.memoryjs.awaitDebugEvent(register, timeout); 128 | 129 | if (debugEvent) { 130 | this.memoryjs.handleDebugEvent(debugEvent.processId, debugEvent.threadId); 131 | 132 | // Global event for all registers 133 | this.emit('debugEvent', { 134 | register, 135 | event: debugEvent, 136 | }); 137 | 138 | // Event per register 139 | this.emit(register, debugEvent); 140 | } 141 | }, 100); 142 | 143 | this.intervals.push({ 144 | register, 145 | id, 146 | }); 147 | } 148 | } 149 | 150 | module.exports = Debugger; 151 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/src/utils.js: -------------------------------------------------------------------------------- 1 | const SIZEOF_STDSTRING_32BIT = 24; 2 | const SIZEOF_STDSTRING_64BIT = 32; 3 | const STDSTRING_LENGTH_OFFSET = 0x10; 4 | 5 | /** 6 | * Custom string consumer/producer for Structron (due to complexity of `std::string`) 7 | * `std::string` is a container for a string which makes reading/writing to it tricky, 8 | * it will either store the string itself, or a pointer to the string, based on the 9 | * length of the string. When we want to read from or write to a buffer, we need 10 | * to determine if the string is in the buffer itself, or if the buffer 11 | * just contains a pointer to the string. Based on one of these options, 12 | * we can read from or write to the string. 13 | * 14 | * @param handle the handle to the process 15 | * @param structAddress the base address of the structure in memory 16 | * @param platform the architecture of the process, either "32" or "64" 17 | * @param encoding the encoding type of the string 18 | */ 19 | const STRUCTRON_TYPE_STRING = memoryjs => (handle, structAddress, platform, encoding = 'utf8') => ({ 20 | read(buffer, offset) { 21 | // get string length from `std::string` container 22 | const length = buffer.readUInt32LE(offset + STDSTRING_LENGTH_OFFSET); 23 | 24 | // if length > 15, `std::string` has a pointer to the string 25 | if (length > 15) { 26 | const pointer = platform === '64' ? buffer.readBigInt64LE(offset) : buffer.readUInt32LE(offset); 27 | return memoryjs.readMemory(handle, Number(pointer), memoryjs.STRING); 28 | } 29 | 30 | // if length <= 15, `std::string` directly contains the string 31 | return buffer.toString(encoding, offset, offset + length); 32 | }, 33 | write(value, context, offset) { 34 | // address containing the length of the string 35 | const lengthAddress = structAddress + offset + STDSTRING_LENGTH_OFFSET; 36 | 37 | // get existing `std::string` buffer 38 | const bufferSize = platform === '64' ? SIZEOF_STDSTRING_64BIT : SIZEOF_STDSTRING_32BIT; 39 | const existingBuffer = memoryjs.readBuffer(handle, structAddress + offset, bufferSize); 40 | 41 | // fetch length of string in memory (to determine if it's pointer based) 42 | const length = memoryjs.readMemory(handle, lengthAddress, memoryjs.INT); 43 | 44 | if ((length > 15 && value.length <= 15) || (length <= 15 && value.length > 15)) { 45 | // there are two ways strings are stored: directly or with a pointer, 46 | // we can't go from one to the other (without introducing more complexity), 47 | // so just skip the bytes to prevent crashing. if a pointer is used, we could 48 | // technically write any length, but the next time we try writing, we will read 49 | // the length and assume it's not stored via pointer and will lead to crashes 50 | 51 | // write existing buffer without changes 52 | existingBuffer.copy(context.buffer, offset); 53 | return; 54 | } 55 | 56 | // write new length 57 | memoryjs.writeMemory(handle, lengthAddress, value.length, memoryjs.UINT32); 58 | existingBuffer.writeUInt32LE(value.length, STDSTRING_LENGTH_OFFSET); 59 | 60 | if (length > 15 && value.length > 15) { 61 | // write new string in memory 62 | const pointer = memoryjs.readMemory(handle, structAddress + offset, memoryjs.POINTER); 63 | memoryjs.writeMemory(handle, pointer, value, memoryjs.STRING); 64 | } else if (length <= 15 && value.length <= 15) { 65 | // write new string directly into buffer 66 | existingBuffer.write(value, encoding); 67 | } 68 | 69 | // write our new `std::string` buffer into the buffer we are creating 70 | existingBuffer.copy(context.buffer, offset); 71 | }, 72 | SIZE: platform === '64' ? SIZEOF_STDSTRING_64BIT : SIZEOF_STDSTRING_32BIT, 73 | }); 74 | 75 | module.exports = { STRUCTRON_TYPE_STRING }; 76 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/test/allocationTest.js: -------------------------------------------------------------------------------- 1 | const memoryjs = require('../index'); 2 | const processName = 'notepad.exe'; 3 | 4 | const processObject = memoryjs.openProcess(processName); 5 | 6 | const address = memoryjs.virtualAllocEx( 7 | processObject.handle, 8 | null, 9 | 0x60, 10 | memoryjs.MEM_RESERVE | memoryjs.MEM_COMMIT, 11 | memoryjs.PAGE_EXECUTE_READWRITE, 12 | ); 13 | 14 | console.log(`Allocated address: 0x${address.toString(16).toUpperCase()}`); 15 | 16 | memoryjs.closeProcess(processObject.handle); 17 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/test/debuggerTest.js: -------------------------------------------------------------------------------- 1 | const memoryjs = require('../index'); 2 | const processName = 'Testing Things.exe'; 3 | 4 | const processObject = memoryjs.openProcess(processName); 5 | const processId = processObject.th32ProcessID; 6 | 7 | // Address of variable 8 | const address = 0xEFFBF0; 9 | 10 | // When should we breakpoint? On read, write or execute 11 | const trigger = memoryjs.TRIGGER_ACCESS; 12 | 13 | memoryjs.attachDebugger(processId); 14 | 15 | // There are 4 hardware registers: 16 | // `memoryjs.DR0` through `memoryjs.DR3` 17 | const registerToUse = memoryjs.DR0; 18 | 19 | // Our `address` references an integer variable. An integer 20 | // is 4 bytes therefore we pass `4` to the `size` parameter. 21 | const size = 4; 22 | memoryjs.setHardwareBreakpoint(processId, address, registerToUse, trigger, size); 23 | 24 | // How long to wait for the debug event before timing out 25 | const timeout = 100; 26 | 27 | // The interval duration must be the same or larger than the `timeout` value. 28 | // `awaitDebugEvent` works by waiting a certain amount of time before timing out, 29 | // therefore we only want to call the method again when we're sure the previous 30 | // call has already timed out. 31 | setInterval(() => { 32 | // `debugEvent` can be null if no event occurred 33 | const debugEvent = memoryjs.awaitDebugEvent(registerToUse, timeout); 34 | 35 | // If a breakpoint occurred, handle it 36 | if (debugEvent) { 37 | memoryjs.handleDebugEvent(debugEvent.processId, debugEvent.threadId); 38 | } 39 | }, timeout); 40 | 41 | // Don't forget to detatch the debugger! 42 | // memoryjs.detatchDebugger(processId); 43 | 44 | memoryjs.closeProcess(processObject.handle); 45 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/test/functionTest.js: -------------------------------------------------------------------------------- 1 | const memoryjs = require('../index'); 2 | const processName = 'FunctionTest.exe'; 3 | 4 | // TODO: Start the target process and obtain the absolute address of 5 | // the function that you want to call and update the variable below. 6 | 7 | const processObject = memoryjs.openProcess(processName); 8 | 9 | const args = [{ type: memoryjs.T_FLOAT, value: 12.34 }]; 10 | const returnType = memoryjs.T_FLOAT; 11 | 12 | const { 13 | returnValue, 14 | exitCode, 15 | } = memoryjs.callFunction(processObject.handle, args, returnType, address); 16 | 17 | console.log(`Return value: ${returnValue}`); 18 | console.log(`Exit code: ${exitCode}`); 19 | 20 | memoryjs.closeProcess(processObject.handle); 21 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/test/memoryTest.js: -------------------------------------------------------------------------------- 1 | const memoryjs = require('../index'); 2 | const processName = 'MemoryTest.exe'; 3 | 4 | // TODO: Start the MemoryTest process, and check it's output against the outputs of this 5 | 6 | /* Example Output: 7 | 8 | $ node test/memoryTest 9 | type address value 10 | int 0x3AFCB4 2003818640 11 | dword 0x3AFCA8 2648673792 12 | short 0x3AFC9C 0 13 | long 0x3AFC90 0 14 | float 0x3AFC84 0 15 | double 0x3AFC74 4.031792002834e-312 16 | pointer 0x3AFC68 816043786240 17 | bool 0x3AFC5F false 18 | string 0xB1FAA4 robert 19 | */ 20 | 21 | const processObject = memoryjs.openProcess(processName); 22 | 23 | const data = [{ 24 | type: memoryjs.INT, 25 | name: 'int', 26 | address: 0x003AFCB4, 27 | }, { 28 | type: memoryjs.DWORD, 29 | name: 'dword', 30 | address: 0x003AFCA8, 31 | }, { 32 | type: memoryjs.SHORT, 33 | name: 'short', 34 | address: 0x003AFC9C, 35 | }, { 36 | type: memoryjs.LONG, 37 | name: 'long', 38 | address: 0x003AFC90, 39 | }, { 40 | type: memoryjs.FLOAT, 41 | name: 'float', 42 | address: 0x003AFC84, 43 | }, { 44 | type: memoryjs.DOUBLE, 45 | name: 'double', 46 | address: 0x003AFC74, 47 | }, { 48 | type: memoryjs.POINTER, 49 | name: 'pointer', 50 | address: 0x003AFC68, 51 | }, { 52 | type: memoryjs.BOOL, 53 | name: 'bool', 54 | address: 0x003AFC5F, 55 | }, { 56 | type: memoryjs.STRING, 57 | name: 'string', 58 | address: 0xb1faa4, 59 | }]; 60 | 61 | console.log('type\taddress\t\tvalue'); 62 | 63 | data.forEach(({ type, name, address }) => { 64 | const result = memoryjs.readMemory(processObject.handle, address, type); 65 | console.log(`${name}\t0x${address.toString(16).toUpperCase()}\t${result}`); 66 | }); 67 | 68 | memoryjs.closeProcess(processObject.handle); 69 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/test/project.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30503.244 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MemoryTest", "vcxproj\MemoryTest.vcxproj", "{67039DFF-7CB0-4034-8A19-470C8F8CADE9}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FunctionTest", "vcxproj\FunctionTest.vcxproj", "{D2E35EBA-E7AE-424D-B022-52440B65B235}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ProtectionTest", "vcxproj\ProtectionTest.vcxproj", "{8E68E3F6-3D5C-4D4B-AFCC-F13A9DA4D539}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|x86 = Debug|x86 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {67039DFF-7CB0-4034-8A19-470C8F8CADE9}.Debug|x86.ActiveCfg = Debug|Win32 19 | {67039DFF-7CB0-4034-8A19-470C8F8CADE9}.Debug|x86.Build.0 = Debug|Win32 20 | {67039DFF-7CB0-4034-8A19-470C8F8CADE9}.Release|x86.ActiveCfg = Release|Win32 21 | {67039DFF-7CB0-4034-8A19-470C8F8CADE9}.Release|x86.Build.0 = Release|Win32 22 | {D2E35EBA-E7AE-424D-B022-52440B65B235}.Debug|x86.ActiveCfg = Debug|Win32 23 | {D2E35EBA-E7AE-424D-B022-52440B65B235}.Debug|x86.Build.0 = Debug|Win32 24 | {D2E35EBA-E7AE-424D-B022-52440B65B235}.Release|x86.ActiveCfg = Release|Win32 25 | {D2E35EBA-E7AE-424D-B022-52440B65B235}.Release|x86.Build.0 = Release|Win32 26 | {8E68E3F6-3D5C-4D4B-AFCC-F13A9DA4D539}.Debug|x86.ActiveCfg = Debug|Win32 27 | {8E68E3F6-3D5C-4D4B-AFCC-F13A9DA4D539}.Debug|x86.Build.0 = Debug|Win32 28 | {8E68E3F6-3D5C-4D4B-AFCC-F13A9DA4D539}.Release|x86.ActiveCfg = Release|Win32 29 | {8E68E3F6-3D5C-4D4B-AFCC-F13A9DA4D539}.Release|x86.Build.0 = Release|Win32 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {403312F9-8A55-4219-A498-9B79B46EC898} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/test/protectionTest.js: -------------------------------------------------------------------------------- 1 | const memoryjs = require('../index'); 2 | const processName = 'ProtectionTest.exe'; 3 | 4 | // TODO: Start the TestTarget process, and monitor it's return value. 5 | 6 | const processObject = memoryjs.openProcess(processName); 7 | console.log(processObject); 8 | 9 | memoryjs.setProtection(processObject.handle, 0x00FE102D, 4, memoryjs.PAGE_EXECUTE_READWRITE); 10 | memoryjs.writeMemory(processObject.handle, 0x00FE102D, 1337, memoryjs.INT); 11 | 12 | memoryjs.closeProcess(processObject.handle); 13 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/test/queryTest.js: -------------------------------------------------------------------------------- 1 | const memoryjs = require('../index'); 2 | const processName = 'chrome.exe'; 3 | 4 | const processObject = memoryjs.openProcess(processName); 5 | 6 | const regions = memoryjs.getRegions(processObject.handle).reverse().slice(0, 40); 7 | 8 | // Minimum lengths for each column 9 | const lengths = { 10 | BaseAddress: 'BaseAddress'.length, 11 | AllocationBase: 'AllocationBase'.length, 12 | AllocationProtect: 'AllocationProtect'.length, 13 | RegionSize: 'RegionSize'.length, 14 | State: 'State'.length, 15 | Protect: 'Protect'.length, 16 | Type: 'Type'.length, 17 | szExeFile: 'szExeFile'.length, 18 | }; 19 | 20 | // Calculate maximum lengths 21 | regions.forEach((region) => { 22 | Object.entries(region).forEach(([key, value]) => { 23 | const formatted = `0x${value.toString(16)}`; 24 | if (formatted.length > lengths[key]) { 25 | lengths[key] = formatted.length; 26 | } 27 | }); 28 | }); 29 | 30 | let text = ''; 31 | Object.entries(lengths).forEach(([key, value]) => { 32 | if (key === 'szExeFile') { 33 | text += ` ${key}`.padEnd(value + 2, ' '); 34 | } else { 35 | text += key.padStart(value + 2, ' '); 36 | text += ' |'; 37 | } 38 | }); 39 | console.log(text); 40 | 41 | regions.forEach((region) => { 42 | let text = ''; 43 | Object.entries(region).forEach(([key, value]) => { 44 | if (key === 'szExeFile') { 45 | text += ` ${value}`.padEnd(lengths[key] + 2, ' '); 46 | } else { 47 | text += `0x${value.toString(16)}`.padStart(lengths[key] + 2, ' '); 48 | text += ' |'; 49 | } 50 | }); 51 | 52 | console.log(text); 53 | }); 54 | 55 | memoryjs.closeProcess(processObject.handle); 56 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/test/src/MemoryTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | int main() 8 | { 9 | cout << "type\taddress\t\tvalue" << endl; 10 | 11 | int _int = -2147483647; 12 | cout << "int\t0x" << hex << &_int << dec << "\t" << _int << endl; 13 | 14 | DWORD _dword = 2147483647; 15 | cout << "dword\t0x" << hex << &_dword << dec << "\t" << _dword << endl; 16 | 17 | short _short = -32768; 18 | cout << "short\t0x" << hex << &_short << dec << "\t" << _short << endl; 19 | 20 | long _long = -2147483647; 21 | cout << "long\t0x" << hex << &_long << dec << "\t" << _long << endl; 22 | 23 | float _float = 3.402823466e+38F / 2; 24 | cout << "float\t0x" << hex << &_float << dec << "\t" << _float << endl; 25 | 26 | double _double = 2.2250738585072014e-308; 27 | cout << "double\t0x" << hex << &_double << dec << "\t" << _double << endl; 28 | 29 | intptr_t _intptr_t = 2147483647; 30 | cout << "pointer\t0x" << hex << &_intptr_t << dec << "\t" << _intptr_t << endl; 31 | 32 | bool _bool = true; 33 | cout << "bool\t0x" << hex << &_bool << dec << "\t" << _bool << endl; 34 | 35 | string _string = "robert"; 36 | cout << "string\t0x" << hex << (DWORD64)_string.c_str() << dec << "\t" << _string << endl; 37 | 38 | getchar(); 39 | 40 | return 0; 41 | } 42 | 43 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/test/src/functionTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | float testAdd(float a) { 6 | std::cout << a << std::endl; 7 | return a; 8 | } 9 | 10 | int main() { 11 | DWORD offset = (DWORD)testAdd - (DWORD)GetModuleHandle(NULL); 12 | std::cout << "Function offset from base: 0x" << std::hex << offset << std::dec << std::endl; 13 | std::cout << "Absolute: 0x" << std::hex << (DWORD)testAdd << std::dec << std::endl; 14 | 15 | getchar(); 16 | return 0; 17 | } -------------------------------------------------------------------------------- /App/node_modules/memoryjs/test/src/protectionTest.cpp: -------------------------------------------------------------------------------- 1 | // TestTarget.cpp : Defines the entry point for the console application. 2 | // 3 | #include 4 | #include "stdio.h" 5 | #include 6 | 7 | int value = 0; 8 | 9 | __declspec(noinline) void routine() { 10 | value = 100; 11 | } 12 | 13 | 14 | int main() 15 | { 16 | printf("This program will return an exit code of 0 if you successfuly modify the memory in the given time frame.\n\ 17 | \nWe want to modify the .code section of this exe.\n\ 18 | Address of operation is likely around: 0x%08X.\n\ 19 | Look for the MOV opcode + from its address to get the value address and modify it's memory you will need to change protection on the section of memory.\n", &routine); 20 | 21 | 22 | // Assert that the program is compiled and running with PAGE_EXECUTE_READ on the routine method. 23 | MEMORY_BASIC_INFORMATION mbi; 24 | if (VirtualQuery(&routine, &mbi, sizeof(mbi)) == 0) { 25 | printf("Unable to query memory protection for some reason.\n"); 26 | return 1; 27 | } 28 | if (! (mbi.Protect & PAGE_EXECUTE_READ) ) { 29 | printf("Warning: Expecting memory to be EXECUTE_READ\n"); 30 | return 1; 31 | } 32 | 33 | printf("The address of the value is at 0x%08X but please modify the code in the routine that is setting it to 100 to set it to something else.\n", &value); 34 | printf("On MSVC 2017 Release mode x86 I'm getting this address 0x%08X.\n", ((char*)&routine) + 6); 35 | 36 | 37 | int counter = 0; 38 | while (1) { 39 | // Intentionally set the value to this before and after the call. 40 | value = 0xDEADBEEF; 41 | 42 | routine(); 43 | 44 | if (value != 100) { 45 | routine(); 46 | if (value != 100) { 47 | return 0; 48 | } 49 | else { 50 | printf("Please modify the code not the value in memory.\n"); 51 | } 52 | } 53 | 54 | value = 0xDEADBEEF; 55 | 56 | Sleep(1000); 57 | counter++; 58 | 59 | if (value != 0xDEADBEEF) { 60 | printf("You must modify the code not the value.\n"); 61 | } 62 | 63 | if (counter > 60) { 64 | break; 65 | } 66 | } 67 | 68 | printf("Attempt time expired closing application as failed.\n"); 69 | return 1; 70 | } 71 | 72 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/test/vcxproj/FunctionTest.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | Win32 6 | 7 | 8 | Release 9 | Win32 10 | 11 | 12 | 13 | {D2E35EBA-E7AE-424D-B022-52440B65B235} 14 | 15 | 16 | 17 | Application 18 | v142 19 | v141 20 | 21 | 22 | false 23 | $(SolutionDir) 24 | $(Configuration)\$(ProjectName)\ 25 | $(ProjectName) 26 | 27 | 28 | false 29 | $(SolutionDir) 30 | $(Configuration)\$(ProjectName)\ 31 | $(ProjectName) 32 | 33 | 34 | 35 | false 36 | 37 | 38 | 39 | 40 | false 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/test/vcxproj/MemoryTest.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | Win32 6 | 7 | 8 | Release 9 | Win32 10 | 11 | 12 | 13 | {67039DFF-7CB0-4034-8A19-470C8F8CADE9} 14 | 15 | 16 | 17 | Application 18 | v142 19 | v141 20 | 21 | 22 | false 23 | $(SolutionDir) 24 | $(Configuration)\$(ProjectName)\ 25 | $(ProjectName) 26 | 27 | 28 | false 29 | $(SolutionDir) 30 | $(Configuration)\$(ProjectName)\ 31 | $(ProjectName) 32 | 33 | 34 | 35 | false 36 | 37 | 38 | 39 | 40 | false 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /App/node_modules/memoryjs/test/vcxproj/ProtectionTest.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | Win32 6 | 7 | 8 | Release 9 | Win32 10 | 11 | 12 | 13 | {8E68E3F6-3D5C-4D4B-AFCC-F13A9DA4D539} 14 | 15 | 16 | 17 | Application 18 | v142 19 | v141 20 | 21 | 22 | false 23 | $(SolutionDir) 24 | $(Configuration)\$(ProjectName)\ 25 | $(ProjectName) 26 | 27 | 28 | false 29 | $(SolutionDir) 30 | $(Configuration)\$(ProjectName)\ 31 | $(ProjectName) 32 | 33 | 34 | 35 | false 36 | 37 | 38 | 39 | 40 | false 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /App/node_modules/node-stream-zip/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Antelle https://github.com/antelle 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | == dependency license: adm-zip == 23 | 24 | Copyright (c) 2012 Another-D-Mention Software and other contributors, 25 | http://www.another-d-mention.ro/ 26 | 27 | Permission is hereby granted, free of charge, to any person obtaining 28 | a copy of this software and associated documentation files (the 29 | "Software"), to deal in the Software without restriction, including 30 | without limitation the rights to use, copy, modify, merge, publish, 31 | distribute, sublicense, and/or sell copies of the Software, and to 32 | permit persons to whom the Software is furnished to do so, subject to 33 | the following conditions: 34 | 35 | The above copyright notice and this permission notice shall be 36 | included in all copies or substantial portions of the Software. 37 | 38 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 39 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 40 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 41 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 42 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 43 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 44 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /App/node_modules/node-stream-zip/README.md: -------------------------------------------------------------------------------- 1 | # node-stream-zip ![CI Checks](https://github.com/antelle/node-stream-zip/workflows/CI%20Checks/badge.svg) 2 | 3 | node.js library for reading and extraction of ZIP archives. 4 | Features: 5 | 6 | - it never loads entire archive into memory, everything is read by chunks 7 | - large archives support 8 | - all operations are non-blocking, no sync i/o 9 | - fast initialization 10 | - no dependencies, no binary addons 11 | - decompression with built-in zlib module 12 | - deflate, sfx, macosx/windows built-in archives 13 | - ZIP64 support 14 | 15 | ## Installation 16 | 17 | ```sh 18 | npm i node-stream-zip 19 | ``` 20 | 21 | ## Usage 22 | 23 | There are two APIs provided: 24 | 1. [promise-based / async](#async-api) 25 | 2. [callbacks](#callback-api) 26 | 27 | It's recommended to use the new, promise API, however the legacy callback API 28 | may be more flexible for certain operations. 29 | 30 | ### Async API 31 | 32 | Open a zip file 33 | ```javascript 34 | const StreamZip = require('node-stream-zip'); 35 | const zip = new StreamZip.async({ file: 'archive.zip' }); 36 | ``` 37 | 38 | Stream one entry to stdout 39 | ```javascript 40 | const stm = await zip.stream('path/inside/zip.txt'); 41 | stm.pipe(process.stdout); 42 | stm.on('end', () => zip.close()); 43 | ``` 44 | 45 | Read a file as buffer 46 | ```javascript 47 | const data = await zip.entryData('path/inside/zip.txt'); 48 | await zip.close(); 49 | ``` 50 | 51 | Extract one file to disk 52 | ```javascript 53 | await zip.extract('path/inside/zip.txt', './extracted.txt'); 54 | await zip.close(); 55 | ``` 56 | 57 | List entries 58 | ```javascript 59 | const entriesCount = await zip.entriesCount; 60 | console.log(`Entries read: ${entriesCount}`); 61 | 62 | const entries = await zip.entries(); 63 | for (const entry of Object.values(entries)) { 64 | const desc = entry.isDirectory ? 'directory' : `${entry.size} bytes`; 65 | console.log(`Entry ${entry.name}: ${desc}`); 66 | } 67 | 68 | // Do not forget to close the file once you're done 69 | await zip.close(); 70 | ``` 71 | 72 | Extract a folder from archive to disk 73 | ```javascript 74 | fs.mkdirSync('extracted'); 75 | await zip.extract('path/inside/zip/', './extracted'); 76 | await zip.close(); 77 | ``` 78 | 79 | Extract everything 80 | ```javascript 81 | fs.mkdirSync('extracted'); 82 | const count = await zip.extract(null, './extracted'); 83 | console.log(`Extracted ${count} entries`); 84 | await zip.close(); 85 | ``` 86 | 87 | When extracting a folder, you can listen to `extract` event 88 | ```javascript 89 | zip.on('extract', (entry, file) => { 90 | console.log(`Extracted ${entry.name} to ${file}`); 91 | }); 92 | ``` 93 | 94 | `entry` event is generated for every entry during loading 95 | ```javascript 96 | zip.on('entry', entry => { 97 | // you can already stream this entry, 98 | // without waiting until all entry descriptions are read (suitable for very large archives) 99 | console.log(`Read entry ${entry.name}`); 100 | }); 101 | ``` 102 | 103 | ### Callback API 104 | 105 | Open a zip file 106 | ```javascript 107 | const StreamZip = require('node-stream-zip'); 108 | const zip = new StreamZip({ file: 'archive.zip' }); 109 | 110 | // Handle errors 111 | zip.on('error', err => { /*...*/ }); 112 | ``` 113 | 114 | List entries 115 | ```javascript 116 | zip.on('ready', () => { 117 | console.log('Entries read: ' + zip.entriesCount); 118 | for (const entry of Object.values(zip.entries())) { 119 | const desc = entry.isDirectory ? 'directory' : `${entry.size} bytes`; 120 | console.log(`Entry ${entry.name}: ${desc}`); 121 | } 122 | // Do not forget to close the file once you're done 123 | zip.close(); 124 | }); 125 | ``` 126 | 127 | Stream one entry to stdout 128 | ```javascript 129 | zip.on('ready', () => { 130 | zip.stream('path/inside/zip.txt', (err, stm) => { 131 | stm.pipe(process.stdout); 132 | stm.on('end', () => zip.close()); 133 | }); 134 | }); 135 | ``` 136 | 137 | Extract one file to disk 138 | ```javascript 139 | zip.on('ready', () => { 140 | zip.extract('path/inside/zip.txt', './extracted.txt', err => { 141 | console.log(err ? 'Extract error' : 'Extracted'); 142 | zip.close(); 143 | }); 144 | }); 145 | ``` 146 | 147 | Extract a folder from archive to disk 148 | ```javascript 149 | zip.on('ready', () => { 150 | fs.mkdirSync('extracted'); 151 | zip.extract('path/inside/zip/', './extracted', err => { 152 | console.log(err ? 'Extract error' : 'Extracted'); 153 | zip.close(); 154 | }); 155 | }); 156 | ``` 157 | 158 | Extract everything 159 | ```javascript 160 | zip.on('ready', () => { 161 | fs.mkdirSync('extracted'); 162 | zip.extract(null, './extracted', (err, count) => { 163 | console.log(err ? 'Extract error' : `Extracted ${count} entries`); 164 | zip.close(); 165 | }); 166 | }); 167 | ``` 168 | 169 | Read a file as buffer in sync way 170 | ```javascript 171 | zip.on('ready', () => { 172 | const data = zip.entryDataSync('path/inside/zip.txt'); 173 | zip.close(); 174 | }); 175 | ``` 176 | 177 | When extracting a folder, you can listen to `extract` event 178 | ```javascript 179 | zip.on('extract', (entry, file) => { 180 | console.log(`Extracted ${entry.name} to ${file}`); 181 | }); 182 | ``` 183 | 184 | `entry` event is generated for every entry during loading 185 | ```javascript 186 | zip.on('entry', entry => { 187 | // you can already stream this entry, 188 | // without waiting until all entry descriptions are read (suitable for very large archives) 189 | console.log(`Read entry ${entry.name}`); 190 | }); 191 | ``` 192 | 193 | ## Options 194 | 195 | You can pass these options to the constructor 196 | - `storeEntries: true` - you will be able to work with entries inside zip archive, otherwise the only way to access them is `entry` event 197 | - `skipEntryNameValidation: true` - by default, entry name is checked for malicious characters, like `../` or `c:\123`, pass this flag to disable validation errors 198 | 199 | ## Methods 200 | 201 | - `zip.entries()` - get all entries description 202 | - `zip.entry(name)` - get entry description by name 203 | - `zip.stream(entry, function(err, stm) { })` - get entry data reader stream 204 | - `zip.entryDataSync(entry)` - get entry data in sync way 205 | - `zip.close()` - cleanup after all entries have been read, streamed, extracted, and you don't need the archive 206 | 207 | ## Building 208 | 209 | The project doesn't require building. To run unit tests with [nodeunit](https://github.com/caolan/nodeunit): 210 | ```sh 211 | npm test 212 | ``` 213 | 214 | ## Known issues 215 | 216 | - [utf8](https://github.com/rubyzip/rubyzip/wiki/Files-with-non-ascii-filenames) file names 217 | 218 | ## Out of scope 219 | 220 | - AES encrypted files: the library will throw an error if you try to open it 221 | 222 | ## Contributors 223 | 224 | ZIP parsing code has been partially forked from [cthackers/adm-zip](https://github.com/cthackers/adm-zip) (MIT license). 225 | -------------------------------------------------------------------------------- /App/node_modules/node-stream-zip/node_stream_zip.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare namespace StreamZip { 4 | interface StreamZipOptions { 5 | /** 6 | * File to read 7 | * @default undefined 8 | */ 9 | file?: string; 10 | 11 | /** 12 | * Alternatively, you can pass fd here 13 | * @default undefined 14 | */ 15 | fd?: number; 16 | 17 | /** 18 | * You will be able to work with entries inside zip archive, 19 | * otherwise the only way to access them is entry event 20 | * @default true 21 | */ 22 | storeEntries?: boolean; 23 | 24 | /** 25 | * By default, entry name is checked for malicious characters, like ../ or c:\123, 26 | * pass this flag to disable validation error 27 | * @default false 28 | */ 29 | skipEntryNameValidation?: boolean; 30 | 31 | /** 32 | * Filesystem read chunk size 33 | * @default automatic based on file size 34 | */ 35 | chunkSize?: number; 36 | 37 | /** 38 | * Encoding used to decode file names 39 | * @default UTF8 40 | */ 41 | nameEncoding?: string; 42 | } 43 | 44 | interface ZipEntry { 45 | /** 46 | * file name 47 | */ 48 | name: string; 49 | 50 | /** 51 | * true if it's a directory entry 52 | */ 53 | isDirectory: boolean; 54 | 55 | /** 56 | * true if it's a file entry, see also isDirectory 57 | */ 58 | isFile: boolean; 59 | 60 | /** 61 | * file comment 62 | */ 63 | comment: string; 64 | 65 | /** 66 | * if the file is encrypted 67 | */ 68 | encrypted: boolean; 69 | 70 | /** 71 | * version made by 72 | */ 73 | verMade: number; 74 | 75 | /** 76 | * version needed to extract 77 | */ 78 | version: number; 79 | 80 | /** 81 | * encrypt, decrypt flags 82 | */ 83 | flags: number; 84 | 85 | /** 86 | * compression method 87 | */ 88 | method: number; 89 | 90 | /** 91 | * modification time 92 | */ 93 | time: number; 94 | 95 | /** 96 | * uncompressed file crc-32 value 97 | */ 98 | crc: number; 99 | 100 | /** 101 | * compressed size 102 | */ 103 | compressedSize: number; 104 | 105 | /** 106 | * uncompressed size 107 | */ 108 | size: number; 109 | 110 | /** 111 | * volume number start 112 | */ 113 | diskStart: number; 114 | 115 | /** 116 | * internal file attributes 117 | */ 118 | inattr: number; 119 | 120 | /** 121 | * external file attributes 122 | */ 123 | attr: number; 124 | 125 | /** 126 | * LOC header offset 127 | */ 128 | offset: number; 129 | } 130 | 131 | class StreamZipAsync { 132 | constructor(config: StreamZipOptions); 133 | 134 | entriesCount: Promise; 135 | comment: Promise; 136 | 137 | entry(name: string): Promise; 138 | entries(): Promise<{ [name: string]: ZipEntry }>; 139 | entryData(entry: string | ZipEntry): Promise; 140 | stream(entry: string | ZipEntry): Promise; 141 | extract(entry: string | ZipEntry | null, outPath: string): Promise; 142 | 143 | on(event: 'entry', handler: (entry: ZipEntry) => void): void; 144 | on(event: 'extract', handler: (entry: ZipEntry, outPath: string) => void): void; 145 | 146 | close(): Promise; 147 | } 148 | } 149 | 150 | type StreamZipOptions = StreamZip.StreamZipOptions; 151 | type ZipEntry = StreamZip.ZipEntry; 152 | 153 | declare class StreamZip { 154 | constructor(config: StreamZipOptions); 155 | 156 | /** 157 | * number of entries in the archive 158 | */ 159 | entriesCount: number; 160 | 161 | /** 162 | * archive comment 163 | */ 164 | comment: string; 165 | 166 | on(event: 'error', handler: (error: any) => void): void; 167 | on(event: 'entry', handler: (entry: ZipEntry) => void): void; 168 | on(event: 'ready', handler: () => void): void; 169 | on(event: 'extract', handler: (entry: ZipEntry, outPath: string) => void): void; 170 | 171 | entry(name: string): ZipEntry | undefined; 172 | 173 | entries(): { [name: string]: ZipEntry }; 174 | 175 | stream( 176 | entry: string | ZipEntry, 177 | callback: (err: any | null, stream?: NodeJS.ReadableStream) => void 178 | ): void; 179 | 180 | entryDataSync(entry: string | ZipEntry): Buffer; 181 | 182 | openEntry( 183 | entry: string | ZipEntry, 184 | callback: (err: any | null, entry?: ZipEntry) => void, 185 | sync: boolean 186 | ): void; 187 | 188 | extract( 189 | entry: string | ZipEntry | null, 190 | outPath: string, 191 | callback: (err?: any, res?: number) => void 192 | ): void; 193 | 194 | close(callback?: (err?: any) => void): void; 195 | 196 | static async: typeof StreamZip.StreamZipAsync; 197 | } 198 | 199 | export = StreamZip; 200 | -------------------------------------------------------------------------------- /App/node_modules/node-stream-zip/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-stream-zip", 3 | "version": "1.15.0", 4 | "description": "node.js library for reading and extraction of ZIP archives", 5 | "keywords": [ 6 | "zip", 7 | "archive", 8 | "unzip", 9 | "stream" 10 | ], 11 | "homepage": "https://github.com/antelle/node-stream-zip", 12 | "author": "Antelle (https://github.com/antelle)", 13 | "bugs": { 14 | "email": "antelle.net@gmail.com", 15 | "url": "https://github.com/antelle/node-stream-zip/issues" 16 | }, 17 | "license": "MIT", 18 | "files": [ 19 | "LICENSE", 20 | "node_stream_zip.js", 21 | "node_stream_zip.d.ts" 22 | ], 23 | "scripts": { 24 | "lint": "eslint node_stream_zip.js test/tests.js", 25 | "check-types": "tsc node_stream_zip.d.ts", 26 | "test": "nodeunit test/tests.js" 27 | }, 28 | "main": "node_stream_zip.js", 29 | "types": "node_stream_zip.d.ts", 30 | "repository": { 31 | "type": "git", 32 | "url": "https://github.com/antelle/node-stream-zip.git" 33 | }, 34 | "engines": { 35 | "node": ">=0.12.0" 36 | }, 37 | "devDependencies": { 38 | "@types/node": "^14.14.6", 39 | "eslint": "^7.19.0", 40 | "nodeunit": "^0.11.3", 41 | "prettier": "^2.2.1" 42 | }, 43 | "funding": { 44 | "type": "github", 45 | "url": "https://github.com/sponsors/antelle" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Lang/about-translations.md: -------------------------------------------------------------------------------- 1 | ### About translations 2 | I would like to thank everyone who contributed to the translation of this project - thanks to all of you, none of this would have been possible! 💜 3 | 4 | - English (Default): [TheMitoSan](https://github.com/themitosan) _(Revisions by [ArbestRi](https://github.com/ArbestRi))_ 5 | - Brazilian Portuguese: [TheMitoSan](https://github.com/themitosan) 6 | - French: [Mizmalik](https://github.com/Mizmalik) 7 | - Chinese (Simplified): [nini22P](https://github.com/nini22P) 8 | - Russian: ThatSameGuy _(Revisions by [gandalfthewhite](https://github.com/gandalfthewhite19890404) and [ArtemVideoGames](https://github.com/ArtemVideoGames))_ 9 | - Italian: [Dan Adrian Radut (Aka. B8nee)](https://github.com/B8nee) 10 | - Japanese: [mktm235](https://github.com/mktm235) 11 | - Ukrainian: [ArtemVideoGames](https://github.com/ArtemVideoGames) _(Revision by ThatSameGuy)_ 12 | - Dutch: [MrSn0wy](https://github.com/MrSn0wy) 13 | - Arabic: [Shrvzr](https://github.com/Shrvzr) 14 | - Turkish: [phyesix](https://github.com/phyesix) -------------------------------------------------------------------------------- /Lang/ja-ja.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "lang": "日本語 (JA-JA)", 4 | 5 | "variables": { 6 | "labelEnableHack": " ", 7 | "emuStatusRunning": "動作中", 8 | "logWindowTitle": "fpPS4実行中", 9 | "killEmuStatus": "エミュレーションが終了しました。ログウィンドウを閉じて終了します。", 10 | "logCleared": "情報 - ログを削除しました!", 11 | "about": "fpPS4ランチャー by TheMitoSan - バージョン: %VARIABLE_0%\n\n作成者 TheMitoSan\n(https://twitter.com/themitosan)\n\nfpPS4はred-prigによって作成/開発されました\n(https://github.com/red -prig/fpPS4)\n\nmemoryjsプラグインはRob--によって作成/開発されました\n(https://github.com/rob--/memoryjs)\n\nnode-stream-zipプラグインantelleによって作成/開発されました \n(https://github.com/antelle/node-stream-zip)\n\nSVGアイコンは https://www.svgrepo.com/ から取得しました", 12 | "mainLog": "fpPS4ランチャー by TheMitoSan - バージョン: %VARIABLE_0%\nnw.js (node-webkit) バージョン %VARIABLE_1% [%VARIABLE_2%]", 13 | "settingsErrorCreatePath": "エラー - フォルダの作成に失敗しました\n(%VARIABLE_0%)\n%VARIABLE_1%", 14 | "settingsErrorfpPS4NotFound": "エラー - fpPS4の実行ファイルが見つかりません\n設定で実行可能ファイルを選択するか、「Emu」フォルダーに配置して「OK」をクリックしてください", 15 | "settingsConfirmRemoveAllGameSettings": "警告 - このオプションはゲームリストからすべての設定を削除します\n実行しますか?", 16 | "settingsConfirmRemoveGameSettings": "警告 - %VARIABLE_0%からすべての設定が削除されます\n\n実行しますか?", 17 | "settingsRemovedGameSettings": "情報 - ( %VARIABLE_0% ) 設定ファイルが正常に削除されました", 18 | "settingsRemoveGameSettingsError": "エラー - ( %VARIABLE_0% ) ファイルを削除できません\n詳細: %VARIABLE_1%", 19 | "settingsRemoveGameSettings404": "警告 - ( %VARIABLE_0% ) 設定ファイルが見つかりません", 20 | "infoProcessComplete": "情報 - プロセスが完了しました\n詳細についてはログを参照してください", 21 | "infoSettingsUpdated": "情報 - 設定ファイルが正常に更新されました", 22 | "settingsLoadError": "エラー - 設定の読み込みに失敗しました\n %VARIABLE 0%", 23 | "settingsSaveError": "エラー - 設定を保存できません\n %VARIABLE 0%", 24 | "runEmuArgs": "情報 - fpPS4を実行しています: %VARIABLE_0%\n実行ファイルパス: %VARIABLE_1%", 25 | "closeEmuStatus": "情報 - %VARIABLE_0%が終了コード %VARIABLE_1% で終了しました", 26 | "removedLibModules": "情報 - 以前にインポートされたすべてのモジュールはダンプに害を及ぼす可能性があるため削除されました", 27 | "removeLibModule": "情報 - ( %VARIABLE_0% ) 削除されたモジュール: %VARIABLE_1%", 28 | "removeModuleError": "エラー - このモジュールを削除できません\n理由: %VARIABLE_0%", 29 | "updateGameSettings": "情報 - ( %VARIABLE_0% ) 設定ファイルが正常に更新されました", 30 | "updateGameSettingsError": "エラー - %VARIABLE_1%の%VARIABLE_0%の設定ファイルを更新できませんでした\n詳細: %VARIABLE_2%", 31 | "skipUpdateGameSettings": "情報 - ( %VARIABLE_0% ) 変更がないため設定ファイルは更新されません", 32 | "errorSaveFile": "エラー - ファイルを保存できません\n詳細: %VARIABLE_0%", 33 | "saveSucessfullPath": "情報 - ファイルは正常に書き込まれました\n場所: %VARIABLE_0%", 34 | "createdSettingsFile": "情報 - ( %VARIABLE_0% ) 設定ファイルが正常に生成されました", 35 | "errorCreateSettingsFile": "エラー - 設定ファイル %VARIABLE_0% を %VARIABLE_1%に作成できませんでした\n詳細: %VARIABLE_2%", 36 | "patchLoadedSucessfully": "情報 - パッチが正常にロードされました\n名前: %VARIABLE_0%\nタイプ: %VARIABLE_1%", 37 | "patchLoadErrorMismatch": "エラー - 選択されたものはパッチじゃないか、現在のゲームと一致しません\nパッチID: %VARIABLE_0%\n現在のアプリ/ゲーム: %VARIABLE_1%", 38 | "patchLoadErrorParamSfo404": "エラー - このパッチの PARAM.SFO が見つかりません", 39 | "gameListLoadWarnPlayGo": "警告 - %VARIABLE_0%からplaygo-chunk.datが見つかりません\nこのアプリ/ゲームが自作ではない場合は適切にダンプされていることを確認してください", 40 | "gameListLoadWarnParamSfo": "警告 - %VARIABLE_0% PARAM.SFOが見つかりません\nこのアプリ/ゲームが自作ではない場合は適切にダンプされていることを確認してください", 41 | "gameListDoubleIdError": "注意 - 同じIDを持つ別のタイトルが既に存在するため %VARIABLE_0% をゲームリストに追加できませんでした(%VARIABLE_1%)", 42 | "gameListNoGameFound": "情報 - 現在のフォルダーにアプリ/ゲームが見つかりません ( %VARIABLE_0% )", 43 | "gameListSearch404": "見つけることができません", 44 | "checkDumpPlayGoOnApp": "情報 - ( %VARIABLE_0% ) playgo-chunk.dat が sce_sys/appに見つかりました - コピーが sce_sys に作成されました。", 45 | "gameListLoadSuccessful": "INFO - ゲームリストが正常に表示されました( %VARIABLE_0% 個 )", 46 | "gameListVersion": "バージョン", 47 | "selectGameLoadPatchErrorParamSfo": "エラー - このパッチから PARAM.SFO をロードできません\n%VARIABLE_0%", 48 | "path": "パス", 49 | "gamelistGamePath404": "情報 - 選択したアプリ/ゲームフォルダが存在しません\n%VARIABLE_0%", 50 | "updateEmuFetchActionsError": "エラー - GitHubから情報を取得できませんでした", 51 | "updateEmuIsLatestVersion": "情報 - すでに最新バージョンを使用しています\n検証ID (SHA): %VARIABLE_0%", 52 | "updateEmuShaAvailable": "情報 - 新しいバージョンが利用可能です\n\nローカルバージョン:%VARIABLE_0%\n新しいバージョン:%VARIABLE_1%\n\n更新しますか?", 53 | "updateEmuShaUnavailable": "情報 - ランチャーは更新が行われていないことを検出しました\n(またはfpPS4実行可能ファイルが見つかりませんでした)\n\nこの問題は自動更新手順を使用することで解決できます.\n\n続行しますか?", 54 | "updateEmuDownloadFailed": "エラー - fpPS4アップデートのダウンロードに失敗しました\n応答ステータス: %VARIABLE_0% - OK: %VARIABLE_1%", 55 | "updateEmuProcessComplete": "情報 - アップデート完了\n新しいバージョン (検証ID / SHA): %VARIABLE_0%", 56 | "updateEmu-1-4": "fpPS4のアップデートをダウンロード中 ()", 57 | "updateEmu-2-4": "アップデートの抽出中", 58 | "updateEmu-3-4": "残ったファイルの削除", 59 | "updateEmu-4-4": "アップデート完了", 60 | "settingsLogEmuSha": "INFO - fpPS4のバージョン: (%VARIABLE_0%)", 61 | "dumpStatus_OK": "完全", 62 | "dumpStatus_WARN": "不完全", 63 | "dumpStatus_HB": "自作ソフト(Homebrew)", 64 | "updateEmuWorkflow404": "", 65 | "updater_noWorkflowListAvailable": "", 66 | "Sdl2NotFound": "", 67 | "errorListUnableLocateGamePath": "", 68 | "updateEmuSettingsWorkflow404": "", 69 | "nonWindowsOsWarn": "", 70 | "cGameCompatStatus_BOOTS": "", 71 | "cGameCompatStatus_MENUS": "", 72 | "cGameCompatStatus_INGAME": "", 73 | "cGameCompatStatus_UNKNOWN": "", 74 | "cGameCompatStatus_NOTHING": "", 75 | "cGameCompatStatus_PLAYABLE": "", 76 | "warnUnableFindGameCompatDb": "", 77 | "warnUserOffline": "" 78 | }, 79 | 80 | "input_text": { 81 | "INPUT_gameListSearch": {"placeholder": "検索", "value": ""} 82 | }, 83 | 84 | "title": { 85 | "DIV_selectedGameStatus_dump": "緑 : すべてのファイルが存在します\n黄 : 一部のファイルが欠落しています-詳細についてはログを確認してください\nシアン : 実行可能ファイルは.elfファイルです", 86 | "DIV_selectedGameStatus_compat": "" 87 | }, 88 | 89 | "innerHTML": { 90 | 91 | "DIV_SETTINGS_TITLE": "設定", 92 | "DIV_SETTINGS_LANGUAGE": "言語", 93 | "LABEL_SETTINGS_CURRENT_LANGUAGE": "選択中の言語", 94 | "LABEL_SETTINGS_LANGUAGE_RESTART": "この設定を適用するにはランチャーの再起動が必要です", 95 | "DIV_SETTINGS_PATHS": "パス", 96 | "LABEL_SETTINGS_APP_GAMES_PATH": "アプリ/ゲームのフォルダ", 97 | "LABEL_SETTINGS_FPPS4_PATH": "エミュレータの場所", 98 | "DIV_SETTINGS_GAME_LIST": "アプリ/ゲームの一覧", 99 | "LABEL_SETTINGS_GRID_BORDER_RADIUS": "(グリッド) アイコンのボーダーのサイズ", 100 | "LABEL_SETTINGS_GAME_SEARCH_MODE": "検索モード", 101 | "LABEL_SETTINGS_GAME_LIST_BACKGROUND_BLUR": "背景ぼかし", 102 | "LABEL_SETTINGS_GAME_LIST_BACKGROUND_OPACITY": "背景の不透明度", 103 | "LABEL_SETTINGS_GAME_LIST_SEARCH_CASE_SENSITIVE": "詳細検索を有効にする (大文字と小文字)", 104 | "LABEL_SETTINGS_GAME_LIST_NORMAL_SHOW_BG": "リスト内の各ゲームの背景を表示", 105 | "LABEL_SETTINGS_SHOW_METADATA_GAME_ENTRY": "リスト内の各ゲームの情報(またはフォルダパス)を表示", 106 | "DIV_SETTINGS_EMU_RUNNING": "エミュレータの実行", 107 | "LABEL_SETTINGS_EMU_RUNNING_BACKGROUND_BLUR": "背景ぼかし", 108 | "LABEL_SETTINGS_EMU_RUNNING_BACKGROUND_OPACITY": "背景の不透明度", 109 | "LABEL_SETTINGS_SHOW_METADATA_EMU_RUNNING": "タイトルの下にメタデータ(またはファイルパス)を表示する", 110 | "DIV_SETTINGS_LOG_OPTIONS": "ログオプション", 111 | "LABEL_SETTINGS_LOG_START_WINDOW_STATE": "エミュレーション開始時のログウィンドウのサイズ", 112 | "LABEL_SETTINGS_PROMPT_KEY_FPPS4_CLOSES": "エミュレーション終了時にログウィンドウに「続行するには何かキーを押してください」(press any key)を表示", 113 | "LABEL_SETTINGS_ENABLE_PARAMSFO_SUPPORT": "PARAM.SFOのサポートを有効にします(適用するには、「すべてのゲーム設定を削除」をクリックしランチャーを再起動してください)", 114 | "LABEL_SETTINGS_REMOVE_PROJECT_GP4_FILES": "ゲームリストのロード時にProject.gp4を削除する", 115 | "LABEL_FPPS4_OPTIONS": "PS4 オプション", 116 | "LABEL_FPPS4_OPTIONS_DUMP_STATUS": "ダンプステータス", 117 | "LABEL_FPPS4_OPTIONS_ENABLE_PATCHES": "パッチを有効化", 118 | "LABEL_FPPS4_OPTIONS_SELECT_GAMEPAD_MODE": "", 119 | "LABEL_FPPS4_OPTIONS_PATCH_VERSION": "バージョン", 120 | "LABEL_FPPS4_OPTIONS_PATCH_TYPE": "タイプ", 121 | "LABEL_EMU_RUNNING_STATUS": "ステータス", 122 | "LABEL_FPPS4_OPTIONS_APP_VERSION": "バージョン", 123 | "LABEL_FPPS4_OPTIONS_LAUNCHER_OPTIONS": "ランチャーオプション", 124 | "LABEL_FPPS4_OPTIONS_HACKS": "ハック一覧", 125 | "LABEL_SETTINGS_SHOW_METADATA_GUI": "エミュレーション中にアプリ/ゲームのアイコンと名前を表示する", 126 | "LABEL_SETTINGS_EXPERIMENTAL_FPPS4_INTERNAL_LOG": " エミュレータの出力を表示します(標準出力エラー出力)を内部ログに表示します( F12を押す --> コンソール)", 127 | "DIV_SETTINGS_FPPS4_UPDATER": "fpPS4をアップデート", 128 | "LABEL_SETTINGS_ENABLE_LAUNCHER_FPPS4_UPDATES": "fpPS4アップデータを有効化", 129 | "LABEL_SETTINGS_FPPS4_UPDATE_BRANCH": "アップデートを検索するブランチ", 130 | "LABEL_FPPS4_UPDATER_STATUS_LANG": "ステータス", 131 | "DIV_SETTINGS_GENERAL": "全般", 132 | "LABEL_SETTINGS_GUI_ZOOM_SCALE": "インターフェースのサイズ(スケール)", 133 | "LABEL_SETTINGS_GUI_ZOOM_SCALE_INFO": "警告 : 画面の解像度が1920x1080未満の場合、この設定を変更することはおすすめしません", 134 | "LABEL_SETTINGS_START_EMU_FULLSCREEN": "フルスクリーンでfpPS4を起動", 135 | "LABEL_SETTINGS_ENABLE_GAME_COMPAT_CHECK": "", 136 | "LABEL_SETTINGS_GAMEPAD_LED_COLOR": "", 137 | "LABEL_FPPS4_OPTIONS_SELECT_GAMEPAD_LED_COLOR": "" 138 | 139 | }, 140 | 141 | "select": { 142 | 143 | "SELECT_settingsSearchMode": { 144 | "appName": "名前", 145 | "titleId": "タイトルID" 146 | }, 147 | "SELECT_settingsStartExternalWindow": { 148 | "normal": "普通", 149 | "max": "最大", 150 | "min": "最小" 151 | }, 152 | "FPPS4_OPTIONS_SELECT_GAMEPAD_MODE": { 153 | "xinput": "xinput", 154 | "sdl2": "sdl2", 155 | "keyboard": "Keyboard" 156 | } 157 | 158 | }, 159 | 160 | "value": { 161 | 162 | "BTN_SETTINGS_SELECT_APPS_GAMES_PATH": "フォルダーを選択", 163 | "BTN_SETTINGS_OPEN_APPS_GAMES_PATH": "フォルダを開く", 164 | "BTN_SETTINGS_SELECT_FPPS4_PATH": "ファイルを選択", 165 | "BTN_SETTINGS_DELETE_ALL_GAME_SETTINGS": "すべてのゲーム設定を削除", 166 | "BTN_SETTINGS_APPLY_CLOSE": "適用して閉じる", 167 | "BTN_SETTINGS_CLOSE": "閉じる", 168 | "BTN_REFRESH": "ゲームリスト再更新", 169 | "BTN_SETTINGS": "設定", 170 | "BTN_CLEAR_LOG": "ログ削除", 171 | "BTN_ABOUT": "fpPS4について", 172 | "BTN_KILL": "fpPS4を終了", 173 | "BTN_FPPS4_OPTIONS_SELECT_PATCH_LOCATION": "パッチを選択", 174 | "BTN_FPPS4_OPTIONS_OPEN_APP_LOCATION": "アプリ/ゲームのフォルダを開く", 175 | "BTN_FPPS4_OPTIONS_RESET_SETTINGS": "オプションをリセット", 176 | "BTN_launcherOptionsExportMetadata": "メタデータをエクスポート", 177 | "BTN_RUN": "fpPS4を起動", 178 | "BTN_SETTINGS_RESTART_LAUNCHER": "ランチャーを再起動", 179 | "BTN_UPDATE_FPPS4": "fpPS4を更新", 180 | "BTN_SETTINGS_FORCE_FPPS4_UPDATE": "fpPS4を強制的にアップデート" 181 | 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /Lang/zh-s.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "lang": "简体中文 (ZH-S)", 4 | 5 | "variables": { 6 | "labelEnableHack": "启用", 7 | "emuStatusRunning": "模拟器运行中", 8 | "logWindowTitle": "Running fpPS4", 9 | "killEmuStatus": "主进程被关闭 - 关闭日志窗口以继续", 10 | "logCleared": "INFO - 日志已被清除!", 11 | "about": "fpPS4 Temmie's Launcher - 版本:%VARIABLE_0%\n\n由 TheMitoSan 创建\n(https://twitter.com/themitosan)\n\nfpPS4 由 red-prig 创建\n(https://github.com/red-prig/fpPS4)\n\n插件 memoryjs 由 Rob-- 创建\n(https://github.com/rob--/memoryjs)\n\n插件 node-stream-zip 由 antelle 创建\n(https://github.com/antelle/node-stream-zip)\n\nSVG 图标来自 https://www.svgrepo.com/", 12 | "mainLog": "fpPS4 Temmie's Launcher - 版本: %VARIABLE_0%\n运行中的 nw.js (node-webkit) 版本: %VARIABLE_1% [%VARIABLE_2%]", 13 | "settingsErrorCreatePath": "ERROR - 无法创建文件夹!\n(%VARIABLE_0%)\n%VARIABLE_1%", 14 | "settingsErrorfpPS4NotFound": "ERROR - 无法找到 fpPS4 的可执行文件!\n在设置中选择可执行文件或将其放在 \"Emu\" 文件夹中,然后点击确定。", 15 | "settingsConfirmRemoveAllGameSettings": "WARNING - 即将删除游戏列表中所有的游戏设置。\n\n确定要继续吗?", 16 | "settingsConfirmRemoveGameSettings": "WARNING - 即将删除 %VARIABLE_0% 的游戏设置\n\n确定要继续吗?", 17 | "settingsRemovedGameSettings": "INFO - (%VARIABLE_0%) 的设置文件已被成功删除!", 18 | "settingsRemoveGameSettingsError": "ERROR - 无法删除 (%VARIABLE_0%) 的设置文件!\n原因: %VARIABLE_1%", 19 | "settingsRemoveGameSettings404": "WARNING - 无法找到 (%VARIABLE_0%) 的设置文件!", 20 | "infoProcessComplete": "INFO - 进程已完成!\n\n更多详情见日志", 21 | "infoSettingsUpdated": "INFO - 设置文件已成功更新!", 22 | "settingsLoadError": "ERROR - 无法加载设置!\n %VARIABLE_0%", 23 | "settingsSaveError": "ERROR - 无法保存设置!\n %VARIABLE_0%", 24 | "runEmuArgs": "INFO - fpPS4 启动选项: %VARIABLE_0%\n可执行文件路径: %VARIABLE_1%", 25 | "closeEmuStatus": "INFO - %VARIABLE_0% 被关闭,退出代码为 %VARIABLE_1%", 26 | "removedLibModules": "INFO - 以前所有导入的模块都已被删除,因为它们可能会有兼容性问题。", 27 | "removeLibModule": "INFO - (%VARIABLE_0%) 移除模块: %VARIABLE_1%", 28 | "removeModuleError": "ERROR - 无法删除该模块!\n原因: %VARIABLE_0%", 29 | "updateGameSettings": "INFO - (%VARIABLE_0%) 的设置文件已被成功更新!", 30 | "updateGameSettingsError": "ERROR - 无法在 %VARIABLE_1% 中更新 %VARIABLE_0% 的设置文件!\n原因: %VARIABLE_2%", 31 | "skipUpdateGameSettings": "INFO - (%VARIABLE_0%) 跳过更新设置文件,因为它没有任何变化!", 32 | "errorSaveFile": "ERROR - 无法保存文件!\n原因: %VARIABLE_0%", 33 | "saveSucessfullPath": "INFO - 保存成功!\n路径: %VARIABLE_0%", 34 | "createdSettingsFile": "INFO - 成功保存 (%VARIABLE_0%) 的设置文件", 35 | "errorCreateSettingsFile": "ERROR - 无法在 %VARIABLE_1% 中保存 %VARIABLE_0% 的设置文件!\n原因: %VARIABLE_2%", 36 | "patchLoadedSucessfully": "INFO - 成功加载补丁!\n标题: %VARIABLE_0%\n类型: %VARIABLE_1%", 37 | "patchLoadErrorMismatch": "ERROR - 这不是补丁 - 或者选择的补丁与当前游戏不匹配!\n补丁ID:%VARIABLE_0%\n当前应用/游戏ID:%VARIABLE_1%", 38 | "patchLoadErrorParamSfo404": "ERROR - 没有在这个补丁中找到 PARAM.SFO!", 39 | "gameListLoadWarnPlayGo": "WARNING - 在 %VARIABLE_0% 中无法找到 playgo-chunk.dat!\n如果这不是一个自制软件,请检查这个应用/游戏是否被正确地 Dump。", 40 | "gameListLoadWarnParamSfo": "WARNING - 在 %VARIABLE_0% 中无法找到 PARAM.SFO!\n如果这不是一个自制软件,请检查这个应用/游戏是否被正确地 Dump。", 41 | "gameListDoubleIdError": "WARNING - 无法添加 %VARIABLE_0% 到游戏列表中,因为存在相同游戏ID的应用/游戏!(%VARIABLE_1%)", 42 | "gameListNoGameFound": "INFO - 在当前文件夹中没有找到应用/游戏 (%VARIABLE_0%)", 43 | "gameListSearch404": "没有找到", 44 | "checkDumpPlayGoOnApp": "INFO - 在 (%VARIABLE_0%) 的 sce_sys/app 中发现了 playgo-chunk.dat - 它将被复制到 sce_sys", 45 | "gameListLoadSuccessful": "INFO - 游戏列表已成功加载!(%VARIABLE_0% 项已被发现)", 46 | "gameListVersion": "版本号", 47 | "selectGameLoadPatchErrorParamSfo": "ERROR - 无法从这个补丁中加载 PARAM.SFO!\n%VARIABLE_0%", 48 | "path": "路径", 49 | "gamelistGamePath404": "ERROR - 没有找到已选择的应用/游戏路径!\n%VARIABLE_0%", 50 | "updateEmuFetchActionsError": "ERROR - 无法获取 GitHub actions 数据!", 51 | "updateEmuIsLatestVersion": "INFO - fpPS4 已经是最新的版本!\nCommit ID (SHA): %VARIABLE_0%", 52 | "updateEmuShaAvailable": "INFO - 检测到新的更新!\n\n本地版本: %VARIABLE_0%\n新版本: %VARIABLE_1%\n\n你想要更新吗?", 53 | "updateEmuShaUnavailable": "INFO - 启动器检测到您尚未更新 fpPS4 (或者没有发现 fpPS4 可执行文件)\n\n你可以使用 fpPS4 更新程序进行修复。\n你想要执行更新程序吗?", 54 | "updateEmuDownloadFailed": "ERROR - 无法下载 fpPS4 更新!\nResponse status: %VARIABLE_0% - OK: %VARIABLE_1%", 55 | "updateEmuProcessComplete": "INFO - 更新成功!\n新的 fpPS4 版本 (commit id / sha): %VARIABLE_0%", 56 | "updateEmu-1-4": "正在下载 fpPS4 更新 ()", 57 | "updateEmu-2-4": "提取更新", 58 | "updateEmu-3-4": "删除多余文件", 59 | "updateEmu-4-4": "更新成功!", 60 | "settingsLogEmuSha": "INFO - fpPS4 版本:(%VARIABLE_0%)", 61 | "dumpStatus_OK": "完成", 62 | "dumpStatus_WARN": "文件缺失", 63 | "dumpStatus_HB": "自制", 64 | "updateEmuWorkflow404": "", 65 | "updater_noWorkflowListAvailable": "Workflow 列表无法读取", 66 | "Sdl2NotFound": "模拟器文件夹中未发现 SDL2.dll , 请安装并使用 SDL2", 67 | "errorListUnableLocateGamePath": "", 68 | "updateEmuSettingsWorkflow404": "", 69 | "nonWindowsOsWarn": "", 70 | "cGameCompatStatus_BOOTS": "", 71 | "cGameCompatStatus_MENUS": "", 72 | "cGameCompatStatus_INGAME": "", 73 | "cGameCompatStatus_UNKNOWN": "", 74 | "cGameCompatStatus_NOTHING": "", 75 | "cGameCompatStatus_PLAYABLE": "", 76 | "warnUnableFindGameCompatDb": "", 77 | "warnUserOffline": "" 78 | }, 79 | 80 | "input_text": { 81 | "INPUT_gameListSearch": {"placeholder": "搜索: 游戏标题...","value": ""} 82 | }, 83 | 84 | "title": { 85 | "DIV_selectedGameStatus_dump": "绿色: 所有的文件都存在\n黄色: 缺失一些文件 - 更多细节请查看日志\n青色: 可执行文件是一个 .elf 文件", 86 | "DIV_selectedGameStatus_compat": "" 87 | }, 88 | 89 | "innerHTML": { 90 | 91 | "DIV_SETTINGS_TITLE": "启动器设置", 92 | "DIV_SETTINGS_LANGUAGE": "语言", 93 | "LABEL_SETTINGS_CURRENT_LANGUAGE": "当前语言", 94 | "LABEL_SETTINGS_LANGUAGE_RESTART": "你需要重新启动启动器以生效", 95 | "DIV_SETTINGS_PATHS": "路径", 96 | "LABEL_SETTINGS_APP_GAMES_PATH": "应用/游戏路径", 97 | "LABEL_SETTINGS_FPPS4_PATH": "模拟器路径", 98 | "DIV_SETTINGS_GAME_LIST": "应用/游戏列表", 99 | "LABEL_SETTINGS_GRID_BORDER_RADIUS": "(网格) 图标圆角半径", 100 | "LABEL_SETTINGS_GAME_SEARCH_MODE": "搜索模式", 101 | "LABEL_SETTINGS_GAME_LIST_BACKGROUND_BLUR": "背景模糊", 102 | "LABEL_SETTINGS_GAME_LIST_BACKGROUND_OPACITY": "背景不透明度", 103 | "LABEL_SETTINGS_GAME_LIST_SEARCH_CASE_SENSITIVE": "启用精确搜索 (区分大小写)", 104 | "LABEL_SETTINGS_GAME_LIST_NORMAL_SHOW_BG": "显示列表中每个游戏的背景", 105 | "LABEL_SETTINGS_SHOW_METADATA_GAME_ENTRY": "显示列表中每个游戏的元数据 (或可执行文件路径)", 106 | "DIV_SETTINGS_EMU_RUNNING": "模拟器运行中", 107 | "LABEL_SETTINGS_EMU_RUNNING_BACKGROUND_BLUR": "背景模糊", 108 | "LABEL_SETTINGS_EMU_RUNNING_BACKGROUND_OPACITY": "背景不透明度", 109 | "LABEL_SETTINGS_SHOW_METADATA_EMU_RUNNING": "在标题下方显示游戏的元数据 (或可执行文件路径)", 110 | "DIV_SETTINGS_LOG_OPTIONS": "日志选项", 111 | "LABEL_SETTINGS_LOG_START_WINDOW_STATE": "启动日志窗口", 112 | "LABEL_SETTINGS_PROMPT_KEY_FPPS4_CLOSES": "fpPS4 关闭后按任意键关闭", 113 | "DIV_SETTINGS_MISC": "其它", 114 | "LABEL_SETTINGS_ENABLE_PARAMSFO_SUPPORT": "启用 PARAM.SFO 文件支持 (点击 \"删除所有游戏设置\" 并重新启动启动器以生效)", 115 | "LABEL_SETTINGS_REMOVE_PROJECT_GP4_FILES": "在加载游戏列表时删除所有游戏的 Project.gp4 文件", 116 | "LABEL_FPPS4_OPTIONS": "PS4 选项", 117 | "LABEL_FPPS4_OPTIONS_DUMP_STATUS": "Dump 状态", 118 | "LABEL_FPPS4_OPTIONS_ENABLE_PATCHES": "启用游戏补丁", 119 | "LABEL_FPPS4_OPTIONS_SELECT_GAMEPAD_MODE": "选择控制器模式", 120 | "LABEL_FPPS4_OPTIONS_PATCH_VERSION": "版本号", 121 | "LABEL_FPPS4_OPTIONS_PATCH_TYPE": "类型", 122 | "LABEL_EMU_RUNNING_STATUS": "运行状态", 123 | "LABEL_FPPS4_OPTIONS_APP_VERSION": "版本号", 124 | "LABEL_FPPS4_OPTIONS_LAUNCHER_OPTIONS": "启动器选项", 125 | "LABEL_FPPS4_OPTIONS_HACKS": "Hacks", 126 | "LABEL_SETTINGS_SHOW_METADATA_GUI": "在界面上显示图标和标题", 127 | "LABEL_SETTINGS_EXPERIMENTAL_FPPS4_INTERNAL_LOG": " 在内部控制台显示 fpPS4 进程日志 (stdoutstderr) (按 F12 --> Console)", 128 | "DIV_SETTINGS_FPPS4_UPDATER": "fpPS4 更新程序", 129 | "LABEL_SETTINGS_ENABLE_LAUNCHER_FPPS4_UPDATES": "启用 fpPS4 更新程序", 130 | "LABEL_SETTINGS_FPPS4_UPDATE_BRANCH": "检测更新的分支", 131 | "LABEL_FPPS4_UPDATER_STATUS_LANG": "状态", 132 | "DIV_SETTINGS_GENERAL": "通用", 133 | "LABEL_SETTINGS_GUI_ZOOM_SCALE": "界面缩放比例", 134 | "LABEL_SETTINGS_GUI_ZOOM_SCALE_INFO": "警告:如果你的屏幕分辨率低于 1920x1080,不建议更改这个设置。", 135 | "LABEL_SETTINGS_START_EMU_FULLSCREEN": "以全屏模式启动 fpPS4", 136 | "LABEL_SETTINGS_ENABLE_GAME_COMPAT_CHECK": "", 137 | "LABEL_SETTINGS_GAMEPAD_LED_COLOR": "", 138 | "LABEL_FPPS4_OPTIONS_SELECT_GAMEPAD_LED_COLOR": "" 139 | 140 | }, 141 | 142 | "select": { 143 | 144 | "SELECT_settingsSearchMode": { 145 | "appName": "标题", 146 | "titleId": "游戏ID" 147 | }, 148 | "SELECT_settingsStartExternalWindow": { 149 | "normal": "常规窗口", 150 | "max": "最大化", 151 | "min": "最小化" 152 | }, 153 | "FPPS4_OPTIONS_SELECT_GAMEPAD_MODE": { 154 | "xinput": "xinput", 155 | "sdl2": "sdl2", 156 | "keyboard": "Keyboard" 157 | } 158 | 159 | }, 160 | 161 | "value": { 162 | 163 | "BTN_SETTINGS_SELECT_APPS_GAMES_PATH": "选择路径", 164 | "BTN_SETTINGS_OPEN_APPS_GAMES_PATH": "打开路径", 165 | "BTN_SETTINGS_SELECT_FPPS4_PATH": "选择文件", 166 | "BTN_SETTINGS_DELETE_ALL_GAME_SETTINGS": "删除所有游戏设置", 167 | "BTN_SETTINGS_APPLY_CLOSE": "应用并关闭", 168 | "BTN_SETTINGS_CLOSE": "关闭", 169 | "BTN_REFRESH": "刷新列表", 170 | "BTN_SETTINGS": "设置", 171 | "BTN_CLEAR_LOG": "清除日志", 172 | "BTN_ABOUT": "关于", 173 | "BTN_KILL": "关闭 fpPS4", 174 | "BTN_FPPS4_OPTIONS_SELECT_PATCH_LOCATION": "选择补丁位置", 175 | "BTN_FPPS4_OPTIONS_OPEN_APP_LOCATION": "打开应用/游戏位置", 176 | "BTN_FPPS4_OPTIONS_RESET_SETTINGS": "重置设置", 177 | "BTN_launcherOptionsExportMetadata": "导出元数据", 178 | "BTN_RUN": "运行 fpPS4", 179 | "BTN_SETTINGS_RESTART_LAUNCHER": "重新启动启动器", 180 | "BTN_UPDATE_FPPS4": "更新 fpPS4", 181 | "BTN_SETTINGS_FORCE_FPPS4_UPDATE": "强制更新 fpPS4" 182 | 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /Nwjs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themitosan/fpPS4-Temmie-s-Launcher/cc971d1c4ea3d37ad89536f5fd52706a70939d66/Nwjs/.gitkeep -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | fpPS4_TL_Logo
3 | fpPS4 Temmie's Launcher 4 |

5 | 6 | Created by [TheMitoSan](https://github.com/themitosan) _(Previously known as **Temmie**Heartz)_, This is a simple launcher created for [fpPS4](https://github.com/red-prig/fpPS4) compatibility layer. 7 | 8 |

9 | 10 |


11 | 12 | ## How to install 13 | 14 | ### Windows 15 | 16 | #### Stable Version 17 | 18 | - Download latest release _(you can get it [here](https://github.com/themitosan/fpPS4-Temmie-s-Launcher/releases))_ 19 | - Extract all files on your desired location 20 | - Run `launcher.exe` 21 | 22 | #### Latest Features (recommended) 23 | 24 | - [Download / Clone this repo](https://github.com/themitosan/fpPS4-Temmie-s-Launcher/archive/refs/heads/main.zip) 25 | - Download [nw.js](https://dl.nwjs.io/v0.70.1/nwjs-sdk-v0.70.1-win-x64.zip) version `0.70.1` 26 | - Extract all files from `nw` on `Nwjs` folder 27 | - Run `launcher.bat` 28 | 29 | ### Linux 30 | > [!WARNING]\ 31 | > Running fpPS4 / Launcher on a Non-Windows OS **isn't the best way / recommended to test / use this software!** The main compatibility layer is being developed to **run only on Windows**. In order to run fpPS4, you will need [Wine](https://www.winehq.org) installed - but be aware that it may result in **less performance with bugs / glitches**. 32 | 33 | [Click here to know how to install Wine](https://wiki.winehq.org/Download) 34 | 35 | #### Release Version 36 | 37 | - Download latest release _(you can get it [here](https://github.com/themitosan/fpPS4-Temmie-s-Launcher/releases))_ 38 | - Extract all files on your desired location 39 | - Run `chmod +x launcher.sh && ./launcher.sh` 40 | 41 | #### Installer Method (recomended) 42 | 43 | > [!IMPORTANT]\ 44 | > Make sure to have `curl`, `tar` and `unzip` packages installed on your system! 45 | - [Download / Clone this repo](https://github.com/themitosan/fpPS4-Temmie-s-Launcher/archive/refs/heads/main.zip) 46 | - To run installer script, run `chmod +x install.sh && ./install.sh` 47 | 48 | ### Manual Installation 49 | 50 | - [Download / Clone this repo](https://github.com/themitosan/fpPS4-Temmie-s-Launcher/archive/refs/heads/main.zip) 51 | - Download latest [nw.js](https://nwjs.io/) version 52 | - Extract all files from `nw` on `Nwjs` folder 53 | - Open terminal on main project path and run `chmod +x ./launcher.sh` 54 | 55 | In order to start, run `./launcher.sh` 56 | 57 | ## General Tips 58 | 59 | - You can add `launcher.sh` as a *non-steam game* in your **Steam**! 60 | - You can hide a specific title to show on list by appending `!` before folder name _(Example: `!Apollo Save Tool`)_ 61 | - If you want to update launcher and have `git` installed, run these commands below: 62 | 63 | ``` 64 | git reset --hard 65 | git pull origin main 66 | ``` 67 | 68 | ### How to import your dumps 69 | You can see all required procedures on main [fpPS4 discord server](https://discord.gg/up9qatpX7M). 70 | 71 | ## External plugins used on this project 72 | - [memoryjs](https://github.com/rob--/memoryjs) - created by [Rob--](https://github.com/rob--) 73 | - [node-stream-zip](https://github.com/antelle/node-stream-zip) - created by [antelle](https://github.com/antelle) 74 | - [TMS.js](https://github.com/themitosan/TMS.js) by [TheMitoSan](https://github.com/themitosan) *(Hi!)* 75 | 76 | _**IMPORTANT**: This software does not allow you to obtain free PS4 Games / Apps._ 77 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | ### === Variables === 3 | ######################################################################### 4 | 5 | # NW.js Version 6 | NWJS_VER="0.92.0" 7 | 8 | # SDL Version 9 | SDL_VER="2.30.7" 10 | 11 | # Files / dirs to be removed 12 | REM_FILES_DIR_LIST=( 13 | "sdl2" 14 | "sdl2.zip" 15 | "nwjs.tar.gz" 16 | "nwjs-sdk-v$NWJS_VER-linux-x64" 17 | ) 18 | 19 | 20 | ######################################################################### 21 | ### === Functions === 22 | ######################################################################### 23 | 24 | # Remove files and dirs 25 | removeFilesDirs(){ 26 | 27 | # Process remove list 28 | for entry in "${REM_FILES_DIR_LIST[@]}" 29 | do 30 | 31 | # Check if file / dir exists. If so, remove it. 32 | if [ -f $entry ]; then 33 | echo -e "Removing $entry" 34 | rm "$entry" 35 | fi 36 | if [ -d $entry ]; then 37 | echo -e "Removing $entry" 38 | rm -rf "$entry" 39 | fi 40 | 41 | done 42 | 43 | } 44 | 45 | ######################################################################### 46 | ### === Main Script === 47 | ######################################################################### 48 | 49 | clear 50 | echo 51 | echo " #=============================================================#" 52 | echo 53 | echo " fpPS4 Temmie's Launcher - Install Script" 54 | echo " Written by @themitosan" 55 | echo 56 | echo " IMPORTANT: This script requires internet connection and" 57 | echo " curl, tar and unzip packages installed to work!" 58 | echo 59 | echo " #=============================================================#" 60 | 61 | echo 62 | echo "=== Removing possible leftover files / folders" 63 | removeFilesDirs 64 | echo Done! 65 | 66 | echo 67 | echo "=== Downloading nw.js (Ver. $NWJS_VER)" 68 | curl https://dl.nwjs.io/v$NWJS_VER/nwjs-sdk-v$NWJS_VER-linux-x64.tar.gz -o nwjs.tar.gz 69 | echo Done! 70 | 71 | echo 72 | echo "=== Downloading SDL2 (Ver. $SDL_VER)" 73 | curl -L https://github.com/libsdl-org/SDL/releases/download/release-$SDL_VER/SDL2-$SDL_VER-win32-x64.zip -o sdl2.zip 74 | echo Done! 75 | 76 | echo 77 | echo "=== Extracting nw.js" 78 | tar -xvzf nwjs.tar.gz 79 | echo Done! 80 | 81 | echo 82 | echo "=== Extracting SDL2" 83 | unzip -d sdl2 sdl2.zip 84 | echo Done! 85 | 86 | echo 87 | echo "=== Prepare nw.js folder" 88 | cd Nwjs 89 | rm -rf * 90 | echo "" > .gitkeep 91 | cd .. 92 | echo Done! 93 | 94 | echo 95 | echo "=== Checking if Emu folder exists" 96 | if ! [ -d Emu ]; then 97 | echo "Creating Emu dir..." 98 | mkdir Emu 99 | fi 100 | echo Done! 101 | 102 | echo 103 | echo "=== Moving files" 104 | mv -f nwjs-sdk-v$NWJS_VER-linux-x64/* Nwjs/ 105 | mv -f sdl2/SDL2.dll Emu/ 106 | echo Done! 107 | 108 | echo 109 | echo "=== Removing leftover files / folders" 110 | removeFilesDirs 111 | echo Done! 112 | 113 | echo 114 | echo "=== Updating permissions for running / updating launcher (chmod)" 115 | chmod +x launcher.sh 116 | chmod +x update.sh 117 | chmod +x Nwjs/nw 118 | echo Done! 119 | 120 | echo 121 | echo -e "\033[1;32m==== Process Complete! ====\033[0m" 122 | echo '---> In order to start Launcher, run "./launcher.sh"' 123 | echo '---> To update, run "./update.sh"' 124 | echo 125 | echo 'TIP: You can add "launcher.sh" as a non-steam game on your Steam!' 126 | echo 127 | echo Also - You will need wine to run fpPS4 on non-windows systems! 128 | echo The installation process may change depending of which distro you are running. 129 | echo 130 | read -p "Press [ENTER] to exit" 131 | clear -------------------------------------------------------------------------------- /launcher.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | color a 3 | title Running fpPS4 Temmie's Launcher - Please wait... 4 | echo Running fpPS4 Temmie's Launcher - Please wait... 5 | start /b Nwjs\nw . -------------------------------------------------------------------------------- /launcher.sh: -------------------------------------------------------------------------------- 1 | stty -echo 2 | clear 3 | echo "Running fpPS4 Temmie's Launcher - Please Wait..." 4 | ./Nwjs/nw . 5 | stty echo 6 | exit -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.2.4", 3 | "license": "GPL-2", 4 | "author": "TheMitoSan", 5 | "main": "App/index.htm", 6 | "name": "fpPS4 Temmie's Launcher", 7 | "chromium-args": "--disable-raf-throttling", 8 | "description": "A simple launcher for fpPS4 project", 9 | "window": { 10 | "frame": true, 11 | "width": 1186, 12 | "height": 710, 13 | "toolbar": true, 14 | "min_width": 1102, 15 | "min_height": 680, 16 | "fullscreen": false, 17 | "position": "center", 18 | "theme-color": "#002", 19 | "icon": "App/img/logo.png", 20 | "title": "fpPS4 Temmie's Launcher - Please wait..." 21 | }, 22 | "bugs": { 23 | "url": "https://github.com/themitosan/fpPS4-Temmie-s-Launcher/issues" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "https://github.com/themitosan/fpPS4-Temmie-s-Launcher.git" 28 | } 29 | } -------------------------------------------------------------------------------- /update.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cls 3 | title Updating fpPS4 Temmie's Launcher - Please Wait... 4 | echo. 5 | echo Updating fpPS4 Temmie's Launcher - Please wait 6 | echo IMPORTANT: Make sure to have Git installed on your OS! 7 | echo. 8 | git reset --hard 9 | git pull 10 | pause 11 | exit -------------------------------------------------------------------------------- /update.sh: -------------------------------------------------------------------------------- 1 | stty -echo 2 | clear 3 | echo 4 | echo "Updating fpPS4 Temmie's Launcher - Please Wait..." 5 | echo "IMPORTANT: Make sure to have git installed on your OS / Distro!" 6 | echo 7 | git reset --hard 8 | git pull 9 | stty echo 10 | exit 11 | --------------------------------------------------------------------------------