gb_cartridge_scene_1.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. #include "../gb_cartridge_app.h"
  2. #include <furi.h>
  3. #include <furi_hal.h>
  4. #include <input/input.h>
  5. #include <gui/elements.h>
  6. #include <dolphin/dolphin.h>
  7. #include <stdio.h> // Para sprintf
  8. #include <string.h> // Para strlen
  9. struct GBCartridgeScene1 {
  10. View* view;
  11. GBCartridgeScene1Callback callback;
  12. void* context;
  13. GBCartridge* app;
  14. };
  15. typedef struct {
  16. char* cart_title;
  17. char* cart_serial;
  18. char* cart_checksum;
  19. char* cart_ROMSize;
  20. char* cart_RAMSize;
  21. char* cart_gb_type;
  22. bool cart_gb_sgb;
  23. int ramBanks;
  24. int romBanks;
  25. uint8_t cart_logo[48*8];
  26. } GameBoyCartridgeModel;
  27. void gameboy_information_handle_rx_data_cb(uint8_t* buf, size_t len, void* context) {
  28. furi_assert(context);
  29. UNUSED(len);
  30. UNUSED(buf);
  31. GBCartridge* instance = context;
  32. with_view_model(
  33. instance->gb_cartridge_scene_1->view,
  34. GameBoyCartridgeModel * model,
  35. {
  36. cJSON* json = cJSON_Parse((char*)buf);
  37. if (json == NULL) {
  38. model->cart_title = "Parse error";
  39. } else {;
  40. // Title
  41. cJSON* title = cJSON_GetObjectItemCaseSensitive(json, "title");
  42. if (cJSON_IsString(title) && (title->valuestring != NULL))
  43. {
  44. model->cart_title = strdup(title->valuestring);
  45. } else {
  46. model->cart_title = "None";
  47. }
  48. // Serial
  49. cJSON* serial = cJSON_GetObjectItemCaseSensitive(json, "serial");
  50. if (cJSON_IsString(serial) && (serial->valuestring != NULL))
  51. {
  52. model->cart_serial = strdup(serial->valuestring);
  53. } else {
  54. model->cart_serial = "";
  55. }
  56. // Checksum
  57. cJSON* checksum = cJSON_GetObjectItemCaseSensitive(json, "checksum");
  58. if (cJSON_IsString(checksum) && (checksum->valuestring != NULL))
  59. {
  60. model->cart_checksum = strdup(checksum->valuestring);
  61. } else {
  62. model->cart_checksum = "None";
  63. }
  64. // ROMSize
  65. cJSON* ROMSize = cJSON_GetObjectItemCaseSensitive(json, "ROMSize");
  66. if (cJSON_IsString(ROMSize) && (ROMSize->valuestring != NULL))
  67. {
  68. model->cart_ROMSize = strdup(ROMSize->valuestring);
  69. } else {
  70. model->cart_ROMSize = "None";
  71. }
  72. // RAMSize
  73. cJSON* RAMSize = cJSON_GetObjectItemCaseSensitive(json, "RAMSize");
  74. if (cJSON_IsString(RAMSize) && (RAMSize->valuestring != NULL))
  75. {
  76. model->cart_RAMSize = strdup(RAMSize->valuestring);
  77. } else {
  78. model->cart_RAMSize = "None";
  79. }
  80. // GB Type
  81. cJSON* gb_type = cJSON_GetObjectItemCaseSensitive(json, "gb_type");
  82. if (cJSON_IsString(gb_type) && (gb_type->valuestring != NULL))
  83. {
  84. model->cart_gb_type = strdup(gb_type->valuestring);
  85. } else {
  86. model->cart_gb_type = "dump";
  87. }
  88. // SGB ?
  89. cJSON* gb_sgb = cJSON_GetObjectItemCaseSensitive(json, "gb_sgb");
  90. if (cJSON_IsBool(gb_sgb))
  91. {
  92. model->cart_gb_sgb = cJSON_IsTrue(gb_sgb);
  93. } else {
  94. model->cart_gb_sgb = false;
  95. }
  96. // Rom Banks
  97. cJSON* romBanks = cJSON_GetObjectItemCaseSensitive(json, "romBanks");
  98. if(cJSON_IsNumber(romBanks)) {
  99. model->romBanks = romBanks->valueint;
  100. } else {
  101. model->romBanks = 0;
  102. }
  103. // Ram Banks
  104. cJSON* ramBanks = cJSON_GetObjectItemCaseSensitive(json, "ramBanks");
  105. if(cJSON_IsNumber(ramBanks)) {
  106. model->ramBanks = ramBanks->valueint;
  107. } else {
  108. model->ramBanks = 0;
  109. }
  110. cJSON* gb_logo = cJSON_GetObjectItemCaseSensitive(json, "logo");
  111. if (cJSON_IsArray(gb_logo)) {
  112. // Leer los elementos del arreglo "logo"
  113. for (int i = 0; i < cJSON_GetArraySize(gb_logo); i++) {
  114. cJSON *logoElement = cJSON_GetArrayItem(gb_logo, i);
  115. if (cJSON_IsNumber(logoElement)) {
  116. model->cart_logo[i] = logoElement->valueint;
  117. }
  118. }
  119. }
  120. FuriString* path = furi_string_alloc();
  121. // int buffer_size = strlen(model->cart_title) + strlen(model->cart_serial) + strlen(model->cart_gb_type) + 3; // 3 para los guiones bajos y el punto
  122. // char filename[255];
  123. if (strcmp(model->cart_serial, "") == 0) {
  124. furi_string_cat_printf(path, "%s", model->cart_title);
  125. } else {
  126. furi_string_cat_printf(path, "%s_%s", model->cart_title, model->cart_serial);
  127. }
  128. // snprintf(filename,255, "%s_%s.%s", model->cart_title, model->cart_serial, model->cart_gb_type);
  129. instance->cart_dump_rom_filename = (char*)furi_string_get_cstr(path);
  130. instance->cart_dump_rom_extension = model->cart_gb_type;
  131. instance->cart_dump_ram_filename = (char*)furi_string_get_cstr(path);
  132. instance->cart_dump_ram_extension = "sav";
  133. instance->rom_banks = model->romBanks;
  134. instance->ram_banks = model->ramBanks;
  135. }
  136. },true);
  137. }
  138. void gb_cartridge_scene_1_set_callback(
  139. GBCartridgeScene1* instance,
  140. GBCartridgeScene1Callback callback,
  141. void* context) {
  142. furi_assert(instance);
  143. furi_assert(callback);
  144. instance->callback = callback;
  145. instance->context = context;
  146. instance->app = (GBCartridge*)context;
  147. }
  148. // https://www.youtube.com/watch?v=ix5yZm4fwFQ
  149. void draw_logo(Canvas* canvas, GameBoyCartridgeModel* model, int start_x, int start_y) {
  150. uint16_t x, y;
  151. uint8_t row[4];
  152. uint8_t list[48*8] = {0};
  153. for (x = 0; x < 48/2; x += 2) {
  154. row[0] = (model->cart_logo[x] >> 4) & 0xF;
  155. row[1] = model->cart_logo[x] & 0xF;
  156. row[2] = (model->cart_logo[x + 1] >> 4) & 0xF;
  157. row[3] = model->cart_logo[x + 1] & 0xF;
  158. for (y = 0; y < 4; y++) {
  159. // set first bit
  160. if ((row[y] / 8) == 1) {
  161. list[(x*2) + (y * 48)] = 1;
  162. row[y] -= 8;
  163. }
  164. // then second bit
  165. if ((row[y] / 4) == 1) {
  166. list[((x*2) + 1) + (y * 48)] = 1;
  167. row[y] -= 4;
  168. }
  169. // then third bit
  170. if ((row[y] / 2) == 1) {
  171. list[((x*2) + 2) + (y * 48)] = 1;
  172. row[y] -= 2;
  173. }
  174. // then fourth bit
  175. if ((row[y] / 1) == 1) {
  176. list[((x*2) + 3) + (y * 48)] = 1;
  177. }
  178. }
  179. }
  180. // then do bottom half
  181. for (x = 48/2; x < 96/2; x += 2) {
  182. // convert 2 bytes of data
  183. row[0] = (model->cart_logo[x] >> 4) & 0xF;
  184. row[1] = model->cart_logo[x] & 0xF;
  185. row[2] = (model->cart_logo[x + 1] >> 4) & 0xF;
  186. row[3] = model->cart_logo[x + 1] & 0xF;
  187. for (y = 0; y < 4; y++) {
  188. // set first bit
  189. if ((row[y] / 8) == 1) {
  190. list[144 + (x*2) + (y * 48)] = 1;
  191. row[y] -= 8;
  192. }
  193. // then second bit
  194. if ((row[y] / 4) == 1) {
  195. list[145 + (x*2) + (y * 48)] = 1;
  196. row[y] -= 4;
  197. }
  198. // then third bit
  199. if ((row[y] / 2) == 1) {
  200. list[146 + (x*2) + (y * 48)] = 1;
  201. row[y] -= 2;
  202. }
  203. // then fourth bit
  204. if ((row[y] / 1) == 1) {
  205. list[147 + (x*2) + (y * 48)] = 1;
  206. }
  207. }
  208. }
  209. // UNUSED(row);
  210. // ESCALA 1
  211. for (y = 0; y < 8; y++) {
  212. for (x = 0; x < 48; x++) {
  213. int indice = y * 48 + x;
  214. if(list[indice] == 1) {
  215. canvas_draw_dot(canvas, x + start_x, y + start_y);
  216. }
  217. }
  218. }
  219. }
  220. void gb_cartridge_scene_1_draw(Canvas* canvas, GameBoyCartridgeModel* model) {
  221. UNUSED(model);
  222. canvas_clear(canvas);
  223. // canvas_set_color(canvas, ColorBlack);
  224. // canvas_set_font(canvas, FontPrimary);
  225. // canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, "This is Scene 1");
  226. // canvas_set_font(canvas, FontSecondary);
  227. // canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, "An empty scene to be");
  228. // canvas_draw_str_aligned(canvas, 0, 32, AlignLeft, AlignTop, "used as boilerplate");
  229. // Clear the screen.
  230. canvas_set_color(canvas, ColorBlack);
  231. canvas_set_bitmap_mode(canvas, 1);
  232. canvas_set_font(canvas, FontPrimary);
  233. canvas_draw_str_aligned(canvas, 128 / 2, 1, AlignCenter, AlignTop, model->cart_title);
  234. // canvas_draw_str_aligned(canvas, 128 / 2, 1, AlignCenter, AlignTop, "Prueba"); // title
  235. canvas_set_font(canvas, FontSecondary);
  236. canvas_draw_str(canvas, 2, 20, "Game Code / REV");
  237. canvas_set_font(canvas, FontPrimary);
  238. // canvas_draw_str(canvas, 87, 20, "APSS-0"); // serial
  239. canvas_draw_str_aligned(canvas, 126, 20, AlignRight, AlignBottom, model->cart_serial);
  240. canvas_set_font(canvas, FontSecondary);
  241. canvas_draw_str(canvas, 2, 30, "Boot Logo");
  242. // canvas_draw_box(canvas, 78, 22, 48, 8); // TODO: Implementar
  243. draw_logo(canvas, model, 78, 22);
  244. canvas_set_font(canvas, FontSecondary);
  245. canvas_draw_str(canvas, 2, 40, "ROM Checksum");
  246. canvas_set_font(canvas, FontPrimary);
  247. // canvas_draw_str(canvas, 87, 40, "0X04C7"); // checksum
  248. canvas_draw_str_aligned(canvas, 126, 39, AlignRight, AlignBottom, model->cart_checksum);
  249. canvas_set_font(canvas, FontSecondary);
  250. canvas_draw_str(canvas, 2, 50, "ROM Size");
  251. canvas_set_font(canvas, FontPrimary);
  252. // canvas_draw_str(canvas, 98, 49, "1 MiB"); // ROMSize
  253. canvas_draw_str_aligned(canvas, 126, 49, AlignRight, AlignBottom, model->cart_ROMSize);
  254. canvas_set_font(canvas, FontSecondary);
  255. canvas_draw_str(canvas, 2, 60, "Save Type");
  256. canvas_set_font(canvas, FontPrimary);
  257. // canvas_draw_str(canvas, 63, 60, "SRAM 32KiB"); // RAMSize
  258. canvas_draw_str_aligned(canvas, 126, 59, AlignRight, AlignBottom, model->cart_RAMSize);
  259. }
  260. static void gb_cartridge_scene_1_model_init(GameBoyCartridgeModel* const model) {
  261. UNUSED(model);
  262. // FuriString* cart_title;
  263. // FuriString* cart_serial;
  264. // FuriString* cart_checksum;
  265. // FuriString* cart_ROMSize;
  266. // FuriString* cart_RAMSize;
  267. model->cart_title = "Loading...";
  268. model->cart_serial = "";
  269. model->cart_checksum = "";
  270. model->cart_ROMSize = "";
  271. model->cart_RAMSize = "";
  272. model->cart_gb_type = "";
  273. model->cart_gb_sgb = false;
  274. for (int i = 0; i < 48*8; i++) {
  275. model->cart_logo[i] = 0;
  276. }
  277. }
  278. bool gb_cartridge_scene_1_input(InputEvent* event, void* context) {
  279. furi_assert(context);
  280. GBCartridgeScene1* instance = context;
  281. bool consumed = false;
  282. if (event->type == InputTypeRelease) {
  283. switch(event->key) {
  284. case InputKeyBack:
  285. // with_view_model(
  286. // instance->view,
  287. // GameBoyCartridgeModel * model,
  288. // {
  289. // UNUSED(model);
  290. // instance->callback(GBCartridgeCustomEventScene1Back, instance->context);
  291. // },
  292. // true);
  293. consumed = true;
  294. break;
  295. case InputKeyOk:
  296. with_view_model(
  297. ((GBCartridge*)instance->app)->gb_cartridge_scene_1->view,
  298. GameBoyCartridgeModel * model,
  299. {
  300. model->cart_title = "Refresh...";
  301. model->cart_serial = "";
  302. model->cart_checksum = "";
  303. model->cart_ROMSize = "";
  304. model->cart_RAMSize = "";
  305. // Reiniciar el array a 0 utilizando un bucle
  306. for (size_t i = 0; i < sizeof(model->cart_logo)/sizeof(model->cart_logo[0]); i++) {
  307. model->cart_logo[i] = 0;
  308. }
  309. // Register callbacks to receive data
  310. uart_set_handle_rx_data_cb(((GBCartridge*)instance->app)->uart, gameboy_information_handle_rx_data_cb); // setup callback for general log rx thread
  311. const char gbcartridge_command[] = "gbcartridge -i\n";
  312. uart_tx((uint8_t*)gbcartridge_command, strlen(gbcartridge_command));
  313. },
  314. true);
  315. consumed = true;
  316. break;
  317. case InputKeyLeft:
  318. case InputKeyRight:
  319. case InputKeyUp:
  320. case InputKeyDown:
  321. with_view_model(
  322. instance->view,
  323. GameBoyCartridgeModel* model,
  324. {
  325. UNUSED(model);
  326. },
  327. true);
  328. consumed = true;
  329. break;
  330. case InputKeyMAX:
  331. break;
  332. }
  333. }
  334. return consumed;
  335. }
  336. void gb_cartridge_scene_1_exit(void* context) {
  337. furi_assert(context);
  338. }
  339. void gb_cartridge_scene_1_enter(void* context) {
  340. furi_assert(context);
  341. GBCartridgeScene1* instance = (GBCartridgeScene1*)context;
  342. with_view_model(
  343. instance->view,
  344. GameBoyCartridgeModel * model,
  345. {
  346. gb_cartridge_scene_1_model_init(model);
  347. },
  348. true
  349. );
  350. // Register callbacks to receive data
  351. uart_set_handle_rx_data_cb(((GBCartridge*)instance->app)->uart, gameboy_information_handle_rx_data_cb); // setup callback for general log rx thread
  352. const char gbcartridge_command[] = "gbcartridge -i\n";
  353. uart_tx((uint8_t*)gbcartridge_command, strlen(gbcartridge_command));
  354. }
  355. GBCartridgeScene1* gb_cartridge_scene_1_alloc() {
  356. GBCartridgeScene1* instance = malloc(sizeof(GBCartridgeScene1));
  357. instance->view = view_alloc();
  358. view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(GameBoyCartridgeModel));
  359. view_set_context(instance->view, instance); // furi_assert crashes in events without this
  360. view_set_draw_callback(instance->view, (ViewDrawCallback)gb_cartridge_scene_1_draw);
  361. view_set_input_callback(instance->view, gb_cartridge_scene_1_input);
  362. view_set_enter_callback(instance->view, gb_cartridge_scene_1_enter);
  363. view_set_exit_callback(instance->view, gb_cartridge_scene_1_exit);
  364. with_view_model(
  365. instance->view,
  366. GameBoyCartridgeModel * model,
  367. {
  368. gb_cartridge_scene_1_model_init(model);
  369. },
  370. true
  371. );
  372. return instance;
  373. }
  374. void gb_cartridge_scene_1_free(GBCartridgeScene1* instance) {
  375. furi_assert(instance);
  376. with_view_model(
  377. instance->view,
  378. GameBoyCartridgeModel * model,
  379. {
  380. UNUSED(model);
  381. },
  382. true);
  383. view_free(instance->view);
  384. free(instance);
  385. }
  386. View* gb_cartridge_scene_1_get_view(GBCartridgeScene1* instance) {
  387. furi_assert(instance);
  388. return instance->view;
  389. }