mirror of
https://github.com/Polprzewodnikowy/N64FlashcartMenu.git
synced 2024-11-21 18:19:19 +01:00
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:
parent
558a690f3e
commit
37162cac42
1
Makefile
1
Makefile
@ -45,6 +45,7 @@ SRCS = \
|
||||
menu/views/file_info.c \
|
||||
menu/views/image_viewer.c \
|
||||
menu/views/load.c \
|
||||
menu/views/load_emulator.c \
|
||||
menu/views/music_player.c \
|
||||
menu/views/startup.c \
|
||||
menu/views/system_info.c \
|
||||
|
@ -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`
|
||||
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
|
||||
**Work in progress!**
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 4409fe77e208aaef77800b684602d4ea5a74da0b
|
||||
Subproject commit e37421e8e392e51470bb9dc6b5cd7a7419eeb10c
|
@ -32,6 +32,7 @@ static flashcart_t *flashcart = &((flashcart_t) {
|
||||
.init = dummy_init,
|
||||
.deinit = NULL,
|
||||
.load_rom = NULL,
|
||||
.load_file = NULL,
|
||||
.load_save = NULL,
|
||||
.set_save_type = NULL,
|
||||
.set_save_writeback = NULL,
|
||||
@ -111,6 +112,13 @@ flashcart_error_t flashcart_load_rom (char *rom_path, bool byte_swap, flashcart_
|
||||
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 error;
|
||||
uint32_t sectors[WRITEBACK_MAX_SECTORS] __attribute__((aligned(8)));
|
||||
|
@ -42,6 +42,7 @@ typedef struct {
|
||||
flashcart_error_t (*init) (void);
|
||||
flashcart_error_t (*deinit) (void);
|
||||
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 (*set_save_type) (flashcart_save_type_t save_type);
|
||||
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_deinit (void);
|
||||
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);
|
||||
|
||||
|
||||
|
@ -193,6 +193,34 @@ static flashcart_error_t sc64_load_rom (char *rom_path, flashcart_progress_callb
|
||||
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) {
|
||||
void *address = NULL;
|
||||
uint32_t value;
|
||||
@ -294,6 +322,7 @@ static flashcart_t flashcart_sc64 = {
|
||||
.init = sc64_init,
|
||||
.deinit = sc64_deinit,
|
||||
.load_rom = sc64_load_rom,
|
||||
.load_file = sc64_load_file,
|
||||
.load_save = sc64_load_save,
|
||||
.set_save_type = sc64_set_save_type,
|
||||
.set_save_writeback = sc64_set_save_writeback,
|
||||
|
@ -141,6 +141,10 @@ void menu_run (boot_params_t *boot_params) {
|
||||
view_load_display(menu, display);
|
||||
break;
|
||||
|
||||
case MENU_MODE_EMULATOR_LOAD:
|
||||
view_load_emulator_display(menu, display);
|
||||
break;
|
||||
|
||||
case MENU_MODE_ERROR:
|
||||
view_error_display(menu, display);
|
||||
break;
|
||||
@ -191,6 +195,10 @@ void menu_run (boot_params_t *boot_params) {
|
||||
view_load_init(menu);
|
||||
break;
|
||||
|
||||
case MENU_MODE_EMULATOR_LOAD:
|
||||
view_load_emulator_init(menu);
|
||||
break;
|
||||
|
||||
case MENU_MODE_ERROR:
|
||||
view_error_init(menu);
|
||||
break;
|
||||
|
@ -28,6 +28,7 @@ typedef enum {
|
||||
MENU_MODE_MUSIC_PLAYER,
|
||||
MENU_MODE_CREDITS,
|
||||
MENU_MODE_LOAD,
|
||||
MENU_MODE_EMULATOR_LOAD,
|
||||
MENU_MODE_ERROR,
|
||||
MENU_MODE_FAULT,
|
||||
MENU_MODE_BOOT,
|
||||
@ -37,6 +38,7 @@ typedef enum {
|
||||
typedef enum {
|
||||
ENTRY_TYPE_DIR,
|
||||
ENTRY_TYPE_ROM,
|
||||
ENTRY_TYPE_EMULATOR,
|
||||
ENTRY_TYPE_SAVE,
|
||||
ENTRY_TYPE_IMAGE,
|
||||
ENTRY_TYPE_MUSIC,
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
|
||||
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 *image_extensions[] = { "png", NULL };
|
||||
static const char *music_extensions[] = { "mp3", NULL };
|
||||
@ -28,6 +29,10 @@ static int compare_entry (const void *pa, const void *pb) {
|
||||
return -1;
|
||||
} else if (b->type == ENTRY_TYPE_ROM) {
|
||||
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) {
|
||||
return -1;
|
||||
} else if (b->type == ENTRY_TYPE_SAVE) {
|
||||
@ -90,6 +95,8 @@ static bool load_directory (menu_t *menu) {
|
||||
entry->type = ENTRY_TYPE_DIR;
|
||||
} else if (file_has_extensions(info.fname, rom_extensions)) {
|
||||
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)) {
|
||||
entry->type = ENTRY_TYPE_SAVE;
|
||||
} else if (file_has_extensions(info.fname, image_extensions)) {
|
||||
@ -188,6 +195,9 @@ static void process (menu_t *menu) {
|
||||
case ENTRY_TYPE_ROM:
|
||||
menu->next_mode = MENU_MODE_LOAD;
|
||||
break;
|
||||
case ENTRY_TYPE_EMULATOR:
|
||||
menu->next_mode = MENU_MODE_EMULATOR_LOAD;
|
||||
break;
|
||||
case ENTRY_TYPE_IMAGE:
|
||||
menu->next_mode = MENU_MODE_IMAGE_VIEWER;
|
||||
break;
|
||||
|
197
src/menu/views/load_emulator.c
Normal file
197
src/menu/views/load_emulator.c
Normal 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);
|
||||
}
|
||||
}
|
@ -41,6 +41,9 @@ void view_credits_display (menu_t *menu, surface_t *display);
|
||||
void view_load_init (menu_t *menu);
|
||||
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_display (menu_t *menu, surface_t *display);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user