From 56791595c0989e8016ad85d7bf0d6c1882fbdc3c Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Mon, 28 Apr 2025 23:24:58 -0400 Subject: [PATCH] Add initial files --- .gitignore | 3 +++ Cargo.toml | 4 ++++ LICENSE | 21 +++++++++++++++++++ README.md | 17 +++++++++++++++ build.rs | 3 +++ examples/basic.rs | 34 ++++++++++++++++++++++++++++++ src/lib.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 135 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 build.rs create mode 100644 examples/basic.rs create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b8cf338 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +sqexPatch.dll +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..890d0db --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "sqexpatch-sys" +version = "0.1.0" +edition = "2024" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9b31696 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Joshua Goins + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b3b9efa --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# sqexpatch-sys + +Incomplete Rust bindings for `sqexPatch.dll`. + +## Building + +Since `sqexPatch.dll` is 32-bit, make sure to use the i686 target. For example, when cross-compiling on Linux: + +```shell +cargo build --target i686-pc-windows-gnu +``` + +An example application is provided with the `basic` example to test if it's working. + +## License + +This project is licensed under the MIT license. diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..078ee13 --- /dev/null +++ b/build.rs @@ -0,0 +1,3 @@ +fn main() { + println!("cargo:rustc-link-search=."); +} diff --git a/examples/basic.rs b/examples/basic.rs new file mode 100644 index 0000000..ca4275e --- /dev/null +++ b/examples/basic.rs @@ -0,0 +1,34 @@ +use std::ffi::{c_char, CStr, CString}; + +use sqexpatch_sys::*; + +fn main() { + unsafe { + println!("sqexPatchGetVersion = {}", CStr::from_ptr(sqexPatchGetVersion() as *const i8).to_str().unwrap()); + + let string1 = CString::new("boot").unwrap(); + let string2 = CString::new("ffxivboot.exe/149504/5f2a70612aa58378eb347869e75adeb8f5581a1b").unwrap(); + let res = sqexPatchInitialize(string1.as_ptr() as *const c_char, string2.as_ptr() as *const c_char); + println!("sqexPatchInitialize = {res}"); + + let empty = CString::new("win32").unwrap(); + let host = CString::new("patch-bootver.ffxiv.localhost").unwrap(); + let channel = CString::new("1.0").unwrap(); + let platform = CString::new("win32_neo_game").unwrap(); + let res = sqexPatchVersionCheckStart(1, host.as_ptr() as *const c_char, 6900, host.as_ptr() as *const c_char, empty.as_ptr() as *const c_char, platform.as_ptr() as *const c_char, channel.as_ptr() as *const c_char); + println!("sqexPatchVersionCheckStart = {res}"); + + let res = sqexPatchVersionCheckIsBusy(1); + println!("sqexPatchVersionCheckIsBusy = {res}"); + + while sqexPatchVersionCheckIsBusy(1) { + //println!("Waiting..."); + } + + let res = sqexPatchVersionCheckGetResponse(1); + println!("sqexPatchVersionCheckGetResponse = {res}"); + + let res = sqexPatchGetNumOfPatch(1); + println!("sqexPatchGetNumOfPatch = {:#?}", res); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..f22d49b --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,53 @@ +use std::ffi::c_char; + +#[link(name = "sqexPatch")] +unsafe extern "C" { + /// 90000 seems to be a common error code? + + pub fn sqexGetPatchTypeName(param_1: u32, param_2: i32, param_3: u32,param_4: u32); + + pub fn sqexPatchDownloadCancel(param_1: i32) -> u32; + pub fn sqexPatchDownloadGetResponse(param_1: i32, param_2: u32, param_3: u32) -> u32; + pub fn sqexPatchDownloadIsBusy(param_1: i32, param_2: *mut i32) -> bool; + pub fn sqexPatchDownloadStart(param_1: i32, param_2: *mut u32, param_3: u32); + + pub fn sqexPatchFinalize(param_1: i32) -> u32; + pub fn sqexPatchGetErrorCodes(param_1: *mut u32, param_2: i32) -> i32; + pub fn sqexPatchGetFileHashString(param_1: u32, param_2: u32, param_3: u32); + pub fn sqexPatchGetNumOfPatch(repo: i32) -> *mut i32; + pub fn sqexPatchGetPatchElement(param_1: i32, param_2: *mut i32, param_3: i32) -> u32; + + /// e.x. "2.00" + pub fn sqexPatchGetVersion() -> *const c_char; + pub fn sqexPatchGetVersionName(param_1: u32, param_2: u32, param_3: i32, param_4: u32); + + /// type_name must be either "boot" or "game", otherwise returns -1 + /// hashes is the boot hash/session id like "ffxivboot.exe/149504/5f2a70612aa58378eb347869e75adeb8f5581a1b". Fun fact, the Chinese version literally hardcodes this, so it's just some random person's SID?!?! + /// Can return either 1 (success?) or -1 (fail) + pub fn sqexPatchInitialize(type_name: *const c_char, hashes: *const c_char) -> i32; + + pub fn sqexPatchInstallCancel(param_1: i32) -> u32; + pub fn sqexPatchInstallGetResponse(param_1: i32) -> u32; + pub fn sqexPatchInstallIsBusy(param_1: i32, param_2: u32) -> bool; + pub fn sqexPatchInstallStart(param_1: i32, param_2: u32, param_3: u32) -> u32; + + pub fn sqexPatchSetVersionName(param_1: u32, param_2: u32, param_3: *mut c_char); + + /// Cancels the request, 0 if successful and 0x15f90 if not. + pub fn sqexPatchVersionCheckCancel(repo: i32) -> u32; + + /// Returns 0x15f90 if the repo is invalid, returns 0 for... everything else (yes, really.) + pub fn sqexPatchVersionCheckGetResponse(repo: i32) -> i32; + + /// Returns true if we're still contacting the patch server for this repo. + pub fn sqexPatchVersionCheckIsBusy(repo: i32) -> bool; + + /// Note: Repos start at 1 + + /// host_header is NOT the url, that's the "Host" header! + /// Contacts the patch server, for a version check I guess? + /// Note: I'm 99% sure this happens in another thread, so you need to keep checking IsBusy. + pub fn sqexPatchVersionCheckStart(repo: i32, host: *const c_char, param_3: i32, host_header: *const c_char, param_5: *const c_char, platform: *const c_char, channel: *const c_char) -> i32; + + pub fn sqexPatchCheckStartEx(param_1: i32, param_2: u32, param_3: i32, param_4: u32, param_5: u32, param_6: u32, param_7: u32, param_8: *mut u32, param_9: u32); +}