Add emulator loading and screen (#20)

<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
This PR shows a possible way forward for adding support for emulators.
Superseeds #13 

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
The ability to load partial ROM's and/or overwrite parts of them is
required by emulators, patches and potentially gameshark/action replay.

## How Has This Been Tested?
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [x] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [x] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>

---------

Co-authored-by: Mateusz Faderewski <sc@mateuszfaderewski.pl>
This commit is contained in:
Robin Jones 2023-08-05 14:56:10 +01:00 committed by GitHub
parent 558a690f3e
commit 37162cac42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 268 additions and 1 deletions

View File

@ -45,6 +45,7 @@ SRCS = \
menu/views/file_info.c \ menu/views/file_info.c \
menu/views/image_viewer.c \ menu/views/image_viewer.c \
menu/views/load.c \ menu/views/load.c \
menu/views/load_emulator.c \
menu/views/music_player.c \ menu/views/music_player.c \
menu/views/startup.c \ menu/views/startup.c \
menu/views/system_info.c \ menu/views/system_info.c \

View File

@ -26,6 +26,13 @@ To use boxart, you need to place png files of size 158x112 in the folder `sd://m
Each file must be named according to the 2 letter ROM ID. e.g. for goldeneye, this would be `GE.png` Each file must be named according to the 2 letter ROM ID. e.g. for goldeneye, this would be `GE.png`
A known set of PNG files can be downloaded from https://mega.nz/file/6cNGwSqI#8X5ukb65n3YMlGaUtSOGXkKo9HxVnnMOgqn94Epcr7w A known set of PNG files can be downloaded from https://mega.nz/file/6cNGwSqI#8X5ukb65n3YMlGaUtSOGXkKo9HxVnnMOgqn94Epcr7w
### Emulator support
Emulators should be added to the `sd:/emulators/` folder
The menu currently supports the following emulators and associated ROM's:
* neon64v2 (https://github.com/hcs64/neon64v2) - emu.nes
* gb64 (https://lambertjamesd.github.io/gb64/romwrapper/romwrapper.html) - emu.gb, emu.gbc
# Developer documentation # Developer documentation
**Work in progress!** **Work in progress!**

@ -1 +1 @@
Subproject commit 4409fe77e208aaef77800b684602d4ea5a74da0b Subproject commit e37421e8e392e51470bb9dc6b5cd7a7419eeb10c

View File

@ -32,6 +32,7 @@ static flashcart_t *flashcart = &((flashcart_t) {
.init = dummy_init, .init = dummy_init,
.deinit = NULL, .deinit = NULL,
.load_rom = NULL, .load_rom = NULL,
.load_file = NULL,
.load_save = NULL, .load_save = NULL,
.set_save_type = NULL, .set_save_type = NULL,
.set_save_writeback = NULL, .set_save_writeback = NULL,
@ -111,6 +112,13 @@ flashcart_error_t flashcart_load_rom (char *rom_path, bool byte_swap, flashcart_
return error; return error;
} }
flashcart_error_t flashcart_load_file (char *file_path, uint32_t start_offset_address) {
if ((file_path == NULL) || (!file_exists(file_path))) {
return FLASHCART_ERROR_ARGS;
}
return flashcart->load_file(file_path, start_offset_address);
}
flashcart_error_t flashcart_load_save (char *save_path, flashcart_save_type_t save_type) { flashcart_error_t flashcart_load_save (char *save_path, flashcart_save_type_t save_type) {
flashcart_error_t error; flashcart_error_t error;
uint32_t sectors[WRITEBACK_MAX_SECTORS] __attribute__((aligned(8))); uint32_t sectors[WRITEBACK_MAX_SECTORS] __attribute__((aligned(8)));

View File

@ -42,6 +42,7 @@ typedef struct {
flashcart_error_t (*init) (void); flashcart_error_t (*init) (void);
flashcart_error_t (*deinit) (void); flashcart_error_t (*deinit) (void);
flashcart_error_t (*load_rom) (char *rom_path, flashcart_progress_callback_t *progress); flashcart_error_t (*load_rom) (char *rom_path, flashcart_progress_callback_t *progress);
flashcart_error_t (*load_file) (char *file_path, uint32_t start_offset_address);
flashcart_error_t (*load_save) (char *save_path); flashcart_error_t (*load_save) (char *save_path);
flashcart_error_t (*set_save_type) (flashcart_save_type_t save_type); flashcart_error_t (*set_save_type) (flashcart_save_type_t save_type);
flashcart_error_t (*set_save_writeback) (uint32_t *sectors); flashcart_error_t (*set_save_writeback) (uint32_t *sectors);
@ -51,6 +52,7 @@ typedef struct {
flashcart_error_t flashcart_init (void); flashcart_error_t flashcart_init (void);
flashcart_error_t flashcart_deinit (void); flashcart_error_t flashcart_deinit (void);
flashcart_error_t flashcart_load_rom (char *rom_path, bool byte_swap, flashcart_progress_callback_t *progress); flashcart_error_t flashcart_load_rom (char *rom_path, bool byte_swap, flashcart_progress_callback_t *progress);
flashcart_error_t flashcart_load_file (char *file_path, uint32_t start_offset_address);
flashcart_error_t flashcart_load_save (char *save_path, flashcart_save_type_t save_type); flashcart_error_t flashcart_load_save (char *save_path, flashcart_save_type_t save_type);

View File

@ -193,6 +193,34 @@ static flashcart_error_t sc64_load_rom (char *rom_path, flashcart_progress_callb
return FLASHCART_OK; return FLASHCART_OK;
} }
static flashcart_error_t sc64_load_file (char *file_path, uint32_t start_offset_address) {
FIL fil;
UINT br;
if (f_open(&fil, file_path, FA_READ) != FR_OK) {
return FLASHCART_ERROR_LOAD;
}
fix_file_size(&fil);
size_t file_size = f_size(&fil);
if (file_size > MiB(8)) { // FIXME: should be checked with the start offset address.
f_close(&fil);
return FLASHCART_ERROR_LOAD;
}
if (f_read(&fil, (void *) (ROM_ADDRESS + start_offset_address), file_size, &br) != FR_OK) {
f_close(&fil);
return FLASHCART_ERROR_LOAD;
}
if (f_close(&fil) != FR_OK) {
return FLASHCART_ERROR_LOAD;
}
return FLASHCART_OK;
}
static flashcart_error_t sc64_load_save (char *save_path) { static flashcart_error_t sc64_load_save (char *save_path) {
void *address = NULL; void *address = NULL;
uint32_t value; uint32_t value;
@ -294,6 +322,7 @@ static flashcart_t flashcart_sc64 = {
.init = sc64_init, .init = sc64_init,
.deinit = sc64_deinit, .deinit = sc64_deinit,
.load_rom = sc64_load_rom, .load_rom = sc64_load_rom,
.load_file = sc64_load_file,
.load_save = sc64_load_save, .load_save = sc64_load_save,
.set_save_type = sc64_set_save_type, .set_save_type = sc64_set_save_type,
.set_save_writeback = sc64_set_save_writeback, .set_save_writeback = sc64_set_save_writeback,

View File

@ -141,6 +141,10 @@ void menu_run (boot_params_t *boot_params) {
view_load_display(menu, display); view_load_display(menu, display);
break; break;
case MENU_MODE_EMULATOR_LOAD:
view_load_emulator_display(menu, display);
break;
case MENU_MODE_ERROR: case MENU_MODE_ERROR:
view_error_display(menu, display); view_error_display(menu, display);
break; break;
@ -191,6 +195,10 @@ void menu_run (boot_params_t *boot_params) {
view_load_init(menu); view_load_init(menu);
break; break;
case MENU_MODE_EMULATOR_LOAD:
view_load_emulator_init(menu);
break;
case MENU_MODE_ERROR: case MENU_MODE_ERROR:
view_error_init(menu); view_error_init(menu);
break; break;

View File

@ -28,6 +28,7 @@ typedef enum {
MENU_MODE_MUSIC_PLAYER, MENU_MODE_MUSIC_PLAYER,
MENU_MODE_CREDITS, MENU_MODE_CREDITS,
MENU_MODE_LOAD, MENU_MODE_LOAD,
MENU_MODE_EMULATOR_LOAD,
MENU_MODE_ERROR, MENU_MODE_ERROR,
MENU_MODE_FAULT, MENU_MODE_FAULT,
MENU_MODE_BOOT, MENU_MODE_BOOT,
@ -37,6 +38,7 @@ typedef enum {
typedef enum { typedef enum {
ENTRY_TYPE_DIR, ENTRY_TYPE_DIR,
ENTRY_TYPE_ROM, ENTRY_TYPE_ROM,
ENTRY_TYPE_EMULATOR,
ENTRY_TYPE_SAVE, ENTRY_TYPE_SAVE,
ENTRY_TYPE_IMAGE, ENTRY_TYPE_IMAGE,
ENTRY_TYPE_MUSIC, ENTRY_TYPE_MUSIC,

View File

@ -10,6 +10,7 @@
static const char *rom_extensions[] = { "z64", "n64", "v64", NULL }; static const char *rom_extensions[] = { "z64", "n64", "v64", NULL };
static const char *emulator_extensions[] = { "nes", "gb", "gbc", "smc", "gen", "smd", NULL };
static const char *save_extensions[] = { "sav", NULL }; // TODO: "eep", "sra", "srm", "fla" could be used if transfered from different flashcarts. static const char *save_extensions[] = { "sav", NULL }; // TODO: "eep", "sra", "srm", "fla" could be used if transfered from different flashcarts.
static const char *image_extensions[] = { "png", NULL }; static const char *image_extensions[] = { "png", NULL };
static const char *music_extensions[] = { "mp3", NULL }; static const char *music_extensions[] = { "mp3", NULL };
@ -28,6 +29,10 @@ static int compare_entry (const void *pa, const void *pb) {
return -1; return -1;
} else if (b->type == ENTRY_TYPE_ROM) { } else if (b->type == ENTRY_TYPE_ROM) {
return 1; return 1;
} else if (a->type == ENTRY_TYPE_EMULATOR) {
return -1;
} else if (b->type == ENTRY_TYPE_EMULATOR) {
return 1;
} else if (a->type == ENTRY_TYPE_SAVE) { } else if (a->type == ENTRY_TYPE_SAVE) {
return -1; return -1;
} else if (b->type == ENTRY_TYPE_SAVE) { } else if (b->type == ENTRY_TYPE_SAVE) {
@ -90,6 +95,8 @@ static bool load_directory (menu_t *menu) {
entry->type = ENTRY_TYPE_DIR; entry->type = ENTRY_TYPE_DIR;
} else if (file_has_extensions(info.fname, rom_extensions)) { } else if (file_has_extensions(info.fname, rom_extensions)) {
entry->type = ENTRY_TYPE_ROM; entry->type = ENTRY_TYPE_ROM;
}else if (file_has_extensions(info.fname, emulator_extensions)) {
entry->type = ENTRY_TYPE_EMULATOR;
} else if (file_has_extensions(info.fname, save_extensions)) { } else if (file_has_extensions(info.fname, save_extensions)) {
entry->type = ENTRY_TYPE_SAVE; entry->type = ENTRY_TYPE_SAVE;
} else if (file_has_extensions(info.fname, image_extensions)) { } else if (file_has_extensions(info.fname, image_extensions)) {
@ -188,6 +195,9 @@ static void process (menu_t *menu) {
case ENTRY_TYPE_ROM: case ENTRY_TYPE_ROM:
menu->next_mode = MENU_MODE_LOAD; menu->next_mode = MENU_MODE_LOAD;
break; break;
case ENTRY_TYPE_EMULATOR:
menu->next_mode = MENU_MODE_EMULATOR_LOAD;
break;
case ENTRY_TYPE_IMAGE: case ENTRY_TYPE_IMAGE:
menu->next_mode = MENU_MODE_IMAGE_VIEWER; menu->next_mode = MENU_MODE_IMAGE_VIEWER;
break; break;

View File

@ -0,0 +1,197 @@
#include <libdragon.h>
#include "boot/boot.h"
#include "flashcart/flashcart.h"
#include "views.h"
#include "utils/fs.h"
#ifndef EMULATOR_FOLDER
#define EMULATOR_FOLDER "/emulators/"
#endif
static const char *emu_nes_rom_extensions[] = { "nes", NULL };
static const char *emu_gameboy_rom_extensions[] = { "gb", NULL };
static const char *emu_gameboy_color_rom_extensions[] = { "gbc", NULL };
static const char *emu_sega_rom_extensions[] = {"smc", "gen", "smd", NULL };
const uint32_t eum_rom_start_address = 0x200000;
static bool load_pending;
static void draw_progress (float progress) {
surface_t *d = display_try_get();
if (d) {
rdpq_attach(d, NULL);
component_background_draw();
component_loader_draw(progress);
rdpq_detach_show();
}
}
static void load_emulator_nes_rom (path_t *path, menu_t *menu) {
if (file_exists(EMULATOR_FOLDER"emu.nes")) { // || neon64bu.rom
menu->flashcart_error = flashcart_load_rom(EMULATOR_FOLDER"emu.nes", false, draw_progress);
/* Combine EMU and ROM before loading. See https://github.com/hcs64/neon64v2/tree/master/pkg */
menu->flashcart_error = flashcart_load_file(path_get(path), eum_rom_start_address);
if (menu->flashcart_error != FLASHCART_OK) {
menu->next_mode = MENU_MODE_FAULT;
path_free(path);
return;
}
path_ext_replace(path, "sav");
menu->flashcart_error = flashcart_load_save(path_get(path), FLASHCART_SAVE_TYPE_SRAM_BANKED);
if (menu->flashcart_error != FLASHCART_OK) {
menu->next_mode = MENU_MODE_FAULT;
path_free(path);
return;
}
}
}
static void load_emulator_gameboy_rom (path_t *path, menu_t *menu) {
if (file_exists(EMULATOR_FOLDER"emu.gb")) { // || gb.v64
menu->flashcart_error = flashcart_load_rom(EMULATOR_FOLDER"emu.gb", false, draw_progress);
/* Combine EMU and ROM before loading. */
menu->flashcart_error = flashcart_load_file(path_get(path), eum_rom_start_address);
if (menu->flashcart_error != FLASHCART_OK) {
menu->next_mode = MENU_MODE_FAULT;
path_free(path);
return;
}
path_ext_replace(path, "sav");
menu->flashcart_error = flashcart_load_save(path_get(path), FLASHCART_SAVE_TYPE_FLASHRAM);
if (menu->flashcart_error != FLASHCART_OK) {
menu->next_mode = MENU_MODE_FAULT;
path_free(path);
return;
}
}
}
static void load_emulator_gameboy_color_rom (path_t *path, menu_t *menu) {
if (file_exists(EMULATOR_FOLDER"emu.gbc")) { // || gbc.v64
menu->flashcart_error = flashcart_load_rom(EMULATOR_FOLDER"emu.gbc", false, draw_progress);
/* Combine EMU and ROM before loading. */
menu->flashcart_error = flashcart_load_file(path_get(path), eum_rom_start_address);
if (menu->flashcart_error != FLASHCART_OK) {
menu->next_mode = MENU_MODE_FAULT;
path_free(path);
return;
}
path_ext_replace(path, "sav");
menu->flashcart_error = flashcart_load_save(path_get(path), FLASHCART_SAVE_TYPE_FLASHRAM);
if (menu->flashcart_error != FLASHCART_OK) {
menu->next_mode = MENU_MODE_FAULT;
path_free(path);
return;
}
}
}
static void load (menu_t *menu) {
menu->next_mode = MENU_MODE_BOOT;
path_t *path = path_clone(menu->browser.directory);
path_push(path, menu->browser.list[menu->browser.selected].name);
if (file_has_extensions (path_get(path), emu_nes_rom_extensions)) {
load_emulator_nes_rom(path, menu);
}
else if (file_has_extensions (path_get(path), emu_gameboy_rom_extensions)) {
load_emulator_gameboy_rom(path, menu);
}
else if (file_has_extensions (path_get(path), emu_gameboy_color_rom_extensions)) {
load_emulator_gameboy_color_rom(path, menu);
}
else if (file_has_extensions (path_get(path), emu_sega_rom_extensions)) {
//load_emulator_sega_rom(path, menu);
}
path_free(path);
menu->boot_params->device_type = BOOT_DEVICE_TYPE_ROM;
menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH;
menu->boot_params->detect_cic_seed = true;
}
static void process (menu_t *menu) {
if (menu->actions.enter) {
load_pending = true;
} else if (menu->actions.back) {
menu->next_mode = MENU_MODE_BROWSER;
}
}
static void draw (menu_t *menu, surface_t *d) {
rdpq_attach(d, NULL);
component_background_draw();
if (load_pending) {
component_loader_draw(0.0f);
} else {
component_layout_draw();
component_main_text_draw(
ALIGN_CENTER, VALIGN_TOP,
"Emulator information\n"
"THE EMULATOR\n"
"Rom Name\n"
"\n"
"%s",
menu->browser.list[menu->browser.selected].name
);
component_actions_bar_text_draw(
ALIGN_LEFT, VALIGN_TOP,
"A: Load and run Emulator ROM\n"
"B: Exit"
);
}
rdpq_detach_show();
}
void view_load_emulator_init (menu_t *menu) {
load_pending = false;
path_t *path = path_clone(menu->browser.directory);
path_push(path, menu->browser.list[menu->browser.selected].name);
path_free(path);
}
void view_load_emulator_display (menu_t *menu, surface_t *display) {
process(menu);
draw(menu, display);
if (load_pending) {
load_pending = false;
load(menu);
}
}

View File

@ -41,6 +41,9 @@ void view_credits_display (menu_t *menu, surface_t *display);
void view_load_init (menu_t *menu); void view_load_init (menu_t *menu);
void view_load_display (menu_t *menu, surface_t *display); void view_load_display (menu_t *menu, surface_t *display);
void view_load_emulator_init (menu_t *menu);
void view_load_emulator_display (menu_t *menu, surface_t *display);
void view_error_init (menu_t *menu); void view_error_init (menu_t *menu);
void view_error_display (menu_t *menu, surface_t *display); void view_error_display (menu_t *menu, surface_t *display);