mirror of
https://github.com/Grasscutters/Cultivation.git
synced 2025-12-13 15:44:35 +01:00
emergency metadata recovery
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
"disabled": "Disabled",
|
"disabled": "Disabled",
|
||||||
"game_path": "Set Game Install Path",
|
"game_path": "Set Game Install Path",
|
||||||
"game_executable": "Set Game Executable",
|
"game_executable": "Set Game Executable",
|
||||||
|
"recover_metadata": "Emergency Metadata Recovery",
|
||||||
"grasscutter_jar": "Set Grasscutter JAR",
|
"grasscutter_jar": "Set Grasscutter JAR",
|
||||||
"toggle_encryption": "Toggle Encryption",
|
"toggle_encryption": "Toggle Encryption",
|
||||||
"java_path": "Set Custom Java Path",
|
"java_path": "Set Custom Java Path",
|
||||||
|
|||||||
@@ -66,7 +66,13 @@ pub fn copy_file_with_new_name(path: String, new_path: String, new_name: String)
|
|||||||
|
|
||||||
// If the new path doesn't exist, create it.
|
// If the new path doesn't exist, create it.
|
||||||
if !dir_exists(new_path_buf.pop().to_string().as_str()) {
|
if !dir_exists(new_path_buf.pop().to_string().as_str()) {
|
||||||
std::fs::create_dir_all(&new_path).unwrap();
|
match std::fs::create_dir_all(&new_path) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => {
|
||||||
|
println!("Failed to create directory: {}", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy old to new
|
// Copy old to new
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ fn main() {
|
|||||||
lang::get_lang,
|
lang::get_lang,
|
||||||
lang::get_languages,
|
lang::get_languages,
|
||||||
web::valid_url,
|
web::valid_url,
|
||||||
|
web::web_get,
|
||||||
metadata_patcher::patch_metadata
|
metadata_patcher::patch_metadata
|
||||||
])
|
])
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
|
|||||||
@@ -16,3 +16,9 @@ pub(crate) async fn valid_url(url: String) -> bool {
|
|||||||
|
|
||||||
response.status().as_str() == "200"
|
response.status().as_str() == "200"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn web_get(url: String) -> String {
|
||||||
|
// Send a GET request to the specified URL and send the response body back to the client.
|
||||||
|
query(&url).await
|
||||||
|
}
|
||||||
@@ -206,6 +206,7 @@ class App extends React.Component<IProps, IState> {
|
|||||||
// Options menu
|
// Options menu
|
||||||
this.state.optionsOpen ? (
|
this.state.optionsOpen ? (
|
||||||
<Options
|
<Options
|
||||||
|
downloadManager={downloadHandler}
|
||||||
closeFn={() => this.setState({ optionsOpen: !this.state.optionsOpen })}
|
closeFn={() => this.setState({ optionsOpen: !this.state.optionsOpen })}
|
||||||
/>
|
/>
|
||||||
) : null
|
) : null
|
||||||
|
|||||||
@@ -12,9 +12,12 @@ import * as server from '../../../utils/server'
|
|||||||
|
|
||||||
import './Options.css'
|
import './Options.css'
|
||||||
import BigButton from '../common/BigButton'
|
import BigButton from '../common/BigButton'
|
||||||
|
import DownloadHandler from '../../../utils/download'
|
||||||
|
import * as meta from '../../../utils/metadata'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
closeFn: () => void;
|
closeFn: () => void;
|
||||||
|
downloadManager: DownloadHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
@@ -62,6 +65,7 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
this.toggleGrasscutterWithGame = this.toggleGrasscutterWithGame.bind(this)
|
this.toggleGrasscutterWithGame = this.toggleGrasscutterWithGame.bind(this)
|
||||||
this.setCustomBackground = this.setCustomBackground.bind(this)
|
this.setCustomBackground = this.setCustomBackground.bind(this)
|
||||||
this.toggleEncryption = this.toggleEncryption.bind(this)
|
this.toggleEncryption = this.toggleEncryption.bind(this)
|
||||||
|
this.restoreMetadata = this.restoreMetadata.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
@@ -190,6 +194,11 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async restoreMetadata() {
|
||||||
|
console.log(this.props)
|
||||||
|
await meta.restoreMetadata(this.props.downloadManager)
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Menu closeFn={this.props.closeFn} className="Options" heading="Options">
|
<Menu closeFn={this.props.closeFn} className="Options" heading="Options">
|
||||||
@@ -201,6 +210,16 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
<DirInput onChange={this.setGameExecutable} value={this.state?.game_install_path} extensions={['exe']} />
|
<DirInput onChange={this.setGameExecutable} value={this.state?.game_install_path} extensions={['exe']} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="OptionSection" id="menuOptionsContainermetaDownload">
|
||||||
|
<div className="OptionLabel" id="menuOptionsLabelmetaDownload">
|
||||||
|
<Tr text="options.recover_metadata" />
|
||||||
|
</div>
|
||||||
|
<div className="OptionValue" id="menuOptionsButtonmetaDownload">
|
||||||
|
<BigButton onClick={this.restoreMetadata} id="metaDownload">
|
||||||
|
<Tr text='components.download' />
|
||||||
|
</BigButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{
|
{
|
||||||
this.state.swag && (
|
this.state.swag && (
|
||||||
<div className='OptionSection' id="menuOptionsContainerAkebi">
|
<div className='OptionSection' id="menuOptionsContainerAkebi">
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { invoke } from '@tauri-apps/api'
|
import { invoke } from '@tauri-apps/api'
|
||||||
import { dataDir } from '@tauri-apps/api/path'
|
import { dataDir } from '@tauri-apps/api/path'
|
||||||
import { getConfig } from './configuration'
|
import { getConfig } from './configuration'
|
||||||
|
import DownloadHandler from './download'
|
||||||
import { getGameExecutable, getGameFolder } from './game'
|
import { getGameExecutable, getGameFolder } from './game'
|
||||||
|
|
||||||
export async function patchMetadata() {
|
export async function patchMetadata() {
|
||||||
@@ -149,7 +150,11 @@ export async function unpatchGame() {
|
|||||||
path2: await getGameMetadataPath() + '\\global-metadata.dat'
|
path2: await getGameMetadataPath() + '\\global-metadata.dat'
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!metaPatched) {
|
const metaExists = await invoke('dir_exists', {
|
||||||
|
path: await getGameMetadataPath() + '\\global-metadata.dat'
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!metaPatched && metaExists) {
|
||||||
// Game isn't patched
|
// Game isn't patched
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -178,3 +183,50 @@ export async function getGameMetadataPath() {
|
|||||||
export async function getBackupMetadataPath() {
|
export async function getBackupMetadataPath() {
|
||||||
return await dataDir() + 'cultivation\\metadata'
|
return await dataDir() + 'cultivation\\metadata'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function globalMetadataLink() {
|
||||||
|
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 backupExists = await invoke('dir_exists', {
|
||||||
|
path: await getBackupMetadataPath() + '\\global-metadata-unpatched.dat'
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!backupExists) {
|
||||||
|
console.log('No backup found! Replacing with global metadata link')
|
||||||
|
|
||||||
|
const metaLink = await globalMetadataLink()
|
||||||
|
|
||||||
|
if (!metaLink) {
|
||||||
|
console.log('Coudl not get global metadata link!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download the file
|
||||||
|
manager.addDownload(metaLink, await getBackupMetadataPath() + '\\global-metadata-unpatched.dat', () => {
|
||||||
|
unpatchGame()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Restoring backedup metadata')
|
||||||
|
|
||||||
|
await unpatchGame()
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user