#include #include #include #include #include "gw_linker.h" #include "rg_emulators.h" // #include "rg_favorites.h" #include "bitmaps.h" #include "gui.h" #include "rom_manager.h" #include "gw_lcd.h" #include "main.h" #include "main_gb.h" #include "main_nes.h" #include "main_smsplusgx.h" #include "main_pce.h" #include "main_snes.h" // Increase when adding new emulators #define MAX_EMULATORS 7 static retro_emulator_t emulators[MAX_EMULATORS]; static int emulators_count = 0; retro_emulator_t *file_to_emu(retro_emulator_file_t *file) { for (int i = 0; i < MAX_EMULATORS; i++) if (emulators[i].system == file->system) return &emulators[i]; return NULL; } static void event_handler(gui_event_t event, tab_t *tab) { retro_emulator_t *emu = (retro_emulator_t *)tab->arg; listbox_item_t *item = gui_get_selected_item(tab); retro_emulator_file_t *file = (retro_emulator_file_t *)(item ? item->arg : NULL); if (event == TAB_INIT) { emulator_init(emu); if (emu->roms.count > 0) { sprintf(tab->status, " Games: %d", emu->roms.count); gui_resize_list(tab, emu->roms.count); for (int i = 0; i < emu->roms.count; i++) { tab->listbox.items[i].text = emu->roms.files[i].name; tab->listbox.items[i].arg = (void *)&emu->roms.files[i]; } gui_sort_list(tab, 0); tab->is_empty = false; } else { sprintf(tab->status, " No games"); gui_resize_list(tab, 8); size_t len = 0; tab->listbox.items[0].text = asnprintf(NULL, &len, "Place roms in folder: /roms/%s", emu->dirname); len = 0; tab->listbox.items[2].text = asnprintf(NULL, &len, "With file extension: .%s", emu->ext); tab->listbox.items[4].text = "Use SELECT and START to navigate."; tab->listbox.cursor = 3; tab->is_empty = true; } } /* The rest of the events require a file to be selected */ if (file == NULL) return; if (event == KEY_PRESS_A) { emulator_show_file_menu(file); gui_redraw(); } else if (event == KEY_PRESS_B) { emulator_show_file_info(file); gui_redraw(); } else if (event == TAB_IDLE) { if (file->checksum == 0) { emulator_crc32_file(file); } // if (gui.show_cover && gui.idle_counter == (gui.show_cover == 1 ? 8 : 1)) { // gui_draw_cover(file); // } } else if (event == TAB_REDRAW) { if (gui.show_cover) gui_draw_cover(file); } } static void add_emulator(const char *system, const char *dirname, const char* ext, const char *part, uint16_t crc_offset, const void *logo, const void *header) { assert(emulators_count <= MAX_EMULATORS); retro_emulator_t *p = &emulators[emulators_count++]; strcpy(p->system_name, system); strcpy(p->dirname, dirname); strcpy(p->ext, ext); p->partition = 0; p->roms.count = 0; p->roms.files = NULL; p->initialized = false; p->crc_offset = crc_offset; gui_add_tab(dirname, logo, header, p, event_handler); emulator_init(p); } void emulator_init(retro_emulator_t *emu) { if (emu->initialized) return; emu->initialized = true; printf("Retro-Go: Initializing emulator '%s'\n", emu->system_name); const rom_system_t *system = rom_manager_system(&rom_mgr, emu->system_name); if (system) { emu->system = system; emu->roms.files = system->roms; emu->roms.count = system->roms_count; } else { while(1) { lcd_backlight_on(); HAL_Delay(100); lcd_backlight_off(); HAL_Delay(100); } } // retro_emulator_file_t *file = &emu->roms.files[emu->roms.count++]; // strcpy(file->folder, "/"); // strcpy(file->name, "test"); // strcpy(file->ext, "gb"); // file->emulator = (void*)emu; // file->crc_offset = emu->crc_offset; // file->checksum = 0; // char path[128]; // char *files = NULL; // size_t count = 0; // sprintf(path, ODROID_BASE_PATH_CRC_CACHE "/%s", emu->dirname); // odroid_sdcard_mkdir(path); // sprintf(path, ODROID_BASE_PATH_SAVES "/%s", emu->dirname); // odroid_sdcard_mkdir(path); // sprintf(path, ODROID_BASE_PATH_ROMS "/%s", emu->dirname); // odroid_sdcard_mkdir(path); // if (odroid_sdcard_list(path, &files, &count) == 0 && count > 0) // { // emu->roms.files = rg_alloc(count * sizeof(retro_emulator_file_t), MEM_ANY); // emu->roms.count = 0; // char *ptr = files; // for (int i = 0; i < count; ++i) // { // const char *name = ptr; // const char *ext = odroid_sdcard_get_extension(ptr); // size_t name_len = strlen(name); // // Advance pointer to next entry // ptr += name_len + 1; // if (!ext || strcasecmp(emu->ext, ext) != 0) // && strcasecmp("zip", ext) != 0 // continue; // retro_emulator_file_t *file = &emu->roms.files[emu->roms.count++]; // strcpy(file->folder, path); // strcpy(file->name, name); // strcpy(file->ext, ext); // file->name[name_len-strlen(ext)-1] = 0; // file->emulator = (void*)emu; // file->crc_offset = emu->crc_offset; // file->checksum = 0; // } // } // free(files); } const uint32_t *emu_get_file_address(retro_emulator_file_t *file) { // static char buffer[192]; // if (file == NULL) return NULL; // sprintf(buffer, "%s/%s.%s", file->folder, file->name, file->ext); // return (const char*)&buffer; return (uint32_t *) file->address; } /*bool emulator_build_file_object(const char *path, retro_emulator_file_t *file) { const char *name = odroid_sdcard_get_filename(path); const char *ext = odroid_sdcard_get_extension(path); if (ext == NULL || name == NULL) return false; memset(file, 0, sizeof(retro_emulator_file_t)); strncpy(file->folder, path, strlen(path)-strlen(name)-1); strncpy(file->name, name, strlen(name)-strlen(ext)-1); strcpy(file->ext, ext); const char *dirname = odroid_sdcard_get_filename(file->folder); for (int i = 0; i < emulators_count; ++i) { if (strcmp(emulators[i].dirname, dirname) == 0) { file->crc_offset = emulators[i].crc_offset; file->emulator = &emulators[i]; return true; } } return false; }*/ void emulator_crc32_file(retro_emulator_file_t *file) { // if (file == NULL || file->checksum > 0) // return; // const int chunk_size = 32768; // const char *file_path = emu_get_file_path(file); // char *cache_path = odroid_system_get_path(ODROID_PATH_CRC_CACHE, file_path); // FILE *fp, *fp2; // file->missing_cover = 0; // if ((fp = fopen(cache_path, "rb")) != NULL) // { // fread(&file->checksum, 4, 1, fp); // fclose(fp); // } // else if ((fp = fopen(file_path, "rb")) != NULL) // { // void *buffer = malloc(chunk_size); // uint32_t crc_tmp = 0; // uint32_t count = 0; // gui_draw_notice(" CRC32", C_GREEN); // fseek(fp, file->crc_offset, SEEK_SET); // while (true) // { // odroid_input_read_gamepad(&gui.joystick); // if (gui.joystick.bitmask > 0) break; // count = fread(buffer, 1, chunk_size, fp); // if (count == 0) break; // crc_tmp = crc32_le(crc_tmp, buffer, count); // if (count < chunk_size) break; // } // free(buffer); // if (feof(fp)) // { // file->checksum = crc_tmp; // if ((fp2 = fopen(cache_path, "wb")) != NULL) // { // fwrite(&file->checksum, 4, 1, fp2); // fclose(fp2); // } // } // fclose(fp); // } // else // { // file->checksum = 1; // } // free(cache_path); // gui_draw_notice(" ", C_RED); } void emulator_show_file_info(retro_emulator_file_t *file) { char filename_value[128]; char type_value[32]; char size_value[32]; char crc_value[32]; crc_value[0] = '\x00'; odroid_dialog_choice_t choices[] = { {0, "File", filename_value, 1, NULL}, {0, "Type", type_value, 1, NULL}, {0, "Folder", "...", 1, NULL}, {0, "Size", size_value, 1, NULL}, {0, "CRC32", crc_value, 1, NULL}, {0, "---", "", -1, NULL}, {1, "Close", "", 1, NULL}, ODROID_DIALOG_CHOICE_LAST }; sprintf(choices[0].value, "%.127s", file->name); sprintf(choices[1].value, "%s", file->ext); // sprintf(choices[2].value, "%s", file->folder); // sprintf(choices[3].value, "%d KB", odroid_sdcard_get_filesize(emu_get_file_path(file)) / 1024); sprintf(choices[3].value, "%d KB", file->size / 1024); if (file->checksum > 1) { if (file->crc_offset) sprintf(choices[4].value, "%08lX (%d)", file->checksum, file->crc_offset); else sprintf(choices[4].value, "%08lX", file->checksum); } odroid_overlay_dialog("Properties", choices, -1); } void emulator_show_file_menu(retro_emulator_file_t *file) { // char *save_path = odroid_system_get_path(ODROID_PATH_SAVE_STATE, emu_get_file_path(file)); // char *sram_path = odroid_system_get_path(ODROID_PATH_SAVE_SRAM, emu_get_file_path(file)); // bool has_save = odroid_sdcard_get_filesize(save_path) > 0; // bool has_sram = odroid_sdcard_get_filesize(sram_path) > 0; // bool is_fav = favorite_find(file) != NULL; bool has_save = 1; bool has_sram = 0; bool is_fav = 0; odroid_dialog_choice_t choices[] = { {0, "Resume game ", "", has_save, NULL}, {1, "New game ", "", 1, NULL}, {0, "------------", "", -1, NULL}, {3, is_fav ? "Del favorite" : "Add favorite", "", 1, NULL}, {2, "Delete save ", "", has_save || has_sram, NULL}, ODROID_DIALOG_CHOICE_LAST }; int sel = odroid_overlay_dialog(NULL, choices, has_save ? 0 : 1); if (sel == 0 || sel == 1) { gui_save_current_tab(); emulator_start(file, sel == 0, false); } else if (sel == 2) { if (odroid_overlay_confirm("Delete save file?", false) == 1) { store_erase(file->save_address, file->save_size); } } else if (sel == 3) { // if (is_fav) // favorite_remove(file); // else // favorite_add(file); } // free(save_path); // free(sram_path); } void emulator_start(retro_emulator_file_t *file, bool load_state, bool start_paused) { printf("Retro-Go: Starting game: %s\n", file->name); rom_manager_set_active_file(file); // odroid_settings_StartAction_set(load_state ? ODROID_START_ACTION_RESUME : ODROID_START_ACTION_NEWGAME); // odroid_settings_RomFilePath_set(path); // odroid_settings_commit(); // odroid_system_switch_app(((retro_emulator_t *)file->emulator)->partition); retro_emulator_t *emu = file_to_emu(file); // TODO: Make this cleaner if(strcmp(emu->system_name, "Nintendo Gameboy") == 0) { #ifdef ENABLE_EMULATOR_GB memcpy(&__RAM_EMU_START__, &_OVERLAY_GB_LOAD_START, (size_t)&_OVERLAY_GB_SIZE); memset(&_OVERLAY_GB_BSS_START, 0x0, (size_t)&_OVERLAY_GB_BSS_SIZE); SCB_CleanDCache_by_Addr((uint32_t *)&__RAM_EMU_START__, (size_t)&_OVERLAY_GB_SIZE); app_main_gb(load_state, start_paused); #endif } else if(strcmp(emu->system_name, "Nintendo Entertainment System") == 0) { #ifdef ENABLE_EMULATOR_NES memcpy(&__RAM_EMU_START__, &_OVERLAY_NES_LOAD_START, (size_t)&_OVERLAY_NES_SIZE); memset(&_OVERLAY_NES_BSS_START, 0x0, (size_t)&_OVERLAY_NES_BSS_SIZE); SCB_CleanDCache_by_Addr((uint32_t *)&__RAM_EMU_START__, (size_t)&_OVERLAY_NES_SIZE); app_main_nes(load_state, start_paused); #endif } else if(strcmp(emu->system_name, "Sega Master System") == 0 || strcmp(emu->system_name, "Sega Game Gear") == 0 || strcmp(emu->system_name, "Sega SG-1000") == 0 || strcmp(emu->system_name, "Colecovision") == 0 ) { #if defined(ENABLE_EMULATOR_SMS) || defined(ENABLE_EMULATOR_GG) || defined(ENABLE_EMULATOR_COL) || defined(ENABLE_EMULATOR_SG1000) memcpy(&__RAM_EMU_START__, &_OVERLAY_SMS_LOAD_START, (size_t)&_OVERLAY_SMS_SIZE); memset(&_OVERLAY_SMS_BSS_START, 0x0, (size_t)&_OVERLAY_SMS_BSS_SIZE); SCB_CleanDCache_by_Addr((uint32_t *)&__RAM_EMU_START__, (size_t)&_OVERLAY_SMS_SIZE); if (! strcmp(emu->system_name, "Colecovision")) app_main_smsplusgx(load_state, start_paused, SMSPLUSGX_ENGINE_COLECO); else if (! strcmp(emu->system_name, "Sega SG-1000")) app_main_smsplusgx(load_state, start_paused, SMSPLUSGX_ENGINE_SG1000); else app_main_smsplusgx(load_state, start_paused, SMSPLUSGX_ENGINE_OTHERS); #endif } else if(strcmp(emu->system_name, "PC Engine") == 0) { #ifdef ENABLE_EMULATOR_PCE memcpy(&__RAM_EMU_START__, &_OVERLAY_PCE_LOAD_START, (size_t)&_OVERLAY_PCE_SIZE); memset(&_OVERLAY_PCE_BSS_START, 0x0, (size_t)&_OVERLAY_PCE_BSS_SIZE); SCB_CleanDCache_by_Addr((uint32_t *)&__RAM_EMU_START__, (size_t)&_OVERLAY_PCE_SIZE); app_main_pce(load_state, start_paused); #endif } else if(strcmp(emu->system_name, "Super Nintendo Entertainment System") == 0) { #ifdef ENABLE_EMULATOR_SNES printf("__RAM_EMU_START__: 0x%08lx\n", &__RAM_EMU_START__); printf("_OVERLAY_SNES_LOAD_START: 0x%08lx\n", &_OVERLAY_SNES_LOAD_START); printf("_OVERLAY_SNES_SIZE: 0x%08lx\n", &_OVERLAY_SNES_SIZE); printf("_OVERLAY_SNES_BSS_START: 0x%08lx\n", &_OVERLAY_SNES_BSS_START); printf("_OVERLAY_SNES_BSS_SIZE: 0x%08lx\n", &_OVERLAY_SNES_BSS_SIZE); memcpy(&__RAM_EMU_START__, &_OVERLAY_SNES_LOAD_START, (size_t)&_OVERLAY_SNES_SIZE); memset(&_OVERLAY_SNES_BSS_START, 0x0, (size_t)&_OVERLAY_SNES_BSS_SIZE); SCB_CleanDCache_by_Addr((uint32_t *)&__RAM_EMU_START__, (size_t)&_OVERLAY_SNES_SIZE); app_main_snes(load_state, start_paused); #endif } } void emulators_init() { #if !( \ defined(ENABLE_EMULATOR_GB) || \ defined(ENABLE_EMULATOR_NES) || \ defined(ENABLE_EMULATOR_GG) || \ defined(ENABLE_EMULATOR_SMS) || \ defined(ENABLE_EMULATOR_COL) || \ defined(ENABLE_EMULATOR_SG1000) || \ defined(ENABLE_EMULATOR_PCE) || \ defined(ENABLE_EMULATOR_SNES) \ ) // Add gameboy as a placeholder in case no emulator is built. add_emulator("Nintendo Gameboy", "gb", "gb", "gnuboy-go", 0, logo_gb, header_gb); #endif #ifdef ENABLE_EMULATOR_GB add_emulator("Nintendo Gameboy", "gb", "gb", "gnuboy-go", 0, logo_gb, header_gb); // add_emulator("Nintendo Gameboy Color", "gbc", "gbc", "gnuboy-go", 0, logo_gbc, header_gbc); #endif #ifdef ENABLE_EMULATOR_NES add_emulator("Nintendo Entertainment System", "nes", "nes", "nofrendo-go", 16, logo_nes, header_nes); #endif #ifdef ENABLE_EMULATOR_SMS add_emulator("Sega Master System", "sms", "sms", "smsplusgx-go", 0, logo_sms, header_sms); #endif #ifdef ENABLE_EMULATOR_GG add_emulator("Sega Game Gear", "gg", "gg", "smsplusgx-go", 0, logo_gg, header_gg); #endif #ifdef ENABLE_EMULATOR_COL add_emulator("Colecovision", "col", "col", "smsplusgx-go", 0, logo_col, header_col); #endif #ifdef ENABLE_EMULATOR_SG1000 add_emulator("Sega SG-1000", "sg", "sg", "smsplusgx-go", 0, logo_sg1000, header_sg1000); #endif #ifdef ENABLE_EMULATOR_PCE add_emulator("PC Engine", "pce", "pce", "huexpress-go", 0, logo_nes, header_pce); #endif #ifdef ENABLE_EMULATOR_SNES // TODO: Add a logo/header add_emulator("Super Nintendo Entertainment System", "snes", "smc", "snes", 0, logo_nes, header_nes); #endif // add_emulator("ColecoVision", "col", "col", "smsplusgx-go", 0, logo_col, header_col); // add_emulator("PC Engine", "pce", "pce", "huexpress-go", 0, logo_pce, header_pce); // add_emulator("Atari Lynx", "lnx", "lnx", "handy-go", 64, logo_lnx, header_lnx); // add_emulator("Atari 2600", "a26", "a26", "stella-go", 0, logo_a26, header_a26); } bool emulator_is_file_valid(retro_emulator_file_t *file) { for (int i = 0; i < emulators_count; i++) { for (int j = 0; j < emulators[i].roms.count; j++) { if (&emulators[i].roms.files[j] == file) { return true; } } } return false; }