From d2b2db151119e20d3711ff958d24dd93ac1c91d1 Mon Sep 17 00:00:00 2001 From: Mateusz Faderewski Date: Thu, 4 Jan 2024 11:14:29 +0100 Subject: [PATCH 01/20] Fixed PAL60 mode + updated libdragon --- src/menu/menu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/menu/menu.c b/src/menu/menu.c index 3e695f0d..7cd3758a 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -20,8 +20,6 @@ #include "views/views.h" -#define TV_TYPE_RAM *((uint32_t *) (0x80000300)) - #define CACHE_DIRECTORY "sd:/menu/cache" #define BACKGROUND_CACHE "sd:/menu/cache/background.data" @@ -33,6 +31,8 @@ static menu_t *menu; static tv_type_t tv_type; static volatile int frame_counter = 0; +extern tv_type_t __boot_tvtype; + static void frame_counter_handler (void) { frame_counter += 1; @@ -99,7 +99,7 @@ static void menu_init (boot_params_t *boot_params) { tv_type = get_tv_type(); if ((tv_type == TV_PAL) && menu->settings.pal60_enabled) { // HACK: Set TV type to NTSC, so PAL console would output 60 Hz signal instead. - TV_TYPE_RAM = TV_NTSC; + __boot_tvtype = TV_NTSC; } display_init(RESOLUTION_640x480, DEPTH_16_BPP, 2, GAMMA_NONE, FILTERS_DISABLED); @@ -111,7 +111,7 @@ static void menu_deinit (menu_t *menu) { unregister_VI_handler(frame_counter_handler); // NOTE: Restore previous TV type so boot procedure wouldn't passthrough wrong value. - TV_TYPE_RAM = tv_type; + __boot_tvtype = tv_type; hdmi_send_game_id(menu->boot_params); From 661f89c87069358468a33db420e06ab0f31decd6 Mon Sep 17 00:00:00 2001 From: Mateusz Faderewski Date: Sun, 7 Jan 2024 05:24:26 +0100 Subject: [PATCH 02/20] Add CNAME to GitHub pages --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d0d2703c..98730deb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -110,3 +110,4 @@ jobs: with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./output/docs + cname: menu.summercart64.dev From ff478bccc78cbcfd96d482aac03595dc20c26cd1 Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Wed, 10 Jan 2024 16:06:20 +0000 Subject: [PATCH 03/20] Update .gitignore Ignore whole tools folder, it might contain other flashcart tools such as UnfLoader --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2cfc2261..de80dacb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ /build /filesystem /output -/tools/sc64/* +/tools/* # There should never be ROMs uploaded, but just incase, make sure they are excluded. **/*.n64 From 5532ae10a4fa350bb591714eec02edcc209b6c1d Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Wed, 10 Jan 2024 17:20:50 +0000 Subject: [PATCH 04/20] Update README.md Improve supported carts --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index f6670732..13100300 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,16 @@ # N64 Flashcart Menu An open source menu for N64 flashcarts. +## Supported Flashcarts + +### Fully supported +* SummerCart64 +* 64Drive + +### Work in Progress (See [PR's](https://github.com/Polprzewodnikowy/N64FlashcartMenu/pulls)) +* ED64 +* ED64P + ## Current (notable) menu features * Fully Open Source. From 0996e88fa1ddd3cff7544713d8dccf65f71132aa Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Wed, 10 Jan 2024 17:42:54 +0000 Subject: [PATCH 05/20] Fix link by removing it --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 13100300..0283dfe3 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ An open source menu for N64 flashcarts. * SummerCart64 * 64Drive -### Work in Progress (See [PR's](https://github.com/Polprzewodnikowy/N64FlashcartMenu/pulls)) +### Work in Progress * ED64 * ED64P From 80eb7bbd9c01a736b44751b7e6f0cccdb5c80083 Mon Sep 17 00:00:00 2001 From: Mateusz Faderewski Date: Wed, 17 Jan 2024 15:43:02 +0100 Subject: [PATCH 06/20] Added CIC selection menu + minor fixes and code cleanup --- Makefile | 8 +- libdragon | 2 +- src/boot/cic.c | 4 +- src/boot/cic.h | 2 +- src/flashcart/64drive/64drive.c | 2 +- src/flashcart/sc64/sc64.c | 4 +- src/menu/components.h | 3 +- src/menu/components/context_menu.c | 2 +- src/menu/menu.c | 62 ++++++---- src/menu/menu_state.h | 1 - src/menu/mp3_player.c | 6 +- src/menu/png_decoder.c | 1 + src/menu/rom_info.c | 179 ++++++++++++++++++----------- src/menu/rom_info.h | 87 ++++++++++---- src/menu/sound.c | 2 +- src/menu/usb_comm.c | 2 + src/menu/views/browser.c | 68 +++++------ src/menu/views/credits.c | 12 +- src/menu/views/load_disk.c | 18 ++- src/menu/views/load_rom.c | 144 +++++++++++++---------- src/menu/views/settings_editor.c | 23 ++-- src/menu/views/system_info.c | 41 +++---- src/menu/views/views.h | 18 +-- 23 files changed, 417 insertions(+), 274 deletions(-) diff --git a/Makefile b/Makefile index 2cc28f62..db6493d9 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,8 @@ FILESYSTEM_DIR = filesystem BUILD_DIR = build OUTPUT_DIR = output -FLAGS += -DMENU_VERSION=\"0.0.1.$(shell date +%Y-%m-%dT%H:%M:%SZ).ALPHA\" +MENU_VERSION ?= "Rolling release" +BUILD_TIMESTAMP = "$(shell TZ='UTC' date "+%Y-%m-%d %H:%M:%S %:z")" include $(N64_INST)/include/n64.mk @@ -88,6 +89,9 @@ $(FILESYSTEM_DIR)/%.font64: $(ASSETS_DIR)/%.ttf $(BUILD_DIR)/$(PROJECT_NAME).dfs: $(FILESYSTEM) +$(BUILD_DIR)/menu/views/credits.o: .FORCE +$(BUILD_DIR)/menu/views/credits.o: FLAGS+=-DMENU_VERSION=\"$(MENU_VERSION)\" -DBUILD_TIMESTAMP=\"$(BUILD_TIMESTAMP)\" + $(BUILD_DIR)/$(PROJECT_NAME).elf: $(OBJS) disassembly: $(BUILD_DIR)/$(PROJECT_NAME).elf @@ -144,4 +148,6 @@ endif # test: # TODO: run tests +.FORCE: + -include $(DEPS) diff --git a/libdragon b/libdragon index 6323128d..185c4a34 160000 --- a/libdragon +++ b/libdragon @@ -1 +1 @@ -Subproject commit 6323128d72fdf32dfaa134f40191ba72e5527076 +Subproject commit 185c4a34f265c90d027f4054024cff1675529d7f diff --git a/src/boot/cic.c b/src/boot/cic.c index 58f03fbc..990e95ca 100644 --- a/src/boot/cic.c +++ b/src/boot/cic.c @@ -102,7 +102,7 @@ cic_type_t cic_detect (uint8_t *ipl3) { switch (cic_calculate_ipl3_checksum(ipl3, 0x3F)) { case 0x45CC73EE317AULL: return CIC_6101; // 6101 case 0x44160EC5D9AFULL: return CIC_7102; // 7102 - case 0xA536C0F1D859ULL: return CIC_6102_7101; // 6102 / 7101 + case 0xA536C0F1D859ULL: return CIC_x102; // 6102 / 7101 } switch (cic_calculate_ipl3_checksum(ipl3, 0x78)) { case 0x586FD4709867ULL: return CIC_x103; // 6103 / 7103 @@ -133,7 +133,7 @@ uint8_t cic_get_seed (cic_type_t cic_type) { case CIC_5167: return 0xDD; case CIC_6101: return 0x3F; case CIC_7102: return 0x3F; - case CIC_6102_7101: return 0x3F; + case CIC_x102: return 0x3F; case CIC_x103: return 0x78; case CIC_x105: return 0x91; case CIC_x106: return 0x85; diff --git a/src/boot/cic.h b/src/boot/cic.h index 4f97c6c4..3de811bb 100644 --- a/src/boot/cic.h +++ b/src/boot/cic.h @@ -13,7 +13,7 @@ typedef enum { CIC_5167, CIC_6101, CIC_7102, - CIC_6102_7101, + CIC_x102, CIC_x103, CIC_x105, CIC_x106, diff --git a/src/flashcart/64drive/64drive.c b/src/flashcart/64drive/64drive.c index 6bcee6cb..b2ed3b36 100644 --- a/src/flashcart/64drive/64drive.c +++ b/src/flashcart/64drive/64drive.c @@ -96,7 +96,7 @@ static flashcart_err_t d64_load_rom (char *rom_path, flashcart_progress_callback size_t sdram_size = MiB(64); - size_t chunk_size = MiB(1); + size_t chunk_size = KiB(128); for (int offset = 0; offset < sdram_size; offset += chunk_size) { size_t block_size = MIN(sdram_size - offset, chunk_size); if (f_read(&fil, (void *) (ROM_ADDRESS + offset), block_size, &br) != FR_OK) { diff --git a/src/flashcart/sc64/sc64.c b/src/flashcart/sc64/sc64.c index a0878c9d..58f4c450 100644 --- a/src/flashcart/sc64/sc64.c +++ b/src/flashcart/sc64/sc64.c @@ -288,7 +288,7 @@ static flashcart_err_t sc64_load_rom (char *rom_path, flashcart_progress_callbac size_t shadow_size = shadow_enabled ? MIN(rom_size - sdram_size, KiB(128)) : 0; size_t extended_size = extended_enabled ? rom_size - MiB(64) : 0; - size_t chunk_size = MiB(1); + size_t chunk_size = KiB(128); for (int offset = 0; offset < sdram_size; offset += chunk_size) { size_t block_size = MIN(sdram_size - offset, chunk_size); if (f_read(&fil, (void *) (ROM_ADDRESS + offset), block_size, &br) != FR_OK) { @@ -450,7 +450,7 @@ static flashcart_err_t sc64_load_64dd_ipl (char *ipl_path, flashcart_progress_ca return FLASHCART_ERR_LOAD; } - size_t chunk_size = KiB(256); + size_t chunk_size = KiB(128); for (int offset = 0; offset < ipl_size; offset += chunk_size) { size_t block_size = MIN(ipl_size - offset, chunk_size); if (f_read(&fil, (void *) (IPL_ADDRESS + offset), block_size, &br) != FR_OK) { diff --git a/src/menu/components.h b/src/menu/components.h index 127c1d7b..a8cecb51 100644 --- a/src/menu/components.h +++ b/src/menu/components.h @@ -45,7 +45,8 @@ typedef struct component_context_menu { struct component_context_menu *submenu; struct { const char *text; - void (*action) (menu_t *menu); + void (*action) (menu_t *menu, void *arg); + void *arg; struct component_context_menu *submenu; } list[]; } component_context_menu_t; diff --git a/src/menu/components/context_menu.c b/src/menu/components/context_menu.c index 82b3daa0..f2e32c42 100644 --- a/src/menu/components/context_menu.c +++ b/src/menu/components/context_menu.c @@ -48,7 +48,7 @@ bool component_context_menu_process (menu_t *menu, component_context_menu_t *cm) cm->submenu->selected = 0; cm->submenu->parent = cm; } else if (cm->list[cm->selected].action) { - cm->list[cm->selected].action(menu); + cm->list[cm->selected].action(menu, cm->list[cm->selected].arg); top->hide_pending = true; } } else if (menu->actions.go_up) { diff --git a/src/menu/menu.c b/src/menu/menu.c index 7cd3758a..06e9ef2d 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -20,6 +20,7 @@ #include "views/views.h" +#define MENU_DIRECTORY "sd:/menu" #define CACHE_DIRECTORY "sd:/menu/cache" #define BACKGROUND_CACHE "sd:/menu/cache/background.data" @@ -77,6 +78,8 @@ static void menu_init (boot_params_t *boot_params) { menu->error_message = NULL; + directory_create(MENU_DIRECTORY); + settings_load(&menu->settings); directory_create(CACHE_DIRECTORY); @@ -133,30 +136,39 @@ static void menu_deinit (menu_t *menu) { display_close(); } - -// NOTE: Keep this array in sync with menu_mode_t -static struct views_s { +typedef const struct { + menu_mode_t id; void (*init) (menu_t *menu); void (*show) (menu_t *menu, surface_t *display); -} views[__MENU_MODE_COUNT] = { - { NULL, NULL }, // MENU_MODE_NONE - { view_startup_init, view_startup_display }, // MENU_MODE_STARTUP - { view_browser_init, view_browser_display }, // MENU_MODE_BROWSER - { view_file_info_init, view_file_info_display }, // MENU_MODE_FILE_INFO - { view_system_info_init, view_system_info_display }, // MENU_MODE_SYSTEM_INFO - { view_image_viewer_init, view_image_viewer_display }, // MENU_MODE_IMAGE_VIEWER - { view_music_player_init, view_music_player_display }, // MENU_MODE_MUSIC_PLAYER - { view_credits_init, view_credits_display }, // MENU_MODE_CREDITS - { view_settings_init, view_settings_display }, // MENU_MODE_SETTINGS_EDITOR - { view_rtc_init, view_rtc_display }, // MENU_MODE_RTC - { view_load_rom_init, view_load_rom_display }, // MENU_MODE_LOAD_ROM - { view_load_disk_init, view_load_disk_display }, // MENU_MODE_LOAD_DISK - { view_load_emulator_init, view_load_emulator_display }, // MENU_MODE_LOAD_EMULATOR - { view_error_init, view_error_display }, // MENU_MODE_ERROR - { view_fault_init, view_fault_display }, // MENU_MODE_FAULT - { NULL, NULL }, // MENU_MODE_BOOT +} view_t; + +static view_t menu_views[] = { + { MENU_MODE_STARTUP, view_startup_init, view_startup_display }, + { MENU_MODE_BROWSER, view_browser_init, view_browser_display }, + { MENU_MODE_FILE_INFO, view_file_info_init, view_file_info_display }, + { MENU_MODE_SYSTEM_INFO, view_system_info_init, view_system_info_display }, + { MENU_MODE_IMAGE_VIEWER, view_image_viewer_init, view_image_viewer_display }, + { MENU_MODE_MUSIC_PLAYER, view_music_player_init, view_music_player_display }, + { MENU_MODE_CREDITS, view_credits_init, view_credits_display }, + { MENU_MODE_SETTINGS_EDITOR, view_settings_init, view_settings_display }, + { MENU_MODE_RTC, view_rtc_init, view_rtc_display }, + { MENU_MODE_LOAD_ROM, view_load_rom_init, view_load_rom_display }, + { MENU_MODE_LOAD_DISK, view_load_disk_init, view_load_disk_display }, + { MENU_MODE_LOAD_EMULATOR, view_load_emulator_init, view_load_emulator_display }, + { MENU_MODE_ERROR, view_error_init, view_error_display }, + { MENU_MODE_FAULT, view_fault_init, view_fault_display }, }; +static view_t *menu_get_view (menu_mode_t id) { + for (int i = 0; i < sizeof(menu_views) / sizeof(view_t); i++) { + if (menu_views[i].id == id) { + return &menu_views[i]; + } + } + return NULL; +} + + void menu_run (boot_params_t *boot_params) { menu_init(boot_params); @@ -168,8 +180,9 @@ void menu_run (boot_params_t *boot_params) { actions_update(menu); - if (views[menu->mode].show) { - views[menu->mode].show(menu, display); + view_t *view = menu_get_view(menu->mode); + if (view && view->show) { + view->show(menu, display); } else { rdpq_attach_clear(display, NULL); rdpq_detach_wait(); @@ -183,8 +196,9 @@ void menu_run (boot_params_t *boot_params) { while (menu->mode != menu->next_mode) { menu->mode = menu->next_mode; - if (views[menu->mode].init) { - views[menu->mode].init(menu); + view_t *next_view = menu_get_view(menu->next_mode); + if (next_view && next_view->init) { + next_view->init(menu); } } diff --git a/src/menu/menu_state.h b/src/menu/menu_state.h index d29573d4..9fcee137 100644 --- a/src/menu/menu_state.h +++ b/src/menu/menu_state.h @@ -39,7 +39,6 @@ typedef enum { MENU_MODE_ERROR, MENU_MODE_FAULT, MENU_MODE_BOOT, - __MENU_MODE_COUNT, } menu_mode_t; /** @brief File entry type enumeration */ diff --git a/src/menu/mp3_player.c b/src/menu/mp3_player.c index 3dfbd7da..87109a3e 100644 --- a/src/menu/mp3_player.c +++ b/src/menu/mp3_player.c @@ -27,7 +27,7 @@ typedef struct { mp3dec_t dec; mp3dec_frame_info_t info; - uint8_t buffer[MAX_FREE_FORMAT_FRAME_SIZE]; + uint8_t buffer[16 * 1024]; uint8_t *buffer_ptr; size_t buffer_left; @@ -54,6 +54,10 @@ static void mp3player_fill_buffer (void) { return; } + if (p->buffer_left >= ALIGN(MAX_FREE_FORMAT_FRAME_SIZE, FS_SECTOR_SIZE)) { + return; + } + if ((p->buffer_ptr != p->buffer) && (p->buffer_left > 0)) { memmove(p->buffer, p->buffer_ptr, p->buffer_left); p->buffer_ptr = p->buffer; diff --git a/src/menu/png_decoder.c b/src/menu/png_decoder.c index 951057b6..54d185d3 100644 --- a/src/menu/png_decoder.c +++ b/src/menu/png_decoder.c @@ -4,6 +4,7 @@ #include "png_decoder.h" #include "utils/fs.h" + /** @brief PNG File Information Structure. */ typedef struct { FIL fil; diff --git a/src/menu/rom_info.c b/src/menu/rom_info.c index 8d8f1f49..6a2223c5 100644 --- a/src/menu/rom_info.c +++ b/src/menu/rom_info.c @@ -3,6 +3,7 @@ #include #include +#include "boot/cic.h" #include "rom_info.h" #include "utils/fs.h" @@ -16,6 +17,7 @@ #define CLOCK_RATE_DEFAULT (0x0000000F) + /** @brief ROM File Information Structure. */ typedef struct __attribute__((packed)) { uint32_t pi_dom1_config; @@ -652,15 +654,34 @@ static match_t find_rom_in_database (rom_header_t *rom_header) { return *match; } -static uint32_t fix_boot_address (cic_type_t cic_type, uint32_t boot_address) { +static rom_cic_type_t detect_cic_type (uint8_t *ipl3) { + switch (cic_detect(ipl3)) { + case CIC_5101: return ROM_CIC_TYPE_5101; + case CIC_5167: return ROM_CIC_TYPE_5167; + case CIC_6101: return ROM_CIC_TYPE_6101; + case CIC_7102: return ROM_CIC_TYPE_7102; + case CIC_x102: return ROM_CIC_TYPE_x102; + case CIC_x103: return ROM_CIC_TYPE_x103; + case CIC_x105: return ROM_CIC_TYPE_x105; + case CIC_x106: return ROM_CIC_TYPE_x106; + case CIC_8301: return ROM_CIC_TYPE_8301; + case CIC_8302: return ROM_CIC_TYPE_8302; + case CIC_8303: return ROM_CIC_TYPE_8303; + case CIC_8401: return ROM_CIC_TYPE_8401; + case CIC_8501: return ROM_CIC_TYPE_8501; + default: return ROM_CIC_TYPE_UNKNOWN; + } +} + +static uint32_t fix_boot_address (rom_cic_type_t cic_type, uint32_t boot_address) { switch (cic_type) { - case CIC_x103: return (boot_address - 0x100000); - case CIC_x106: return (boot_address - 0x200000); + case ROM_CIC_TYPE_x103: return (boot_address - 0x100000); + case ROM_CIC_TYPE_x106: return (boot_address - 0x200000); default: return boot_address; } } -static rom_tv_type_t determine_tv_type (destination_type_t rom_destination_code) { +static rom_tv_type_t determine_tv_type (rom_destination_type_t rom_destination_code) { // check the market type from the ROM destination_code and return best guess! switch (rom_destination_code) { case MARKET_NORTH_AMERICA: @@ -695,7 +716,7 @@ static rom_tv_type_t determine_tv_type (destination_type_t rom_destination_code) } static void extract_rom_info (match_t *match, rom_header_t *rom_header, rom_info_t *rom_info) { - rom_info->cic_type = cic_detect(rom_header->ipl3); + rom_info->cic_type = detect_cic_type(rom_header->ipl3); if (match->type == MATCH_TYPE_HOMEBREW_HEADER) { if (rom_header->version & (1 << 0)) { @@ -746,63 +767,72 @@ static void extract_rom_info (match_t *match, rom_header_t *rom_header, rom_info } static void load_overrides (path_t *path, rom_info_t *rom_info) { + path_t *overrides_path = path_clone(path); + + path_ext_replace(overrides_path, "ini"); + + mini_t *ini = mini_load(path_get(overrides_path)); + + rom_info->override.cic = false; rom_info->override.save = false; rom_info->override.tv = false; - path_t *overrides_path = path_clone(path); + if (ini) { + rom_info->override.cic_type = mini_get_int(ini, NULL, "cic_type", ROM_CIC_TYPE_AUTOMATIC); + if (rom_info->override.cic_type != ROM_CIC_TYPE_AUTOMATIC) { + rom_info->override.cic = true; + } - path_ext_replace(overrides_path, "ini"); + rom_info->override.save_type = mini_get_int(ini, NULL, "save_type", SAVE_TYPE_AUTOMATIC); + if (rom_info->override.save_type != SAVE_TYPE_AUTOMATIC) { + rom_info->override.save = true; + } - if (!file_exists(path_get(overrides_path))) { - path_free(overrides_path); - return; + rom_info->override.tv_type = mini_get_int(ini, NULL, "tv_type", ROM_TV_TYPE_AUTOMATIC); + if (rom_info->override.tv_type != ROM_TV_TYPE_AUTOMATIC) { + rom_info->override.tv = true; + } + + mini_free(ini); } - mini_t *ini = mini_try_load(path_get(overrides_path)); - - rom_info->override.save_type = mini_get_int(ini, NULL, "save_type", SAVE_TYPE_AUTOMATIC); - if (rom_info->override.save_type != SAVE_TYPE_AUTOMATIC) { - rom_info->override.save = true; - } - - rom_info->override.tv_type = mini_get_int(ini, NULL, "tv_type", ROM_TV_TYPE_AUTOMATIC); - if (rom_info->override.tv_type != ROM_TV_TYPE_AUTOMATIC) { - rom_info->override.tv = true; - } - - mini_free(ini); - path_free(overrides_path); } - -rom_err_t rom_info_override_save_type (path_t *path, rom_info_t *rom_info, rom_save_type_t save_type) { +static rom_err_t save_override (path_t *path, const char *id, int value, int default_value) { path_t *overrides_path = path_clone(path); path_ext_replace(overrides_path, "ini"); mini_t *ini = mini_try_load(path_get(overrides_path)); - rom_info->override.save_type = save_type; - - if (rom_info->override.save_type == SAVE_TYPE_AUTOMATIC) { - rom_info->override.save = false; - mini_delete_value(ini, NULL, "save_type"); - } else { - rom_info->override.save = true; - mini_set_int(ini, NULL, "save_type", rom_info->override.save_type); + if (!ini) { + return ROM_ERR_IO; } - bool empty_override_file = mini_empty(ini); + if (value == default_value) { + mini_delete_value(ini, NULL, id); + } else { + mini_set_int(ini, NULL, id, value); + } - if (!empty_override_file) { - mini_save(ini, MINI_FLAGS_NONE); + bool empty = mini_empty(ini); + + if (!empty) { + if (mini_save(ini, MINI_FLAGS_NONE) != MINI_OK) { + path_free(overrides_path); + mini_free(ini); + return ROM_ERR_IO; + } } mini_free(ini); - if (empty_override_file) { - file_delete(path_get(overrides_path)); + if (empty) { + if (file_delete(path_get(overrides_path))) { + path_free(overrides_path); + return ROM_ERR_IO; + } } path_free(overrides_path); @@ -810,38 +840,45 @@ rom_err_t rom_info_override_save_type (path_t *path, rom_info_t *rom_info, rom_s return ROM_OK; } -rom_err_t rom_info_override_tv_type (path_t *path, rom_info_t *rom_info, rom_tv_type_t tv_type) { - path_t *overrides_path = path_clone(path); - path_ext_replace(overrides_path, "ini"); - - mini_t *ini = mini_try_load(path_get(overrides_path)); - - rom_info->override.tv_type = tv_type; - - if (rom_info->override.tv_type == ROM_TV_TYPE_AUTOMATIC) { - rom_info->override.tv = false; - mini_delete_value(ini, NULL, "tv_type"); +rom_cic_type_t rom_info_get_cic_type (rom_info_t *rom_info) { + if (rom_info->override.cic) { + return rom_info->override.cic_type; } else { - rom_info->override.tv = true; - mini_set_int(ini, NULL, "tv_type", rom_info->override.tv_type); + return rom_info->cic_type; + } +} + +bool rom_info_get_cic_seed (rom_info_t *rom_info, uint8_t *seed) { + cic_type_t cic_type; + + switch (rom_info_get_cic_type(rom_info)) { + case ROM_CIC_TYPE_5101: cic_type = CIC_5101; break; + case ROM_CIC_TYPE_5167: cic_type = CIC_5167; break; + case ROM_CIC_TYPE_6101: cic_type = CIC_6101; break; + case ROM_CIC_TYPE_7102: cic_type = CIC_7102; break; + case ROM_CIC_TYPE_x102: cic_type = CIC_x102; break; + case ROM_CIC_TYPE_x103: cic_type = CIC_x103; break; + case ROM_CIC_TYPE_x105: cic_type = CIC_x105; break; + case ROM_CIC_TYPE_x106: cic_type = CIC_x106; break; + case ROM_CIC_TYPE_8301: cic_type = CIC_8301; break; + case ROM_CIC_TYPE_8302: cic_type = CIC_8302; break; + case ROM_CIC_TYPE_8303: cic_type = CIC_8303; break; + case ROM_CIC_TYPE_8401: cic_type = CIC_8401; break; + case ROM_CIC_TYPE_8501: cic_type = CIC_8501; break; + default: cic_type = CIC_UNKNOWN; break; } - bool empty_override_file = mini_empty(ini); + *seed = cic_get_seed(cic_type); - if (!empty_override_file) { - mini_save(ini, MINI_FLAGS_NONE); - } + return (!rom_info->override.cic); +} - mini_free(ini); +rom_err_t rom_info_override_cic_type (path_t *path, rom_info_t *rom_info, rom_cic_type_t cic_type) { + rom_info->override.cic = (cic_type != ROM_CIC_TYPE_AUTOMATIC); + rom_info->override.cic_type = cic_type; - if (empty_override_file) { - file_delete(path_get(overrides_path)); - } - - path_free(overrides_path); - - return ROM_OK; + return save_override(path, "cic_type", rom_info->override.cic_type, ROM_CIC_TYPE_AUTOMATIC); } rom_save_type_t rom_info_get_save_type (rom_info_t *rom_info) { @@ -852,6 +889,13 @@ rom_save_type_t rom_info_get_save_type (rom_info_t *rom_info) { } } +rom_err_t rom_info_override_save_type (path_t *path, rom_info_t *rom_info, rom_save_type_t save_type) { + rom_info->override.save = (save_type != SAVE_TYPE_AUTOMATIC); + rom_info->override.save_type = save_type; + + return save_override(path, "save_type", rom_info->override.save_type, SAVE_TYPE_AUTOMATIC); +} + rom_tv_type_t rom_info_get_tv_type (rom_info_t *rom_info) { if (rom_info->override.tv) { return rom_info->override.tv_type; @@ -860,6 +904,13 @@ rom_tv_type_t rom_info_get_tv_type (rom_info_t *rom_info) { } } +rom_err_t rom_info_override_tv_type (path_t *path, rom_info_t *rom_info, rom_tv_type_t tv_type) { + rom_info->override.tv = (tv_type != ROM_TV_TYPE_AUTOMATIC); + rom_info->override.tv_type = tv_type; + + return save_override(path, "tv_type", rom_info->override.tv_type, ROM_TV_TYPE_AUTOMATIC); +} + rom_err_t rom_info_load (path_t *path, rom_info_t *rom_info) { FIL fil; UINT br; diff --git a/src/menu/rom_info.h b/src/menu/rom_info.h index a4088c64..cb241a2e 100644 --- a/src/menu/rom_info.h +++ b/src/menu/rom_info.h @@ -1,7 +1,7 @@ /** * @file rom_info.h * @brief N64 ROM Database. - * @note Only works with N64 ROM's by checking the first 1024 bytes of the file. + * @note Only works with N64 ROM's by checking the first 4096 bytes of the file. * @ingroup menu */ @@ -12,7 +12,6 @@ #include #include -#include "boot/cic.h" #include "path.h" @@ -31,7 +30,7 @@ typedef enum { ENDIANNESS_LITTLE, /** @brief Is Byte Swapped Endian. */ ENDIANNESS_BYTE_SWAP, -} endianness_t; +} rom_endianness_t; /** @brief ROM media type enumeration. */ typedef enum { @@ -45,7 +44,7 @@ typedef enum { N64_DISK_EXPANDABLE = 'E', /** @brief Is an Aleck64 program. */ N64_ALECK64 = 'Z' -} category_type_t; +} rom_category_type_t; /** @brief ROM market region & language type enumeration. */ typedef enum { @@ -89,27 +88,46 @@ typedef enum { MARKET_OTHER_Y = 'Y', // many EU ROM's uses this. /** @brief The ROM is designed for an undefined region and TBD language(s). */ MARKET_OTHER_Z = 'Z' // no known ROM's use this. -} destination_type_t; +} rom_destination_type_t; + +/** @brief ROM CIC type enumeration. */ +typedef enum { + ROM_CIC_TYPE_UNKNOWN = 0, // No known CIC type detected + ROM_CIC_TYPE_5101 = 5101, // Aleck64 CIC-5101 + ROM_CIC_TYPE_5167 = 5167, // 64DD ROM conversion CIC-5167 + ROM_CIC_TYPE_6101 = 6101, // NTSC CIC-6101 + ROM_CIC_TYPE_7102 = 7102, // PAL CIC-7102 + ROM_CIC_TYPE_x102 = 6102, // NTSC CIC-6102 / PAL CIC-7101 + ROM_CIC_TYPE_x103 = 6103, // NTSC CIC-6103 / PAL CIC-7103 + ROM_CIC_TYPE_x105 = 6105, // NTSC CIC-6105 / PAL CIC-7105 + ROM_CIC_TYPE_x106 = 6106, // NTSC CIC-6106 / PAL CIC-7106 + ROM_CIC_TYPE_8301 = 8301, // NDDJ0 64DD IPL + ROM_CIC_TYPE_8302 = 8302, // NDDJ1 64DD IPL + ROM_CIC_TYPE_8303 = 8303, // NDDJ2 64DD IPL + ROM_CIC_TYPE_8401 = 8401, // NDXJ0 64DD IPL + ROM_CIC_TYPE_8501 = 8501, // NDDE0 64DD IPL + ROM_CIC_TYPE_AUTOMATIC = -1, // Guess CIC from IPL3 +} rom_cic_type_t; /** @brief ROM save type enumeration. */ typedef enum { /** @brief There is no expected save type. */ - SAVE_TYPE_NONE, - SAVE_TYPE_EEPROM_4K, - SAVE_TYPE_EEPROM_16K, - SAVE_TYPE_SRAM, - SAVE_TYPE_SRAM_BANKED, - SAVE_TYPE_SRAM_128K, - SAVE_TYPE_FLASHRAM, - SAVE_TYPE_FLASHRAM_PKST2, + SAVE_TYPE_NONE = 0, + SAVE_TYPE_EEPROM_4K = 1, + SAVE_TYPE_EEPROM_16K = 2, + SAVE_TYPE_SRAM = 3, + SAVE_TYPE_SRAM_BANKED = 4, + SAVE_TYPE_SRAM_128K = 5, + SAVE_TYPE_FLASHRAM = 6, + SAVE_TYPE_FLASHRAM_PKST2 = 7, SAVE_TYPE_AUTOMATIC = -1, } rom_save_type_t; typedef enum { - ROM_TV_TYPE_PAL, - ROM_TV_TYPE_NTSC, - ROM_TV_TYPE_MPAL, - ROM_TV_TYPE_UNKNOWN, + ROM_TV_TYPE_PAL = 0, + ROM_TV_TYPE_NTSC = 1, + ROM_TV_TYPE_MPAL = 2, + ROM_TV_TYPE_UNKNOWN = 3, ROM_TV_TYPE_AUTOMATIC = -1, } rom_tv_type_t; @@ -129,49 +147,62 @@ typedef enum { /** @brief The ROM is faulty when using 8MB of memory. */ EXPANSION_PAK_FAULTY, -} expansion_pak_t; +} rom_expansion_pak_t; /** @brief ROM Information Structure. */ typedef struct { /** @brief The file endian. */ - endianness_t endianness; + rom_endianness_t endianness; + /** @brief The clock rate defined in the ROM's header. */ float clock_rate; + /** @brief The boot address defined in the ROM's header. */ uint32_t boot_address; + struct { /** @brief The SDK version defined in the ROM's header. */ uint8_t version; /** @brief The SDK revision defined in the ROM's header. */ char revision; } libultra; + /** @brief The check code defined in the ROM's header. */ uint64_t check_code; + /** @brief The title defined in the ROM's header. */ char title[20]; + union { /** @brief The game code defined in the ROM's header. */ char game_code[4]; struct { /** @brief The game media type. */ - category_type_t category_code : 8; + rom_category_type_t category_code : 8; /** @brief The game unique identifier. */ char unique_code[2]; /** @brief The game region and or market. */ - destination_type_t destination_code : 8; + rom_destination_type_t destination_code : 8; }; }; + /** @brief The ROM version defined in the ROM's header. */ uint8_t version; - cic_type_t cic_type; + /** @brief The CIC type required by the ROM. */ + rom_cic_type_t cic_type; /** @brief The save type required by the ROM. */ rom_save_type_t save_type; + /** @brief The TV type required by the ROM. */ rom_tv_type_t tv_type; + /** @brief Overrides of auto-detected CIC/save/TV types. */ struct { + bool cic; + rom_cic_type_t cic_type; + bool save; rom_save_type_t save_type; @@ -188,15 +219,21 @@ typedef struct { bool real_time_clock; bool disk_conversion; bool combo_rom_disk_game; - expansion_pak_t expansion_pak; + rom_expansion_pak_t expansion_pak; } features; } rom_info_t; -rom_err_t rom_info_override_save_type (path_t *path, rom_info_t *rom_info, rom_save_type_t save_type); -rom_err_t rom_info_override_tv_type (path_t *path, rom_info_t *rom_info, rom_tv_type_t tv_type); +rom_cic_type_t rom_info_get_cic_type (rom_info_t *rom_info); +bool rom_info_get_cic_seed (rom_info_t *rom_info, uint8_t *seed); +rom_err_t rom_info_override_cic_type (path_t *path, rom_info_t *rom_info, rom_cic_type_t cic_type); + rom_save_type_t rom_info_get_save_type (rom_info_t *rom_info); +rom_err_t rom_info_override_save_type (path_t *path, rom_info_t *rom_info, rom_save_type_t save_type); + rom_tv_type_t rom_info_get_tv_type (rom_info_t *rom_info); +rom_err_t rom_info_override_tv_type (path_t *path, rom_info_t *rom_info, rom_tv_type_t tv_type); + rom_err_t rom_info_load (path_t *path, rom_info_t *rom_info); diff --git a/src/menu/sound.c b/src/menu/sound.c index d613b4f2..9e58d001 100644 --- a/src/menu/sound.c +++ b/src/menu/sound.c @@ -6,7 +6,7 @@ #define DEFAULT_FREQUENCY (44100) -#define NUM_BUFFERS (2) +#define NUM_BUFFERS (4) #define NUM_CHANNELS (2) diff --git a/src/menu/usb_comm.c b/src/menu/usb_comm.c index c823a3bd..fd218cd3 100644 --- a/src/menu/usb_comm.c +++ b/src/menu/usb_comm.c @@ -12,10 +12,12 @@ #define MAX_FILE_SIZE MiB(4) + /** @brief The supported USB commands structure. */ typedef struct { /** @brief The command identifier. */ const char *id; + /** @brief The command operation. */ void (*op) (menu_t *menu); } usb_comm_command_t; diff --git a/src/menu/views/browser.c b/src/menu/views/browser.c index 75ab0a4a..a7517946 100644 --- a/src/menu/views/browser.c +++ b/src/menu/views/browser.c @@ -135,6 +135,22 @@ static bool load_directory (menu_t *menu) { return false; } +static bool reload_directory (menu_t *menu) { + int selected = menu->browser.selected; + + if (load_directory(menu)) { + return true; + } + + menu->browser.selected = selected; + if (menu->browser.selected >= menu->browser.entries) { + menu->browser.selected = menu->browser.entries - 1; + } + menu->browser.entry = menu->browser.selected >= 0 ? &menu->browser.list[menu->browser.selected] : NULL; + + return false; +} + static bool push_directory (menu_t *menu, char *directory) { path_t *previous_directory = path_clone(menu->browser.directory); @@ -175,13 +191,11 @@ static bool pop_directory (menu_t *menu) { return false; } -static void show_properties (menu_t *menu) { +static void show_properties (menu_t *menu, void *arg) { menu->next_mode = MENU_MODE_FILE_INFO; } -static void delete_entry (menu_t *menu) { - int selected = menu->browser.selected; - +static void delete_entry (menu_t *menu, void *arg) { path_t *path = path_clone_push(menu->browser.directory, menu->browser.entry->name); if (menu->browser.entry->type == ENTRY_TYPE_DIR) { @@ -200,20 +214,13 @@ static void delete_entry (menu_t *menu) { path_free(path); - if (load_directory(menu)) { + if (reload_directory(menu)) { menu->browser.valid = false; menu_show_error(menu, "Couldn't refresh directory contents after delete operation"); - return; } - - menu->browser.selected = selected; - if (menu->browser.selected >= menu->browser.entries) { - menu->browser.selected = menu->browser.entries - 1; - } - menu->browser.entry = menu->browser.selected >= 0 ? &menu->browser.list[menu->browser.selected] : NULL; } -static void set_default_directory (menu_t *menu) { +static void set_default_directory (menu_t *menu, void *arg) { free(menu->settings.default_directory); menu->settings.default_directory = strdup(strip_sd_prefix(path_get(menu->browser.directory))); settings_save(&menu->settings); @@ -228,28 +235,17 @@ static component_context_menu_t entry_context_menu = { } }; -static void edit_settings (menu_t *menu) { - menu->next_mode = MENU_MODE_SETTINGS_EDITOR; -} - -static void show_system_info (menu_t *menu) { - menu->next_mode = MENU_MODE_SYSTEM_INFO; -} - -static void show_credits (menu_t *menu) { - menu->next_mode = MENU_MODE_CREDITS; -} - -static void edit_rtc (menu_t *menu) { - menu->next_mode = MENU_MODE_RTC; +static void set_menu_next_mode (menu_t *menu, void *arg) { + menu_mode_t next_mode = (menu_mode_t) (arg); + menu->next_mode = next_mode; } static component_context_menu_t settings_context_menu = { .list = { - { .text = "Edit settings", .action = edit_settings }, - { .text = "Show system info", .action = show_system_info }, - { .text = "Show credits", .action = show_credits }, - { .text = "Adjust RTC", .action = edit_rtc }, + { .text = "Edit settings", .action = set_menu_next_mode, .arg = (void *) (MENU_MODE_SETTINGS_EDITOR) }, + { .text = "Show system info", .action = set_menu_next_mode, .arg = (void *) (MENU_MODE_SYSTEM_INFO) }, + { .text = "Show credits", .action = set_menu_next_mode, .arg = (void *) (MENU_MODE_CREDITS) }, + { .text = "Adjust RTC", .action = set_menu_next_mode, .arg = (void *) (MENU_MODE_RTC) }, COMPONENT_CONTEXT_MENU_LIST_END, } }; @@ -386,18 +382,12 @@ void view_browser_init (menu_t *menu) { menu->browser.valid = true; } } + if (menu->browser.reload) { menu->browser.reload = false; - int selected = menu->browser.selected; - if (load_directory(menu)) { + if (reload_directory(menu)) { menu_show_error(menu, "Error while reloading current directory"); menu->browser.valid = false; - } else { - menu->browser.selected = selected; - if (menu->browser.selected >= menu->browser.entries) { - menu->browser.selected = menu->browser.entries - 1; - } - menu->browser.entry = menu->browser.selected >= 0 ? &menu->browser.list[menu->browser.selected] : NULL; } } } diff --git a/src/menu/views/credits.c b/src/menu/views/credits.c index a6e42a9a..dc1d0665 100644 --- a/src/menu/views/credits.c +++ b/src/menu/views/credits.c @@ -2,7 +2,11 @@ #ifndef MENU_VERSION -#define MENU_VERSION "0.0.0.6.ALPHA" +#define MENU_VERSION "Unknown" +#endif + +#ifndef BUILD_TIMESTAMP +#define BUILD_TIMESTAMP "Unknown" #endif @@ -28,7 +32,8 @@ static void draw (menu_t *menu, surface_t *d) { ALIGN_LEFT, VALIGN_TOP, "\n" "\n" - "Menu Revision: V%s\n" + "Menu version: %s\n" + "Build timestamp: %s\n" "\n" "Github:\n" " https://github.com/Polprzewodnikowy/N64FlashcartMenu\n" @@ -44,7 +49,8 @@ static void draw (menu_t *menu, surface_t *d) { " mini.c (BSD 2-Clause License)\n" " minimp3 (CC0 1.0 Universal)\n" " miniz (MIT License)", - MENU_VERSION + MENU_VERSION, + BUILD_TIMESTAMP ); component_actions_bar_text_draw( diff --git a/src/menu/views/load_disk.c b/src/menu/views/load_disk.c index 14d9d80b..0a8ebb62 100644 --- a/src/menu/views/load_disk.c +++ b/src/menu/views/load_disk.c @@ -126,9 +126,21 @@ static void load (menu_t *menu) { } menu->next_mode = MENU_MODE_BOOT; - menu->boot_params->device_type = load_rom ? BOOT_DEVICE_TYPE_ROM : BOOT_DEVICE_TYPE_64DD; - menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH; - menu->boot_params->detect_cic_seed = true; + + if (load_rom) { + menu->boot_params->device_type = BOOT_DEVICE_TYPE_ROM; + menu->boot_params->detect_cic_seed = rom_info_get_cic_seed(&menu->load.rom_info, &menu->boot_params->cic_seed); + switch (rom_info_get_tv_type(&menu->load.rom_info)) { + case ROM_TV_TYPE_PAL: menu->boot_params->tv_type = BOOT_TV_TYPE_PAL; break; + case ROM_TV_TYPE_NTSC: menu->boot_params->tv_type = BOOT_TV_TYPE_NTSC; break; + case ROM_TV_TYPE_MPAL: menu->boot_params->tv_type = BOOT_TV_TYPE_MPAL; break; + default: menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH; break; + } + } else { + menu->boot_params->device_type = BOOT_DEVICE_TYPE_64DD; + menu->boot_params->tv_type = BOOT_TV_TYPE_NTSC; + menu->boot_params->detect_cic_seed = true; + } } diff --git a/src/menu/views/load_rom.c b/src/menu/views/load_rom.c index 6beeaa0f..c5da6ed4 100644 --- a/src/menu/views/load_rom.c +++ b/src/menu/views/load_rom.c @@ -8,15 +8,15 @@ static bool load_pending; static component_boxart_t *boxart; -static char *convert_error_message (disk_err_t err) { +static char *convert_error_message (rom_err_t err) { switch (err) { - case ROM_ERR_IO: return "I/O error during loading ROM information"; + case ROM_ERR_IO: return "I/O error during loading/storing ROM information"; case ROM_ERR_NO_FILE: return "Couldn't open ROM file"; default: return "Unknown ROM info load error"; } } -static const char *format_rom_endianness (endianness_t endianness) { +static const char *format_rom_endianness (rom_endianness_t endianness) { switch (endianness) { case ENDIANNESS_BIG: return "Big (default)"; case ENDIANNESS_LITTLE: return "Little (unsupported)"; @@ -25,7 +25,7 @@ static const char *format_rom_endianness (endianness_t endianness) { } } -static const char *format_rom_media_type (category_type_t media_type) { +static const char *format_rom_media_type (rom_category_type_t media_type) { switch (media_type) { case N64_CART: return "Cartridge"; case N64_DISK: return "Disk"; @@ -36,7 +36,7 @@ static const char *format_rom_media_type (category_type_t media_type) { } } -static const char *format_rom_destination_market (destination_type_t market_type) { +static const char *format_rom_destination_market (rom_destination_type_t market_type) { // TODO: These are all assumptions and should be corrected if required. // From http://n64devkit.square7.ch/info/submission/pal/01-01.html switch (market_type) { @@ -87,7 +87,7 @@ static const char *format_rom_tv_type (rom_tv_type_t tv_type) { } } -static char *format_rom_expansion_pak_info (expansion_pak_t expansion_pak_info) { +static const char *format_rom_expansion_pak_info (rom_expansion_pak_t expansion_pak_info) { switch (expansion_pak_info) { case EXPANSION_PAK_REQUIRED: return "Required"; case EXPANSION_PAK_RECOMMENDED: return "Recommended"; @@ -97,72 +97,94 @@ static char *format_rom_expansion_pak_info (expansion_pak_t expansion_pak_info) } } -static const char *format_cic_type (cic_type_t cic_type) { +static const char *format_cic_type (rom_cic_type_t cic_type) { switch (cic_type) { - case CIC_5101: return "5101"; - case CIC_5167: return "5167"; - case CIC_6101: return "6101"; - case CIC_7102: return "7102"; - case CIC_6102_7101: return "6102 / 7101"; - case CIC_x103: return "6103 / 7103"; - case CIC_x105: return "6105 / 7105"; - case CIC_x106: return "6106 / 7106"; - case CIC_8301: return "8301"; - case CIC_8302: return "8302"; - case CIC_8303: return "8303"; - case CIC_8401: return "8401"; - case CIC_8501: return "8501"; + case ROM_CIC_TYPE_5101: return "5101"; + case ROM_CIC_TYPE_5167: return "5167"; + case ROM_CIC_TYPE_6101: return "6101"; + case ROM_CIC_TYPE_7102: return "7102"; + case ROM_CIC_TYPE_x102: return "6102 / 7101"; + case ROM_CIC_TYPE_x103: return "6103 / 7103"; + case ROM_CIC_TYPE_x105: return "6105 / 7105"; + case ROM_CIC_TYPE_x106: return "6106 / 7106"; + case ROM_CIC_TYPE_8301: return "8301"; + case ROM_CIC_TYPE_8302: return "8302"; + case ROM_CIC_TYPE_8303: return "8303"; + case ROM_CIC_TYPE_8401: return "8401"; + case ROM_CIC_TYPE_8501: return "8501"; default: return "Unknown"; } } -static void set_save_type (menu_t *menu, rom_save_type_t save_type) { - rom_info_override_save_type(menu->load.rom_path, &menu->load.rom_info, save_type); +static void set_cic_type (menu_t *menu, void *arg) { + rom_cic_type_t cic_type = (rom_cic_type_t) (arg); + rom_err_t err = rom_info_override_cic_type(menu->load.rom_path, &menu->load.rom_info, cic_type); + if (err != ROM_OK) { + menu_show_error(menu, convert_error_message(err)); + } menu->browser.reload = true; } -static void set_save_type_automatic (menu_t *menu) { set_save_type(menu, SAVE_TYPE_AUTOMATIC); } -static void set_save_type_none (menu_t *menu) { set_save_type(menu, SAVE_TYPE_NONE); } -static void set_save_type_eeprom_4kbit (menu_t *menu) { set_save_type(menu, SAVE_TYPE_EEPROM_4K); } -static void set_save_type_eeprom_16kbit (menu_t *menu) { set_save_type(menu, SAVE_TYPE_EEPROM_16K); } -static void set_save_type_sram_256kbit (menu_t *menu) { set_save_type(menu, SAVE_TYPE_SRAM); } -static void set_save_type_sram_768kbit (menu_t *menu) { set_save_type(menu, SAVE_TYPE_SRAM_BANKED); } -static void set_save_type_sram_1mbit (menu_t *menu) { set_save_type(menu, SAVE_TYPE_SRAM_128K); } -static void set_save_type_flash_ram_1mbit (menu_t *menu) { set_save_type(menu, SAVE_TYPE_FLASHRAM); } +static void set_save_type (menu_t *menu, void *arg) { + rom_save_type_t save_type = (rom_save_type_t) (arg); + rom_err_t err = rom_info_override_save_type(menu->load.rom_path, &menu->load.rom_info, save_type); + if (err != ROM_OK) { + menu_show_error(menu, convert_error_message(err)); + } + menu->browser.reload = true; +} -static component_context_menu_t set_save_type_context_menu = { .list = { - { .text = "Automatic", .action = set_save_type_automatic }, - { .text = "None", .action = set_save_type_none }, - { .text = "EEPROM 4kbit", .action = set_save_type_eeprom_4kbit }, - { .text = "EEPROM 16kbit", .action = set_save_type_eeprom_16kbit }, - { .text = "SRAM 256kbit", .action = set_save_type_sram_256kbit }, - { .text = "SRAM 768kbit", .action = set_save_type_sram_768kbit }, - { .text = "SRAM 1Mbit", .action = set_save_type_sram_1mbit }, - { .text = "FlashRAM 1Mbit", .action = set_save_type_flash_ram_1mbit }, +static void set_tv_type (menu_t *menu, void *arg) { + rom_tv_type_t tv_type = (rom_tv_type_t) (arg); + rom_err_t err = rom_info_override_tv_type(menu->load.rom_path, &menu->load.rom_info, tv_type); + if (err != ROM_OK) { + menu_show_error(menu, convert_error_message(err)); + } + menu->browser.reload = true; +} + +static component_context_menu_t set_cic_type_context_menu = { .list = { + {.text = "Automatic", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_AUTOMATIC) }, + {.text = "CIC-6101", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_6101) }, + {.text = "CIC-7102", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_7102) }, + {.text = "CIC-6102 / CIC-7101", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_x102) }, + {.text = "CIC-6103 / CIC-7103", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_x103) }, + {.text = "CIC-6105 / CIC-7105", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_x105) }, + {.text = "CIC-6106 / CIC-7106", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_x106) }, + {.text = "Aleck64 CIC-5101", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_5101) }, + {.text = "64DD ROM conversion CIC-5167", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_5167) }, + {.text = "NDDJ0 64DD IPL", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_8301) }, + {.text = "NDDJ1 64DD IPL", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_8302) }, + {.text = "NDDJ2 64DD IPL", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_8303) }, + {.text = "NDXJ0 64DD IPL", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_8401) }, + {.text = "NDDE0 64DD IPL", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_8501) }, COMPONENT_CONTEXT_MENU_LIST_END, }}; -static void set_tv_type (menu_t *menu, rom_tv_type_t tv_type) { - rom_info_override_tv_type(menu->load.rom_path, &menu->load.rom_info, tv_type); - menu->browser.reload = true; -} - -static void set_tv_type_automatic (menu_t *menu) { set_tv_type(menu, ROM_TV_TYPE_AUTOMATIC); } -static void set_tv_type_pal (menu_t *menu) { set_tv_type(menu, ROM_TV_TYPE_PAL); } -static void set_tv_type_ntsc (menu_t *menu) { set_tv_type(menu, ROM_TV_TYPE_NTSC); } -static void set_tv_type_mpal (menu_t *menu) { set_tv_type(menu, ROM_TV_TYPE_MPAL); } +static component_context_menu_t set_save_type_context_menu = { .list = { + { .text = "Automatic", .action = set_save_type, .arg = (void *) (SAVE_TYPE_AUTOMATIC) }, + { .text = "None", .action = set_save_type, .arg = (void *) (SAVE_TYPE_NONE) }, + { .text = "EEPROM 4kbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_EEPROM_4K) }, + { .text = "EEPROM 16kbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_EEPROM_16K) }, + { .text = "SRAM 256kbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_SRAM) }, + { .text = "SRAM 768kbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_SRAM_BANKED) }, + { .text = "SRAM 1Mbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_SRAM_128K) }, + { .text = "FlashRAM 1Mbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_FLASHRAM) }, + COMPONENT_CONTEXT_MENU_LIST_END, +}}; static component_context_menu_t set_tv_type_context_menu = { .list = { - { .text = "Automatic", .action = set_tv_type_automatic }, - { .text = "PAL", .action = set_tv_type_pal }, - { .text = "NTSC", .action = set_tv_type_ntsc }, - { .text = "MPAL", .action = set_tv_type_mpal }, + { .text = "Automatic", .action = set_tv_type, .arg = (void *) (ROM_TV_TYPE_AUTOMATIC) }, + { .text = "PAL", .action = set_tv_type, .arg = (void *) (ROM_TV_TYPE_PAL) }, + { .text = "NTSC", .action = set_tv_type, .arg = (void *) (ROM_TV_TYPE_NTSC) }, + { .text = "MPAL", .action = set_tv_type, .arg = (void *) (ROM_TV_TYPE_MPAL) }, COMPONENT_CONTEXT_MENU_LIST_END, }}; static component_context_menu_t options_context_menu = { .list = { - { .text = "Set save type", .submenu = &set_save_type_context_menu }, - { .text = "Set TV type", .submenu = &set_tv_type_context_menu }, + { .text = "Set CIC Type", .submenu = &set_cic_type_context_menu }, + { .text = "Set Save Type", .submenu = &set_save_type_context_menu }, + { .text = "Set TV Type", .submenu = &set_tv_type_context_menu }, COMPONENT_CONTEXT_MENU_LIST_END, }}; @@ -230,7 +252,7 @@ static void draw (menu_t *menu, surface_t *d) { format_rom_save_type(rom_info_get_save_type(&menu->load.rom_info)), format_rom_tv_type(rom_info_get_tv_type(&menu->load.rom_info)), format_rom_expansion_pak_info(menu->load.rom_info.features.expansion_pak), - format_cic_type(menu->load.rom_info.cic_type), + format_cic_type(rom_info_get_cic_type(&menu->load.rom_info)), menu->load.rom_info.boot_address, (menu->load.rom_info.libultra.version / 10.0f), menu->load.rom_info.libultra.revision, menu->load.rom_info.clock_rate @@ -281,8 +303,8 @@ static void load (menu_t *menu) { menu->next_mode = MENU_MODE_BOOT; menu->boot_params->device_type = BOOT_DEVICE_TYPE_ROM; - menu->boot_params->detect_cic_seed = true; - switch(rom_info_get_tv_type(&menu->load.rom_info)) { + menu->boot_params->detect_cic_seed = rom_info_get_cic_seed(&menu->load.rom_info, &menu->boot_params->cic_seed); + switch (rom_info_get_tv_type(&menu->load.rom_info)) { case ROM_TV_TYPE_PAL: menu->boot_params->tv_type = BOOT_TV_TYPE_PAL; break; case ROM_TV_TYPE_NTSC: menu->boot_params->tv_type = BOOT_TV_TYPE_NTSC; break; case ROM_TV_TYPE_MPAL: menu->boot_params->tv_type = BOOT_TV_TYPE_MPAL; break; @@ -296,18 +318,20 @@ static void deinit (void) { void view_load_rom_init (menu_t *menu) { + load_pending = false; + if (menu->load.rom_path) { path_free(menu->load.rom_path); - menu->load.rom_path = NULL; } - load_pending = false; - menu->load.rom_path = path_clone_push(menu->browser.directory, menu->browser.entry->name); rom_err_t err = rom_info_load(menu->load.rom_path, &menu->load.rom_info); if (err != ROM_OK) { + path_free(menu->load.rom_path); + menu->load.rom_path = NULL; menu_show_error(menu, convert_error_message(err)); + return; } boxart = component_boxart_init(menu->load.rom_info.game_code); diff --git a/src/menu/views/settings_editor.c b/src/menu/views/settings_editor.c index c624ba1a..7158a0b3 100644 --- a/src/menu/views/settings_editor.c +++ b/src/menu/views/settings_editor.c @@ -1,13 +1,14 @@ #include "views.h" -static char *format_boolean_type (int state) { + +static const char *format_switch (bool state) { switch (state) { - case 0: return "Off"; - case 1: return "On"; - default: return "Unknown"; + case true: return "On"; + case false: return "Off"; } } + static void process (menu_t *menu) { if (menu->actions.back) { menu->next_mode = MENU_MODE_BROWSER; @@ -40,16 +41,15 @@ static void draw (menu_t *menu, surface_t *d) { "bgm_enabled: %s\n" "sound_enabled: %s\n" "rumble_enabled: %s\n", - format_boolean_type(menu->settings.pal60_enabled), - format_boolean_type(menu->settings.hidden_files_enabled), + format_switch(menu->settings.pal60_enabled), + format_switch(menu->settings.hidden_files_enabled), menu->settings.default_directory, - format_boolean_type(menu->settings.use_saves_folder), - format_boolean_type(menu->settings.bgm_enabled), - format_boolean_type(menu->settings.sound_enabled), - format_boolean_type(menu->settings.rumble_enabled) + format_switch(menu->settings.use_saves_folder), + format_switch(menu->settings.bgm_enabled), + format_switch(menu->settings.sound_enabled), + format_switch(menu->settings.rumble_enabled) ); - component_actions_bar_text_draw( ALIGN_LEFT, VALIGN_TOP, "\n" @@ -66,5 +66,6 @@ void view_settings_init (menu_t *menu) { void view_settings_display (menu_t *menu, surface_t *display) { process(menu); + draw(menu, display); } diff --git a/src/menu/views/system_info.c b/src/menu/views/system_info.c index a0ebfcad..02b78f72 100644 --- a/src/menu/views/system_info.c +++ b/src/menu/views/system_info.c @@ -7,27 +7,25 @@ static int joypad[4]; static int accessory[4]; -static char *format_accessory (int joypad) { +static const char *format_accessory (int joypad) { switch (accessory[joypad]) { - case JOYPAD_ACCESSORY_TYPE_RUMBLE_PAK: - return "[Rumble Pak is inserted]"; - case JOYPAD_ACCESSORY_TYPE_CONTROLLER_PAK: - return "[Controller Pak is inserted]"; - case JOYPAD_ACCESSORY_TYPE_TRANSFER_PAK: - return "[Transfer Pak is inserted]"; - case JOYPAD_ACCESSORY_TYPE_BIO_SENSOR: - return "[BIO Sensor is inserted]"; - case JOYPAD_ACCESSORY_TYPE_SNAP_STATION: - return "[Snap Station is inserted]"; - case JOYPAD_ACCESSORY_TYPE_NONE: - return ""; - default: - return "[unknown accessory inserted]"; + case JOYPAD_ACCESSORY_TYPE_RUMBLE_PAK: return "[Rumble Pak is inserted]"; + case JOYPAD_ACCESSORY_TYPE_CONTROLLER_PAK: return "[Controller Pak is inserted]"; + case JOYPAD_ACCESSORY_TYPE_TRANSFER_PAK: return "[Transfer Pak is inserted]"; + case JOYPAD_ACCESSORY_TYPE_BIO_SENSOR: return "[BIO Sensor is inserted]"; + case JOYPAD_ACCESSORY_TYPE_SNAP_STATION: return "[Snap Station is inserted]"; + case JOYPAD_ACCESSORY_TYPE_NONE: return ""; + default: return "[unknown accessory inserted]"; } } static void process (menu_t *menu) { + JOYPAD_PORT_FOREACH (port) { + joypad[port] = (joypad_get_style(port) != JOYPAD_STYLE_NONE); + accessory[port] = joypad_get_accessory_type(port); + } + if (menu->actions.back) { menu->next_mode = MENU_MODE_BROWSER; } @@ -53,10 +51,10 @@ static void draw (menu_t *menu, surface_t *d) { "\n" "Expansion PAK is %sinserted\n" "\n" - "JoyPad 1 is %sconnected %s\n" - "JoyPad 2 is %sconnected %s\n" - "JoyPad 3 is %sconnected %s\n" - "JoyPad 4 is %sconnected %s\n", + "Joypad 1 is %sconnected %s\n" + "Joypad 2 is %sconnected %s\n" + "Joypad 3 is %sconnected %s\n" + "Joypad 4 is %sconnected %s\n", menu->current_time >= 0 ? ctime(&menu->current_time) : "Unknown\n", is_memory_expanded() ? "" : "not ", (joypad[0]) ? "" : "not ", format_accessory(0), @@ -76,10 +74,7 @@ static void draw (menu_t *menu, surface_t *d) { void view_system_info_init (menu_t *menu) { - JOYPAD_PORT_FOREACH (port) { - joypad[port] = (joypad_get_style(port) != JOYPAD_STYLE_NONE); - accessory[port] = joypad_get_accessory_type(port); - } + // Nothing to initialize (yet) } void view_system_info_display (menu_t *menu, surface_t *display) { diff --git a/src/menu/views/views.h b/src/menu/views/views.h index 2b7e0660..4d956b0d 100644 --- a/src/menu/views/views.h +++ b/src/menu/views/views.h @@ -23,12 +23,12 @@ void view_startup_display (menu_t *menu, surface_t *display); void view_browser_init (menu_t *menu); void view_browser_display (menu_t *menu, surface_t *display); -void view_system_info_init (menu_t *menu); -void view_system_info_display (menu_t *menu, surface_t *display); - void view_file_info_init (menu_t *menu); void view_file_info_display (menu_t *menu, surface_t *display); +void view_system_info_init (menu_t *menu); +void view_system_info_display (menu_t *menu, surface_t *display); + void view_image_viewer_init (menu_t *menu); void view_image_viewer_display (menu_t *menu, surface_t *display); @@ -38,18 +38,18 @@ void view_music_player_display (menu_t *menu, surface_t *display); void view_credits_init (menu_t *menu); void view_credits_display (menu_t *menu, surface_t *display); -void view_load_rom_init (menu_t *menu); -void view_load_rom_display (menu_t *menu, surface_t *display); - -void view_load_disk_init (menu_t *menu); -void view_load_disk_display (menu_t *menu, surface_t *display); - void view_settings_init (menu_t *menu); void view_settings_display (menu_t *menu, surface_t *display); void view_rtc_init (menu_t *menu); void view_rtc_display (menu_t *menu, surface_t *display); +void view_load_rom_init (menu_t *menu); +void view_load_rom_display (menu_t *menu, surface_t *display); + +void view_load_disk_init (menu_t *menu); +void view_load_disk_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); From d85f24a0d8510942a5275405008e45ac8d5d7728 Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Wed, 17 Jan 2024 14:47:31 +0000 Subject: [PATCH 07/20] Add RTC feature for flashcart (#58) ## Description Adds a feature to the flashcart to determine whether RTC is supported. This is at minimum required for correct handling of what to show on the main menu. It also will help with a future context menu to set the RTC time. ## Motivation and Context Some flashcarts (ED64 X5, V2.5, V2.0, V1.0 & ED64P ) do not support it. ## How Has This Been Tested? ## Screenshots ## Types of changes - [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: - [ ] My code follows the code style of this project. - [ ] My change requires a change to the documentation. - [ ] I have updated the documentation accordingly. - [ ] I have added tests to cover my changes. - [ ] All new and existing tests passed. Signed-off-by: GITHUB_USER --- src/flashcart/64drive/64drive.c | 1 + src/flashcart/flashcart.h | 1 + src/flashcart/sc64/sc64.c | 1 + 3 files changed, 3 insertions(+) diff --git a/src/flashcart/64drive/64drive.c b/src/flashcart/64drive/64drive.c index b2ed3b36..bf0094c0 100644 --- a/src/flashcart/64drive/64drive.c +++ b/src/flashcart/64drive/64drive.c @@ -73,6 +73,7 @@ static flashcart_err_t d64_deinit (void) { static bool d64_has_feature (flashcart_features_t feature) { switch (feature) { case FLASHCART_FEATURE_64DD: return false; + case FLASHCART_FEATURE_RTC: return true; default: return false; } } diff --git a/src/flashcart/flashcart.h b/src/flashcart/flashcart.h index 0e97e807..0adb56e3 100644 --- a/src/flashcart/flashcart.h +++ b/src/flashcart/flashcart.h @@ -27,6 +27,7 @@ typedef enum { /** @brief List of optional supported flashcart features */ typedef enum { FLASHCART_FEATURE_64DD, + FLASHCART_FEATURE_RTC, } flashcart_features_t; /** @brief Flashcart save type enumeration */ diff --git a/src/flashcart/sc64/sc64.c b/src/flashcart/sc64/sc64.c index 58f4c450..895b6d5d 100644 --- a/src/flashcart/sc64/sc64.c +++ b/src/flashcart/sc64/sc64.c @@ -260,6 +260,7 @@ static flashcart_err_t sc64_deinit (void) { static bool sc64_has_feature (flashcart_features_t feature) { switch (feature) { case FLASHCART_FEATURE_64DD: return true; + case FLASHCART_FEATURE_RTC: return true; default: return false; } } From cc46a3b9fb5ecc8223e8065690d2e8796a0fbadd Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Wed, 17 Jan 2024 14:51:10 +0000 Subject: [PATCH 08/20] Add USB feature for flashcart (#85) ## Description Adds a feature to the flashcart to determine whether USB is supported. ## Motivation and Context Some flashcarts (ED64 X5, V2.5, V2.0, V1.0 & ED64P ) do not support it. ## How Has This Been Tested? ## Screenshots ## Types of changes - [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: - [ ] My code follows the code style of this project. - [ ] My change requires a change to the documentation. - [ ] I have updated the documentation accordingly. - [ ] I have added tests to cover my changes. - [ ] All new and existing tests passed. Signed-off-by: GITHUB_USER Co-authored-by: Mateusz Faderewski --- src/flashcart/64drive/64drive.c | 1 + src/flashcart/flashcart.h | 1 + src/flashcart/sc64/sc64.c | 1 + 3 files changed, 3 insertions(+) diff --git a/src/flashcart/64drive/64drive.c b/src/flashcart/64drive/64drive.c index bf0094c0..9b0b8b08 100644 --- a/src/flashcart/64drive/64drive.c +++ b/src/flashcart/64drive/64drive.c @@ -74,6 +74,7 @@ static bool d64_has_feature (flashcart_features_t feature) { switch (feature) { case FLASHCART_FEATURE_64DD: return false; case FLASHCART_FEATURE_RTC: return true; + case FLASHCART_FEATURE_USB: return true; default: return false; } } diff --git a/src/flashcart/flashcart.h b/src/flashcart/flashcart.h index 0adb56e3..001516bb 100644 --- a/src/flashcart/flashcart.h +++ b/src/flashcart/flashcart.h @@ -28,6 +28,7 @@ typedef enum { typedef enum { FLASHCART_FEATURE_64DD, FLASHCART_FEATURE_RTC, + FLASHCART_FEATURE_USB, } flashcart_features_t; /** @brief Flashcart save type enumeration */ diff --git a/src/flashcart/sc64/sc64.c b/src/flashcart/sc64/sc64.c index 895b6d5d..b7febf16 100644 --- a/src/flashcart/sc64/sc64.c +++ b/src/flashcart/sc64/sc64.c @@ -261,6 +261,7 @@ static bool sc64_has_feature (flashcart_features_t feature) { switch (feature) { case FLASHCART_FEATURE_64DD: return true; case FLASHCART_FEATURE_RTC: return true; + case FLASHCART_FEATURE_USB: return true; default: return false; } } From a2e50a9c82b41741beb304c3b72c0b6729c5b340 Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Wed, 17 Jan 2024 14:54:30 +0000 Subject: [PATCH 09/20] Add flashcart submenu view (#86) ## Description Adds a view to show the flashcart information ## Motivation and Context Will be useful to show its features etc. ## How Has This Been Tested? ## Screenshots ## Types of changes - [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: - [ ] My code follows the code style of this project. - [ ] My change requires a change to the documentation. - [ ] I have updated the documentation accordingly. - [ ] I have added tests to cover my changes. - [ ] All new and existing tests passed. Signed-off-by: GITHUB_USER Co-authored-by: Mateusz Faderewski --- Makefile | 1 + src/menu/menu.c | 1 + src/menu/menu_state.h | 1 + src/menu/views/browser.c | 1 + src/menu/views/flashcart_info.c | 46 +++++++++++++++++++++++++++++++++ src/menu/views/views.h | 3 +++ 6 files changed, 53 insertions(+) create mode 100644 src/menu/views/flashcart_info.c diff --git a/Makefile b/Makefile index db6493d9..f9c10b67 100644 --- a/Makefile +++ b/Makefile @@ -64,6 +64,7 @@ SRCS = \ menu/views/system_info.c \ menu/views/settings_editor.c \ menu/views/rtc.c \ + menu/views/flashcart_info.c \ utils/fs.c FONTS = \ diff --git a/src/menu/menu.c b/src/menu/menu.c index 06e9ef2d..53bd497f 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -152,6 +152,7 @@ static view_t menu_views[] = { { MENU_MODE_CREDITS, view_credits_init, view_credits_display }, { MENU_MODE_SETTINGS_EDITOR, view_settings_init, view_settings_display }, { MENU_MODE_RTC, view_rtc_init, view_rtc_display }, + { MENU_MODE_FLASHCART, view_flashcart_info_init, view_flashcart_info_display }, { MENU_MODE_LOAD_ROM, view_load_rom_init, view_load_rom_display }, { MENU_MODE_LOAD_DISK, view_load_disk_init, view_load_disk_display }, { MENU_MODE_LOAD_EMULATOR, view_load_emulator_init, view_load_emulator_display }, diff --git a/src/menu/menu_state.h b/src/menu/menu_state.h index 9fcee137..071ce99e 100644 --- a/src/menu/menu_state.h +++ b/src/menu/menu_state.h @@ -33,6 +33,7 @@ typedef enum { MENU_MODE_CREDITS, MENU_MODE_SETTINGS_EDITOR, MENU_MODE_RTC, + MENU_MODE_FLASHCART, MENU_MODE_LOAD_ROM, MENU_MODE_LOAD_DISK, MENU_MODE_LOAD_EMULATOR, diff --git a/src/menu/views/browser.c b/src/menu/views/browser.c index a7517946..f6c2c9de 100644 --- a/src/menu/views/browser.c +++ b/src/menu/views/browser.c @@ -246,6 +246,7 @@ static component_context_menu_t settings_context_menu = { { .text = "Show system info", .action = set_menu_next_mode, .arg = (void *) (MENU_MODE_SYSTEM_INFO) }, { .text = "Show credits", .action = set_menu_next_mode, .arg = (void *) (MENU_MODE_CREDITS) }, { .text = "Adjust RTC", .action = set_menu_next_mode, .arg = (void *) (MENU_MODE_RTC) }, + { .text = "Show cart info", .action = set_menu_next_mode, .arg = (void *) (MENU_MODE_FLASHCART) }, COMPONENT_CONTEXT_MENU_LIST_END, } }; diff --git a/src/menu/views/flashcart_info.c b/src/menu/views/flashcart_info.c new file mode 100644 index 00000000..084cc073 --- /dev/null +++ b/src/menu/views/flashcart_info.c @@ -0,0 +1,46 @@ +#include "views.h" + + +static void process (menu_t *menu) { + 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(); + + component_layout_draw(); + + component_main_text_draw( + ALIGN_CENTER, VALIGN_TOP, + "FLASHCART INFORMATION\n" + ); + + component_main_text_draw( + ALIGN_LEFT, VALIGN_TOP, + "\n" + "\n" + ); + + + component_actions_bar_text_draw( + ALIGN_LEFT, VALIGN_TOP, + "\n" + "B: Back" + ); + + rdpq_detach_show(); +} + + +void view_flashcart_info_init (menu_t *menu) { + // Nothing to initialize (yet) +} + +void view_flashcart_info_display (menu_t *menu, surface_t *display) { + process(menu); + draw(menu, display); +} diff --git a/src/menu/views/views.h b/src/menu/views/views.h index 4d956b0d..74342dcc 100644 --- a/src/menu/views/views.h +++ b/src/menu/views/views.h @@ -44,6 +44,9 @@ void view_settings_display (menu_t *menu, surface_t *display); void view_rtc_init (menu_t *menu); void view_rtc_display (menu_t *menu, surface_t *display); +void view_flashcart_info_init (menu_t *menu); +void view_flashcart_info_display (menu_t *menu, surface_t *display); + void view_load_rom_init (menu_t *menu); void view_load_rom_display (menu_t *menu, surface_t *display); From 14e45c023020d2ff0bf93db2b818d33d935f41f7 Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Wed, 17 Jan 2024 16:03:00 +0000 Subject: [PATCH 10/20] Add Text viewer (#87) ## Description Adds a basic text viewer. It does not (yet) support full file content. superseeds #21 ## Motivation and Context There were too many changes to merge from the original PR. ## How Has This Been Tested? On an SC64 via devcontainer. ## Screenshots ## Types of changes - [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: - [ ] My code follows the code style of this project. - [ ] My change requires a change to the documentation. - [ ] I have updated the documentation accordingly. - [ ] I have added tests to cover my changes. - [ ] All new and existing tests passed. Signed-off-by: GITHUB_USER --------- Co-authored-by: Mateusz Faderewski Co-authored-by: Mateusz Faderewski --- Makefile | 1 + src/menu/menu.c | 1 + src/menu/menu_state.h | 2 + src/menu/views/browser.c | 11 +++++ src/menu/views/text_viewer.c | 78 ++++++++++++++++++++++++++++++++++++ src/menu/views/views.h | 3 ++ 6 files changed, 96 insertions(+) create mode 100644 src/menu/views/text_viewer.c diff --git a/Makefile b/Makefile index f9c10b67..7c0ec049 100644 --- a/Makefile +++ b/Makefile @@ -56,6 +56,7 @@ SRCS = \ menu/views/fault.c \ menu/views/file_info.c \ menu/views/image_viewer.c \ + menu/views/text_viewer.c \ menu/views/load_disk.c \ menu/views/load_emulator.c \ menu/views/load_rom.c \ diff --git a/src/menu/menu.c b/src/menu/menu.c index 53bd497f..1b8832cb 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -148,6 +148,7 @@ static view_t menu_views[] = { { MENU_MODE_FILE_INFO, view_file_info_init, view_file_info_display }, { MENU_MODE_SYSTEM_INFO, view_system_info_init, view_system_info_display }, { MENU_MODE_IMAGE_VIEWER, view_image_viewer_init, view_image_viewer_display }, + { MENU_MODE_TEXT_VIEWER, view_text_viewer_init, view_text_viewer_display }, { MENU_MODE_MUSIC_PLAYER, view_music_player_init, view_music_player_display }, { MENU_MODE_CREDITS, view_credits_init, view_credits_display }, { MENU_MODE_SETTINGS_EDITOR, view_settings_init, view_settings_display }, diff --git a/src/menu/menu_state.h b/src/menu/menu_state.h index 071ce99e..f0d93492 100644 --- a/src/menu/menu_state.h +++ b/src/menu/menu_state.h @@ -29,6 +29,7 @@ typedef enum { MENU_MODE_FILE_INFO, MENU_MODE_SYSTEM_INFO, MENU_MODE_IMAGE_VIEWER, + MENU_MODE_TEXT_VIEWER, MENU_MODE_MUSIC_PLAYER, MENU_MODE_CREDITS, MENU_MODE_SETTINGS_EDITOR, @@ -50,6 +51,7 @@ typedef enum { ENTRY_TYPE_EMULATOR, ENTRY_TYPE_SAVE, ENTRY_TYPE_IMAGE, + ENTRY_TYPE_TEXT, ENTRY_TYPE_MUSIC, ENTRY_TYPE_OTHER, } entry_type_t; diff --git a/src/menu/views/browser.c b/src/menu/views/browser.c index f6c2c9de..01cdb41b 100644 --- a/src/menu/views/browser.c +++ b/src/menu/views/browser.c @@ -14,6 +14,7 @@ static const char *disk_extensions[] = { "ndd", NULL }; static const char *emulator_extensions[] = { "nes", "sfc", "smc", "gb", "gbc", "sms", "gg", "sg", 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 *text_extensions[] = { "txt", "ini", "yml", "yaml", NULL }; static const char *music_extensions[] = { "mp3", NULL }; @@ -46,6 +47,10 @@ static int compare_entry (const void *pa, const void *pb) { return -1; } else if (b->type == ENTRY_TYPE_IMAGE) { return 1; + } else if (a->type == ENTRY_TYPE_TEXT) { + return -1; + } else if (b->type == ENTRY_TYPE_TEXT) { + return 1; } else if (a->type == ENTRY_TYPE_MUSIC) { return -1; } else if (b->type == ENTRY_TYPE_MUSIC) { @@ -110,6 +115,8 @@ static bool load_directory (menu_t *menu) { entry->type = ENTRY_TYPE_SAVE; } else if (file_has_extensions(info.fname, image_extensions)) { entry->type = ENTRY_TYPE_IMAGE; + } else if (file_has_extensions(info.fname, text_extensions)) { + entry->type = ENTRY_TYPE_TEXT; } else if (file_has_extensions(info.fname, music_extensions)) { entry->type = ENTRY_TYPE_MUSIC; } else { @@ -297,6 +304,9 @@ static void process (menu_t *menu) { case ENTRY_TYPE_IMAGE: menu->next_mode = MENU_MODE_IMAGE_VIEWER; break; + case ENTRY_TYPE_TEXT: + menu->next_mode = MENU_MODE_TEXT_VIEWER; + break; case ENTRY_TYPE_MUSIC: menu->next_mode = MENU_MODE_MUSIC_PLAYER; break; @@ -334,6 +344,7 @@ static void draw (menu_t *menu, surface_t *d) { case ENTRY_TYPE_ROM: action = "A: Load"; break; case ENTRY_TYPE_DISK: action = "A: Load"; break; case ENTRY_TYPE_IMAGE: action = "A: Show"; break; + case ENTRY_TYPE_TEXT: action = "A: View"; break; case ENTRY_TYPE_MUSIC: action = "A: Play"; break; default: action = "A: Info"; break; } diff --git a/src/menu/views/text_viewer.c b/src/menu/views/text_viewer.c new file mode 100644 index 00000000..adef2df9 --- /dev/null +++ b/src/menu/views/text_viewer.c @@ -0,0 +1,78 @@ +#include + +#include "utils/fs.h" +#include "views.h" + +static char *file_content; + +static void process (menu_t *menu) { + 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(); + + component_layout_draw(); + + component_main_text_draw( + ALIGN_CENTER, VALIGN_TOP, + "TEXT VIEWER\n" + "\n" + ); + + component_main_text_draw( + ALIGN_LEFT, VALIGN_TOP, + "\n" + "\n" + "%s\n", + file_content + ); + + + component_actions_bar_text_draw( + ALIGN_LEFT, VALIGN_TOP, + "\n" + "B: Back" + ); + + rdpq_detach_show(); +} + + +void view_text_viewer_init (menu_t *menu) { + path_t *path = path_clone_push(menu->browser.directory, menu->browser.entry->name); + + uint32_t file_size = file_get_size(path_get(path)); + + if (file_size > 1024) { // FIXME: this is just a placeholder until scrolling is implemented. + file_size = 1024; // For the moment, we just set it to that, since any more would be a waste. + } + + file_content = calloc(file_size, 1); + + // read file content + FIL fil; + UINT br; + + if (f_open(&fil, strip_sd_prefix(path_get(path)), FA_READ) != FR_OK) { + debugf("Error loading file\n"); + } + if (f_read(&fil, file_content, file_size, &br) != FR_OK) { + f_close(&fil); + debugf("Error loading file content\n"); + } + if (f_close(&fil) != FR_OK) { + debugf("Error closing file\n"); + } + + path_free(path); +} + +void view_text_viewer_display (menu_t *menu, surface_t *display) { + process(menu); + draw(menu, display); +} diff --git a/src/menu/views/views.h b/src/menu/views/views.h index 74342dcc..8da900dc 100644 --- a/src/menu/views/views.h +++ b/src/menu/views/views.h @@ -32,6 +32,9 @@ void view_system_info_display (menu_t *menu, surface_t *display); void view_image_viewer_init (menu_t *menu); void view_image_viewer_display (menu_t *menu, surface_t *display); +void view_text_viewer_init (menu_t *menu); +void view_text_viewer_display (menu_t *menu, surface_t *display); + void view_music_player_init (menu_t *menu); void view_music_player_display (menu_t *menu, surface_t *display); From b283f48de26bef00b3b12466c15257416af892fb Mon Sep 17 00:00:00 2001 From: Mateusz Faderewski Date: Fri, 19 Jan 2024 04:21:05 +0100 Subject: [PATCH 11/20] Added color for text files --- src/menu/components/file_list.c | 18 +++++++++++------- src/menu/fonts.c | 9 +++++---- src/menu/fonts.h | 9 +++++---- src/menu/menu_state.h | 2 +- src/menu/views/browser.c | 12 ++++++------ 5 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/menu/components/file_list.c b/src/menu/components/file_list.c index 15a438fc..1de3cd19 100644 --- a/src/menu/components/file_list.c +++ b/src/menu/components/file_list.c @@ -37,7 +37,7 @@ void component_file_list_draw (entry_t *list, int entries, int selected) { component_main_text_draw( ALIGN_LEFT, VALIGN_TOP, "^%02X** empty directory **", - STL_UNKNOWN + STL_GRAY ); } else { rdpq_paragraph_t *file_list_layout; @@ -80,12 +80,16 @@ void component_file_list_draw (entry_t *list, int entries, int selected) { menu_font_style_t style; switch (entry->type) { - case ENTRY_TYPE_DIR: style = STL_DIRECTORY; break; - case ENTRY_TYPE_SAVE: style = STL_SAVE; break; - case ENTRY_TYPE_OTHER: style = STL_UNKNOWN; break; - case ENTRY_TYPE_IMAGE: style = STL_MEDIA; break; - case ENTRY_TYPE_MUSIC: style = STL_MEDIA; break; - default: style = STL_DEFAULT; break; + case ENTRY_TYPE_DIR: style = STL_YELLOW; break; + case ENTRY_TYPE_ROM: style = STL_DEFAULT; break; + case ENTRY_TYPE_DISK: style = STL_DEFAULT; break; + case ENTRY_TYPE_EMULATOR: style = STL_DEFAULT; break; + case ENTRY_TYPE_SAVE: style = STL_GREEN; break; + case ENTRY_TYPE_IMAGE: style = STL_BLUE; break; + case ENTRY_TYPE_MUSIC: style = STL_BLUE; break; + case ENTRY_TYPE_TEXT: style = STL_ORANGE; break; + case ENTRY_TYPE_OTHER: style = STL_GRAY; break; + default: style = STL_GRAY; break; } rdpq_paragraph_builder_style(style); diff --git a/src/menu/fonts.c b/src/menu/fonts.c index 27eccd09..e8eccc95 100644 --- a/src/menu/fonts.c +++ b/src/menu/fonts.c @@ -7,10 +7,11 @@ static void load_default_font (void) { rdpq_font_t *default_font = rdpq_font_load("rom:/FiraMonoBold.font64"); rdpq_font_style(default_font, STL_DEFAULT, &((rdpq_fontstyle_t) { .color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF) })); - rdpq_font_style(default_font, STL_DIRECTORY, &((rdpq_fontstyle_t) { .color = RGBA32(0xFF, 0xFF, 0x70, 0xFF) })); - rdpq_font_style(default_font, STL_SAVE, &((rdpq_fontstyle_t) { .color = RGBA32(0x70, 0xFF, 0x70, 0xFF) })); - rdpq_font_style(default_font, STL_MEDIA, &((rdpq_fontstyle_t) { .color = RGBA32(0x70, 0xBC, 0xFF, 0xFF) })); - rdpq_font_style(default_font, STL_UNKNOWN, &((rdpq_fontstyle_t) { .color = RGBA32(0xA0, 0xA0, 0xA0, 0xFF) })); + rdpq_font_style(default_font, STL_GREEN, &((rdpq_fontstyle_t) { .color = RGBA32(0x70, 0xFF, 0x70, 0xFF) })); + rdpq_font_style(default_font, STL_BLUE, &((rdpq_fontstyle_t) { .color = RGBA32(0x70, 0xBC, 0xFF, 0xFF) })); + rdpq_font_style(default_font, STL_YELLOW, &((rdpq_fontstyle_t) { .color = RGBA32(0xFF, 0xFF, 0x70, 0xFF) })); + rdpq_font_style(default_font, STL_ORANGE, &((rdpq_fontstyle_t) { .color = RGBA32(0xFF, 0x99, 0x00, 0xFF) })); + rdpq_font_style(default_font, STL_GRAY, &((rdpq_fontstyle_t) { .color = RGBA32(0xA0, 0xA0, 0xA0, 0xFF) })); rdpq_text_register_font(FNT_DEFAULT, default_font); } diff --git a/src/menu/fonts.h b/src/menu/fonts.h index 2e006efc..2ddade59 100644 --- a/src/menu/fonts.h +++ b/src/menu/fonts.h @@ -15,10 +15,11 @@ typedef enum { /** @brief Font style enumeration. */ typedef enum { STL_DEFAULT = 0, - STL_DIRECTORY, - STL_SAVE, - STL_MEDIA, - STL_UNKNOWN, + STL_GREEN, + STL_BLUE, + STL_YELLOW, + STL_ORANGE, + STL_GRAY, } menu_font_style_t; diff --git a/src/menu/menu_state.h b/src/menu/menu_state.h index f0d93492..265443fe 100644 --- a/src/menu/menu_state.h +++ b/src/menu/menu_state.h @@ -51,8 +51,8 @@ typedef enum { ENTRY_TYPE_EMULATOR, ENTRY_TYPE_SAVE, ENTRY_TYPE_IMAGE, - ENTRY_TYPE_TEXT, ENTRY_TYPE_MUSIC, + ENTRY_TYPE_TEXT, ENTRY_TYPE_OTHER, } entry_type_t; diff --git a/src/menu/views/browser.c b/src/menu/views/browser.c index 01cdb41b..00061d67 100644 --- a/src/menu/views/browser.c +++ b/src/menu/views/browser.c @@ -47,14 +47,14 @@ static int compare_entry (const void *pa, const void *pb) { return -1; } else if (b->type == ENTRY_TYPE_IMAGE) { return 1; - } else if (a->type == ENTRY_TYPE_TEXT) { - return -1; - } else if (b->type == ENTRY_TYPE_TEXT) { - return 1; } else if (a->type == ENTRY_TYPE_MUSIC) { return -1; } else if (b->type == ENTRY_TYPE_MUSIC) { return 1; + } else if (a->type == ENTRY_TYPE_TEXT) { + return -1; + } else if (b->type == ENTRY_TYPE_TEXT) { + return 1; } } @@ -355,14 +355,14 @@ static void draw (menu_t *menu, surface_t *d) { "%s\n" "^%02XB: Back^00", menu->browser.entries == 0 ? "" : action, - path_is_root(menu->browser.directory) ? STL_UNKNOWN : STL_DEFAULT + path_is_root(menu->browser.directory) ? STL_GRAY : STL_DEFAULT ); component_actions_bar_text_draw( ALIGN_RIGHT, VALIGN_TOP, "Start: Settings\n" "^%02XR: Options^00", - menu->browser.entries == 0 ? STL_UNKNOWN : STL_DEFAULT + menu->browser.entries == 0 ? STL_GRAY : STL_DEFAULT ); if (menu->current_time >= 0) { From 2e962bbf22ceb24bca69688f19c2305507d162e2 Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Sat, 3 Feb 2024 00:43:43 +0000 Subject: [PATCH 12/20] Improve save documentation --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 0283dfe3..2c8f8498 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,13 @@ An open source menu for N64 flashcarts. ## Getting started Using your PC, insert the SD card and ensure it is formatted for compatibility (We recommend FAT32 in most instances). +### Save files +By default, all save files (whether `FlashRam`, `SRAM` or `EEPROM`) use the `.sav` extension and match the filenane to the ROM. +Each save file can be found in the `/saves` folder located in the same directory as the ROM and shares the same file name, apart from the extension. +If transfering a file from a different flashcart such as the ED64, it will be necessary to change the extension of the file to `sav`. +i.e. for `Glover (USA).eep` you would need to change the extension to `Glover (USA).sav` +**NOTE:** certain emulator saves may be incompatible. + ### ROM Boxart To use boxart, you need to place png files of size 158x112 in the folder `/menu/boxart` on the SD card. From 757ee226777fcc8057b55498bc474a89167bf955 Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Sat, 3 Feb 2024 00:49:58 +0000 Subject: [PATCH 13/20] Documentation fixes --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2c8f8498..42918408 100644 --- a/README.md +++ b/README.md @@ -44,10 +44,14 @@ An open source menu for N64 flashcarts. Using your PC, insert the SD card and ensure it is formatted for compatibility (We recommend FAT32 in most instances). ### Save files -By default, all save files (whether `FlashRam`, `SRAM` or `EEPROM`) use the `.sav` extension and match the filenane to the ROM. +By default, all save files (whether `FlashRam`, `SRAM` or `EEPROM`) use the `.sav` extension and match the filename of the ROM. + Each save file can be found in the `/saves` folder located in the same directory as the ROM and shares the same file name, apart from the extension. + If transfering a file from a different flashcart such as the ED64, it will be necessary to change the extension of the file to `sav`. + i.e. for `Glover (USA).eep` you would need to change the extension to `Glover (USA).sav` + **NOTE:** certain emulator saves may be incompatible. From 6906cff1b29ba8e81fb9267662659cc90a218d0c Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Sat, 3 Feb 2024 00:55:40 +0000 Subject: [PATCH 14/20] Improve save note Add caveats --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 42918408..5df18ec5 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ If transfering a file from a different flashcart such as the ED64, it will be ne i.e. for `Glover (USA).eep` you would need to change the extension to `Glover (USA).sav` -**NOTE:** certain emulator saves may be incompatible. +**NOTE:** certain emulator saves or saves created for a different ROM version or region may be incompatible. ### ROM Boxart From c310c5f9e1d2dbcbec03a1b4fb020f7e898d9384 Mon Sep 17 00:00:00 2001 From: Robin Jones Date: Tue, 19 Mar 2024 17:11:22 +0000 Subject: [PATCH 15/20] Update submodules (#93) ## Description Updates libpng and libdragon to latest. --- libdragon | 2 +- src/libs/libspng | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libdragon b/libdragon index 185c4a34..c99272b5 160000 --- a/libdragon +++ b/libdragon @@ -1 +1 @@ -Subproject commit 185c4a34f265c90d027f4054024cff1675529d7f +Subproject commit c99272b552d398e479a3de6ed9b6563a710b3cc2 diff --git a/src/libs/libspng b/src/libs/libspng index e5c1fc47..adc94393 160000 --- a/src/libs/libspng +++ b/src/libs/libspng @@ -1 +1 @@ -Subproject commit e5c1fc470fceaca08b8c30dc40768c28b82b9e12 +Subproject commit adc94393dbeddf9e027d1b2dfff7c1bab975224e From 3becd1ff59cb64a1c62fbf5417dc38d6d04855df Mon Sep 17 00:00:00 2001 From: Mateusz Faderewski Date: Wed, 24 Apr 2024 02:45:09 +0200 Subject: [PATCH 16/20] Use `stdio` calls for file/directory interaction instead of `fatfs` (#95) ## Description This PR changes most of the calls to the fatfs lib with the standard C ones. Additionally, there's couple of changes required to adapt to new interface and several bug fixes. As a bonus menu can now be run in ares emulator and on the iQue player, adapting to the available storage options - DragonFS in the ROM and iQue flash modules (bbfs). ## Motivation and Context To make it easier to use storage medium other than SD cards on platforms other than N64 with flashcart. ## How Has This Been Tested? SummerCart64 flashcart and ares emulator ## Screenshots N/A ## Types of changes - [x] Improvement (non-breaking change that adds a new feature) - [x] Bug fix (fixes an issue) - [x] Breaking change (breaking change) - [x] Config and build (change in the configuration and build system, has no impact on code or features) ## Checklist: - [x] My code follows the code style of this project. - [x] My change requires a change to the documentation. - [ ] I have updated the documentation accordingly. - [ ] I have added tests to cover my changes. - [ ] All new and existing tests passed. Signed-off-by: Polprzewodnikowy --- .devcontainer/devcontainer.json | 4 +- .gitignore | 12 ++- .gitmodules | 2 +- Makefile | 9 +- filesystem/.gitkeep | 0 libdragon | 2 +- src/flashcart/64drive/64drive.c | 18 ++-- src/flashcart/flashcart.c | 90 +++++++++------- src/flashcart/flashcart.h | 5 +- src/flashcart/flashcart_utils.c | 72 +++++++++++-- src/flashcart/flashcart_utils.h | 16 ++- src/flashcart/sc64/sc64.c | 82 +++++++-------- src/libs/miniz | 2 +- src/menu/cart_load.c | 4 +- src/menu/components.h | 2 +- src/menu/components/background.c | 36 ++++--- src/menu/components/boxart.c | 50 +++++---- src/menu/components/common.c | 12 +++ src/menu/components/file_list.c | 18 ++-- src/menu/disk_info.c | 40 +++---- src/menu/disk_info.h | 5 +- src/menu/fonts.c | 15 ++- src/menu/fonts.h | 2 +- src/menu/menu.c | 54 ++++++---- src/menu/menu_state.h | 8 +- src/menu/mp3_player.c | 80 +++++++------- src/menu/path.c | 4 +- src/menu/path.h | 2 +- src/menu/png_decoder.c | 28 ++--- src/menu/rom_info.c | 19 ++-- src/menu/settings.c | 23 +++-- src/menu/settings.h | 6 +- src/menu/usb_comm.c | 33 +++--- src/menu/views/browser.c | 161 ++++++++++++++++------------- src/menu/views/file_info.c | 23 ++--- src/menu/views/load_disk.c | 2 +- src/menu/views/load_rom.c | 2 +- src/menu/views/settings_editor.c | 16 +-- src/menu/views/text_viewer.c | 96 ++++++++++------- src/utils/fs.c | 172 ++++++++++--------------------- src/utils/fs.h | 7 +- 41 files changed, 670 insertions(+), 564 deletions(-) create mode 100644 filesystem/.gitkeep diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 9c9a1878..effc134b 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -6,7 +6,7 @@ "mounts": [ "source=n64flashcartmenu-bashhistory,target=/commandhistory,type=volume" ], - "postCreateCommand": "git submodule update --init && cd ./libdragon && ./build.sh", + "postCreateCommand": "git submodule update --init && cd ./libdragon && make clobber -j && make libdragon tools -j && make install tools-install -j", "customizations": { "vscode": { "extensions": [ @@ -24,4 +24,4 @@ } } } -} +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index de80dacb..bc2d5ddf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,17 @@ +# Ignore editor specific config folders/files /.vscode + +# Ignore compilation result directories /build -/filesystem /output + +# Ignore generated files in the libdragon FS +/filesystem/FiraMonoBold.font64 + +# Ignore external development tools /tools/* -# There should never be ROMs uploaded, but just incase, make sure they are excluded. +# Ignore any N64 ROM **/*.n64 +**/*.v64 **/*.z64 diff --git a/.gitmodules b/.gitmodules index f830561a..af1ca991 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,7 @@ [submodule "libdragon"] path = libdragon url = https://github.com/DragonMinded/libdragon - branch = unstable + branch = preview ignore = dirty [submodule "src/libs/libspng"] path = src/libs/libspng diff --git a/Makefile b/Makefile index 7c0ec049..fed6ed87 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,11 @@ BUILD_TIMESTAMP = "$(shell TZ='UTC' date "+%Y-%m-%d %H:%M:%S %:z")" include $(N64_INST)/include/n64.mk +N64_ROM_SAVETYPE = none +N64_ROM_RTC = 1 +N64_ROM_REGIONFREE = 1 +N64_ROM_REGION = E + N64_CFLAGS += -iquote $(SOURCE_DIR) -iquote $(ASSETS_DIR) -I $(SOURCE_DIR)/libs -flto=auto $(FLAGS) SRCS = \ @@ -128,7 +133,9 @@ all: $(OUTPUT_DIR)/$(PROJECT_NAME).n64 64drive ed64 ed64-clone sc64 .PHONY: all clean: - @rm -rf ./$(BUILD_DIR) ./$(FILESYSTEM_DIR) ./$(OUTPUT_DIR) + @rm -f ./$(FILESYSTEM) + @find ./$(FILESYSTEM_DIR) -type d -empty -delete + @rm -rf ./$(BUILD_DIR) ./$(OUTPUT_DIR) .PHONY: clean run: $(OUTPUT_DIR)/$(PROJECT_NAME).n64 diff --git a/filesystem/.gitkeep b/filesystem/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/libdragon b/libdragon index c99272b5..8dd362b8 160000 --- a/libdragon +++ b/libdragon @@ -1 +1 @@ -Subproject commit c99272b552d398e479a3de6ed9b6563a710b3cc2 +Subproject commit 8dd362b8d858ae60befe9d27bc55cf770b9b50af diff --git a/src/flashcart/64drive/64drive.c b/src/flashcart/64drive/64drive.c index 9b0b8b08..4a23d4d2 100644 --- a/src/flashcart/64drive/64drive.c +++ b/src/flashcart/64drive/64drive.c @@ -83,11 +83,11 @@ static flashcart_err_t d64_load_rom (char *rom_path, flashcart_progress_callback FIL fil; UINT br; - if (f_open(&fil, strip_sd_prefix(rom_path), FA_READ) != FR_OK) { + if (f_open(&fil, strip_fs_prefix(rom_path), FA_READ) != FR_OK) { return FLASHCART_ERR_LOAD; } - fix_file_size(&fil); + fatfs_fix_file_size(&fil); size_t rom_size = f_size(&fil); @@ -125,11 +125,11 @@ static flashcart_err_t d64_load_file (char *file_path, uint32_t rom_offset, uint FIL fil; UINT br; - if (f_open(&fil, strip_sd_prefix(file_path), FA_READ) != FR_OK) { + if (f_open(&fil, strip_fs_prefix(file_path), FA_READ) != FR_OK) { return FLASHCART_ERR_LOAD; } - fix_file_size(&fil); + fatfs_fix_file_size(&fil); size_t file_size = f_size(&fil) - file_offset; @@ -164,7 +164,7 @@ static flashcart_err_t d64_load_save (char *save_path) { FIL fil; UINT br; - if (f_open(&fil, strip_sd_prefix(save_path), FA_READ) != FR_OK) { + if (f_open(&fil, strip_fs_prefix(save_path), FA_READ) != FR_OK) { return FLASHCART_ERR_LOAD; } @@ -251,7 +251,13 @@ static flashcart_err_t d64_set_save_type (flashcart_save_type_t save_type) { return FLASHCART_OK; } -static flashcart_err_t d64_set_save_writeback (uint32_t *sectors) { +static flashcart_err_t d64_set_save_writeback (char *save_path) { + uint32_t sectors[SAVE_WRITEBACK_MAX_SECTORS] __attribute__((aligned(8))); + + if (fatfs_get_file_sectors(save_path, sectors, ADDRESS_TYPE_MEM, SAVE_WRITEBACK_MAX_SECTORS)) { + return FLASHCART_ERR_LOAD; + } + if (d64_ll_write_save_writeback_lba_list(sectors)) { return FLASHCART_ERR_INT; } diff --git a/src/flashcart/flashcart.c b/src/flashcart/flashcart.c index bd2e4163..c5aaaacb 100644 --- a/src/flashcart/flashcart.c +++ b/src/flashcart/flashcart.c @@ -8,14 +8,12 @@ #include "utils/utils.h" #include "flashcart.h" +#include "flashcart_utils.h" #include "64drive/64drive.h" #include "sc64/sc64.h" -#define SAVE_WRITEBACK_MAX_SECTORS (256) - - static const size_t SAVE_SIZE[__FLASHCART_SAVE_TYPE_END] = { 0, 512, @@ -27,34 +25,44 @@ static const size_t SAVE_SIZE[__FLASHCART_SAVE_TYPE_END] = { KiB(128), }; -static uint32_t save_writeback_sectors[SAVE_WRITEBACK_MAX_SECTORS] __attribute__((aligned(8))); +static flashcart_err_t dummy_init (void) { + return FLASHCART_OK; +} -static void save_writeback_sectors_callback (uint32_t sector_count, uint32_t file_sector, uint32_t cluster_sector, uint32_t cluster_size) { - for (uint32_t i = 0; i < cluster_size; i++) { - uint32_t offset = file_sector + i; - uint32_t sector = cluster_sector + i; - - if ((offset > SAVE_WRITEBACK_MAX_SECTORS) || (offset > sector_count)) { - return; - } - - save_writeback_sectors[offset] = sector; +static bool dummy_has_feature (flashcart_features_t feature) { + switch (feature) { + default: + return false; } } +static flashcart_err_t dummy_load_rom (char *rom_path, flashcart_progress_callback_t *progress) { + return FLASHCART_OK; +} -static flashcart_err_t dummy_init (void) { +static flashcart_err_t dummy_load_file (char *file_path, uint32_t rom_offset, uint32_t file_offset) { + return FLASHCART_OK; +} + +static flashcart_err_t dummy_load_save (char *save_path) { + return FLASHCART_OK; +} + +static flashcart_err_t dummy_set_save_type (flashcart_save_type_t save_type) { return FLASHCART_OK; } static flashcart_t *flashcart = &((flashcart_t) { .init = dummy_init, .deinit = NULL, - .load_rom = NULL, - .load_file = NULL, - .load_save = NULL, - .set_save_type = NULL, + .has_feature = dummy_has_feature, + .load_rom = dummy_load_rom, + .load_file = dummy_load_file, + .load_save = dummy_load_save, + .load_64dd_ipl = NULL, + .load_64dd_disk = NULL, + .set_save_type = dummy_set_save_type, .set_save_writeback = NULL, }); @@ -69,7 +77,6 @@ static flashcart_t *flashcart = &((flashcart_t) { char *flashcart_convert_error_message (flashcart_err_t err) { switch (err) { case FLASHCART_OK: return "No error"; - case FLASHCART_ERR_NOT_DETECTED: return "No flashcart hardware was detected"; case FLASHCART_ERR_OUTDATED: return "Outdated flashcart firmware"; case FLASHCART_ERR_SD_CARD: return "Error during SD card initialization"; case FLASHCART_ERR_ARGS: return "Invalid argument passed to flashcart function"; @@ -80,15 +87,17 @@ char *flashcart_convert_error_message (flashcart_err_t err) { } } -flashcart_err_t flashcart_init (void) { +flashcart_err_t flashcart_init (const char **storage_prefix) { flashcart_err_t err; - bool sd_card_initialized = debug_init_sdfs("sd:/", -1); + if (sys_bbplayer()) { + // TODO: Add iQue callbacks + *storage_prefix = "bbfs:/"; + return FLASHCART_OK; + } -#ifndef NDEBUG - // NOTE: Some flashcarts doesn't have USB port, can't throw error here - debug_init_usblog(); -#endif + *storage_prefix = "sd:/"; + bool sd_card_initialized = debug_init_sdfs(*storage_prefix, -1); switch (cart_type) { case CART_CI: // 64drive @@ -96,6 +105,8 @@ flashcart_err_t flashcart_init (void) { break; case CART_EDX: // Series X EverDrive-64 + break; + case CART_ED: // Original EverDrive-64 break; @@ -103,15 +114,22 @@ flashcart_err_t flashcart_init (void) { flashcart = sc64_get_flashcart(); break; - default: - return FLASHCART_ERR_NOT_DETECTED; + default: // Probably emulator + *storage_prefix = "rom:/"; + debug_init_isviewer(); + break; } +#ifndef NDEBUG + // NOTE: Some flashcarts doesn't have USB port, can't throw error here + debug_init_usblog(); +#endif + if ((err = flashcart->init()) != FLASHCART_OK) { return err; } - if (!sd_card_initialized) { + if ((cart_type != CART_NULL) && (!sd_card_initialized)) { return FLASHCART_ERR_SD_CARD; } @@ -184,19 +202,11 @@ flashcart_err_t flashcart_load_save (char *save_path, flashcart_save_type_t save return err; } - if (flashcart->set_save_writeback) { - for (int i = 0; i < SAVE_WRITEBACK_MAX_SECTORS; i++) { - save_writeback_sectors[i] = 0; - } - if (file_get_sectors(save_path, save_writeback_sectors_callback)) { - return FLASHCART_ERR_LOAD; - } - if ((err = flashcart->set_save_writeback(save_writeback_sectors)) != FLASHCART_OK) { - return err; - } + if (!flashcart->set_save_writeback) { + return FLASHCART_OK; } - return FLASHCART_OK; + return flashcart->set_save_writeback(save_path); } flashcart_err_t flashcart_load_64dd_ipl (char *ipl_path, flashcart_progress_callback_t *progress) { diff --git a/src/flashcart/flashcart.h b/src/flashcart/flashcart.h index 001516bb..b45a32d0 100644 --- a/src/flashcart/flashcart.h +++ b/src/flashcart/flashcart.h @@ -15,7 +15,6 @@ /** @brief Flashcart error enumeration */ typedef enum { FLASHCART_OK, - FLASHCART_ERR_NOT_DETECTED, FLASHCART_ERR_OUTDATED, FLASHCART_ERR_SD_CARD, FLASHCART_ERR_ARGS, @@ -75,12 +74,12 @@ typedef struct { /** @brief The flashcart set save type function */ flashcart_err_t (*set_save_type) (flashcart_save_type_t save_type); /** @brief The flashcart set save writeback function */ - flashcart_err_t (*set_save_writeback) (uint32_t *sectors); + flashcart_err_t (*set_save_writeback) (char *save_path); } flashcart_t; char *flashcart_convert_error_message (flashcart_err_t err); -flashcart_err_t flashcart_init (void); +flashcart_err_t flashcart_init (const char **storage_prefix); flashcart_err_t flashcart_deinit (void); bool flashcart_has_feature (flashcart_features_t feature); flashcart_err_t flashcart_load_rom (char *rom_path, bool byte_swap, flashcart_progress_callback_t *progress); diff --git a/src/flashcart/flashcart_utils.c b/src/flashcart/flashcart_utils.c index a89230c3..3eb13605 100644 --- a/src/flashcart/flashcart_utils.c +++ b/src/flashcart/flashcart_utils.c @@ -5,13 +5,6 @@ #include "utils/utils.h" -void fix_file_size (FIL *fil) { - // HACK: Align file size to the SD sector size to prevent FatFs from doing partial sector load. - // We are relying on direct transfer from SD to SDRAM without CPU intervention. - // Sending some extra bytes isn't an issue here. - fil->obj.objsize = ALIGN(f_size(fil), FS_SECTOR_SIZE); -} - void pi_dma_read_data (void *src, void *dst, size_t length) { data_cache_hit_writeback_invalidate(dst, length); dma_read_async(dst, (uint32_t) (src), length); @@ -27,3 +20,68 @@ void pi_dma_write_data (void *src, void *dst, size_t length) { dma_write_raw_async(src, (uint32_t) (dst), length); dma_wait(); } + +void fatfs_fix_file_size (FIL *fil) { + // HACK: Align file size to the SD sector size to prevent FatFs from doing partial sector load. + // We are relying on direct transfer from SD to SDRAM without CPU intervention. + // Sending some extra bytes isn't an issue here. + fil->obj.objsize = ALIGN(f_size(fil), FS_SECTOR_SIZE); +} + +bool fatfs_get_file_sectors (char *path, uint32_t *address, address_type_t type, uint32_t max_sectors) { + FATFS *fs; + FIL fil; + bool error = false; + + if (f_open(&fil, strip_fs_prefix(path), FA_READ) != FR_OK) { + return true; + } + + fatfs_fix_file_size(&fil); + + fs = fil.obj.fs; + + uint32_t sector_count = MIN(f_size(&fil) / FS_SECTOR_SIZE, max_sectors); + + for (uint32_t file_sector = 0; file_sector < sector_count; file_sector += fs->csize) { + if ((f_lseek(&fil, (file_sector * FS_SECTOR_SIZE) + (FS_SECTOR_SIZE / 2))) != FR_OK) { + error = true; + break; + } + + uint32_t cluster = fil.clust; + + if (cluster >= fs->n_fatent) { + error = true; + break; + } + + uint32_t cluster_sector = (fs->database + ((LBA_t) (fs->csize) * (cluster - 2))); + + for (uint32_t i = 0; i < fs->csize; i++) { + uint32_t sector = (cluster_sector + i); + + if ((file_sector + i) >= sector_count) { + break; + } + + switch (type) { + case ADDRESS_TYPE_MEM: + *address = sector; + break; + + case ADDRESS_TYPE_PI: + io_write((uint32_t) (address), sector); + break; + } + + address++; + } + } + + if (f_close(&fil) != FR_OK) { + error = true; + } + + return error; +} diff --git a/src/flashcart/flashcart_utils.h b/src/flashcart/flashcart_utils.h index 38d1b589..c0b5d6d8 100644 --- a/src/flashcart/flashcart_utils.h +++ b/src/flashcart/flashcart_utils.h @@ -8,12 +8,26 @@ #define FLASHCART_UTILS_H__ +#include +#include +#include + #include -void fix_file_size (FIL *fil); +#define SAVE_WRITEBACK_MAX_SECTORS (256) + + +typedef enum { + ADDRESS_TYPE_MEM, + ADDRESS_TYPE_PI, +} address_type_t; + + void pi_dma_read_data (void *src, void *dst, size_t length); void pi_dma_write_data (void *src, void *dst, size_t length); +void fatfs_fix_file_size (FIL *fil); +bool fatfs_get_file_sectors (char *path, uint32_t *address, address_type_t address_type, uint32_t max_sectors); #endif diff --git a/src/flashcart/sc64/sc64.c b/src/flashcart/sc64/sc64.c index b7febf16..1b105fb0 100644 --- a/src/flashcart/sc64/sc64.c +++ b/src/flashcart/sc64/sc64.c @@ -75,9 +75,6 @@ static const uint8_t vzone_to_pzone[DISK_TYPES][DISK_ZONES] = { static const uint8_t rom_zones[DISK_TYPES] = { 5, 7, 9, 11, 13, 15, 16 }; -static uint32_t disk_sectors_start_offset; - - static flashcart_err_t load_to_flash (FIL *fil, void *address, size_t size, UINT *br, flashcart_progress_callback_t *progress) { size_t erase_block_size; UINT bp; @@ -110,23 +107,6 @@ static flashcart_err_t load_to_flash (FIL *fil, void *address, size_t size, UINT return FLASHCART_OK; } -static uint32_t disk_sectors_start (uint32_t offset) { - disk_sectors_start_offset = offset; - return (offset + (DISK_MAX_SECTORS * sizeof(uint32_t))); -} - -static void disk_sectors_callback (uint32_t sector_count, uint32_t file_sector, uint32_t cluster_sector, uint32_t cluster_size) { - for (uint32_t i = 0; i < cluster_size; i++) { - uint32_t offset = file_sector + i; - uint32_t sector = cluster_sector + i; - - if ((offset > DISK_MAX_SECTORS) || (offset > sector_count)) { - return; - } - - io_write(ROM_ADDRESS + disk_sectors_start_offset + (offset * sizeof(uint32_t)), sector); - } -} static bool disk_zone_track_is_bad (uint8_t zone, uint8_t track, flashcart_disk_parameters_t *disk_parameters) { for (int i = 0; i < DISK_BAD_TRACKS_PER_ZONE; i++) { @@ -153,7 +133,7 @@ static void disk_set_thb_mapping (uint32_t offset, uint16_t track, uint8_t head, io_write(ROM_ADDRESS + offset + (index * sizeof(uint32_t)), mapping); } -static uint32_t disk_load_thb_table (uint32_t offset, flashcart_disk_parameters_t *disk_parameters) { +static void disk_load_thb_table (flashcart_disk_parameters_t *disk_parameters, uint32_t *thb_table_offset, uint32_t *current_offset) { int file_offset = 0; uint16_t lba = 0; @@ -175,15 +155,15 @@ static uint32_t disk_load_thb_table (uint32_t offset, flashcart_disk_parameters_ uint16_t track = track_offset + zone_track; if (disk_zone_track_is_bad(pzone, zone_track, disk_parameters)) { - disk_set_thb_mapping(offset, track, head, 0, false, false, 0); - disk_set_thb_mapping(offset, track, head, 1, false, false, 0); + disk_set_thb_mapping(*current_offset, track, head, 0, false, false, 0); + disk_set_thb_mapping(*current_offset, track, head, 1, false, false, 0); continue; } for (uint8_t block = 0; block < DISK_BLOCKS; block += 1) { bool valid = !(disk_system_lba_is_bad(lba, disk_parameters)); bool writable = (vzone >= rom_zones[disk_parameters->disk_type]); - disk_set_thb_mapping(offset, track, head, (starting_block ^ block), valid, writable, file_offset); + disk_set_thb_mapping(*current_offset, track, head, (starting_block ^ block), valid, writable, file_offset); file_offset += (sector_length * DISK_SECTORS_PER_BLOCK); lba += 1; } @@ -192,7 +172,19 @@ static uint32_t disk_load_thb_table (uint32_t offset, flashcart_disk_parameters_ } } - return (offset + (DISK_TRACKS * DISK_HEADS * DISK_BLOCKS * sizeof(uint32_t))); + *thb_table_offset = *current_offset; + *current_offset += (DISK_TRACKS * DISK_HEADS * DISK_BLOCKS * sizeof(uint32_t)); +} + +static bool disk_load_sector_table (char *path, uint32_t *sector_table_offset, uint32_t *current_offset) { + if (fatfs_get_file_sectors(path, (uint32_t *) (ROM_ADDRESS + *current_offset), ADDRESS_TYPE_PI, DISK_MAX_SECTORS)) { + return true; + } + + *sector_table_offset = *current_offset; + *current_offset += (DISK_MAX_SECTORS * sizeof(uint32_t)); + + return false; } @@ -270,11 +262,11 @@ static flashcart_err_t sc64_load_rom (char *rom_path, flashcart_progress_callbac FIL fil; UINT br; - if (f_open(&fil, strip_sd_prefix(rom_path), FA_READ) != FR_OK) { + if (f_open(&fil, strip_fs_prefix(rom_path), FA_READ) != FR_OK) { return FLASHCART_ERR_LOAD; } - fix_file_size(&fil); + fatfs_fix_file_size(&fil); size_t rom_size = f_size(&fil); @@ -351,11 +343,11 @@ static flashcart_err_t sc64_load_file (char *file_path, uint32_t rom_offset, uin FIL fil; UINT br; - if (f_open(&fil, strip_sd_prefix(file_path), FA_READ) != FR_OK) { + if (f_open(&fil, strip_fs_prefix(file_path), FA_READ) != FR_OK) { return FLASHCART_ERR_LOAD; } - fix_file_size(&fil); + fatfs_fix_file_size(&fil); size_t file_size = f_size(&fil) - file_offset; @@ -413,7 +405,7 @@ static flashcart_err_t sc64_load_save (char *save_path) { FIL fil; UINT br; - if (f_open(&fil, strip_sd_prefix(save_path), FA_READ) != FR_OK) { + if (f_open(&fil, strip_fs_prefix(save_path), FA_READ) != FR_OK) { return FLASHCART_ERR_LOAD; } @@ -439,11 +431,11 @@ static flashcart_err_t sc64_load_64dd_ipl (char *ipl_path, flashcart_progress_ca FIL fil; UINT br; - if (f_open(&fil, strip_sd_prefix(ipl_path), FA_READ) != FR_OK) { + if (f_open(&fil, strip_fs_prefix(ipl_path), FA_READ) != FR_OK) { return FLASHCART_ERR_LOAD; } - fix_file_size(&fil); + fatfs_fix_file_size(&fil); size_t ipl_size = f_size(&fil); @@ -476,19 +468,17 @@ static flashcart_err_t sc64_load_64dd_ipl (char *ipl_path, flashcart_progress_ca } static flashcart_err_t sc64_load_64dd_disk (char *disk_path, flashcart_disk_parameters_t *disk_parameters) { - sc64_disk_mapping_t mapping = { .count = 0 }; - uint32_t next_mapping_offset = DISK_MAPPING_ROM_OFFSET; + sc64_disk_mapping_t mapping; + uint32_t mapping_offset = DISK_MAPPING_ROM_OFFSET; + sc64_drive_type_t drive_type = (disk_parameters->development_drive ? DRIVE_TYPE_DEVELOPMENT : DRIVE_TYPE_RETAIL); // TODO: Support loading multiple disks - // LOOP START - mapping.disks[mapping.count].thb_table = next_mapping_offset; - mapping.disks[mapping.count].sector_table = disk_load_thb_table(mapping.disks[mapping.count].thb_table, disk_parameters); - next_mapping_offset = disk_sectors_start(mapping.disks[mapping.count].sector_table); - if (file_get_sectors(disk_path, disk_sectors_callback)) { + for (mapping.count = 0; mapping.count < 1; mapping.count++) { + disk_load_thb_table(disk_parameters++, &mapping.disks[mapping.count].thb_table, &mapping_offset); + if (disk_load_sector_table(disk_path++, &mapping.disks[mapping.count].sector_table, &mapping_offset)) { return FLASHCART_ERR_LOAD; } - mapping.count += 1; - // LOOP END + } if (mapping.count == 0) { return FLASHCART_ERR_ARGS; @@ -498,8 +488,6 @@ static flashcart_err_t sc64_load_64dd_disk (char *disk_path, flashcart_disk_para return FLASHCART_ERR_INT; } - sc64_drive_type_t drive_type = disk_parameters->development_drive ? DRIVE_TYPE_DEVELOPMENT : DRIVE_TYPE_RETAIL; - const struct { sc64_cfg_id_t id; uint32_t value; @@ -559,7 +547,13 @@ static flashcart_err_t sc64_set_save_type (flashcart_save_type_t save_type) { return FLASHCART_OK; } -static flashcart_err_t sc64_set_save_writeback (uint32_t *sectors) { +static flashcart_err_t sc64_set_save_writeback (char *save_path) { + uint32_t sectors[SAVE_WRITEBACK_MAX_SECTORS] __attribute__((aligned(8))); + + if (fatfs_get_file_sectors(save_path, sectors, ADDRESS_TYPE_MEM, SAVE_WRITEBACK_MAX_SECTORS)) { + return FLASHCART_ERR_LOAD; + } + pi_dma_write_data(sectors, SC64_BUFFERS->BUFFER, 1024); if (sc64_ll_writeback_enable(SC64_BUFFERS->BUFFER) != SC64_OK) { diff --git a/src/libs/miniz b/src/libs/miniz index 18795fa6..16413c21 160000 --- a/src/libs/miniz +++ b/src/libs/miniz @@ -1 +1 @@ -Subproject commit 18795fa61e590521381ba9e1fa4a4ab362b095f6 +Subproject commit 16413c213de38e703d883006193734e8b1178d5d diff --git a/src/menu/cart_load.c b/src/menu/cart_load.c index 32bf4a28..13ef7aca 100644 --- a/src/menu/cart_load.c +++ b/src/menu/cart_load.c @@ -112,7 +112,7 @@ cart_load_err_t cart_load_64dd_ipl_and_disk (menu_t *menu, flashcart_progress_ca return CART_LOAD_ERR_EXP_PAK_NOT_FOUND; } - path_t *path = path_init("sd:/", DDIPL_LOCATION); + path_t *path = path_init(menu->storage_prefix, DDIPL_LOCATION); flashcart_disk_parameters_t disk_parameters; disk_parameters.development_drive = (menu->load.disk_info.region == DISK_REGION_DEVELOPMENT); @@ -154,7 +154,7 @@ cart_load_err_t cart_load_64dd_ipl_and_disk (menu_t *menu, flashcart_progress_ca } cart_load_err_t cart_load_emulator (menu_t *menu, cart_load_emu_type_t emu_type, flashcart_progress_callback_t progress) { - path_t *path = path_init("sd:/", EMU_LOCATION); + path_t *path = path_init(menu->storage_prefix, EMU_LOCATION); flashcart_save_type_t save_type = FLASHCART_SAVE_TYPE_NONE; uint32_t emulated_rom_offset = 0x200000; diff --git a/src/menu/components.h b/src/menu/components.h index a8cecb51..cff0f4c2 100644 --- a/src/menu/components.h +++ b/src/menu/components.h @@ -64,7 +64,7 @@ typedef struct { surface_t *image; } component_boxart_t; -component_boxart_t *component_boxart_init (char *game_code); +component_boxart_t *component_boxart_init (const char *storage_prefix, char *game_code); void component_boxart_free (component_boxart_t *b); void component_boxart_draw (component_boxart_t *b); diff --git a/src/menu/components/background.c b/src/menu/components/background.c index ac838098..45119107 100644 --- a/src/menu/components/background.c +++ b/src/menu/components/background.c @@ -1,4 +1,4 @@ -#include +#include #include #include "../components.h" @@ -31,22 +31,21 @@ static void load_from_cache (component_background_t *c) { return; } - FIL fil; - UINT bytes_read; + FILE *f; - if (f_open(&fil, strip_sd_prefix(c->cache_location), FA_READ) != FR_OK) { + if ((f = fopen(c->cache_location, "rb")) == NULL) { return; } cache_metadata_t cache_metadata; - if ((f_read(&fil, &cache_metadata, sizeof(cache_metadata), &bytes_read) != FR_OK) || (bytes_read != sizeof(cache_metadata))) { - f_close(&fil); + if (fread(&cache_metadata, sizeof(cache_metadata), 1, f) != 1) { + fclose(f); return; } if (cache_metadata.magic != CACHE_METADATA_MAGIC || cache_metadata.width > DISPLAY_WIDTH || cache_metadata.height > DISPLAY_HEIGHT) { - f_close(&fil); + fclose(f); return; } @@ -57,17 +56,17 @@ static void load_from_cache (component_background_t *c) { surface_free(c->image); free(c->image); c->image = NULL; - f_close(&fil); + fclose(f); return; } - if ((f_read(&fil, c->image->buffer, cache_metadata.size, &bytes_read) != FR_OK) || (bytes_read != cache_metadata.size)) { + if (fread(c->image->buffer, cache_metadata.size, 1, f) != 1) { surface_free(c->image); free(c->image); c->image = NULL; } - f_close(&fil); + fclose(f); } static void save_to_cache (component_background_t *c) { @@ -75,9 +74,9 @@ static void save_to_cache (component_background_t *c) { return; } - FIL fil; - UINT bytes_written; - if (f_open(&fil, strip_sd_prefix(c->cache_location), FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) { + FILE *f; + + if ((f = fopen(c->cache_location, "wb")) == NULL) { return; } @@ -88,10 +87,10 @@ static void save_to_cache (component_background_t *c) { .size = (c->image->height * c->image->stride), }; - f_write(&fil, &cache_metadata, sizeof(cache_metadata), &bytes_written); - f_write(&fil, c->image->buffer, cache_metadata.size, &bytes_written); + fwrite(&cache_metadata, sizeof(cache_metadata), 1, f); + fwrite(c->image->buffer, cache_metadata.size, 1, f); - f_close(&fil); + fclose(f); } static void prepare_background (component_background_t *c) { @@ -166,7 +165,7 @@ static void display_list_free (void *arg) { void component_background_init (char *cache_location) { if (!background) { background = calloc(1, sizeof(component_background_t)); - background->cache_location = cache_location; + background->cache_location = strdup(cache_location); load_from_cache(background); prepare_background(background); } @@ -183,6 +182,9 @@ void component_background_free (void) { rdpq_call_deferred(display_list_free, background->image_display_list); background->image_display_list = NULL; } + if (background->cache_location) { + free(background->cache_location); + } free(background); background = NULL; } diff --git a/src/menu/components/boxart.c b/src/menu/components/boxart.c index 6ed3826a..54121b6e 100644 --- a/src/menu/components/boxart.c +++ b/src/menu/components/boxart.c @@ -6,9 +6,8 @@ #include "constants.h" #include "utils/fs.h" -#ifndef BOXART_DIRECTORY -#define BOXART_DIRECTORY "sd:/menu/boxart" -#endif + +#define BOXART_DIRECTORY "menu/boxart" static void png_decoder_callback (png_err_t err, surface_t *decoded_image, void *callback_data) { @@ -18,25 +17,38 @@ static void png_decoder_callback (png_err_t err, surface_t *decoded_image, void } -component_boxart_t *component_boxart_init (char *game_code) { - component_boxart_t *b = calloc(1, sizeof(component_boxart_t)); +component_boxart_t *component_boxart_init (const char *storage_prefix, char *game_code) { + component_boxart_t *b; + char file_name[8]; - if (b) { - b->loading = true; - char *path = alloca(strlen(BOXART_DIRECTORY) + 1 + 7 + 1); - - // TODO: This is bad, we should only check for 3 letter codes - sprintf(path, "%s/%.3s.png", BOXART_DIRECTORY, game_code); - if (png_decoder_start(path, BOXART_WIDTH, BOXART_HEIGHT, png_decoder_callback, b) != PNG_OK) { - sprintf(path, "%s/%.2s.png", BOXART_DIRECTORY, &game_code[1]); - if (png_decoder_start(path, BOXART_WIDTH, BOXART_HEIGHT, png_decoder_callback, b) != PNG_OK) { - free(b); - b = NULL; - } - } + if ((b = calloc(1, sizeof(component_boxart_t))) == NULL) { + return NULL; } - return b; + b->loading = true; + + path_t *path = path_init(storage_prefix, BOXART_DIRECTORY); + + sprintf(file_name, "%.3s.png", game_code); + path_push(path, file_name); + if (png_decoder_start(path_get(path), BOXART_WIDTH, BOXART_HEIGHT, png_decoder_callback, b) == PNG_OK) { + path_free(path); + return b; + } + path_pop(path); + + // TODO: This is bad, we should only check for 3 letter codes + sprintf(file_name, "%.2s.png", game_code + 1); + path_push(path, file_name); + if (png_decoder_start(path_get(path), BOXART_WIDTH, BOXART_HEIGHT, png_decoder_callback, b) == PNG_OK) { + path_free(path); + return b; + } + + path_free(path); + free(b); + + return NULL; } void component_boxart_free (component_boxart_t *b) { diff --git a/src/menu/components/common.c b/src/menu/components/common.c index 5440cb08..66db56eb 100644 --- a/src/menu/components/common.c +++ b/src/menu/components/common.c @@ -121,6 +121,10 @@ void component_messagebox_draw (char *fmt, ...) { .wrap = WRAP_WORD }, FNT_DEFAULT, formatted, ¶graph_nbytes); + if (formatted != buffer) { + free(formatted); + } + component_dialog_draw( paragraph->bbox.x1 - paragraph->bbox.x0 + MESSAGEBOX_MARGIN, paragraph->bbox.y1 - paragraph->bbox.y0 + MESSAGEBOX_MARGIN @@ -154,6 +158,10 @@ void component_main_text_draw (rdpq_align_t align, rdpq_valign_t valign, char *f formatted, nbytes ); + + if (formatted != buffer) { + free(formatted); + } } void component_actions_bar_text_draw (rdpq_align_t align, rdpq_valign_t valign, char *fmt, ...) { @@ -179,4 +187,8 @@ void component_actions_bar_text_draw (rdpq_align_t align, rdpq_valign_t valign, formatted, nbytes ); + + if (formatted != buffer) { + free(formatted); + } } diff --git a/src/menu/components/file_list.c b/src/menu/components/file_list.c index 1de3cd19..300b46ff 100644 --- a/src/menu/components/file_list.c +++ b/src/menu/components/file_list.c @@ -8,15 +8,19 @@ static const char *dir_prefix = "/"; -static int format_file_size (char *buffer, int size) { - if (size < 8 * 1024) { - return sprintf(buffer, "%d B", size); +static int format_file_size (char *buffer, int64_t size) { + if (size < 0) { + return sprintf(buffer, "unknown"); + } else if (size == 0) { + return sprintf(buffer, "empty"); + } else if (size < 8 * 1024) { + return sprintf(buffer, "%lld B", size); } else if (size < 8 * 1024 * 1024) { - return sprintf(buffer, "%d kB", size / 1024); + return sprintf(buffer, "%lld kB", size / 1024); } else if (size < 1 * 1024 * 1024 * 1024) { - return sprintf(buffer, "%d MB", size / 1024 / 1024); + return sprintf(buffer, "%lld MB", size / 1024 / 1024); } else { - return sprintf(buffer, "%d GB", size / 1024 / 1024 / 1024); + return sprintf(buffer, "%lld GB", size / 1024 / 1024 / 1024); } } @@ -139,7 +143,7 @@ void component_file_list_draw (entry_t *list, int entries, int selected) { NULL ); - char file_size[8]; + char file_size[16]; for (int i = starting_position; i < entries; i++) { entry_t *entry = &list[i]; diff --git a/src/menu/disk_info.c b/src/menu/disk_info.c index 32680a3b..5c2c7388 100644 --- a/src/menu/disk_info.c +++ b/src/menu/disk_info.c @@ -1,6 +1,6 @@ -#include #include #include +#include #include #include "disk_info.h" @@ -39,18 +39,14 @@ static const int disk_id_lbas[DISK_ID_LBA_COUNT] = { }; -static bool load_system_area_lba (FIL *fil, int lba, uint8_t *buffer) { - UINT bytes_read; +static bool load_system_area_lba (FILE *f, int lba, uint8_t *buffer) { if (lba >= SYSTEM_AREA_LBA_COUNT) { return true; } - if (f_lseek(fil, SYSTEM_AREA_LBA_LENGTH * lba) != FR_OK) { + if (fseek(f, SYSTEM_AREA_LBA_LENGTH * lba, SEEK_SET)) { return true; } - if (f_read(fil, buffer, SYSTEM_AREA_LBA_LENGTH, &bytes_read) != FR_OK) { - return true; - } - if (bytes_read != SYSTEM_AREA_LBA_LENGTH) { + if (fread(buffer, SYSTEM_AREA_LBA_LENGTH, 1, f) != 1) { return true; } return false; @@ -113,7 +109,7 @@ static void update_bad_system_area_lbas (disk_info_t *disk_info) { } } -static disk_err_t load_and_verify_system_data_lba (FIL *fil, disk_info_t *disk_info) { +static disk_err_t load_and_verify_system_data_lba (FILE *f, disk_info_t *disk_info) { uint8_t buffer[SYSTEM_AREA_LBA_LENGTH]; int sector_length; @@ -122,7 +118,7 @@ static disk_err_t load_and_verify_system_data_lba (FIL *fil, disk_info_t *disk_i for (int i = 0; i < SYSTEM_DATA_LBA_COUNT; i++) { int lba = system_data_lbas[i]; - if (load_system_area_lba(fil, lba, buffer)) { + if (load_system_area_lba(f, lba, buffer)) { return DISK_ERR_IO; } @@ -155,7 +151,7 @@ static disk_err_t load_and_verify_system_data_lba (FIL *fil, disk_info_t *disk_i return valid_system_data_lba_found ? DISK_OK : DISK_ERR_INVALID; } -static disk_err_t load_and_verify_disk_id_lba (FIL *fil, disk_info_t *disk_info) { +static disk_err_t load_and_verify_disk_id_lba (FILE *f, disk_info_t *disk_info) { uint8_t buffer[SYSTEM_AREA_LBA_LENGTH]; bool valid_disk_id_lba_found = false; @@ -163,7 +159,7 @@ static disk_err_t load_and_verify_disk_id_lba (FIL *fil, disk_info_t *disk_info) for (int i = 0; i < DISK_ID_LBA_COUNT; i++) { int lba = disk_id_lbas[i]; - if (load_system_area_lba(fil, lba, buffer)) { + if (load_system_area_lba(f, lba, buffer)) { return DISK_ERR_IO; } @@ -180,29 +176,27 @@ static disk_err_t load_and_verify_disk_id_lba (FIL *fil, disk_info_t *disk_info) } -disk_err_t disk_info_load (char *path, disk_info_t *disk_info) { - FIL fil; +disk_err_t disk_info_load (path_t *path, disk_info_t *disk_info) { + FILE *f; disk_err_t err; for (int i = 0; i < SYSTEM_AREA_LBA_COUNT; i++) { disk_info->bad_system_area_lbas[i] = false; } - if (f_open(&fil, strip_sd_prefix(path), FA_READ) != FR_OK) { + if ((f = fopen(path_get(path), "rb")) == NULL) { return DISK_ERR_NO_FILE; } - - if ((err = load_and_verify_system_data_lba(&fil, disk_info)) != DISK_OK) { - f_close(&fil); + setbuf(f, NULL); + if ((err = load_and_verify_system_data_lba(f, disk_info)) != DISK_OK) { + fclose(f); return err; } - - if ((err = load_and_verify_disk_id_lba(&fil, disk_info)) != DISK_OK) { - f_close(&fil); + if ((err = load_and_verify_disk_id_lba(f, disk_info)) != DISK_OK) { + fclose(f); return err; } - - if (f_close(&fil) != FR_OK) { + if (fclose(f)) { return DISK_ERR_IO; } diff --git a/src/menu/disk_info.h b/src/menu/disk_info.h index ae35be69..fe5175ec 100644 --- a/src/menu/disk_info.h +++ b/src/menu/disk_info.h @@ -11,6 +11,9 @@ #include #include +#include "path.h" + + /** @brief Disk state enumeration. */ typedef enum { DISK_OK, @@ -49,7 +52,7 @@ typedef struct { } disk_info_t; -disk_err_t disk_info_load (char *path, disk_info_t *disk_info); +disk_err_t disk_info_load (path_t *path, disk_info_t *disk_info); #endif diff --git a/src/menu/fonts.c b/src/menu/fonts.c index e8eccc95..f694ab80 100644 --- a/src/menu/fonts.c +++ b/src/menu/fonts.c @@ -1,10 +1,17 @@ #include #include "fonts.h" +#include "utils/fs.h" -static void load_default_font (void) { - rdpq_font_t *default_font = rdpq_font_load("rom:/FiraMonoBold.font64"); +static void load_default_font (char *custom_font_path) { + char *font_path = "rom:/FiraMonoBold.font64"; + + if (custom_font_path && file_exists(custom_font_path)) { + font_path = custom_font_path; + } + + rdpq_font_t *default_font = rdpq_font_load(font_path); rdpq_font_style(default_font, STL_DEFAULT, &((rdpq_fontstyle_t) { .color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF) })); rdpq_font_style(default_font, STL_GREEN, &((rdpq_fontstyle_t) { .color = RGBA32(0x70, 0xFF, 0x70, 0xFF) })); @@ -17,6 +24,6 @@ static void load_default_font (void) { } -void fonts_init (void) { - load_default_font(); +void fonts_init (char *custom_font_path) { + load_default_font(custom_font_path); } diff --git a/src/menu/fonts.h b/src/menu/fonts.h index 2ddade59..9fb00092 100644 --- a/src/menu/fonts.h +++ b/src/menu/fonts.h @@ -23,7 +23,7 @@ typedef enum { } menu_font_style_t; -void fonts_init (void); +void fonts_init (char *custom_font_path); #endif diff --git a/src/menu/menu.c b/src/menu/menu.c index 1b8832cb..beef676d 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -20,12 +20,15 @@ #include "views/views.h" -#define MENU_DIRECTORY "sd:/menu" -#define CACHE_DIRECTORY "sd:/menu/cache" -#define BACKGROUND_CACHE "sd:/menu/cache/background.data" +#define MENU_DIRECTORY "/menu" +#define MENU_SETTINGS_FILE "config.ini" +#define MENU_CUSTOM_FONT_FILE "custom.font64" -#define FRAMERATE_DIVIDER (2) -#define LAG_REPORT (false) +#define MENU_CACHE_DIRECTORY "cache" +#define BACKGROUND_CACHE_FILE "background.data" + +#define FRAMERATE_DIVIDER (2) +#define LAG_REPORT (false) static menu_t *menu; @@ -62,7 +65,6 @@ static void menu_init (boot_params_t *boot_params) { rdpq_init(); dfs_init(DFS_DEFAULT_LOCATION); - fonts_init(); sound_init_default(); menu = calloc(1, sizeof(menu_t)); @@ -71,31 +73,39 @@ static void menu_init (boot_params_t *boot_params) { menu->mode = MENU_MODE_NONE; menu->next_mode = MENU_MODE_STARTUP; - menu->flashcart_err = flashcart_init(); + menu->flashcart_err = flashcart_init(&menu->storage_prefix); if (menu->flashcart_err != FLASHCART_OK) { menu->next_mode = MENU_MODE_FAULT; } - menu->error_message = NULL; + path_t *path = path_init(menu->storage_prefix, MENU_DIRECTORY); - directory_create(MENU_DIRECTORY); + directory_create(path_get(path)); + path_push(path, MENU_SETTINGS_FILE); + settings_init(path_get(path)); settings_load(&menu->settings); + path_pop(path); - directory_create(CACHE_DIRECTORY); + path_push(path, MENU_CUSTOM_FONT_FILE); + fonts_init(path_get(path)); + path_pop(path); - component_background_init(BACKGROUND_CACHE); + path_push(path, MENU_CACHE_DIRECTORY); + directory_create(path_get(path)); + + path_push(path, BACKGROUND_CACHE_FILE); + component_background_init(path_get(path)); + + path_free(path); menu->boot_params = boot_params; - bool default_directory_exists = directory_exists(menu->settings.default_directory); - char *init_directory = default_directory_exists ? menu->settings.default_directory : ""; - - menu->browser.valid = false; - menu->browser.reload = false; - menu->browser.directory = path_init("sd:/", init_directory); - - menu->load.rom_path = NULL; + menu->browser.directory = path_init(menu->storage_prefix, menu->settings.default_directory); + if (!directory_exists(path_get(menu->browser.directory))) { + path_free(menu->browser.directory); + menu->browser.directory = path_init(menu->storage_prefix, "/"); + } hdmi_clear_game_id(); @@ -118,6 +128,12 @@ static void menu_deinit (menu_t *menu) { hdmi_send_game_id(menu->boot_params); + path_free(menu->load.disk_path); + path_free(menu->load.rom_path); + for (int i = 0; i < menu->browser.entries; i++) { + free(menu->browser.list[i].name); + } + free(menu->browser.list); path_free(menu->browser.directory); free(menu); diff --git a/src/menu/menu_state.h b/src/menu/menu_state.h index 265443fe..c2ad221d 100644 --- a/src/menu/menu_state.h +++ b/src/menu/menu_state.h @@ -18,9 +18,6 @@ #include "settings.h" -#define BROWSER_LIST_SIZE 2048 - - /** @brief Menu mode enumeration */ typedef enum { MENU_MODE_NONE, @@ -60,7 +57,7 @@ typedef enum { typedef struct { char *name; entry_type_t type; - int size; + int64_t size; } entry_t; /** @brief Menu Structure */ @@ -68,6 +65,7 @@ typedef struct { menu_mode_t mode; menu_mode_t next_mode; + const char *storage_prefix; settings_t settings; boot_params_t *boot_params; @@ -93,7 +91,7 @@ typedef struct { bool valid; bool reload; path_t *directory; - entry_t list[BROWSER_LIST_SIZE]; + entry_t *list; int entries; entry_t *entry; int selected; diff --git a/src/menu/mp3_player.c b/src/menu/mp3_player.c index 87109a3e..b41a170c 100644 --- a/src/menu/mp3_player.c +++ b/src/menu/mp3_player.c @@ -1,4 +1,6 @@ -#include +#include +#include + #include #include "mp3_player.h" @@ -18,19 +20,18 @@ /** @brief MP3 File Information Structure. */ typedef struct { bool loaded; - bool io_error; - - FIL fil; - FSIZE_t data_start; - int seek_predecode_frames; - - mp3dec_t dec; - mp3dec_frame_info_t info; + FILE *f; + size_t file_size; + size_t data_start; uint8_t buffer[16 * 1024]; uint8_t *buffer_ptr; size_t buffer_left; + mp3dec_t dec; + mp3dec_frame_info_t info; + + int seek_predecode_frames; float duration; float bitrate; @@ -48,9 +49,7 @@ static void mp3player_reset_decoder (void) { } static void mp3player_fill_buffer (void) { - UINT bytes_read; - - if (f_eof(&p->fil)) { + if (feof(p->f)) { return; } @@ -63,11 +62,7 @@ static void mp3player_fill_buffer (void) { p->buffer_ptr = p->buffer; } - if (f_read(&p->fil, p->buffer + p->buffer_left, sizeof(p->buffer) - p->buffer_left, &bytes_read) == FR_OK) { - p->buffer_left += bytes_read; - } else { - p->io_error = true; - } + p->buffer_left += fread(p->buffer + p->buffer_left, 1, sizeof(p->buffer) - p->buffer_left, p->f); } static void mp3player_wave_read (void *ctx, samplebuffer_t *sbuf, int wpos, int wlen, bool seeking) { @@ -109,7 +104,7 @@ static void mp3player_calculate_duration (int samples) { uint32_t frames; int delay, padding; - long data_size = (f_size(&p->fil) - p->data_start); + long data_size = (p->file_size - p->data_start); if (mp3dec_check_vbrtag((const uint8_t *) (p->buffer_ptr), p->info.frame_bytes, &frames, &delay, &padding) > 0) { p->duration = (frames * samples) / (float) (p->info.hz); p->bitrate = (data_size * 8) / p->duration; @@ -137,7 +132,6 @@ mp3player_err_t mp3player_init (void) { mp3player_reset_decoder(); p->loaded = false; - p->io_error = false; p->wave = (waveform_t) { .name = "mp3player", @@ -164,22 +158,32 @@ mp3player_err_t mp3player_load (char *path) { mp3player_unload(); } - if (f_open(&p->fil, strip_sd_prefix(path), FA_READ) != FR_OK) { + if ((p->f = fopen(path, "rb")) == NULL) { return MP3PLAYER_ERR_IO; } + setbuf(p->f, NULL); + + struct stat st; + if (fstat(fileno(p->f), &st)) { + fclose(p->f); + return MP3PLAYER_ERR_IO; + } + p->file_size = st.st_size; mp3player_reset_decoder(); - while (!(f_eof(&p->fil) && p->buffer_left == 0)) { + while (!(feof(p->f) && p->buffer_left == 0)) { mp3player_fill_buffer(); - if (p->io_error) { + if (ferror(p->f)) { + fclose(p->f); return MP3PLAYER_ERR_IO; } size_t id3v2_skip = mp3dec_skip_id3v2((const uint8_t *) (p->buffer_ptr), p->buffer_left); if (id3v2_skip > 0) { - if (f_lseek(&p->fil, f_tell(&p->fil) - p->buffer_left + id3v2_skip) != FR_OK) { + if (fseek(p->f, (-p->buffer_left) + id3v2_skip, SEEK_CUR)) { + fclose(p->f); return MP3PLAYER_ERR_IO; } mp3player_reset_decoder(); @@ -189,7 +193,7 @@ mp3player_err_t mp3player_load (char *path) { int samples = mp3dec_decode_frame(&p->dec, p->buffer_ptr, p->buffer_left, NULL, &p->info); if (samples > 0) { p->loaded = true; - p->data_start = f_tell(&p->fil) - p->buffer_left + p->info.frame_offset; + p->data_start = ftell(p->f) - p->buffer_left + p->info.frame_offset; p->buffer_ptr += p->info.frame_offset; p->buffer_left -= p->info.frame_offset; @@ -206,7 +210,7 @@ mp3player_err_t mp3player_load (char *path) { p->buffer_left -= p->info.frame_bytes; } - if (f_close(&p->fil) != FR_OK) { + if (fclose(p->f)) { return MP3PLAYER_ERR_IO; } @@ -217,12 +221,12 @@ void mp3player_unload (void) { mp3player_stop(); if (p->loaded) { p->loaded = false; - f_close(&p->fil); + fclose(p->f); } } mp3player_err_t mp3player_process (void) { - if (p->io_error) { + if (ferror(p->f)) { mp3player_unload(); return MP3PLAYER_ERR_IO; } @@ -239,7 +243,7 @@ bool mp3player_is_playing (void) { } bool mp3player_is_finished (void) { - return p->loaded && f_eof(&p->fil) && (p->buffer_left == 0); + return p->loaded && feof(p->f) && (p->buffer_left == 0); } mp3player_err_t mp3player_play (void) { @@ -248,8 +252,7 @@ mp3player_err_t mp3player_play (void) { } if (!mp3player_is_playing()) { if (mp3player_is_finished()) { - if (f_lseek(&p->fil, p->data_start) != FR_OK) { - p->io_error = true; + if (fseek(p->f, p->data_start, SEEK_SET)) { return MP3PLAYER_ERR_IO; } mp3player_reset_decoder(); @@ -292,25 +295,24 @@ mp3player_err_t mp3player_seek (int seconds) { return MP3PLAYER_OK; } - long position = ((long) (f_tell(&p->fil)) - p->buffer_left + bytes_to_move); + long position = (ftell(p->f) - p->buffer_left + bytes_to_move); if (position < (long) (p->data_start)) { position = p->data_start; } - if (f_lseek(&p->fil, position) != FR_OK) { - p->io_error = true; + if (fseek(p->f, position, SEEK_SET)) { return MP3PLAYER_ERR_IO; } mp3player_reset_decoder(); mp3player_fill_buffer(); - p->seek_predecode_frames = (position == p->data_start) ? 0 : SEEK_PREDECODE_FRAMES; - - if (p->io_error) { + if (ferror(p->f)) { return MP3PLAYER_ERR_IO; } + p->seek_predecode_frames = (position == p->data_start) ? 0 : SEEK_PREDECODE_FRAMES; + return MP3PLAYER_OK; } @@ -346,9 +348,9 @@ float mp3player_get_progress (void) { return 0.0f; } - FSIZE_t data_size = f_size(&p->fil) - p->data_start; - FSIZE_t data_consumed = f_tell(&p->fil) - p->buffer_left; - FSIZE_t data_position = (data_consumed > p->data_start) ? (data_consumed - p->data_start) : 0; + long data_size = p->file_size - p->data_start; + long data_consumed = ftell(p->f) - p->buffer_left; + long data_position = (data_consumed > p->data_start) ? (data_consumed - p->data_start) : 0; return data_position / (float) (data_size); } diff --git a/src/menu/path.c b/src/menu/path.c index 58638dea..59f622f2 100644 --- a/src/menu/path.c +++ b/src/menu/path.c @@ -19,7 +19,7 @@ static void path_resize (path_t *path, size_t min_length) { assert(path->buffer != NULL); } -static path_t *path_create (char *string) { +static path_t *path_create (const char *string) { if (string == NULL) { string = ""; } @@ -43,7 +43,7 @@ static void path_append (path_t *path, char *string) { } -path_t *path_init (char *prefix, char *string) { +path_t *path_init (const char *prefix, char *string) { path_t *path = path_create(prefix); size_t prefix_length = strlen(prefix); if ((prefix_length > 0) && (prefix[prefix_length - 1] == '/')) { diff --git a/src/menu/path.h b/src/menu/path.h index 3bf51950..df2672d2 100644 --- a/src/menu/path.h +++ b/src/menu/path.h @@ -20,7 +20,7 @@ typedef struct { } path_t; -path_t *path_init (char *prefix, char *string); +path_t *path_init (const char *prefix, char *string); void path_free (path_t *path); path_t *path_clone (path_t *string); path_t *path_clone_push (path_t *path, char *string); diff --git a/src/menu/png_decoder.c b/src/menu/png_decoder.c index 54d185d3..14dbf8ed 100644 --- a/src/menu/png_decoder.c +++ b/src/menu/png_decoder.c @@ -1,4 +1,5 @@ -#include +#include + #include #include "png_decoder.h" @@ -7,7 +8,7 @@ /** @brief PNG File Information Structure. */ typedef struct { - FIL fil; + FILE *f; spng_ctx *ctx; struct spng_ihdr ihdr; @@ -25,7 +26,7 @@ static png_decoder_t *decoder; static void png_decoder_deinit (bool free_image) { if (decoder != NULL) { - f_close(&decoder->fil); + fclose(decoder->f); if (decoder->ctx != NULL) { spng_ctx_free(decoder->ctx); } @@ -41,21 +42,6 @@ static void png_decoder_deinit (bool free_image) { } } -static int png_file_read (spng_ctx *ctx, void *user, void *dst_src, size_t length) { - UINT bytes_read = 0; - png_decoder_t *d = (png_decoder_t *) (user); - - if (f_read(&d->fil, dst_src, length, &bytes_read) != FR_OK) { - return SPNG_IO_ERROR; - } - - if (bytes_read != length) { - return SPNG_EOF; - } - - return SPNG_OK; -} - png_err_t png_decoder_start (char *path, int max_width, int max_height, png_callback_t *callback, void *callback_data) { if (decoder != NULL) { @@ -67,11 +53,13 @@ png_err_t png_decoder_start (char *path, int max_width, int max_height, png_call return PNG_ERR_OUT_OF_MEM; } - if (f_open(&decoder->fil, strip_sd_prefix(path), FA_READ) != FR_OK) { + if ((decoder->f = fopen(path, "rb")) == NULL) { png_decoder_deinit(false); return PNG_ERR_NO_FILE; } + setbuf(decoder->f, NULL); + if ((decoder->ctx = spng_ctx_new(SPNG_CTX_IGNORE_ADLER32)) == NULL) { png_decoder_deinit(false); return PNG_ERR_OUT_OF_MEM; @@ -87,7 +75,7 @@ png_err_t png_decoder_start (char *path, int max_width, int max_height, png_call return PNG_ERR_INT; } - if (spng_set_png_stream(decoder->ctx, png_file_read, decoder) != SPNG_OK) { + if (spng_set_png_file(decoder->ctx, decoder->f) != SPNG_OK) { png_decoder_deinit(false); return PNG_ERR_INT; } diff --git a/src/menu/rom_info.c b/src/menu/rom_info.c index 6a2223c5..b9711701 100644 --- a/src/menu/rom_info.c +++ b/src/menu/rom_info.c @@ -1,6 +1,6 @@ +#include #include -#include #include #include "boot/cic.h" @@ -829,7 +829,7 @@ static rom_err_t save_override (path_t *path, const char *id, int value, int def mini_free(ini); if (empty) { - if (file_delete(path_get(overrides_path))) { + if (remove(path_get(overrides_path))) { path_free(overrides_path); return ROM_ERR_IO; } @@ -912,21 +912,18 @@ rom_err_t rom_info_override_tv_type (path_t *path, rom_info_t *rom_info, rom_tv_ } rom_err_t rom_info_load (path_t *path, rom_info_t *rom_info) { - FIL fil; - UINT br; + FILE *f; rom_header_t rom_header; - if (f_open(&fil, strip_sd_prefix(path_get(path)), FA_READ) != FR_OK) { + if ((f = fopen(path_get(path), "rb")) == NULL) { return ROM_ERR_NO_FILE; } - if (f_read(&fil, &rom_header, sizeof(rom_header), &br) != FR_OK) { - f_close(&fil); + setbuf(f, NULL); + if (fread(&rom_header, sizeof(rom_header), 1, f) != 1) { + fclose(f); return ROM_ERR_IO; } - if (f_close(&fil) != FR_OK) { - return ROM_ERR_IO; - } - if (br != sizeof(rom_header)) { + if (fclose(f)) { return ROM_ERR_IO; } diff --git a/src/menu/settings.c b/src/menu/settings.c index 6f8dba03..3e046ed0 100644 --- a/src/menu/settings.c +++ b/src/menu/settings.c @@ -5,14 +5,12 @@ #include "utils/fs.h" -#ifndef SETTINGS_FILE_PATH -#define SETTINGS_FILE_PATH "sd:/menu/config.ini" -#endif +static char *settings_path = NULL; static settings_t init = { .pal60_enabled = false, - .hidden_files_enabled = false, + .show_protected_entries = false, .default_directory = "/", .use_saves_folder = true, @@ -23,15 +21,22 @@ static settings_t init = { }; +void settings_init (char *path) { + if (settings_path) { + free(settings_path); + } + settings_path = strdup(path); +} + void settings_load (settings_t *settings) { - if (!file_exists(SETTINGS_FILE_PATH)) { + if (!file_exists(settings_path)) { settings_save(&init); } - mini_t *ini = mini_try_load(SETTINGS_FILE_PATH); + mini_t *ini = mini_try_load(settings_path); settings->pal60_enabled = mini_get_bool(ini, "menu", "pal60", init.pal60_enabled); // TODO: consider changing file setting name - settings->hidden_files_enabled = mini_get_bool(ini, "menu", "show_hidden_files", init.hidden_files_enabled); + settings->show_protected_entries = mini_get_bool(ini, "menu", "show_protected_entries", init.show_protected_entries); settings->default_directory = strdup(mini_get_string(ini, "menu", "default_directory", init.default_directory)); settings->use_saves_folder = mini_get_bool(ini, "menu", "use_saves_folder", init.use_saves_folder); @@ -44,10 +49,10 @@ void settings_load (settings_t *settings) { } void settings_save (settings_t *settings) { - mini_t *ini = mini_create(SETTINGS_FILE_PATH); + mini_t *ini = mini_create(settings_path); mini_set_bool(ini, "menu", "pal60", settings->pal60_enabled); - mini_set_bool(ini, "menu", "show_hidden_files", settings->hidden_files_enabled); + mini_set_bool(ini, "menu", "show_protected_entries", settings->show_protected_entries); mini_set_string(ini, "menu", "default_directory", settings->default_directory); mini_set_bool(ini, "menu", "use_saves_folder", settings->use_saves_folder); diff --git a/src/menu/settings.h b/src/menu/settings.h index d2529998..e9931b05 100644 --- a/src/menu/settings.h +++ b/src/menu/settings.h @@ -13,8 +13,8 @@ typedef struct { /** @brief Use 60 Hz refresh rate on a PAL console */ bool pal60_enabled; - /** @brief Show files marked as hidden in the browser */ - bool hidden_files_enabled; + /** @brief Show files/directories that are filtered in the browser */ + bool show_protected_entries; /** @brief Default directory to navigate to when menu loads */ char *default_directory; @@ -33,6 +33,8 @@ typedef struct { } settings_t; +/** @brief Init settings path */ +void settings_init (char *path); /** @brief The settings to load */ void settings_load (settings_t *settings); /** @brief The settings to save */ diff --git a/src/menu/usb_comm.c b/src/menu/usb_comm.c index fd218cd3..743ddee2 100644 --- a/src/menu/usb_comm.c +++ b/src/menu/usb_comm.c @@ -2,8 +2,9 @@ // Main use of these functions is to aid menu development // (for example replace files on the SD card or reboot menu). -#include +#include #include + #include #include "usb_comm.h" @@ -73,15 +74,12 @@ static void command_reboot (menu_t *menu) { }; static void command_send_file (menu_t *menu) { - char path[256]; + FILE *f; + char buffer[256]; + uint8_t data[8192]; char length[8]; - FIL f; - int remaining; - uint8_t data[8192]; - UINT bytes_written; - - if (usb_comm_read_string(path, sizeof(path), ' ')) { + if (usb_comm_read_string(buffer, sizeof(buffer), ' ')) { return usb_comm_send_error("Invalid path argument\n"); } @@ -93,11 +91,16 @@ static void command_send_file (menu_t *menu) { return usb_comm_send_error("Invalid file length argument\n"); } - if (f_open(&f, path, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK) { + path_t *path = path_init(menu->storage_prefix, buffer); + + if ((f = fopen(path_get(path), "wb")) == NULL) { + path_free(path); return usb_comm_send_error("Couldn't create file\n"); } + setbuf(f, NULL); + path_free(path); - remaining = atoi(length); + int remaining = atoi(length); if (remaining > MAX_FILE_SIZE) { return usb_comm_send_error("File size too big\n"); @@ -106,18 +109,14 @@ static void command_send_file (menu_t *menu) { while (remaining > 0) { int block_size = MIN(remaining, sizeof(data)); usb_read(data, block_size); - if (f_write(&f, data, block_size, &bytes_written) != FR_OK) { - f_close(&f); - return usb_comm_send_error("Couldn't write data to the file\n"); - } - if (bytes_written != block_size) { - f_close(&f); + if (fwrite(data, 1, block_size, f) != block_size) { + fclose(f); return usb_comm_send_error("Couldn't write all required data to the file\n"); } remaining -= block_size; } - if (f_close(&f) != FR_OK) { + if (fclose(f)) { return usb_comm_send_error("Couldn't flush data to the file\n"); } diff --git a/src/menu/views/browser.c b/src/menu/views/browser.c index 00061d67..8cca0201 100644 --- a/src/menu/views/browser.c +++ b/src/menu/views/browser.c @@ -1,9 +1,8 @@ #include #include +#include #include -#include - #include "../fonts.h" #include "utils/fs.h" #include "views.h" @@ -17,6 +16,17 @@ static const char *image_extensions[] = { "png", NULL }; static const char *text_extensions[] = { "txt", "ini", "yml", "yaml", NULL }; static const char *music_extensions[] = { "mp3", NULL }; +static const char *hidden_paths[] = { + "/menu.bin", + "/menu", + "/N64FlashcartMenu.n64", + "/OS64.v64", + "/OS64P.v64", + "/sc64menu.n64", + "/System Volume Information", + NULL +}; + static int compare_entry (const void *pa, const void *pb) { entry_t *a = (entry_t *) (pa); @@ -61,74 +71,87 @@ static int compare_entry (const void *pa, const void *pb) { return strcasecmp((const char *) (a->name), (const char *) (b->name)); } -static bool load_directory (menu_t *menu) { - DIR dir; - FILINFO info; - +static void browser_list_free (menu_t *menu) { for (int i = menu->browser.entries - 1; i >= 0; i--) { free(menu->browser.list[i].name); } + free(menu->browser.list); + + menu->browser.list = NULL; menu->browser.entries = 0; - menu->browser.selected = -1; menu->browser.entry = NULL; + menu->browser.selected = -1; +} - if (f_opendir(&dir, strip_sd_prefix(path_get(menu->browser.directory))) != FR_OK) { - return true; +static bool load_directory (menu_t *menu) { + int result; + dir_t info; + + browser_list_free(menu); + + path_t *path = path_clone(menu->browser.directory); + + result = dir_findfirst(path_get(path), &info); + + while (result == 0) { + bool hide = false; + + if (!menu->settings.show_protected_entries) { + path_push(path, info.d_name); + + for (int i = 0; hidden_paths[i] != NULL; i++) { + if (strcmp(strip_fs_prefix(path_get(path)), hidden_paths[i]) == 0) { + hide = true; + break; + } + } + + path_pop(path); + } + + if (!hide) { + menu->browser.list = realloc(menu->browser.list, (menu->browser.entries + 1) * sizeof(entry_t)); + + entry_t *entry = &menu->browser.list[menu->browser.entries++]; + + entry->name = strdup(info.d_name); + if (!entry->name) { + path_free(path); + browser_list_free(menu); + return true; + } + + if (info.d_type == DT_DIR) { + entry->type = ENTRY_TYPE_DIR; + } else if (file_has_extensions(entry->name, rom_extensions)) { + entry->type = ENTRY_TYPE_ROM; + } else if (file_has_extensions(entry->name, disk_extensions)) { + entry->type = ENTRY_TYPE_DISK; + }else if (file_has_extensions(entry->name, emulator_extensions)) { + entry->type = ENTRY_TYPE_EMULATOR; + } else if (file_has_extensions(entry->name, save_extensions)) { + entry->type = ENTRY_TYPE_SAVE; + } else if (file_has_extensions(entry->name, image_extensions)) { + entry->type = ENTRY_TYPE_IMAGE; + } else if (file_has_extensions(entry->name, text_extensions)) { + entry->type = ENTRY_TYPE_TEXT; + } else if (file_has_extensions(entry->name, music_extensions)) { + entry->type = ENTRY_TYPE_MUSIC; + } else { + entry->type = ENTRY_TYPE_OTHER; + } + + entry->size = info.d_size; + } + + result = dir_findnext(path_get(path), &info); } - while (menu->browser.entries < BROWSER_LIST_SIZE) { - if (f_readdir(&dir, &info) != FR_OK) { - return true; - } + path_free(path); - size_t length = strlen(info.fname); - - if (length == 0) { - break; - } - - if (info.fattrib & AM_SYS) { - continue; - } - if ((info.fattrib & AM_HID) && !menu->settings.hidden_files_enabled) { - continue; - } - - entry_t *entry = &menu->browser.list[menu->browser.entries]; - - entry->name = strdup(info.fname); - if (!entry->name) { - f_closedir(&dir); - return true; - } - - if (info.fattrib & AM_DIR) { - 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, disk_extensions)) { - entry->type = ENTRY_TYPE_DISK; - }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)) { - entry->type = ENTRY_TYPE_IMAGE; - } else if (file_has_extensions(info.fname, text_extensions)) { - entry->type = ENTRY_TYPE_TEXT; - } else if (file_has_extensions(info.fname, music_extensions)) { - entry->type = ENTRY_TYPE_MUSIC; - } else { - entry->type = ENTRY_TYPE_OTHER; - } - - entry->size = info.fsize; - - menu->browser.entries += 1; - } - - if (f_closedir(&dir) != FR_OK) { + if (result < -1) { + browser_list_free(menu); return true; } @@ -205,18 +228,14 @@ static void show_properties (menu_t *menu, void *arg) { static void delete_entry (menu_t *menu, void *arg) { path_t *path = path_clone_push(menu->browser.directory, menu->browser.entry->name); - if (menu->browser.entry->type == ENTRY_TYPE_DIR) { - if (directory_delete(path_get(path))) { + if (remove(path_get(path))) { + if (menu->browser.entry->type == ENTRY_TYPE_DIR) { menu_show_error(menu, "Couldn't delete directory\nDirectory might not be empty"); - path_free(path); - return; - } - } else { - if (file_delete(path_get(path))) { + } else { menu_show_error(menu, "Couldn't delete file"); - path_free(path); - return; } + path_free(path); + return; } path_free(path); @@ -229,7 +248,7 @@ static void delete_entry (menu_t *menu, void *arg) { static void set_default_directory (menu_t *menu, void *arg) { free(menu->settings.default_directory); - menu->settings.default_directory = strdup(strip_sd_prefix(path_get(menu->browser.directory))); + menu->settings.default_directory = strdup(strip_fs_prefix(path_get(menu->browser.directory))); settings_save(&menu->settings); } @@ -388,7 +407,7 @@ void view_browser_init (menu_t *menu) { component_context_menu_init(&settings_context_menu); if (load_directory(menu)) { path_free(menu->browser.directory); - menu->browser.directory = path_init("sd:/", ""); + menu->browser.directory = path_init(menu->storage_prefix, ""); menu_show_error(menu, "Error while opening initial directory"); } else { menu->browser.valid = true; diff --git a/src/menu/views/file_info.c b/src/menu/views/file_info.c index 83506caa..f4819de8 100644 --- a/src/menu/views/file_info.c +++ b/src/menu/views/file_info.c @@ -1,4 +1,4 @@ -#include +#include #include "utils/fs.h" #include "views.h" @@ -16,7 +16,7 @@ static const char *dexdrive_extensions[] = { "mpk", NULL }; static const char *emulator_extensions[] = { "emu", NULL }; -static FILINFO info; +static struct stat st; static char *format_file_type (char *name, bool is_directory) { @@ -75,17 +75,14 @@ static void draw (menu_t *menu, surface_t *d) { "\n" "\n" " Size: %d bytes\n" - " Attributes: %s%s%s%s%s\n" + " Attributes: %s %s\n" "%s" - " Modified: %u-%02u-%02u %02u:%02u", - info.fsize, - (info.fattrib & AM_DIR) ? "Directory " : "File ", - (info.fattrib & AM_RDO) ? "| Read only " : "", - (info.fattrib & AM_SYS) ? "| System " : "", - (info.fattrib & AM_ARC) ? "| Archive " : "", - (info.fattrib & AM_HID) ? "| Hidden " : "", - format_file_type(info.fname, info.fattrib & AM_DIR), - (info.fdate >> 9) + 1980, info.fdate >> 5 & 0x0F, info.fdate & 0x1F, info.ftime >> 11, info.ftime >> 5 & 0x3F + " Modified: %s", + st.st_size, + S_ISDIR(st.st_mode) ? "Directory" : "File", + st.st_mode & S_IWUSR ? "" : "(Read only)", + format_file_type(menu->browser.entry->name, S_ISDIR(st.st_mode)), + ctime(&st.st_mtim.tv_sec) ); component_actions_bar_text_draw( @@ -101,7 +98,7 @@ static void draw (menu_t *menu, surface_t *d) { void view_file_info_init (menu_t *menu) { path_t *path = path_clone_push(menu->browser.directory, menu->browser.entry->name); - if (f_stat(strip_sd_prefix(path_get(path)), &info) != FR_OK) { + if (stat(path_get(path), &st)) { menu_show_error(menu, "Couldn't obtain file information"); } diff --git a/src/menu/views/load_disk.c b/src/menu/views/load_disk.c index 0a8ebb62..986a18b6 100644 --- a/src/menu/views/load_disk.c +++ b/src/menu/views/load_disk.c @@ -154,7 +154,7 @@ void view_load_disk_init (menu_t *menu) { menu->load.disk_path = path_clone_push(menu->browser.directory, menu->browser.entry->name); - disk_err_t err = disk_info_load(path_get(menu->load.disk_path), &menu->load.disk_info); + disk_err_t err = disk_info_load(menu->load.disk_path, &menu->load.disk_info); if (err != DISK_OK) { menu_show_error(menu, convert_error_message(err)); } diff --git a/src/menu/views/load_rom.c b/src/menu/views/load_rom.c index c5da6ed4..3e4e295c 100644 --- a/src/menu/views/load_rom.c +++ b/src/menu/views/load_rom.c @@ -334,7 +334,7 @@ void view_load_rom_init (menu_t *menu) { return; } - boxart = component_boxart_init(menu->load.rom_info.game_code); + boxart = component_boxart_init(menu->storage_prefix, menu->load.rom_info.game_code); component_context_menu_init(&options_context_menu); } diff --git a/src/menu/views/settings_editor.c b/src/menu/views/settings_editor.c index 7158a0b3..5e9d1854 100644 --- a/src/menu/views/settings_editor.c +++ b/src/menu/views/settings_editor.c @@ -34,15 +34,15 @@ static void draw (menu_t *menu, surface_t *d) { "\n" "To change the settings, please adjust them\n" "directly in the 'menu/config.ini' file.\n\n" - "pal60_enabled: %s\n" - "hidden_files_enabled: %s\n" - "default_directory: %s\n" - "use_saves_folder: %s\n" - "bgm_enabled: %s\n" - "sound_enabled: %s\n" - "rumble_enabled: %s\n", + "pal60_enabled: %s\n" + "show_protected_entries: %s\n" + "default_directory: %s\n" + "use_saves_folder: %s\n" + "bgm_enabled: %s\n" + "sound_enabled: %s\n" + "rumble_enabled: %s\n", format_switch(menu->settings.pal60_enabled), - format_switch(menu->settings.hidden_files_enabled), + format_switch(menu->settings.show_protected_entries), menu->settings.default_directory, format_switch(menu->settings.use_saves_folder), format_switch(menu->settings.bgm_enabled), diff --git a/src/menu/views/text_viewer.c b/src/menu/views/text_viewer.c index adef2df9..277eeba5 100644 --- a/src/menu/views/text_viewer.c +++ b/src/menu/views/text_viewer.c @@ -1,9 +1,13 @@ -#include +#include +#include #include "utils/fs.h" +#include "utils/utils.h" #include "views.h" -static char *file_content; + +static char *text; + static void process (menu_t *menu) { if (menu->actions.back) { @@ -18,20 +22,15 @@ static void draw (menu_t *menu, surface_t *d) { component_layout_draw(); - component_main_text_draw( - ALIGN_CENTER, VALIGN_TOP, - "TEXT VIEWER\n" - "\n" - ); - - component_main_text_draw( - ALIGN_LEFT, VALIGN_TOP, - "\n" - "\n" - "%s\n", - file_content - ); - + if (text) { + component_main_text_draw( + ALIGN_LEFT, VALIGN_TOP, + "%s\n", + text + ); + } else { + component_messagebox_draw("Text file is empty"); + } component_actions_bar_text_draw( ALIGN_LEFT, VALIGN_TOP, @@ -42,37 +41,62 @@ static void draw (menu_t *menu, surface_t *d) { rdpq_detach_show(); } +static void deinit (void) { + if (text) { + free(text); + text = NULL; + } +} + void view_text_viewer_init (menu_t *menu) { path_t *path = path_clone_push(menu->browser.directory, menu->browser.entry->name); - uint32_t file_size = file_get_size(path_get(path)); + FILE *f; - if (file_size > 1024) { // FIXME: this is just a placeholder until scrolling is implemented. - file_size = 1024; // For the moment, we just set it to that, since any more would be a waste. - } - - file_content = calloc(file_size, 1); - - // read file content - FIL fil; - UINT br; - - if (f_open(&fil, strip_sd_prefix(path_get(path)), FA_READ) != FR_OK) { - debugf("Error loading file\n"); - } - if (f_read(&fil, file_content, file_size, &br) != FR_OK) { - f_close(&fil); - debugf("Error loading file content\n"); - } - if (f_close(&fil) != FR_OK) { - debugf("Error closing file\n"); + if ((f = fopen(path_get(path), "r")) == NULL) { + path_free(path); + menu_show_error(menu, "Couldn't open text file"); + return; } path_free(path); + + struct stat st; + if (fstat(fileno(f), &st)) { + fclose(f); + menu_show_error(menu, "Couldn't get text file size"); + return; + } + + // TODO: Implement proper text file viewer with both vertical and horizontal scrolling + size_t size = MIN(st.st_size, 1024); + + if (size) { + if ((text = calloc(sizeof(char), size)) == NULL) { + fclose(f); + menu_show_error(menu, "Couldn't allocate memory for the text file contents"); + return; + } + + if (fread(text, size, 1, f) != 1) { + fclose(f); + menu_show_error(menu, "Couldn't read text file contents"); + return; + } + } + + if (fclose(f)) { + menu_show_error(menu, "Couldn't close text file"); + } } void view_text_viewer_display (menu_t *menu, surface_t *display) { process(menu); + draw(menu, display); + + if (menu->next_mode != MENU_MODE_TEXT_VIEWER) { + deinit(); + } } diff --git a/src/utils/fs.c b/src/utils/fs.c index 648ecfdf..41b69c48 100644 --- a/src/utils/fs.c +++ b/src/utils/fs.c @@ -1,145 +1,85 @@ +#include #include #include -#include - -#include +#include +#include #include "fs.h" #include "utils.h" -char *strip_sd_prefix (char *path) { - const char *prefix = "sd:/"; - +char *strip_fs_prefix (char *path) { + const char *prefix = ":/"; char *found = strstr(path, prefix); if (found) { - return found + strlen(prefix) - 1; + return (found + strlen(prefix) - 1); } - return path; } bool file_exists (char *path) { - FRESULT fr; - FILINFO fno; - - fr = f_stat(strip_sd_prefix(path), &fno); - - return ((fr == FR_OK) && (!(fno.fattrib & AM_DIR))); + struct stat st; + int error = stat(path, &st); + return ((error == 0) && S_ISREG(st.st_mode)); } - -size_t file_get_size (char *path) { - FILINFO fno; - - if (f_stat(strip_sd_prefix(path), &fno) != FR_OK) { - return 0; +int64_t file_get_size (char *path) { + struct stat st; + if (stat(path, &st)) { + return -1; } - - return (size_t) (fno.fsize); + return (int64_t) (st.st_size); } -bool file_delete (char *path) { - if (file_exists(path)) { - return (f_unlink(strip_sd_prefix(path)) != FR_OK); +bool file_allocate (char *path, size_t size) { + FILE *f; + if ((f = fopen(path, "wb")) == NULL) { + return true; + } + if (fseek(f, size, SEEK_SET)) { + fclose(f); + return true; + } + if (ftell(f) != size) { + fclose(f); + return true; + } + if (fclose(f)) { + return true; } return false; } -bool file_allocate (char *path, size_t size) { - FIL fil; - bool error = false; - - if (f_open(&fil, strip_sd_prefix(path), FA_WRITE | FA_CREATE_NEW) != FR_OK) { - return true; - } - - if (f_lseek(&fil, size) != FR_OK) { - error = true; - } - - if (f_tell(&fil) != size) { - error = true; - } - - if (f_close(&fil) != FR_OK) { - error = true; - } - - return error; -} - bool file_fill (char *path, uint8_t value) { - FIL fil; + FILE *f; bool error = false; uint8_t buffer[FS_SECTOR_SIZE * 8]; - FRESULT res; - UINT bytes_to_write; - UINT bytes_written; + size_t bytes_to_write; for (int i = 0; i < sizeof(buffer); i++) { buffer[i] = value; } - if (f_open(&fil, strip_sd_prefix(path), FA_WRITE) != FR_OK) { + if ((f = fopen(path, "rb+")) == NULL) { return true; } - for (int i = 0; i < f_size(&fil); i += sizeof(buffer)) { - bytes_to_write = MIN(f_size(&fil) - f_tell(&fil), sizeof(buffer)); - res = f_write(&fil, buffer, bytes_to_write, &bytes_written); - if ((res != FR_OK) || (bytes_to_write != bytes_written)) { + setbuf(f, NULL); + + fseek(f, 0, SEEK_END); + size_t size = ftell(f); + fseek(f, 0, SEEK_SET); + + for (size_t i = 0; i < size; i += sizeof(buffer)) { + bytes_to_write = MIN(size - ftell(f), sizeof(buffer)); + if (fwrite(buffer, 1, bytes_to_write, f) != bytes_to_write) { error = true; break; } } - if (f_tell(&fil) != f_size(&fil)) { - error = true; - } - - if (f_close(&fil) != FR_OK) { - error = true; - } - - return error; -} - -bool file_get_sectors (char *path, void (*callback) (uint32_t sector_count, uint32_t file_sector, uint32_t cluster_sector, uint32_t cluster_size)) { - FATFS *fs; - FIL fil; - bool error = false; - - if (!callback) { - return true; - } - - if (f_open(&fil, strip_sd_prefix(path), FA_READ) != FR_OK) { - return true; - } - - fs = fil.obj.fs; - - uint32_t sector_count = (ALIGN(f_size(&fil), FS_SECTOR_SIZE) / FS_SECTOR_SIZE); - - uint32_t cluster_sector = 0; - - for (int file_sector = 0; file_sector < sector_count; file_sector += fs->csize) { - if ((f_lseek(&fil, (file_sector * FS_SECTOR_SIZE) + (FS_SECTOR_SIZE / 2))) != FR_OK) { - error = true; - break; - } - uint32_t cluster = fil.clust; - if (cluster >= fs->n_fatent) { - error = true; - break; - } - cluster_sector = (fs->database + ((LBA_t) (fs->csize) * (cluster - 2))); - callback(sector_count, file_sector, cluster_sector, fs->csize); - } - - if (f_close(&fil) != FR_OK) { + if (fclose(f)) { error = true; } @@ -165,20 +105,9 @@ bool file_has_extensions (char *path, const char *extensions[]) { bool directory_exists (char *path) { - FRESULT fr; - FILINFO fno; - - fr = f_stat(strip_sd_prefix(path), &fno); - - return ((fr == FR_OK) && (fno.fattrib & AM_DIR)); -} - -bool directory_delete (char *path) { - if (directory_exists(path)) { - return (f_unlink(strip_sd_prefix(path)) != FR_OK); - } - - return false; + struct stat st; + int error = stat(path, &st); + return ((error == 0) && S_ISDIR(st.st_mode)); } bool directory_create (char *path) { @@ -188,8 +117,12 @@ bool directory_create (char *path) { return false; } - char *directory = strdup(strip_sd_prefix(path)); - char *separator = directory; + char *directory = strdup(path); + char *separator = strip_fs_prefix(directory); + + if (separator != directory) { + separator++; + } do { separator = strchr(separator, '/'); @@ -199,8 +132,7 @@ bool directory_create (char *path) { } if (directory[0] != '\0') { - FRESULT res = f_mkdir(directory); - if ((res != FR_OK) && (res != FR_EXIST)) { + if (mkdir(directory, 0777) && (errno != EEXIST)) { error = true; break; } diff --git a/src/utils/fs.h b/src/utils/fs.h index 7661ecb0..88f81a7a 100644 --- a/src/utils/fs.h +++ b/src/utils/fs.h @@ -10,18 +10,15 @@ #define FS_SECTOR_SIZE (512) -char *strip_sd_prefix (char *path); +char *strip_fs_prefix (char *path); bool file_exists (char *path); -size_t file_get_size (char *path); -bool file_delete (char *path); +int64_t file_get_size (char *path); bool file_allocate (char *path, size_t size); bool file_fill (char *path, uint8_t value); -bool file_get_sectors (char *path, void (*callback) (uint32_t sector_count, uint32_t file_sector, uint32_t cluster_sector, uint32_t cluster_size)); bool file_has_extensions (char *path, const char *extensions[]); bool directory_exists (char *path); -bool directory_delete (char *path); bool directory_create (char *path); From cbe69e26595240d6461ab63785bfd559d0188381 Mon Sep 17 00:00:00 2001 From: Mateusz Faderewski Date: Fri, 26 Apr 2024 23:03:15 +0200 Subject: [PATCH 17/20] Init BBFS on iQue --- src/flashcart/flashcart.c | 4 ++++ src/flashcart/flashcart.h | 1 + 2 files changed, 5 insertions(+) diff --git a/src/flashcart/flashcart.c b/src/flashcart/flashcart.c index c5aaaacb..0cb55e84 100644 --- a/src/flashcart/flashcart.c +++ b/src/flashcart/flashcart.c @@ -79,6 +79,7 @@ char *flashcart_convert_error_message (flashcart_err_t err) { case FLASHCART_OK: return "No error"; case FLASHCART_ERR_OUTDATED: return "Outdated flashcart firmware"; case FLASHCART_ERR_SD_CARD: return "Error during SD card initialization"; + case FLASHCART_ERR_BBFS: return "Error during iQue NAND initialization"; case FLASHCART_ERR_ARGS: return "Invalid argument passed to flashcart function"; case FLASHCART_ERR_LOAD: return "Error during loading data into flashcart"; case FLASHCART_ERR_INT: return "Internal flashcart error"; @@ -93,6 +94,9 @@ flashcart_err_t flashcart_init (const char **storage_prefix) { if (sys_bbplayer()) { // TODO: Add iQue callbacks *storage_prefix = "bbfs:/"; + if (bbfs_init()) { + return FLASHCART_ERR_BBFS; + } return FLASHCART_OK; } diff --git a/src/flashcart/flashcart.h b/src/flashcart/flashcart.h index b45a32d0..a481ecf2 100644 --- a/src/flashcart/flashcart.h +++ b/src/flashcart/flashcart.h @@ -17,6 +17,7 @@ typedef enum { FLASHCART_OK, FLASHCART_ERR_OUTDATED, FLASHCART_ERR_SD_CARD, + FLASHCART_ERR_BBFS, FLASHCART_ERR_ARGS, FLASHCART_ERR_LOAD, FLASHCART_ERR_INT, From fc410da89121cdf61c7471f760afbab2abfda1d8 Mon Sep 17 00:00:00 2001 From: Mateusz Faderewski Date: Fri, 26 Apr 2024 23:04:19 +0200 Subject: [PATCH 18/20] clang-format WIP --- .clang-format | 20 ++++++++++++++++++++ Makefile | 7 +++++++ src/menu/cart_load.c | 7 +++---- src/menu/rom_info.c | 3 +++ src/menu/views/browser.c | 5 +++-- 5 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..41c4bdbb --- /dev/null +++ b/.clang-format @@ -0,0 +1,20 @@ +BasedOnStyle: LLVM + +AlignAfterOpenBracket: BlockIndent +AlignEscapedNewlines: DontAlign +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortCaseLabelsOnASingleLine: true +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: InlineOnly +AlwaysBreakBeforeMultilineStrings: true +BinPackArguments: false +BinPackParameters: false +BreakBeforeBraces: Attach +ColumnLimit: 120 +IndentWidth: 4 +SpaceBeforeParens: Custom +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterFunctionDeclarationName: true + AfterFunctionDefinitionName: true +UseTab: Never diff --git a/Makefile b/Makefile index fed6ed87..dcebf60c 100644 --- a/Makefile +++ b/Makefile @@ -138,6 +138,13 @@ clean: @rm -rf ./$(BUILD_DIR) ./$(OUTPUT_DIR) .PHONY: clean +format: + @find ./$(SOURCE_DIR) \ + -path \./$(SOURCE_DIR)/libs -prune \ + -o -iname *.c -print \ + -o -iname *.h -print \ + | xargs clang-format -i + run: $(OUTPUT_DIR)/$(PROJECT_NAME).n64 ifeq ($(OS),Windows_NT) ./localdeploy.bat diff --git a/src/menu/cart_load.c b/src/menu/cart_load.c index 13ef7aca..f7b341ab 100644 --- a/src/menu/cart_load.c +++ b/src/menu/cart_load.c @@ -19,10 +19,9 @@ static bool is_64dd_connected (void) { - return ( - ((io_read(0x05000540) & 0x0000FFFF) == 0x0000) || - (io_read(0x06001010) == 0x2129FFF8) - ); + bool is_64dd_io_present = ((io_read(0x05000540) & 0x0000FFFF) == 0x0000); + bool is_64dd_ipl_present = (io_read(0x06001010) == 0x2129FFF8); + return (is_64dd_io_present || is_64dd_ipl_present); } static bool create_saves_subdirectory (path_t *path) { diff --git a/src/menu/rom_info.c b/src/menu/rom_info.c index b9711701..4713eee7 100644 --- a/src/menu/rom_info.c +++ b/src/menu/rom_info.c @@ -141,6 +141,8 @@ typedef struct { // List shamelessly stolen from https://github.com/ares-emulator/ares/blob/master/mia/medium/nintendo-64.cpp + +// clang-format off static const match_t database[] = { MATCH_HOMEBREW_HEADER("ED"), // Homebrew header (ED) @@ -576,6 +578,7 @@ static const match_t database[] = { MATCH_END, }; +// clang-format on static void fix_rom_header_endianness (rom_header_t *rom_header, rom_info_t *rom_info) { diff --git a/src/menu/views/browser.c b/src/menu/views/browser.c index 8cca0201..b5c46e0d 100644 --- a/src/menu/views/browser.c +++ b/src/menu/views/browser.c @@ -11,7 +11,8 @@ static const char *rom_extensions[] = { "z64", "n64", "v64", "rom", NULL }; static const char *disk_extensions[] = { "ndd", NULL }; static const char *emulator_extensions[] = { "nes", "sfc", "smc", "gb", "gbc", "sms", "gg", "sg", NULL }; -static const char *save_extensions[] = { "sav", NULL }; // TODO: "eep", "sra", "srm", "fla" could be used if transfered from different flashcarts. +// TODO: "eep", "sra", "srm", "fla" could be used if transfered from different flashcarts. +static const char *save_extensions[] = { "sav", NULL }; static const char *image_extensions[] = { "png", NULL }; static const char *text_extensions[] = { "txt", "ini", "yml", "yaml", NULL }; static const char *music_extensions[] = { "mp3", NULL }; @@ -24,7 +25,7 @@ static const char *hidden_paths[] = { "/OS64P.v64", "/sc64menu.n64", "/System Volume Information", - NULL + NULL, }; From a189e139b2b1ac146f418c29371f32d5c2cbbeed Mon Sep 17 00:00:00 2001 From: Mateusz Faderewski Date: Fri, 26 Apr 2024 23:04:39 +0200 Subject: [PATCH 19/20] Extend character set in the font --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index dcebf60c..f55c9532 100644 --- a/Makefile +++ b/Makefile @@ -86,7 +86,7 @@ FILESYSTEM = \ $(MINIZ_OBJS): N64_CFLAGS+=-DMINIZ_NO_TIME -fcompare-debug-second $(SPNG_OBJS): N64_CFLAGS+=-isystem $(SOURCE_DIR)/libs/miniz -DSPNG_USE_MINIZ -fcompare-debug-second -$(FILESYSTEM_DIR)/FiraMonoBold.font64: MKFONT_FLAGS+=-c 1 --size 16 -r 20-7F -r 2026-2026 --ellipsis 2026,1 +$(FILESYSTEM_DIR)/FiraMonoBold.font64: MKFONT_FLAGS+=-c 1 --size 16 -r 20-1FF -r 2026-2026 --ellipsis 2026,1 $(@info $(shell mkdir -p ./$(FILESYSTEM_DIR) &> /dev/null)) From 23af1748117653f01e84bda58f433122632315e1 Mon Sep 17 00:00:00 2001 From: Mateusz Faderewski Date: Sat, 27 Apr 2024 13:12:09 +0200 Subject: [PATCH 20/20] Implement vertical scrolling for text viewer --- src/menu/components.h | 2 +- src/menu/components/common.c | 10 +-- src/menu/components/constants.h | 12 +-- src/menu/components/file_list.c | 20 ++--- src/menu/views/browser.c | 1 + src/menu/views/error.c | 1 - src/menu/views/text_viewer.c | 150 +++++++++++++++++++++++--------- 7 files changed, 133 insertions(+), 63 deletions(-) diff --git a/src/menu/components.h b/src/menu/components.h index cff0f4c2..4925ee5b 100644 --- a/src/menu/components.h +++ b/src/menu/components.h @@ -24,7 +24,7 @@ void component_progressbar_draw (int x0, int y0, int x1, int y1, float progress) void component_seekbar_draw (float progress); void component_loader_draw (float position); void component_scrollbar_draw (int x, int y, int width, int height, int position, int items, int visible_items); -void component_file_list_scrollbar_draw (int position, int items, int visible_items); +void component_list_scrollbar_draw (int position, int items, int visible_items); void component_dialog_draw (int width, int height); void component_messagebox_draw (char *fmt, ...); void component_main_text_draw (rdpq_align_t align, rdpq_valign_t valign, char *fmt, ...); diff --git a/src/menu/components/common.c b/src/menu/components/common.c index 66db56eb..6bf708f8 100644 --- a/src/menu/components/common.c +++ b/src/menu/components/common.c @@ -80,12 +80,12 @@ void component_scrollbar_draw (int x, int y, int width, int height, int position } } -void component_file_list_scrollbar_draw (int position, int items, int visible_items) { +void component_list_scrollbar_draw (int position, int items, int visible_items) { component_scrollbar_draw( - FILE_LIST_SCROLLBAR_X, - FILE_LIST_SCROLLBAR_Y, - FILE_LIST_SCROLLBAR_WIDTH, - FILE_LIST_SCROLLBAR_HEIGHT, + LIST_SCROLLBAR_X, + LIST_SCROLLBAR_Y, + LIST_SCROLLBAR_WIDTH, + LIST_SCROLLBAR_HEIGHT, position, items, visible_items diff --git a/src/menu/components/constants.h b/src/menu/components/constants.h index 7522d461..f3eb3e39 100644 --- a/src/menu/components/constants.h +++ b/src/menu/components/constants.h @@ -77,19 +77,19 @@ #define BOXART_Y (LAYOUT_ACTIONS_SEPARATOR_Y - BOXART_HEIGHT - 24) /** @brief The scroll bar width. */ -#define FILE_LIST_SCROLLBAR_WIDTH (12) +#define LIST_SCROLLBAR_WIDTH (12) /** @brief The scroll bar height. */ -#define FILE_LIST_SCROLLBAR_HEIGHT (LAYOUT_ACTIONS_SEPARATOR_Y - OVERSCAN_HEIGHT) +#define LIST_SCROLLBAR_HEIGHT (LAYOUT_ACTIONS_SEPARATOR_Y - OVERSCAN_HEIGHT) /** @brief The scroll bar position on the X axis. */ -#define FILE_LIST_SCROLLBAR_X (VISIBLE_AREA_X1 - FILE_LIST_SCROLLBAR_WIDTH) +#define LIST_SCROLLBAR_X (VISIBLE_AREA_X1 - LIST_SCROLLBAR_WIDTH) /** @brief The scroll bar position on the Y axis. */ -#define FILE_LIST_SCROLLBAR_Y (VISIBLE_AREA_Y0) +#define LIST_SCROLLBAR_Y (VISIBLE_AREA_Y0) /** @brief The maximum amount of file list entries. */ -#define FILE_LIST_ENTRIES (20) +#define LIST_ENTRIES (20) /** @brief The maximum width available for a file list entry. */ #define FILE_LIST_MAX_WIDTH (480) -#define FILE_LIST_HIGHLIGHT_WIDTH (VISIBLE_AREA_X1 - VISIBLE_AREA_X0 - FILE_LIST_SCROLLBAR_WIDTH) +#define FILE_LIST_HIGHLIGHT_WIDTH (VISIBLE_AREA_X1 - VISIBLE_AREA_X0 - LIST_SCROLLBAR_WIDTH) #define FILE_LIST_HIGHLIGHT_X (VISIBLE_AREA_X0) /** @brief The default background colour. */ diff --git a/src/menu/components/file_list.c b/src/menu/components/file_list.c index 300b46ff..b00ef329 100644 --- a/src/menu/components/file_list.c +++ b/src/menu/components/file_list.c @@ -28,14 +28,14 @@ static int format_file_size (char *buffer, int64_t size) { void component_file_list_draw (entry_t *list, int entries, int selected) { int starting_position = 0; - if (entries > FILE_LIST_ENTRIES && selected >= (FILE_LIST_ENTRIES / 2)) { - starting_position = selected - (FILE_LIST_ENTRIES / 2); - if (starting_position >= entries - FILE_LIST_ENTRIES) { - starting_position = entries - FILE_LIST_ENTRIES; + if (entries > LIST_ENTRIES && selected >= (LIST_ENTRIES / 2)) { + starting_position = selected - (LIST_ENTRIES / 2); + if (starting_position >= entries - LIST_ENTRIES) { + starting_position = entries - LIST_ENTRIES; } } - component_file_list_scrollbar_draw(selected, entries, FILE_LIST_ENTRIES); + component_list_scrollbar_draw(selected, entries, LIST_ENTRIES); if (entries == 0) { component_main_text_draw( @@ -47,10 +47,10 @@ void component_file_list_draw (entry_t *list, int entries, int selected) { rdpq_paragraph_t *file_list_layout; rdpq_paragraph_t *layout; - size_t name_lengths[FILE_LIST_ENTRIES]; + size_t name_lengths[LIST_ENTRIES]; size_t total_length = 1; - for (int i = 0; i < FILE_LIST_ENTRIES; i++) { + for (int i = 0; i < LIST_ENTRIES; i++) { int entry_index = starting_position + i; if (entry_index >= entries) { @@ -76,7 +76,7 @@ void component_file_list_draw (entry_t *list, int entries, int selected) { file_list_layout ); - for (int i = 0; i < FILE_LIST_ENTRIES; i++) { + for (int i = 0; i < LIST_ENTRIES; i++) { int entry_index = starting_position + i; entry_t *entry = &list[entry_index]; @@ -134,7 +134,7 @@ void component_file_list_draw (entry_t *list, int entries, int selected) { rdpq_paragraph_builder_begin( &(rdpq_textparms_t) { - .width = VISIBLE_AREA_WIDTH - FILE_LIST_SCROLLBAR_WIDTH - (TEXT_MARGIN_HORIZONTAL * 2), + .width = VISIBLE_AREA_WIDTH - LIST_SCROLLBAR_WIDTH - (TEXT_MARGIN_HORIZONTAL * 2), .height = LAYOUT_ACTIONS_SEPARATOR_Y - VISIBLE_AREA_Y0 - (TEXT_MARGIN_VERTICAL * 2), .align = ALIGN_RIGHT, .wrap = WRAP_ELLIPSES, @@ -152,7 +152,7 @@ void component_file_list_draw (entry_t *list, int entries, int selected) { rdpq_paragraph_builder_span(file_size, format_file_size(file_size, entry->size)); } - if ((i + 1) == (starting_position + FILE_LIST_ENTRIES)) { + if ((i + 1) == (starting_position + LIST_ENTRIES)) { break; } diff --git a/src/menu/views/browser.c b/src/menu/views/browser.c index b5c46e0d..c8481f4a 100644 --- a/src/menu/views/browser.c +++ b/src/menu/views/browser.c @@ -230,6 +230,7 @@ static void delete_entry (menu_t *menu, void *arg) { path_t *path = path_clone_push(menu->browser.directory, menu->browser.entry->name); if (remove(path_get(path))) { + menu->browser.valid = false; if (menu->browser.entry->type == ENTRY_TYPE_DIR) { menu_show_error(menu, "Couldn't delete directory\nDirectory might not be empty"); } else { diff --git a/src/menu/views/error.c b/src/menu/views/error.c index 8e565a55..a5c0b84a 100644 --- a/src/menu/views/error.c +++ b/src/menu/views/error.c @@ -50,5 +50,4 @@ void view_error_display (menu_t *menu, surface_t *display) { void menu_show_error (menu_t *menu, char *error_message) { menu->next_mode = MENU_MODE_ERROR; menu->error_message = error_message; - menu->browser.valid = false; } diff --git a/src/menu/views/text_viewer.c b/src/menu/views/text_viewer.c index 277eeba5..6b688479 100644 --- a/src/menu/views/text_viewer.c +++ b/src/menu/views/text_viewer.c @@ -1,17 +1,66 @@ #include #include -#include "utils/fs.h" +#include "../components/constants.h" +#include "../fonts.h" #include "utils/utils.h" #include "views.h" -static char *text; +#define MAX_FILE_SIZE KiB(128) + + +typedef struct { + FILE *f; + char *contents; + size_t length; + int lines; + int current_line; + int offset; + bool vertical_scroll_possible; +} text_file_t; + +static text_file_t *text; + + +static void perform_vertical_scroll (int lines) { + if (!text->vertical_scroll_possible) { + return; + } + + int direction = (lines < 0) ? -1 : 1; + int next_offset = text->offset; + + for (int i = 0; i < abs(lines); i++) { + while (true) { + next_offset += direction; + if (next_offset <= 0) { + text->current_line = 0; + text->offset = 0; + return; + } + if (next_offset > text->length) { + return; + } + if (text->contents[next_offset - 1] == '\n') { + break; + } + } + text->current_line += direction; + text->offset = next_offset; + } +} static void process (menu_t *menu) { if (menu->actions.back) { menu->next_mode = MENU_MODE_BROWSER; + } else if (text) { + if (menu->actions.go_up) { + perform_vertical_scroll(menu->actions.go_fast ? -10 : -1); + } else if (menu->actions.go_down) { + perform_vertical_scroll(menu->actions.go_fast ? 10 : 1); + } } } @@ -22,20 +71,19 @@ static void draw (menu_t *menu, surface_t *d) { component_layout_draw(); - if (text) { - component_main_text_draw( - ALIGN_LEFT, VALIGN_TOP, - "%s\n", - text - ); - } else { - component_messagebox_draw("Text file is empty"); - } + component_main_text_draw( + ALIGN_LEFT, VALIGN_TOP, + "%s\n", + text->contents + text->offset + ); + + component_list_scrollbar_draw(text->current_line, text->lines, LIST_ENTRIES); component_actions_bar_text_draw( ALIGN_LEFT, VALIGN_TOP, - "\n" - "B: Back" + "^%02XUp / Down: Scroll^00\n" + "B: Back", + text->vertical_scroll_possible ? STL_DEFAULT : STL_GRAY ); rdpq_detach_show(); @@ -43,6 +91,12 @@ static void draw (menu_t *menu, surface_t *d) { static void deinit (void) { if (text) { + if (text->f) { + fclose(text->f); + } + if (text->contents) { + free(text->contents); + } free(text); text = NULL; } @@ -50,45 +104,61 @@ static void deinit (void) { void view_text_viewer_init (menu_t *menu) { - path_t *path = path_clone_push(menu->browser.directory, menu->browser.entry->name); - - FILE *f; - - if ((f = fopen(path_get(path), "r")) == NULL) { - path_free(path); - menu_show_error(menu, "Couldn't open text file"); - return; + if ((text = calloc(1, sizeof(text_file_t))) == NULL) { + return menu_show_error(menu, "Couldn't allocate memory for the text file"); } + path_t *path = path_clone_push(menu->browser.directory, menu->browser.entry->name); + text->f = fopen(path_get(path), "r"); path_free(path); + if (text->f == NULL) { + deinit(); + return menu_show_error(menu, "Couldn't open text file"); + } + struct stat st; - if (fstat(fileno(f), &st)) { - fclose(f); - menu_show_error(menu, "Couldn't get text file size"); - return; + if (fstat(fileno(text->f), &st)) { + deinit(); + return menu_show_error(menu, "Couldn't get text file size"); + } + text->length = st.st_size; + + if (text->length <= 0) { + deinit(); + return menu_show_error(menu, "Text file is empty"); } - // TODO: Implement proper text file viewer with both vertical and horizontal scrolling - size_t size = MIN(st.st_size, 1024); + if (text->length > MAX_FILE_SIZE) { + deinit(); + return menu_show_error(menu, "Text file is too big to be displayed"); + } - if (size) { - if ((text = calloc(sizeof(char), size)) == NULL) { - fclose(f); - menu_show_error(menu, "Couldn't allocate memory for the text file contents"); - return; - } + if ((text->contents = malloc((text->length + 1) * sizeof(char))) == NULL) { + deinit(); + return menu_show_error(menu, "Couldn't allocate memory for the text file contents"); + } - if (fread(text, size, 1, f) != 1) { - fclose(f); - menu_show_error(menu, "Couldn't read text file contents"); - return; + if (fread(text->contents, text->length, 1, text->f) != 1) { + deinit(); + return menu_show_error(menu, "Couldn't read text file contents"); + } + text->contents[text->length] = '\0'; + + if (fclose(text->f)) { + deinit(); + return menu_show_error(menu, "Couldn't close text file"); + } + text->f = NULL; + + text->lines = 1; + for (size_t i = 0; i < text->length; i++) { + if (text->contents[i] == '\n') { + text->lines += 1; } } - if (fclose(f)) { - menu_show_error(menu, "Couldn't close text file"); - } + text->vertical_scroll_possible = (text->lines > LIST_ENTRIES); } void view_text_viewer_display (menu_t *menu, surface_t *display) {