182 lines
5.2 KiB
JavaScript
182 lines
5.2 KiB
JavaScript
|
const { app, BrowserWindow, ipcMain, dialog } = require('electron');
|
||
|
const fs = require('fs');
|
||
|
const path = require('path');
|
||
|
const AdmZip = require('adm-zip'); // For ZIP extraction
|
||
|
const { promisify } = require('util');
|
||
|
const readdir = promisify(fs.readdir);
|
||
|
const stat = promisify(fs.stat);
|
||
|
const { exec } = require('child_process');
|
||
|
|
||
|
let mainWindow;
|
||
|
|
||
|
function createWindow() {
|
||
|
mainWindow = new BrowserWindow({
|
||
|
width: 1000,
|
||
|
height: 800,
|
||
|
webPreferences: {
|
||
|
nodeIntegration: true,
|
||
|
contextIsolation: false,
|
||
|
webviewTag: true, // Make sure webview is enabled
|
||
|
}
|
||
|
});
|
||
|
|
||
|
mainWindow.loadFile('index.html');
|
||
|
}
|
||
|
|
||
|
app.whenReady().then(() => {
|
||
|
createWindow();
|
||
|
|
||
|
app.on('activate', () => {
|
||
|
if (BrowserWindow.getAllWindows().length === 0) {
|
||
|
createWindow();
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
|
||
|
app.on('window-all-closed', () => {
|
||
|
if (process.platform !== 'darwin') {
|
||
|
app.quit();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
|
||
|
// Function to download and extract ZIP files
|
||
|
ipcMain.handle('download-and-extract', async (event, downloadUrl, libraryFolder, fileName) => {
|
||
|
try {
|
||
|
const downloadPath = path.join(libraryFolder, fileName);
|
||
|
|
||
|
// Start downloading the game from the URL
|
||
|
const response = await fetch(downloadUrl);
|
||
|
if (!response.ok) throw new Error('Download failed');
|
||
|
|
||
|
const buffer = await response.arrayBuffer();
|
||
|
const fileBuffer = Buffer.from(buffer);
|
||
|
|
||
|
// Write the downloaded zip file to disk
|
||
|
fs.writeFileSync(downloadPath, fileBuffer);
|
||
|
|
||
|
// Now extract the downloaded ZIP
|
||
|
await extractZip(downloadPath, libraryFolder);
|
||
|
|
||
|
// Return the path to the extracted folder
|
||
|
return { success: true, folderPath: libraryFolder };
|
||
|
|
||
|
} catch (error) {
|
||
|
console.error('Error during download or extraction:', error);
|
||
|
return { success: false, error: error.message };
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Function to extract ZIP files
|
||
|
async function extractZip(zipPath, extractToFolder) {
|
||
|
try {
|
||
|
const zip = new AdmZip(zipPath);
|
||
|
zip.extractAllTo(extractToFolder, true);
|
||
|
console.log(`Extracted ZIP to ${extractToFolder}`);
|
||
|
|
||
|
// Clean up the ZIP file after extraction
|
||
|
fs.unlinkSync(zipPath); // Delete the ZIP file
|
||
|
} catch (error) {
|
||
|
console.error('Error extracting ZIP:', error);
|
||
|
throw new Error('Error extracting ZIP');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Scan for .exe files in the Library folder (excluding `redist` folders)
|
||
|
ipcMain.handle('scan-library', async (event, libraryFolder) => {
|
||
|
try {
|
||
|
const gameExecutables = await scanForExecutables(libraryFolder);
|
||
|
return gameExecutables;
|
||
|
} catch (error) {
|
||
|
console.error('Error scanning library:', error);
|
||
|
return [];
|
||
|
}
|
||
|
});
|
||
|
|
||
|
|
||
|
async function scanForExecutables(directory) {
|
||
|
console.log(directory.exeFiles)
|
||
|
return directory.exeFiles;
|
||
|
}
|
||
|
|
||
|
module.exports = { scanForExecutables };
|
||
|
// Handle the Wine binary selection
|
||
|
ipcMain.handle('select-wine-binary', async () => {
|
||
|
const result = await dialog.showOpenDialog({
|
||
|
properties: ['openFile'],
|
||
|
filters: [{ name: 'Executables', extensions: ['exe'] }],
|
||
|
});
|
||
|
|
||
|
if (result.canceled || result.filePaths.length === 0) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
winePath = result.filePaths[0]; // Store the path to the Wine binary
|
||
|
return winePath;
|
||
|
});
|
||
|
|
||
|
// Handle the selection of a Library folder and get list of `.exe` files
|
||
|
ipcMain.handle('select-library-folder', async () => {
|
||
|
const result = await dialog.showOpenDialog({
|
||
|
properties: ['openDirectory'],
|
||
|
});
|
||
|
|
||
|
if (result.canceled || result.filePaths.length === 0) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
const libraryFolderPath = result.filePaths[0];
|
||
|
const exeFiles = await scanDirectory(libraryFolderPath); // Scan the folder for .exe files
|
||
|
return { libraryFolderPath, exeFiles };
|
||
|
});
|
||
|
|
||
|
// Recursive function to scan a directory for `.exe` files
|
||
|
async function scanDirectory(dirPath) {
|
||
|
const exeFiles = [];
|
||
|
const files = await fs.promises.readdir(dirPath, { withFileTypes: true });
|
||
|
|
||
|
for (const file of files) {
|
||
|
const fullPath = path.join(dirPath, file.name);
|
||
|
|
||
|
if (file.isDirectory()) {
|
||
|
// Recursively scan subdirectories
|
||
|
const subDirFiles = await scanDirectory(fullPath);
|
||
|
exeFiles.push(...subDirFiles);
|
||
|
} else if (file.isFile() && file.name.toLowerCase().endsWith('.exe')) {
|
||
|
// Add `.exe` files to the list
|
||
|
exeFiles.push(fullPath);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return exeFiles;
|
||
|
}
|
||
|
|
||
|
// Handle running the game via Wine
|
||
|
ipcMain.handle('run-game-with-wine', async (event, gameExePath) => {
|
||
|
if (!winePath) {
|
||
|
return { success: false, message: 'Wine binary not selected' };
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
await runWineCommand(winePath, gameExePath);
|
||
|
return { success: true };
|
||
|
} catch (error) {
|
||
|
return { success: false, message: error.message };
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Helper function to execute Wine command
|
||
|
function runWineCommand(wineBinary, gameExePath) {
|
||
|
return new Promise((resolve, reject) => {
|
||
|
const command = `"${wineBinary}" "${gameExePath}"`;
|
||
|
|
||
|
exec(command, (error, stdout, stderr) => {
|
||
|
if (error) {
|
||
|
reject(new Error(`Error running Wine command: ${stderr || stdout}`));
|
||
|
} else {
|
||
|
resolve(stdout);
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
}
|