mirror of
https://github.com/Grasscutters/Cultivation.git
synced 2025-12-16 09:04:45 +01:00
BIG FAT CLEANUP PART ONE
This commit is contained in:
@@ -67,24 +67,27 @@ fn decrypt_metadata(file_path: &str) -> Vec<u8> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut data = Vec::new();
|
let mut data = Vec::new();
|
||||||
|
|
||||||
|
// Read metadata file
|
||||||
match file.read_to_end(&mut data) {
|
match file.read_to_end(&mut data) {
|
||||||
Ok(_) => {
|
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();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Failed to read global-metadata: {}", e);
|
println!("Failed to read global-metadata: {}", e);
|
||||||
return Vec::new();
|
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<u8>) -> Vec<u8> {
|
fn replace_keys(data: &Vec<u8>) -> Vec<u8> {
|
||||||
|
|||||||
@@ -3,13 +3,15 @@ import Checkbox from './common/Checkbox'
|
|||||||
import BigButton from './common/BigButton'
|
import BigButton from './common/BigButton'
|
||||||
import TextInput from './common/TextInput'
|
import TextInput from './common/TextInput'
|
||||||
import HelpButton from './common/HelpButton'
|
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 { translate } from '../../utils/language'
|
||||||
import { invoke } from '@tauri-apps/api/tauri'
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
|
||||||
import Server from '../../resources/icons/server.svg'
|
import Server from '../../resources/icons/server.svg'
|
||||||
import './ServerLaunchSection.css'
|
import './ServerLaunchSection.css'
|
||||||
import {dataDir} from '@tauri-apps/api/path'
|
import {dataDir} from '@tauri-apps/api/path'
|
||||||
|
import { getGameExecutable } from '../../utils/game'
|
||||||
|
import { patchGame, unpatchGame } from '../../utils/metadata'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
@@ -72,28 +74,6 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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() {
|
async toggleGrasscutter() {
|
||||||
const config = await getConfig()
|
const config = await getConfig()
|
||||||
|
|
||||||
@@ -107,104 +87,24 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
|
|||||||
await saveConfig(config)
|
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() {
|
async playGame() {
|
||||||
const config = await getConfig()
|
const config = await getConfig()
|
||||||
|
|
||||||
if(this.getGameExecutable(config) == null) {
|
if(!await getGameExecutable()) {
|
||||||
|
alert('Game executable not set!')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect to proxy
|
// Connect to proxy
|
||||||
if (config.toggle_grasscutter) {
|
if (config.toggle_grasscutter) {
|
||||||
// Check if metadata has been backed up
|
const patched = await patchGame()
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Patching Metadata')
|
if (!patched) {
|
||||||
if(!await this.patchMetadata()) {
|
alert('Could not patch game!')
|
||||||
// Patch failed
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Assume metadata has not been patched
|
|
||||||
console.log('Patching Metadata')
|
|
||||||
if(!await this.patchMetadata()) {
|
|
||||||
// Patch failed
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let game_exe = this.getGameExecutable(config) as string
|
const game_exe = await getGameExecutable()
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save last connected server and port
|
// Save last connected server and port
|
||||||
await setConfigOption('last_ip', this.state.ip)
|
await setConfigOption('last_ip', this.state.ip)
|
||||||
@@ -221,13 +121,10 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
|
|||||||
|
|
||||||
// Open server as well if the options are set
|
// Open server as well if the options are set
|
||||||
if (config.grasscutter_with_game) {
|
if (config.grasscutter_with_game) {
|
||||||
let jarFolder = config.grasscutter_path
|
const jarFolderArr = config.grasscutter_path.replace(/\\/g, '/').split('/')
|
||||||
|
jarFolderArr.pop()
|
||||||
|
|
||||||
if (jarFolder.includes('/')) {
|
const jarFolder = jarFolderArr.join('/')
|
||||||
jarFolder = jarFolder.substring(0, config.grasscutter_path.lastIndexOf('/'))
|
|
||||||
} else {
|
|
||||||
jarFolder = jarFolder.substring(0, config.grasscutter_path.lastIndexOf('\\'))
|
|
||||||
}
|
|
||||||
|
|
||||||
await invoke('run_jar', {
|
await invoke('run_jar', {
|
||||||
path: config.grasscutter_path,
|
path: config.grasscutter_path,
|
||||||
@@ -236,30 +133,28 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Check if metadata has been backed up
|
const unpatched = await unpatchGame()
|
||||||
if (await invoke('dir_exists', { path: await this.getBackupMetadataPath() + '\\global-metadata-unpatched.dat'})) {
|
|
||||||
// Check if metadata is patched
|
|
||||||
|
|
||||||
// Compare metadata files
|
if (!unpatched) {
|
||||||
if (await invoke('are_files_identical', { path1: await this.getBackupMetadataPath() + '\\global-metadata-patched.dat', path2: this.getGameMetadataPath(config) + '\\global-metadata.dat'})) {
|
alert(`Could not unpatch game, aborting launch! (You can find your metadata backup in ${await dataDir()}\\cultivation\\)`)
|
||||||
// Metadata is patched, so we need to unpatch it
|
return
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Launch the program
|
// Launch the program
|
||||||
const gameExists = await invoke('dir_exists', {
|
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) })
|
if (gameExists) {
|
||||||
else alert('Game not found! At: ' + this.getGameExecutable(config))
|
await invoke('run_program', {
|
||||||
|
path: config.game_install_path
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
alert('Game not found! At: ' + config.game_install_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
async launchServer() {
|
async launchServer() {
|
||||||
|
|||||||
12
src/utils/game.ts
Normal file
12
src/utils/game.ts
Normal file
@@ -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', '')
|
||||||
|
}
|
||||||
183
src/utils/metadata.ts
Normal file
183
src/utils/metadata.ts
Normal file
@@ -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'
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user