Autopatching on game launch

Plus adding some non-functional options for later
Need to add support for Chinese version of the game
This commit is contained in:
Benj
2022-07-07 01:25:54 +08:00
parent 6124d6949c
commit 99687f0550
12 changed files with 183 additions and 20 deletions

8
src-tauri/Cargo.lock generated
View File

@@ -742,7 +742,9 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
name = "cultivation"
version = "0.1.0"
dependencies = [
"cc",
"duct",
"file_diff",
"futures-util",
"http",
"hudsucker",
@@ -1043,6 +1045,12 @@ dependencies = [
"rustc_version 0.3.3",
]
[[package]]
name = "file_diff"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31a7a908b8f32538a2143e59a6e4e2508988832d5d4d6f7c156b3cbc762643a5"
[[package]]
name = "filetime"
version = "0.2.16"

View File

@@ -62,6 +62,9 @@ rcgen = { version = "0.9", features = ["x509-parser"] }
libloading = "0.7"
regex = "1"
# other
file_diff = "1.0.0"
[features]
# by default Tauri runs in production mode
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL

View File

@@ -31,7 +31,8 @@
"grasscutter_latest": "Download Grasscutter Latest",
"grasscutter_stable_update": "Update Grasscutter Stable",
"grasscutter_latest_update": "Update Grasscutter Latest",
"resources": "Download Grasscutter Resources"
"resources": "Download Grasscutter Resources",
"game": "Download Game"
},
"download_status": {
"downloading": "Downloading",
@@ -56,6 +57,7 @@
"gc_dev_jar": "Download the latest development Grasscutter build, which includes jar file and data files.",
"gc_stable_data": "Download the current stable Grasscutter data files, which does not come with a jar file. This is useful for updating.",
"gc_dev_data": "Download the latest development Grasscutter data files, which does not come with a jar file. This is useful for updating.",
"resources": "These are also required to run a Grasscutter server. This button will be grey if you have an existing resources folder with contents inside"
"resources": "These are also required to run a Grasscutter server. This button will be grey if you have an existing resources folder with contents inside",
"game": "This will download a fresh copy of \"the certain anime game\" and set your game executable to it. This is useful if you don't want to patch your main game."
}
}

View File

@@ -1,4 +1,5 @@
use std::fs;
use file_diff::diff;
#[tauri::command]
pub fn rename(path: String, new_name: String) {
@@ -32,4 +33,9 @@ pub fn dir_is_empty(path: &str) -> bool {
#[tauri::command]
pub fn dir_delete(path: &str) {
fs::remove_dir_all(path).unwrap();
}
#[tauri::command]
pub fn are_files_identical(path1: &str, path2: &str) -> bool {
return diff(path1, path2);
}

View File

@@ -50,6 +50,8 @@ fn main() {
system_helpers::run_jar,
system_helpers::open_in_browser,
system_helpers::copy_file,
system_helpers::copy_file_with_new_name,
system_helpers::delete_file,
system_helpers::install_location,
system_helpers::is_elevated,
proxy::set_proxy_addr,
@@ -59,6 +61,7 @@ fn main() {
file_helpers::dir_exists,
file_helpers::dir_is_empty,
file_helpers::dir_delete,
file_helpers::are_files_identical,
downloader::download_file,
downloader::stop_download,
lang::get_lang,

View File

@@ -1,5 +1,6 @@
use regex::Regex;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::Read;
use std::io::Write;
@@ -23,16 +24,38 @@ fn dll_encrypt_global_metadata(data : *mut u8, size : u64) -> Result<bool, Box<d
}
#[tauri::command]
pub fn patch_metadata(metadata_folder: &str) {
let metadata_file = &(metadata_folder.to_owned() + "\\global-metadata.dat");
pub fn patch_metadata(metadata_folder: &str) -> bool {
let metadata_file = &(metadata_folder.to_owned() + "\\global-metadata-unpatched.dat");
println!("Patching metadata file: {}", metadata_file);
let decrypted = decrypt_metadata(metadata_file);
if do_vecs_match(&decrypted, &Vec::new()) {
println!("Failed to decrypt metadata file.");
return false;
}
let modified = replace_keys(&decrypted);
if do_vecs_match(&modified, &Vec::new()) {
println!("Failed to replace keys in metadata file.");
return false;
}
let encrypted = encrypt_metadata(&modified);
if do_vecs_match(&encrypted, &Vec::new()) {
println!("Failed to re-encrypt metadata file.");
return false;
}
//write encrypted to file
let mut file = File::create(&(metadata_folder.to_owned() + "\\encrypted-metadata.dat")).unwrap();
let mut file = match OpenOptions::new().create(true).write(true).open(&(metadata_folder.to_owned() + "\\global-metadata-patched.dat")) {
Ok(file) => file,
Err(e) => {
println!("Failed to open global-metadata: {}", e);
return false;
}
};
file.write_all(&encrypted).unwrap();
return true;
}
fn decrypt_metadata(file_path: &str) -> Vec<u8> {
@@ -124,3 +147,8 @@ fn encrypt_metadata(old_data: &Vec<u8>) -> Vec<u8> {
}
};
}
fn do_vecs_match<T: PartialEq>(a: &Vec<T>, b: &Vec<T>) -> bool {
let matching = a.iter().zip(b.iter()).filter(|&(a, b)| a == b).count();
matching == a.len() && matching == b.len()
}

View File

@@ -66,6 +66,36 @@ pub fn copy_file(path: String, new_path: String) -> bool {
}
}
#[tauri::command]
pub fn copy_file_with_new_name(path: String, new_path: String, new_name: String) -> bool {
let mut new_path_buf = std::path::PathBuf::from(&new_path);
// If the new path doesn't exist, create it.
if !file_helpers::dir_exists(new_path_buf.pop().to_string().as_str()) {
std::fs::create_dir_all(&new_path).unwrap();
}
// Copy old to new
match std::fs::copy(&path, format!("{}/{}", new_path, new_name)) {
Ok(_) => true,
Err(e) => {
println!("Failed to copy file: {}", e);
false
}
}
}
#[tauri::command]
pub fn delete_file(path: String) -> bool {
match std::fs::remove_file(path) {
Ok(_) => return true,
Err(e) => {
println!("Failed to delete file: {}", e);
return false
}
};
}
#[tauri::command]
pub fn install_location() -> String {
let mut exe_path = std::env::current_exe().unwrap();