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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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