storage.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026
  1. #include <game/storage.h>
  2. #include <stdio.h>
  3. #include <furi.h>
  4. #include <furi_hal.h>
  5. #include <stdint.h>
  6. #include <stdbool.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <limits.h>
  11. bool save_uint32(const char *path_name, uint32_t value)
  12. {
  13. char buffer[32];
  14. snprintf(buffer, sizeof(buffer), "%lu", value);
  15. return save_char(path_name, buffer);
  16. }
  17. // Helper function to save an int8_t
  18. bool save_int8(const char *path_name, int8_t value)
  19. {
  20. char buffer[32];
  21. snprintf(buffer, sizeof(buffer), "%d", value);
  22. return save_char(path_name, buffer);
  23. }
  24. // Helper function to save a float
  25. bool save_float(const char *path_name, float value)
  26. {
  27. char buffer[32];
  28. snprintf(buffer, sizeof(buffer), "%.6f", (double)value); // Limit to 6 decimal places
  29. return save_char(path_name, buffer);
  30. }
  31. bool save_player_context(PlayerContext *player_context)
  32. {
  33. if (!player_context)
  34. {
  35. FURI_LOG_E(TAG, "Invalid player context");
  36. return false;
  37. }
  38. // Create the directory for saving settings
  39. char directory_path[256];
  40. snprintf(directory_path, sizeof(directory_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world/data/player");
  41. // Create the directory
  42. Storage *storage = furi_record_open(RECORD_STORAGE);
  43. storage_common_mkdir(storage, directory_path);
  44. furi_record_close(RECORD_STORAGE);
  45. // 1. Username (String)
  46. if (!save_char("player/username", player_context->username))
  47. {
  48. FURI_LOG_E(TAG, "Failed to save player username");
  49. return false;
  50. }
  51. // 2. Level (uint32_t)
  52. if (!save_uint32("player/level", player_context->level))
  53. {
  54. FURI_LOG_E(TAG, "Failed to save player level");
  55. return false;
  56. }
  57. // 3. XP (uint32_t)
  58. if (!save_uint32("player/xp", player_context->xp))
  59. {
  60. FURI_LOG_E(TAG, "Failed to save player xp");
  61. return false;
  62. }
  63. // 4. Health (uint32_t)
  64. if (!save_uint32("player/health", player_context->health))
  65. {
  66. FURI_LOG_E(TAG, "Failed to save player health");
  67. return false;
  68. }
  69. // 5. Strength (uint32_t)
  70. if (!save_uint32("player/strength", player_context->strength))
  71. {
  72. FURI_LOG_E(TAG, "Failed to save player strength");
  73. return false;
  74. }
  75. // 6. Max Health (uint32_t)
  76. if (!save_uint32("player/max_health", player_context->max_health))
  77. {
  78. FURI_LOG_E(TAG, "Failed to save player max health");
  79. return false;
  80. }
  81. // 7. Health Regen (uint32_t)
  82. if (!save_uint32("player/health_regen", player_context->health_regen))
  83. {
  84. FURI_LOG_E(TAG, "Failed to save player health regen");
  85. return false;
  86. }
  87. // 8. Elapsed Health Regen (float)
  88. if (!save_float("player/elapsed_health_regen", player_context->elapsed_health_regen))
  89. {
  90. FURI_LOG_E(TAG, "Failed to save player elapsed health regen");
  91. return false;
  92. }
  93. // 9. Attack Timer (float)
  94. if (!save_float("player/attack_timer", player_context->attack_timer))
  95. {
  96. FURI_LOG_E(TAG, "Failed to save player attack timer");
  97. return false;
  98. }
  99. // 10. Elapsed Attack Timer (float)
  100. if (!save_float("player/elapsed_attack_timer", player_context->elapsed_attack_timer))
  101. {
  102. FURI_LOG_E(TAG, "Failed to save player elapsed attack timer");
  103. return false;
  104. }
  105. // 11. Direction (enum PlayerDirection)
  106. {
  107. char direction_str[2];
  108. switch (player_context->direction)
  109. {
  110. case PLAYER_UP:
  111. strncpy(direction_str, "0", sizeof(direction_str));
  112. break;
  113. case PLAYER_DOWN:
  114. strncpy(direction_str, "1", sizeof(direction_str));
  115. break;
  116. case PLAYER_LEFT:
  117. strncpy(direction_str, "2", sizeof(direction_str));
  118. break;
  119. case PLAYER_RIGHT:
  120. default:
  121. strncpy(direction_str, "3", sizeof(direction_str));
  122. break;
  123. }
  124. direction_str[1] = '\0'; // Ensure null termination
  125. if (!save_char("player/direction", direction_str))
  126. {
  127. FURI_LOG_E(TAG, "Failed to save player direction");
  128. return false;
  129. }
  130. }
  131. // 12. State (enum PlayerState)
  132. {
  133. char state_str[2];
  134. switch (player_context->state)
  135. {
  136. case PLAYER_IDLE:
  137. strncpy(state_str, "0", sizeof(state_str));
  138. break;
  139. case PLAYER_MOVING:
  140. strncpy(state_str, "1", sizeof(state_str));
  141. break;
  142. case PLAYER_ATTACKING:
  143. strncpy(state_str, "2", sizeof(state_str));
  144. break;
  145. case PLAYER_ATTACKED:
  146. strncpy(state_str, "3", sizeof(state_str));
  147. break;
  148. case PLAYER_DEAD:
  149. strncpy(state_str, "4", sizeof(state_str));
  150. break;
  151. default:
  152. strncpy(state_str, "5", sizeof(state_str)); // Assuming '5' for unknown states
  153. break;
  154. }
  155. state_str[1] = '\0'; // Ensure null termination
  156. if (!save_char("player/state", state_str))
  157. {
  158. FURI_LOG_E(TAG, "Failed to save player state");
  159. return false;
  160. }
  161. }
  162. // 13. Start Position X (float)
  163. if (!save_float("player/start_position_x", player_context->start_position.x))
  164. {
  165. FURI_LOG_E(TAG, "Failed to save player start position x");
  166. return false;
  167. }
  168. // 14. Start Position Y (float)
  169. if (!save_float("player/start_position_y", player_context->start_position.y))
  170. {
  171. FURI_LOG_E(TAG, "Failed to save player start position y");
  172. return false;
  173. }
  174. // 15. dx (int8_t)
  175. if (!save_int8("player/dx", player_context->dx))
  176. {
  177. FURI_LOG_E(TAG, "Failed to save player dx");
  178. return false;
  179. }
  180. // 16. dy (int8_t)
  181. if (!save_int8("player/dy", player_context->dy))
  182. {
  183. FURI_LOG_E(TAG, "Failed to save player dy");
  184. return false;
  185. }
  186. return true;
  187. }
  188. // Helper function to load a string safely
  189. bool load_string(const char *path_name, char *buffer, size_t buffer_size)
  190. {
  191. if (!path_name || !buffer || buffer_size == 0)
  192. {
  193. FURI_LOG_E(TAG, "Invalid arguments to load_string");
  194. return false;
  195. }
  196. if (!load_char(path_name, buffer, buffer_size))
  197. {
  198. FURI_LOG_E(TAG, "Failed to load string from path: %s", path_name);
  199. return false;
  200. }
  201. return true;
  202. }
  203. // Helper function to load an integer
  204. bool load_number(const char *path_name, int *value)
  205. {
  206. if (!path_name || !value)
  207. {
  208. FURI_LOG_E(TAG, "Invalid arguments to load_number");
  209. return false;
  210. }
  211. char buffer[64];
  212. if (!load_char(path_name, buffer, sizeof(buffer)))
  213. {
  214. FURI_LOG_E(TAG, "Failed to load number from path: %s", path_name);
  215. return false;
  216. }
  217. *value = atoi(buffer);
  218. return true;
  219. }
  220. // Helper function to load a float
  221. bool load_float(const char *path_name, float *value)
  222. {
  223. if (!path_name || !value)
  224. {
  225. FURI_LOG_E(TAG, "Invalid arguments to load_float");
  226. return false;
  227. }
  228. char buffer[64];
  229. if (!load_char(path_name, buffer, sizeof(buffer)))
  230. {
  231. FURI_LOG_E(TAG, "Failed to load float from path: %s", path_name);
  232. return false;
  233. }
  234. // check if the string is a valid float
  235. char *endptr;
  236. *value = strtof(buffer, &endptr);
  237. if (endptr == buffer)
  238. {
  239. FURI_LOG_E(TAG, "Failed to parse float from path: %s", path_name);
  240. return false;
  241. }
  242. return true;
  243. }
  244. // Helper function to load an int8_t
  245. bool load_int8(const char *path_name, int8_t *value)
  246. {
  247. if (!path_name || !value)
  248. {
  249. FURI_LOG_E(TAG, "Invalid arguments to load_int8");
  250. return false;
  251. }
  252. char buffer[64];
  253. if (!load_char(path_name, buffer, sizeof(buffer)))
  254. {
  255. FURI_LOG_E(TAG, "Failed to load int8 from path: %s", path_name);
  256. return false;
  257. }
  258. long temp = strtol(buffer, NULL, 10);
  259. if (temp < INT8_MIN || temp > INT8_MAX)
  260. {
  261. FURI_LOG_E(TAG, "Value out of range for int8: %ld", temp);
  262. return false;
  263. }
  264. // check if the string is a valid int8
  265. char *endptr;
  266. *value = (int8_t)strtol(buffer, &endptr, 10);
  267. if (endptr == buffer)
  268. {
  269. FURI_LOG_E(TAG, "Failed to parse int8 from path: %s", path_name);
  270. return false;
  271. }
  272. return true;
  273. }
  274. // Helper function to load a uint32_t
  275. bool load_uint32(const char *path_name, uint32_t *value)
  276. {
  277. if (!path_name || !value)
  278. {
  279. FURI_LOG_E(TAG, "Invalid arguments to load_uint32");
  280. return false;
  281. }
  282. char buffer[64];
  283. if (!load_char(path_name, buffer, sizeof(buffer)))
  284. {
  285. FURI_LOG_E(TAG, "Failed to load uint32 from path: %s", path_name);
  286. return false;
  287. }
  288. // check if the string is a valid uint32
  289. char *endptr;
  290. *value = strtoul(buffer, &endptr, 10);
  291. if (endptr == buffer)
  292. {
  293. FURI_LOG_E(TAG, "Failed to parse uint32 from path: %s", path_name);
  294. return false;
  295. }
  296. return true;
  297. }
  298. // 1. Username (String)
  299. static bool load_player_username(char *username, size_t username_size)
  300. {
  301. if (!username || username_size == 0)
  302. {
  303. FURI_LOG_E(TAG, "Invalid username buffer");
  304. return false;
  305. }
  306. // Try to load the string
  307. if (!load_string("player/username", username, username_size))
  308. {
  309. // If loading failed, log a warning and set a default
  310. FURI_LOG_W(TAG, "No data or parse error for username. Using default: 'Unknown'");
  311. memset(username, 0, username_size);
  312. strncpy(username, "Unknown", username_size - 1);
  313. }
  314. // Always return true unless pointer was invalid
  315. return true;
  316. }
  317. // 2. Level (uint32_t)
  318. static bool load_player_level(uint32_t *level)
  319. {
  320. if (!level)
  321. {
  322. FURI_LOG_E(TAG, "Invalid level pointer");
  323. return false;
  324. }
  325. uint32_t temp = 1; // Default
  326. if (!load_uint32("player/level", &temp))
  327. {
  328. FURI_LOG_W(TAG, "No data or parse error for level. Using default: 1");
  329. temp = 1;
  330. }
  331. *level = temp;
  332. return true;
  333. }
  334. // 3. XP (uint32_t)
  335. static bool load_player_xp(uint32_t *xp)
  336. {
  337. if (!xp)
  338. {
  339. FURI_LOG_E(TAG, "Invalid xp pointer");
  340. return false;
  341. }
  342. uint32_t temp = 0; // Default
  343. if (!load_uint32("player/xp", &temp))
  344. {
  345. FURI_LOG_W(TAG, "No data or parse error for xp. Using default: 0");
  346. temp = 0;
  347. }
  348. *xp = temp;
  349. return true;
  350. }
  351. // 4. Health (uint32_t)
  352. static bool load_player_health(uint32_t *health)
  353. {
  354. if (!health)
  355. {
  356. FURI_LOG_E(TAG, "Invalid health pointer");
  357. return false;
  358. }
  359. uint32_t temp = 100; // Default
  360. if (!load_uint32("player/health", &temp))
  361. {
  362. FURI_LOG_W(TAG, "No data or parse error for health. Using default: 100");
  363. temp = 100;
  364. }
  365. *health = temp;
  366. return true;
  367. }
  368. // 5. Strength (uint32_t)
  369. static bool load_player_strength(uint32_t *strength)
  370. {
  371. if (!strength)
  372. {
  373. FURI_LOG_E(TAG, "Invalid strength pointer");
  374. return false;
  375. }
  376. uint32_t temp = 10; // Default
  377. if (!load_uint32("player/strength", &temp))
  378. {
  379. FURI_LOG_W(TAG, "No data or parse error for strength. Using default: 10");
  380. temp = 10;
  381. }
  382. *strength = temp;
  383. return true;
  384. }
  385. // 6. Max Health (uint32_t)
  386. static bool load_player_max_health(uint32_t *max_health)
  387. {
  388. if (!max_health)
  389. {
  390. FURI_LOG_E(TAG, "Invalid max_health pointer");
  391. return false;
  392. }
  393. uint32_t temp = 100; // Default
  394. if (!load_uint32("player/max_health", &temp))
  395. {
  396. FURI_LOG_W(TAG, "No data or parse error for max_health. Using default: 100");
  397. temp = 100;
  398. }
  399. *max_health = temp;
  400. return true;
  401. }
  402. // 7. Health Regen (uint32_t)
  403. static bool load_player_health_regen(uint32_t *health_regen)
  404. {
  405. if (!health_regen)
  406. {
  407. FURI_LOG_E(TAG, "Invalid health_regen pointer");
  408. return false;
  409. }
  410. uint32_t temp = 1; // Default
  411. if (!load_uint32("player/health_regen", &temp))
  412. {
  413. FURI_LOG_W(TAG, "No data or parse error for health_regen. Using default: 0");
  414. temp = 1;
  415. }
  416. *health_regen = temp;
  417. return true;
  418. }
  419. // 8. Elapsed Health Regen (float)
  420. static bool load_player_elapsed_health_regen(float *elapsed_health_regen)
  421. {
  422. if (!elapsed_health_regen)
  423. {
  424. FURI_LOG_E(TAG, "Invalid elapsed_health_regen pointer");
  425. return false;
  426. }
  427. float temp = 0.0f; // Default
  428. if (!load_float("player/elapsed_health_regen", &temp))
  429. {
  430. FURI_LOG_W(TAG, "No data or parse error for elapsed_health_regen. Using default: 0.0f");
  431. temp = 0.0f;
  432. }
  433. *elapsed_health_regen = temp;
  434. return true;
  435. }
  436. // 9. Attack Timer (float)
  437. static bool load_player_attack_timer(float *attack_timer)
  438. {
  439. if (!attack_timer)
  440. {
  441. FURI_LOG_E(TAG, "Invalid attack_timer pointer");
  442. return false;
  443. }
  444. float temp = 0.1f; // Default
  445. if (!load_float("player/attack_timer", &temp))
  446. {
  447. FURI_LOG_W(TAG, "No data or parse error for attack_timer. Using default: 0.0f");
  448. temp = 0.1f;
  449. }
  450. *attack_timer = temp;
  451. return true;
  452. }
  453. // 10. Elapsed Attack Timer (float)
  454. static bool load_player_elapsed_attack_timer(float *elapsed_attack_timer)
  455. {
  456. if (!elapsed_attack_timer)
  457. {
  458. FURI_LOG_E(TAG, "Invalid elapsed_attack_timer pointer");
  459. return false;
  460. }
  461. float temp = 0.0f; // Default
  462. if (!load_float("player/elapsed_attack_timer", &temp))
  463. {
  464. FURI_LOG_W(TAG, "No data or parse error for elapsed_attack_timer. Using default: 0.0f");
  465. temp = 0.0f;
  466. }
  467. *elapsed_attack_timer = temp;
  468. return true;
  469. }
  470. // 11. Direction (enum PlayerDirection)
  471. static bool load_player_direction(PlayerDirection *direction)
  472. {
  473. if (!direction)
  474. {
  475. FURI_LOG_E(TAG, "Invalid direction pointer");
  476. return false;
  477. }
  478. // Default to 3 -> PLAYER_RIGHT (adjust if you prefer another default)
  479. int direction_int = 3;
  480. if (!load_number("player/direction", &direction_int))
  481. {
  482. FURI_LOG_W(TAG, "No data or parse error for direction. Defaulting to PLAYER_RIGHT");
  483. direction_int = 3;
  484. }
  485. switch (direction_int)
  486. {
  487. case 0:
  488. *direction = PLAYER_UP;
  489. break;
  490. case 1:
  491. *direction = PLAYER_DOWN;
  492. break;
  493. case 2:
  494. *direction = PLAYER_LEFT;
  495. break;
  496. case 3:
  497. *direction = PLAYER_RIGHT;
  498. break;
  499. default:
  500. FURI_LOG_W(TAG, "Invalid direction value: %d. Defaulting to PLAYER_RIGHT", direction_int);
  501. *direction = PLAYER_RIGHT;
  502. break;
  503. }
  504. return true;
  505. }
  506. // 12. State (enum PlayerState)
  507. static bool load_player_state(PlayerState *state)
  508. {
  509. if (!state)
  510. {
  511. FURI_LOG_E(TAG, "Invalid state pointer");
  512. return false;
  513. }
  514. // Default to 0 -> PLAYER_IDLE
  515. int state_int = 0;
  516. if (!load_number("player/state", &state_int))
  517. {
  518. FURI_LOG_W(TAG, "No data or parse error for state. Defaulting to PLAYER_IDLE");
  519. state_int = 0;
  520. }
  521. switch (state_int)
  522. {
  523. case 0:
  524. *state = PLAYER_IDLE;
  525. break;
  526. case 1:
  527. *state = PLAYER_MOVING;
  528. break;
  529. case 2:
  530. *state = PLAYER_ATTACKING;
  531. break;
  532. case 3:
  533. *state = PLAYER_ATTACKED;
  534. break;
  535. case 4:
  536. *state = PLAYER_DEAD;
  537. break;
  538. default:
  539. FURI_LOG_W(TAG, "Invalid state value: %d. Defaulting to PLAYER_IDLE", state_int);
  540. *state = PLAYER_IDLE;
  541. break;
  542. }
  543. return true;
  544. }
  545. // 13. Start Position X (float)
  546. static bool load_player_start_position_x(float *start_position_x)
  547. {
  548. if (!start_position_x)
  549. {
  550. FURI_LOG_E(TAG, "Invalid start_position_x pointer");
  551. return false;
  552. }
  553. float temp = 192.0f; // Default
  554. if (!load_float("player/start_position_x", &temp))
  555. {
  556. FURI_LOG_W(TAG, "No data or parse error for start_position_x. Using default: 0.0f");
  557. temp = 192.0f;
  558. }
  559. *start_position_x = temp;
  560. return true;
  561. }
  562. // 14. Start Position Y (float)
  563. static bool load_player_start_position_y(float *start_position_y)
  564. {
  565. if (!start_position_y)
  566. {
  567. FURI_LOG_E(TAG, "Invalid start_position_y pointer");
  568. return false;
  569. }
  570. float temp = 96.0f; // Default
  571. if (!load_float("player/start_position_y", &temp))
  572. {
  573. FURI_LOG_W(TAG, "No data or parse error for start_position_y. Using default: 0.0f");
  574. temp = 96.0f;
  575. }
  576. *start_position_y = temp;
  577. return true;
  578. }
  579. // 15. dx (int8_t)
  580. static bool load_player_dx(int8_t *dx)
  581. {
  582. if (!dx)
  583. {
  584. FURI_LOG_E(TAG, "Invalid dx pointer");
  585. return false;
  586. }
  587. int8_t temp = 1; // Default
  588. if (!load_int8("player/dx", &temp))
  589. {
  590. FURI_LOG_W(TAG, "No data or parse error for dx. Using default: 0");
  591. temp = 1;
  592. }
  593. *dx = temp;
  594. return true;
  595. }
  596. // 16. dy (int8_t)
  597. static bool load_player_dy(int8_t *dy)
  598. {
  599. if (!dy)
  600. {
  601. FURI_LOG_E(TAG, "Invalid dy pointer");
  602. return false;
  603. }
  604. int8_t temp = 0; // Default
  605. if (!load_int8("player/dy", &temp))
  606. {
  607. FURI_LOG_W(TAG, "No data or parse error for dy. Using default: 0");
  608. temp = 0;
  609. }
  610. *dy = temp;
  611. return true;
  612. }
  613. bool load_player_context(PlayerContext *player_context)
  614. {
  615. if (!player_context)
  616. {
  617. FURI_LOG_E(TAG, "Invalid player context");
  618. return false;
  619. }
  620. // 1. Username (String)
  621. if (!load_player_username(player_context->username, sizeof(player_context->username)))
  622. {
  623. FURI_LOG_E(TAG, "Failed to load player username");
  624. return false;
  625. }
  626. // 2. Level (uint32_t)
  627. if (!load_player_level(&player_context->level))
  628. {
  629. FURI_LOG_E(TAG, "Failed to load player level");
  630. return false;
  631. }
  632. // 3. XP (uint32_t)
  633. if (!load_player_xp(&player_context->xp))
  634. {
  635. FURI_LOG_E(TAG, "Failed to load player xp");
  636. return false;
  637. }
  638. // 4. Health (uint32_t)
  639. if (!load_player_health(&player_context->health))
  640. {
  641. FURI_LOG_E(TAG, "Failed to load player health");
  642. return false;
  643. }
  644. // 5. Strength (uint32_t)
  645. if (!load_player_strength(&player_context->strength))
  646. {
  647. FURI_LOG_E(TAG, "Failed to load player strength");
  648. return false;
  649. }
  650. // 6. Max Health (uint32_t)
  651. if (!load_player_max_health(&player_context->max_health))
  652. {
  653. FURI_LOG_E(TAG, "Failed to load player max health");
  654. return false;
  655. }
  656. // 7. Health Regen (uint32_t)
  657. if (!load_player_health_regen(&player_context->health_regen))
  658. {
  659. FURI_LOG_E(TAG, "Failed to load player health regen");
  660. return false;
  661. }
  662. // 8. Elapsed Health Regen (float)
  663. if (!load_player_elapsed_health_regen(&player_context->elapsed_health_regen))
  664. {
  665. FURI_LOG_E(TAG, "Failed to load player elapsed health regen");
  666. return false;
  667. }
  668. // 9. Attack Timer (float)
  669. if (!load_player_attack_timer(&player_context->attack_timer))
  670. {
  671. FURI_LOG_E(TAG, "Failed to load player attack timer");
  672. return false;
  673. }
  674. // 10. Elapsed Attack Timer (float)
  675. if (!load_player_elapsed_attack_timer(&player_context->elapsed_attack_timer))
  676. {
  677. FURI_LOG_E(TAG, "Failed to load player elapsed attack timer");
  678. return false;
  679. }
  680. // 11. Direction (enum PlayerDirection)
  681. if (!load_player_direction(&player_context->direction))
  682. {
  683. FURI_LOG_E(TAG, "Failed to load player direction");
  684. return false;
  685. }
  686. // 12. State (enum PlayerState)
  687. if (!load_player_state(&player_context->state))
  688. {
  689. FURI_LOG_E(TAG, "Failed to load player state");
  690. return false;
  691. }
  692. // 13. Start Position X (float)
  693. if (!load_player_start_position_x(&player_context->start_position.x))
  694. {
  695. FURI_LOG_E(TAG, "Failed to load player start position x");
  696. return false;
  697. }
  698. // 14. Start Position Y (float)
  699. if (!load_player_start_position_y(&player_context->start_position.y))
  700. {
  701. FURI_LOG_E(TAG, "Failed to load player start position y");
  702. return false;
  703. }
  704. // 15. dx (int8_t)
  705. if (!load_player_dx(&player_context->dx))
  706. {
  707. FURI_LOG_E(TAG, "Failed to load player dx");
  708. return false;
  709. }
  710. // 16. dy (int8_t)
  711. if (!load_player_dy(&player_context->dy))
  712. {
  713. FURI_LOG_E(TAG, "Failed to load player dy");
  714. return false;
  715. }
  716. return true;
  717. }
  718. static inline void furi_string_remove_str(FuriString *string, const char *needle)
  719. {
  720. furi_string_replace_str(string, needle, "", 0);
  721. }
  722. static FuriString *enemy_data(const FuriString *world_data)
  723. {
  724. size_t enemy_data_pos = furi_string_search_str(world_data, "enemy_data", 0);
  725. if (enemy_data_pos == FURI_STRING_FAILURE)
  726. {
  727. FURI_LOG_E("Game", "Failed to find enemy_data in world data");
  728. return NULL;
  729. }
  730. size_t bracket_start = furi_string_search_char(world_data, '[', enemy_data_pos);
  731. if (bracket_start == FURI_STRING_FAILURE)
  732. {
  733. FURI_LOG_E("Game", "Failed to find start of enemy_data array");
  734. return NULL;
  735. }
  736. size_t bracket_end = furi_string_search_char(world_data, ']', bracket_start);
  737. if (bracket_end == FURI_STRING_FAILURE)
  738. {
  739. FURI_LOG_E("Game", "Failed to find end of enemy_data array");
  740. return NULL;
  741. }
  742. FuriString *enemy_data_str = furi_string_alloc();
  743. if (!enemy_data_str)
  744. {
  745. FURI_LOG_E("Game", "Failed to allocate enemy_data string");
  746. return NULL;
  747. }
  748. furi_string_cat_str(enemy_data_str, "{\"enemy_data\":");
  749. {
  750. FuriString *temp_sub = furi_string_alloc();
  751. furi_string_set_strn(
  752. temp_sub,
  753. furi_string_get_cstr(world_data) + bracket_start,
  754. (bracket_end + 1) - bracket_start);
  755. furi_string_cat(enemy_data_str, temp_sub);
  756. furi_string_free(temp_sub);
  757. }
  758. furi_string_cat_str(enemy_data_str, "}");
  759. return enemy_data_str;
  760. }
  761. static FuriString *json_data(const FuriString *world_data)
  762. {
  763. size_t json_data_pos = furi_string_search_str(world_data, "json_data", 0);
  764. if (json_data_pos == FURI_STRING_FAILURE)
  765. {
  766. FURI_LOG_E("Game", "Failed to find json_data in world data");
  767. return NULL;
  768. }
  769. size_t bracket_start = furi_string_search_char(world_data, '[', json_data_pos);
  770. if (bracket_start == FURI_STRING_FAILURE)
  771. {
  772. FURI_LOG_E("Game", "Failed to find start of json_data array");
  773. return NULL;
  774. }
  775. size_t bracket_end = furi_string_search_char(world_data, ']', bracket_start);
  776. if (bracket_end == FURI_STRING_FAILURE)
  777. {
  778. FURI_LOG_E("Game", "Failed to find end of json_data array");
  779. return NULL;
  780. }
  781. FuriString *json_data_str = furi_string_alloc();
  782. if (!json_data_str)
  783. {
  784. FURI_LOG_E("Game", "Failed to allocate json_data string");
  785. return NULL;
  786. }
  787. furi_string_cat_str(json_data_str, "{\"json_data\":");
  788. {
  789. FuriString *temp_sub = furi_string_alloc();
  790. furi_string_set_strn(
  791. temp_sub,
  792. furi_string_get_cstr(world_data) + bracket_start,
  793. (bracket_end + 1) - bracket_start);
  794. furi_string_cat(json_data_str, temp_sub);
  795. furi_string_free(temp_sub);
  796. }
  797. furi_string_cat_str(json_data_str, "}");
  798. return json_data_str;
  799. }
  800. bool separate_world_data(char *id, FuriString *world_data)
  801. {
  802. if (!id || !world_data)
  803. {
  804. FURI_LOG_E("Game", "Invalid parameters");
  805. return false;
  806. }
  807. FuriString *file_json_data = json_data(world_data);
  808. if (!file_json_data || furi_string_size(file_json_data) == 0)
  809. {
  810. FURI_LOG_E("Game", "Failed to get json data in separate_world_data");
  811. return false;
  812. }
  813. // Save file_json_data to disk
  814. char directory_path[256];
  815. snprintf(directory_path, sizeof(directory_path),
  816. STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world/worlds/%s", id);
  817. Storage *storage = furi_record_open(RECORD_STORAGE);
  818. storage_common_mkdir(storage, directory_path);
  819. File *file = storage_file_alloc(storage);
  820. char file_path[256];
  821. snprintf(file_path, sizeof(file_path),
  822. STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world/worlds/%s/%s_json_data.json",
  823. id, id);
  824. if (!storage_file_open(file, file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS))
  825. {
  826. FURI_LOG_E("Game", "Failed to open file for writing: %s", file_path);
  827. storage_file_free(file);
  828. furi_record_close(RECORD_STORAGE);
  829. furi_string_free(file_json_data);
  830. return false;
  831. }
  832. size_t data_size = furi_string_size(file_json_data);
  833. if (storage_file_write(file, furi_string_get_cstr(file_json_data), data_size) != data_size)
  834. {
  835. FURI_LOG_E("Game", "Failed to write json_data");
  836. }
  837. storage_file_close(file);
  838. furi_string_replace_at(file_json_data, 0, 1, "");
  839. furi_string_replace_at(file_json_data, furi_string_size(file_json_data) - 1, 1, "");
  840. // include the comma at the end of the json_data array
  841. furi_string_cat_str(file_json_data, ",");
  842. furi_string_remove_str(world_data, furi_string_get_cstr(file_json_data));
  843. furi_string_free(file_json_data);
  844. FuriString *file_enemy_data = enemy_data(world_data);
  845. if (!file_enemy_data)
  846. {
  847. FURI_LOG_E("Game", "Failed to get enemy data");
  848. return false;
  849. }
  850. snprintf(file_path, sizeof(file_path),
  851. STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world/worlds/%s/%s_enemy_data.json",
  852. id, id);
  853. if (!storage_file_open(file, file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS))
  854. {
  855. FURI_LOG_E("Game", "Failed to open file for writing: %s", file_path);
  856. storage_file_free(file);
  857. furi_record_close(RECORD_STORAGE);
  858. furi_string_free(file_enemy_data);
  859. return false;
  860. }
  861. data_size = furi_string_size(file_enemy_data);
  862. if (storage_file_write(file, furi_string_get_cstr(file_enemy_data), data_size) != data_size)
  863. {
  864. FURI_LOG_E("Game", "Failed to write enemy_data");
  865. }
  866. // Clean up
  867. furi_string_free(file_enemy_data);
  868. storage_file_close(file);
  869. storage_file_free(file);
  870. furi_record_close(RECORD_STORAGE);
  871. return true;
  872. }