diff --git a/index.html b/index.html index d869802..7d0c8cf 100644 --- a/index.html +++ b/index.html @@ -2,9 +2,34 @@ - Title + WeMod Patcher + - Setup with electron +
+
+
+

WeMod Patcher

+

Select the folder with the WeMod version like: “app-X.XX.X” and click patch

+
+ +
+ +
+ + +
+
+ + + +
+
Waiting for action...
+
+
+
+ \ No newline at end of file diff --git a/index.js b/index.js index d4c922b..f37e483 100644 --- a/index.js +++ b/index.js @@ -1,26 +1,92 @@ -const { app, BrowserWindow } = require('electron') -const path = require('path') +const { app, BrowserWindow, ipcMain, dialog } = require('electron'); +const path = require('path'); +const fs = require("fs"); -app.disableHardwareAcceleration() -app.commandLine.appendSwitch('disable-http-cache') - -function createWindow () { +function createWindow() { const win = new BrowserWindow({ width: 800, height: 600, + resizable: false, + autoHideMenuBar: true, webPreferences: { nodeIntegration: true, - contextIsolation: false, - spellcheck: false - }, - autoHideMenuBar: true - }) + contextIsolation: false + } + }); - win.loadFile('index.html') + win.loadFile('index.html'); } -app.whenReady().then(createWindow) +const checkWeModPath = (root) => fs.existsSync(path.join(root, 'WeMod.exe')) && + fs.existsSync(path.join(root, 'resources/app.asar')) + +app.whenReady().then(createWindow); app.on('window-all-closed', () => { - app.quit() -}) \ No newline at end of file + if (process.platform !== 'darwin') { + app.quit(); + } +}); + +ipcMain.handle('select-file', async () => { + const result = await dialog.showOpenDialog({ + properties: ["openDirectory"], + defaultPath: process.env.LOCALAPPDATA || path.join(process.env.HOME || process.env.USERPROFILE, 'AppData', 'Local') + }); + + if (!result.canceled && result.filePaths.length > 0) { + return { + filePath: result.filePaths[0], + fileName: path.basename(result.filePaths[0]), + valid: checkWeModPath(result.filePaths[0]) + }; + } + return null; +}); + +ipcMain.handle('resolve-default-path', async () => { + const defaultDir = path.join(process.env.LOCALAPPDATA || path.join(process.env.HOME || process.env.USERPROFILE, 'AppData', 'Local'), 'WeMod'); + + if (!fs.existsSync(defaultDir)) { + return null; + } + + 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, + }; + }); + + let appDir = null; + appFolders.sort((a, b) => b.mtime - a.mtime); + for (const folder of appFolders) { + if (checkWeModPath(folder.path)) { + appDir = folder.path; + break; + } + } + + return appDir; +}) + +ipcMain.handle('start-patch', async (event, filePath) => { + try { + // stub + return { + success: true, + message: 'Patch completed successfully!' + }; + } catch (error) { + return { + success: false, + message: error.message + }; + } +}); \ No newline at end of file diff --git a/renderer.js b/renderer.js new file mode 100644 index 0000000..31b5411 --- /dev/null +++ b/renderer.js @@ -0,0 +1,83 @@ +const { ipcRenderer } = require('electron'); + +class PatcherUI { + constructor() { + this.selectedPath = ''; + this.initializeElements(); + this.bindEvents(); + } + + initializeElements() { + this.filePathInput = document.getElementById('file-path'); + this.browseBtn = document.getElementById('browse-btn'); + this.patchBtn = document.getElementById('patch-btn'); + this.logSection = document.querySelector('.log-section'); + } + + bindEvents() { + this.browseBtn.addEventListener('click', () => this.selectFile()); + this.patchBtn.addEventListener('click', () => this.startPatch()); + } + + setPath(path) { + this.filePathInput.value = this.selectedPath = path + this.patchBtn.disabled = !path; + } + + async selectFile() { + try { + const result = await ipcRenderer.invoke('select-file'); + if (!result?.filePath) { + return; + } + + if(!result.valid) { + this.addLog(`The folder “${result.filePath}” is not recognized as a Wemod directory`, 'error') + return; + } + + this.setPath(result.filePath) + this.addLog('Folder selected: ' + result.fileName, 'info'); + } catch (error) { + this.addLog('Error selecting file: ' + error.message, 'error'); + } + } + + addLog(message, type = 'info') { + const entry = document.createElement('div'); + entry.className = `log-entry ${type}`; + entry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`; + this.logSection.appendChild(entry); + this.logSection.scrollTop = this.logSection.scrollHeight; + } + + async startPatch() { + if (!this.selectedPath) return; + + this.patchBtn.disabled = true; + this.addLog('Starting patch process...', 'info'); + + try { + const result = await ipcRenderer.invoke('start-patch', this.selectedPath); + this.addLog(result.message, result.success ? 'success' : 'error'); + } catch (error) { + this.addLog('Patch failed: ' + error.message, 'error'); + } finally { + this.patchBtn.disabled = false; + } + } + + resolveDefault() { + 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) + }) + } +} + +window.addEventListener('DOMContentLoaded', async() => { + const ui = new PatcherUI(); + await ui.resolveDefault() +}); \ No newline at end of file diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..f4cfe48 --- /dev/null +++ b/styles.css @@ -0,0 +1,152 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; +} + +body { + background: #191a33; + color: #beb2b2; + padding: 0 50px 0 50px; + height: 100vh; + display: flex; +} + +.container { + background: #21223d; + padding: 24px; + border: 1px solid #1e2039; + height: 100%; + width: 100%; + max-width: 1000px; + margin: auto; + gap: 24px; +} + +.main-section { + display: flex; + height: 100%; + justify-content: center; + flex-direction: column; + gap: 20px; +} + +.header { + display: flex; + flex-direction: column; +} + +.header h1 { + font-size: 26px; + margin-bottom: 8px; + color: #6d67fd; + background: #3a3881; + border-radius: 10px; + padding: 5px 10px; + width: fit-content; +} + +.header p { + color: #beb2b2; + font-size: 13px; +} + +.path-section { + background: #26254b; + padding: 16px; + border-radius: 6px; + border: 1px solid #1e2039; +} + +.path-section label { + display: block; + margin-bottom: 8px; + font-size: 13px; + color: #f8f6f6; +} + +.path-input-container { + display: flex; + gap: 8px; +} + +.path-input { + flex: 1; + background: #21223d; + color: #beb2b2; + padding: 8px 12px; + border: 1px solid #6d67fd; + border-radius: 4px; + font-size: 14px; +} + +.browse-btn { + background: #6d67fd; + border: none; + padding: 8px 16px; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + color: #f8f6f6; + transition: background 0.2s; +} + +.browse-btn:hover { + background: #6d67fd; +} + +.patch-btn { + background: #6d67fd; + color: #f8f6f6; + border: none; + padding: 12px 24px; + border-radius: 4px; + font-size: 16px; + font-weight: 500; + cursor: pointer; + transition: all 0.2s; +} + +.patch-btn:hover:enabled { + scale: 105%; +} + +.patch-btn:disabled { + background: #3a3881; + cursor: not-allowed; + opacity: 0.7; +} + +.log-section { + background: #26254b; + color: #beb2b2; + padding: 16px; + border-radius: 6px; + height: 100%; + font-family: monospace; + font-size: 14px; + overflow-y: auto; + border: 1px solid #1e2039; +} + +.log-entry { + margin-bottom: 8px; + line-height: 1.4; +} + +.success { + color: #08fc81; +} + +.error { + color: #f04343; +} + +.warning { + color: #facc15; +} + +.info { + color: #6d67fd; +} \ No newline at end of file