Use stdio calls for file/directory interaction instead of fatfs (#95)

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

## Description
<!--- Describe your changes in detail -->
This PR changes most of the calls to the fatfs lib with the standard C
ones.
Additionally, there's couple of changes required to adapt to new
interface and several bug fixes.
As a bonus menu can now be run in ares emulator and on the iQue player,
adapting to the available storage options - DragonFS in the ROM and iQue
flash modules (bbfs).

## Motivation and Context
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->
To make it easier to use storage medium other than SD cards on platforms
other than N64 with flashcart.

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

## Screenshots
<!-- (if appropriate): -->
N/A

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

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

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: Polprzewodnikowy <sc@mateuszfaderewski.pl>
This commit is contained in:
Mateusz Faderewski 2024-04-24 02:45:09 +02:00 committed by GitHub
parent c310c5f9e1
commit 3becd1ff59
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
41 changed files with 670 additions and 564 deletions

View File

@ -6,7 +6,7 @@
"mounts": [ "mounts": [
"source=n64flashcartmenu-bashhistory,target=/commandhistory,type=volume" "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": { "customizations": {
"vscode": { "vscode": {
"extensions": [ "extensions": [

12
.gitignore vendored
View File

@ -1,9 +1,17 @@
# Ignore editor specific config folders/files
/.vscode /.vscode
# Ignore compilation result directories
/build /build
/filesystem
/output /output
# Ignore generated files in the libdragon FS
/filesystem/FiraMonoBold.font64
# Ignore external development tools
/tools/* /tools/*
# There should never be ROMs uploaded, but just incase, make sure they are excluded. # Ignore any N64 ROM
**/*.n64 **/*.n64
**/*.v64
**/*.z64 **/*.z64

2
.gitmodules vendored
View File

@ -1,7 +1,7 @@
[submodule "libdragon"] [submodule "libdragon"]
path = libdragon path = libdragon
url = https://github.com/DragonMinded/libdragon url = https://github.com/DragonMinded/libdragon
branch = unstable branch = preview
ignore = dirty ignore = dirty
[submodule "src/libs/libspng"] [submodule "src/libs/libspng"]
path = src/libs/libspng path = src/libs/libspng

View File

@ -13,6 +13,11 @@ BUILD_TIMESTAMP = "$(shell TZ='UTC' date "+%Y-%m-%d %H:%M:%S %:z")"
include $(N64_INST)/include/n64.mk 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) N64_CFLAGS += -iquote $(SOURCE_DIR) -iquote $(ASSETS_DIR) -I $(SOURCE_DIR)/libs -flto=auto $(FLAGS)
SRCS = \ SRCS = \
@ -128,7 +133,9 @@ all: $(OUTPUT_DIR)/$(PROJECT_NAME).n64 64drive ed64 ed64-clone sc64
.PHONY: all .PHONY: all
clean: 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 .PHONY: clean
run: $(OUTPUT_DIR)/$(PROJECT_NAME).n64 run: $(OUTPUT_DIR)/$(PROJECT_NAME).n64

0
filesystem/.gitkeep Normal file
View File

@ -1 +1 @@
Subproject commit c99272b552d398e479a3de6ed9b6563a710b3cc2 Subproject commit 8dd362b8d858ae60befe9d27bc55cf770b9b50af

View File

@ -83,11 +83,11 @@ static flashcart_err_t d64_load_rom (char *rom_path, flashcart_progress_callback
FIL fil; FIL fil;
UINT br; 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; return FLASHCART_ERR_LOAD;
} }
fix_file_size(&fil); fatfs_fix_file_size(&fil);
size_t rom_size = f_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; FIL fil;
UINT br; 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; return FLASHCART_ERR_LOAD;
} }
fix_file_size(&fil); fatfs_fix_file_size(&fil);
size_t file_size = f_size(&fil) - file_offset; 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; FIL fil;
UINT br; 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; 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; 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)) { if (d64_ll_write_save_writeback_lba_list(sectors)) {
return FLASHCART_ERR_INT; return FLASHCART_ERR_INT;
} }

View File

@ -8,14 +8,12 @@
#include "utils/utils.h" #include "utils/utils.h"
#include "flashcart.h" #include "flashcart.h"
#include "flashcart_utils.h"
#include "64drive/64drive.h" #include "64drive/64drive.h"
#include "sc64/sc64.h" #include "sc64/sc64.h"
#define SAVE_WRITEBACK_MAX_SECTORS (256)
static const size_t SAVE_SIZE[__FLASHCART_SAVE_TYPE_END] = { static const size_t SAVE_SIZE[__FLASHCART_SAVE_TYPE_END] = {
0, 0,
512, 512,
@ -27,34 +25,44 @@ static const size_t SAVE_SIZE[__FLASHCART_SAVE_TYPE_END] = {
KiB(128), 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) { static bool dummy_has_feature (flashcart_features_t feature) {
for (uint32_t i = 0; i < cluster_size; i++) { switch (feature) {
uint32_t offset = file_sector + i; default:
uint32_t sector = cluster_sector + i; return false;
if ((offset > SAVE_WRITEBACK_MAX_SECTORS) || (offset > sector_count)) {
return;
}
save_writeback_sectors[offset] = sector;
} }
} }
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; return FLASHCART_OK;
} }
static flashcart_t *flashcart = &((flashcart_t) { static flashcart_t *flashcart = &((flashcart_t) {
.init = dummy_init, .init = dummy_init,
.deinit = NULL, .deinit = NULL,
.load_rom = NULL, .has_feature = dummy_has_feature,
.load_file = NULL, .load_rom = dummy_load_rom,
.load_save = NULL, .load_file = dummy_load_file,
.set_save_type = NULL, .load_save = dummy_load_save,
.load_64dd_ipl = NULL,
.load_64dd_disk = NULL,
.set_save_type = dummy_set_save_type,
.set_save_writeback = NULL, .set_save_writeback = NULL,
}); });
@ -69,7 +77,6 @@ static flashcart_t *flashcart = &((flashcart_t) {
char *flashcart_convert_error_message (flashcart_err_t err) { char *flashcart_convert_error_message (flashcart_err_t err) {
switch (err) { switch (err) {
case FLASHCART_OK: return "No error"; 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_OUTDATED: return "Outdated flashcart firmware";
case FLASHCART_ERR_SD_CARD: return "Error during SD card initialization"; case FLASHCART_ERR_SD_CARD: return "Error during SD card initialization";
case FLASHCART_ERR_ARGS: return "Invalid argument passed to flashcart function"; 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; 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 *storage_prefix = "sd:/";
// NOTE: Some flashcarts doesn't have USB port, can't throw error here bool sd_card_initialized = debug_init_sdfs(*storage_prefix, -1);
debug_init_usblog();
#endif
switch (cart_type) { switch (cart_type) {
case CART_CI: // 64drive case CART_CI: // 64drive
@ -96,6 +105,8 @@ flashcart_err_t flashcart_init (void) {
break; break;
case CART_EDX: // Series X EverDrive-64 case CART_EDX: // Series X EverDrive-64
break;
case CART_ED: // Original EverDrive-64 case CART_ED: // Original EverDrive-64
break; break;
@ -103,15 +114,22 @@ flashcart_err_t flashcart_init (void) {
flashcart = sc64_get_flashcart(); flashcart = sc64_get_flashcart();
break; break;
default: default: // Probably emulator
return FLASHCART_ERR_NOT_DETECTED; *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) { if ((err = flashcart->init()) != FLASHCART_OK) {
return err; return err;
} }
if (!sd_card_initialized) { if ((cart_type != CART_NULL) && (!sd_card_initialized)) {
return FLASHCART_ERR_SD_CARD; 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; return err;
} }
if (flashcart->set_save_writeback) { if (!flashcart->set_save_writeback) {
for (int i = 0; i < SAVE_WRITEBACK_MAX_SECTORS; i++) { return FLASHCART_OK;
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;
}
} }
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) { flashcart_err_t flashcart_load_64dd_ipl (char *ipl_path, flashcart_progress_callback_t *progress) {

View File

@ -15,7 +15,6 @@
/** @brief Flashcart error enumeration */ /** @brief Flashcart error enumeration */
typedef enum { typedef enum {
FLASHCART_OK, FLASHCART_OK,
FLASHCART_ERR_NOT_DETECTED,
FLASHCART_ERR_OUTDATED, FLASHCART_ERR_OUTDATED,
FLASHCART_ERR_SD_CARD, FLASHCART_ERR_SD_CARD,
FLASHCART_ERR_ARGS, FLASHCART_ERR_ARGS,
@ -75,12 +74,12 @@ typedef struct {
/** @brief The flashcart set save type function */ /** @brief The flashcart set save type function */
flashcart_err_t (*set_save_type) (flashcart_save_type_t save_type); flashcart_err_t (*set_save_type) (flashcart_save_type_t save_type);
/** @brief The flashcart set save writeback function */ /** @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; } flashcart_t;
char *flashcart_convert_error_message (flashcart_err_t err); 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); flashcart_err_t flashcart_deinit (void);
bool flashcart_has_feature (flashcart_features_t feature); 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); flashcart_err_t flashcart_load_rom (char *rom_path, bool byte_swap, flashcart_progress_callback_t *progress);

View File

@ -5,13 +5,6 @@
#include "utils/utils.h" #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) { void pi_dma_read_data (void *src, void *dst, size_t length) {
data_cache_hit_writeback_invalidate(dst, length); data_cache_hit_writeback_invalidate(dst, length);
dma_read_async(dst, (uint32_t) (src), 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_write_raw_async(src, (uint32_t) (dst), length);
dma_wait(); 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;
}

View File

@ -8,12 +8,26 @@
#define FLASHCART_UTILS_H__ #define FLASHCART_UTILS_H__
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <fatfs/ff.h> #include <fatfs/ff.h>
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_read_data (void *src, void *dst, size_t length);
void pi_dma_write_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 #endif

View File

@ -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 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) { 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; size_t erase_block_size;
UINT bp; UINT bp;
@ -110,23 +107,6 @@ static flashcart_err_t load_to_flash (FIL *fil, void *address, size_t size, UINT
return FLASHCART_OK; 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) { 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++) { 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); 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; int file_offset = 0;
uint16_t lba = 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; uint16_t track = track_offset + zone_track;
if (disk_zone_track_is_bad(pzone, zone_track, disk_parameters)) { 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(*current_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, 1, false, false, 0);
continue; continue;
} }
for (uint8_t block = 0; block < DISK_BLOCKS; block += 1) { for (uint8_t block = 0; block < DISK_BLOCKS; block += 1) {
bool valid = !(disk_system_lba_is_bad(lba, disk_parameters)); bool valid = !(disk_system_lba_is_bad(lba, disk_parameters));
bool writable = (vzone >= rom_zones[disk_parameters->disk_type]); 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); file_offset += (sector_length * DISK_SECTORS_PER_BLOCK);
lba += 1; 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; FIL fil;
UINT br; 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; return FLASHCART_ERR_LOAD;
} }
fix_file_size(&fil); fatfs_fix_file_size(&fil);
size_t rom_size = f_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; FIL fil;
UINT br; 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; return FLASHCART_ERR_LOAD;
} }
fix_file_size(&fil); fatfs_fix_file_size(&fil);
size_t file_size = f_size(&fil) - file_offset; 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; FIL fil;
UINT br; 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; return FLASHCART_ERR_LOAD;
} }
@ -439,11 +431,11 @@ static flashcart_err_t sc64_load_64dd_ipl (char *ipl_path, flashcart_progress_ca
FIL fil; FIL fil;
UINT br; 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; return FLASHCART_ERR_LOAD;
} }
fix_file_size(&fil); fatfs_fix_file_size(&fil);
size_t ipl_size = f_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) { static flashcart_err_t sc64_load_64dd_disk (char *disk_path, flashcart_disk_parameters_t *disk_parameters) {
sc64_disk_mapping_t mapping = { .count = 0 }; sc64_disk_mapping_t mapping;
uint32_t next_mapping_offset = DISK_MAPPING_ROM_OFFSET; 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 // TODO: Support loading multiple disks
// LOOP START for (mapping.count = 0; mapping.count < 1; mapping.count++) {
mapping.disks[mapping.count].thb_table = next_mapping_offset; disk_load_thb_table(disk_parameters++, &mapping.disks[mapping.count].thb_table, &mapping_offset);
mapping.disks[mapping.count].sector_table = disk_load_thb_table(mapping.disks[mapping.count].thb_table, disk_parameters); if (disk_load_sector_table(disk_path++, &mapping.disks[mapping.count].sector_table, &mapping_offset)) {
next_mapping_offset = disk_sectors_start(mapping.disks[mapping.count].sector_table);
if (file_get_sectors(disk_path, disk_sectors_callback)) {
return FLASHCART_ERR_LOAD; return FLASHCART_ERR_LOAD;
} }
mapping.count += 1; }
// LOOP END
if (mapping.count == 0) { if (mapping.count == 0) {
return FLASHCART_ERR_ARGS; 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; return FLASHCART_ERR_INT;
} }
sc64_drive_type_t drive_type = disk_parameters->development_drive ? DRIVE_TYPE_DEVELOPMENT : DRIVE_TYPE_RETAIL;
const struct { const struct {
sc64_cfg_id_t id; sc64_cfg_id_t id;
uint32_t value; uint32_t value;
@ -559,7 +547,13 @@ static flashcart_err_t sc64_set_save_type (flashcart_save_type_t save_type) {
return FLASHCART_OK; 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); pi_dma_write_data(sectors, SC64_BUFFERS->BUFFER, 1024);
if (sc64_ll_writeback_enable(SC64_BUFFERS->BUFFER) != SC64_OK) { if (sc64_ll_writeback_enable(SC64_BUFFERS->BUFFER) != SC64_OK) {

2
src/libs/miniz vendored

@ -1 +1 @@
Subproject commit 18795fa61e590521381ba9e1fa4a4ab362b095f6 Subproject commit 16413c213de38e703d883006193734e8b1178d5d

View File

@ -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; 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; flashcart_disk_parameters_t disk_parameters;
disk_parameters.development_drive = (menu->load.disk_info.region == DISK_REGION_DEVELOPMENT); 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) { 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; flashcart_save_type_t save_type = FLASHCART_SAVE_TYPE_NONE;
uint32_t emulated_rom_offset = 0x200000; uint32_t emulated_rom_offset = 0x200000;

View File

@ -64,7 +64,7 @@ typedef struct {
surface_t *image; surface_t *image;
} component_boxart_t; } 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_free (component_boxart_t *b);
void component_boxart_draw (component_boxart_t *b); void component_boxart_draw (component_boxart_t *b);

View File

@ -1,4 +1,4 @@
#include <fatfs/ff.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "../components.h" #include "../components.h"
@ -31,22 +31,21 @@ static void load_from_cache (component_background_t *c) {
return; return;
} }
FIL fil; FILE *f;
UINT bytes_read;
if (f_open(&fil, strip_sd_prefix(c->cache_location), FA_READ) != FR_OK) { if ((f = fopen(c->cache_location, "rb")) == NULL) {
return; return;
} }
cache_metadata_t cache_metadata; cache_metadata_t cache_metadata;
if ((f_read(&fil, &cache_metadata, sizeof(cache_metadata), &bytes_read) != FR_OK) || (bytes_read != sizeof(cache_metadata))) { if (fread(&cache_metadata, sizeof(cache_metadata), 1, f) != 1) {
f_close(&fil); fclose(f);
return; return;
} }
if (cache_metadata.magic != CACHE_METADATA_MAGIC || cache_metadata.width > DISPLAY_WIDTH || cache_metadata.height > DISPLAY_HEIGHT) { if (cache_metadata.magic != CACHE_METADATA_MAGIC || cache_metadata.width > DISPLAY_WIDTH || cache_metadata.height > DISPLAY_HEIGHT) {
f_close(&fil); fclose(f);
return; return;
} }
@ -57,17 +56,17 @@ static void load_from_cache (component_background_t *c) {
surface_free(c->image); surface_free(c->image);
free(c->image); free(c->image);
c->image = NULL; c->image = NULL;
f_close(&fil); fclose(f);
return; 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); surface_free(c->image);
free(c->image); free(c->image);
c->image = NULL; c->image = NULL;
} }
f_close(&fil); fclose(f);
} }
static void save_to_cache (component_background_t *c) { static void save_to_cache (component_background_t *c) {
@ -75,9 +74,9 @@ static void save_to_cache (component_background_t *c) {
return; return;
} }
FIL fil; FILE *f;
UINT bytes_written;
if (f_open(&fil, strip_sd_prefix(c->cache_location), FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) { if ((f = fopen(c->cache_location, "wb")) == NULL) {
return; return;
} }
@ -88,10 +87,10 @@ static void save_to_cache (component_background_t *c) {
.size = (c->image->height * c->image->stride), .size = (c->image->height * c->image->stride),
}; };
f_write(&fil, &cache_metadata, sizeof(cache_metadata), &bytes_written); fwrite(&cache_metadata, sizeof(cache_metadata), 1, f);
f_write(&fil, c->image->buffer, cache_metadata.size, &bytes_written); fwrite(c->image->buffer, cache_metadata.size, 1, f);
f_close(&fil); fclose(f);
} }
static void prepare_background (component_background_t *c) { 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) { void component_background_init (char *cache_location) {
if (!background) { if (!background) {
background = calloc(1, sizeof(component_background_t)); background = calloc(1, sizeof(component_background_t));
background->cache_location = cache_location; background->cache_location = strdup(cache_location);
load_from_cache(background); load_from_cache(background);
prepare_background(background); prepare_background(background);
} }
@ -183,6 +182,9 @@ void component_background_free (void) {
rdpq_call_deferred(display_list_free, background->image_display_list); rdpq_call_deferred(display_list_free, background->image_display_list);
background->image_display_list = NULL; background->image_display_list = NULL;
} }
if (background->cache_location) {
free(background->cache_location);
}
free(background); free(background);
background = NULL; background = NULL;
} }

View File

@ -6,9 +6,8 @@
#include "constants.h" #include "constants.h"
#include "utils/fs.h" #include "utils/fs.h"
#ifndef BOXART_DIRECTORY
#define BOXART_DIRECTORY "sd:/menu/boxart" #define BOXART_DIRECTORY "menu/boxart"
#endif
static void png_decoder_callback (png_err_t err, surface_t *decoded_image, void *callback_data) { 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 *component_boxart_init (const char *storage_prefix, char *game_code) {
component_boxart_t *b = calloc(1, sizeof(component_boxart_t)); component_boxart_t *b;
char file_name[8];
if ((b = calloc(1, sizeof(component_boxart_t))) == NULL) {
return NULL;
}
if (b) {
b->loading = true; b->loading = true;
char *path = alloca(strlen(BOXART_DIRECTORY) + 1 + 7 + 1);
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 // TODO: This is bad, we should only check for 3 letter codes
sprintf(path, "%s/%.3s.png", BOXART_DIRECTORY, game_code); sprintf(file_name, "%.2s.png", game_code + 1);
if (png_decoder_start(path, BOXART_WIDTH, BOXART_HEIGHT, png_decoder_callback, b) != PNG_OK) { path_push(path, file_name);
sprintf(path, "%s/%.2s.png", BOXART_DIRECTORY, &game_code[1]); if (png_decoder_start(path_get(path), BOXART_WIDTH, BOXART_HEIGHT, png_decoder_callback, b) == PNG_OK) {
if (png_decoder_start(path, BOXART_WIDTH, BOXART_HEIGHT, png_decoder_callback, b) != PNG_OK) { path_free(path);
free(b); return b;
b = NULL;
}
}
} }
return b; path_free(path);
free(b);
return NULL;
} }
void component_boxart_free (component_boxart_t *b) { void component_boxart_free (component_boxart_t *b) {

View File

@ -121,6 +121,10 @@ void component_messagebox_draw (char *fmt, ...) {
.wrap = WRAP_WORD .wrap = WRAP_WORD
}, FNT_DEFAULT, formatted, &paragraph_nbytes); }, FNT_DEFAULT, formatted, &paragraph_nbytes);
if (formatted != buffer) {
free(formatted);
}
component_dialog_draw( component_dialog_draw(
paragraph->bbox.x1 - paragraph->bbox.x0 + MESSAGEBOX_MARGIN, paragraph->bbox.x1 - paragraph->bbox.x0 + MESSAGEBOX_MARGIN,
paragraph->bbox.y1 - paragraph->bbox.y0 + 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, formatted,
nbytes nbytes
); );
if (formatted != buffer) {
free(formatted);
}
} }
void component_actions_bar_text_draw (rdpq_align_t align, rdpq_valign_t valign, char *fmt, ...) { 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, formatted,
nbytes nbytes
); );
if (formatted != buffer) {
free(formatted);
}
} }

View File

@ -8,15 +8,19 @@
static const char *dir_prefix = "/"; static const char *dir_prefix = "/";
static int format_file_size (char *buffer, int size) { static int format_file_size (char *buffer, int64_t size) {
if (size < 8 * 1024) { if (size < 0) {
return sprintf(buffer, "%d B", size); 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) { } 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) { } else if (size < 1 * 1024 * 1024 * 1024) {
return sprintf(buffer, "%d MB", size / 1024 / 1024); return sprintf(buffer, "%lld MB", size / 1024 / 1024);
} else { } 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 NULL
); );
char file_size[8]; char file_size[16];
for (int i = starting_position; i < entries; i++) { for (int i = starting_position; i < entries; i++) {
entry_t *entry = &list[i]; entry_t *entry = &list[i];

View File

@ -1,6 +1,6 @@
#include <fatfs/ff.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include <string.h> #include <string.h>
#include "disk_info.h" #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) { static bool load_system_area_lba (FILE *f, int lba, uint8_t *buffer) {
UINT bytes_read;
if (lba >= SYSTEM_AREA_LBA_COUNT) { if (lba >= SYSTEM_AREA_LBA_COUNT) {
return true; 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; return true;
} }
if (f_read(fil, buffer, SYSTEM_AREA_LBA_LENGTH, &bytes_read) != FR_OK) { if (fread(buffer, SYSTEM_AREA_LBA_LENGTH, 1, f) != 1) {
return true;
}
if (bytes_read != SYSTEM_AREA_LBA_LENGTH) {
return true; return true;
} }
return false; 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]; uint8_t buffer[SYSTEM_AREA_LBA_LENGTH];
int sector_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++) { for (int i = 0; i < SYSTEM_DATA_LBA_COUNT; i++) {
int lba = system_data_lbas[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; 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; 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]; uint8_t buffer[SYSTEM_AREA_LBA_LENGTH];
bool valid_disk_id_lba_found = false; 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++) { for (int i = 0; i < DISK_ID_LBA_COUNT; i++) {
int lba = disk_id_lbas[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; 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) { disk_err_t disk_info_load (path_t *path, disk_info_t *disk_info) {
FIL fil; FILE *f;
disk_err_t err; disk_err_t err;
for (int i = 0; i < SYSTEM_AREA_LBA_COUNT; i++) { for (int i = 0; i < SYSTEM_AREA_LBA_COUNT; i++) {
disk_info->bad_system_area_lbas[i] = false; 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; return DISK_ERR_NO_FILE;
} }
setbuf(f, NULL);
if ((err = load_and_verify_system_data_lba(&fil, disk_info)) != DISK_OK) { if ((err = load_and_verify_system_data_lba(f, disk_info)) != DISK_OK) {
f_close(&fil); fclose(f);
return err; return err;
} }
if ((err = load_and_verify_disk_id_lba(f, disk_info)) != DISK_OK) {
if ((err = load_and_verify_disk_id_lba(&fil, disk_info)) != DISK_OK) { fclose(f);
f_close(&fil);
return err; return err;
} }
if (fclose(f)) {
if (f_close(&fil) != FR_OK) {
return DISK_ERR_IO; return DISK_ERR_IO;
} }

View File

@ -11,6 +11,9 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include "path.h"
/** @brief Disk state enumeration. */ /** @brief Disk state enumeration. */
typedef enum { typedef enum {
DISK_OK, DISK_OK,
@ -49,7 +52,7 @@ typedef struct {
} disk_info_t; } 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 #endif

View File

@ -1,10 +1,17 @@
#include <libdragon.h> #include <libdragon.h>
#include "fonts.h" #include "fonts.h"
#include "utils/fs.h"
static void load_default_font (void) { static void load_default_font (char *custom_font_path) {
rdpq_font_t *default_font = rdpq_font_load("rom:/FiraMonoBold.font64"); 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_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) })); 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) { void fonts_init (char *custom_font_path) {
load_default_font(); load_default_font(custom_font_path);
} }

View File

@ -23,7 +23,7 @@ typedef enum {
} menu_font_style_t; } menu_font_style_t;
void fonts_init (void); void fonts_init (char *custom_font_path);
#endif #endif

View File

@ -20,9 +20,12 @@
#include "views/views.h" #include "views/views.h"
#define MENU_DIRECTORY "sd:/menu" #define MENU_DIRECTORY "/menu"
#define CACHE_DIRECTORY "sd:/menu/cache" #define MENU_SETTINGS_FILE "config.ini"
#define BACKGROUND_CACHE "sd:/menu/cache/background.data" #define MENU_CUSTOM_FONT_FILE "custom.font64"
#define MENU_CACHE_DIRECTORY "cache"
#define BACKGROUND_CACHE_FILE "background.data"
#define FRAMERATE_DIVIDER (2) #define FRAMERATE_DIVIDER (2)
#define LAG_REPORT (false) #define LAG_REPORT (false)
@ -62,7 +65,6 @@ static void menu_init (boot_params_t *boot_params) {
rdpq_init(); rdpq_init();
dfs_init(DFS_DEFAULT_LOCATION); dfs_init(DFS_DEFAULT_LOCATION);
fonts_init();
sound_init_default(); sound_init_default();
menu = calloc(1, sizeof(menu_t)); 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->mode = MENU_MODE_NONE;
menu->next_mode = MENU_MODE_STARTUP; 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) { if (menu->flashcart_err != FLASHCART_OK) {
menu->next_mode = MENU_MODE_FAULT; 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); 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; menu->boot_params = boot_params;
bool default_directory_exists = directory_exists(menu->settings.default_directory); menu->browser.directory = path_init(menu->storage_prefix, menu->settings.default_directory);
char *init_directory = default_directory_exists ? menu->settings.default_directory : ""; if (!directory_exists(path_get(menu->browser.directory))) {
path_free(menu->browser.directory);
menu->browser.valid = false; menu->browser.directory = path_init(menu->storage_prefix, "/");
menu->browser.reload = false; }
menu->browser.directory = path_init("sd:/", init_directory);
menu->load.rom_path = NULL;
hdmi_clear_game_id(); hdmi_clear_game_id();
@ -118,6 +128,12 @@ static void menu_deinit (menu_t *menu) {
hdmi_send_game_id(menu->boot_params); 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); path_free(menu->browser.directory);
free(menu); free(menu);

View File

@ -18,9 +18,6 @@
#include "settings.h" #include "settings.h"
#define BROWSER_LIST_SIZE 2048
/** @brief Menu mode enumeration */ /** @brief Menu mode enumeration */
typedef enum { typedef enum {
MENU_MODE_NONE, MENU_MODE_NONE,
@ -60,7 +57,7 @@ typedef enum {
typedef struct { typedef struct {
char *name; char *name;
entry_type_t type; entry_type_t type;
int size; int64_t size;
} entry_t; } entry_t;
/** @brief Menu Structure */ /** @brief Menu Structure */
@ -68,6 +65,7 @@ typedef struct {
menu_mode_t mode; menu_mode_t mode;
menu_mode_t next_mode; menu_mode_t next_mode;
const char *storage_prefix;
settings_t settings; settings_t settings;
boot_params_t *boot_params; boot_params_t *boot_params;
@ -93,7 +91,7 @@ typedef struct {
bool valid; bool valid;
bool reload; bool reload;
path_t *directory; path_t *directory;
entry_t list[BROWSER_LIST_SIZE]; entry_t *list;
int entries; int entries;
entry_t *entry; entry_t *entry;
int selected; int selected;

View File

@ -1,4 +1,6 @@
#include <fatfs/ff.h> #include <stdio.h>
#include <sys/stat.h>
#include <libdragon.h> #include <libdragon.h>
#include "mp3_player.h" #include "mp3_player.h"
@ -18,19 +20,18 @@
/** @brief MP3 File Information Structure. */ /** @brief MP3 File Information Structure. */
typedef struct { typedef struct {
bool loaded; 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[16 * 1024];
uint8_t *buffer_ptr; uint8_t *buffer_ptr;
size_t buffer_left; size_t buffer_left;
mp3dec_t dec;
mp3dec_frame_info_t info;
int seek_predecode_frames;
float duration; float duration;
float bitrate; float bitrate;
@ -48,9 +49,7 @@ static void mp3player_reset_decoder (void) {
} }
static void mp3player_fill_buffer (void) { static void mp3player_fill_buffer (void) {
UINT bytes_read; if (feof(p->f)) {
if (f_eof(&p->fil)) {
return; return;
} }
@ -63,11 +62,7 @@ static void mp3player_fill_buffer (void) {
p->buffer_ptr = p->buffer; 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 += fread(p->buffer + p->buffer_left, 1, sizeof(p->buffer) - p->buffer_left, p->f);
p->buffer_left += bytes_read;
} else {
p->io_error = true;
}
} }
static void mp3player_wave_read (void *ctx, samplebuffer_t *sbuf, int wpos, int wlen, bool seeking) { 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; uint32_t frames;
int delay, padding; 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) { 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->duration = (frames * samples) / (float) (p->info.hz);
p->bitrate = (data_size * 8) / p->duration; p->bitrate = (data_size * 8) / p->duration;
@ -137,7 +132,6 @@ mp3player_err_t mp3player_init (void) {
mp3player_reset_decoder(); mp3player_reset_decoder();
p->loaded = false; p->loaded = false;
p->io_error = false;
p->wave = (waveform_t) { p->wave = (waveform_t) {
.name = "mp3player", .name = "mp3player",
@ -164,22 +158,32 @@ mp3player_err_t mp3player_load (char *path) {
mp3player_unload(); 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; 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(); mp3player_reset_decoder();
while (!(f_eof(&p->fil) && p->buffer_left == 0)) { while (!(feof(p->f) && p->buffer_left == 0)) {
mp3player_fill_buffer(); mp3player_fill_buffer();
if (p->io_error) { if (ferror(p->f)) {
fclose(p->f);
return MP3PLAYER_ERR_IO; return MP3PLAYER_ERR_IO;
} }
size_t id3v2_skip = mp3dec_skip_id3v2((const uint8_t *) (p->buffer_ptr), p->buffer_left); size_t id3v2_skip = mp3dec_skip_id3v2((const uint8_t *) (p->buffer_ptr), p->buffer_left);
if (id3v2_skip > 0) { 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; return MP3PLAYER_ERR_IO;
} }
mp3player_reset_decoder(); 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); int samples = mp3dec_decode_frame(&p->dec, p->buffer_ptr, p->buffer_left, NULL, &p->info);
if (samples > 0) { if (samples > 0) {
p->loaded = true; 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_ptr += p->info.frame_offset;
p->buffer_left -= 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; p->buffer_left -= p->info.frame_bytes;
} }
if (f_close(&p->fil) != FR_OK) { if (fclose(p->f)) {
return MP3PLAYER_ERR_IO; return MP3PLAYER_ERR_IO;
} }
@ -217,12 +221,12 @@ void mp3player_unload (void) {
mp3player_stop(); mp3player_stop();
if (p->loaded) { if (p->loaded) {
p->loaded = false; p->loaded = false;
f_close(&p->fil); fclose(p->f);
} }
} }
mp3player_err_t mp3player_process (void) { mp3player_err_t mp3player_process (void) {
if (p->io_error) { if (ferror(p->f)) {
mp3player_unload(); mp3player_unload();
return MP3PLAYER_ERR_IO; return MP3PLAYER_ERR_IO;
} }
@ -239,7 +243,7 @@ bool mp3player_is_playing (void) {
} }
bool mp3player_is_finished (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) { mp3player_err_t mp3player_play (void) {
@ -248,8 +252,7 @@ mp3player_err_t mp3player_play (void) {
} }
if (!mp3player_is_playing()) { if (!mp3player_is_playing()) {
if (mp3player_is_finished()) { if (mp3player_is_finished()) {
if (f_lseek(&p->fil, p->data_start) != FR_OK) { if (fseek(p->f, p->data_start, SEEK_SET)) {
p->io_error = true;
return MP3PLAYER_ERR_IO; return MP3PLAYER_ERR_IO;
} }
mp3player_reset_decoder(); mp3player_reset_decoder();
@ -292,25 +295,24 @@ mp3player_err_t mp3player_seek (int seconds) {
return MP3PLAYER_OK; 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)) { if (position < (long) (p->data_start)) {
position = p->data_start; position = p->data_start;
} }
if (f_lseek(&p->fil, position) != FR_OK) { if (fseek(p->f, position, SEEK_SET)) {
p->io_error = true;
return MP3PLAYER_ERR_IO; return MP3PLAYER_ERR_IO;
} }
mp3player_reset_decoder(); mp3player_reset_decoder();
mp3player_fill_buffer(); mp3player_fill_buffer();
p->seek_predecode_frames = (position == p->data_start) ? 0 : SEEK_PREDECODE_FRAMES; if (ferror(p->f)) {
if (p->io_error) {
return MP3PLAYER_ERR_IO; return MP3PLAYER_ERR_IO;
} }
p->seek_predecode_frames = (position == p->data_start) ? 0 : SEEK_PREDECODE_FRAMES;
return MP3PLAYER_OK; return MP3PLAYER_OK;
} }
@ -346,9 +348,9 @@ float mp3player_get_progress (void) {
return 0.0f; return 0.0f;
} }
FSIZE_t data_size = f_size(&p->fil) - p->data_start; long data_size = p->file_size - p->data_start;
FSIZE_t data_consumed = f_tell(&p->fil) - p->buffer_left; long data_consumed = ftell(p->f) - p->buffer_left;
FSIZE_t data_position = (data_consumed > p->data_start) ? (data_consumed - p->data_start) : 0; long data_position = (data_consumed > p->data_start) ? (data_consumed - p->data_start) : 0;
return data_position / (float) (data_size); return data_position / (float) (data_size);
} }

View File

@ -19,7 +19,7 @@ static void path_resize (path_t *path, size_t min_length) {
assert(path->buffer != NULL); assert(path->buffer != NULL);
} }
static path_t *path_create (char *string) { static path_t *path_create (const char *string) {
if (string == NULL) { if (string == NULL) {
string = ""; 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); path_t *path = path_create(prefix);
size_t prefix_length = strlen(prefix); size_t prefix_length = strlen(prefix);
if ((prefix_length > 0) && (prefix[prefix_length - 1] == '/')) { if ((prefix_length > 0) && (prefix[prefix_length - 1] == '/')) {

View File

@ -20,7 +20,7 @@ typedef struct {
} path_t; } 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); void path_free (path_t *path);
path_t *path_clone (path_t *string); path_t *path_clone (path_t *string);
path_t *path_clone_push (path_t *path, char *string); path_t *path_clone_push (path_t *path, char *string);

View File

@ -1,4 +1,5 @@
#include <fatfs/ff.h> #include <stdio.h>
#include <libspng/spng/spng.h> #include <libspng/spng/spng.h>
#include "png_decoder.h" #include "png_decoder.h"
@ -7,7 +8,7 @@
/** @brief PNG File Information Structure. */ /** @brief PNG File Information Structure. */
typedef struct { typedef struct {
FIL fil; FILE *f;
spng_ctx *ctx; spng_ctx *ctx;
struct spng_ihdr ihdr; struct spng_ihdr ihdr;
@ -25,7 +26,7 @@ static png_decoder_t *decoder;
static void png_decoder_deinit (bool free_image) { static void png_decoder_deinit (bool free_image) {
if (decoder != NULL) { if (decoder != NULL) {
f_close(&decoder->fil); fclose(decoder->f);
if (decoder->ctx != NULL) { if (decoder->ctx != NULL) {
spng_ctx_free(decoder->ctx); 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) { png_err_t png_decoder_start (char *path, int max_width, int max_height, png_callback_t *callback, void *callback_data) {
if (decoder != NULL) { 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; 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); png_decoder_deinit(false);
return PNG_ERR_NO_FILE; return PNG_ERR_NO_FILE;
} }
setbuf(decoder->f, NULL);
if ((decoder->ctx = spng_ctx_new(SPNG_CTX_IGNORE_ADLER32)) == NULL) { if ((decoder->ctx = spng_ctx_new(SPNG_CTX_IGNORE_ADLER32)) == NULL) {
png_decoder_deinit(false); png_decoder_deinit(false);
return PNG_ERR_OUT_OF_MEM; 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; 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); png_decoder_deinit(false);
return PNG_ERR_INT; return PNG_ERR_INT;
} }

View File

@ -1,6 +1,6 @@
#include <stdio.h>
#include <string.h> #include <string.h>
#include <fatfs/ff.h>
#include <mini.c/src/mini.h> #include <mini.c/src/mini.h>
#include "boot/cic.h" #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); mini_free(ini);
if (empty) { if (empty) {
if (file_delete(path_get(overrides_path))) { if (remove(path_get(overrides_path))) {
path_free(overrides_path); path_free(overrides_path);
return ROM_ERR_IO; 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) { rom_err_t rom_info_load (path_t *path, rom_info_t *rom_info) {
FIL fil; FILE *f;
UINT br;
rom_header_t rom_header; 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; return ROM_ERR_NO_FILE;
} }
if (f_read(&fil, &rom_header, sizeof(rom_header), &br) != FR_OK) { setbuf(f, NULL);
f_close(&fil); if (fread(&rom_header, sizeof(rom_header), 1, f) != 1) {
fclose(f);
return ROM_ERR_IO; return ROM_ERR_IO;
} }
if (f_close(&fil) != FR_OK) { if (fclose(f)) {
return ROM_ERR_IO;
}
if (br != sizeof(rom_header)) {
return ROM_ERR_IO; return ROM_ERR_IO;
} }

View File

@ -5,14 +5,12 @@
#include "utils/fs.h" #include "utils/fs.h"
#ifndef SETTINGS_FILE_PATH static char *settings_path = NULL;
#define SETTINGS_FILE_PATH "sd:/menu/config.ini"
#endif
static settings_t init = { static settings_t init = {
.pal60_enabled = false, .pal60_enabled = false,
.hidden_files_enabled = false, .show_protected_entries = false,
.default_directory = "/", .default_directory = "/",
.use_saves_folder = true, .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) { void settings_load (settings_t *settings) {
if (!file_exists(SETTINGS_FILE_PATH)) { if (!file_exists(settings_path)) {
settings_save(&init); 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->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->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); 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) { 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", "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_string(ini, "menu", "default_directory", settings->default_directory);
mini_set_bool(ini, "menu", "use_saves_folder", settings->use_saves_folder); mini_set_bool(ini, "menu", "use_saves_folder", settings->use_saves_folder);

View File

@ -13,8 +13,8 @@ typedef struct {
/** @brief Use 60 Hz refresh rate on a PAL console */ /** @brief Use 60 Hz refresh rate on a PAL console */
bool pal60_enabled; bool pal60_enabled;
/** @brief Show files marked as hidden in the browser */ /** @brief Show files/directories that are filtered in the browser */
bool hidden_files_enabled; bool show_protected_entries;
/** @brief Default directory to navigate to when menu loads */ /** @brief Default directory to navigate to when menu loads */
char *default_directory; char *default_directory;
@ -33,6 +33,8 @@ typedef struct {
} settings_t; } settings_t;
/** @brief Init settings path */
void settings_init (char *path);
/** @brief The settings to load */ /** @brief The settings to load */
void settings_load (settings_t *settings); void settings_load (settings_t *settings);
/** @brief The settings to save */ /** @brief The settings to save */

View File

@ -2,8 +2,9 @@
// Main use of these functions is to aid menu development // Main use of these functions is to aid menu development
// (for example replace files on the SD card or reboot menu). // (for example replace files on the SD card or reboot menu).
#include <fatfs/ff.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <usb.h> #include <usb.h>
#include "usb_comm.h" #include "usb_comm.h"
@ -73,15 +74,12 @@ static void command_reboot (menu_t *menu) {
}; };
static void command_send_file (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]; char length[8];
FIL f; if (usb_comm_read_string(buffer, sizeof(buffer), ' ')) {
int remaining;
uint8_t data[8192];
UINT bytes_written;
if (usb_comm_read_string(path, sizeof(path), ' ')) {
return usb_comm_send_error("Invalid path argument\n"); 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"); 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"); 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) { if (remaining > MAX_FILE_SIZE) {
return usb_comm_send_error("File size too big\n"); 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) { while (remaining > 0) {
int block_size = MIN(remaining, sizeof(data)); int block_size = MIN(remaining, sizeof(data));
usb_read(data, block_size); usb_read(data, block_size);
if (f_write(&f, data, block_size, &bytes_written) != FR_OK) { if (fwrite(data, 1, block_size, f) != block_size) {
f_close(&f); fclose(f);
return usb_comm_send_error("Couldn't write data to the file\n");
}
if (bytes_written != block_size) {
f_close(&f);
return usb_comm_send_error("Couldn't write all required data to the file\n"); return usb_comm_send_error("Couldn't write all required data to the file\n");
} }
remaining -= block_size; 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"); return usb_comm_send_error("Couldn't flush data to the file\n");
} }

View File

@ -1,9 +1,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/errno.h>
#include <time.h> #include <time.h>
#include <fatfs/ff.h>
#include "../fonts.h" #include "../fonts.h"
#include "utils/fs.h" #include "utils/fs.h"
#include "views.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 *text_extensions[] = { "txt", "ini", "yml", "yaml", NULL };
static const char *music_extensions[] = { "mp3", 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) { static int compare_entry (const void *pa, const void *pb) {
entry_t *a = (entry_t *) (pa); 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)); return strcasecmp((const char *) (a->name), (const char *) (b->name));
} }
static bool load_directory (menu_t *menu) { static void browser_list_free (menu_t *menu) {
DIR dir;
FILINFO info;
for (int i = menu->browser.entries - 1; i >= 0; i--) { for (int i = menu->browser.entries - 1; i >= 0; i--) {
free(menu->browser.list[i].name); free(menu->browser.list[i].name);
} }
free(menu->browser.list);
menu->browser.list = NULL;
menu->browser.entries = 0; menu->browser.entries = 0;
menu->browser.selected = -1;
menu->browser.entry = NULL; menu->browser.entry = NULL;
menu->browser.selected = -1;
}
if (f_opendir(&dir, strip_sd_prefix(path_get(menu->browser.directory))) != FR_OK) { static bool load_directory (menu_t *menu) {
return true; int result;
} dir_t info;
while (menu->browser.entries < BROWSER_LIST_SIZE) { browser_list_free(menu);
if (f_readdir(&dir, &info) != FR_OK) {
return true;
}
size_t length = strlen(info.fname); path_t *path = path_clone(menu->browser.directory);
if (length == 0) { 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; 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]; path_pop(path);
}
entry->name = strdup(info.fname); 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) { if (!entry->name) {
f_closedir(&dir); path_free(path);
browser_list_free(menu);
return true; return true;
} }
if (info.fattrib & AM_DIR) { if (info.d_type == DT_DIR) {
entry->type = ENTRY_TYPE_DIR; entry->type = ENTRY_TYPE_DIR;
} else if (file_has_extensions(info.fname, rom_extensions)) { } else if (file_has_extensions(entry->name, rom_extensions)) {
entry->type = ENTRY_TYPE_ROM; entry->type = ENTRY_TYPE_ROM;
} else if (file_has_extensions(info.fname, disk_extensions)) { } else if (file_has_extensions(entry->name, disk_extensions)) {
entry->type = ENTRY_TYPE_DISK; entry->type = ENTRY_TYPE_DISK;
}else if (file_has_extensions(info.fname, emulator_extensions)) { }else if (file_has_extensions(entry->name, emulator_extensions)) {
entry->type = ENTRY_TYPE_EMULATOR; entry->type = ENTRY_TYPE_EMULATOR;
} else if (file_has_extensions(info.fname, save_extensions)) { } else if (file_has_extensions(entry->name, save_extensions)) {
entry->type = ENTRY_TYPE_SAVE; entry->type = ENTRY_TYPE_SAVE;
} else if (file_has_extensions(info.fname, image_extensions)) { } else if (file_has_extensions(entry->name, image_extensions)) {
entry->type = ENTRY_TYPE_IMAGE; entry->type = ENTRY_TYPE_IMAGE;
} else if (file_has_extensions(info.fname, text_extensions)) { } else if (file_has_extensions(entry->name, text_extensions)) {
entry->type = ENTRY_TYPE_TEXT; entry->type = ENTRY_TYPE_TEXT;
} else if (file_has_extensions(info.fname, music_extensions)) { } else if (file_has_extensions(entry->name, music_extensions)) {
entry->type = ENTRY_TYPE_MUSIC; entry->type = ENTRY_TYPE_MUSIC;
} else { } else {
entry->type = ENTRY_TYPE_OTHER; entry->type = ENTRY_TYPE_OTHER;
} }
entry->size = info.fsize; entry->size = info.d_size;
menu->browser.entries += 1;
} }
if (f_closedir(&dir) != FR_OK) { result = dir_findnext(path_get(path), &info);
}
path_free(path);
if (result < -1) {
browser_list_free(menu);
return true; return true;
} }
@ -205,19 +228,15 @@ static void show_properties (menu_t *menu, void *arg) {
static void delete_entry (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); path_t *path = path_clone_push(menu->browser.directory, menu->browser.entry->name);
if (remove(path_get(path))) {
if (menu->browser.entry->type == ENTRY_TYPE_DIR) { if (menu->browser.entry->type == ENTRY_TYPE_DIR) {
if (directory_delete(path_get(path))) {
menu_show_error(menu, "Couldn't delete directory\nDirectory might not be empty"); menu_show_error(menu, "Couldn't delete directory\nDirectory might not be empty");
path_free(path);
return;
}
} else { } else {
if (file_delete(path_get(path))) {
menu_show_error(menu, "Couldn't delete file"); menu_show_error(menu, "Couldn't delete file");
}
path_free(path); path_free(path);
return; return;
} }
}
path_free(path); 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) { static void set_default_directory (menu_t *menu, void *arg) {
free(menu->settings.default_directory); 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); settings_save(&menu->settings);
} }
@ -388,7 +407,7 @@ void view_browser_init (menu_t *menu) {
component_context_menu_init(&settings_context_menu); component_context_menu_init(&settings_context_menu);
if (load_directory(menu)) { if (load_directory(menu)) {
path_free(menu->browser.directory); 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"); menu_show_error(menu, "Error while opening initial directory");
} else { } else {
menu->browser.valid = true; menu->browser.valid = true;

View File

@ -1,4 +1,4 @@
#include <fatfs/ff.h> #include <sys/stat.h>
#include "utils/fs.h" #include "utils/fs.h"
#include "views.h" #include "views.h"
@ -16,7 +16,7 @@ static const char *dexdrive_extensions[] = { "mpk", NULL };
static const char *emulator_extensions[] = { "emu", 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) { 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"
"\n" "\n"
" Size: %d bytes\n" " Size: %d bytes\n"
" Attributes: %s%s%s%s%s\n" " Attributes: %s %s\n"
"%s" "%s"
" Modified: %u-%02u-%02u %02u:%02u", " Modified: %s",
info.fsize, st.st_size,
(info.fattrib & AM_DIR) ? "Directory " : "File ", S_ISDIR(st.st_mode) ? "Directory" : "File",
(info.fattrib & AM_RDO) ? "| Read only " : "", st.st_mode & S_IWUSR ? "" : "(Read only)",
(info.fattrib & AM_SYS) ? "| System " : "", format_file_type(menu->browser.entry->name, S_ISDIR(st.st_mode)),
(info.fattrib & AM_ARC) ? "| Archive " : "", ctime(&st.st_mtim.tv_sec)
(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
); );
component_actions_bar_text_draw( 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) { void view_file_info_init (menu_t *menu) {
path_t *path = path_clone_push(menu->browser.directory, menu->browser.entry->name); 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"); menu_show_error(menu, "Couldn't obtain file information");
} }

View File

@ -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); 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) { if (err != DISK_OK) {
menu_show_error(menu, convert_error_message(err)); menu_show_error(menu, convert_error_message(err));
} }

View File

@ -334,7 +334,7 @@ void view_load_rom_init (menu_t *menu) {
return; 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); component_context_menu_init(&options_context_menu);
} }

View File

@ -35,14 +35,14 @@ static void draw (menu_t *menu, surface_t *d) {
"To change the settings, please adjust them\n" "To change the settings, please adjust them\n"
"directly in the 'menu/config.ini' file.\n\n" "directly in the 'menu/config.ini' file.\n\n"
"pal60_enabled: %s\n" "pal60_enabled: %s\n"
"hidden_files_enabled: %s\n" "show_protected_entries: %s\n"
"default_directory: %s\n" "default_directory: %s\n"
"use_saves_folder: %s\n" "use_saves_folder: %s\n"
"bgm_enabled: %s\n" "bgm_enabled: %s\n"
"sound_enabled: %s\n" "sound_enabled: %s\n"
"rumble_enabled: %s\n", "rumble_enabled: %s\n",
format_switch(menu->settings.pal60_enabled), format_switch(menu->settings.pal60_enabled),
format_switch(menu->settings.hidden_files_enabled), format_switch(menu->settings.show_protected_entries),
menu->settings.default_directory, menu->settings.default_directory,
format_switch(menu->settings.use_saves_folder), format_switch(menu->settings.use_saves_folder),
format_switch(menu->settings.bgm_enabled), format_switch(menu->settings.bgm_enabled),

View File

@ -1,9 +1,13 @@
#include <fatfs/ff.h> #include <stdio.h>
#include <sys/stat.h>
#include "utils/fs.h" #include "utils/fs.h"
#include "utils/utils.h"
#include "views.h" #include "views.h"
static char *file_content;
static char *text;
static void process (menu_t *menu) { static void process (menu_t *menu) {
if (menu->actions.back) { if (menu->actions.back) {
@ -18,20 +22,15 @@ static void draw (menu_t *menu, surface_t *d) {
component_layout_draw(); component_layout_draw();
component_main_text_draw( if (text) {
ALIGN_CENTER, VALIGN_TOP,
"TEXT VIEWER\n"
"\n"
);
component_main_text_draw( component_main_text_draw(
ALIGN_LEFT, VALIGN_TOP, ALIGN_LEFT, VALIGN_TOP,
"\n"
"\n"
"%s\n", "%s\n",
file_content text
); );
} else {
component_messagebox_draw("Text file is empty");
}
component_actions_bar_text_draw( component_actions_bar_text_draw(
ALIGN_LEFT, VALIGN_TOP, ALIGN_LEFT, VALIGN_TOP,
@ -42,37 +41,62 @@ static void draw (menu_t *menu, surface_t *d) {
rdpq_detach_show(); rdpq_detach_show();
} }
static void deinit (void) {
if (text) {
free(text);
text = NULL;
}
}
void view_text_viewer_init (menu_t *menu) { void view_text_viewer_init (menu_t *menu) {
path_t *path = path_clone_push(menu->browser.directory, menu->browser.entry->name); 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. if ((f = fopen(path_get(path), "r")) == NULL) {
file_size = 1024; // For the moment, we just set it to that, since any more would be a waste. path_free(path);
} menu_show_error(menu, "Couldn't open text file");
return;
file_content = calloc(file_size, 1);
// read file content
FIL fil;
UINT br;
if (f_open(&fil, strip_sd_prefix(path_get(path)), FA_READ) != FR_OK) {
debugf("Error loading file\n");
}
if (f_read(&fil, file_content, file_size, &br) != FR_OK) {
f_close(&fil);
debugf("Error loading file content\n");
}
if (f_close(&fil) != FR_OK) {
debugf("Error closing file\n");
} }
path_free(path); 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) { void view_text_viewer_display (menu_t *menu, surface_t *display) {
process(menu); process(menu);
draw(menu, display); draw(menu, display);
if (menu->next_mode != MENU_MODE_TEXT_VIEWER) {
deinit();
}
} }

View File

@ -1,145 +1,85 @@
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <strings.h> #include <sys/errno.h>
#include <sys/stat.h>
#include <fatfs/ff.h>
#include "fs.h" #include "fs.h"
#include "utils.h" #include "utils.h"
char *strip_sd_prefix (char *path) { char *strip_fs_prefix (char *path) {
const char *prefix = "sd:/"; const char *prefix = ":/";
char *found = strstr(path, prefix); char *found = strstr(path, prefix);
if (found) { if (found) {
return found + strlen(prefix) - 1; return (found + strlen(prefix) - 1);
} }
return path; return path;
} }
bool file_exists (char *path) { bool file_exists (char *path) {
FRESULT fr; struct stat st;
FILINFO fno; int error = stat(path, &st);
return ((error == 0) && S_ISREG(st.st_mode));
fr = f_stat(strip_sd_prefix(path), &fno);
return ((fr == FR_OK) && (!(fno.fattrib & AM_DIR)));
} }
int64_t file_get_size (char *path) {
size_t file_get_size (char *path) { struct stat st;
FILINFO fno; if (stat(path, &st)) {
return -1;
if (f_stat(strip_sd_prefix(path), &fno) != FR_OK) {
return 0;
} }
return (int64_t) (st.st_size);
return (size_t) (fno.fsize);
} }
bool file_delete (char *path) { bool file_allocate (char *path, size_t size) {
if (file_exists(path)) { FILE *f;
return (f_unlink(strip_sd_prefix(path)) != FR_OK); 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; 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) { bool file_fill (char *path, uint8_t value) {
FIL fil; FILE *f;
bool error = false; bool error = false;
uint8_t buffer[FS_SECTOR_SIZE * 8]; uint8_t buffer[FS_SECTOR_SIZE * 8];
FRESULT res; size_t bytes_to_write;
UINT bytes_to_write;
UINT bytes_written;
for (int i = 0; i < sizeof(buffer); i++) { for (int i = 0; i < sizeof(buffer); i++) {
buffer[i] = value; buffer[i] = value;
} }
if (f_open(&fil, strip_sd_prefix(path), FA_WRITE) != FR_OK) { if ((f = fopen(path, "rb+")) == NULL) {
return true; return true;
} }
for (int i = 0; i < f_size(&fil); i += sizeof(buffer)) { setbuf(f, NULL);
bytes_to_write = MIN(f_size(&fil) - f_tell(&fil), sizeof(buffer));
res = f_write(&fil, buffer, bytes_to_write, &bytes_written); fseek(f, 0, SEEK_END);
if ((res != FR_OK) || (bytes_to_write != bytes_written)) { 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; error = true;
break; break;
} }
} }
if (f_tell(&fil) != f_size(&fil)) { if (fclose(f)) {
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) {
error = true; error = true;
} }
@ -165,20 +105,9 @@ bool file_has_extensions (char *path, const char *extensions[]) {
bool directory_exists (char *path) { bool directory_exists (char *path) {
FRESULT fr; struct stat st;
FILINFO fno; int error = stat(path, &st);
return ((error == 0) && S_ISDIR(st.st_mode));
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;
} }
bool directory_create (char *path) { bool directory_create (char *path) {
@ -188,8 +117,12 @@ bool directory_create (char *path) {
return false; return false;
} }
char *directory = strdup(strip_sd_prefix(path)); char *directory = strdup(path);
char *separator = directory; char *separator = strip_fs_prefix(directory);
if (separator != directory) {
separator++;
}
do { do {
separator = strchr(separator, '/'); separator = strchr(separator, '/');
@ -199,8 +132,7 @@ bool directory_create (char *path) {
} }
if (directory[0] != '\0') { if (directory[0] != '\0') {
FRESULT res = f_mkdir(directory); if (mkdir(directory, 0777) && (errno != EEXIST)) {
if ((res != FR_OK) && (res != FR_EXIST)) {
error = true; error = true;
break; break;
} }

View File

@ -10,18 +10,15 @@
#define FS_SECTOR_SIZE (512) #define FS_SECTOR_SIZE (512)
char *strip_sd_prefix (char *path); char *strip_fs_prefix (char *path);
bool file_exists (char *path); bool file_exists (char *path);
size_t file_get_size (char *path); int64_t file_get_size (char *path);
bool file_delete (char *path);
bool file_allocate (char *path, size_t size); bool file_allocate (char *path, size_t size);
bool file_fill (char *path, uint8_t value); 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 file_has_extensions (char *path, const char *extensions[]);
bool directory_exists (char *path); bool directory_exists (char *path);
bool directory_delete (char *path);
bool directory_create (char *path); bool directory_create (char *path);