mirror of
https://github.com/luanslimadev/Wand-Enhancer.git
synced 2026-08-28 22:01:17 +00:00
Moved to electron. Beta desktop app 0.0.1
This commit is contained in:
@@ -12,7 +12,7 @@ Yes, this is safe. This script is open-source, and you can check the code yourse
|
||||
|
||||
With this patch you will be able to use the latest version together with Pro.
|
||||
|
||||
## 👀 What features will be available?
|
||||
## 💫 What features will be available?
|
||||
|
||||
✅ Unlimited usage time <br/>
|
||||
✅ No ads <br/>
|
||||
@@ -21,21 +21,25 @@ With this patch you will be able to use the latest version together with Pro.
|
||||
❌ Connect phone <br/>
|
||||
❌ Hotkeys (hotkey functionality breaks after patch for unknown reason)
|
||||
|
||||
## ❓ How to use?
|
||||
## 👀 How to use?
|
||||
|
||||
1. **Clone the Repository (or just download source code)**
|
||||
``` bash
|
||||
git clone https://github.com/k1tbyte/Wemod-patcher.git
|
||||
cd Wemod-patcher
|
||||
```
|
||||
2. **Run the script**
|
||||
``` bash
|
||||
bun install/npm install/yarn install/whatever you use
|
||||
node ./unlocker.js
|
||||
```
|
||||
3. **Follow the instructions**
|
||||
1. Go to [Releases](https://github.com/k1tbyte/Wemod-Patcher/releases) page.
|
||||
2. Download latest version
|
||||
3. Run and click the patch
|
||||
|
||||
---
|
||||
|
||||
## ❓ Q&A
|
||||
|
||||
- I applied the patch but when I inject I get stuck on 'Loading mods...'.
|
||||
- Just close WeMod and try again
|
||||
- During the game, some hacks are enabled without my input
|
||||
- This is a bug after the patch, you have to turn off hotkeys in WeMod settings
|
||||
- Why is the patch executable file size so large? It seems to me that you want to harm my system.
|
||||
- The thing is that the application is written in Electron, so it also puts chromium, nodejs and some libraries in the exe. Maybe Electron is a temporary solution and in the future I will consider another option
|
||||
|
||||
---
|
||||
|
||||
## 📜 License
|
||||
This project is licensed under the Apache-2.0 - see the [LICENSE](LICENSE.txt) file for details.
|
||||
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 103 KiB |
+13
@@ -28,6 +28,19 @@
|
||||
<div class="log-section">
|
||||
<div class="log-entry info">Waiting for action...</div>
|
||||
</div>
|
||||
|
||||
<footer class="footer">
|
||||
<div>
|
||||
<span id="version-label"></span>
|
||||
<button id="updateBtn" class="hidden">
|
||||
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<svg id="sourceBtn" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="40" height="40" viewBox="0 0 24 24">
|
||||
<path fill="#ffff" d="M10.9,2.1c-4.6,0.5-8.3,4.2-8.8,8.7c-0.5,4.7,2.2,8.9,6.3,10.5C8.7,21.4,9,21.2,9,20.8v-1.6c0,0-0.4,0.1-0.9,0.1 c-1.4,0-2-1.2-2.1-1.9c-0.1-0.4-0.3-0.7-0.6-1C5.1,16.3,5,16.3,5,16.2C5,16,5.3,16,5.4,16c0.6,0,1.1,0.7,1.3,1c0.5,0.8,1.1,1,1.4,1 c0.4,0,0.7-0.1,0.9-0.2c0.1-0.7,0.4-1.4,1-1.8c-2.3-0.5-4-1.8-4-4c0-1.1,0.5-2.2,1.2-3C7.1,8.8,7,8.3,7,7.6C7,7.2,7,6.6,7.3,6 c0,0,1.4,0,2.8,1.3C10.6,7.1,11.3,7,12,7s1.4,0.1,2,0.3C15.3,6,16.8,6,16.8,6C17,6.6,17,7.2,17,7.6c0,0.8-0.1,1.2-0.2,1.4 c0.7,0.8,1.2,1.8,1.2,3c0,2.2-1.7,3.5-4,4c0.6,0.5,1,1.4,1,2.3v2.6c0,0.3,0.3,0.6,0.7,0.5c3.7-1.5,6.3-5.1,6.3-9.3 C22,6.1,16.9,1.4,10.9,2.1z"></path>
|
||||
</svg>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<script src="renderer.js"></script>
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron');
|
||||
const path = require('path');
|
||||
const fs = require("fs");
|
||||
const Unlocker = require("./unlocker");
|
||||
const GitHubUpdater = require("./updater");
|
||||
|
||||
const packageJsonPath = path.join(__dirname, 'package.json');
|
||||
const packageData = require(packageJsonPath)
|
||||
const updater = new GitHubUpdater(packageData.author, packageData.name);
|
||||
|
||||
|
||||
function createWindow() {
|
||||
const win = new BrowserWindow({
|
||||
@@ -20,6 +27,12 @@ function createWindow() {
|
||||
const checkWeModPath = (root) => fs.existsSync(path.join(root, 'WeMod.exe')) &&
|
||||
fs.existsSync(path.join(root, 'resources/app.asar'))
|
||||
|
||||
function log(message, type = 'info') {
|
||||
BrowserWindow.getAllWindows().forEach(win => {
|
||||
win.webContents.send('log', { message, type });
|
||||
});
|
||||
}
|
||||
|
||||
app.whenReady().then(createWindow);
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
@@ -44,6 +57,11 @@ ipcMain.handle('select-file', async () => {
|
||||
return null;
|
||||
});
|
||||
|
||||
ipcMain.handle('apply-patch', async (event, path) => {
|
||||
const unlocker = new Unlocker(path, (e) => log(e))
|
||||
await unlocker.start()
|
||||
})
|
||||
|
||||
ipcMain.handle('resolve-default-path', async () => {
|
||||
const defaultDir = path.join(process.env.LOCALAPPDATA || path.join(process.env.HOME || process.env.USERPROFILE, 'AppData', 'Local'), 'WeMod');
|
||||
|
||||
@@ -89,4 +107,28 @@ ipcMain.handle('start-patch', async (event, filePath) => {
|
||||
message: error.message
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.handle("get-current-version", () => {
|
||||
return packageData.version
|
||||
})
|
||||
|
||||
|
||||
ipcMain.handle("check-updates", async () => {
|
||||
return await updater.checkForUpdates()
|
||||
})
|
||||
|
||||
ipcMain.on('open-link', () => {
|
||||
require('electron').shell.openExternal("https://github.com/k1tbyte/Wemod-Patcher")
|
||||
})
|
||||
|
||||
ipcMain.on("apply-update", async (event, source) => {
|
||||
try {
|
||||
log("Downloading update ...")
|
||||
const path = await updater.downloadUpdate(source)
|
||||
log("Preparation")
|
||||
updater.applyUpdate(path)
|
||||
} catch (err) {
|
||||
log(err, "error")
|
||||
}
|
||||
})
|
||||
@@ -55,6 +55,13 @@ async function patchBySignature(filePath, functionSignature, patchBytes, patchOf
|
||||
if (matchIndex !== -1) {
|
||||
const functionStartPosition = filePosition + matchIndex;
|
||||
|
||||
const checkBuffer = Buffer.alloc(patchBytes.length);
|
||||
await fileHandle.read(checkBuffer, 0, patchBytes.length, functionStartPosition + patchOffset);
|
||||
|
||||
if (Buffer.compare(checkBuffer, Buffer.from(patchBytes)) === 0) {
|
||||
return 0; // Memory already patched
|
||||
}
|
||||
|
||||
// Go to patch position
|
||||
await fileHandle.write(Buffer.from(patchBytes), 0, patchBytes.length, functionStartPosition + patchOffset);
|
||||
|
||||
|
||||
+11
-3
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "wemod-patcher",
|
||||
"version": "1.0.0",
|
||||
"version": "0.0.1",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "electron .",
|
||||
"build": "electron-builder --win portable"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "kitbyte",
|
||||
"author": "k1tbyte",
|
||||
"license": "Apache-2.0",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
@@ -23,14 +23,22 @@
|
||||
"directories": {
|
||||
"output": "dist"
|
||||
},
|
||||
"asar": true,
|
||||
"win": {
|
||||
"icon": "./appicon.ico",
|
||||
"target": [{
|
||||
"target": "portable",
|
||||
"arch": ["x64"]
|
||||
}],
|
||||
"artifactName": "${productName}.exe"
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": false,
|
||||
"perMachine": false,
|
||||
"allowToChangeInstallationDirectory": true,
|
||||
"installerIcon": "./appicon.ico",
|
||||
"uninstallerIcon": "./appicon.ico",
|
||||
"installerHeaderIcon": "./appicon.ico"
|
||||
},
|
||||
"compression": "maximum",
|
||||
"removePackageScripts": true,
|
||||
"removePackageKeywords": true,
|
||||
|
||||
+31
-2
@@ -5,13 +5,22 @@ class PatcherUI {
|
||||
this.selectedPath = '';
|
||||
this.initializeElements();
|
||||
this.bindEvents();
|
||||
|
||||
ipcRenderer.on('log', (event, { message, type }) => {
|
||||
this.addLog(message, type);
|
||||
});
|
||||
}
|
||||
|
||||
initializeElements() {
|
||||
this.filePathInput = document.getElementById('file-path');
|
||||
this.browseBtn = document.getElementById('browse-btn');
|
||||
this.patchBtn = document.getElementById('patch-btn');
|
||||
this.updateBtn = document.getElementById('updateBtn');
|
||||
this.versionLabel = document.getElementById('version-label');
|
||||
this.logSection = document.querySelector('.log-section');
|
||||
document.getElementById(`sourceBtn`).addEventListener('click', () => {
|
||||
ipcRenderer.send("open-link")
|
||||
})
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
@@ -58,8 +67,11 @@ class PatcherUI {
|
||||
this.addLog('Starting patch process...', 'info');
|
||||
|
||||
try {
|
||||
const result = await ipcRenderer.invoke('start-patch', this.selectedPath);
|
||||
this.addLog(result.message, result.success ? 'success' : 'error');
|
||||
await ipcRenderer.invoke(
|
||||
'apply-patch',
|
||||
this.selectedPath
|
||||
);
|
||||
this.addLog("Success", 'success');
|
||||
} catch (error) {
|
||||
this.addLog('Patch failed: ' + error.message, 'error');
|
||||
} finally {
|
||||
@@ -68,12 +80,29 @@ class PatcherUI {
|
||||
}
|
||||
|
||||
resolveDefault() {
|
||||
|
||||
ipcRenderer.invoke("get-current-version").then((v) =>
|
||||
this.versionLabel.textContent = `Current version: ${v}`);
|
||||
|
||||
ipcRenderer.invoke("resolve-default-path").then(path => {
|
||||
this.addLog(path ? 'The WeMod folder has been found!' :
|
||||
"WeMod folder was not found. You need to specify the path manually", path ? "success" : "warning"
|
||||
);
|
||||
this.setPath(path)
|
||||
})
|
||||
|
||||
ipcRenderer.invoke("check-updates").then(result => {
|
||||
if(!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateBtn.className = ""
|
||||
this.updateBtn.textContent = `Update to ${result.version}`
|
||||
this.updateBtn.addEventListener('click', () => {
|
||||
this.updateBtn.className = "hidden"
|
||||
ipcRenderer.send("apply-update", result);
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+38
-2
@@ -15,7 +15,7 @@ body {
|
||||
|
||||
.container {
|
||||
background: #21223d;
|
||||
padding: 24px;
|
||||
padding: 24px 24px 15px;
|
||||
border: 1px solid #1e2039;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
@@ -29,7 +29,6 @@ body {
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.header {
|
||||
@@ -55,6 +54,7 @@ body {
|
||||
.path-section {
|
||||
background: #26254b;
|
||||
padding: 16px;
|
||||
margin-top: 20px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #1e2039;
|
||||
}
|
||||
@@ -97,6 +97,7 @@ body {
|
||||
}
|
||||
|
||||
.patch-btn {
|
||||
margin: 15px 0;
|
||||
background: #6d67fd;
|
||||
color: #f8f6f6;
|
||||
border: none;
|
||||
@@ -149,4 +150,39 @@ body {
|
||||
|
||||
.info {
|
||||
color: #6d67fd;
|
||||
}
|
||||
|
||||
#sourceBtn {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.footer span {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#updateBtn {
|
||||
background: springgreen;
|
||||
color: #1e2039;
|
||||
font-weight: bold;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
border-radius: 10px;
|
||||
margin-left: 10px;
|
||||
padding: 10px 25px;
|
||||
transition: all 0.1s;
|
||||
}
|
||||
|
||||
#updateBtn:hover {
|
||||
scale: 105%
|
||||
}
|
||||
+80
-140
@@ -1,13 +1,13 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const readline = require('readline');
|
||||
const asar = require('asar');
|
||||
const { execSync } = require("child_process");
|
||||
const patchBySignature = require("./memoryScanner");
|
||||
|
||||
|
||||
const regex = /getUserAccount\(\)\{.*?return\s+this\.#\w+\.fetch\(\{.*?}\).*?}/g;
|
||||
const asarPatch = "getUserAccount(){return this.#<fetch_field_name>.fetch({endpoint:\"/v3/account\",method:\"GET\",name:\"/v3/account\",collectMetrics:0}).then(response=>{response.subscription={period:\"yearly\",state:\"active\"};response.flags=78;return response;})}"
|
||||
const signature = "E8 ?? ?? ?? ?? 85 C0 75 ?? F6 C3 01 74 ?? 48 89 F9 E8 ?? ?? ?? ??"
|
||||
const signature = "E8 ?? ?? ?? ?? ?? C0 75 ?? F6 C3 01 74 ?? 48 89 F9 E8 ?? ?? ?? ??"
|
||||
const patchBytes = [0x31]
|
||||
const patchOffset = 0x5
|
||||
// ...
|
||||
@@ -16,157 +16,97 @@ const patchOffset = 0x5
|
||||
// call near ptr funk_1445527E0
|
||||
// ...
|
||||
|
||||
console.log("WeMod unlocker by K1tbyte")
|
||||
let defaultDir = path.join(process.env.LOCALAPPDATA || path.join(process.env.HOME || process.env.USERPROFILE, 'AppData', 'Local'), 'WeMod');
|
||||
let appDir = null
|
||||
class Unlocker {
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
const read = (title, onSubmit, onExit) => {
|
||||
rl.question(title, (answer) => {
|
||||
try {
|
||||
onSubmit(answer)
|
||||
} finally {
|
||||
rl.close()
|
||||
onExit?.()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const checkWeModPaths = (dir) => {
|
||||
return fs.existsSync(dir) && fs.existsSync(path.join(dir, 'WeMod.exe')) && fs.existsSync(path.join(dir, 'resources'))
|
||||
}
|
||||
|
||||
function getFetchFieldName(code) {
|
||||
const match = code.match(/this\.#([a-zA-Z_$][0-9a-zA-Z_$]*)\.fetch/);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
const patchAsar = unpackedPath => {
|
||||
let items = fs.readdirSync(unpackedPath,{ withFileTypes: true })
|
||||
items = items.filter(item => !item.isDirectory() && /^app-\w+/.test(item.name));
|
||||
if(items.length === 0) {
|
||||
console.log(" - No app bundle found")
|
||||
return;
|
||||
constructor(appDir, logger) {
|
||||
this.appDir = appDir;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
let asarPatchApplied = false;
|
||||
for (const item of items) {
|
||||
const data = fs.readFileSync(path.join(unpackedPath, item.name), { encoding: 'utf8'})
|
||||
#getFetchFieldName(code) {
|
||||
const match = code.match(/this\.#([a-zA-Z_$][0-9a-zA-Z_$]*)\.fetch/);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
const matches = data.match(regex)
|
||||
if(!matches) {
|
||||
continue;
|
||||
}
|
||||
if(matches.length > 1) {
|
||||
console.error(" - Multiple target functions found. Looks like the version is not supported")
|
||||
throw new Error("Multiple target functions found");
|
||||
#patchAsar (unpackedPath) {
|
||||
let items = fs.readdirSync(unpackedPath,{ withFileTypes: true })
|
||||
items = items.filter(item => !item.isDirectory() && /^app-\w+/.test(item.name));
|
||||
if(items.length === 0) {
|
||||
throw new Error(" - No app bundle found");
|
||||
}
|
||||
|
||||
const fetchFieldName = getFetchFieldName(matches[0]);
|
||||
if(!fetchFieldName) {
|
||||
console.error(" - Fetch field name not found")
|
||||
throw new Error("Fetch field name not found");
|
||||
}
|
||||
let asarPatchApplied = false;
|
||||
for (const item of items) {
|
||||
const data = fs.readFileSync(path.join(unpackedPath, item.name), { encoding: 'utf8'})
|
||||
|
||||
const patch = asarPatch.replace(/<fetch_field_name>/g, fetchFieldName)
|
||||
|
||||
console.log(" - Found target function in: ", item.name)
|
||||
console.log(" - Patching asar...")
|
||||
fs.writeFileSync(path.join(unpackedPath, item.name), data.replace(regex, patch), {encoding: 'utf8'})
|
||||
console.log(" - Patch applied")
|
||||
asarPatchApplied = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!asarPatchApplied) {
|
||||
throw new Error("Failed to apply patch");
|
||||
}
|
||||
}
|
||||
|
||||
const patchPE = async () => {
|
||||
console.log(" - Patching PE...")
|
||||
const pePath = path.join(appDir, 'WeMod.exe')
|
||||
const procStart = await patchBySignature(pePath, signature, patchBytes, patchOffset)
|
||||
if(procStart === -1) {
|
||||
console.log(" - Signature not found or already patched")
|
||||
return;
|
||||
}
|
||||
console.log(" - Patch saved")
|
||||
}
|
||||
|
||||
const start = async () => {
|
||||
console.log(" - Extracting asar...")
|
||||
try {
|
||||
const asarPath = path.join(appDir, 'resources', 'app.asar')
|
||||
asar.extractAll(asarPath, path.join(appDir, 'resources', 'app.asar.unpacked'))
|
||||
} catch(e) {
|
||||
console.error("Failed to extract asar", e)
|
||||
return;
|
||||
}
|
||||
|
||||
const unpackedPath = path.join(appDir, 'resources', 'app.asar.unpacked')
|
||||
|
||||
fs.renameSync(path.join(appDir, 'resources', 'app.asar'), path.join(appDir, 'resources', 'app.asar.backup'))
|
||||
console.log(" - Backup saved")
|
||||
patchAsar(unpackedPath)
|
||||
|
||||
await asar.createPackageWithOptions(unpackedPath, path.join(appDir, 'resources', 'app.asar'),
|
||||
{ unpack: path.join(unpackedPath,"static/unpacked/**") })
|
||||
console.log(" - Patch saved")
|
||||
|
||||
await patchPE()
|
||||
console.log("Done!")
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const prepare = async () => {
|
||||
console.log(" - Path: ", appDir || "Not found")
|
||||
|
||||
if (!appDir) {
|
||||
read("WeMod directory not found. Enter the path manually: ", (answer) => {
|
||||
if (checkWeModPaths(answer)) {
|
||||
appDir = answer;
|
||||
return;
|
||||
const matches = data.match(regex)
|
||||
if(!matches) {
|
||||
continue;
|
||||
}
|
||||
if(matches.length > 1) {
|
||||
throw new Error(" - Multiple target functions found. Looks like the version is not supported");
|
||||
}
|
||||
console.log("Invalid path")
|
||||
}, prepare);
|
||||
return;
|
||||
}
|
||||
|
||||
read("Continue? (y/n): ", (answer) => {
|
||||
if(answer.toLowerCase() === 'y') {
|
||||
start()
|
||||
}
|
||||
})
|
||||
}
|
||||
const fetchFieldName = this.#getFetchFieldName(matches[0]);
|
||||
if(!fetchFieldName) {
|
||||
throw new Error(" - Fetch field name not found");
|
||||
}
|
||||
|
||||
if(fs.existsSync(defaultDir)) {
|
||||
const items = fs.readdirSync(defaultDir, { withFileTypes: true });
|
||||
const appFolders = items
|
||||
.filter(item => item.isDirectory() && /^app-\w+/.test(item.name))
|
||||
.map(item => {
|
||||
const folderPath = path.join(defaultDir, item.name);
|
||||
const stats = fs.statSync(folderPath);
|
||||
return {
|
||||
name: item.name,
|
||||
path: folderPath,
|
||||
mtime: stats.mtime,
|
||||
};
|
||||
});
|
||||
const patch = asarPatch.replace(/<fetch_field_name>/g, fetchFieldName)
|
||||
|
||||
appFolders.sort((a, b) => b.mtime - a.mtime);
|
||||
for(const folder of appFolders) {
|
||||
if(checkWeModPaths(folder.path)) {
|
||||
appDir = folder.path;
|
||||
this.logger(" - Found target function in: " + item.name)
|
||||
this.logger(" - Patching asar...")
|
||||
fs.writeFileSync(path.join(unpackedPath, item.name), data.replace(regex, patch), {encoding: 'utf8'})
|
||||
this.logger(" - Patch applied")
|
||||
asarPatchApplied = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!asarPatchApplied) {
|
||||
throw new Error("Failed to apply patch");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async #patchPE () {
|
||||
this.logger(" - Patching PE...")
|
||||
const pePath = path.join(this.appDir, 'WeMod.exe')
|
||||
const procStart = await patchBySignature(pePath, signature, patchBytes, patchOffset)
|
||||
if(procStart === -1) {
|
||||
throw new Error(" - Signature not found or already patched")
|
||||
}
|
||||
|
||||
this.logger(procStart === 0 ? " - PE already patched" : " - Patch saved")
|
||||
}
|
||||
|
||||
async start () {
|
||||
this.logger(" - Extracting asar...")
|
||||
const asarPath = path.join(this.appDir, 'resources', 'app.asar')
|
||||
const unpackedPath = path.join(this.appDir, 'resources', 'app.asar.unpacked')
|
||||
const backupPath = path.join(this.appDir, 'resources', 'app.asar.backup')
|
||||
|
||||
if(fs.existsSync(backupPath)) {
|
||||
this.logger(" - Backup already exists")
|
||||
} else {
|
||||
execSync(`copy "${asarPath}" "${backupPath}"`, { encoding: "utf-8" });
|
||||
this.logger(" - Backup saved")
|
||||
}
|
||||
|
||||
try {
|
||||
asar.extractAll(asarPath, unpackedPath)
|
||||
} catch(e) {
|
||||
throw new Error("Failed to extract asar: " + e)
|
||||
}
|
||||
|
||||
this.#patchAsar(unpackedPath)
|
||||
|
||||
await asar.createPackageWithOptions(unpackedPath, asarPath,
|
||||
{ unpack: path.join(unpackedPath,"static/unpacked/**") })
|
||||
this.logger(" - Patch saved")
|
||||
|
||||
await this.#patchPE()
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await prepare();
|
||||
})();
|
||||
module.exports = Unlocker;
|
||||
|
||||
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
const { app } = require("electron");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { exec } = require("child_process");
|
||||
|
||||
class GitHubUpdater {
|
||||
constructor(owner, repo, currentVersion) {
|
||||
this.owner = owner;
|
||||
this.repo = repo;
|
||||
this.currentVersion = currentVersion || app.getVersion();
|
||||
this.apiUrl = `https://api.github.com/repos/${owner}/${repo}/releases/latest`;
|
||||
this.downloadDir = path.join(app.getPath("temp"), "update");
|
||||
}
|
||||
|
||||
async checkForUpdates() {
|
||||
const response = await fetch(this.apiUrl, {
|
||||
headers: { "User-Agent": "GitHub-Updater" },
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`An error occurred while checking for an update: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const release = await response.json();
|
||||
const latestVersion = release.tag_name;
|
||||
const assets = release.assets;
|
||||
|
||||
if (this.currentVersion === latestVersion) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const asset = assets.find(a => a.name.endsWith(".exe"));
|
||||
if (!asset) {
|
||||
throw new Error("Unable to find files to update");
|
||||
}
|
||||
|
||||
return {
|
||||
version: latestVersion,
|
||||
url: asset.browser_download_url,
|
||||
name: asset.name,
|
||||
};
|
||||
}
|
||||
|
||||
async downloadUpdate(updateInfo) {
|
||||
const response = await fetch(updateInfo.url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error downloading file: ${response.statusText}`);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(this.downloadDir)) {
|
||||
fs.mkdirSync(this.downloadDir, { recursive: true });
|
||||
}
|
||||
|
||||
const filePath = path.join(this.downloadDir, updateInfo.name);
|
||||
const fileStream = fs.createWriteStream(filePath);
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
const downloadStream = response.body.getReader();
|
||||
|
||||
const pump = async () => {
|
||||
try {
|
||||
while (true) {
|
||||
const { done, value } = await downloadStream.read();
|
||||
if (done) {
|
||||
fileStream.end();
|
||||
resolve();
|
||||
break;
|
||||
}
|
||||
fileStream.write(value);
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
fileStream.on("error", (error) => {
|
||||
reject(new Error(`File write error: ${error.message}`));
|
||||
});
|
||||
|
||||
pump();
|
||||
});
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
async applyUpdate(filePath) {
|
||||
try {
|
||||
const currentExecutable = process.env.PORTABLE_EXECUTABLE_FILE;
|
||||
const updateScript = `Start-Sleep -Seconds 3; Copy-Item -Path '${filePath}' -Destination '${currentExecutable}' -Force; Remove-Item -Path '${filePath}' -Force; Start-Sleep -Seconds 2; Start-Process -FilePath '${currentExecutable}';`;
|
||||
|
||||
exec(`start /b "" powershell -WindowStyle Hidden -Command "${updateScript}"`, {
|
||||
windowsHide: true,
|
||||
stdio: 'ignore'
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
app.quit();
|
||||
}, 1000);
|
||||
|
||||
} catch (error) {
|
||||
throw new Error(`Update failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GitHubUpdater;
|
||||
Reference in New Issue
Block a user