1
Fork 0
mirror of https://github.com/redstrate/Physis.git synced 2025-04-20 11:47:46 +00:00

Add game installation functionality

This commit is contained in:
Joshua Goins 2022-07-20 19:07:36 -04:00
parent db2ea22201
commit e8183b3af6
3 changed files with 120 additions and 1 deletions

3
build.rs Normal file
View file

@ -0,0 +1,3 @@
fn main() {
println!("cargo:rustc-link-lib=dylib=unshield");
}

115
src/installer.rs Normal file
View file

@ -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);
}
}

View file

@ -32,3 +32,4 @@ mod macros;
pub mod blowfish; pub mod blowfish;
mod blowfish_constants; mod blowfish_constants;
pub mod installer;