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/.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/.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 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 4a49daf9..9d8acdc1 100644 --- a/Makefile +++ b/Makefile @@ -8,10 +8,16 @@ 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 +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 = \ @@ -58,6 +64,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 \ @@ -66,6 +73,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 = \ @@ -81,7 +89,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)) @@ -91,6 +99,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 @@ -125,9 +136,18 @@ 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 +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 @@ -147,4 +167,6 @@ endif # test: # TODO: run tests +.FORCE: + -include $(DEPS) diff --git a/README.md b/README.md index c6c94a7c..a0112659 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 +* ED64 +* ED64P + ## Current (notable) menu features * Fully Open Source. @@ -33,6 +43,17 @@ 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 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 or saves created for a different ROM version or region 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. diff --git a/filesystem/.gitkeep b/filesystem/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/libdragon b/libdragon index 6323128d..8dd362b8 160000 --- a/libdragon +++ b/libdragon @@ -1 +1 @@ -Subproject commit 6323128d72fdf32dfaa134f40191ba72e5527076 +Subproject commit 8dd362b8d858ae60befe9d27bc55cf770b9b50af 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..4a23d4d2 100644 --- a/src/flashcart/64drive/64drive.c +++ b/src/flashcart/64drive/64drive.c @@ -73,6 +73,8 @@ 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; + case FLASHCART_FEATURE_USB: return true; default: return false; } } @@ -81,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); @@ -96,7 +98,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) { @@ -123,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; @@ -162,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; } @@ -249,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 7063125b..3d2ca934 100644 --- a/src/flashcart/flashcart.c +++ b/src/flashcart/flashcart.c @@ -8,15 +8,13 @@ #include "utils/utils.h" #include "flashcart.h" +#include "flashcart_utils.h" #include "64drive/64drive.h" #include "sc64/sc64.h" #include "ed64/ed64.h" -#define SAVE_WRITEBACK_MAX_SECTORS (256) - - static const size_t SAVE_SIZE[__FLASHCART_SAVE_TYPE_END] = { 0, 512, @@ -28,34 +26,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, }); @@ -70,9 +78,9 @@ 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_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"; @@ -81,15 +89,20 @@ 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:/"; + if (bbfs_init()) { + return FLASHCART_ERR_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 @@ -97,7 +110,8 @@ flashcart_err_t flashcart_init (void) { break; case CART_EDX: // Series X EverDrive-64 - // break; // FIXME: Commented out as required to fall through due to need of F/W 3.06 and UNFLoader (at minimum). + break; + case CART_ED: // Original EverDrive-64 flashcart = ed64_get_flashcart(); break; @@ -106,15 +120,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; } @@ -187,19 +208,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 0e97e807..a481ecf2 100644 --- a/src/flashcart/flashcart.h +++ b/src/flashcart/flashcart.h @@ -15,9 +15,9 @@ /** @brief Flashcart error enumeration */ typedef enum { FLASHCART_OK, - FLASHCART_ERR_NOT_DETECTED, FLASHCART_ERR_OUTDATED, FLASHCART_ERR_SD_CARD, + FLASHCART_ERR_BBFS, FLASHCART_ERR_ARGS, FLASHCART_ERR_LOAD, FLASHCART_ERR_INT, @@ -27,6 +27,8 @@ typedef enum { /** @brief List of optional supported flashcart features */ typedef enum { FLASHCART_FEATURE_64DD, + FLASHCART_FEATURE_RTC, + FLASHCART_FEATURE_USB, } flashcart_features_t; /** @brief Flashcart save type enumeration */ @@ -73,12 +75,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 a0878c9d..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; } @@ -260,6 +252,8 @@ 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; + case FLASHCART_FEATURE_USB: return true; default: return false; } } @@ -268,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); @@ -288,7 +282,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) { @@ -349,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; @@ -411,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; } @@ -437,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); @@ -450,7 +444,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) { @@ -474,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; @@ -496,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; @@ -557,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/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 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..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) { @@ -112,7 +111,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 +153,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 127c1d7b..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, ...); @@ -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; @@ -63,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..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 @@ -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/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/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/components/file_list.c b/src/menu/components/file_list.c index 15a438fc..b00ef329 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); } } @@ -24,29 +28,29 @@ static int format_file_size (char *buffer, int 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( ALIGN_LEFT, VALIGN_TOP, "^%02X** empty directory **", - STL_UNKNOWN + STL_GRAY ); } else { 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) { @@ -72,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]; @@ -80,12 +84,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); @@ -126,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, @@ -135,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]; @@ -144,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/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 27eccd09..f694ab80 100644 --- a/src/menu/fonts.c +++ b/src/menu/fonts.c @@ -1,21 +1,29 @@ #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_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); } -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 2e006efc..9fb00092 100644 --- a/src/menu/fonts.h +++ b/src/menu/fonts.h @@ -15,14 +15,15 @@ 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; -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 3e695f0d..beef676d 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -20,19 +20,23 @@ #include "views/views.h" -#define TV_TYPE_RAM *((uint32_t *) (0x80000300)) +#define MENU_DIRECTORY "/menu" +#define MENU_SETTINGS_FILE "config.ini" +#define MENU_CUSTOM_FONT_FILE "custom.font64" -#define CACHE_DIRECTORY "sd:/menu/cache" -#define BACKGROUND_CACHE "sd:/menu/cache/background.data" +#define MENU_CACHE_DIRECTORY "cache" +#define BACKGROUND_CACHE_FILE "background.data" -#define FRAMERATE_DIVIDER (2) -#define LAG_REPORT (false) +#define FRAMERATE_DIVIDER (2) +#define LAG_REPORT (false) 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; @@ -61,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)); @@ -70,36 +73,46 @@ 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(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(); 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,10 +124,16 @@ 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); + 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); @@ -133,30 +152,41 @@ 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_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 }, + { 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 }, + { 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 +198,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 +214,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..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, @@ -29,17 +26,18 @@ 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, MENU_MODE_RTC, + MENU_MODE_FLASHCART, MENU_MODE_LOAD_ROM, MENU_MODE_LOAD_DISK, MENU_MODE_LOAD_EMULATOR, MENU_MODE_ERROR, MENU_MODE_FAULT, MENU_MODE_BOOT, - __MENU_MODE_COUNT, } menu_mode_t; /** @brief File entry type enumeration */ @@ -51,6 +49,7 @@ typedef enum { ENTRY_TYPE_SAVE, ENTRY_TYPE_IMAGE, ENTRY_TYPE_MUSIC, + ENTRY_TYPE_TEXT, ENTRY_TYPE_OTHER, } entry_type_t; @@ -58,7 +57,7 @@ typedef enum { typedef struct { char *name; entry_type_t type; - int size; + int64_t size; } entry_t; /** @brief Menu Structure */ @@ -66,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; @@ -91,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 3dfbd7da..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; + 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; - uint8_t buffer[MAX_FREE_FORMAT_FRAME_SIZE]; - uint8_t *buffer_ptr; - size_t buffer_left; - + int seek_predecode_frames; float duration; float bitrate; @@ -48,9 +49,11 @@ static void mp3player_reset_decoder (void) { } static void mp3player_fill_buffer (void) { - UINT bytes_read; + if (feof(p->f)) { + return; + } - if (f_eof(&p->fil)) { + if (p->buffer_left >= ALIGN(MAX_FREE_FORMAT_FRAME_SIZE, FS_SECTOR_SIZE)) { return; } @@ -59,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) { @@ -105,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; @@ -133,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", @@ -160,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(); @@ -185,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; @@ -202,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; } @@ -213,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; } @@ -235,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) { @@ -244,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(); @@ -288,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; } @@ -342,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 951057b6..14dbf8ed 100644 --- a/src/menu/png_decoder.c +++ b/src/menu/png_decoder.c @@ -1,12 +1,14 @@ -#include +#include + #include #include "png_decoder.h" #include "utils/fs.h" + /** @brief PNG File Information Structure. */ typedef struct { - FIL fil; + FILE *f; spng_ctx *ctx; struct spng_ihdr ihdr; @@ -24,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); } @@ -40,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) { @@ -66,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; @@ -86,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 8d8f1f49..4713eee7 100644 --- a/src/menu/rom_info.c +++ b/src/menu/rom_info.c @@ -1,8 +1,9 @@ +#include #include -#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; @@ -139,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) @@ -574,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) { @@ -652,15 +657,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 +719,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 +770,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 (remove(path_get(overrides_path))) { + path_free(overrides_path); + return ROM_ERR_IO; + } } path_free(overrides_path); @@ -810,38 +843,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 +892,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,22 +907,26 @@ 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; + 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/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/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/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..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" @@ -12,10 +13,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; @@ -71,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"); } @@ -91,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"); @@ -104,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 75ab0a4a..c8481f4a 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" @@ -12,10 +11,23 @@ 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 }; +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); @@ -50,78 +62,97 @@ static int compare_entry (const void *pa, const void *pb) { 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; } } 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, 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; } @@ -135,6 +166,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,47 +222,35 @@ 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) { - if (directory_delete(path_get(path))) { + 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"); - 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); - 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))); + menu->settings.default_directory = strdup(strip_fs_prefix(path_get(menu->browser.directory))); settings_save(&menu->settings); } @@ -228,28 +263,18 @@ 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) }, + { .text = "Show cart info", .action = set_menu_next_mode, .arg = (void *) (MENU_MODE_FLASHCART) }, COMPONENT_CONTEXT_MENU_LIST_END, } }; @@ -300,6 +325,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; @@ -337,6 +365,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; } @@ -347,14 +376,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) { @@ -380,24 +409,18 @@ 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; } } + 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/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/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/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/load_disk.c b/src/menu/views/load_disk.c index 14d9d80b..986a18b6 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; + } } @@ -142,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 6beeaa0f..3e4e295c 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,21 +318,23 @@ 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); + 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 c624ba1a..5e9d1854 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; @@ -33,23 +34,22 @@ 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", - format_boolean_type(menu->settings.pal60_enabled), - format_boolean_type(menu->settings.hidden_files_enabled), + "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.show_protected_entries), 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/text_viewer.c b/src/menu/views/text_viewer.c new file mode 100644 index 00000000..6b688479 --- /dev/null +++ b/src/menu/views/text_viewer.c @@ -0,0 +1,172 @@ +#include +#include + +#include "../components/constants.h" +#include "../fonts.h" +#include "utils/utils.h" +#include "views.h" + + +#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); + } + } +} + +static void draw (menu_t *menu, surface_t *d) { + rdpq_attach(d, NULL); + + component_background_draw(); + + component_layout_draw(); + + 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, + "^%02XUp / Down: Scroll^00\n" + "B: Back", + text->vertical_scroll_possible ? STL_DEFAULT : STL_GRAY + ); + + rdpq_detach_show(); +} + +static void deinit (void) { + if (text) { + if (text->f) { + fclose(text->f); + } + if (text->contents) { + free(text->contents); + } + free(text); + text = NULL; + } +} + + +void view_text_viewer_init (menu_t *menu) { + 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(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"); + } + + if (text->length > MAX_FILE_SIZE) { + deinit(); + return menu_show_error(menu, "Text file is too big to be displayed"); + } + + 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->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; + } + } + + text->vertical_scroll_possible = (text->lines > LIST_ENTRIES); +} + +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/menu/views/views.h b/src/menu/views/views.h index 2b7e0660..8da900dc 100644 --- a/src/menu/views/views.h +++ b/src/menu/views/views.h @@ -23,33 +23,39 @@ 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); +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); 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_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); + +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); 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);