gb_cartridge_scene_1.c 15 KB

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