From b903c27a222abeb47ab6453d2cc3ee57d06a043e Mon Sep 17 00:00:00 2001 From: SpikeHD Date: Wed, 13 Jul 2022 18:10:41 -0700 Subject: [PATCH] BIG FAT CLEANUP PART ONE --- src-tauri/src/metadata_patcher.rs | 27 ++-- src/ui/components/ServerLaunchSection.tsx | 159 ++++--------------- src/utils/game.ts | 12 ++ src/utils/metadata.ts | 183 ++++++++++++++++++++++ 4 files changed, 237 insertions(+), 144 deletions(-) create mode 100644 src/utils/game.ts create mode 100644 src/utils/metadata.ts diff --git a/src-tauri/src/metadata_patcher.rs b/src-tauri/src/metadata_patcher.rs index 2716de5..2c7a93a 100644 --- a/src-tauri/src/metadata_patcher.rs +++ b/src-tauri/src/metadata_patcher.rs @@ -67,24 +67,27 @@ fn decrypt_metadata(file_path: &str) -> Vec { } }; let mut data = Vec::new(); + + // Read metadata file match file.read_to_end(&mut data) { - Ok(_) => { - match dll_decrypt_global_metadata(data.as_mut_ptr(), data.len().try_into().unwrap()) { - Ok(_) => { - println!("Successfully decrypted global-metadata"); - return data; - } - Err(e) => { - println!("Failed to decrypt global-metadata: {}", e); - return Vec::new(); - } - }; - } + Ok(_) => (), Err(e) => { println!("Failed to read global-metadata: {}", e); return Vec::new(); } } + + // Decrypt metadata file + match dll_decrypt_global_metadata(data.as_mut_ptr(), data.len().try_into().unwrap()) { + Ok(_) => { + println!("Successfully decrypted global-metadata"); + return data; + } + Err(e) => { + println!("Failed to decrypt global-metadata: {}", e); + return Vec::new(); + } + }; } fn replace_keys(data: &Vec) -> Vec { diff --git a/src/ui/components/ServerLaunchSection.tsx b/src/ui/components/ServerLaunchSection.tsx index 6dd459b..788e659 100644 --- a/src/ui/components/ServerLaunchSection.tsx +++ b/src/ui/components/ServerLaunchSection.tsx @@ -3,13 +3,15 @@ import Checkbox from './common/Checkbox' import BigButton from './common/BigButton' import TextInput from './common/TextInput' import HelpButton from './common/HelpButton' -import { Configuration, getConfig, saveConfig, setConfigOption } from '../../utils/configuration' +import { getConfig, saveConfig, setConfigOption } from '../../utils/configuration' import { translate } from '../../utils/language' import { invoke } from '@tauri-apps/api/tauri' import Server from '../../resources/icons/server.svg' import './ServerLaunchSection.css' import {dataDir} from '@tauri-apps/api/path' +import { getGameExecutable } from '../../utils/game' +import { patchGame, unpatchGame } from '../../utils/metadata' interface IProps { [key: string]: any @@ -72,28 +74,6 @@ export default class ServerLaunchSection extends React.Component }) } - getGameExecutable(config : Configuration) { - if(!config.game_install_path || !config.game_executable) { - alert('Game executable and/or path not set!') - return null - } - - return config.game_install_path + '\\' + config.game_executable - } - - getGameMetadataPath(config : Configuration) { - if(!config.game_install_path || !config.game_executable) { - alert('Game executable and/or path not set!') - return null - } - - return config.game_install_path + '\\' + config.game_executable.replace('.exe', '_Data') + '\\Managed\\Metadata' - } - - async getBackupMetadataPath() { - return await dataDir() + 'cultivation\\metadata' - } - async toggleGrasscutter() { const config = await getConfig() @@ -107,104 +87,24 @@ export default class ServerLaunchSection extends React.Component await saveConfig(config) } - async patchMetadata() { - const config = await getConfig() - - if(!await invoke('dir_exists', {path: this.getGameMetadataPath(config) + '\\global-metadata.dat'})) { - alert('Global metadata not found!') - return - } - - // Copy unpatched metadata to backup location - console.log('Copying unpatched metadata to backup location') - if(await invoke('copy_file_with_new_name', { path: this.getGameMetadataPath(config) + '\\global-metadata.dat', newPath: await this.getBackupMetadataPath(), newName: 'global-metadata-unpatched.dat' })) { - // Backup successful - - // Patch backedup metadata - console.log('Patching backedup metadata') - if(await invoke('patch_metadata', {metadataFolder: await this.getBackupMetadataPath()})) { - // Patch successful - // Replace game metadata with patched metadata - console.log('Replacing unpatched game metadata with patched metadata') - if(await invoke('copy_file_with_new_name', { path: await this.getBackupMetadataPath() + '\\global-metadata-patched.dat', newPath: this.getGameMetadataPath(config), newName: 'global-metadata.dat' })) { - console.log('Replacement successful!') - return true - } else { - // Replace failed - alert('Failed to replace game metadata!') - return false - } - } else { - alert ('Failed to patch metadata!') - return false - } - } else { - alert ('Failed to backup metadata!') - console.log(await this.getBackupMetadataPath()) - return false - } - } - async playGame() { const config = await getConfig() - if(this.getGameExecutable(config) == null) { + if(!await getGameExecutable()) { + alert('Game executable not set!') return } // Connect to proxy if (config.toggle_grasscutter) { - // Check if metadata has been backed up - if (await invoke('dir_exists', { path: await this.getBackupMetadataPath() + '\\global-metadata-unpatched.dat'})) { - // Assume metadata has been patched - - // Compare metadata files - if (!(await invoke('are_files_identical', { path1: await this.getBackupMetadataPath() + '\\global-metadata-patched.dat', path2: this.getGameMetadataPath(config) + '\\global-metadata.dat'}))) { - // Metadata is not patched - // Check to see if unpatched backup matches the game's version - console.log('Metadata is not patched') - if (await invoke('are_files_identical', { path1: await this.getBackupMetadataPath() + '\\global-metadata-unpatched.dat', path2: this.getGameMetadataPath(config) + '\\global-metadata.dat'})) { - // Current metadata matches unpatched metadata - // Game's metadata is not patched, so we need to replace it with the patched metadata - console.log('Replacing unpatched metadata') - if(!(await invoke('copy_file_with_new_name', { path: await this.getBackupMetadataPath() + '\\global-metadata-patched.dat', newPath: this.getGameMetadataPath(config), newName: 'global-metadata.dat' }))) { - // Replace failed - alert('Failed to replace game metadata!') - return - } - } else { - // Game has probably been updated. We need to repatch the game... - - // Delete backed up metadata - if(!(await invoke('delete_file', { path: await this.getBackupMetadataPath() + '\\global-metadata-unpatched.dat' })) && !(await invoke('delete_file', { path: await this.getBackupMetadataPath() + '\\global-metadata-patched.dat' }))) { - // Delete failed - alert('Failed to delete backed up metadata!') - return - } + const patched = await patchGame() - console.log('Patching Metadata') - if(!await this.patchMetadata()) { - // Patch failed - return - } - } - } - } else { - // Assume metadata has not been patched - console.log('Patching Metadata') - if(!await this.patchMetadata()) { - // Patch failed - return - } + if (!patched) { + alert('Could not patch game!') + return } - let game_exe = this.getGameExecutable(config) as string - - if (game_exe.includes('\\')) { - game_exe = game_exe.substring((this.getGameExecutable(config) as string).lastIndexOf('\\') + 1) - } else { - game_exe = game_exe.substring((this.getGameExecutable(config) as string).lastIndexOf('/') + 1) - } + const game_exe = await getGameExecutable() // Save last connected server and port await setConfigOption('last_ip', this.state.ip) @@ -221,13 +121,10 @@ export default class ServerLaunchSection extends React.Component // Open server as well if the options are set if (config.grasscutter_with_game) { - let jarFolder = config.grasscutter_path + const jarFolderArr = config.grasscutter_path.replace(/\\/g, '/').split('/') + jarFolderArr.pop() - if (jarFolder.includes('/')) { - jarFolder = jarFolder.substring(0, config.grasscutter_path.lastIndexOf('/')) - } else { - jarFolder = jarFolder.substring(0, config.grasscutter_path.lastIndexOf('\\')) - } + const jarFolder = jarFolderArr.join('/') await invoke('run_jar', { path: config.grasscutter_path, @@ -236,30 +133,28 @@ export default class ServerLaunchSection extends React.Component }) } } else { - // Check if metadata has been backed up - if (await invoke('dir_exists', { path: await this.getBackupMetadataPath() + '\\global-metadata-unpatched.dat'})) { - // Check if metadata is patched + const unpatched = await unpatchGame() - // Compare metadata files - if (await invoke('are_files_identical', { path1: await this.getBackupMetadataPath() + '\\global-metadata-patched.dat', path2: this.getGameMetadataPath(config) + '\\global-metadata.dat'})) { - // Metadata is patched, so we need to unpatch it - console.log('Replacing patched game metadata with unpatched metadata') - if(!(await invoke('copy_file_with_new_name', { path: await this.getBackupMetadataPath() + '\\global-metadata-unpatched.dat', newPath: this.getGameMetadataPath(config), newName: 'global-metadata.dat' }))) { - // Replace failed - alert('Failed to unpatch game metadata!') - return - } - } + if (!unpatched) { + alert(`Could not unpatch game, aborting launch! (You can find your metadata backup in ${await dataDir()}\\cultivation\\)`) + return } } // Launch the program const gameExists = await invoke('dir_exists', { - path: this.getGameExecutable(config) + path: config.game_install_path }) - if (gameExists) await invoke('run_program', { path: this.getGameExecutable(config) }) - else alert('Game not found! At: ' + this.getGameExecutable(config)) + if (gameExists) { + await invoke('run_program', { + path: config.game_install_path + }) + + return + } + + alert('Game not found! At: ' + config.game_install_path) } async launchServer() { diff --git a/src/utils/game.ts b/src/utils/game.ts new file mode 100644 index 0000000..d884fad --- /dev/null +++ b/src/utils/game.ts @@ -0,0 +1,12 @@ +import { getConfig } from './configuration' + +export async function getGameExecutable() { + const config = await getConfig() + + if(!config.game_install_path) { + return null + } + + const pathArr = config.game_executable.replace(/\\/g, '/').split('/') + return pathArr[pathArr.length - 1].replace('.exe', '') +} \ No newline at end of file diff --git a/src/utils/metadata.ts b/src/utils/metadata.ts new file mode 100644 index 0000000..101b29e --- /dev/null +++ b/src/utils/metadata.ts @@ -0,0 +1,183 @@ +import { invoke } from '@tauri-apps/api' +import { dataDir } from '@tauri-apps/api/path' +import { getConfig } from './configuration' +import { getGameExecutable } from './game' + +export async function patchMetadata() { + const metadataExists = await invoke('dir_exists', { + path: getGameMetadataPath() + '\\global-metadata.dat' + }) + + if (!metadataExists) { + alert('Global metadata not found!') + return + } + + console.log('Copying unpatched metadata to backup location') + + // Copy unpatched metadata to backup location + const copiedMeta = await invoke('copy_file_with_new_name', { + path: await getGameMetadataPath() + '\\global-metadata.dat', + newPath: await getBackupMetadataPath(), + newName: 'global-metadata-unpatched.dat' + }) + + if (!copiedMeta) { + alert('Failed to backup metadata!') + console.log(await getBackupMetadataPath()) + return false + } + + // backup was successful! Time to patch + + console.log('Patching backedup metadata') + + const patchedMeta = await invoke('patch_metadata', { + metadataFolder: await getBackupMetadataPath(), + }) + + if (!patchedMeta) { + alert('Failed to patch metadata!') + return false + } + + // Patch also worked! Time to replace + console.log('Replacing unpatched game metadata with patched metadata') + + const replacedMeta = await invoke('copy_file_with_new_name', { + path: await getBackupMetadataPath() + '\\global-metadata-patched.dat', + newPath: await getGameMetadataPath(), + newName: 'global-metadata.dat' + }) + + if (!replacedMeta) { + alert('Failed to replace game metadata!') + return false + } + + console.log('Replacement successful!') + + return true +} + +export async function patchGame() { + const backupExists = await invoke('dir_exists', { + path: await getBackupMetadataPath() + '\\global-metadata-unpatched.dat' + }) + + if (!backupExists) { + // No backup found? Patching creates one + const patched = await patchMetadata() + + if (!patched) { + return false + } + } + + // Are we already patched? If so, that's fine, just continue as normal + const gameIsPatched = await invoke('are_files_identical', { + path1: await getBackupMetadataPath() + '\\global-metadata-patched.dat', + path2: await getGameMetadataPath() + '\\global-metadata.dat' + }) + + if (gameIsPatched) { + return true + } + + // Is the current backup the same as the games current metadata? + const backupIsCurrent = await invoke('are_files_identical', { + path1: await getBackupMetadataPath() + '\\global-metadata-unpatched.dat', + path2: await getGameMetadataPath() + '\\global-metadata.dat' + }) + + // Game has probably been updated. We need to repatch the game... + if (!backupIsCurrent) { + const deletedOldBackup = await invoke('delete_file', { + path: await getBackupMetadataPath() + '\\global-metadata-unpatched.dat' + }) + const deletedOldPatched = await invoke('delete_file', { + path: await getBackupMetadataPath() + '\\global-metadata-patched.dat' + }) + + // It's fine if these deletes fail. The game will be replaced anyway. + if (!deletedOldBackup) { + console.log('Warning: Failed to delete old backup!') + } + + if (!deletedOldPatched) { + console.log('Warning: Failed to delete old patched metadata!') + } + + console.log('Patching Metadata') + + const patched = await patchMetadata() + + if (!patched) { + return false + } + + return true + } + + console.log('Metadata is not patched') + console.log('Replacing unpatched metadata') + + // Finally, replace the unpatched metadata with the patched one + const replaced = await invoke('copy_file_with_new_name', { + path: await getBackupMetadataPath() + '\\global-metadata-patched.dat', + newPath: await getGameMetadataPath(), + newName: 'global-metadata.dat' + }) + + if (!replaced) { + return false + } + + return true +} + +export async function unpatchGame() { + const backupExists = await invoke('dir_exists', { + path: getBackupMetadataPath + '\\global-metadata-unpatched.dat' + }) + + if (!backupExists) { + // Let's just hope the game isn't on a patched metadata since we don't have a backup... + return true + } + + const metaPatched = await invoke('are_files_identical', { + path1: await getBackupMetadataPath() + '\\global-metadata-patched.dat', + path2: await getGameMetadataPath() + '\\global-metadata.dat' + }) + + if (!metaPatched) { + // Game isn't patched + return true + } + + console.log('Replacing patched game metadata with unpatched metadata') + + const replaced = await invoke('copy_file_with_new_name', { + path: await getBackupMetadataPath() + '\\global-metadata-unpatched.dat', + newPath: getGameMetadataPath(), + newName: 'global-metadata.dat' + }) + + return replaced +} + +export async function getGameMetadataPath() { + const config = await getConfig() + const gameExec = await getGameExecutable() + + if (!gameExec) { + return null + } + + return config.game_install_path + '\\' + gameExec.replace('.exe', '_Data') + '\\Managed\\Metadata' +} + +export async function getBackupMetadataPath() { + return await dataDir() + 'cultivation\\metadata' +} \ No newline at end of file