gb_cartridge_scene_1.c 15 KB

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