Skip to main content

tapsdk_pc_sys/
lib.rs

1//! Raw FFI bindings to TapTap PC SDK
2//!
3//! This crate provides unsafe, low-level bindings to the TapTap PC SDK.
4//! For a safe, high-level API, use the `tapsdk-pc` crate instead.
5//!
6//! # Platform Support
7//!
8//! **Windows only.** This SDK only supports Windows (x86_64).
9//! On macOS and Linux, all functions will panic with an "unsupported platform" error.
10
11#![allow(non_upper_case_globals)]
12#![allow(non_camel_case_types)]
13#![allow(non_snake_case)]
14#![allow(dead_code)]
15#![allow(clippy::all)]
16
17// Include the generated bindings (real on Windows, stubs on other platforms)
18include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
19
20// Re-export commonly used constants for convenience
21
22/// SDK initialization result codes
23pub mod init_result {
24    /// Initialization successful
25    pub const OK: u32 = 0;
26    /// Generic failure
27    pub const FAILED_GENERIC: u32 = 1;
28    /// TapTap platform not found
29    pub const NO_PLATFORM: u32 = 2;
30    /// Not launched by TapTap platform
31    pub const NOT_LAUNCHED_BY_PLATFORM: u32 = 3;
32    /// Platform version mismatch
33    pub const PLATFORM_VERSION_MISMATCH: u32 = 4;
34}
35
36/// Authorization result codes
37pub mod authorize_result {
38    /// Unknown error
39    pub const UNKNOWN: u32 = 0;
40    /// Authorization flow started successfully
41    pub const OK: u32 = 1;
42    /// Failed to start authorization
43    pub const FAILED: u32 = 2;
44    /// Authorization already in progress
45    pub const IN_FLIGHT: u32 = 3;
46}
47
48/// Event ID constants
49pub mod event_id {
50    /// Unknown event
51    pub const UNKNOWN: u32 = 0;
52    /// System state changed
53    pub const SYSTEM_STATE_CHANGED: u32 = 1;
54    /// Authorization finished
55    pub const AUTHORIZE_FINISHED: u32 = 2002;
56    /// Game playable status changed
57    pub const GAME_PLAYABLE_STATUS_CHANGED: u32 = 4001;
58    /// DLC playable status changed
59    pub const DLC_PLAYABLE_STATUS_CHANGED: u32 = 4002;
60    /// Cloud save list response
61    pub const CLOUD_SAVE_LIST: u32 = 6001;
62    /// Cloud save create response
63    pub const CLOUD_SAVE_CREATE: u32 = 6002;
64    /// Cloud save update response
65    pub const CLOUD_SAVE_UPDATE: u32 = 6003;
66    /// Cloud save delete response
67    pub const CLOUD_SAVE_DELETE: u32 = 6004;
68    /// Cloud save get data response
69    pub const CLOUD_SAVE_GET_DATA: u32 = 6005;
70    /// Cloud save get cover response
71    pub const CLOUD_SAVE_GET_COVER: u32 = 6006;
72}
73
74/// System state constants
75pub mod system_state {
76    /// Unknown state
77    pub const UNKNOWN: u32 = 0;
78    /// Platform is online
79    pub const PLATFORM_ONLINE: u32 = 1;
80    /// Platform is offline
81    pub const PLATFORM_OFFLINE: u32 = 2;
82    /// Platform is shutting down
83    pub const PLATFORM_SHUTDOWN: u32 = 3;
84}
85
86/// Error codes
87pub mod error_code {
88    /// Success
89    pub const SUCCESS: i64 = 0;
90    /// Unknown error
91    pub const UNKNOWN: i64 = 1;
92    /// Unauthorized
93    pub const UNAUTHORIZED: i64 = 2;
94    /// Method not allowed
95    pub const METHOD_NOT_ALLOWED: i64 = 3;
96    /// Unimplemented
97    pub const UNIMPLEMENTED: i64 = 4;
98    /// Invalid arguments
99    pub const INVALID_ARGUMENTS: i64 = 5;
100    /// Forbidden
101    pub const FORBIDDEN: i64 = 6;
102    /// User is deactivated
103    pub const USER_IS_DEACTIVATED: i64 = 7;
104    /// Internal server error
105    pub const INTERNAL_SERVER_ERROR: i64 = 8;
106    /// Internal SDK error
107    pub const INTERNAL_SDK_ERROR: i64 = 9;
108    /// Network error
109    pub const NETWORK_ERROR: i64 = 10;
110
111    // Cloud save specific errors (400000-499999)
112    /// Invalid file size
113    pub const CLOUD_SAVE_INVALID_FILE_SIZE: i64 = 400000;
114    /// Upload rate limit exceeded
115    pub const CLOUD_SAVE_UPLOAD_RATE_LIMIT: i64 = 400001;
116    /// File not found
117    pub const CLOUD_SAVE_FILE_NOT_FOUND: i64 = 400002;
118    /// File count limit per client exceeded
119    pub const CLOUD_SAVE_FILE_COUNT_LIMIT_PER_CLIENT: i64 = 400003;
120    /// Storage size limit per client exceeded
121    pub const CLOUD_SAVE_STORAGE_SIZE_LIMIT_PER_CLIENT: i64 = 400004;
122    /// Total storage size limit exceeded
123    pub const CLOUD_SAVE_TOTAL_STORAGE_SIZE_LIMIT: i64 = 400005;
124    /// Timeout
125    pub const CLOUD_SAVE_TIMEOUT: i64 = 400006;
126    /// Concurrent call disallowed
127    pub const CLOUD_SAVE_CONCURRENT_CALL_DISALLOWED: i64 = 400007;
128    /// Storage server error
129    pub const CLOUD_SAVE_STORAGE_SERVER_ERROR: i64 = 400008;
130    /// Invalid name
131    pub const CLOUD_SAVE_INVALID_NAME: i64 = 400009;
132}
133
134/// Cloud save result codes
135pub mod cloudsave_result {
136    /// Request initiated successfully
137    pub const OK: u32 = 0;
138    /// SDK not initialized
139    pub const UNINITIALIZED: u32 = 1;
140    /// TapTap client not running
141    pub const NO_TAPTAP_CLIENT: u32 = 2;
142    /// TapTap client outdated
143    pub const TAPTAP_CLIENT_OUTDATED: u32 = 3;
144    /// Invalid argument
145    pub const INVALID_ARGUMENT: u32 = 4;
146    /// SDK internal failure
147    pub const SDK_FAILED: u32 = 5;
148    /// Failed to read save file
149    pub const FAILED_TO_READ_SAVE_FILE: u32 = 6;
150    /// Save file too large (>10MB)
151    pub const SAVE_FILE_TOO_LARGE: u32 = 7;
152    /// Failed to read cover file
153    pub const FAILED_TO_READ_COVER_FILE: u32 = 8;
154    /// Cover file too large (>512KB)
155    pub const COVER_FILE_TOO_LARGE: u32 = 9;
156}
157
158/// Check if the current platform is supported
159#[inline]
160pub fn is_platform_supported() -> bool {
161    cfg!(target_os = "windows")
162}
163
164/// Returns an error message for unsupported platforms
165#[inline]
166pub fn unsupported_platform_error() -> &'static str {
167    "TapTap PC SDK is only supported on Windows. This platform (macOS/Linux) is not supported."
168}
169
170#[cfg(all(test, target_os = "windows"))]
171mod tests {
172    use super::*;
173    use std::ffi::CString;
174
175    #[test]
176    fn test_constants() {
177        // Verify our constant definitions match expected values
178        assert_eq!(init_result::OK, 0);
179        assert_eq!(init_result::NO_PLATFORM, 2);
180        assert_eq!(event_id::AUTHORIZE_FINISHED, 2002);
181        assert_eq!(error_code::SUCCESS, 0);
182        assert_eq!(cloudsave_result::OK, 0);
183    }
184
185    #[test]
186    fn test_dll_loads_and_functions_exist() {
187        // This test verifies the DLL is properly linked and functions are callable
188        // The functions should be resolvable even if they fail at runtime
189
190        // Test TapSDK_RestartAppIfNecessary - should return false since we're not in TapTap
191        let client_id = CString::new("test_client_id").unwrap();
192        let result = unsafe { TapSDK_RestartAppIfNecessary(client_id.as_ptr()) };
193        // Should return false since we're not launched from TapTap
194        assert!(
195            !result,
196            "RestartAppIfNecessary should return false when not in TapTap"
197        );
198    }
199
200    #[test]
201    fn test_sdk_init_without_taptap() {
202        // Test that SDK init fails gracefully without TapTap client
203        let pub_key = CString::new("test_public_key").unwrap();
204        let mut err_msg: [std::os::raw::c_char; 1024] = [0; 1024];
205
206        let result = unsafe { TapSDK_Init(err_msg.as_mut_ptr() as *mut _, pub_key.as_ptr()) };
207
208        // Without TapTap client, should return NoPlatform (2) or NotLaunchedByPlatform (3)
209        assert!(
210            result == init_result::NO_PLATFORM || result == init_result::NOT_LAUNCHED_BY_PLATFORM,
211            "SDK init should fail with NoPlatform or NotLaunchedByPlatform, got: {}",
212            result
213        );
214    }
215
216    #[test]
217    fn test_ownership_check_without_init() {
218        // Ownership check should safely return false when SDK not initialized
219        let owned = unsafe { TapApps_IsOwned() };
220        assert!(!owned, "IsOwned should return false when SDK not initialized");
221    }
222
223    #[test]
224    fn test_get_client_id_without_init() {
225        // GetClientID should safely return false when SDK not initialized
226        let mut buffer: [std::os::raw::c_char; 256] = [0; 256];
227        let result = unsafe { TapSDK_GetClientID(buffer.as_mut_ptr()) };
228        assert!(
229            !result,
230            "GetClientID should return false when SDK not initialized"
231        );
232    }
233
234    #[test]
235    fn test_get_open_id_without_init() {
236        // GetOpenID should safely return false when SDK not initialized
237        let mut buffer: [std::os::raw::c_char; 256] = [0; 256];
238        let result = unsafe { TapUser_GetOpenID(buffer.as_mut_ptr()) };
239        assert!(
240            !result,
241            "GetOpenID should return false when SDK not initialized"
242        );
243    }
244
245    #[test]
246    fn test_dlc_check_without_init() {
247        // DLC ownership check should safely return false when SDK not initialized
248        let dlc_id = CString::new("test_dlc").unwrap();
249        let owned = unsafe { TapDLC_IsOwned(dlc_id.as_ptr()) };
250        assert!(
251            !owned,
252            "IsDlcOwned should return false when SDK not initialized"
253        );
254    }
255
256    #[test]
257    fn test_run_callbacks_without_init() {
258        // RunCallbacks should be safe to call even without initialization
259        // This just verifies it doesn't crash
260        unsafe { TapSDK_RunCallbacks() };
261    }
262
263    #[test]
264    fn test_struct_sizes() {
265        // Verify struct sizes are reasonable (helps catch alignment issues)
266        assert!(
267            std::mem::size_of::<TapSDK_Error>() >= 16,
268            "TapSDK_Error should be at least 16 bytes"
269        );
270        assert!(
271            std::mem::size_of::<AuthorizeFinishedResponse>() > 0,
272            "AuthorizeFinishedResponse should have size"
273        );
274        assert!(
275            std::mem::size_of::<TapCloudSaveInfo>() > 0,
276            "TapCloudSaveInfo should have size"
277        );
278    }
279
280    #[test]
281    fn test_cloudsave_without_init() {
282        // CloudSave functions should return appropriate error when SDK not initialized
283        let handle = unsafe { TapCloudSave() };
284
285        if !handle.is_null() {
286            let result = unsafe { TapCloudSave_AsyncList(handle, 1) };
287            // Should return Uninitialized or SdkFailed
288            assert!(
289                result == cloudsave_result::UNINITIALIZED
290                    || result == cloudsave_result::SDK_FAILED
291                    || result == cloudsave_result::NO_TAPTAP_CLIENT,
292                "CloudSave list should fail when not initialized, got: {}",
293                result
294            );
295        }
296    }
297}
298
299#[cfg(all(test, not(target_os = "windows")))]
300mod tests {
301    use super::*;
302
303    #[test]
304    fn test_platform_not_supported() {
305        assert!(!is_platform_supported());
306    }
307
308    #[test]
309    #[should_panic(expected = "only supported on Windows")]
310    fn test_functions_panic_on_unsupported_platform() {
311        use std::ffi::CString;
312        let client_id = CString::new("test").unwrap();
313        unsafe {
314            TapSDK_RestartAppIfNecessary(client_id.as_ptr());
315        }
316    }
317}