diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 9c9a1878..effc134b 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -6,7 +6,7 @@ "mounts": [ "source=n64flashcartmenu-bashhistory,target=/commandhistory,type=volume" ], - "postCreateCommand": "git submodule update --init && cd ./libdragon && ./build.sh", + "postCreateCommand": "git submodule update --init && cd ./libdragon && make clobber -j && make libdragon tools -j && make install tools-install -j", "customizations": { "vscode": { "extensions": [ @@ -24,4 +24,4 @@ } } } -} +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index de80dacb..bc2d5ddf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,17 @@ +# Ignore editor specific config folders/files /.vscode + +# Ignore compilation result directories /build -/filesystem /output + +# Ignore generated files in the libdragon FS +/filesystem/FiraMonoBold.font64 + +# Ignore external development tools /tools/* -# There should never be ROMs uploaded, but just incase, make sure they are excluded. +# Ignore any N64 ROM **/*.n64 +**/*.v64 **/*.z64 diff --git a/.gitmodules b/.gitmodules index f830561a..af1ca991 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,7 @@ [submodule "libdragon"] path = libdragon url = https://github.com/DragonMinded/libdragon - branch = unstable + branch = preview ignore = dirty [submodule "src/libs/libspng"] path = src/libs/libspng diff --git a/Makefile b/Makefile index 7c0ec049..fed6ed87 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,11 @@ BUILD_TIMESTAMP = "$(shell TZ='UTC' date "+%Y-%m-%d %H:%M:%S %:z")" include $(N64_INST)/include/n64.mk +N64_ROM_SAVETYPE = none +N64_ROM_RTC = 1 +N64_ROM_REGIONFREE = 1 +N64_ROM_REGION = E + N64_CFLAGS += -iquote $(SOURCE_DIR) -iquote $(ASSETS_DIR) -I $(SOURCE_DIR)/libs -flto=auto $(FLAGS) SRCS = \ @@ -128,7 +133,9 @@ all: $(OUTPUT_DIR)/$(PROJECT_NAME).n64 64drive ed64 ed64-clone sc64 .PHONY: all clean: - @rm -rf ./$(BUILD_DIR) ./$(FILESYSTEM_DIR) ./$(OUTPUT_DIR) + @rm -f ./$(FILESYSTEM) + @find ./$(FILESYSTEM_DIR) -type d -empty -delete + @rm -rf ./$(BUILD_DIR) ./$(OUTPUT_DIR) .PHONY: clean run: $(OUTPUT_DIR)/$(PROJECT_NAME).n64 diff --git a/filesystem/.gitkeep b/filesystem/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/libdragon b/libdragon index c99272b5..8dd362b8 160000 --- a/libdragon +++ b/libdragon @@ -1 +1 @@ -Subproject commit c99272b552d398e479a3de6ed9b6563a710b3cc2 +Subproject commit 8dd362b8d858ae60befe9d27bc55cf770b9b50af diff --git a/src/flashcart/64drive/64drive.c b/src/flashcart/64drive/64drive.c index 9b0b8b08..4a23d4d2 100644 --- a/src/flashcart/64drive/64drive.c +++ b/src/flashcart/64drive/64drive.c @@ -83,11 +83,11 @@ static flashcart_err_t d64_load_rom (char *rom_path, flashcart_progress_callback FIL fil; UINT br; - if (f_open(&fil, strip_sd_prefix(rom_path), FA_READ) != FR_OK) { + if (f_open(&fil, strip_fs_prefix(rom_path), FA_READ) != FR_OK) { return FLASHCART_ERR_LOAD; } - fix_file_size(&fil); + fatfs_fix_file_size(&fil); size_t rom_size = f_size(&fil); @@ -125,11 +125,11 @@ static flashcart_err_t d64_load_file (char *file_path, uint32_t rom_offset, uint FIL fil; UINT br; - if (f_open(&fil, strip_sd_prefix(file_path), FA_READ) != FR_OK) { + if (f_open(&fil, strip_fs_prefix(file_path), FA_READ) != FR_OK) { return FLASHCART_ERR_LOAD; } - fix_file_size(&fil); + fatfs_fix_file_size(&fil); size_t file_size = f_size(&fil) - file_offset; @@ -164,7 +164,7 @@ static flashcart_err_t d64_load_save (char *save_path) { FIL fil; UINT br; - if (f_open(&fil, strip_sd_prefix(save_path), FA_READ) != FR_OK) { + if (f_open(&fil, strip_fs_prefix(save_path), FA_READ) != FR_OK) { return FLASHCART_ERR_LOAD; } @@ -251,7 +251,13 @@ static flashcart_err_t d64_set_save_type (flashcart_save_type_t save_type) { return FLASHCART_OK; } -static flashcart_err_t d64_set_save_writeback (uint32_t *sectors) { +static flashcart_err_t d64_set_save_writeback (char *save_path) { + uint32_t sectors[SAVE_WRITEBACK_MAX_SECTORS] __attribute__((aligned(8))); + + if (fatfs_get_file_sectors(save_path, sectors, ADDRESS_TYPE_MEM, SAVE_WRITEBACK_MAX_SECTORS)) { + return FLASHCART_ERR_LOAD; + } + if (d64_ll_write_save_writeback_lba_list(sectors)) { return FLASHCART_ERR_INT; } diff --git a/src/flashcart/flashcart.c b/src/flashcart/flashcart.c index bd2e4163..c5aaaacb 100644 --- a/src/flashcart/flashcart.c +++ b/src/flashcart/flashcart.c @@ -8,14 +8,12 @@ #include "utils/utils.h" #include "flashcart.h" +#include "flashcart_utils.h" #include "64drive/64drive.h" #include "sc64/sc64.h" -#define SAVE_WRITEBACK_MAX_SECTORS (256) - - static const size_t SAVE_SIZE[__FLASHCART_SAVE_TYPE_END] = { 0, 512, @@ -27,34 +25,44 @@ static const size_t SAVE_SIZE[__FLASHCART_SAVE_TYPE_END] = { KiB(128), }; -static uint32_t save_writeback_sectors[SAVE_WRITEBACK_MAX_SECTORS] __attribute__((aligned(8))); +static flashcart_err_t dummy_init (void) { + return FLASHCART_OK; +} -static void save_writeback_sectors_callback (uint32_t sector_count, uint32_t file_sector, uint32_t cluster_sector, uint32_t cluster_size) { - for (uint32_t i = 0; i < cluster_size; i++) { - uint32_t offset = file_sector + i; - uint32_t sector = cluster_sector + i; - - if ((offset > SAVE_WRITEBACK_MAX_SECTORS) || (offset > sector_count)) { - return; - } - - save_writeback_sectors[offset] = sector; +static bool dummy_has_feature (flashcart_features_t feature) { + switch (feature) { + default: + return false; } } +static flashcart_err_t dummy_load_rom (char *rom_path, flashcart_progress_callback_t *progress) { + return FLASHCART_OK; +} -static flashcart_err_t dummy_init (void) { +static flashcart_err_t dummy_load_file (char *file_path, uint32_t rom_offset, uint32_t file_offset) { + return FLASHCART_OK; +} + +static flashcart_err_t dummy_load_save (char *save_path) { + return FLASHCART_OK; +} + +static flashcart_err_t dummy_set_save_type (flashcart_save_type_t save_type) { return FLASHCART_OK; } static flashcart_t *flashcart = &((flashcart_t) { .init = dummy_init, .deinit = NULL, - .load_rom = NULL, - .load_file = NULL, - .load_save = NULL, - .set_save_type = NULL, + .has_feature = dummy_has_feature, + .load_rom = dummy_load_rom, + .load_file = dummy_load_file, + .load_save = dummy_load_save, + .load_64dd_ipl = NULL, + .load_64dd_disk = NULL, + .set_save_type = dummy_set_save_type, .set_save_writeback = NULL, }); @@ -69,7 +77,6 @@ static flashcart_t *flashcart = &((flashcart_t) { char *flashcart_convert_error_message (flashcart_err_t err) { switch (err) { case FLASHCART_OK: return "No error"; - case FLASHCART_ERR_NOT_DETECTED: return "No flashcart hardware was detected"; case FLASHCART_ERR_OUTDATED: return "Outdated flashcart firmware"; case FLASHCART_ERR_SD_CARD: return "Error during SD card initialization"; case FLASHCART_ERR_ARGS: return "Invalid argument passed to flashcart function"; @@ -80,15 +87,17 @@ char *flashcart_convert_error_message (flashcart_err_t err) { } } -flashcart_err_t flashcart_init (void) { +flashcart_err_t flashcart_init (const char **storage_prefix) { flashcart_err_t err; - bool sd_card_initialized = debug_init_sdfs("sd:/", -1); + if (sys_bbplayer()) { + // TODO: Add iQue callbacks + *storage_prefix = "bbfs:/"; + return FLASHCART_OK; + } -#ifndef NDEBUG - // NOTE: Some flashcarts doesn't have USB port, can't throw error here - debug_init_usblog(); -#endif + *storage_prefix = "sd:/"; + bool sd_card_initialized = debug_init_sdfs(*storage_prefix, -1); switch (cart_type) { case CART_CI: // 64drive @@ -96,6 +105,8 @@ flashcart_err_t flashcart_init (void) { break; case CART_EDX: // Series X EverDrive-64 + break; + case CART_ED: // Original EverDrive-64 break; @@ -103,15 +114,22 @@ flashcart_err_t flashcart_init (void) { flashcart = sc64_get_flashcart(); break; - default: - return FLASHCART_ERR_NOT_DETECTED; + default: // Probably emulator + *storage_prefix = "rom:/"; + debug_init_isviewer(); + break; } +#ifndef NDEBUG + // NOTE: Some flashcarts doesn't have USB port, can't throw error here + debug_init_usblog(); +#endif + if ((err = flashcart->init()) != FLASHCART_OK) { return err; } - if (!sd_card_initialized) { + if ((cart_type != CART_NULL) && (!sd_card_initialized)) { return FLASHCART_ERR_SD_CARD; } @@ -184,19 +202,11 @@ flashcart_err_t flashcart_load_save (char *save_path, flashcart_save_type_t save return err; } - if (flashcart->set_save_writeback) { - for (int i = 0; i < SAVE_WRITEBACK_MAX_SECTORS; i++) { - save_writeback_sectors[i] = 0; - } - if (file_get_sectors(save_path, save_writeback_sectors_callback)) { - return FLASHCART_ERR_LOAD; - } - if ((err = flashcart->set_save_writeback(save_writeback_sectors)) != FLASHCART_OK) { - return err; - } + if (!flashcart->set_save_writeback) { + return FLASHCART_OK; } - return FLASHCART_OK; + return flashcart->set_save_writeback(save_path); } flashcart_err_t flashcart_load_64dd_ipl (char *ipl_path, flashcart_progress_callback_t *progress) { diff --git a/src/flashcart/flashcart.h b/src/flashcart/flashcart.h index 001516bb..b45a32d0 100644 --- a/src/flashcart/flashcart.h +++ b/src/flashcart/flashcart.h @@ -15,7 +15,6 @@ /** @brief Flashcart error enumeration */ typedef enum { FLASHCART_OK, - FLASHCART_ERR_NOT_DETECTED, FLASHCART_ERR_OUTDATED, FLASHCART_ERR_SD_CARD, FLASHCART_ERR_ARGS, @@ -75,12 +74,12 @@ typedef struct { /** @brief The flashcart set save type function */ flashcart_err_t (*set_save_type) (flashcart_save_type_t save_type); /** @brief The flashcart set save writeback function */ - flashcart_err_t (*set_save_writeback) (uint32_t *sectors); + flashcart_err_t (*set_save_writeback) (char *save_path); } flashcart_t; char *flashcart_convert_error_message (flashcart_err_t err); -flashcart_err_t flashcart_init (void); +flashcart_err_t flashcart_init (const char **storage_prefix); flashcart_err_t flashcart_deinit (void); bool flashcart_has_feature (flashcart_features_t feature); flashcart_err_t flashcart_load_rom (char *rom_path, bool byte_swap, flashcart_progress_callback_t *progress); diff --git a/src/flashcart/flashcart_utils.c b/src/flashcart/flashcart_utils.c index a89230c3..3eb13605 100644 --- a/src/flashcart/flashcart_utils.c +++ b/src/flashcart/flashcart_utils.c @@ -5,13 +5,6 @@ #include "utils/utils.h" -void fix_file_size (FIL *fil) { - // HACK: Align file size to the SD sector size to prevent FatFs from doing partial sector load. - // We are relying on direct transfer from SD to SDRAM without CPU intervention. - // Sending some extra bytes isn't an issue here. - fil->obj.objsize = ALIGN(f_size(fil), FS_SECTOR_SIZE); -} - void pi_dma_read_data (void *src, void *dst, size_t length) { data_cache_hit_writeback_invalidate(dst, length); dma_read_async(dst, (uint32_t) (src), length); @@ -27,3 +20,68 @@ void pi_dma_write_data (void *src, void *dst, size_t length) { dma_write_raw_async(src, (uint32_t) (dst), length); dma_wait(); } + +void fatfs_fix_file_size (FIL *fil) { + // HACK: Align file size to the SD sector size to prevent FatFs from doing partial sector load. + // We are relying on direct transfer from SD to SDRAM without CPU intervention. + // Sending some extra bytes isn't an issue here. + fil->obj.objsize = ALIGN(f_size(fil), FS_SECTOR_SIZE); +} + +bool fatfs_get_file_sectors (char *path, uint32_t *address, address_type_t type, uint32_t max_sectors) { + FATFS *fs; + FIL fil; + bool error = false; + + if (f_open(&fil, strip_fs_prefix(path), FA_READ) != FR_OK) { + return true; + } + + fatfs_fix_file_size(&fil); + + fs = fil.obj.fs; + + uint32_t sector_count = MIN(f_size(&fil) / FS_SECTOR_SIZE, max_sectors); + + for (uint32_t file_sector = 0; file_sector < sector_count; file_sector += fs->csize) { + if ((f_lseek(&fil, (file_sector * FS_SECTOR_SIZE) + (FS_SECTOR_SIZE / 2))) != FR_OK) { + error = true; + break; + } + + uint32_t cluster = fil.clust; + + if (cluster >= fs->n_fatent) { + error = true; + break; + } + + uint32_t cluster_sector = (fs->database + ((LBA_t) (fs->csize) * (cluster - 2))); + + for (uint32_t i = 0; i < fs->csize; i++) { + uint32_t sector = (cluster_sector + i); + + if ((file_sector + i) >= sector_count) { + break; + } + + switch (type) { + case ADDRESS_TYPE_MEM: + *address = sector; + break; + + case ADDRESS_TYPE_PI: + io_write((uint32_t) (address), sector); + break; + } + + address++; + } + } + + if (f_close(&fil) != FR_OK) { + error = true; + } + + return error; +} diff --git a/src/flashcart/flashcart_utils.h b/src/flashcart/flashcart_utils.h index 38d1b589..c0b5d6d8 100644 --- a/src/flashcart/flashcart_utils.h +++ b/src/flashcart/flashcart_utils.h @@ -8,12 +8,26 @@ #define FLASHCART_UTILS_H__ +#include +#include +#include + #include -void fix_file_size (FIL *fil); +#define SAVE_WRITEBACK_MAX_SECTORS (256) + + +typedef enum { + ADDRESS_TYPE_MEM, + ADDRESS_TYPE_PI, +} address_type_t; + + void pi_dma_read_data (void *src, void *dst, size_t length); void pi_dma_write_data (void *src, void *dst, size_t length); +void fatfs_fix_file_size (FIL *fil); +bool fatfs_get_file_sectors (char *path, uint32_t *address, address_type_t address_type, uint32_t max_sectors); #endif diff --git a/src/flashcart/sc64/sc64.c b/src/flashcart/sc64/sc64.c index b7febf16..1b105fb0 100644 --- a/src/flashcart/sc64/sc64.c +++ b/src/flashcart/sc64/sc64.c @@ -75,9 +75,6 @@ static const uint8_t vzone_to_pzone[DISK_TYPES][DISK_ZONES] = { static const uint8_t rom_zones[DISK_TYPES] = { 5, 7, 9, 11, 13, 15, 16 }; -static uint32_t disk_sectors_start_offset; - - static flashcart_err_t load_to_flash (FIL *fil, void *address, size_t size, UINT *br, flashcart_progress_callback_t *progress) { size_t erase_block_size; UINT bp; @@ -110,23 +107,6 @@ static flashcart_err_t load_to_flash (FIL *fil, void *address, size_t size, UINT return FLASHCART_OK; } -static uint32_t disk_sectors_start (uint32_t offset) { - disk_sectors_start_offset = offset; - return (offset + (DISK_MAX_SECTORS * sizeof(uint32_t))); -} - -static void disk_sectors_callback (uint32_t sector_count, uint32_t file_sector, uint32_t cluster_sector, uint32_t cluster_size) { - for (uint32_t i = 0; i < cluster_size; i++) { - uint32_t offset = file_sector + i; - uint32_t sector = cluster_sector + i; - - if ((offset > DISK_MAX_SECTORS) || (offset > sector_count)) { - return; - } - - io_write(ROM_ADDRESS + disk_sectors_start_offset + (offset * sizeof(uint32_t)), sector); - } -} static bool disk_zone_track_is_bad (uint8_t zone, uint8_t track, flashcart_disk_parameters_t *disk_parameters) { for (int i = 0; i < DISK_BAD_TRACKS_PER_ZONE; i++) { @@ -153,7 +133,7 @@ static void disk_set_thb_mapping (uint32_t offset, uint16_t track, uint8_t head, io_write(ROM_ADDRESS + offset + (index * sizeof(uint32_t)), mapping); } -static uint32_t disk_load_thb_table (uint32_t offset, flashcart_disk_parameters_t *disk_parameters) { +static void disk_load_thb_table (flashcart_disk_parameters_t *disk_parameters, uint32_t *thb_table_offset, uint32_t *current_offset) { int file_offset = 0; uint16_t lba = 0; @@ -175,15 +155,15 @@ static uint32_t disk_load_thb_table (uint32_t offset, flashcart_disk_parameters_ uint16_t track = track_offset + zone_track; if (disk_zone_track_is_bad(pzone, zone_track, disk_parameters)) { - disk_set_thb_mapping(offset, track, head, 0, false, false, 0); - disk_set_thb_mapping(offset, track, head, 1, false, false, 0); + disk_set_thb_mapping(*current_offset, track, head, 0, false, false, 0); + disk_set_thb_mapping(*current_offset, track, head, 1, false, false, 0); continue; } for (uint8_t block = 0; block < DISK_BLOCKS; block += 1) { bool valid = !(disk_system_lba_is_bad(lba, disk_parameters)); bool writable = (vzone >= rom_zones[disk_parameters->disk_type]); - disk_set_thb_mapping(offset, track, head, (starting_block ^ block), valid, writable, file_offset); + disk_set_thb_mapping(*current_offset, track, head, (starting_block ^ block), valid, writable, file_offset); file_offset += (sector_length * DISK_SECTORS_PER_BLOCK); lba += 1; } @@ -192,7 +172,19 @@ static uint32_t disk_load_thb_table (uint32_t offset, flashcart_disk_parameters_ } } - return (offset + (DISK_TRACKS * DISK_HEADS * DISK_BLOCKS * sizeof(uint32_t))); + *thb_table_offset = *current_offset; + *current_offset += (DISK_TRACKS * DISK_HEADS * DISK_BLOCKS * sizeof(uint32_t)); +} + +static bool disk_load_sector_table (char *path, uint32_t *sector_table_offset, uint32_t *current_offset) { + if (fatfs_get_file_sectors(path, (uint32_t *) (ROM_ADDRESS + *current_offset), ADDRESS_TYPE_PI, DISK_MAX_SECTORS)) { + return true; + } + + *sector_table_offset = *current_offset; + *current_offset += (DISK_MAX_SECTORS * sizeof(uint32_t)); + + return false; } @@ -270,11 +262,11 @@ static flashcart_err_t sc64_load_rom (char *rom_path, flashcart_progress_callbac FIL fil; UINT br; - if (f_open(&fil, strip_sd_prefix(rom_path), FA_READ) != FR_OK) { + if (f_open(&fil, strip_fs_prefix(rom_path), FA_READ) != FR_OK) { return FLASHCART_ERR_LOAD; } - fix_file_size(&fil); + fatfs_fix_file_size(&fil); size_t rom_size = f_size(&fil); @@ -351,11 +343,11 @@ static flashcart_err_t sc64_load_file (char *file_path, uint32_t rom_offset, uin FIL fil; UINT br; - if (f_open(&fil, strip_sd_prefix(file_path), FA_READ) != FR_OK) { + if (f_open(&fil, strip_fs_prefix(file_path), FA_READ) != FR_OK) { return FLASHCART_ERR_LOAD; } - fix_file_size(&fil); + fatfs_fix_file_size(&fil); size_t file_size = f_size(&fil) - file_offset; @@ -413,7 +405,7 @@ static flashcart_err_t sc64_load_save (char *save_path) { FIL fil; UINT br; - if (f_open(&fil, strip_sd_prefix(save_path), FA_READ) != FR_OK) { + if (f_open(&fil, strip_fs_prefix(save_path), FA_READ) != FR_OK) { return FLASHCART_ERR_LOAD; } @@ -439,11 +431,11 @@ static flashcart_err_t sc64_load_64dd_ipl (char *ipl_path, flashcart_progress_ca FIL fil; UINT br; - if (f_open(&fil, strip_sd_prefix(ipl_path), FA_READ) != FR_OK) { + if (f_open(&fil, strip_fs_prefix(ipl_path), FA_READ) != FR_OK) { return FLASHCART_ERR_LOAD; } - fix_file_size(&fil); + fatfs_fix_file_size(&fil); size_t ipl_size = f_size(&fil); @@ -476,19 +468,17 @@ static flashcart_err_t sc64_load_64dd_ipl (char *ipl_path, flashcart_progress_ca } static flashcart_err_t sc64_load_64dd_disk (char *disk_path, flashcart_disk_parameters_t *disk_parameters) { - sc64_disk_mapping_t mapping = { .count = 0 }; - uint32_t next_mapping_offset = DISK_MAPPING_ROM_OFFSET; + sc64_disk_mapping_t mapping; + uint32_t mapping_offset = DISK_MAPPING_ROM_OFFSET; + sc64_drive_type_t drive_type = (disk_parameters->development_drive ? DRIVE_TYPE_DEVELOPMENT : DRIVE_TYPE_RETAIL); // TODO: Support loading multiple disks - // LOOP START - mapping.disks[mapping.count].thb_table = next_mapping_offset; - mapping.disks[mapping.count].sector_table = disk_load_thb_table(mapping.disks[mapping.count].thb_table, disk_parameters); - next_mapping_offset = disk_sectors_start(mapping.disks[mapping.count].sector_table); - if (file_get_sectors(disk_path, disk_sectors_callback)) { + for (mapping.count = 0; mapping.count < 1; mapping.count++) { + disk_load_thb_table(disk_parameters++, &mapping.disks[mapping.count].thb_table, &mapping_offset); + if (disk_load_sector_table(disk_path++, &mapping.disks[mapping.count].sector_table, &mapping_offset)) { return FLASHCART_ERR_LOAD; } - mapping.count += 1; - // LOOP END + } if (mapping.count == 0) { return FLASHCART_ERR_ARGS; @@ -498,8 +488,6 @@ static flashcart_err_t sc64_load_64dd_disk (char *disk_path, flashcart_disk_para return FLASHCART_ERR_INT; } - sc64_drive_type_t drive_type = disk_parameters->development_drive ? DRIVE_TYPE_DEVELOPMENT : DRIVE_TYPE_RETAIL; - const struct { sc64_cfg_id_t id; uint32_t value; @@ -559,7 +547,13 @@ static flashcart_err_t sc64_set_save_type (flashcart_save_type_t save_type) { return FLASHCART_OK; } -static flashcart_err_t sc64_set_save_writeback (uint32_t *sectors) { +static flashcart_err_t sc64_set_save_writeback (char *save_path) { + uint32_t sectors[SAVE_WRITEBACK_MAX_SECTORS] __attribute__((aligned(8))); + + if (fatfs_get_file_sectors(save_path, sectors, ADDRESS_TYPE_MEM, SAVE_WRITEBACK_MAX_SECTORS)) { + return FLASHCART_ERR_LOAD; + } + pi_dma_write_data(sectors, SC64_BUFFERS->BUFFER, 1024); if (sc64_ll_writeback_enable(SC64_BUFFERS->BUFFER) != SC64_OK) { diff --git a/src/libs/miniz b/src/libs/miniz index 18795fa6..16413c21 160000 --- a/src/libs/miniz +++ b/src/libs/miniz @@ -1 +1 @@ -Subproject commit 18795fa61e590521381ba9e1fa4a4ab362b095f6 +Subproject commit 16413c213de38e703d883006193734e8b1178d5d diff --git a/src/menu/cart_load.c b/src/menu/cart_load.c index 32bf4a28..13ef7aca 100644 --- a/src/menu/cart_load.c +++ b/src/menu/cart_load.c @@ -112,7 +112,7 @@ cart_load_err_t cart_load_64dd_ipl_and_disk (menu_t *menu, flashcart_progress_ca return CART_LOAD_ERR_EXP_PAK_NOT_FOUND; } - path_t *path = path_init("sd:/", DDIPL_LOCATION); + path_t *path = path_init(menu->storage_prefix, DDIPL_LOCATION); flashcart_disk_parameters_t disk_parameters; disk_parameters.development_drive = (menu->load.disk_info.region == DISK_REGION_DEVELOPMENT); @@ -154,7 +154,7 @@ cart_load_err_t cart_load_64dd_ipl_and_disk (menu_t *menu, flashcart_progress_ca } cart_load_err_t cart_load_emulator (menu_t *menu, cart_load_emu_type_t emu_type, flashcart_progress_callback_t progress) { - path_t *path = path_init("sd:/", EMU_LOCATION); + path_t *path = path_init(menu->storage_prefix, EMU_LOCATION); flashcart_save_type_t save_type = FLASHCART_SAVE_TYPE_NONE; uint32_t emulated_rom_offset = 0x200000; diff --git a/src/menu/components.h b/src/menu/components.h index a8cecb51..cff0f4c2 100644 --- a/src/menu/components.h +++ b/src/menu/components.h @@ -64,7 +64,7 @@ typedef struct { surface_t *image; } component_boxart_t; -component_boxart_t *component_boxart_init (char *game_code); +component_boxart_t *component_boxart_init (const char *storage_prefix, char *game_code); void component_boxart_free (component_boxart_t *b); void component_boxart_draw (component_boxart_t *b); diff --git a/src/menu/components/background.c b/src/menu/components/background.c index ac838098..45119107 100644 --- a/src/menu/components/background.c +++ b/src/menu/components/background.c @@ -1,4 +1,4 @@ -#include +#include #include #include "../components.h" @@ -31,22 +31,21 @@ static void load_from_cache (component_background_t *c) { return; } - FIL fil; - UINT bytes_read; + FILE *f; - if (f_open(&fil, strip_sd_prefix(c->cache_location), FA_READ) != FR_OK) { + if ((f = fopen(c->cache_location, "rb")) == NULL) { return; } cache_metadata_t cache_metadata; - if ((f_read(&fil, &cache_metadata, sizeof(cache_metadata), &bytes_read) != FR_OK) || (bytes_read != sizeof(cache_metadata))) { - f_close(&fil); + if (fread(&cache_metadata, sizeof(cache_metadata), 1, f) != 1) { + fclose(f); return; } if (cache_metadata.magic != CACHE_METADATA_MAGIC || cache_metadata.width > DISPLAY_WIDTH || cache_metadata.height > DISPLAY_HEIGHT) { - f_close(&fil); + fclose(f); return; } @@ -57,17 +56,17 @@ static void load_from_cache (component_background_t *c) { surface_free(c->image); free(c->image); c->image = NULL; - f_close(&fil); + fclose(f); return; } - if ((f_read(&fil, c->image->buffer, cache_metadata.size, &bytes_read) != FR_OK) || (bytes_read != cache_metadata.size)) { + if (fread(c->image->buffer, cache_metadata.size, 1, f) != 1) { surface_free(c->image); free(c->image); c->image = NULL; } - f_close(&fil); + fclose(f); } static void save_to_cache (component_background_t *c) { @@ -75,9 +74,9 @@ static void save_to_cache (component_background_t *c) { return; } - FIL fil; - UINT bytes_written; - if (f_open(&fil, strip_sd_prefix(c->cache_location), FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) { + FILE *f; + + if ((f = fopen(c->cache_location, "wb")) == NULL) { return; } @@ -88,10 +87,10 @@ static void save_to_cache (component_background_t *c) { .size = (c->image->height * c->image->stride), }; - f_write(&fil, &cache_metadata, sizeof(cache_metadata), &bytes_written); - f_write(&fil, c->image->buffer, cache_metadata.size, &bytes_written); + fwrite(&cache_metadata, sizeof(cache_metadata), 1, f); + fwrite(c->image->buffer, cache_metadata.size, 1, f); - f_close(&fil); + fclose(f); } static void prepare_background (component_background_t *c) { @@ -166,7 +165,7 @@ static void display_list_free (void *arg) { void component_background_init (char *cache_location) { if (!background) { background = calloc(1, sizeof(component_background_t)); - background->cache_location = cache_location; + background->cache_location = strdup(cache_location); load_from_cache(background); prepare_background(background); } @@ -183,6 +182,9 @@ void component_background_free (void) { rdpq_call_deferred(display_list_free, background->image_display_list); background->image_display_list = NULL; } + if (background->cache_location) { + free(background->cache_location); + } free(background); background = NULL; } diff --git a/src/menu/components/boxart.c b/src/menu/components/boxart.c index 6ed3826a..54121b6e 100644 --- a/src/menu/components/boxart.c +++ b/src/menu/components/boxart.c @@ -6,9 +6,8 @@ #include "constants.h" #include "utils/fs.h" -#ifndef BOXART_DIRECTORY -#define BOXART_DIRECTORY "sd:/menu/boxart" -#endif + +#define BOXART_DIRECTORY "menu/boxart" static void png_decoder_callback (png_err_t err, surface_t *decoded_image, void *callback_data) { @@ -18,25 +17,38 @@ static void png_decoder_callback (png_err_t err, surface_t *decoded_image, void } -component_boxart_t *component_boxart_init (char *game_code) { - component_boxart_t *b = calloc(1, sizeof(component_boxart_t)); +component_boxart_t *component_boxart_init (const char *storage_prefix, char *game_code) { + component_boxart_t *b; + char file_name[8]; - if (b) { - b->loading = true; - char *path = alloca(strlen(BOXART_DIRECTORY) + 1 + 7 + 1); - - // TODO: This is bad, we should only check for 3 letter codes - sprintf(path, "%s/%.3s.png", BOXART_DIRECTORY, game_code); - if (png_decoder_start(path, BOXART_WIDTH, BOXART_HEIGHT, png_decoder_callback, b) != PNG_OK) { - sprintf(path, "%s/%.2s.png", BOXART_DIRECTORY, &game_code[1]); - if (png_decoder_start(path, BOXART_WIDTH, BOXART_HEIGHT, png_decoder_callback, b) != PNG_OK) { - free(b); - b = NULL; - } - } + if ((b = calloc(1, sizeof(component_boxart_t))) == NULL) { + return NULL; } - return b; + b->loading = true; + + path_t *path = path_init(storage_prefix, BOXART_DIRECTORY); + + sprintf(file_name, "%.3s.png", game_code); + path_push(path, file_name); + if (png_decoder_start(path_get(path), BOXART_WIDTH, BOXART_HEIGHT, png_decoder_callback, b) == PNG_OK) { + path_free(path); + return b; + } + path_pop(path); + + // TODO: This is bad, we should only check for 3 letter codes + sprintf(file_name, "%.2s.png", game_code + 1); + path_push(path, file_name); + if (png_decoder_start(path_get(path), BOXART_WIDTH, BOXART_HEIGHT, png_decoder_callback, b) == PNG_OK) { + path_free(path); + return b; + } + + path_free(path); + free(b); + + return NULL; } void component_boxart_free (component_boxart_t *b) { diff --git a/src/menu/components/common.c b/src/menu/components/common.c index 5440cb08..66db56eb 100644 --- a/src/menu/components/common.c +++ b/src/menu/components/common.c @@ -121,6 +121,10 @@ void component_messagebox_draw (char *fmt, ...) { .wrap = WRAP_WORD }, FNT_DEFAULT, formatted, ¶graph_nbytes); + if (formatted != buffer) { + free(formatted); + } + component_dialog_draw( paragraph->bbox.x1 - paragraph->bbox.x0 + MESSAGEBOX_MARGIN, paragraph->bbox.y1 - paragraph->bbox.y0 + MESSAGEBOX_MARGIN @@ -154,6 +158,10 @@ void component_main_text_draw (rdpq_align_t align, rdpq_valign_t valign, char *f formatted, nbytes ); + + if (formatted != buffer) { + free(formatted); + } } void component_actions_bar_text_draw (rdpq_align_t align, rdpq_valign_t valign, char *fmt, ...) { @@ -179,4 +187,8 @@ void component_actions_bar_text_draw (rdpq_align_t align, rdpq_valign_t valign, formatted, nbytes ); + + if (formatted != buffer) { + free(formatted); + } } diff --git a/src/menu/components/file_list.c b/src/menu/components/file_list.c index 1de3cd19..300b46ff 100644 --- a/src/menu/components/file_list.c +++ b/src/menu/components/file_list.c @@ -8,15 +8,19 @@ static const char *dir_prefix = "/"; -static int format_file_size (char *buffer, int size) { - if (size < 8 * 1024) { - return sprintf(buffer, "%d B", size); +static int format_file_size (char *buffer, int64_t size) { + if (size < 0) { + return sprintf(buffer, "unknown"); + } else if (size == 0) { + return sprintf(buffer, "empty"); + } else if (size < 8 * 1024) { + return sprintf(buffer, "%lld B", size); } else if (size < 8 * 1024 * 1024) { - return sprintf(buffer, "%d kB", size / 1024); + return sprintf(buffer, "%lld kB", size / 1024); } else if (size < 1 * 1024 * 1024 * 1024) { - return sprintf(buffer, "%d MB", size / 1024 / 1024); + return sprintf(buffer, "%lld MB", size / 1024 / 1024); } else { - return sprintf(buffer, "%d GB", size / 1024 / 1024 / 1024); + return sprintf(buffer, "%lld GB", size / 1024 / 1024 / 1024); } } @@ -139,7 +143,7 @@ void component_file_list_draw (entry_t *list, int entries, int selected) { NULL ); - char file_size[8]; + char file_size[16]; for (int i = starting_position; i < entries; i++) { entry_t *entry = &list[i]; diff --git a/src/menu/disk_info.c b/src/menu/disk_info.c index 32680a3b..5c2c7388 100644 --- a/src/menu/disk_info.c +++ b/src/menu/disk_info.c @@ -1,6 +1,6 @@ -#include #include #include +#include #include #include "disk_info.h" @@ -39,18 +39,14 @@ static const int disk_id_lbas[DISK_ID_LBA_COUNT] = { }; -static bool load_system_area_lba (FIL *fil, int lba, uint8_t *buffer) { - UINT bytes_read; +static bool load_system_area_lba (FILE *f, int lba, uint8_t *buffer) { if (lba >= SYSTEM_AREA_LBA_COUNT) { return true; } - if (f_lseek(fil, SYSTEM_AREA_LBA_LENGTH * lba) != FR_OK) { + if (fseek(f, SYSTEM_AREA_LBA_LENGTH * lba, SEEK_SET)) { return true; } - if (f_read(fil, buffer, SYSTEM_AREA_LBA_LENGTH, &bytes_read) != FR_OK) { - return true; - } - if (bytes_read != SYSTEM_AREA_LBA_LENGTH) { + if (fread(buffer, SYSTEM_AREA_LBA_LENGTH, 1, f) != 1) { return true; } return false; @@ -113,7 +109,7 @@ static void update_bad_system_area_lbas (disk_info_t *disk_info) { } } -static disk_err_t load_and_verify_system_data_lba (FIL *fil, disk_info_t *disk_info) { +static disk_err_t load_and_verify_system_data_lba (FILE *f, disk_info_t *disk_info) { uint8_t buffer[SYSTEM_AREA_LBA_LENGTH]; int sector_length; @@ -122,7 +118,7 @@ static disk_err_t load_and_verify_system_data_lba (FIL *fil, disk_info_t *disk_i for (int i = 0; i < SYSTEM_DATA_LBA_COUNT; i++) { int lba = system_data_lbas[i]; - if (load_system_area_lba(fil, lba, buffer)) { + if (load_system_area_lba(f, lba, buffer)) { return DISK_ERR_IO; } @@ -155,7 +151,7 @@ static disk_err_t load_and_verify_system_data_lba (FIL *fil, disk_info_t *disk_i return valid_system_data_lba_found ? DISK_OK : DISK_ERR_INVALID; } -static disk_err_t load_and_verify_disk_id_lba (FIL *fil, disk_info_t *disk_info) { +static disk_err_t load_and_verify_disk_id_lba (FILE *f, disk_info_t *disk_info) { uint8_t buffer[SYSTEM_AREA_LBA_LENGTH]; bool valid_disk_id_lba_found = false; @@ -163,7 +159,7 @@ static disk_err_t load_and_verify_disk_id_lba (FIL *fil, disk_info_t *disk_info) for (int i = 0; i < DISK_ID_LBA_COUNT; i++) { int lba = disk_id_lbas[i]; - if (load_system_area_lba(fil, lba, buffer)) { + if (load_system_area_lba(f, lba, buffer)) { return DISK_ERR_IO; } @@ -180,29 +176,27 @@ static disk_err_t load_and_verify_disk_id_lba (FIL *fil, disk_info_t *disk_info) } -disk_err_t disk_info_load (char *path, disk_info_t *disk_info) { - FIL fil; +disk_err_t disk_info_load (path_t *path, disk_info_t *disk_info) { + FILE *f; disk_err_t err; for (int i = 0; i < SYSTEM_AREA_LBA_COUNT; i++) { disk_info->bad_system_area_lbas[i] = false; } - if (f_open(&fil, strip_sd_prefix(path), FA_READ) != FR_OK) { + if ((f = fopen(path_get(path), "rb")) == NULL) { return DISK_ERR_NO_FILE; } - - if ((err = load_and_verify_system_data_lba(&fil, disk_info)) != DISK_OK) { - f_close(&fil); + setbuf(f, NULL); + if ((err = load_and_verify_system_data_lba(f, disk_info)) != DISK_OK) { + fclose(f); return err; } - - if ((err = load_and_verify_disk_id_lba(&fil, disk_info)) != DISK_OK) { - f_close(&fil); + if ((err = load_and_verify_disk_id_lba(f, disk_info)) != DISK_OK) { + fclose(f); return err; } - - if (f_close(&fil) != FR_OK) { + if (fclose(f)) { return DISK_ERR_IO; } diff --git a/src/menu/disk_info.h b/src/menu/disk_info.h index ae35be69..fe5175ec 100644 --- a/src/menu/disk_info.h +++ b/src/menu/disk_info.h @@ -11,6 +11,9 @@ #include #include +#include "path.h" + + /** @brief Disk state enumeration. */ typedef enum { DISK_OK, @@ -49,7 +52,7 @@ typedef struct { } disk_info_t; -disk_err_t disk_info_load (char *path, disk_info_t *disk_info); +disk_err_t disk_info_load (path_t *path, disk_info_t *disk_info); #endif diff --git a/src/menu/fonts.c b/src/menu/fonts.c index e8eccc95..f694ab80 100644 --- a/src/menu/fonts.c +++ b/src/menu/fonts.c @@ -1,10 +1,17 @@ #include #include "fonts.h" +#include "utils/fs.h" -static void load_default_font (void) { - rdpq_font_t *default_font = rdpq_font_load("rom:/FiraMonoBold.font64"); +static void load_default_font (char *custom_font_path) { + char *font_path = "rom:/FiraMonoBold.font64"; + + if (custom_font_path && file_exists(custom_font_path)) { + font_path = custom_font_path; + } + + rdpq_font_t *default_font = rdpq_font_load(font_path); rdpq_font_style(default_font, STL_DEFAULT, &((rdpq_fontstyle_t) { .color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF) })); rdpq_font_style(default_font, STL_GREEN, &((rdpq_fontstyle_t) { .color = RGBA32(0x70, 0xFF, 0x70, 0xFF) })); @@ -17,6 +24,6 @@ static void load_default_font (void) { } -void fonts_init (void) { - load_default_font(); +void fonts_init (char *custom_font_path) { + load_default_font(custom_font_path); } diff --git a/src/menu/fonts.h b/src/menu/fonts.h index 2ddade59..9fb00092 100644 --- a/src/menu/fonts.h +++ b/src/menu/fonts.h @@ -23,7 +23,7 @@ typedef enum { } menu_font_style_t; -void fonts_init (void); +void fonts_init (char *custom_font_path); #endif diff --git a/src/menu/menu.c b/src/menu/menu.c index 1b8832cb..beef676d 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -20,12 +20,15 @@ #include "views/views.h" -#define MENU_DIRECTORY "sd:/menu" -#define CACHE_DIRECTORY "sd:/menu/cache" -#define BACKGROUND_CACHE "sd:/menu/cache/background.data" +#define MENU_DIRECTORY "/menu" +#define MENU_SETTINGS_FILE "config.ini" +#define MENU_CUSTOM_FONT_FILE "custom.font64" -#define FRAMERATE_DIVIDER (2) -#define LAG_REPORT (false) +#define MENU_CACHE_DIRECTORY "cache" +#define BACKGROUND_CACHE_FILE "background.data" + +#define FRAMERATE_DIVIDER (2) +#define LAG_REPORT (false) static menu_t *menu; @@ -62,7 +65,6 @@ static void menu_init (boot_params_t *boot_params) { rdpq_init(); dfs_init(DFS_DEFAULT_LOCATION); - fonts_init(); sound_init_default(); menu = calloc(1, sizeof(menu_t)); @@ -71,31 +73,39 @@ static void menu_init (boot_params_t *boot_params) { menu->mode = MENU_MODE_NONE; menu->next_mode = MENU_MODE_STARTUP; - menu->flashcart_err = flashcart_init(); + menu->flashcart_err = flashcart_init(&menu->storage_prefix); if (menu->flashcart_err != FLASHCART_OK) { menu->next_mode = MENU_MODE_FAULT; } - menu->error_message = NULL; + path_t *path = path_init(menu->storage_prefix, MENU_DIRECTORY); - directory_create(MENU_DIRECTORY); + directory_create(path_get(path)); + path_push(path, MENU_SETTINGS_FILE); + settings_init(path_get(path)); settings_load(&menu->settings); + path_pop(path); - directory_create(CACHE_DIRECTORY); + path_push(path, MENU_CUSTOM_FONT_FILE); + fonts_init(path_get(path)); + path_pop(path); - component_background_init(BACKGROUND_CACHE); + path_push(path, MENU_CACHE_DIRECTORY); + directory_create(path_get(path)); + + path_push(path, BACKGROUND_CACHE_FILE); + component_background_init(path_get(path)); + + path_free(path); menu->boot_params = boot_params; - bool default_directory_exists = directory_exists(menu->settings.default_directory); - char *init_directory = default_directory_exists ? menu->settings.default_directory : ""; - - menu->browser.valid = false; - menu->browser.reload = false; - menu->browser.directory = path_init("sd:/", init_directory); - - menu->load.rom_path = NULL; + menu->browser.directory = path_init(menu->storage_prefix, menu->settings.default_directory); + if (!directory_exists(path_get(menu->browser.directory))) { + path_free(menu->browser.directory); + menu->browser.directory = path_init(menu->storage_prefix, "/"); + } hdmi_clear_game_id(); @@ -118,6 +128,12 @@ static void menu_deinit (menu_t *menu) { hdmi_send_game_id(menu->boot_params); + path_free(menu->load.disk_path); + path_free(menu->load.rom_path); + for (int i = 0; i < menu->browser.entries; i++) { + free(menu->browser.list[i].name); + } + free(menu->browser.list); path_free(menu->browser.directory); free(menu); diff --git a/src/menu/menu_state.h b/src/menu/menu_state.h index 265443fe..c2ad221d 100644 --- a/src/menu/menu_state.h +++ b/src/menu/menu_state.h @@ -18,9 +18,6 @@ #include "settings.h" -#define BROWSER_LIST_SIZE 2048 - - /** @brief Menu mode enumeration */ typedef enum { MENU_MODE_NONE, @@ -60,7 +57,7 @@ typedef enum { typedef struct { char *name; entry_type_t type; - int size; + int64_t size; } entry_t; /** @brief Menu Structure */ @@ -68,6 +65,7 @@ typedef struct { menu_mode_t mode; menu_mode_t next_mode; + const char *storage_prefix; settings_t settings; boot_params_t *boot_params; @@ -93,7 +91,7 @@ typedef struct { bool valid; bool reload; path_t *directory; - entry_t list[BROWSER_LIST_SIZE]; + entry_t *list; int entries; entry_t *entry; int selected; diff --git a/src/menu/mp3_player.c b/src/menu/mp3_player.c index 87109a3e..b41a170c 100644 --- a/src/menu/mp3_player.c +++ b/src/menu/mp3_player.c @@ -1,4 +1,6 @@ -#include +#include +#include + #include #include "mp3_player.h" @@ -18,19 +20,18 @@ /** @brief MP3 File Information Structure. */ typedef struct { bool loaded; - bool io_error; - - FIL fil; - FSIZE_t data_start; - int seek_predecode_frames; - - mp3dec_t dec; - mp3dec_frame_info_t info; + FILE *f; + size_t file_size; + size_t data_start; uint8_t buffer[16 * 1024]; uint8_t *buffer_ptr; size_t buffer_left; + mp3dec_t dec; + mp3dec_frame_info_t info; + + int seek_predecode_frames; float duration; float bitrate; @@ -48,9 +49,7 @@ static void mp3player_reset_decoder (void) { } static void mp3player_fill_buffer (void) { - UINT bytes_read; - - if (f_eof(&p->fil)) { + if (feof(p->f)) { return; } @@ -63,11 +62,7 @@ static void mp3player_fill_buffer (void) { p->buffer_ptr = p->buffer; } - if (f_read(&p->fil, p->buffer + p->buffer_left, sizeof(p->buffer) - p->buffer_left, &bytes_read) == FR_OK) { - p->buffer_left += bytes_read; - } else { - p->io_error = true; - } + p->buffer_left += fread(p->buffer + p->buffer_left, 1, sizeof(p->buffer) - p->buffer_left, p->f); } static void mp3player_wave_read (void *ctx, samplebuffer_t *sbuf, int wpos, int wlen, bool seeking) { @@ -109,7 +104,7 @@ static void mp3player_calculate_duration (int samples) { uint32_t frames; int delay, padding; - long data_size = (f_size(&p->fil) - p->data_start); + long data_size = (p->file_size - p->data_start); if (mp3dec_check_vbrtag((const uint8_t *) (p->buffer_ptr), p->info.frame_bytes, &frames, &delay, &padding) > 0) { p->duration = (frames * samples) / (float) (p->info.hz); p->bitrate = (data_size * 8) / p->duration; @@ -137,7 +132,6 @@ mp3player_err_t mp3player_init (void) { mp3player_reset_decoder(); p->loaded = false; - p->io_error = false; p->wave = (waveform_t) { .name = "mp3player", @@ -164,22 +158,32 @@ mp3player_err_t mp3player_load (char *path) { mp3player_unload(); } - if (f_open(&p->fil, strip_sd_prefix(path), FA_READ) != FR_OK) { + if ((p->f = fopen(path, "rb")) == NULL) { return MP3PLAYER_ERR_IO; } + setbuf(p->f, NULL); + + struct stat st; + if (fstat(fileno(p->f), &st)) { + fclose(p->f); + return MP3PLAYER_ERR_IO; + } + p->file_size = st.st_size; mp3player_reset_decoder(); - while (!(f_eof(&p->fil) && p->buffer_left == 0)) { + while (!(feof(p->f) && p->buffer_left == 0)) { mp3player_fill_buffer(); - if (p->io_error) { + if (ferror(p->f)) { + fclose(p->f); return MP3PLAYER_ERR_IO; } size_t id3v2_skip = mp3dec_skip_id3v2((const uint8_t *) (p->buffer_ptr), p->buffer_left); if (id3v2_skip > 0) { - if (f_lseek(&p->fil, f_tell(&p->fil) - p->buffer_left + id3v2_skip) != FR_OK) { + if (fseek(p->f, (-p->buffer_left) + id3v2_skip, SEEK_CUR)) { + fclose(p->f); return MP3PLAYER_ERR_IO; } mp3player_reset_decoder(); @@ -189,7 +193,7 @@ mp3player_err_t mp3player_load (char *path) { int samples = mp3dec_decode_frame(&p->dec, p->buffer_ptr, p->buffer_left, NULL, &p->info); if (samples > 0) { p->loaded = true; - p->data_start = f_tell(&p->fil) - p->buffer_left + p->info.frame_offset; + p->data_start = ftell(p->f) - p->buffer_left + p->info.frame_offset; p->buffer_ptr += p->info.frame_offset; p->buffer_left -= p->info.frame_offset; @@ -206,7 +210,7 @@ mp3player_err_t mp3player_load (char *path) { p->buffer_left -= p->info.frame_bytes; } - if (f_close(&p->fil) != FR_OK) { + if (fclose(p->f)) { return MP3PLAYER_ERR_IO; } @@ -217,12 +221,12 @@ void mp3player_unload (void) { mp3player_stop(); if (p->loaded) { p->loaded = false; - f_close(&p->fil); + fclose(p->f); } } mp3player_err_t mp3player_process (void) { - if (p->io_error) { + if (ferror(p->f)) { mp3player_unload(); return MP3PLAYER_ERR_IO; } @@ -239,7 +243,7 @@ bool mp3player_is_playing (void) { } bool mp3player_is_finished (void) { - return p->loaded && f_eof(&p->fil) && (p->buffer_left == 0); + return p->loaded && feof(p->f) && (p->buffer_left == 0); } mp3player_err_t mp3player_play (void) { @@ -248,8 +252,7 @@ mp3player_err_t mp3player_play (void) { } if (!mp3player_is_playing()) { if (mp3player_is_finished()) { - if (f_lseek(&p->fil, p->data_start) != FR_OK) { - p->io_error = true; + if (fseek(p->f, p->data_start, SEEK_SET)) { return MP3PLAYER_ERR_IO; } mp3player_reset_decoder(); @@ -292,25 +295,24 @@ mp3player_err_t mp3player_seek (int seconds) { return MP3PLAYER_OK; } - long position = ((long) (f_tell(&p->fil)) - p->buffer_left + bytes_to_move); + long position = (ftell(p->f) - p->buffer_left + bytes_to_move); if (position < (long) (p->data_start)) { position = p->data_start; } - if (f_lseek(&p->fil, position) != FR_OK) { - p->io_error = true; + if (fseek(p->f, position, SEEK_SET)) { return MP3PLAYER_ERR_IO; } mp3player_reset_decoder(); mp3player_fill_buffer(); - p->seek_predecode_frames = (position == p->data_start) ? 0 : SEEK_PREDECODE_FRAMES; - - if (p->io_error) { + if (ferror(p->f)) { return MP3PLAYER_ERR_IO; } + p->seek_predecode_frames = (position == p->data_start) ? 0 : SEEK_PREDECODE_FRAMES; + return MP3PLAYER_OK; } @@ -346,9 +348,9 @@ float mp3player_get_progress (void) { return 0.0f; } - FSIZE_t data_size = f_size(&p->fil) - p->data_start; - FSIZE_t data_consumed = f_tell(&p->fil) - p->buffer_left; - FSIZE_t data_position = (data_consumed > p->data_start) ? (data_consumed - p->data_start) : 0; + long data_size = p->file_size - p->data_start; + long data_consumed = ftell(p->f) - p->buffer_left; + long data_position = (data_consumed > p->data_start) ? (data_consumed - p->data_start) : 0; return data_position / (float) (data_size); } diff --git a/src/menu/path.c b/src/menu/path.c index 58638dea..59f622f2 100644 --- a/src/menu/path.c +++ b/src/menu/path.c @@ -19,7 +19,7 @@ static void path_resize (path_t *path, size_t min_length) { assert(path->buffer != NULL); } -static path_t *path_create (char *string) { +static path_t *path_create (const char *string) { if (string == NULL) { string = ""; } @@ -43,7 +43,7 @@ static void path_append (path_t *path, char *string) { } -path_t *path_init (char *prefix, char *string) { +path_t *path_init (const char *prefix, char *string) { path_t *path = path_create(prefix); size_t prefix_length = strlen(prefix); if ((prefix_length > 0) && (prefix[prefix_length - 1] == '/')) { diff --git a/src/menu/path.h b/src/menu/path.h index 3bf51950..df2672d2 100644 --- a/src/menu/path.h +++ b/src/menu/path.h @@ -20,7 +20,7 @@ typedef struct { } path_t; -path_t *path_init (char *prefix, char *string); +path_t *path_init (const char *prefix, char *string); void path_free (path_t *path); path_t *path_clone (path_t *string); path_t *path_clone_push (path_t *path, char *string); diff --git a/src/menu/png_decoder.c b/src/menu/png_decoder.c index 54d185d3..14dbf8ed 100644 --- a/src/menu/png_decoder.c +++ b/src/menu/png_decoder.c @@ -1,4 +1,5 @@ -#include +#include + #include #include "png_decoder.h" @@ -7,7 +8,7 @@ /** @brief PNG File Information Structure. */ typedef struct { - FIL fil; + FILE *f; spng_ctx *ctx; struct spng_ihdr ihdr; @@ -25,7 +26,7 @@ static png_decoder_t *decoder; static void png_decoder_deinit (bool free_image) { if (decoder != NULL) { - f_close(&decoder->fil); + fclose(decoder->f); if (decoder->ctx != NULL) { spng_ctx_free(decoder->ctx); } @@ -41,21 +42,6 @@ static void png_decoder_deinit (bool free_image) { } } -static int png_file_read (spng_ctx *ctx, void *user, void *dst_src, size_t length) { - UINT bytes_read = 0; - png_decoder_t *d = (png_decoder_t *) (user); - - if (f_read(&d->fil, dst_src, length, &bytes_read) != FR_OK) { - return SPNG_IO_ERROR; - } - - if (bytes_read != length) { - return SPNG_EOF; - } - - return SPNG_OK; -} - png_err_t png_decoder_start (char *path, int max_width, int max_height, png_callback_t *callback, void *callback_data) { if (decoder != NULL) { @@ -67,11 +53,13 @@ png_err_t png_decoder_start (char *path, int max_width, int max_height, png_call return PNG_ERR_OUT_OF_MEM; } - if (f_open(&decoder->fil, strip_sd_prefix(path), FA_READ) != FR_OK) { + if ((decoder->f = fopen(path, "rb")) == NULL) { png_decoder_deinit(false); return PNG_ERR_NO_FILE; } + setbuf(decoder->f, NULL); + if ((decoder->ctx = spng_ctx_new(SPNG_CTX_IGNORE_ADLER32)) == NULL) { png_decoder_deinit(false); return PNG_ERR_OUT_OF_MEM; @@ -87,7 +75,7 @@ png_err_t png_decoder_start (char *path, int max_width, int max_height, png_call return PNG_ERR_INT; } - if (spng_set_png_stream(decoder->ctx, png_file_read, decoder) != SPNG_OK) { + if (spng_set_png_file(decoder->ctx, decoder->f) != SPNG_OK) { png_decoder_deinit(false); return PNG_ERR_INT; } diff --git a/src/menu/rom_info.c b/src/menu/rom_info.c index 6a2223c5..b9711701 100644 --- a/src/menu/rom_info.c +++ b/src/menu/rom_info.c @@ -1,6 +1,6 @@ +#include #include -#include #include #include "boot/cic.h" @@ -829,7 +829,7 @@ static rom_err_t save_override (path_t *path, const char *id, int value, int def mini_free(ini); if (empty) { - if (file_delete(path_get(overrides_path))) { + if (remove(path_get(overrides_path))) { path_free(overrides_path); return ROM_ERR_IO; } @@ -912,21 +912,18 @@ rom_err_t rom_info_override_tv_type (path_t *path, rom_info_t *rom_info, rom_tv_ } rom_err_t rom_info_load (path_t *path, rom_info_t *rom_info) { - FIL fil; - UINT br; + FILE *f; rom_header_t rom_header; - if (f_open(&fil, strip_sd_prefix(path_get(path)), FA_READ) != FR_OK) { + if ((f = fopen(path_get(path), "rb")) == NULL) { return ROM_ERR_NO_FILE; } - if (f_read(&fil, &rom_header, sizeof(rom_header), &br) != FR_OK) { - f_close(&fil); + setbuf(f, NULL); + if (fread(&rom_header, sizeof(rom_header), 1, f) != 1) { + fclose(f); return ROM_ERR_IO; } - if (f_close(&fil) != FR_OK) { - return ROM_ERR_IO; - } - if (br != sizeof(rom_header)) { + if (fclose(f)) { return ROM_ERR_IO; } diff --git a/src/menu/settings.c b/src/menu/settings.c index 6f8dba03..3e046ed0 100644 --- a/src/menu/settings.c +++ b/src/menu/settings.c @@ -5,14 +5,12 @@ #include "utils/fs.h" -#ifndef SETTINGS_FILE_PATH -#define SETTINGS_FILE_PATH "sd:/menu/config.ini" -#endif +static char *settings_path = NULL; static settings_t init = { .pal60_enabled = false, - .hidden_files_enabled = false, + .show_protected_entries = false, .default_directory = "/", .use_saves_folder = true, @@ -23,15 +21,22 @@ static settings_t init = { }; +void settings_init (char *path) { + if (settings_path) { + free(settings_path); + } + settings_path = strdup(path); +} + void settings_load (settings_t *settings) { - if (!file_exists(SETTINGS_FILE_PATH)) { + if (!file_exists(settings_path)) { settings_save(&init); } - mini_t *ini = mini_try_load(SETTINGS_FILE_PATH); + mini_t *ini = mini_try_load(settings_path); settings->pal60_enabled = mini_get_bool(ini, "menu", "pal60", init.pal60_enabled); // TODO: consider changing file setting name - settings->hidden_files_enabled = mini_get_bool(ini, "menu", "show_hidden_files", init.hidden_files_enabled); + settings->show_protected_entries = mini_get_bool(ini, "menu", "show_protected_entries", init.show_protected_entries); settings->default_directory = strdup(mini_get_string(ini, "menu", "default_directory", init.default_directory)); settings->use_saves_folder = mini_get_bool(ini, "menu", "use_saves_folder", init.use_saves_folder); @@ -44,10 +49,10 @@ void settings_load (settings_t *settings) { } void settings_save (settings_t *settings) { - mini_t *ini = mini_create(SETTINGS_FILE_PATH); + mini_t *ini = mini_create(settings_path); mini_set_bool(ini, "menu", "pal60", settings->pal60_enabled); - mini_set_bool(ini, "menu", "show_hidden_files", settings->hidden_files_enabled); + mini_set_bool(ini, "menu", "show_protected_entries", settings->show_protected_entries); mini_set_string(ini, "menu", "default_directory", settings->default_directory); mini_set_bool(ini, "menu", "use_saves_folder", settings->use_saves_folder); diff --git a/src/menu/settings.h b/src/menu/settings.h index d2529998..e9931b05 100644 --- a/src/menu/settings.h +++ b/src/menu/settings.h @@ -13,8 +13,8 @@ typedef struct { /** @brief Use 60 Hz refresh rate on a PAL console */ bool pal60_enabled; - /** @brief Show files marked as hidden in the browser */ - bool hidden_files_enabled; + /** @brief Show files/directories that are filtered in the browser */ + bool show_protected_entries; /** @brief Default directory to navigate to when menu loads */ char *default_directory; @@ -33,6 +33,8 @@ typedef struct { } settings_t; +/** @brief Init settings path */ +void settings_init (char *path); /** @brief The settings to load */ void settings_load (settings_t *settings); /** @brief The settings to save */ diff --git a/src/menu/usb_comm.c b/src/menu/usb_comm.c index fd218cd3..743ddee2 100644 --- a/src/menu/usb_comm.c +++ b/src/menu/usb_comm.c @@ -2,8 +2,9 @@ // Main use of these functions is to aid menu development // (for example replace files on the SD card or reboot menu). -#include +#include #include + #include #include "usb_comm.h" @@ -73,15 +74,12 @@ static void command_reboot (menu_t *menu) { }; static void command_send_file (menu_t *menu) { - char path[256]; + FILE *f; + char buffer[256]; + uint8_t data[8192]; char length[8]; - FIL f; - int remaining; - uint8_t data[8192]; - UINT bytes_written; - - if (usb_comm_read_string(path, sizeof(path), ' ')) { + if (usb_comm_read_string(buffer, sizeof(buffer), ' ')) { return usb_comm_send_error("Invalid path argument\n"); } @@ -93,11 +91,16 @@ static void command_send_file (menu_t *menu) { return usb_comm_send_error("Invalid file length argument\n"); } - if (f_open(&f, path, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK) { + path_t *path = path_init(menu->storage_prefix, buffer); + + if ((f = fopen(path_get(path), "wb")) == NULL) { + path_free(path); return usb_comm_send_error("Couldn't create file\n"); } + setbuf(f, NULL); + path_free(path); - remaining = atoi(length); + int remaining = atoi(length); if (remaining > MAX_FILE_SIZE) { return usb_comm_send_error("File size too big\n"); @@ -106,18 +109,14 @@ static void command_send_file (menu_t *menu) { while (remaining > 0) { int block_size = MIN(remaining, sizeof(data)); usb_read(data, block_size); - if (f_write(&f, data, block_size, &bytes_written) != FR_OK) { - f_close(&f); - return usb_comm_send_error("Couldn't write data to the file\n"); - } - if (bytes_written != block_size) { - f_close(&f); + if (fwrite(data, 1, block_size, f) != block_size) { + fclose(f); return usb_comm_send_error("Couldn't write all required data to the file\n"); } remaining -= block_size; } - if (f_close(&f) != FR_OK) { + if (fclose(f)) { return usb_comm_send_error("Couldn't flush data to the file\n"); } diff --git a/src/menu/views/browser.c b/src/menu/views/browser.c index 00061d67..8cca0201 100644 --- a/src/menu/views/browser.c +++ b/src/menu/views/browser.c @@ -1,9 +1,8 @@ #include #include +#include #include -#include - #include "../fonts.h" #include "utils/fs.h" #include "views.h" @@ -17,6 +16,17 @@ static const char *image_extensions[] = { "png", NULL }; static const char *text_extensions[] = { "txt", "ini", "yml", "yaml", NULL }; static const char *music_extensions[] = { "mp3", NULL }; +static const char *hidden_paths[] = { + "/menu.bin", + "/menu", + "/N64FlashcartMenu.n64", + "/OS64.v64", + "/OS64P.v64", + "/sc64menu.n64", + "/System Volume Information", + NULL +}; + static int compare_entry (const void *pa, const void *pb) { entry_t *a = (entry_t *) (pa); @@ -61,74 +71,87 @@ static int compare_entry (const void *pa, const void *pb) { return strcasecmp((const char *) (a->name), (const char *) (b->name)); } -static bool load_directory (menu_t *menu) { - DIR dir; - FILINFO info; - +static void browser_list_free (menu_t *menu) { for (int i = menu->browser.entries - 1; i >= 0; i--) { free(menu->browser.list[i].name); } + free(menu->browser.list); + + menu->browser.list = NULL; menu->browser.entries = 0; - menu->browser.selected = -1; menu->browser.entry = NULL; + menu->browser.selected = -1; +} - if (f_opendir(&dir, strip_sd_prefix(path_get(menu->browser.directory))) != FR_OK) { - return true; +static bool load_directory (menu_t *menu) { + int result; + dir_t info; + + browser_list_free(menu); + + path_t *path = path_clone(menu->browser.directory); + + result = dir_findfirst(path_get(path), &info); + + while (result == 0) { + bool hide = false; + + if (!menu->settings.show_protected_entries) { + path_push(path, info.d_name); + + for (int i = 0; hidden_paths[i] != NULL; i++) { + if (strcmp(strip_fs_prefix(path_get(path)), hidden_paths[i]) == 0) { + hide = true; + break; + } + } + + path_pop(path); + } + + if (!hide) { + menu->browser.list = realloc(menu->browser.list, (menu->browser.entries + 1) * sizeof(entry_t)); + + entry_t *entry = &menu->browser.list[menu->browser.entries++]; + + entry->name = strdup(info.d_name); + if (!entry->name) { + path_free(path); + browser_list_free(menu); + return true; + } + + if (info.d_type == DT_DIR) { + entry->type = ENTRY_TYPE_DIR; + } else if (file_has_extensions(entry->name, rom_extensions)) { + entry->type = ENTRY_TYPE_ROM; + } else if (file_has_extensions(entry->name, disk_extensions)) { + entry->type = ENTRY_TYPE_DISK; + }else if (file_has_extensions(entry->name, emulator_extensions)) { + entry->type = ENTRY_TYPE_EMULATOR; + } else if (file_has_extensions(entry->name, save_extensions)) { + entry->type = ENTRY_TYPE_SAVE; + } else if (file_has_extensions(entry->name, image_extensions)) { + entry->type = ENTRY_TYPE_IMAGE; + } else if (file_has_extensions(entry->name, text_extensions)) { + entry->type = ENTRY_TYPE_TEXT; + } else if (file_has_extensions(entry->name, music_extensions)) { + entry->type = ENTRY_TYPE_MUSIC; + } else { + entry->type = ENTRY_TYPE_OTHER; + } + + entry->size = info.d_size; + } + + result = dir_findnext(path_get(path), &info); } - while (menu->browser.entries < BROWSER_LIST_SIZE) { - if (f_readdir(&dir, &info) != FR_OK) { - return true; - } + path_free(path); - size_t length = strlen(info.fname); - - if (length == 0) { - break; - } - - if (info.fattrib & AM_SYS) { - continue; - } - if ((info.fattrib & AM_HID) && !menu->settings.hidden_files_enabled) { - continue; - } - - entry_t *entry = &menu->browser.list[menu->browser.entries]; - - entry->name = strdup(info.fname); - if (!entry->name) { - f_closedir(&dir); - return true; - } - - if (info.fattrib & AM_DIR) { - entry->type = ENTRY_TYPE_DIR; - } else if (file_has_extensions(info.fname, rom_extensions)) { - entry->type = ENTRY_TYPE_ROM; - } else if (file_has_extensions(info.fname, disk_extensions)) { - entry->type = ENTRY_TYPE_DISK; - }else if (file_has_extensions(info.fname, emulator_extensions)) { - entry->type = ENTRY_TYPE_EMULATOR; - } else if (file_has_extensions(info.fname, save_extensions)) { - entry->type = ENTRY_TYPE_SAVE; - } else if (file_has_extensions(info.fname, image_extensions)) { - entry->type = ENTRY_TYPE_IMAGE; - } else if (file_has_extensions(info.fname, text_extensions)) { - entry->type = ENTRY_TYPE_TEXT; - } else if (file_has_extensions(info.fname, music_extensions)) { - entry->type = ENTRY_TYPE_MUSIC; - } else { - entry->type = ENTRY_TYPE_OTHER; - } - - entry->size = info.fsize; - - menu->browser.entries += 1; - } - - if (f_closedir(&dir) != FR_OK) { + if (result < -1) { + browser_list_free(menu); return true; } @@ -205,18 +228,14 @@ static void show_properties (menu_t *menu, void *arg) { static void delete_entry (menu_t *menu, void *arg) { path_t *path = path_clone_push(menu->browser.directory, menu->browser.entry->name); - if (menu->browser.entry->type == ENTRY_TYPE_DIR) { - if (directory_delete(path_get(path))) { + if (remove(path_get(path))) { + if (menu->browser.entry->type == ENTRY_TYPE_DIR) { menu_show_error(menu, "Couldn't delete directory\nDirectory might not be empty"); - path_free(path); - return; - } - } else { - if (file_delete(path_get(path))) { + } else { menu_show_error(menu, "Couldn't delete file"); - path_free(path); - return; } + path_free(path); + return; } path_free(path); @@ -229,7 +248,7 @@ static void delete_entry (menu_t *menu, void *arg) { static void set_default_directory (menu_t *menu, void *arg) { free(menu->settings.default_directory); - menu->settings.default_directory = strdup(strip_sd_prefix(path_get(menu->browser.directory))); + menu->settings.default_directory = strdup(strip_fs_prefix(path_get(menu->browser.directory))); settings_save(&menu->settings); } @@ -388,7 +407,7 @@ void view_browser_init (menu_t *menu) { component_context_menu_init(&settings_context_menu); if (load_directory(menu)) { path_free(menu->browser.directory); - menu->browser.directory = path_init("sd:/", ""); + menu->browser.directory = path_init(menu->storage_prefix, ""); menu_show_error(menu, "Error while opening initial directory"); } else { menu->browser.valid = true; diff --git a/src/menu/views/file_info.c b/src/menu/views/file_info.c index 83506caa..f4819de8 100644 --- a/src/menu/views/file_info.c +++ b/src/menu/views/file_info.c @@ -1,4 +1,4 @@ -#include +#include #include "utils/fs.h" #include "views.h" @@ -16,7 +16,7 @@ static const char *dexdrive_extensions[] = { "mpk", NULL }; static const char *emulator_extensions[] = { "emu", NULL }; -static FILINFO info; +static struct stat st; static char *format_file_type (char *name, bool is_directory) { @@ -75,17 +75,14 @@ static void draw (menu_t *menu, surface_t *d) { "\n" "\n" " Size: %d bytes\n" - " Attributes: %s%s%s%s%s\n" + " Attributes: %s %s\n" "%s" - " Modified: %u-%02u-%02u %02u:%02u", - info.fsize, - (info.fattrib & AM_DIR) ? "Directory " : "File ", - (info.fattrib & AM_RDO) ? "| Read only " : "", - (info.fattrib & AM_SYS) ? "| System " : "", - (info.fattrib & AM_ARC) ? "| Archive " : "", - (info.fattrib & AM_HID) ? "| Hidden " : "", - format_file_type(info.fname, info.fattrib & AM_DIR), - (info.fdate >> 9) + 1980, info.fdate >> 5 & 0x0F, info.fdate & 0x1F, info.ftime >> 11, info.ftime >> 5 & 0x3F + " Modified: %s", + st.st_size, + S_ISDIR(st.st_mode) ? "Directory" : "File", + st.st_mode & S_IWUSR ? "" : "(Read only)", + format_file_type(menu->browser.entry->name, S_ISDIR(st.st_mode)), + ctime(&st.st_mtim.tv_sec) ); component_actions_bar_text_draw( @@ -101,7 +98,7 @@ static void draw (menu_t *menu, surface_t *d) { void view_file_info_init (menu_t *menu) { path_t *path = path_clone_push(menu->browser.directory, menu->browser.entry->name); - if (f_stat(strip_sd_prefix(path_get(path)), &info) != FR_OK) { + if (stat(path_get(path), &st)) { menu_show_error(menu, "Couldn't obtain file information"); } diff --git a/src/menu/views/load_disk.c b/src/menu/views/load_disk.c index 0a8ebb62..986a18b6 100644 --- a/src/menu/views/load_disk.c +++ b/src/menu/views/load_disk.c @@ -154,7 +154,7 @@ void view_load_disk_init (menu_t *menu) { menu->load.disk_path = path_clone_push(menu->browser.directory, menu->browser.entry->name); - disk_err_t err = disk_info_load(path_get(menu->load.disk_path), &menu->load.disk_info); + disk_err_t err = disk_info_load(menu->load.disk_path, &menu->load.disk_info); if (err != DISK_OK) { menu_show_error(menu, convert_error_message(err)); } diff --git a/src/menu/views/load_rom.c b/src/menu/views/load_rom.c index c5da6ed4..3e4e295c 100644 --- a/src/menu/views/load_rom.c +++ b/src/menu/views/load_rom.c @@ -334,7 +334,7 @@ void view_load_rom_init (menu_t *menu) { return; } - boxart = component_boxart_init(menu->load.rom_info.game_code); + boxart = component_boxart_init(menu->storage_prefix, menu->load.rom_info.game_code); component_context_menu_init(&options_context_menu); } diff --git a/src/menu/views/settings_editor.c b/src/menu/views/settings_editor.c index 7158a0b3..5e9d1854 100644 --- a/src/menu/views/settings_editor.c +++ b/src/menu/views/settings_editor.c @@ -34,15 +34,15 @@ static void draw (menu_t *menu, surface_t *d) { "\n" "To change the settings, please adjust them\n" "directly in the 'menu/config.ini' file.\n\n" - "pal60_enabled: %s\n" - "hidden_files_enabled: %s\n" - "default_directory: %s\n" - "use_saves_folder: %s\n" - "bgm_enabled: %s\n" - "sound_enabled: %s\n" - "rumble_enabled: %s\n", + "pal60_enabled: %s\n" + "show_protected_entries: %s\n" + "default_directory: %s\n" + "use_saves_folder: %s\n" + "bgm_enabled: %s\n" + "sound_enabled: %s\n" + "rumble_enabled: %s\n", format_switch(menu->settings.pal60_enabled), - format_switch(menu->settings.hidden_files_enabled), + format_switch(menu->settings.show_protected_entries), menu->settings.default_directory, format_switch(menu->settings.use_saves_folder), format_switch(menu->settings.bgm_enabled), diff --git a/src/menu/views/text_viewer.c b/src/menu/views/text_viewer.c index adef2df9..277eeba5 100644 --- a/src/menu/views/text_viewer.c +++ b/src/menu/views/text_viewer.c @@ -1,9 +1,13 @@ -#include +#include +#include #include "utils/fs.h" +#include "utils/utils.h" #include "views.h" -static char *file_content; + +static char *text; + static void process (menu_t *menu) { if (menu->actions.back) { @@ -18,20 +22,15 @@ static void draw (menu_t *menu, surface_t *d) { component_layout_draw(); - component_main_text_draw( - ALIGN_CENTER, VALIGN_TOP, - "TEXT VIEWER\n" - "\n" - ); - - component_main_text_draw( - ALIGN_LEFT, VALIGN_TOP, - "\n" - "\n" - "%s\n", - file_content - ); - + if (text) { + component_main_text_draw( + ALIGN_LEFT, VALIGN_TOP, + "%s\n", + text + ); + } else { + component_messagebox_draw("Text file is empty"); + } component_actions_bar_text_draw( ALIGN_LEFT, VALIGN_TOP, @@ -42,37 +41,62 @@ static void draw (menu_t *menu, surface_t *d) { rdpq_detach_show(); } +static void deinit (void) { + if (text) { + free(text); + text = NULL; + } +} + void view_text_viewer_init (menu_t *menu) { path_t *path = path_clone_push(menu->browser.directory, menu->browser.entry->name); - uint32_t file_size = file_get_size(path_get(path)); + FILE *f; - if (file_size > 1024) { // FIXME: this is just a placeholder until scrolling is implemented. - file_size = 1024; // For the moment, we just set it to that, since any more would be a waste. - } - - file_content = calloc(file_size, 1); - - // read file content - FIL fil; - UINT br; - - if (f_open(&fil, strip_sd_prefix(path_get(path)), FA_READ) != FR_OK) { - debugf("Error loading file\n"); - } - if (f_read(&fil, file_content, file_size, &br) != FR_OK) { - f_close(&fil); - debugf("Error loading file content\n"); - } - if (f_close(&fil) != FR_OK) { - debugf("Error closing file\n"); + if ((f = fopen(path_get(path), "r")) == NULL) { + path_free(path); + menu_show_error(menu, "Couldn't open text file"); + return; } path_free(path); + + struct stat st; + if (fstat(fileno(f), &st)) { + fclose(f); + menu_show_error(menu, "Couldn't get text file size"); + return; + } + + // TODO: Implement proper text file viewer with both vertical and horizontal scrolling + size_t size = MIN(st.st_size, 1024); + + if (size) { + if ((text = calloc(sizeof(char), size)) == NULL) { + fclose(f); + menu_show_error(menu, "Couldn't allocate memory for the text file contents"); + return; + } + + if (fread(text, size, 1, f) != 1) { + fclose(f); + menu_show_error(menu, "Couldn't read text file contents"); + return; + } + } + + if (fclose(f)) { + menu_show_error(menu, "Couldn't close text file"); + } } void view_text_viewer_display (menu_t *menu, surface_t *display) { process(menu); + draw(menu, display); + + if (menu->next_mode != MENU_MODE_TEXT_VIEWER) { + deinit(); + } } diff --git a/src/utils/fs.c b/src/utils/fs.c index 648ecfdf..41b69c48 100644 --- a/src/utils/fs.c +++ b/src/utils/fs.c @@ -1,145 +1,85 @@ +#include #include #include -#include - -#include +#include +#include #include "fs.h" #include "utils.h" -char *strip_sd_prefix (char *path) { - const char *prefix = "sd:/"; - +char *strip_fs_prefix (char *path) { + const char *prefix = ":/"; char *found = strstr(path, prefix); if (found) { - return found + strlen(prefix) - 1; + return (found + strlen(prefix) - 1); } - return path; } bool file_exists (char *path) { - FRESULT fr; - FILINFO fno; - - fr = f_stat(strip_sd_prefix(path), &fno); - - return ((fr == FR_OK) && (!(fno.fattrib & AM_DIR))); + struct stat st; + int error = stat(path, &st); + return ((error == 0) && S_ISREG(st.st_mode)); } - -size_t file_get_size (char *path) { - FILINFO fno; - - if (f_stat(strip_sd_prefix(path), &fno) != FR_OK) { - return 0; +int64_t file_get_size (char *path) { + struct stat st; + if (stat(path, &st)) { + return -1; } - - return (size_t) (fno.fsize); + return (int64_t) (st.st_size); } -bool file_delete (char *path) { - if (file_exists(path)) { - return (f_unlink(strip_sd_prefix(path)) != FR_OK); +bool file_allocate (char *path, size_t size) { + FILE *f; + if ((f = fopen(path, "wb")) == NULL) { + return true; + } + if (fseek(f, size, SEEK_SET)) { + fclose(f); + return true; + } + if (ftell(f) != size) { + fclose(f); + return true; + } + if (fclose(f)) { + return true; } return false; } -bool file_allocate (char *path, size_t size) { - FIL fil; - bool error = false; - - if (f_open(&fil, strip_sd_prefix(path), FA_WRITE | FA_CREATE_NEW) != FR_OK) { - return true; - } - - if (f_lseek(&fil, size) != FR_OK) { - error = true; - } - - if (f_tell(&fil) != size) { - error = true; - } - - if (f_close(&fil) != FR_OK) { - error = true; - } - - return error; -} - bool file_fill (char *path, uint8_t value) { - FIL fil; + FILE *f; bool error = false; uint8_t buffer[FS_SECTOR_SIZE * 8]; - FRESULT res; - UINT bytes_to_write; - UINT bytes_written; + size_t bytes_to_write; for (int i = 0; i < sizeof(buffer); i++) { buffer[i] = value; } - if (f_open(&fil, strip_sd_prefix(path), FA_WRITE) != FR_OK) { + if ((f = fopen(path, "rb+")) == NULL) { return true; } - for (int i = 0; i < f_size(&fil); i += sizeof(buffer)) { - bytes_to_write = MIN(f_size(&fil) - f_tell(&fil), sizeof(buffer)); - res = f_write(&fil, buffer, bytes_to_write, &bytes_written); - if ((res != FR_OK) || (bytes_to_write != bytes_written)) { + setbuf(f, NULL); + + fseek(f, 0, SEEK_END); + size_t size = ftell(f); + fseek(f, 0, SEEK_SET); + + for (size_t i = 0; i < size; i += sizeof(buffer)) { + bytes_to_write = MIN(size - ftell(f), sizeof(buffer)); + if (fwrite(buffer, 1, bytes_to_write, f) != bytes_to_write) { error = true; break; } } - if (f_tell(&fil) != f_size(&fil)) { - error = true; - } - - if (f_close(&fil) != FR_OK) { - error = true; - } - - return error; -} - -bool file_get_sectors (char *path, void (*callback) (uint32_t sector_count, uint32_t file_sector, uint32_t cluster_sector, uint32_t cluster_size)) { - FATFS *fs; - FIL fil; - bool error = false; - - if (!callback) { - return true; - } - - if (f_open(&fil, strip_sd_prefix(path), FA_READ) != FR_OK) { - return true; - } - - fs = fil.obj.fs; - - uint32_t sector_count = (ALIGN(f_size(&fil), FS_SECTOR_SIZE) / FS_SECTOR_SIZE); - - uint32_t cluster_sector = 0; - - for (int file_sector = 0; file_sector < sector_count; file_sector += fs->csize) { - if ((f_lseek(&fil, (file_sector * FS_SECTOR_SIZE) + (FS_SECTOR_SIZE / 2))) != FR_OK) { - error = true; - break; - } - uint32_t cluster = fil.clust; - if (cluster >= fs->n_fatent) { - error = true; - break; - } - cluster_sector = (fs->database + ((LBA_t) (fs->csize) * (cluster - 2))); - callback(sector_count, file_sector, cluster_sector, fs->csize); - } - - if (f_close(&fil) != FR_OK) { + if (fclose(f)) { error = true; } @@ -165,20 +105,9 @@ bool file_has_extensions (char *path, const char *extensions[]) { bool directory_exists (char *path) { - FRESULT fr; - FILINFO fno; - - fr = f_stat(strip_sd_prefix(path), &fno); - - return ((fr == FR_OK) && (fno.fattrib & AM_DIR)); -} - -bool directory_delete (char *path) { - if (directory_exists(path)) { - return (f_unlink(strip_sd_prefix(path)) != FR_OK); - } - - return false; + struct stat st; + int error = stat(path, &st); + return ((error == 0) && S_ISDIR(st.st_mode)); } bool directory_create (char *path) { @@ -188,8 +117,12 @@ bool directory_create (char *path) { return false; } - char *directory = strdup(strip_sd_prefix(path)); - char *separator = directory; + char *directory = strdup(path); + char *separator = strip_fs_prefix(directory); + + if (separator != directory) { + separator++; + } do { separator = strchr(separator, '/'); @@ -199,8 +132,7 @@ bool directory_create (char *path) { } if (directory[0] != '\0') { - FRESULT res = f_mkdir(directory); - if ((res != FR_OK) && (res != FR_EXIST)) { + if (mkdir(directory, 0777) && (errno != EEXIST)) { error = true; break; } diff --git a/src/utils/fs.h b/src/utils/fs.h index 7661ecb0..88f81a7a 100644 --- a/src/utils/fs.h +++ b/src/utils/fs.h @@ -10,18 +10,15 @@ #define FS_SECTOR_SIZE (512) -char *strip_sd_prefix (char *path); +char *strip_fs_prefix (char *path); bool file_exists (char *path); -size_t file_get_size (char *path); -bool file_delete (char *path); +int64_t file_get_size (char *path); bool file_allocate (char *path, size_t size); bool file_fill (char *path, uint8_t value); -bool file_get_sectors (char *path, void (*callback) (uint32_t sector_count, uint32_t file_sector, uint32_t cluster_sector, uint32_t cluster_size)); bool file_has_extensions (char *path, const char *extensions[]); bool directory_exists (char *path); -bool directory_delete (char *path); bool directory_create (char *path);