TapCloudSaveWrapper.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. using System;
  2. using System.Runtime.InteropServices;
  3. using System.Text;
  4. namespace TapSDK.CloudSave.Standalone
  5. {
  6. internal class TapCloudSaveWrapper
  7. {
  8. #if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
  9. internal const string DllName = "cloudsave_sdk";
  10. #elif UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX
  11. internal const string DllName = "libcloudsave_sdk";
  12. #endif
  13. /**
  14. * 初始化接口,只需要调用一次。非线程安全,并发调用可能崩溃。
  15. *
  16. * cfg 初始化配置,JSON 格式:
  17. *
  18. * TapSDK参数格式
  19. * {
  20. * "region": 2,
  21. * "log_to_console": 1,
  22. * "log_level": 3,
  23. * "data_dir": "/tmp",
  24. * "client_id": "***",
  25. * "client_token": "***",
  26. * "ua": "TapSDK-Android/3.28.0",
  27. * "lang": "zh-CN",
  28. * "platform": "Android",
  29. * "device_id": "123456",
  30. * "sdk_artifact": "Android",
  31. * "sdk_module_ver": "4.6.0",
  32. * "sdk_token": {
  33. * "kid": "***",
  34. * "key": "***"
  35. * }
  36. * }
  37. *
  38. *
  39. * - region 取值:0 国内、1 海外、2 RND、3 海外RND
  40. * - log_to_console 是否输出到控制台:0 不输出、1 输出。
  41. * - log_level 取值:1 Trace、2 Debug、3 Info、4 Warn、5 Error、6 完全不输出
  42. * - data_dir 保存本地缓存和日志文件的目录,不允许为空
  43. * - client_id 不允许为空
  44. * - client_token 不允许为空
  45. * - ua user agent,不允许为空
  46. * - lang 语言,允许为空
  47. * - platform 不允许为空,TapSDK专用参数
  48. * - device_id 设备ID,不允许为空,TapSDK专用参数
  49. * - sdk_artifact 不允许为空,TapSDK专用参数
  50. * - sdk_module_ver 不允许为空,TapSDK专用参数
  51. * - sdk_token 登录态鉴权token,允许为空,TapSDK专用参数
  52. * - kid mac_key id,不允许为空
  53. * - key mac密钥,不允许为空
  54. * - runtime_ver 宿主版本,不允许为空,Tap Miniapp专用参数
  55. * - access_token 登录态鉴权token,允许为空,Tap Miniapp专用参数
  56. * - kid mac_key id,不允许为空
  57. * - key mac密钥,不允许为空
  58. *
  59. * 成功返回 0,失败返回 -1
  60. */
  61. [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
  62. internal static extern int TapSdkCloudSaveInitialize(string cfg);
  63. /**
  64. * 用于释放TapSdkCloudSaveCreateArchive、TapSdkCloudSaveGetArchiveList、TapSdkCloudSaveGetArchiveData等函数返回的堆内存
  65. *
  66. * @param data 需要释放的内存
  67. */
  68. [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
  69. internal static extern void TapSdkCloudSaveFreeMemory(IntPtr intPtr);
  70. /**
  71. * access token发生变化(比如切换账号)时调用。收到接口报access_denied错误时,SDK使用方需要重新登录,然后调用该接口更新access token
  72. *
  73. * @param token 最新的access token,传空指针或"{}"表示用户退出登录。格式如下:
  74. * {
  75. * "kid": "***",
  76. * "key": "***"
  77. * }
  78. *
  79. * @return 成功返回 0,失败(通常是JSON格式错误)返回 -1
  80. */
  81. [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
  82. internal static extern int TapSdkCloudSaveUpdateAccessToken(string token);
  83. /**
  84. * 创建云存档,同步等待服务端返回,然后更新本地存档缓存。不允许并发调用,否则直接抛错
  85. *
  86. * @param metadata 存档元信息,JSON格式
  87. * {
  88. * "name": "存档名,60字节以内,不允许空,不允许汉字",
  89. * "summary": "存档描述,500字节以内,不允许空",
  90. * "extra": "用户自定义信息,1000字节以内,允许空",
  91. * "playtime": 0
  92. * }
  93. * - playtime 秒级
  94. * @param archiveData 存档二进制数据,不允许空指针
  95. * @param archiveDataSize saveData的大小(字节),不允许为0
  96. * @param coverData 封面二进制数据,允许空指针
  97. * @param coverDataSize saveData的大小(字节),允许为0
  98. * @param extraParams Tap Miniapp专用参数,JSON格式。其他接入方传空指针即可
  99. * {
  100. * "X-UA": "***",
  101. * "miniappId": "***"
  102. * }
  103. *
  104. * @return JSON格式,服务端生成的云存档UUID和FileID,或者错误信息。用完后,调用方需要调用TapSdkCloudSaveFreeMemory来释放内存
  105. * {
  106. * "data": {
  107. * "uuid": "存档UUID",
  108. * "file_id": "存档文件ID,用于下载文件"
  109. * },
  110. * "now": 1748340775,
  111. * "success": true
  112. * }
  113. * @note 对于Tap Miniapp,JSON字段使用驼峰风格。比如上面的返回示例,对于Tap Miniapp,是"fileId"而不是"file_id"。
  114. */
  115. [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
  116. internal static extern IntPtr TapSdkCloudSaveCreateArchive(
  117. string metadata,
  118. byte[] archiveData,
  119. int archiveDataSize,
  120. byte[] coverData,
  121. int coverDataSize,
  122. string extraParams
  123. );
  124. /**
  125. * 更新云存档,同步等待服务端返回,然后更新本地存档缓存。不允许并发调用,否则直接抛错
  126. *
  127. * @param archiveUUID TapSdkCloudSaveCreateArchive()返回的云存档UUID,比如:"1bffaa173dde2e7afb5adf6442971961"。必须先创建云存档成功后,通过云存档UUID来更新
  128. * @param metadata 存档元信息,JSON格式
  129. * {
  130. * "name": "存档名,60字节以内,不允许空,不允许汉字",
  131. * "summary": "存档描述,500字节以内,不允许空",
  132. * "extra": "用户自定义信息,1000字节以内,允许空",
  133. * "playtime": 0
  134. * }
  135. * - playtime 秒级
  136. * @param archiveData 存档二进制数据,不允许空指针
  137. * @param archiveDataSize saveData的大小(字节),不允许为0
  138. * @param coverData 封面二进制数据,允许空指针
  139. * @param coverDataSize saveData的大小(字节),允许为0
  140. * @param extraParams Tap Miniapp专用参数,JSON格式。其他接入方传空指针即可
  141. * {
  142. * "X-UA": "***",
  143. * "miniappId": "***"
  144. * }
  145. *
  146. * @return JSON格式,服务端生成的云存档UUID和FileID,或者错误信息。用完后,调用方需要调用TapSdkCloudSaveFreeMemory来释放内存
  147. * {
  148. * "data": {
  149. * "uuid": "存档UUID",
  150. * "file_id": "存档文件ID,用于下载文件"
  151. * },
  152. * "now": 1748340775,
  153. * "success": true
  154. * }
  155. * @note 对于Tap Miniapp,JSON字段使用驼峰风格。比如上面的返回示例,对于Tap Miniapp,是"fileId"而不是"file_id"。
  156. */
  157. [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
  158. internal static extern IntPtr TapSdkCloudSaveUpdateArchive(
  159. string archiveUUID,
  160. string metadata,
  161. byte[] archiveData,
  162. int archiveDataSize,
  163. byte[] coverData,
  164. int coverDataSize,
  165. string extraParams
  166. );
  167. /**
  168. * 发起删除存档请求,同步等待服务端返回,然后删除本地存档缓存。允许并发调用
  169. *
  170. * @param archiveUUID TapSdkCloudSaveCreateArchive()/TapSdkCloudSaveGetArchiveList()返回的云存档UUID,比如:"1bffaa173dde2e7afb5adf6442971961"。必须先创建云存档成功后,通过云存档UUID来删除
  171. * @param extraParams Tap Miniapp专用参数,JSON格式。其他接入方传空指针即可
  172. * {
  173. * "X-UA": "***",
  174. * "miniappId": "***"
  175. * }
  176. *
  177. * @return JSON格式,返回成功或者错误信息。用完后,调用方需要调用TapSdkCloudSaveFreeMemory来释放内存
  178. * {
  179. * "data": {
  180. * "uuid": "被删掉的云存档UUID"
  181. * },
  182. * "now": 1748487810,
  183. * "success": true
  184. * }
  185. */
  186. [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
  187. internal static extern IntPtr TapSdkCloudSaveDeleteArchive(
  188. string archiveUUID,
  189. string extraParams
  190. );
  191. /**
  192. * 获取存档元信息列表,同步等待服务端返回。允许并发调用
  193. *
  194. * @param extraParams Tap Miniapp专用参数,JSON格式。其他接入方传空指针即可
  195. * {
  196. * "X-UA": "***",
  197. * "miniappId": "***"
  198. * }
  199. *
  200. * @return JSON格式,返回存档元信息列表,或者错误信息。用完后,需要调用TapSdkCloudSaveFreeMemory来释放内存
  201. * {
  202. * "data": {
  203. * "saves": [
  204. * {
  205. * "uuid": "738b6c08bdbe459b96607dd10f83d177",
  206. * "file_id": "738b6c08bdbe459b96607dd10f83d177",
  207. * "name": "save",
  208. * "save_size": 184237,
  209. * "cover_size": 7138,
  210. * "summary": "i love this game",
  211. * "extra": "what the hell",
  212. * "playtime": 0,
  213. * "created_time": 1748587677,
  214. * "modified_time": 1748587677
  215. * }
  216. * ]
  217. * },
  218. * "now": 1748588091,
  219. * "success": true
  220. * }
  221. * - playtime 秒级
  222. * @note 对于Tap Miniapp,JSON字段使用驼峰风格。比如上面的返回示例,对于Tap Miniapp,是"fileId/saveSize"而不是"file_id/save_size"。
  223. */
  224. [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
  225. internal static extern IntPtr TapSdkCloudSaveGetArchiveList(string extraParams);
  226. /**
  227. * 读取存档文件,优先使用本地缓存,不存在才下载
  228. *
  229. * @param archiveUUID TapSdkCloudSaveCreateArchive()/TapSdkCloudSaveGetArchiveList()返回的云存档UUID,必须先创建云存档成功后,才能读取
  230. * @param archiveFileID TapSdkCloudSaveCreateArchive()/TapSdkCloudSaveGetArchiveList()返回的存档文件FileID,必须先创建云存档成功后,才能读取
  231. * @param archiveDataSize 出参,实际读取到的存档文件大小。如果读取失败,则其值会是负数
  232. * @param extraParams Tap Miniapp专用参数,JSON格式。其他接入方传空指针即可
  233. * {
  234. * "X-UA": "***",
  235. * "miniappId": "***"
  236. * }
  237. *
  238. * @return 成功时saveSize是实际读取到的存档文件大小,返回存档文件内容;失败时saveSize是负数,返回JSON格式的错误信息,格式如下。用完后,需要调用TapSdkCloudSaveFreeMemory来释放内存
  239. * {
  240. * "data": {
  241. * "code": -1,
  242. * "msg": "InvalidArgument: BINDING: Key: 'GetSavesRequest.uuid' Error:Field validation for 'uuid' failed on the 'len' tag",
  243. * "error": "invalid_request",
  244. * "error_description": "InvalidArgument: BINDING: Key: 'GetSavesRequest.uuid' Error:Field validation for 'uuid' failed on the 'len' tag"
  245. * },
  246. * "now": 1748915185,
  247. * "success": false
  248. * }
  249. * @note 对于Tap Miniapp,JSON字段使用驼峰风格。比如上面的返回示例,对于Tap Miniapp,是"errorDescription"而不是"error_description"。
  250. */
  251. [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
  252. internal static extern IntPtr TapSdkCloudSaveGetArchiveData(
  253. string archiveUUID,
  254. string archiveFileID,
  255. out int archiveDataSize,
  256. string extraParams
  257. );
  258. /**
  259. * 读取封面文件,优先使用本地缓存,不存在才下载
  260. *
  261. * @param archiveUUID TapSdkCloudSaveCreateArchive()/TapSdkCloudSaveGetArchiveList()返回的云存档UUID,必须先创建云存档成功后,才能读取
  262. * @param archiveFileID TapSdkCloudSaveCreateArchive()/TapSdkCloudSaveGetArchiveList()返回的存档文件FileID,必须先创建云存档成功后,才能读取
  263. * @param coverSize 出参,实际读取到的封面文件大小。如果读取失败,则其值会是负数
  264. * @param extraParams Tap Miniapp专用参数,JSON格式。其他接入方传空指针即可
  265. * {
  266. * "X-UA": "***",
  267. * "miniappId": "***"
  268. * }
  269. *
  270. * @return 成功时coverSize是实际读取到的封面文件大小,返回cover文件内容;失败时coverSize是负数,返回JSON格式的错误信息,格式如下。用完后,需要调用TapSdkCloudSaveFreeMemory来释放内存
  271. * {
  272. * "data": {
  273. * "code": -1,
  274. * "msg": "InvalidArgument: BINDING: Key: 'GetSavesRequest.uuid' Error:Field validation for 'uuid' failed on the 'len' tag",
  275. * "error": "invalid_request",
  276. * "error_description": "InvalidArgument: BINDING: Key: 'GetSavesRequest.uuid' Error:Field validation for 'uuid' failed on the 'len' tag"
  277. * },
  278. * "now": 1748915185,
  279. * "success": false
  280. * }
  281. * @note 对于Tap Miniapp,JSON字段使用驼峰风格。比如上面的返回示例,对于Tap Miniapp,是"errorDescription"而不是"error_description"。
  282. */
  283. [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
  284. internal static extern IntPtr TapSdkCloudSaveGetArchiveCover(
  285. string archiveUUID,
  286. string archiveFileID,
  287. out int coverSize,
  288. string extraParams
  289. );
  290. /**
  291. * 设置日志等级
  292. * - logLevel 日志等级:1 trace、2 debug、3 info、4 warn、5 error、> 5 不打日志。建议调试时设为1,正式版设为3。
  293. * - logToConsole 是否输出到控制台:0 不输出、1 输出。
  294. */
  295. [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
  296. internal static extern void TapSdkCloudSaveSetLogLevel(int logLevel, int logToConsole);
  297. /**
  298. * 代码版本,如:1.2.5
  299. *
  300. * @return SDK版本号,全局静态变量,无需释放内存
  301. */
  302. [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
  303. internal static extern IntPtr TapSdkCloudSaveVersion();
  304. /**
  305. * git commit 版本,如:98f5d81a0fdcab9a755878b3e825c2cb510e5196
  306. *
  307. * @return git commit版本,全局静态变量,无需释放内存
  308. */
  309. [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
  310. internal static extern IntPtr TapSdkCloudSaveGitCommit();
  311. internal static string CreateArchive(
  312. string metadata,
  313. byte[] archiveData,
  314. int archiveDataSize,
  315. byte[] coverData,
  316. int coverDataSize
  317. )
  318. {
  319. IntPtr intPtr = TapSdkCloudSaveCreateArchive(
  320. metadata,
  321. archiveData,
  322. archiveDataSize,
  323. coverData,
  324. coverDataSize,
  325. null
  326. );
  327. try
  328. {
  329. string result = GetUTF8String(intPtr);
  330. return result;
  331. }
  332. finally
  333. {
  334. TapSdkCloudSaveFreeMemory(intPtr);
  335. }
  336. }
  337. internal static string UpdateArchive(
  338. string archiveUUID,
  339. string metadata,
  340. byte[] archiveData,
  341. int archiveDataSize,
  342. byte[] coverData,
  343. int coverDataSize
  344. )
  345. {
  346. IntPtr intPtr = TapSdkCloudSaveUpdateArchive(
  347. archiveUUID,
  348. metadata,
  349. archiveData,
  350. archiveDataSize,
  351. coverData,
  352. coverDataSize,
  353. null
  354. );
  355. try
  356. {
  357. string result = GetUTF8String(intPtr);
  358. return result;
  359. }
  360. finally
  361. {
  362. TapSdkCloudSaveFreeMemory(intPtr);
  363. }
  364. }
  365. internal static string DeleteArchive(string archiveUUID)
  366. {
  367. IntPtr intPtr = TapSdkCloudSaveDeleteArchive(archiveUUID, null);
  368. try
  369. {
  370. string result = GetUTF8String(intPtr);
  371. return result;
  372. }
  373. finally
  374. {
  375. TapSdkCloudSaveFreeMemory(intPtr);
  376. }
  377. }
  378. internal static string GetArchiveList()
  379. {
  380. IntPtr intPtr = TapSdkCloudSaveGetArchiveList(null);
  381. try
  382. {
  383. string result = GetUTF8String(intPtr);
  384. return result;
  385. }
  386. finally
  387. {
  388. TapSdkCloudSaveFreeMemory(intPtr);
  389. }
  390. }
  391. internal static byte[] GetArchiveData(
  392. string archiveUUID,
  393. string archiveFileID,
  394. out int archiveDataSize
  395. )
  396. {
  397. int dataSize = 0;
  398. IntPtr intPtr = TapSdkCloudSaveGetArchiveData(
  399. archiveUUID,
  400. archiveFileID,
  401. out dataSize,
  402. null
  403. );
  404. try
  405. {
  406. byte[] result;
  407. if (dataSize >= 0)
  408. {
  409. if (dataSize == 0)
  410. {
  411. result = new byte[0];
  412. }
  413. else
  414. {
  415. result = GetOriginByte(intPtr, dataSize);
  416. }
  417. }
  418. else
  419. {
  420. result = GetUTF8Byte(intPtr);
  421. }
  422. archiveDataSize = dataSize;
  423. return result;
  424. }
  425. finally
  426. {
  427. TapSdkCloudSaveFreeMemory(intPtr);
  428. }
  429. }
  430. internal static byte[] GetArchiveCover(
  431. string archiveUUID,
  432. string archiveFileID,
  433. out int coverSize
  434. )
  435. {
  436. int dataSize;
  437. byte[] result;
  438. IntPtr intPtr = TapSdkCloudSaveGetArchiveCover(
  439. archiveUUID,
  440. archiveFileID,
  441. out dataSize,
  442. null
  443. );
  444. try
  445. {
  446. coverSize = dataSize;
  447. if (dataSize >= 0)
  448. {
  449. if (dataSize == 0)
  450. {
  451. result = new byte[0];
  452. }
  453. else
  454. {
  455. result = GetOriginByte(intPtr, dataSize);
  456. }
  457. }
  458. else
  459. {
  460. result = GetUTF8Byte(intPtr);
  461. }
  462. return result;
  463. }
  464. finally
  465. {
  466. TapSdkCloudSaveFreeMemory(intPtr);
  467. }
  468. }
  469. private static string GetUTF8String(IntPtr ptr)
  470. {
  471. byte[] buffer = GetUTF8Byte(ptr);
  472. // 3. 按 UTF-8 解码
  473. return Encoding.UTF8.GetString(buffer);
  474. }
  475. private static byte[] GetUTF8Byte(IntPtr ptr)
  476. {
  477. // 1. 获取字符串长度(假设以 null 结尾)
  478. int len = 0;
  479. while (Marshal.ReadByte(ptr, len) != 0)
  480. {
  481. len++;
  482. }
  483. // 2. 复制字节到托管数组
  484. byte[] buffer = new byte[len];
  485. Marshal.Copy(ptr, buffer, 0, len);
  486. return buffer;
  487. }
  488. private static byte[] GetOriginByte(IntPtr ptr, int length)
  489. {
  490. byte[] copyByte = new byte[length];
  491. Marshal.Copy(ptr, copyByte, 0, length);
  492. return copyByte;
  493. }
  494. }
  495. }