From 7be4bfa907e3b6b604a2177814785cfb3d696dd6 Mon Sep 17 00:00:00 2001 From: Nekiro Date: Thu, 20 Jan 2022 12:55:28 +0100 Subject: [PATCH] Refactor code --- App.ts | 3 +- README.md | 3 +- package.json | 6 +- src/Main.ts | 22 +++--- src/RccLib.ts | 176 ++++++++++++++++++++++++++++++++++++++++++++ src/ipc/main.ts | 6 +- src/ipc/renderer.ts | 38 +++++----- src/reader.ts | 169 ------------------------------------------ 8 files changed, 212 insertions(+), 211 deletions(-) create mode 100644 src/RccLib.ts delete mode 100644 src/reader.ts diff --git a/App.ts b/App.ts index 605c49a..7cdf92c 100644 --- a/App.ts +++ b/App.ts @@ -1,4 +1,3 @@ -import { app, BrowserWindow } from 'electron'; import Main from './src/Main'; -Main.main(app, BrowserWindow); +Main.main(); diff --git a/README.md b/README.md index cd705d8..fd0c6d7 100644 --- a/README.md +++ b/README.md @@ -16,12 +16,11 @@ Languages: html, typescript, css ## Installation Nekiro's Rcc Editor requires [Node.js](https://nodejs.org/) to run. -Install the dependencies and start the main script. +Install the dependencies and start. ```sh cd nekiro-rcc-editor npm i -npm run compile npm run start ``` diff --git a/package.json b/package.json index c1d4862..3c49e83 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,10 @@ "description": "edit qt .rcc files", "main": "build/App.js", "scripts": { - "start": "electron-forge start", + "start": "npm run compile && electron-forge start", + "build": "npm run compile && electron-forge make", "compile": "tsc && copyfiles src/html/*.html src/css/*.css src/assets/* src/rcc/rcc.exe build", - "package": "electron-forge package", - "build": "npm run compile && electron-forge make" + "package": "electron-forge package" }, "author": "Nekiro", "license": "MIT", diff --git a/src/Main.ts b/src/Main.ts index 3c80cf3..e202cf0 100644 --- a/src/Main.ts +++ b/src/Main.ts @@ -1,12 +1,11 @@ import { BrowserWindow, Menu, dialog, shell, app } from 'electron'; -import { loadRcc, saveRcc, extractToPng } from './reader'; import path from 'path'; -require('./ipc/main'); +import RccLib from './RccLib'; +import './ipc/Main'; export default class Main { static mainWindow: Electron.BrowserWindow; static application: Electron.App; - static BrowserWindow: any; private static onWindowAllClosed() { if (process.platform !== 'darwin') { @@ -15,7 +14,7 @@ export default class Main { } private static createWindow() { - Main.mainWindow = new Main.BrowserWindow({ + Main.mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { @@ -41,7 +40,7 @@ export default class Main { }); if (!result.canceled) { - loadRcc(result.filePaths.pop() ?? null); + RccLib.loadRcc(result.filePaths.pop()); } }, }, @@ -54,13 +53,13 @@ export default class Main { }); if (!result.canceled) { - extractToPng(result.filePaths.pop() ?? null); + RccLib.extractToPng(result.filePaths.pop()); } }, }, { label: 'Save rcc', - click: () => saveRcc(), + click: () => RccLib.saveRcc(), }, { label: 'Save rcc as', @@ -70,7 +69,7 @@ export default class Main { }); if (!result.canceled) { - saveRcc(result.filePath); + RccLib.saveRcc(result.filePath); } }, }, @@ -95,13 +94,12 @@ export default class Main { Main.mainWindow.show(); }); - //Main.mainWindow.webContents.openDevTools(); + Main.mainWindow.webContents.openDevTools(); } - static main(app: Electron.App, browserWindow: typeof BrowserWindow) { - if (require('electron-squirrel-startup')) return; + static main() { + if (require('electron-squirrel-startup')) return; // fixes launching app during installation/uninstallation - Main.BrowserWindow = browserWindow; Main.application = app; Main.application.on('window-all-closed', Main.onWindowAllClosed); Main.application.on('ready', Main.onReady); diff --git a/src/RccLib.ts b/src/RccLib.ts new file mode 100644 index 0000000..16ec0fa --- /dev/null +++ b/src/RccLib.ts @@ -0,0 +1,176 @@ +import { app, dialog } from 'electron'; +import path from 'path'; +import util from 'util'; +const execFile = util.promisify(require('child_process').execFile); +import fs from 'fs-extra'; +import Main from './Main'; +import { Image } from './Types'; + +const imageExt: Array = ['.png', '.jpg']; + +// TODO: refactor this method +const getFiles = async (path = './') => { + const entries = await fs.readdir(path, { withFileTypes: true }); + const files = entries + .filter((file: any) => !file.isDirectory()) + .map((file: any) => ({ ...file, path: path + file.name })); + + const folders = entries.filter((folder: any) => folder.isDirectory()); + for (const folder of folders) { + files.push(...(await getFiles(`${path}/${folder.name}/`))); + } + return files; +}; + +export default class RccLib { + static images: Image[] = []; + static loadedFilePath: string | undefined = undefined; + static localPath: string = path.resolve( + app.isPackaged ? process.resourcesPath : __dirname, + 'rcc' + ); + + public static async loadRcc(filePath?: string): Promise { + if (!filePath) { + return; + } + + // clear previous images + this.images = []; + + // delete res directory + if (fs.existsSync(path.join(this.localPath, 'qresource'))) { + await fs.rmdir(path.join(this.localPath, 'qresource'), { + recursive: true, + }); + } + + await fs.copyFile(filePath, path.join(this.localPath, 'res.rcc')); + + await execFile(path.join(this.localPath, 'rcc.exe'), ['--reverse'], { + cwd: `${this.localPath}/`, + }); + + // get directory content + const files: any[] = await getFiles( + path.join(this.localPath, 'qresource', 'res', 'res.rcc') + ); + + for (const file of files) { + this.images.push({ + name: path.parse(file.name).name, + path: path.relative( + path.join(this.localPath, 'qresource', 'res', 'res.rcc'), + file.path + ), + isImage: imageExt.includes(path.extname(file.path)), + data: Buffer.from(await fs.readFile(file.path, 'binary'), 'binary'), + }); + } + + // sort by name + this.images.sort((a, b) => a.name.localeCompare(b.name)); + + // cleanup + await fs.rmdir(path.join(this.localPath, 'qresource'), { recursive: true }); + await fs.rm(path.join(this.localPath, 'res.rcc')); + + this.loadedFilePath = filePath; + + Main.mainWindow.webContents.send('populate-list', this.images); + } + + public static async extractToPng(directoryPath?: string): Promise { + if (!directoryPath || !this.images.length) { + dialog.showErrorBox('Error', 'Nothing to extract.'); + return; + } + + for (const image of this.images) { + if (image.isImage) { + await fs.outputFile( + path.join(directoryPath as string, image.path), + image.data + ); + } + } + + dialog.showMessageBox(Main.mainWindow, { + message: `Png images extracted successfully. Extracted ${this.images.length} images.`, + type: 'info', + }); + } + + public static async saveRcc( + filePath: string | undefined = this.loadedFilePath + ): Promise { + if (!filePath || this.images.length === 0) { + return; + } + + // create .qrc file + let data = '\n\n'; + + for (const image of this.images) { + data += `${image.path}\n`; + } + + data += '\n'; + + await fs.outputFile(path.join(this.localPath, 'res', 'res.qrc'), data); + + // dump images + for (const image of this.images) { + await fs.outputFile( + path.join(this.localPath, 'res', image.path), + image.data + ); + } + + await execFile( + path.join(this.localPath, 'rcc.exe'), + [ + '--format-version', + '1', + '--binary', + './res/res.qrc', + '-o', + './res/res_output.rcc', + ], + { + cwd: `${this.localPath}/`, + } + ); + + await fs.move(path.join(this.localPath, '/res/res_output.rcc'), filePath, { + overwrite: true, + }); + + // cleanup + await fs.rmdir(path.join(this.localPath, 'res'), { + recursive: true, + }); + + dialog.showMessageBox(Main.mainWindow, { + message: 'Rcc saved successfully.', + type: 'info', + }); + } + + public static async replaceImage( + index: number, + filePath: string + ): Promise { + const image = this.images[index]; + if (!image) { + return null; + } + + image.data = Buffer.from(await fs.readFile(filePath, 'binary'), 'binary'); + return image.data; + } + + public static getImageByIndex(index: number): Buffer { + return this.images[index]?.data; + } +} diff --git a/src/ipc/main.ts b/src/ipc/main.ts index 316f372..1ae9c85 100644 --- a/src/ipc/main.ts +++ b/src/ipc/main.ts @@ -1,12 +1,12 @@ const { ipcMain } = require('electron'); -import { getImageByIndex, replaceImage } from '../reader'; +import RccLib from '../RccLib'; ipcMain.on('get-image-data', (event: Electron.IpcMainEvent, index: number) => { - event.reply('update-preview', getImageByIndex(index)); + event.reply('update-preview', RccLib.getImageByIndex(index)); }); ipcMain.on('replace-image', async (event: Electron.IpcMainEvent, obj) => { - const data = await replaceImage(obj.index, obj.path); + const data = await RccLib.replaceImage(obj.index, obj.path); if (data) { event.reply('update-preview', data); event.reply('update-miniature', { index: obj.index, data }); diff --git a/src/ipc/renderer.ts b/src/ipc/renderer.ts index c24cc08..29d0c8d 100644 --- a/src/ipc/renderer.ts +++ b/src/ipc/renderer.ts @@ -1,35 +1,33 @@ //renderer process import { ipcRenderer } from 'electron'; -import { ImageBuffer } from '../types'; +import { ImageBuffer } from '../Types'; const preview: any = document.querySelector('.preview'); const list: any = document.querySelector('.list'); let focused: any = null; -if (preview) { - preview.addEventListener('drop', (event: any) => { - event.preventDefault(); - event.stopPropagation(); +preview?.addEventListener('drop', (event: any) => { + event.preventDefault(); + event.stopPropagation(); - if (!focused) { - return; - } + if (!focused) { + return; + } - ipcRenderer.send('replace-image', { - index: parseInt(focused.id.split('-')[1], 10), - path: event.dataTransfer.files[0].path, - }); + ipcRenderer.send('replace-image', { + index: parseInt(focused.id.split('-')[1], 10), + path: event.dataTransfer.files[0].path, }); +}); - preview.addEventListener('dragover', (event: any) => { - event.preventDefault(); - event.stopPropagation(); - }); -} +preview?.addEventListener('dragover', (event: any) => { + event.preventDefault(); + event.stopPropagation(); +}); ipcRenderer.on( 'update-preview', - (event: Electron.IpcRendererEvent, data: Buffer) => { + (_: Electron.IpcRendererEvent, data: Buffer) => { if (preview) { preview.src = `data:image/png;base64,${Buffer.from(data).toString( 'base64' @@ -40,7 +38,7 @@ ipcRenderer.on( ipcRenderer.on( 'update-miniature', - (event: Electron.IpcRendererEvent, { index, data }: ImageBuffer) => { + (_: Electron.IpcRendererEvent, { index, data }: ImageBuffer) => { list.querySelector( `#btn-${index} > img` ).src = `data:image/png;base64,${Buffer.from(data).toString('base64')}`; @@ -49,7 +47,7 @@ ipcRenderer.on( ipcRenderer.on( 'populate-list', - (event: Electron.IpcRendererEvent, images: any[]) => { + (_: Electron.IpcRendererEvent, images: any[]) => { focused = null; while (list.firstChild) { diff --git a/src/reader.ts b/src/reader.ts deleted file mode 100644 index 590e11f..0000000 --- a/src/reader.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { dialog, BrowserWindow, app } from 'electron'; -import path from 'path'; -import util from 'util'; -const execFile = util.promisify(require('child_process').execFile); -import fs from 'fs-extra'; -import Main from './Main'; -import { Image } from './types'; - -let loadedFilePath: string | null = null; -let images: Image[] = []; - -const imageExt: Array = ['.png', '.jpg']; - -const resourcePath: string = app.isPackaged ? process.resourcesPath : __dirname; -const localPath: string = path.resolve(resourcePath, 'rcc'); - -const getFiles = async (path = './') => { - const entries = await fs.readdir(path, { withFileTypes: true }); - const files = entries - .filter((file: any) => !file.isDirectory()) - .map((file: any) => ({ ...file, path: path + file.name })); - - const folders = entries.filter((folder: any) => folder.isDirectory()); - for (const folder of folders) { - files.push(...(await getFiles(`${path}/${folder.name}/`))); - } - return files; -}; - -export async function loadRcc(filePath: string | null): Promise { - if (!filePath) { - return; - } - - // clear previous images - images = []; - - // delete res directory - if (fs.existsSync(path.join(localPath, 'qresource'))) { - await fs.rmdir(path.join(localPath, 'qresource'), { recursive: true }); - } - - await fs.copyFile(filePath, path.join(localPath, 'res.rcc')); - - await execFile(path.join(localPath, 'rcc.exe'), ['--reverse'], { - cwd: `${localPath}/`, - }); - - // get directory content - const files: any[] = await getFiles( - path.join(localPath, 'qresource', 'res', 'res.rcc') - ); - - for (const file of files) { - images.push({ - name: path.parse(file.name).name, - path: path.relative( - path.join(localPath, 'qresource', 'res', 'res.rcc'), - file.path - ), - isImage: imageExt.includes(path.extname(file.path)), - data: Buffer.from(await fs.readFile(file.path, 'binary'), 'binary'), - }); - } - - // sort by name - images.sort((a, b) => a.name.localeCompare(b.name)); - - // cleanup - await fs.rmdir(path.join(localPath, 'qresource'), { recursive: true }); - await fs.rm(path.join(localPath, 'res.rcc')); - - loadedFilePath = filePath; - - BrowserWindow.getAllWindows()[0].webContents.send('populate-list', images); -} - -export async function extractToPng( - directoryPath: string | null -): Promise { - if (!images.length) { - dialog.showErrorBox('Error', 'Nothing to extract.'); - return; - } - - for (const image of images) { - if (image.isImage) { - await fs.outputFile( - path.join(directoryPath as string, image.path), - image.data - ); - } - } - - dialog.showMessageBox(Main.mainWindow, { - message: `Png images extracted successfully. Extracted ${images.length} images.`, - type: 'info', - }); -} - -export async function saveRcc( - filePath: string | null = loadedFilePath -): Promise { - if (!filePath || images.length === 0) { - return; - } - - // create .qrc file - let data = '\n\n'; - - for (const image of images) { - data += `${image.path}\n`; - } - - data += '\n'; - - await fs.outputFile(path.join(localPath, 'res', 'res.qrc'), data); - - // dump images - for (const image of images) { - await fs.outputFile(path.join(localPath, 'res', image.path), image.data); - } - - await execFile( - path.join(localPath, 'rcc.exe'), - [ - '--format-version', - '1', - '--binary', - './res/res.qrc', - '-o', - './res/res_output.rcc', - ], - { - cwd: `${localPath}/`, - } - ); - - await fs.move(path.join(localPath, '/res/res_output.rcc'), filePath, { - overwrite: true, - }); - - // cleanup - await fs.rmdir(path.join(localPath, 'res'), { - recursive: true, - }); - - dialog.showMessageBox(Main.mainWindow, { - message: 'Rcc saved successfully.', - type: 'info', - }); -} - -export async function replaceImage( - index: number, - filePath: string -): Promise { - const image = images[index]; - if (!image) { - return null; - } - - image.data = Buffer.from(await fs.readFile(filePath, 'binary'), 'binary'); - return image.data; -} - -export function getImageByIndex(index: number): Buffer { - return images[index]?.data; -}