From e8183b3af6abe869ef546b9cb4420e8935673010 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Wed, 20 Jul 2022 19:07:36 -0400 Subject: [PATCH] Add game installation functionality --- build.rs | 3 ++ src/installer.rs | 115 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 +- 3 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 build.rs create mode 100644 src/installer.rs diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..7ab85b9 --- /dev/null +++ b/build.rs @@ -0,0 +1,3 @@ +fn main() { + println!("cargo:rustc-link-lib=dylib=unshield"); +} diff --git a/src/installer.rs b/src/installer.rs new file mode 100644 index 0000000..85ab24a --- /dev/null +++ b/src/installer.rs @@ -0,0 +1,115 @@ +use std::ffi::{CStr, CString}; +use std::fs; +use std::fs::File; +use std::io::Write; +use std::os::raw::c_char; + +const FILES_TO_EXTRACT: [&str; 3] = ["data1.cab", "data1.hdr", "data2.cab"]; + +const BOOT_COMPONENT_FILES: [&str; 18] = [ + "cef_license.txt", + "FFXIV.ico", + "ffxivboot.exe", + "ffxivboot.ver", + "ffxivboot64.exe", + "ffxivconfig.exe", + "ffxivconfig64.exe", + "ffxivlauncher.exe", + "ffxivlauncher64.exe", + "ffxivsysinfo.exe", + "ffxivsysinfo64.exe", + "ffxivupdater.exe", + "ffxivupdater64.exe", + "FFXIV_sysinfo.ico", + "icudt.dll", + "libcef.dll", + "license.txt", + "locales/reserved.txt" +]; + +const GAME_COMPONENT_FILES: [&str; 1] = ["ffxivgame.ver"]; + +#[repr(C)] +struct Unshield; + +extern "C" { + fn unshield_open(filename: *const c_char) -> *mut Unshield; + fn unshield_close(unshield: *mut Unshield); + + fn unshield_set_log_level(level : i32); + + fn unshield_file_count(unshield: *mut Unshield) -> i32; + fn unshield_file_name(unshield: *mut Unshield, index : i32) -> *const c_char; + fn unshield_file_save(unshield: *mut Unshield, index : i32, filename: *const c_char) -> bool; +} + +/// Installs the game from the provided retail installer. +pub unsafe fn install_game(installer_path : &str, game_directory : &str) { + let installer_file = std::fs::read(installer_path).unwrap(); + + let mut file_size = installer_file.len(); + let mut last_position = 0; + let mut last_filename = ""; + for filename in FILES_TO_EXTRACT { + let needle = format!("Disk1\\{}", filename); + + let position = installer_file.windows(needle.len()).position(|window| window == needle.as_str().as_bytes()); + if position == None { + break; + } + + let position = position.unwrap(); + + if last_position != 0 { + let mut new_file = File::create(last_filename).unwrap(); + + if last_filename == "data1.hdr" { + new_file.write(&installer_file[last_position + 30..position - 42]); + } else { + new_file.write(&installer_file[last_position + 33..position - 42]); + } + + last_position = position; + } + + last_position = position; + last_filename = filename; + file_size -= (position + 4) - last_position; + } + + let mut new_file = File::create(last_filename).unwrap(); + + new_file.write(&installer_file[last_position + 33..installer_file.len() - 42]); + + fs::create_dir_all(format!("{game_directory}/boot")); + fs::create_dir_all(format!("{game_directory}/game")); + + unsafe { + // set unshield to shut up + unshield_set_log_level(0); + + let unshield = unshield_open(b"data1.cab".as_ptr() as *const c_char); + let file_count = unshield_file_count(unshield); + + for i in 0..file_count { + let filename = CStr::from_ptr(unshield_file_name(unshield, i)).to_string_lossy(); + + for boot_name in BOOT_COMPONENT_FILES { + if boot_name == filename { + let save_filename = format!("{game_directory}/boot/{boot_name}"); + unshield_file_save(unshield, i, CString::new(save_filename).unwrap().as_ptr()); + } + } + + for game_name in GAME_COMPONENT_FILES { + if game_name == filename { + let save_filename = format!("{game_directory}/game/{game_name}"); + unshield_file_save(unshield, i, CString::new(save_filename).unwrap().as_ptr()); + } + } + } + + unshield_close(unshield); + } +} + diff --git a/src/lib.rs b/src/lib.rs index daa9068..6a9ab97 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,4 +31,5 @@ pub mod patch; mod macros; pub mod blowfish; -mod blowfish_constants; \ No newline at end of file +mod blowfish_constants; +pub mod installer; \ No newline at end of file