From 164d6409db27b963f79047760c135ca7b4b84459 Mon Sep 17 00:00:00 2001 From: Artemis Tosini Date: Wed, 24 Apr 2024 06:26:02 +0000 Subject: [PATCH] Somehow I managed to make a vtable correctly --- Cargo.lock | 46 ++++++ client/Cargo.toml | 3 +- client/src/abi/mod.rs | 23 +++ client/src/abi/steam_client.rs | 276 +++++++++++++++++++++++++++++++++ client/src/abi/steam_utils.rs | 59 +++++++ client/src/lib.rs | 17 +- client/src/loader.rs | 28 ++++ 7 files changed, 439 insertions(+), 13 deletions(-) create mode 100644 client/src/abi/mod.rs create mode 100644 client/src/abi/steam_client.rs create mode 100644 client/src/abi/steam_utils.rs create mode 100644 client/src/loader.rs diff --git a/Cargo.lock b/Cargo.lock index 37a6e59..c963b45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,6 +65,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "ctor" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "env_filter" version = "0.1.0" @@ -106,6 +116,24 @@ version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +[[package]] +name = "proc-macro2" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + [[package]] name = "regex" version = "1.10.4" @@ -135,6 +163,23 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +[[package]] +name = "syn" +version = "2.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + [[package]] name = "utf8parse" version = "0.2.1" @@ -145,6 +190,7 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" name = "vapore-client" version = "0.0.0" dependencies = [ + "ctor", "env_logger", "log", ] diff --git a/client/Cargo.toml b/client/Cargo.toml index ba96ef1..5997a30 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -6,5 +6,6 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -log = "0.4" +ctor = "0.2" env_logger = "0.11" +log = "0.4" diff --git a/client/src/abi/mod.rs b/client/src/abi/mod.rs new file mode 100644 index 0000000..fb921a6 --- /dev/null +++ b/client/src/abi/mod.rs @@ -0,0 +1,23 @@ +pub mod steam_client; +pub use steam_client::*; +pub mod steam_utils; +pub use steam_utils::*; + +pub type SteamPipe = i32; +pub type SteamUser = i32; + +#[repr(u32)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum AccountType { + Invalid = 0, + Individual = 1, + Multiseat = 2, + GameServer = 3, + AnonGameServer = 4, + Pending = 5, + ContentServer = 6, + Clan = 7, + Chat = 8, + ConsoleUser = 9, + AnonUser = 10, +} diff --git a/client/src/abi/steam_client.rs b/client/src/abi/steam_client.rs new file mode 100644 index 0000000..2c2546e --- /dev/null +++ b/client/src/abi/steam_client.rs @@ -0,0 +1,276 @@ +#![allow(non_snake_case)] +use std::ffi::{c_char, c_void, CStr}; +use std::ptr; + +use log::warn; + +use super::{AccountType, ISteamUtils, SteamPipe, SteamUser}; + +/// The initial steam interface from CreateInterface +#[repr(C)] +pub struct ISteamClient { + pub vtbl: ISteamClientVtbl, + pub version: u32, +} + +impl ISteamClient { + pub fn new(version: &CStr) -> Option> { + let (vtbl, version) = match version.to_bytes() { + b"SteamClient017" => ( + ISteamClientVtbl { + ver017: &STEAM_CLIENT_017_VTBL_DEFAULT as *const ISteamClient017Vtbl, + }, + 17, + ), + b"SteamClient020" => ( + ISteamClientVtbl { + ver020: &STEAM_CLIENT_020_VTBL_DEFAULT as *const ISteamClient020Vtbl, + }, + 20, + ), + _ => return None, + }; + + Some(Box::new(Self { vtbl, version })) + } +} + +/// Vtable for any version of steam client +#[repr(C)] +pub union ISteamClientVtbl { + pub ver017: *const ISteamClient017Vtbl, + pub ver020: *const ISteamClient020Vtbl, +} + +/// Vtable for SteamClient017 +#[repr(C)] +pub struct ISteamClient017Vtbl { + pub CreateSteamPipe: extern "C" fn(self_ptr: *mut ISteamClient) -> SteamPipe, + pub BReleaseSteamPipe: extern "C" fn(self_ptr: *mut ISteamClient, pipe: SteamPipe) -> bool, + pub ConnectToGlobalUser: + extern "C" fn(self_ptr: *mut ISteamClient, pipe: SteamPipe) -> SteamUser, + pub CreateLocalUser: extern "C" fn( + self_ptr: *mut ISteamClient, + pipe: *const SteamPipe, + account_type: AccountType, + ) -> SteamUser, + pub ReleaseUser: *const c_void, + pub GetISteamUser: *const c_void, + pub GetISteamGameServer: *const c_void, + pub SetLocalIPBinding: *const c_void, + pub GetISteamFriends: *const c_void, + pub GetISteamUtils: extern "C" fn( + self_ptr: *mut ISteamClient, + pipe: SteamPipe, + version: *const c_char, + ) -> *const ISteamUtils, + pub GetISteamMatchmaking: *const c_void, + pub GetISteamMatchmakingServers: *const c_void, + pub GetISteamGenericInterface: *const c_void, + pub GetISteamUserStats: *const c_void, + pub GetISteamGameServerStats: *const c_void, + pub GetISteamApps: *const c_void, + pub GetISteamNetworking: *const c_void, + pub GetISteamRemoteStorage: *const c_void, + pub GetISteamScreenshots: *const c_void, + pub RunFrame: *const c_void, + pub GetIPCCallCount: *const c_void, + pub SetWarningMessageHook: *const c_void, + pub BShutdownIfAllPipesClosed: *const c_void, + pub GetISteamHTTP: *const c_void, + pub DEPRECATED_GetISteamUnifiedMessages: *const c_void, + pub GetISteamController: *const c_void, + pub GetISteamUGC: *const c_void, + pub GetISteamAppList: *const c_void, + pub GetISteamMusic: *const c_void, + pub GetISteamMusicRemote: *const c_void, + pub GetISteamHTMLSurface: *const c_void, + pub DEPRECATED_Set_SteamAPI_CPostAPIResultInProcess: *const c_void, + pub DEPRECATED_Remove_SteamAPI_CPostAPIResultInProcess: *const c_void, + pub Set_SteamAPI_CCheckCallbackRegisteredInProcess: *const c_void, + pub GetISteamInventory: *const c_void, + pub GetISteamVideo: *const c_void, + pub GetISteamParentalSettings: *const c_void, +} + +const STEAM_CLIENT_017_VTBL_DEFAULT: ISteamClient017Vtbl = ISteamClient017Vtbl { + CreateSteamPipe, + BReleaseSteamPipe, + ConnectToGlobalUser, + CreateLocalUser, + ReleaseUser: ptr::null(), + GetISteamUser: ptr::null(), + GetISteamGameServer: ptr::null(), + SetLocalIPBinding: ptr::null(), + GetISteamFriends: ptr::null(), + GetISteamUtils, + GetISteamMatchmaking: ptr::null(), + GetISteamMatchmakingServers: ptr::null(), + GetISteamGenericInterface: ptr::null(), + GetISteamUserStats: ptr::null(), + GetISteamGameServerStats: ptr::null(), + GetISteamApps: ptr::null(), + GetISteamNetworking: ptr::null(), + GetISteamRemoteStorage: ptr::null(), + GetISteamScreenshots: ptr::null(), + RunFrame: ptr::null(), + GetIPCCallCount: ptr::null(), + SetWarningMessageHook: ptr::null(), + BShutdownIfAllPipesClosed: ptr::null(), + GetISteamHTTP: ptr::null(), + DEPRECATED_GetISteamUnifiedMessages: ptr::null(), + GetISteamController: ptr::null(), + GetISteamUGC: ptr::null(), + GetISteamAppList: ptr::null(), + GetISteamMusic: ptr::null(), + GetISteamMusicRemote: ptr::null(), + GetISteamHTMLSurface: ptr::null(), + DEPRECATED_Set_SteamAPI_CPostAPIResultInProcess: ptr::null(), + DEPRECATED_Remove_SteamAPI_CPostAPIResultInProcess: ptr::null(), + Set_SteamAPI_CCheckCallbackRegisteredInProcess: ptr::null(), + GetISteamInventory: ptr::null(), + GetISteamVideo: ptr::null(), + GetISteamParentalSettings: ptr::null(), +}; + +/// Vtable for SteamClient020 +#[repr(C)] +pub struct ISteamClient020Vtbl { + pub CreateSteamPipe: extern "C" fn(self_ptr: *mut ISteamClient) -> SteamPipe, + pub BReleaseSteamPipe: extern "C" fn(self_ptr: *mut ISteamClient, pipe: SteamPipe) -> bool, + pub ConnectToGlobalUser: + extern "C" fn(self_ptr: *mut ISteamClient, pipe: SteamPipe) -> SteamUser, + pub CreateLocalUser: extern "C" fn( + self_ptr: *mut ISteamClient, + pipe: *const SteamPipe, + account_type: AccountType, + ) -> SteamUser, + pub ReleaseUser: *const c_void, + pub GetISteamUser: *const c_void, + pub GetISteamGameServer: *const c_void, + pub SetLocalIPBinding: *const c_void, + pub GetISteamFriends: *const c_void, + pub GetISteamUtils: extern "C" fn( + self_ptr: *mut ISteamClient, + pipe: SteamPipe, + version: *const c_char, + ) -> *const ISteamUtils, + pub GetISteamMatchmaking: *const c_void, + pub GetISteamMatchmakingServers: *const c_void, + pub GetISteamGenericInterface: *const c_void, + pub GetISteamUserStats: *const c_void, + pub GetISteamGameServerStats: *const c_void, + pub GetISteamApps: *const c_void, + pub GetISteamNetworking: *const c_void, + pub GetISteamRemoteStorage: *const c_void, + pub GetISteamScreenshots: *const c_void, + pub GetISteamGameSearch: *const c_void, + pub RunFrame: *const c_void, + pub GetIPCCallCount: *const c_void, + pub SetWarningMessageHook: *const c_void, + pub BShutdownIfAllPipesClosed: *const c_void, + pub GetISteamHTTP: *const c_void, + pub DEPRECATED_GetISteamUnifiedMessages: *const c_void, + pub GetISteamController: *const c_void, + pub GetISteamUGC: *const c_void, + pub GetISteamAppList: *const c_void, + pub GetISteamMusic: *const c_void, + pub GetISteamMusicRemote: *const c_void, + pub GetISteamHTMLSurface: *const c_void, + pub DEPRECATED_Set_SteamAPI_CPostAPIResultInProcess: *const c_void, + pub DEPRECATED_Remove_SteamAPI_CPostAPIResultInProcess: *const c_void, + pub Set_SteamAPI_CCheckCallbackRegisteredInProcess: *const c_void, + pub GetISteamInventory: *const c_void, + pub GetISteamVideo: *const c_void, + pub GetISteamParentalSettings: *const c_void, + pub GetISteamInput: *const c_void, + pub GetISteamParties: *const c_void, + pub GetISteamRemotePlay: *const c_void, + pub DestroyAllInterfaces: *const c_void, +} + +const STEAM_CLIENT_020_VTBL_DEFAULT: ISteamClient020Vtbl = ISteamClient020Vtbl { + CreateSteamPipe, + BReleaseSteamPipe, + ConnectToGlobalUser, + CreateLocalUser, + ReleaseUser: ptr::null(), + GetISteamUser: ptr::null(), + GetISteamGameServer: ptr::null(), + SetLocalIPBinding: ptr::null(), + GetISteamFriends: ptr::null(), + GetISteamUtils, + GetISteamMatchmaking: ptr::null(), + GetISteamMatchmakingServers: ptr::null(), + GetISteamGenericInterface: ptr::null(), + GetISteamUserStats: ptr::null(), + GetISteamGameServerStats: ptr::null(), + GetISteamApps: ptr::null(), + GetISteamNetworking: ptr::null(), + GetISteamRemoteStorage: ptr::null(), + GetISteamScreenshots: ptr::null(), + GetISteamGameSearch: ptr::null(), + RunFrame: ptr::null(), + GetIPCCallCount: ptr::null(), + SetWarningMessageHook: ptr::null(), + BShutdownIfAllPipesClosed: ptr::null(), + GetISteamHTTP: ptr::null(), + DEPRECATED_GetISteamUnifiedMessages: ptr::null(), + GetISteamController: ptr::null(), + GetISteamUGC: ptr::null(), + GetISteamAppList: ptr::null(), + GetISteamMusic: ptr::null(), + GetISteamMusicRemote: ptr::null(), + GetISteamHTMLSurface: ptr::null(), + DEPRECATED_Set_SteamAPI_CPostAPIResultInProcess: ptr::null(), + DEPRECATED_Remove_SteamAPI_CPostAPIResultInProcess: ptr::null(), + Set_SteamAPI_CCheckCallbackRegisteredInProcess: ptr::null(), + GetISteamInventory: ptr::null(), + GetISteamVideo: ptr::null(), + GetISteamParentalSettings: ptr::null(), + GetISteamInput: ptr::null(), + GetISteamParties: ptr::null(), + GetISteamRemotePlay: ptr::null(), + DestroyAllInterfaces: ptr::null(), +}; + +extern "C" fn CreateSteamPipe(self_ptr: *mut ISteamClient) -> SteamPipe { + warn!("STUB: ISteamClient::CreateSteamPipe({:?})", self_ptr); + 0 +} +extern "C" fn BReleaseSteamPipe(self_ptr: *mut ISteamClient, pipe: SteamPipe) -> bool { + warn!( + "STUB: ISteamClient::BReleaseSteamPipe({:?}, {})", + self_ptr, pipe + ); + false +} +extern "C" fn ConnectToGlobalUser(self_ptr: *mut ISteamClient, pipe: SteamPipe) -> SteamUser { + warn!( + "STUB: ISteamClient::ConnectToGlobalUser({:?}, {})", + self_ptr, pipe + ); + 0 +} +extern "C" fn CreateLocalUser( + self_ptr: *mut ISteamClient, + pipe: *const SteamPipe, + account_type: AccountType, +) -> SteamUser { + warn!( + "STUB: ISteamClient::CreateLocalUser({:?}, {:?}, {:?})", + self_ptr, pipe, account_type + ); + 0 +} +extern "C" fn GetISteamUtils( + self_ptr: *mut ISteamClient, + pipe: SteamPipe, + version: *const c_char, +) -> *const ISteamUtils { + warn!( + "STUB: ISteamClient::GetISteamUtils({:?}, {}, {:?})", + self_ptr, pipe, version + ); + ptr::null() +} diff --git a/client/src/abi/steam_utils.rs b/client/src/abi/steam_utils.rs new file mode 100644 index 0000000..a0f6f8a --- /dev/null +++ b/client/src/abi/steam_utils.rs @@ -0,0 +1,59 @@ +#![allow(non_snake_case)] +use std::ffi::c_void; + +/// Various weird steam utility functions +#[repr(C)] +pub struct ISteamUtils { + pub vtbl: ISteamUtilsVtbl, + pub version: u32, +} + +/// Vtable for any version of SteamUtils +#[repr(C)] +pub union ISteamUtilsVtbl { + pub ver017: *const ISteamUtils010Vtbl, +} + +/// Vtable for ISteamUtils010 +#[repr(C)] +pub struct ISteamUtils010Vtbl { + pub GetSecondsSinceAppActive: *const c_void, + pub GetSecondsSinceComputerActive: *const c_void, + pub GetConnectedUniverse: *const c_void, + pub GetServerRealTime: *const c_void, + pub GetIPCountry: *const c_void, + pub GetImageSize: *const c_void, + pub GetImageRGBA: *const c_void, + pub GetCSERIPPort: *const c_void, + pub GetCurrentBatteryPower: *const c_void, + pub GetAppID: *const c_void, + pub SetOverlayNotificationPosition: *const c_void, + pub IsAPICallCompleted: *const c_void, + pub GetAPICallFailureReason: *const c_void, + pub GetAPICallResult: *const c_void, + pub RunFrame: *const c_void, + pub GetIPCCallCount: *const c_void, + pub SetWarningMessageHook: *const c_void, + pub IsOverlayEnabled: *const c_void, + pub BOverlayNeedsPresent: *const c_void, + pub CheckFileSignature: *const c_void, + pub ShowGamepadTextInput: *const c_void, + pub GetEnteredGamepadTextLength: *const c_void, + pub GetEnteredGamepadTextInput: *const c_void, + pub GetSteamUILanguage: *const c_void, + pub IsSteamRunningInVR: *const c_void, + pub SetOverlayNotificationInset: *const c_void, + pub IsSteamInBigPictureMode: *const c_void, + pub StartVRDashboard: *const c_void, + pub IsVRHeadsetStreamingEnabled: *const c_void, + pub SetVRHeadsetStreamingEnabled: *const c_void, + pub IsSteamChinaLauncher: *const c_void, + pub InitFilterText: *const c_void, + pub FilterText: *const c_void, + pub GetIPv6ConnectivityState: *const c_void, + pub IsSteamRunningOnSteamDeck: *const c_void, + pub ShowFloatingGamepadTextInput: *const c_void, + pub SetGameLauncherMode: *const c_void, + pub DismissFloatingGamepadTextInput: *const c_void, + pub DismissGamepadTextInput: *const c_void, +} diff --git a/client/src/lib.rs b/client/src/lib.rs index 7d12d9a..32611a8 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -1,14 +1,7 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} +pub mod abi; +pub mod loader; -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } +#[ctor::ctor] +fn init() { + env_logger::init(); } diff --git a/client/src/loader.rs b/client/src/loader.rs new file mode 100644 index 0000000..115f7ad --- /dev/null +++ b/client/src/loader.rs @@ -0,0 +1,28 @@ +use std::{ + ffi::{c_char, CStr}, + ptr, +}; + +use log::trace; + +use crate::abi::ISteamClient; + +/// Lookup a new interface, should be the first function called +/// # Safety +/// version_ptr must be null or a null-terminated string +#[no_mangle] +pub unsafe extern "C" fn CreateInterface(version_ptr: *const c_char) -> *const ISteamClient { + if version_ptr.is_null() { + trace!("CreateInterface(nullptr)"); + return ptr::null(); + } + // SAFETY: We already know it's not null, there's not much else we can do + let version = unsafe { CStr::from_ptr(version_ptr) }; + trace!("CreateInterface({:?})", version); + + match ISteamClient::new(version) { + // TODO: figure out how the hell to deallocate this + Some(bx) => Box::leak(bx), + None => ptr::null(), + } +}