mirror of
https://github.com/Grasscutters/Cultivation.git
synced 2025-12-16 17:14:36 +01:00
Automatically apply RSA patch
This commit is contained in:
@@ -20,7 +20,7 @@ let defaultConfig: Configuration
|
||||
theme: 'default',
|
||||
https_enabled: false,
|
||||
debug_enabled: false,
|
||||
patch_metadata: false,
|
||||
patch_rsa: true,
|
||||
use_internal_proxy: true,
|
||||
wipe_login: false,
|
||||
horny_mode: false,
|
||||
@@ -46,7 +46,7 @@ export interface Configuration {
|
||||
theme: string
|
||||
https_enabled: boolean
|
||||
debug_enabled: boolean
|
||||
patch_metadata: boolean
|
||||
patch_rsa: boolean
|
||||
use_internal_proxy: boolean
|
||||
wipe_login: boolean
|
||||
horny_mode: boolean
|
||||
|
||||
@@ -1,237 +0,0 @@
|
||||
import { invoke } from '@tauri-apps/api'
|
||||
import { dataDir } from '@tauri-apps/api/path'
|
||||
import DownloadHandler from './download'
|
||||
import { getGameDataFolder } from './game'
|
||||
|
||||
export async function patchMetadata() {
|
||||
const metadataExists = await invoke('dir_exists', {
|
||||
path: (await getGameMetadataPath()) + '\\global-metadata.dat',
|
||||
})
|
||||
|
||||
if (!metadataExists) {
|
||||
return false
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
// Remove metadata backup, in case it invalid or something
|
||||
await invoke('delete_file', {
|
||||
path: (await getBackupMetadataPath()) + '\\global-metadata-unpatched.dat',
|
||||
})
|
||||
|
||||
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) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// Do we have a patch already?
|
||||
const patchedExists = await invoke('dir_exists', {
|
||||
path: (await getBackupMetadataPath()) + '\\global-metadata-patched.dat',
|
||||
})
|
||||
|
||||
if (!patchedExists) {
|
||||
// No patch 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: (await 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 replaced = await invoke('copy_file_with_new_name', {
|
||||
path: (await getBackupMetadataPath()) + '\\global-metadata-unpatched.dat',
|
||||
newPath: await getGameMetadataPath(),
|
||||
newName: 'global-metadata.dat',
|
||||
})
|
||||
|
||||
return replaced
|
||||
}
|
||||
|
||||
export async function getGameMetadataPath() {
|
||||
const gameData = await getGameDataFolder()
|
||||
|
||||
if (!gameData) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (gameData + '\\Managed\\Metadata').replace(/\\/g, '/')
|
||||
}
|
||||
|
||||
export async function getBackupMetadataPath() {
|
||||
return (await dataDir()) + 'cultivation\\metadata'
|
||||
}
|
||||
|
||||
export async function globalMetadataLink() {
|
||||
// TODO: Get metadata based on current game version.
|
||||
const versionAPIUrl =
|
||||
'https://sdk-os-static.mihoyo.com/hk4e_global/mdk/launcher/api/resource?channel_id=1&key=gcStgarh&launcher_id=10&sub_channel_id=0'
|
||||
|
||||
// Get versions from API
|
||||
const versions = JSON.parse(
|
||||
await invoke('web_get', {
|
||||
url: versionAPIUrl,
|
||||
})
|
||||
)
|
||||
|
||||
if (!versions || versions.retcode !== 0) {
|
||||
console.log('Failed to get versions from API')
|
||||
return null
|
||||
}
|
||||
|
||||
// Get latest version
|
||||
const latest = versions.data.game.latest
|
||||
|
||||
return (latest.decompressed_path as string) + '/GenshinImpact_Data/Managed/Metadata/global-metadata.dat'
|
||||
}
|
||||
|
||||
export async function restoreMetadata(manager: DownloadHandler) {
|
||||
const metaLink = await globalMetadataLink()
|
||||
|
||||
if (!metaLink) {
|
||||
console.log('Could not get global metadata link!')
|
||||
return false
|
||||
}
|
||||
|
||||
// Should make sure metadata path exists since the user may have deleted it
|
||||
await invoke('dir_create', {
|
||||
path: await getBackupMetadataPath(),
|
||||
})
|
||||
|
||||
// It is possible the unpatched backup is mistakenly patched
|
||||
await invoke('delete_file', {
|
||||
path: (await getBackupMetadataPath()) + '\\global-metadata-unpatched.dat',
|
||||
})
|
||||
|
||||
// Download the file
|
||||
manager.addDownload(metaLink, (await getBackupMetadataPath()) + '\\global-metadata-unpatched.dat', () => {
|
||||
unpatchGame()
|
||||
})
|
||||
console.log('Restoring backedup metadata')
|
||||
|
||||
await unpatchGame()
|
||||
|
||||
return true
|
||||
}
|
||||
112
src/utils/rsa.ts
Normal file
112
src/utils/rsa.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { invoke } from '@tauri-apps/api'
|
||||
import { dataDir } from '@tauri-apps/api/path'
|
||||
import DownloadHandler from './download'
|
||||
import { getGameFolder } from './game'
|
||||
|
||||
interface IProps {
|
||||
downloadHandler: DownloadHandler
|
||||
}
|
||||
|
||||
export async function patchRSA() {
|
||||
const rsaExists = await invoke('dir_exists', {
|
||||
path: (await getBackupRSAPath()) + '\\version.dll',
|
||||
})
|
||||
|
||||
if (rsaExists) {
|
||||
// Already patched
|
||||
return true
|
||||
}
|
||||
|
||||
console.log('Downloading rsa patch to backup location')
|
||||
|
||||
// Download RSA patch to backup location
|
||||
const downloadedRSA = await downloadRSA(this.props.downloadHandler)
|
||||
|
||||
if (!downloadedRSA) {
|
||||
console.log(await getBackupRSAPath())
|
||||
return false
|
||||
}
|
||||
|
||||
console.log('RSA download successful!')
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export async function patchGame() {
|
||||
// Do we have a patch already?
|
||||
const patchedExists = await invoke('dir_exists', {
|
||||
path: (await getBackupRSAPath()) + '\\version.dll',
|
||||
})
|
||||
|
||||
if (!patchedExists) {
|
||||
// No patch found? Patching creates one
|
||||
const patched = await patchRSA()
|
||||
|
||||
if (!patched) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Are we already patched with mhypbase? If so, that's fine, just continue as normal
|
||||
const gameIsPatched = await invoke('are_files_identical', {
|
||||
path1: (await getBackupRSAPath()) + '\\version.dll',
|
||||
path2: (await getGameRSAPath()) + '\\mhypbase.dll',
|
||||
})
|
||||
|
||||
// Tell user they won't be unpatched with manual mhypbase patch
|
||||
if (gameIsPatched) {
|
||||
console.log('You are already patched using mhypbase, so you will not be auto patched and unpatched!')
|
||||
return true
|
||||
}
|
||||
|
||||
// Copy the patch to game files
|
||||
const replaced = await invoke('copy_file', {
|
||||
path: (await getBackupRSAPath()) + '\\version.dll',
|
||||
newPath: await getGameRSAPath(),
|
||||
})
|
||||
|
||||
if (!replaced) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export async function unpatchGame() {
|
||||
// Just delete patch since it's not replacing any existing file
|
||||
const deleted = await invoke('delete_file', {
|
||||
path: (await getGameRSAPath()) + '\\version.dll',
|
||||
})
|
||||
|
||||
return deleted
|
||||
}
|
||||
|
||||
export async function getGameRSAPath() {
|
||||
const gameData = await getGameFolder()
|
||||
|
||||
if (!gameData) {
|
||||
return null
|
||||
}
|
||||
|
||||
return gameData
|
||||
}
|
||||
|
||||
export async function getBackupRSAPath() {
|
||||
return (await dataDir()) + 'cultivation\\rsa'
|
||||
}
|
||||
|
||||
export async function downloadRSA(manager: DownloadHandler) {
|
||||
const rsaLink = 'https://github.com/34736384/RSAPatch/releases/download/v1.1.0/RSAPatch.dll'
|
||||
|
||||
// Should make sure rsa path exists
|
||||
await invoke('dir_create', {
|
||||
path: await getBackupRSAPath(),
|
||||
})
|
||||
|
||||
// Download the file
|
||||
manager.addDownload(rsaLink, (await getBackupRSAPath()) + '\\version.dll', () => {
|
||||
null
|
||||
})
|
||||
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user