Merge branch 'main' into ed64-basic

This commit is contained in:
Robin Jones 2024-04-27 17:40:35 +01:00 committed by GitHub
commit 1566fe9c48
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
56 changed files with 1380 additions and 844 deletions

20
.clang-format Normal file
View File

@ -0,0 +1,20 @@
BasedOnStyle: LLVM
AlignAfterOpenBracket: BlockIndent
AlignEscapedNewlines: DontAlign
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortCaseLabelsOnASingleLine: true
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: InlineOnly
AlwaysBreakBeforeMultilineStrings: true
BinPackArguments: false
BinPackParameters: false
BreakBeforeBraces: Attach
ColumnLimit: 120
IndentWidth: 4
SpaceBeforeParens: Custom
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterFunctionDeclarationName: true
AfterFunctionDefinitionName: true
UseTab: Never

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": [

View File

@ -110,3 +110,4 @@ jobs:
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./output/docs publish_dir: ./output/docs
cname: menu.summercart64.dev

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

@ -8,10 +8,16 @@ FILESYSTEM_DIR = filesystem
BUILD_DIR = build BUILD_DIR = build
OUTPUT_DIR = output OUTPUT_DIR = output
FLAGS += -DMENU_VERSION=\"0.0.1.$(shell date +%Y-%m-%dT%H:%M:%SZ).ALPHA\" MENU_VERSION ?= "Rolling release"
BUILD_TIMESTAMP = "$(shell TZ='UTC' date "+%Y-%m-%d %H:%M:%S %:z")"
include $(N64_INST)/include/n64.mk 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 = \
@ -58,6 +64,7 @@ SRCS = \
menu/views/fault.c \ menu/views/fault.c \
menu/views/file_info.c \ menu/views/file_info.c \
menu/views/image_viewer.c \ menu/views/image_viewer.c \
menu/views/text_viewer.c \
menu/views/load_disk.c \ menu/views/load_disk.c \
menu/views/load_emulator.c \ menu/views/load_emulator.c \
menu/views/load_rom.c \ menu/views/load_rom.c \
@ -66,6 +73,7 @@ SRCS = \
menu/views/system_info.c \ menu/views/system_info.c \
menu/views/settings_editor.c \ menu/views/settings_editor.c \
menu/views/rtc.c \ menu/views/rtc.c \
menu/views/flashcart_info.c \
utils/fs.c utils/fs.c
FONTS = \ FONTS = \
@ -81,7 +89,7 @@ FILESYSTEM = \
$(MINIZ_OBJS): N64_CFLAGS+=-DMINIZ_NO_TIME -fcompare-debug-second $(MINIZ_OBJS): N64_CFLAGS+=-DMINIZ_NO_TIME -fcompare-debug-second
$(SPNG_OBJS): N64_CFLAGS+=-isystem $(SOURCE_DIR)/libs/miniz -DSPNG_USE_MINIZ -fcompare-debug-second $(SPNG_OBJS): N64_CFLAGS+=-isystem $(SOURCE_DIR)/libs/miniz -DSPNG_USE_MINIZ -fcompare-debug-second
$(FILESYSTEM_DIR)/FiraMonoBold.font64: MKFONT_FLAGS+=-c 1 --size 16 -r 20-7F -r 2026-2026 --ellipsis 2026,1 $(FILESYSTEM_DIR)/FiraMonoBold.font64: MKFONT_FLAGS+=-c 1 --size 16 -r 20-1FF -r 2026-2026 --ellipsis 2026,1
$(@info $(shell mkdir -p ./$(FILESYSTEM_DIR) &> /dev/null)) $(@info $(shell mkdir -p ./$(FILESYSTEM_DIR) &> /dev/null))
@ -91,6 +99,9 @@ $(FILESYSTEM_DIR)/%.font64: $(ASSETS_DIR)/%.ttf
$(BUILD_DIR)/$(PROJECT_NAME).dfs: $(FILESYSTEM) $(BUILD_DIR)/$(PROJECT_NAME).dfs: $(FILESYSTEM)
$(BUILD_DIR)/menu/views/credits.o: .FORCE
$(BUILD_DIR)/menu/views/credits.o: FLAGS+=-DMENU_VERSION=\"$(MENU_VERSION)\" -DBUILD_TIMESTAMP=\"$(BUILD_TIMESTAMP)\"
$(BUILD_DIR)/$(PROJECT_NAME).elf: $(OBJS) $(BUILD_DIR)/$(PROJECT_NAME).elf: $(OBJS)
disassembly: $(BUILD_DIR)/$(PROJECT_NAME).elf disassembly: $(BUILD_DIR)/$(PROJECT_NAME).elf
@ -125,9 +136,18 @@ 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
format:
@find ./$(SOURCE_DIR) \
-path \./$(SOURCE_DIR)/libs -prune \
-o -iname *.c -print \
-o -iname *.h -print \
| xargs clang-format -i
run: $(OUTPUT_DIR)/$(PROJECT_NAME).n64 run: $(OUTPUT_DIR)/$(PROJECT_NAME).n64
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
./localdeploy.bat ./localdeploy.bat
@ -147,4 +167,6 @@ endif
# test: # test:
# TODO: run tests # TODO: run tests
.FORCE:
-include $(DEPS) -include $(DEPS)

View File

@ -3,6 +3,16 @@
# N64 Flashcart Menu # N64 Flashcart Menu
An open source menu for N64 flashcarts. An open source menu for N64 flashcarts.
## Supported Flashcarts
### Fully supported
* SummerCart64
* 64Drive
### Work in Progress
* ED64
* ED64P
## Current (notable) menu features ## Current (notable) menu features
* Fully Open Source. * Fully Open Source.
@ -33,6 +43,17 @@ An open source menu for N64 flashcarts.
## Getting started ## Getting started
Using your PC, insert the SD card and ensure it is formatted for compatibility (We recommend FAT32 in most instances). Using your PC, insert the SD card and ensure it is formatted for compatibility (We recommend FAT32 in most instances).
### Save files
By default, all save files (whether `FlashRam`, `SRAM` or `EEPROM`) use the `.sav` extension and match the filename of the ROM.
Each save file can be found in the `/saves` folder located in the same directory as the ROM and shares the same file name, apart from the extension.
If transfering a file from a different flashcart such as the ED64, it will be necessary to change the extension of the file to `sav`.
i.e. for `Glover (USA).eep` you would need to change the extension to `Glover (USA).sav`
**NOTE:** certain emulator saves or saves created for a different ROM version or region may be incompatible.
### ROM Boxart ### ROM Boxart
To use boxart, you need to place png files of size 158x112 in the folder `/menu/boxart` on the SD card. To use boxart, you need to place png files of size 158x112 in the folder `/menu/boxart` on the SD card.

0
filesystem/.gitkeep Normal file
View File

@ -1 +1 @@
Subproject commit 6323128d72fdf32dfaa134f40191ba72e5527076 Subproject commit 8dd362b8d858ae60befe9d27bc55cf770b9b50af

View File

@ -102,7 +102,7 @@ cic_type_t cic_detect (uint8_t *ipl3) {
switch (cic_calculate_ipl3_checksum(ipl3, 0x3F)) { switch (cic_calculate_ipl3_checksum(ipl3, 0x3F)) {
case 0x45CC73EE317AULL: return CIC_6101; // 6101 case 0x45CC73EE317AULL: return CIC_6101; // 6101
case 0x44160EC5D9AFULL: return CIC_7102; // 7102 case 0x44160EC5D9AFULL: return CIC_7102; // 7102
case 0xA536C0F1D859ULL: return CIC_6102_7101; // 6102 / 7101 case 0xA536C0F1D859ULL: return CIC_x102; // 6102 / 7101
} }
switch (cic_calculate_ipl3_checksum(ipl3, 0x78)) { switch (cic_calculate_ipl3_checksum(ipl3, 0x78)) {
case 0x586FD4709867ULL: return CIC_x103; // 6103 / 7103 case 0x586FD4709867ULL: return CIC_x103; // 6103 / 7103
@ -133,7 +133,7 @@ uint8_t cic_get_seed (cic_type_t cic_type) {
case CIC_5167: return 0xDD; case CIC_5167: return 0xDD;
case CIC_6101: return 0x3F; case CIC_6101: return 0x3F;
case CIC_7102: return 0x3F; case CIC_7102: return 0x3F;
case CIC_6102_7101: return 0x3F; case CIC_x102: return 0x3F;
case CIC_x103: return 0x78; case CIC_x103: return 0x78;
case CIC_x105: return 0x91; case CIC_x105: return 0x91;
case CIC_x106: return 0x85; case CIC_x106: return 0x85;

View File

@ -13,7 +13,7 @@ typedef enum {
CIC_5167, CIC_5167,
CIC_6101, CIC_6101,
CIC_7102, CIC_7102,
CIC_6102_7101, CIC_x102,
CIC_x103, CIC_x103,
CIC_x105, CIC_x105,
CIC_x106, CIC_x106,

View File

@ -73,6 +73,8 @@ static flashcart_err_t d64_deinit (void) {
static bool d64_has_feature (flashcart_features_t feature) { static bool d64_has_feature (flashcart_features_t feature) {
switch (feature) { switch (feature) {
case FLASHCART_FEATURE_64DD: return false; case FLASHCART_FEATURE_64DD: return false;
case FLASHCART_FEATURE_RTC: return true;
case FLASHCART_FEATURE_USB: return true;
default: return false; default: return false;
} }
} }
@ -81,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);
@ -96,7 +98,7 @@ static flashcart_err_t d64_load_rom (char *rom_path, flashcart_progress_callback
size_t sdram_size = MiB(64); size_t sdram_size = MiB(64);
size_t chunk_size = MiB(1); size_t chunk_size = KiB(128);
for (int offset = 0; offset < sdram_size; offset += chunk_size) { for (int offset = 0; offset < sdram_size; offset += chunk_size) {
size_t block_size = MIN(sdram_size - offset, chunk_size); size_t block_size = MIN(sdram_size - offset, chunk_size);
if (f_read(&fil, (void *) (ROM_ADDRESS + offset), block_size, &br) != FR_OK) { if (f_read(&fil, (void *) (ROM_ADDRESS + offset), block_size, &br) != FR_OK) {
@ -123,11 +125,11 @@ static flashcart_err_t d64_load_file (char *file_path, uint32_t rom_offset, uint
FIL fil; 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;
@ -162,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;
} }
@ -249,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,15 +8,13 @@
#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"
#include "ed64/ed64.h" #include "ed64/ed64.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,
@ -28,34 +26,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,
}); });
@ -70,9 +78,9 @@ 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_BBFS: return "Error during iQue NAND initialization";
case FLASHCART_ERR_ARGS: return "Invalid argument passed to flashcart function"; case FLASHCART_ERR_ARGS: return "Invalid argument passed to flashcart function";
case FLASHCART_ERR_LOAD: return "Error during loading data into flashcart"; case FLASHCART_ERR_LOAD: return "Error during loading data into flashcart";
case FLASHCART_ERR_INT: return "Internal flashcart error"; case FLASHCART_ERR_INT: return "Internal flashcart error";
@ -81,15 +89,20 @@ char *flashcart_convert_error_message (flashcart_err_t err) {
} }
} }
flashcart_err_t flashcart_init (void) { flashcart_err_t flashcart_init (const char **storage_prefix) {
flashcart_err_t err; flashcart_err_t err;
bool sd_card_initialized = debug_init_sdfs("sd:/", -1); if (sys_bbplayer()) {
// TODO: Add iQue callbacks
*storage_prefix = "bbfs:/";
if (bbfs_init()) {
return FLASHCART_ERR_BBFS;
}
return FLASHCART_OK;
}
#ifndef NDEBUG *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
@ -97,7 +110,8 @@ flashcart_err_t flashcart_init (void) {
break; break;
case CART_EDX: // Series X EverDrive-64 case CART_EDX: // Series X EverDrive-64
// break; // FIXME: Commented out as required to fall through due to need of F/W 3.06 and UNFLoader (at minimum). break;
case CART_ED: // Original EverDrive-64 case CART_ED: // Original EverDrive-64
flashcart = ed64_get_flashcart(); flashcart = ed64_get_flashcart();
break; break;
@ -106,15 +120,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;
} }
@ -187,19 +208,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,9 +15,9 @@
/** @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_BBFS,
FLASHCART_ERR_ARGS, FLASHCART_ERR_ARGS,
FLASHCART_ERR_LOAD, FLASHCART_ERR_LOAD,
FLASHCART_ERR_INT, FLASHCART_ERR_INT,
@ -27,6 +27,8 @@ typedef enum {
/** @brief List of optional supported flashcart features */ /** @brief List of optional supported flashcart features */
typedef enum { typedef enum {
FLASHCART_FEATURE_64DD, FLASHCART_FEATURE_64DD,
FLASHCART_FEATURE_RTC,
FLASHCART_FEATURE_USB,
} flashcart_features_t; } flashcart_features_t;
/** @brief Flashcart save type enumeration */ /** @brief Flashcart save type enumeration */
@ -73,12 +75,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;
} }
@ -260,6 +252,8 @@ static flashcart_err_t sc64_deinit (void) {
static bool sc64_has_feature (flashcart_features_t feature) { static bool sc64_has_feature (flashcart_features_t feature) {
switch (feature) { switch (feature) {
case FLASHCART_FEATURE_64DD: return true; case FLASHCART_FEATURE_64DD: return true;
case FLASHCART_FEATURE_RTC: return true;
case FLASHCART_FEATURE_USB: return true;
default: return false; default: return false;
} }
} }
@ -268,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);
@ -288,7 +282,7 @@ static flashcart_err_t sc64_load_rom (char *rom_path, flashcart_progress_callbac
size_t shadow_size = shadow_enabled ? MIN(rom_size - sdram_size, KiB(128)) : 0; size_t shadow_size = shadow_enabled ? MIN(rom_size - sdram_size, KiB(128)) : 0;
size_t extended_size = extended_enabled ? rom_size - MiB(64) : 0; size_t extended_size = extended_enabled ? rom_size - MiB(64) : 0;
size_t chunk_size = MiB(1); size_t chunk_size = KiB(128);
for (int offset = 0; offset < sdram_size; offset += chunk_size) { for (int offset = 0; offset < sdram_size; offset += chunk_size) {
size_t block_size = MIN(sdram_size - offset, chunk_size); size_t block_size = MIN(sdram_size - offset, chunk_size);
if (f_read(&fil, (void *) (ROM_ADDRESS + offset), block_size, &br) != FR_OK) { if (f_read(&fil, (void *) (ROM_ADDRESS + offset), block_size, &br) != FR_OK) {
@ -349,11 +343,11 @@ static flashcart_err_t sc64_load_file (char *file_path, uint32_t rom_offset, uin
FIL fil; 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;
@ -411,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;
} }
@ -437,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);
@ -450,7 +444,7 @@ static flashcart_err_t sc64_load_64dd_ipl (char *ipl_path, flashcart_progress_ca
return FLASHCART_ERR_LOAD; return FLASHCART_ERR_LOAD;
} }
size_t chunk_size = KiB(256); size_t chunk_size = KiB(128);
for (int offset = 0; offset < ipl_size; offset += chunk_size) { for (int offset = 0; offset < ipl_size; offset += chunk_size) {
size_t block_size = MIN(ipl_size - offset, chunk_size); size_t block_size = MIN(ipl_size - offset, chunk_size);
if (f_read(&fil, (void *) (IPL_ADDRESS + offset), block_size, &br) != FR_OK) { if (f_read(&fil, (void *) (IPL_ADDRESS + offset), block_size, &br) != FR_OK) {
@ -474,19 +468,17 @@ static flashcart_err_t sc64_load_64dd_ipl (char *ipl_path, flashcart_progress_ca
} }
static flashcart_err_t sc64_load_64dd_disk (char *disk_path, flashcart_disk_parameters_t *disk_parameters) { 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;
@ -496,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;
@ -557,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/libspng vendored

@ -1 +1 @@
Subproject commit e5c1fc470fceaca08b8c30dc40768c28b82b9e12 Subproject commit adc94393dbeddf9e027d1b2dfff7c1bab975224e

2
src/libs/miniz vendored

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

View File

@ -19,10 +19,9 @@
static bool is_64dd_connected (void) { static bool is_64dd_connected (void) {
return ( bool is_64dd_io_present = ((io_read(0x05000540) & 0x0000FFFF) == 0x0000);
((io_read(0x05000540) & 0x0000FFFF) == 0x0000) || bool is_64dd_ipl_present = (io_read(0x06001010) == 0x2129FFF8);
(io_read(0x06001010) == 0x2129FFF8) return (is_64dd_io_present || is_64dd_ipl_present);
);
} }
static bool create_saves_subdirectory (path_t *path) { static bool create_saves_subdirectory (path_t *path) {
@ -112,7 +111,7 @@ cart_load_err_t cart_load_64dd_ipl_and_disk (menu_t *menu, flashcart_progress_ca
return CART_LOAD_ERR_EXP_PAK_NOT_FOUND; 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 +153,7 @@ cart_load_err_t cart_load_64dd_ipl_and_disk (menu_t *menu, flashcart_progress_ca
} }
cart_load_err_t cart_load_emulator (menu_t *menu, cart_load_emu_type_t emu_type, flashcart_progress_callback_t progress) { 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

@ -24,7 +24,7 @@ void component_progressbar_draw (int x0, int y0, int x1, int y1, float progress)
void component_seekbar_draw (float progress); void component_seekbar_draw (float progress);
void component_loader_draw (float position); void component_loader_draw (float position);
void component_scrollbar_draw (int x, int y, int width, int height, int position, int items, int visible_items); void component_scrollbar_draw (int x, int y, int width, int height, int position, int items, int visible_items);
void component_file_list_scrollbar_draw (int position, int items, int visible_items); void component_list_scrollbar_draw (int position, int items, int visible_items);
void component_dialog_draw (int width, int height); void component_dialog_draw (int width, int height);
void component_messagebox_draw (char *fmt, ...); void component_messagebox_draw (char *fmt, ...);
void component_main_text_draw (rdpq_align_t align, rdpq_valign_t valign, char *fmt, ...); void component_main_text_draw (rdpq_align_t align, rdpq_valign_t valign, char *fmt, ...);
@ -45,7 +45,8 @@ typedef struct component_context_menu {
struct component_context_menu *submenu; struct component_context_menu *submenu;
struct { struct {
const char *text; const char *text;
void (*action) (menu_t *menu); void (*action) (menu_t *menu, void *arg);
void *arg;
struct component_context_menu *submenu; struct component_context_menu *submenu;
} list[]; } list[];
} component_context_menu_t; } component_context_menu_t;
@ -63,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

@ -80,12 +80,12 @@ void component_scrollbar_draw (int x, int y, int width, int height, int position
} }
} }
void component_file_list_scrollbar_draw (int position, int items, int visible_items) { void component_list_scrollbar_draw (int position, int items, int visible_items) {
component_scrollbar_draw( component_scrollbar_draw(
FILE_LIST_SCROLLBAR_X, LIST_SCROLLBAR_X,
FILE_LIST_SCROLLBAR_Y, LIST_SCROLLBAR_Y,
FILE_LIST_SCROLLBAR_WIDTH, LIST_SCROLLBAR_WIDTH,
FILE_LIST_SCROLLBAR_HEIGHT, LIST_SCROLLBAR_HEIGHT,
position, position,
items, items,
visible_items visible_items
@ -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

@ -77,19 +77,19 @@
#define BOXART_Y (LAYOUT_ACTIONS_SEPARATOR_Y - BOXART_HEIGHT - 24) #define BOXART_Y (LAYOUT_ACTIONS_SEPARATOR_Y - BOXART_HEIGHT - 24)
/** @brief The scroll bar width. */ /** @brief The scroll bar width. */
#define FILE_LIST_SCROLLBAR_WIDTH (12) #define LIST_SCROLLBAR_WIDTH (12)
/** @brief The scroll bar height. */ /** @brief The scroll bar height. */
#define FILE_LIST_SCROLLBAR_HEIGHT (LAYOUT_ACTIONS_SEPARATOR_Y - OVERSCAN_HEIGHT) #define LIST_SCROLLBAR_HEIGHT (LAYOUT_ACTIONS_SEPARATOR_Y - OVERSCAN_HEIGHT)
/** @brief The scroll bar position on the X axis. */ /** @brief The scroll bar position on the X axis. */
#define FILE_LIST_SCROLLBAR_X (VISIBLE_AREA_X1 - FILE_LIST_SCROLLBAR_WIDTH) #define LIST_SCROLLBAR_X (VISIBLE_AREA_X1 - LIST_SCROLLBAR_WIDTH)
/** @brief The scroll bar position on the Y axis. */ /** @brief The scroll bar position on the Y axis. */
#define FILE_LIST_SCROLLBAR_Y (VISIBLE_AREA_Y0) #define LIST_SCROLLBAR_Y (VISIBLE_AREA_Y0)
/** @brief The maximum amount of file list entries. */ /** @brief The maximum amount of file list entries. */
#define FILE_LIST_ENTRIES (20) #define LIST_ENTRIES (20)
/** @brief The maximum width available for a file list entry. */ /** @brief The maximum width available for a file list entry. */
#define FILE_LIST_MAX_WIDTH (480) #define FILE_LIST_MAX_WIDTH (480)
#define FILE_LIST_HIGHLIGHT_WIDTH (VISIBLE_AREA_X1 - VISIBLE_AREA_X0 - FILE_LIST_SCROLLBAR_WIDTH) #define FILE_LIST_HIGHLIGHT_WIDTH (VISIBLE_AREA_X1 - VISIBLE_AREA_X0 - LIST_SCROLLBAR_WIDTH)
#define FILE_LIST_HIGHLIGHT_X (VISIBLE_AREA_X0) #define FILE_LIST_HIGHLIGHT_X (VISIBLE_AREA_X0)
/** @brief The default background colour. */ /** @brief The default background colour. */

View File

@ -48,7 +48,7 @@ bool component_context_menu_process (menu_t *menu, component_context_menu_t *cm)
cm->submenu->selected = 0; cm->submenu->selected = 0;
cm->submenu->parent = cm; cm->submenu->parent = cm;
} else if (cm->list[cm->selected].action) { } else if (cm->list[cm->selected].action) {
cm->list[cm->selected].action(menu); cm->list[cm->selected].action(menu, cm->list[cm->selected].arg);
top->hide_pending = true; top->hide_pending = true;
} }
} else if (menu->actions.go_up) { } else if (menu->actions.go_up) {

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);
} }
} }
@ -24,29 +28,29 @@ static int format_file_size (char *buffer, int size) {
void component_file_list_draw (entry_t *list, int entries, int selected) { void component_file_list_draw (entry_t *list, int entries, int selected) {
int starting_position = 0; int starting_position = 0;
if (entries > FILE_LIST_ENTRIES && selected >= (FILE_LIST_ENTRIES / 2)) { if (entries > LIST_ENTRIES && selected >= (LIST_ENTRIES / 2)) {
starting_position = selected - (FILE_LIST_ENTRIES / 2); starting_position = selected - (LIST_ENTRIES / 2);
if (starting_position >= entries - FILE_LIST_ENTRIES) { if (starting_position >= entries - LIST_ENTRIES) {
starting_position = entries - FILE_LIST_ENTRIES; starting_position = entries - LIST_ENTRIES;
} }
} }
component_file_list_scrollbar_draw(selected, entries, FILE_LIST_ENTRIES); component_list_scrollbar_draw(selected, entries, LIST_ENTRIES);
if (entries == 0) { if (entries == 0) {
component_main_text_draw( component_main_text_draw(
ALIGN_LEFT, VALIGN_TOP, ALIGN_LEFT, VALIGN_TOP,
"^%02X** empty directory **", "^%02X** empty directory **",
STL_UNKNOWN STL_GRAY
); );
} else { } else {
rdpq_paragraph_t *file_list_layout; rdpq_paragraph_t *file_list_layout;
rdpq_paragraph_t *layout; rdpq_paragraph_t *layout;
size_t name_lengths[FILE_LIST_ENTRIES]; size_t name_lengths[LIST_ENTRIES];
size_t total_length = 1; size_t total_length = 1;
for (int i = 0; i < FILE_LIST_ENTRIES; i++) { for (int i = 0; i < LIST_ENTRIES; i++) {
int entry_index = starting_position + i; int entry_index = starting_position + i;
if (entry_index >= entries) { if (entry_index >= entries) {
@ -72,7 +76,7 @@ void component_file_list_draw (entry_t *list, int entries, int selected) {
file_list_layout file_list_layout
); );
for (int i = 0; i < FILE_LIST_ENTRIES; i++) { for (int i = 0; i < LIST_ENTRIES; i++) {
int entry_index = starting_position + i; int entry_index = starting_position + i;
entry_t *entry = &list[entry_index]; entry_t *entry = &list[entry_index];
@ -80,12 +84,16 @@ void component_file_list_draw (entry_t *list, int entries, int selected) {
menu_font_style_t style; menu_font_style_t style;
switch (entry->type) { switch (entry->type) {
case ENTRY_TYPE_DIR: style = STL_DIRECTORY; break; case ENTRY_TYPE_DIR: style = STL_YELLOW; break;
case ENTRY_TYPE_SAVE: style = STL_SAVE; break; case ENTRY_TYPE_ROM: style = STL_DEFAULT; break;
case ENTRY_TYPE_OTHER: style = STL_UNKNOWN; break; case ENTRY_TYPE_DISK: style = STL_DEFAULT; break;
case ENTRY_TYPE_IMAGE: style = STL_MEDIA; break; case ENTRY_TYPE_EMULATOR: style = STL_DEFAULT; break;
case ENTRY_TYPE_MUSIC: style = STL_MEDIA; break; case ENTRY_TYPE_SAVE: style = STL_GREEN; break;
default: style = STL_DEFAULT; break; case ENTRY_TYPE_IMAGE: style = STL_BLUE; break;
case ENTRY_TYPE_MUSIC: style = STL_BLUE; break;
case ENTRY_TYPE_TEXT: style = STL_ORANGE; break;
case ENTRY_TYPE_OTHER: style = STL_GRAY; break;
default: style = STL_GRAY; break;
} }
rdpq_paragraph_builder_style(style); rdpq_paragraph_builder_style(style);
@ -126,7 +134,7 @@ void component_file_list_draw (entry_t *list, int entries, int selected) {
rdpq_paragraph_builder_begin( rdpq_paragraph_builder_begin(
&(rdpq_textparms_t) { &(rdpq_textparms_t) {
.width = VISIBLE_AREA_WIDTH - FILE_LIST_SCROLLBAR_WIDTH - (TEXT_MARGIN_HORIZONTAL * 2), .width = VISIBLE_AREA_WIDTH - LIST_SCROLLBAR_WIDTH - (TEXT_MARGIN_HORIZONTAL * 2),
.height = LAYOUT_ACTIONS_SEPARATOR_Y - VISIBLE_AREA_Y0 - (TEXT_MARGIN_VERTICAL * 2), .height = LAYOUT_ACTIONS_SEPARATOR_Y - VISIBLE_AREA_Y0 - (TEXT_MARGIN_VERTICAL * 2),
.align = ALIGN_RIGHT, .align = ALIGN_RIGHT,
.wrap = WRAP_ELLIPSES, .wrap = WRAP_ELLIPSES,
@ -135,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];
@ -144,7 +152,7 @@ void component_file_list_draw (entry_t *list, int entries, int selected) {
rdpq_paragraph_builder_span(file_size, format_file_size(file_size, entry->size)); rdpq_paragraph_builder_span(file_size, format_file_size(file_size, entry->size));
} }
if ((i + 1) == (starting_position + FILE_LIST_ENTRIES)) { if ((i + 1) == (starting_position + LIST_ENTRIES)) {
break; break;
} }

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,21 +1,29 @@
#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_DIRECTORY, &((rdpq_fontstyle_t) { .color = RGBA32(0xFF, 0xFF, 0x70, 0xFF) })); rdpq_font_style(default_font, STL_GREEN, &((rdpq_fontstyle_t) { .color = RGBA32(0x70, 0xFF, 0x70, 0xFF) }));
rdpq_font_style(default_font, STL_SAVE, &((rdpq_fontstyle_t) { .color = RGBA32(0x70, 0xFF, 0x70, 0xFF) })); rdpq_font_style(default_font, STL_BLUE, &((rdpq_fontstyle_t) { .color = RGBA32(0x70, 0xBC, 0xFF, 0xFF) }));
rdpq_font_style(default_font, STL_MEDIA, &((rdpq_fontstyle_t) { .color = RGBA32(0x70, 0xBC, 0xFF, 0xFF) })); rdpq_font_style(default_font, STL_YELLOW, &((rdpq_fontstyle_t) { .color = RGBA32(0xFF, 0xFF, 0x70, 0xFF) }));
rdpq_font_style(default_font, STL_UNKNOWN, &((rdpq_fontstyle_t) { .color = RGBA32(0xA0, 0xA0, 0xA0, 0xFF) })); rdpq_font_style(default_font, STL_ORANGE, &((rdpq_fontstyle_t) { .color = RGBA32(0xFF, 0x99, 0x00, 0xFF) }));
rdpq_font_style(default_font, STL_GRAY, &((rdpq_fontstyle_t) { .color = RGBA32(0xA0, 0xA0, 0xA0, 0xFF) }));
rdpq_text_register_font(FNT_DEFAULT, default_font); rdpq_text_register_font(FNT_DEFAULT, default_font);
} }
void fonts_init (void) { void fonts_init (char *custom_font_path) {
load_default_font(); load_default_font(custom_font_path);
} }

View File

@ -15,14 +15,15 @@ typedef enum {
/** @brief Font style enumeration. */ /** @brief Font style enumeration. */
typedef enum { typedef enum {
STL_DEFAULT = 0, STL_DEFAULT = 0,
STL_DIRECTORY, STL_GREEN,
STL_SAVE, STL_BLUE,
STL_MEDIA, STL_YELLOW,
STL_UNKNOWN, STL_ORANGE,
STL_GRAY,
} menu_font_style_t; } menu_font_style_t;
void fonts_init (void); void fonts_init (char *custom_font_path);
#endif #endif

View File

@ -20,10 +20,12 @@
#include "views/views.h" #include "views/views.h"
#define TV_TYPE_RAM *((uint32_t *) (0x80000300)) #define MENU_DIRECTORY "/menu"
#define MENU_SETTINGS_FILE "config.ini"
#define MENU_CUSTOM_FONT_FILE "custom.font64"
#define CACHE_DIRECTORY "sd:/menu/cache" #define MENU_CACHE_DIRECTORY "cache"
#define BACKGROUND_CACHE "sd:/menu/cache/background.data" #define BACKGROUND_CACHE_FILE "background.data"
#define FRAMERATE_DIVIDER (2) #define FRAMERATE_DIVIDER (2)
#define LAG_REPORT (false) #define LAG_REPORT (false)
@ -33,6 +35,8 @@ static menu_t *menu;
static tv_type_t tv_type; static tv_type_t tv_type;
static volatile int frame_counter = 0; static volatile int frame_counter = 0;
extern tv_type_t __boot_tvtype;
static void frame_counter_handler (void) { static void frame_counter_handler (void) {
frame_counter += 1; frame_counter += 1;
@ -61,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));
@ -70,36 +73,46 @@ 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(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();
tv_type = get_tv_type(); tv_type = get_tv_type();
if ((tv_type == TV_PAL) && menu->settings.pal60_enabled) { if ((tv_type == TV_PAL) && menu->settings.pal60_enabled) {
// HACK: Set TV type to NTSC, so PAL console would output 60 Hz signal instead. // HACK: Set TV type to NTSC, so PAL console would output 60 Hz signal instead.
TV_TYPE_RAM = TV_NTSC; __boot_tvtype = TV_NTSC;
} }
display_init(RESOLUTION_640x480, DEPTH_16_BPP, 2, GAMMA_NONE, FILTERS_DISABLED); display_init(RESOLUTION_640x480, DEPTH_16_BPP, 2, GAMMA_NONE, FILTERS_DISABLED);
@ -111,10 +124,16 @@ static void menu_deinit (menu_t *menu) {
unregister_VI_handler(frame_counter_handler); unregister_VI_handler(frame_counter_handler);
// NOTE: Restore previous TV type so boot procedure wouldn't passthrough wrong value. // NOTE: Restore previous TV type so boot procedure wouldn't passthrough wrong value.
TV_TYPE_RAM = tv_type; __boot_tvtype = tv_type;
hdmi_send_game_id(menu->boot_params); 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);
@ -133,30 +152,41 @@ static void menu_deinit (menu_t *menu) {
display_close(); display_close();
} }
typedef const struct {
// NOTE: Keep this array in sync with menu_mode_t menu_mode_t id;
static struct views_s {
void (*init) (menu_t *menu); void (*init) (menu_t *menu);
void (*show) (menu_t *menu, surface_t *display); void (*show) (menu_t *menu, surface_t *display);
} views[__MENU_MODE_COUNT] = { } view_t;
{ NULL, NULL }, // MENU_MODE_NONE
{ view_startup_init, view_startup_display }, // MENU_MODE_STARTUP static view_t menu_views[] = {
{ view_browser_init, view_browser_display }, // MENU_MODE_BROWSER { MENU_MODE_STARTUP, view_startup_init, view_startup_display },
{ view_file_info_init, view_file_info_display }, // MENU_MODE_FILE_INFO { MENU_MODE_BROWSER, view_browser_init, view_browser_display },
{ view_system_info_init, view_system_info_display }, // MENU_MODE_SYSTEM_INFO { MENU_MODE_FILE_INFO, view_file_info_init, view_file_info_display },
{ view_image_viewer_init, view_image_viewer_display }, // MENU_MODE_IMAGE_VIEWER { MENU_MODE_SYSTEM_INFO, view_system_info_init, view_system_info_display },
{ view_music_player_init, view_music_player_display }, // MENU_MODE_MUSIC_PLAYER { MENU_MODE_IMAGE_VIEWER, view_image_viewer_init, view_image_viewer_display },
{ view_credits_init, view_credits_display }, // MENU_MODE_CREDITS { MENU_MODE_TEXT_VIEWER, view_text_viewer_init, view_text_viewer_display },
{ view_settings_init, view_settings_display }, // MENU_MODE_SETTINGS_EDITOR { MENU_MODE_MUSIC_PLAYER, view_music_player_init, view_music_player_display },
{ view_rtc_init, view_rtc_display }, // MENU_MODE_RTC { MENU_MODE_CREDITS, view_credits_init, view_credits_display },
{ view_load_rom_init, view_load_rom_display }, // MENU_MODE_LOAD_ROM { MENU_MODE_SETTINGS_EDITOR, view_settings_init, view_settings_display },
{ view_load_disk_init, view_load_disk_display }, // MENU_MODE_LOAD_DISK { MENU_MODE_RTC, view_rtc_init, view_rtc_display },
{ view_load_emulator_init, view_load_emulator_display }, // MENU_MODE_LOAD_EMULATOR { MENU_MODE_FLASHCART, view_flashcart_info_init, view_flashcart_info_display },
{ view_error_init, view_error_display }, // MENU_MODE_ERROR { MENU_MODE_LOAD_ROM, view_load_rom_init, view_load_rom_display },
{ view_fault_init, view_fault_display }, // MENU_MODE_FAULT { MENU_MODE_LOAD_DISK, view_load_disk_init, view_load_disk_display },
{ NULL, NULL }, // MENU_MODE_BOOT { MENU_MODE_LOAD_EMULATOR, view_load_emulator_init, view_load_emulator_display },
{ MENU_MODE_ERROR, view_error_init, view_error_display },
{ MENU_MODE_FAULT, view_fault_init, view_fault_display },
}; };
static view_t *menu_get_view (menu_mode_t id) {
for (int i = 0; i < sizeof(menu_views) / sizeof(view_t); i++) {
if (menu_views[i].id == id) {
return &menu_views[i];
}
}
return NULL;
}
void menu_run (boot_params_t *boot_params) { void menu_run (boot_params_t *boot_params) {
menu_init(boot_params); menu_init(boot_params);
@ -168,8 +198,9 @@ void menu_run (boot_params_t *boot_params) {
actions_update(menu); actions_update(menu);
if (views[menu->mode].show) { view_t *view = menu_get_view(menu->mode);
views[menu->mode].show(menu, display); if (view && view->show) {
view->show(menu, display);
} else { } else {
rdpq_attach_clear(display, NULL); rdpq_attach_clear(display, NULL);
rdpq_detach_wait(); rdpq_detach_wait();
@ -183,8 +214,9 @@ void menu_run (boot_params_t *boot_params) {
while (menu->mode != menu->next_mode) { while (menu->mode != menu->next_mode) {
menu->mode = menu->next_mode; menu->mode = menu->next_mode;
if (views[menu->mode].init) { view_t *next_view = menu_get_view(menu->next_mode);
views[menu->mode].init(menu); if (next_view && next_view->init) {
next_view->init(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,
@ -29,17 +26,18 @@ typedef enum {
MENU_MODE_FILE_INFO, MENU_MODE_FILE_INFO,
MENU_MODE_SYSTEM_INFO, MENU_MODE_SYSTEM_INFO,
MENU_MODE_IMAGE_VIEWER, MENU_MODE_IMAGE_VIEWER,
MENU_MODE_TEXT_VIEWER,
MENU_MODE_MUSIC_PLAYER, MENU_MODE_MUSIC_PLAYER,
MENU_MODE_CREDITS, MENU_MODE_CREDITS,
MENU_MODE_SETTINGS_EDITOR, MENU_MODE_SETTINGS_EDITOR,
MENU_MODE_RTC, MENU_MODE_RTC,
MENU_MODE_FLASHCART,
MENU_MODE_LOAD_ROM, MENU_MODE_LOAD_ROM,
MENU_MODE_LOAD_DISK, MENU_MODE_LOAD_DISK,
MENU_MODE_LOAD_EMULATOR, MENU_MODE_LOAD_EMULATOR,
MENU_MODE_ERROR, MENU_MODE_ERROR,
MENU_MODE_FAULT, MENU_MODE_FAULT,
MENU_MODE_BOOT, MENU_MODE_BOOT,
__MENU_MODE_COUNT,
} menu_mode_t; } menu_mode_t;
/** @brief File entry type enumeration */ /** @brief File entry type enumeration */
@ -51,6 +49,7 @@ typedef enum {
ENTRY_TYPE_SAVE, ENTRY_TYPE_SAVE,
ENTRY_TYPE_IMAGE, ENTRY_TYPE_IMAGE,
ENTRY_TYPE_MUSIC, ENTRY_TYPE_MUSIC,
ENTRY_TYPE_TEXT,
ENTRY_TYPE_OTHER, ENTRY_TYPE_OTHER,
} entry_type_t; } entry_type_t;
@ -58,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 */
@ -66,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;
@ -91,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; FILE *f;
FSIZE_t data_start; size_t file_size;
int seek_predecode_frames; size_t data_start;
uint8_t buffer[16 * 1024];
uint8_t *buffer_ptr;
size_t buffer_left;
mp3dec_t dec; mp3dec_t dec;
mp3dec_frame_info_t info; mp3dec_frame_info_t info;
uint8_t buffer[MAX_FREE_FORMAT_FRAME_SIZE]; int seek_predecode_frames;
uint8_t *buffer_ptr;
size_t buffer_left;
float duration; float duration;
float bitrate; float bitrate;
@ -48,9 +49,11 @@ 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)) {
return;
}
if (f_eof(&p->fil)) { if (p->buffer_left >= ALIGN(MAX_FREE_FORMAT_FRAME_SIZE, FS_SECTOR_SIZE)) {
return; return;
} }
@ -59,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) {
@ -105,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;
@ -133,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",
@ -160,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();
@ -185,7 +193,7 @@ mp3player_err_t mp3player_load (char *path) {
int samples = mp3dec_decode_frame(&p->dec, p->buffer_ptr, p->buffer_left, NULL, &p->info); 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;
@ -202,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;
} }
@ -213,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;
} }
@ -235,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) {
@ -244,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();
@ -288,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;
} }
@ -342,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,12 +1,14 @@
#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"
#include "utils/fs.h" #include "utils/fs.h"
/** @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;
@ -24,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);
} }
@ -40,21 +42,6 @@ static void png_decoder_deinit (bool free_image) {
} }
} }
static int png_file_read (spng_ctx *ctx, void *user, void *dst_src, size_t length) {
UINT bytes_read = 0;
png_decoder_t *d = (png_decoder_t *) (user);
if (f_read(&d->fil, dst_src, length, &bytes_read) != FR_OK) {
return SPNG_IO_ERROR;
}
if (bytes_read != length) {
return SPNG_EOF;
}
return SPNG_OK;
}
png_err_t png_decoder_start (char *path, int max_width, int max_height, png_callback_t *callback, void *callback_data) { 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) {
@ -66,11 +53,13 @@ png_err_t png_decoder_start (char *path, int max_width, int max_height, png_call
return PNG_ERR_OUT_OF_MEM; 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;
@ -86,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,8 +1,9 @@
#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 "rom_info.h" #include "rom_info.h"
#include "utils/fs.h" #include "utils/fs.h"
@ -16,6 +17,7 @@
#define CLOCK_RATE_DEFAULT (0x0000000F) #define CLOCK_RATE_DEFAULT (0x0000000F)
/** @brief ROM File Information Structure. */ /** @brief ROM File Information Structure. */
typedef struct __attribute__((packed)) { typedef struct __attribute__((packed)) {
uint32_t pi_dom1_config; uint32_t pi_dom1_config;
@ -139,6 +141,8 @@ typedef struct {
// List shamelessly stolen from https://github.com/ares-emulator/ares/blob/master/mia/medium/nintendo-64.cpp // List shamelessly stolen from https://github.com/ares-emulator/ares/blob/master/mia/medium/nintendo-64.cpp
// clang-format off
static const match_t database[] = { static const match_t database[] = {
MATCH_HOMEBREW_HEADER("ED"), // Homebrew header (ED) MATCH_HOMEBREW_HEADER("ED"), // Homebrew header (ED)
@ -574,6 +578,7 @@ static const match_t database[] = {
MATCH_END, MATCH_END,
}; };
// clang-format on
static void fix_rom_header_endianness (rom_header_t *rom_header, rom_info_t *rom_info) { static void fix_rom_header_endianness (rom_header_t *rom_header, rom_info_t *rom_info) {
@ -652,15 +657,34 @@ static match_t find_rom_in_database (rom_header_t *rom_header) {
return *match; return *match;
} }
static uint32_t fix_boot_address (cic_type_t cic_type, uint32_t boot_address) { static rom_cic_type_t detect_cic_type (uint8_t *ipl3) {
switch (cic_detect(ipl3)) {
case CIC_5101: return ROM_CIC_TYPE_5101;
case CIC_5167: return ROM_CIC_TYPE_5167;
case CIC_6101: return ROM_CIC_TYPE_6101;
case CIC_7102: return ROM_CIC_TYPE_7102;
case CIC_x102: return ROM_CIC_TYPE_x102;
case CIC_x103: return ROM_CIC_TYPE_x103;
case CIC_x105: return ROM_CIC_TYPE_x105;
case CIC_x106: return ROM_CIC_TYPE_x106;
case CIC_8301: return ROM_CIC_TYPE_8301;
case CIC_8302: return ROM_CIC_TYPE_8302;
case CIC_8303: return ROM_CIC_TYPE_8303;
case CIC_8401: return ROM_CIC_TYPE_8401;
case CIC_8501: return ROM_CIC_TYPE_8501;
default: return ROM_CIC_TYPE_UNKNOWN;
}
}
static uint32_t fix_boot_address (rom_cic_type_t cic_type, uint32_t boot_address) {
switch (cic_type) { switch (cic_type) {
case CIC_x103: return (boot_address - 0x100000); case ROM_CIC_TYPE_x103: return (boot_address - 0x100000);
case CIC_x106: return (boot_address - 0x200000); case ROM_CIC_TYPE_x106: return (boot_address - 0x200000);
default: return boot_address; default: return boot_address;
} }
} }
static rom_tv_type_t determine_tv_type (destination_type_t rom_destination_code) { static rom_tv_type_t determine_tv_type (rom_destination_type_t rom_destination_code) {
// check the market type from the ROM destination_code and return best guess! // check the market type from the ROM destination_code and return best guess!
switch (rom_destination_code) { switch (rom_destination_code) {
case MARKET_NORTH_AMERICA: case MARKET_NORTH_AMERICA:
@ -695,7 +719,7 @@ static rom_tv_type_t determine_tv_type (destination_type_t rom_destination_code)
} }
static void extract_rom_info (match_t *match, rom_header_t *rom_header, rom_info_t *rom_info) { static void extract_rom_info (match_t *match, rom_header_t *rom_header, rom_info_t *rom_info) {
rom_info->cic_type = cic_detect(rom_header->ipl3); rom_info->cic_type = detect_cic_type(rom_header->ipl3);
if (match->type == MATCH_TYPE_HOMEBREW_HEADER) { if (match->type == MATCH_TYPE_HOMEBREW_HEADER) {
if (rom_header->version & (1 << 0)) { if (rom_header->version & (1 << 0)) {
@ -746,19 +770,21 @@ static void extract_rom_info (match_t *match, rom_header_t *rom_header, rom_info
} }
static void load_overrides (path_t *path, rom_info_t *rom_info) { static void load_overrides (path_t *path, rom_info_t *rom_info) {
rom_info->override.save = false;
rom_info->override.tv = false;
path_t *overrides_path = path_clone(path); path_t *overrides_path = path_clone(path);
path_ext_replace(overrides_path, "ini"); path_ext_replace(overrides_path, "ini");
if (!file_exists(path_get(overrides_path))) { mini_t *ini = mini_load(path_get(overrides_path));
path_free(overrides_path);
return;
}
mini_t *ini = mini_try_load(path_get(overrides_path)); rom_info->override.cic = false;
rom_info->override.save = false;
rom_info->override.tv = false;
if (ini) {
rom_info->override.cic_type = mini_get_int(ini, NULL, "cic_type", ROM_CIC_TYPE_AUTOMATIC);
if (rom_info->override.cic_type != ROM_CIC_TYPE_AUTOMATIC) {
rom_info->override.cic = true;
}
rom_info->override.save_type = mini_get_int(ini, NULL, "save_type", SAVE_TYPE_AUTOMATIC); rom_info->override.save_type = mini_get_int(ini, NULL, "save_type", SAVE_TYPE_AUTOMATIC);
if (rom_info->override.save_type != SAVE_TYPE_AUTOMATIC) { if (rom_info->override.save_type != SAVE_TYPE_AUTOMATIC) {
@ -771,38 +797,45 @@ static void load_overrides (path_t *path, rom_info_t *rom_info) {
} }
mini_free(ini); mini_free(ini);
}
path_free(overrides_path); path_free(overrides_path);
} }
static rom_err_t save_override (path_t *path, const char *id, int value, int default_value) {
rom_err_t rom_info_override_save_type (path_t *path, rom_info_t *rom_info, rom_save_type_t save_type) {
path_t *overrides_path = path_clone(path); path_t *overrides_path = path_clone(path);
path_ext_replace(overrides_path, "ini"); path_ext_replace(overrides_path, "ini");
mini_t *ini = mini_try_load(path_get(overrides_path)); mini_t *ini = mini_try_load(path_get(overrides_path));
rom_info->override.save_type = save_type; if (!ini) {
return ROM_ERR_IO;
if (rom_info->override.save_type == SAVE_TYPE_AUTOMATIC) {
rom_info->override.save = false;
mini_delete_value(ini, NULL, "save_type");
} else {
rom_info->override.save = true;
mini_set_int(ini, NULL, "save_type", rom_info->override.save_type);
} }
bool empty_override_file = mini_empty(ini); if (value == default_value) {
mini_delete_value(ini, NULL, id);
} else {
mini_set_int(ini, NULL, id, value);
}
if (!empty_override_file) { bool empty = mini_empty(ini);
mini_save(ini, MINI_FLAGS_NONE);
if (!empty) {
if (mini_save(ini, MINI_FLAGS_NONE) != MINI_OK) {
path_free(overrides_path);
mini_free(ini);
return ROM_ERR_IO;
}
} }
mini_free(ini); mini_free(ini);
if (empty_override_file) { if (empty) {
file_delete(path_get(overrides_path)); if (remove(path_get(overrides_path))) {
path_free(overrides_path);
return ROM_ERR_IO;
}
} }
path_free(overrides_path); path_free(overrides_path);
@ -810,38 +843,45 @@ rom_err_t rom_info_override_save_type (path_t *path, rom_info_t *rom_info, rom_s
return ROM_OK; return ROM_OK;
} }
rom_err_t rom_info_override_tv_type (path_t *path, rom_info_t *rom_info, rom_tv_type_t tv_type) {
path_t *overrides_path = path_clone(path);
path_ext_replace(overrides_path, "ini"); rom_cic_type_t rom_info_get_cic_type (rom_info_t *rom_info) {
if (rom_info->override.cic) {
mini_t *ini = mini_try_load(path_get(overrides_path)); return rom_info->override.cic_type;
rom_info->override.tv_type = tv_type;
if (rom_info->override.tv_type == ROM_TV_TYPE_AUTOMATIC) {
rom_info->override.tv = false;
mini_delete_value(ini, NULL, "tv_type");
} else { } else {
rom_info->override.tv = true; return rom_info->cic_type;
mini_set_int(ini, NULL, "tv_type", rom_info->override.tv_type); }
}
bool rom_info_get_cic_seed (rom_info_t *rom_info, uint8_t *seed) {
cic_type_t cic_type;
switch (rom_info_get_cic_type(rom_info)) {
case ROM_CIC_TYPE_5101: cic_type = CIC_5101; break;
case ROM_CIC_TYPE_5167: cic_type = CIC_5167; break;
case ROM_CIC_TYPE_6101: cic_type = CIC_6101; break;
case ROM_CIC_TYPE_7102: cic_type = CIC_7102; break;
case ROM_CIC_TYPE_x102: cic_type = CIC_x102; break;
case ROM_CIC_TYPE_x103: cic_type = CIC_x103; break;
case ROM_CIC_TYPE_x105: cic_type = CIC_x105; break;
case ROM_CIC_TYPE_x106: cic_type = CIC_x106; break;
case ROM_CIC_TYPE_8301: cic_type = CIC_8301; break;
case ROM_CIC_TYPE_8302: cic_type = CIC_8302; break;
case ROM_CIC_TYPE_8303: cic_type = CIC_8303; break;
case ROM_CIC_TYPE_8401: cic_type = CIC_8401; break;
case ROM_CIC_TYPE_8501: cic_type = CIC_8501; break;
default: cic_type = CIC_UNKNOWN; break;
} }
bool empty_override_file = mini_empty(ini); *seed = cic_get_seed(cic_type);
if (!empty_override_file) { return (!rom_info->override.cic);
mini_save(ini, MINI_FLAGS_NONE); }
}
mini_free(ini); rom_err_t rom_info_override_cic_type (path_t *path, rom_info_t *rom_info, rom_cic_type_t cic_type) {
rom_info->override.cic = (cic_type != ROM_CIC_TYPE_AUTOMATIC);
rom_info->override.cic_type = cic_type;
if (empty_override_file) { return save_override(path, "cic_type", rom_info->override.cic_type, ROM_CIC_TYPE_AUTOMATIC);
file_delete(path_get(overrides_path));
}
path_free(overrides_path);
return ROM_OK;
} }
rom_save_type_t rom_info_get_save_type (rom_info_t *rom_info) { rom_save_type_t rom_info_get_save_type (rom_info_t *rom_info) {
@ -852,6 +892,13 @@ rom_save_type_t rom_info_get_save_type (rom_info_t *rom_info) {
} }
} }
rom_err_t rom_info_override_save_type (path_t *path, rom_info_t *rom_info, rom_save_type_t save_type) {
rom_info->override.save = (save_type != SAVE_TYPE_AUTOMATIC);
rom_info->override.save_type = save_type;
return save_override(path, "save_type", rom_info->override.save_type, SAVE_TYPE_AUTOMATIC);
}
rom_tv_type_t rom_info_get_tv_type (rom_info_t *rom_info) { rom_tv_type_t rom_info_get_tv_type (rom_info_t *rom_info) {
if (rom_info->override.tv) { if (rom_info->override.tv) {
return rom_info->override.tv_type; return rom_info->override.tv_type;
@ -860,22 +907,26 @@ rom_tv_type_t rom_info_get_tv_type (rom_info_t *rom_info) {
} }
} }
rom_err_t rom_info_override_tv_type (path_t *path, rom_info_t *rom_info, rom_tv_type_t tv_type) {
rom_info->override.tv = (tv_type != ROM_TV_TYPE_AUTOMATIC);
rom_info->override.tv_type = tv_type;
return save_override(path, "tv_type", rom_info->override.tv_type, ROM_TV_TYPE_AUTOMATIC);
}
rom_err_t rom_info_load (path_t *path, rom_info_t *rom_info) { 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

@ -1,7 +1,7 @@
/** /**
* @file rom_info.h * @file rom_info.h
* @brief N64 ROM Database. * @brief N64 ROM Database.
* @note Only works with N64 ROM's by checking the first 1024 bytes of the file. * @note Only works with N64 ROM's by checking the first 4096 bytes of the file.
* @ingroup menu * @ingroup menu
*/ */
@ -12,7 +12,6 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include "boot/cic.h"
#include "path.h" #include "path.h"
@ -31,7 +30,7 @@ typedef enum {
ENDIANNESS_LITTLE, ENDIANNESS_LITTLE,
/** @brief Is Byte Swapped Endian. */ /** @brief Is Byte Swapped Endian. */
ENDIANNESS_BYTE_SWAP, ENDIANNESS_BYTE_SWAP,
} endianness_t; } rom_endianness_t;
/** @brief ROM media type enumeration. */ /** @brief ROM media type enumeration. */
typedef enum { typedef enum {
@ -45,7 +44,7 @@ typedef enum {
N64_DISK_EXPANDABLE = 'E', N64_DISK_EXPANDABLE = 'E',
/** @brief Is an Aleck64 program. */ /** @brief Is an Aleck64 program. */
N64_ALECK64 = 'Z' N64_ALECK64 = 'Z'
} category_type_t; } rom_category_type_t;
/** @brief ROM market region & language type enumeration. */ /** @brief ROM market region & language type enumeration. */
typedef enum { typedef enum {
@ -89,27 +88,46 @@ typedef enum {
MARKET_OTHER_Y = 'Y', // many EU ROM's uses this. MARKET_OTHER_Y = 'Y', // many EU ROM's uses this.
/** @brief The ROM is designed for an undefined region and TBD language(s). */ /** @brief The ROM is designed for an undefined region and TBD language(s). */
MARKET_OTHER_Z = 'Z' // no known ROM's use this. MARKET_OTHER_Z = 'Z' // no known ROM's use this.
} destination_type_t; } rom_destination_type_t;
/** @brief ROM CIC type enumeration. */
typedef enum {
ROM_CIC_TYPE_UNKNOWN = 0, // No known CIC type detected
ROM_CIC_TYPE_5101 = 5101, // Aleck64 CIC-5101
ROM_CIC_TYPE_5167 = 5167, // 64DD ROM conversion CIC-5167
ROM_CIC_TYPE_6101 = 6101, // NTSC CIC-6101
ROM_CIC_TYPE_7102 = 7102, // PAL CIC-7102
ROM_CIC_TYPE_x102 = 6102, // NTSC CIC-6102 / PAL CIC-7101
ROM_CIC_TYPE_x103 = 6103, // NTSC CIC-6103 / PAL CIC-7103
ROM_CIC_TYPE_x105 = 6105, // NTSC CIC-6105 / PAL CIC-7105
ROM_CIC_TYPE_x106 = 6106, // NTSC CIC-6106 / PAL CIC-7106
ROM_CIC_TYPE_8301 = 8301, // NDDJ0 64DD IPL
ROM_CIC_TYPE_8302 = 8302, // NDDJ1 64DD IPL
ROM_CIC_TYPE_8303 = 8303, // NDDJ2 64DD IPL
ROM_CIC_TYPE_8401 = 8401, // NDXJ0 64DD IPL
ROM_CIC_TYPE_8501 = 8501, // NDDE0 64DD IPL
ROM_CIC_TYPE_AUTOMATIC = -1, // Guess CIC from IPL3
} rom_cic_type_t;
/** @brief ROM save type enumeration. */ /** @brief ROM save type enumeration. */
typedef enum { typedef enum {
/** @brief There is no expected save type. */ /** @brief There is no expected save type. */
SAVE_TYPE_NONE, SAVE_TYPE_NONE = 0,
SAVE_TYPE_EEPROM_4K, SAVE_TYPE_EEPROM_4K = 1,
SAVE_TYPE_EEPROM_16K, SAVE_TYPE_EEPROM_16K = 2,
SAVE_TYPE_SRAM, SAVE_TYPE_SRAM = 3,
SAVE_TYPE_SRAM_BANKED, SAVE_TYPE_SRAM_BANKED = 4,
SAVE_TYPE_SRAM_128K, SAVE_TYPE_SRAM_128K = 5,
SAVE_TYPE_FLASHRAM, SAVE_TYPE_FLASHRAM = 6,
SAVE_TYPE_FLASHRAM_PKST2, SAVE_TYPE_FLASHRAM_PKST2 = 7,
SAVE_TYPE_AUTOMATIC = -1, SAVE_TYPE_AUTOMATIC = -1,
} rom_save_type_t; } rom_save_type_t;
typedef enum { typedef enum {
ROM_TV_TYPE_PAL, ROM_TV_TYPE_PAL = 0,
ROM_TV_TYPE_NTSC, ROM_TV_TYPE_NTSC = 1,
ROM_TV_TYPE_MPAL, ROM_TV_TYPE_MPAL = 2,
ROM_TV_TYPE_UNKNOWN, ROM_TV_TYPE_UNKNOWN = 3,
ROM_TV_TYPE_AUTOMATIC = -1, ROM_TV_TYPE_AUTOMATIC = -1,
} rom_tv_type_t; } rom_tv_type_t;
@ -129,49 +147,62 @@ typedef enum {
/** @brief The ROM is faulty when using 8MB of memory. */ /** @brief The ROM is faulty when using 8MB of memory. */
EXPANSION_PAK_FAULTY, EXPANSION_PAK_FAULTY,
} expansion_pak_t; } rom_expansion_pak_t;
/** @brief ROM Information Structure. */ /** @brief ROM Information Structure. */
typedef struct { typedef struct {
/** @brief The file endian. */ /** @brief The file endian. */
endianness_t endianness; rom_endianness_t endianness;
/** @brief The clock rate defined in the ROM's header. */ /** @brief The clock rate defined in the ROM's header. */
float clock_rate; float clock_rate;
/** @brief The boot address defined in the ROM's header. */ /** @brief The boot address defined in the ROM's header. */
uint32_t boot_address; uint32_t boot_address;
struct { struct {
/** @brief The SDK version defined in the ROM's header. */ /** @brief The SDK version defined in the ROM's header. */
uint8_t version; uint8_t version;
/** @brief The SDK revision defined in the ROM's header. */ /** @brief The SDK revision defined in the ROM's header. */
char revision; char revision;
} libultra; } libultra;
/** @brief The check code defined in the ROM's header. */ /** @brief The check code defined in the ROM's header. */
uint64_t check_code; uint64_t check_code;
/** @brief The title defined in the ROM's header. */ /** @brief The title defined in the ROM's header. */
char title[20]; char title[20];
union { union {
/** @brief The game code defined in the ROM's header. */ /** @brief The game code defined in the ROM's header. */
char game_code[4]; char game_code[4];
struct { struct {
/** @brief The game media type. */ /** @brief The game media type. */
category_type_t category_code : 8; rom_category_type_t category_code : 8;
/** @brief The game unique identifier. */ /** @brief The game unique identifier. */
char unique_code[2]; char unique_code[2];
/** @brief The game region and or market. */ /** @brief The game region and or market. */
destination_type_t destination_code : 8; rom_destination_type_t destination_code : 8;
}; };
}; };
/** @brief The ROM version defined in the ROM's header. */ /** @brief The ROM version defined in the ROM's header. */
uint8_t version; uint8_t version;
cic_type_t cic_type; /** @brief The CIC type required by the ROM. */
rom_cic_type_t cic_type;
/** @brief The save type required by the ROM. */ /** @brief The save type required by the ROM. */
rom_save_type_t save_type; rom_save_type_t save_type;
/** @brief The TV type required by the ROM. */
rom_tv_type_t tv_type; rom_tv_type_t tv_type;
/** @brief Overrides of auto-detected CIC/save/TV types. */
struct { struct {
bool cic;
rom_cic_type_t cic_type;
bool save; bool save;
rom_save_type_t save_type; rom_save_type_t save_type;
@ -188,15 +219,21 @@ typedef struct {
bool real_time_clock; bool real_time_clock;
bool disk_conversion; bool disk_conversion;
bool combo_rom_disk_game; bool combo_rom_disk_game;
expansion_pak_t expansion_pak; rom_expansion_pak_t expansion_pak;
} features; } features;
} rom_info_t; } rom_info_t;
rom_err_t rom_info_override_save_type (path_t *path, rom_info_t *rom_info, rom_save_type_t save_type); rom_cic_type_t rom_info_get_cic_type (rom_info_t *rom_info);
rom_err_t rom_info_override_tv_type (path_t *path, rom_info_t *rom_info, rom_tv_type_t tv_type); bool rom_info_get_cic_seed (rom_info_t *rom_info, uint8_t *seed);
rom_err_t rom_info_override_cic_type (path_t *path, rom_info_t *rom_info, rom_cic_type_t cic_type);
rom_save_type_t rom_info_get_save_type (rom_info_t *rom_info); rom_save_type_t rom_info_get_save_type (rom_info_t *rom_info);
rom_err_t rom_info_override_save_type (path_t *path, rom_info_t *rom_info, rom_save_type_t save_type);
rom_tv_type_t rom_info_get_tv_type (rom_info_t *rom_info); rom_tv_type_t rom_info_get_tv_type (rom_info_t *rom_info);
rom_err_t rom_info_override_tv_type (path_t *path, rom_info_t *rom_info, rom_tv_type_t tv_type);
rom_err_t rom_info_load (path_t *path, rom_info_t *rom_info); rom_err_t rom_info_load (path_t *path, rom_info_t *rom_info);

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

@ -6,7 +6,7 @@
#define DEFAULT_FREQUENCY (44100) #define DEFAULT_FREQUENCY (44100)
#define NUM_BUFFERS (2) #define NUM_BUFFERS (4)
#define NUM_CHANNELS (2) #define NUM_CHANNELS (2)

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"
@ -12,10 +13,12 @@
#define MAX_FILE_SIZE MiB(4) #define MAX_FILE_SIZE MiB(4)
/** @brief The supported USB commands structure. */ /** @brief The supported USB commands structure. */
typedef struct { typedef struct {
/** @brief The command identifier. */ /** @brief The command identifier. */
const char *id; const char *id;
/** @brief The command operation. */ /** @brief The command operation. */
void (*op) (menu_t *menu); void (*op) (menu_t *menu);
} usb_comm_command_t; } usb_comm_command_t;
@ -71,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");
} }
@ -91,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");
@ -104,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"
@ -12,10 +11,23 @@
static const char *rom_extensions[] = { "z64", "n64", "v64", "rom", NULL }; static const char *rom_extensions[] = { "z64", "n64", "v64", "rom", NULL };
static const char *disk_extensions[] = { "ndd", NULL }; static const char *disk_extensions[] = { "ndd", NULL };
static const char *emulator_extensions[] = { "nes", "sfc", "smc", "gb", "gbc", "sms", "gg", "sg", NULL }; static const char *emulator_extensions[] = { "nes", "sfc", "smc", "gb", "gbc", "sms", "gg", "sg", NULL };
static const char *save_extensions[] = { "sav", NULL }; // TODO: "eep", "sra", "srm", "fla" could be used if transfered from different flashcarts. // TODO: "eep", "sra", "srm", "fla" could be used if transfered from different flashcarts.
static const char *save_extensions[] = { "sav", NULL };
static const char *image_extensions[] = { "png", NULL }; static const char *image_extensions[] = { "png", 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);
@ -50,78 +62,97 @@ static int compare_entry (const void *pa, const void *pb) {
return -1; return -1;
} else if (b->type == ENTRY_TYPE_MUSIC) { } else if (b->type == ENTRY_TYPE_MUSIC) {
return 1; return 1;
} else if (a->type == ENTRY_TYPE_TEXT) {
return -1;
} else if (b->type == ENTRY_TYPE_TEXT) {
return 1;
} }
} }
return strcasecmp((const char *) (a->name), (const char *) (b->name)); 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, music_extensions)) { } 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; 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;
} }
@ -135,6 +166,22 @@ static bool load_directory (menu_t *menu) {
return false; return false;
} }
static bool reload_directory (menu_t *menu) {
int selected = menu->browser.selected;
if (load_directory(menu)) {
return true;
}
menu->browser.selected = selected;
if (menu->browser.selected >= menu->browser.entries) {
menu->browser.selected = menu->browser.entries - 1;
}
menu->browser.entry = menu->browser.selected >= 0 ? &menu->browser.list[menu->browser.selected] : NULL;
return false;
}
static bool push_directory (menu_t *menu, char *directory) { static bool push_directory (menu_t *menu, char *directory) {
path_t *previous_directory = path_clone(menu->browser.directory); path_t *previous_directory = path_clone(menu->browser.directory);
@ -175,47 +222,35 @@ static bool pop_directory (menu_t *menu) {
return false; return false;
} }
static void show_properties (menu_t *menu) { static void show_properties (menu_t *menu, void *arg) {
menu->next_mode = MENU_MODE_FILE_INFO; menu->next_mode = MENU_MODE_FILE_INFO;
} }
static void delete_entry (menu_t *menu) { static void delete_entry (menu_t *menu, void *arg) {
int selected = menu->browser.selected;
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))) {
menu->browser.valid = false;
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);
if (load_directory(menu)) { if (reload_directory(menu)) {
menu->browser.valid = false; menu->browser.valid = false;
menu_show_error(menu, "Couldn't refresh directory contents after delete operation"); menu_show_error(menu, "Couldn't refresh directory contents after delete operation");
return;
} }
menu->browser.selected = selected;
if (menu->browser.selected >= menu->browser.entries) {
menu->browser.selected = menu->browser.entries - 1;
}
menu->browser.entry = menu->browser.selected >= 0 ? &menu->browser.list[menu->browser.selected] : NULL;
} }
static void set_default_directory (menu_t *menu) { static void set_default_directory (menu_t *menu, void *arg) {
free(menu->settings.default_directory); 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);
} }
@ -228,28 +263,18 @@ static component_context_menu_t entry_context_menu = {
} }
}; };
static void edit_settings (menu_t *menu) { static void set_menu_next_mode (menu_t *menu, void *arg) {
menu->next_mode = MENU_MODE_SETTINGS_EDITOR; menu_mode_t next_mode = (menu_mode_t) (arg);
} menu->next_mode = next_mode;
static void show_system_info (menu_t *menu) {
menu->next_mode = MENU_MODE_SYSTEM_INFO;
}
static void show_credits (menu_t *menu) {
menu->next_mode = MENU_MODE_CREDITS;
}
static void edit_rtc (menu_t *menu) {
menu->next_mode = MENU_MODE_RTC;
} }
static component_context_menu_t settings_context_menu = { static component_context_menu_t settings_context_menu = {
.list = { .list = {
{ .text = "Edit settings", .action = edit_settings }, { .text = "Edit settings", .action = set_menu_next_mode, .arg = (void *) (MENU_MODE_SETTINGS_EDITOR) },
{ .text = "Show system info", .action = show_system_info }, { .text = "Show system info", .action = set_menu_next_mode, .arg = (void *) (MENU_MODE_SYSTEM_INFO) },
{ .text = "Show credits", .action = show_credits }, { .text = "Show credits", .action = set_menu_next_mode, .arg = (void *) (MENU_MODE_CREDITS) },
{ .text = "Adjust RTC", .action = edit_rtc }, { .text = "Adjust RTC", .action = set_menu_next_mode, .arg = (void *) (MENU_MODE_RTC) },
{ .text = "Show cart info", .action = set_menu_next_mode, .arg = (void *) (MENU_MODE_FLASHCART) },
COMPONENT_CONTEXT_MENU_LIST_END, COMPONENT_CONTEXT_MENU_LIST_END,
} }
}; };
@ -300,6 +325,9 @@ static void process (menu_t *menu) {
case ENTRY_TYPE_IMAGE: case ENTRY_TYPE_IMAGE:
menu->next_mode = MENU_MODE_IMAGE_VIEWER; menu->next_mode = MENU_MODE_IMAGE_VIEWER;
break; break;
case ENTRY_TYPE_TEXT:
menu->next_mode = MENU_MODE_TEXT_VIEWER;
break;
case ENTRY_TYPE_MUSIC: case ENTRY_TYPE_MUSIC:
menu->next_mode = MENU_MODE_MUSIC_PLAYER; menu->next_mode = MENU_MODE_MUSIC_PLAYER;
break; break;
@ -337,6 +365,7 @@ static void draw (menu_t *menu, surface_t *d) {
case ENTRY_TYPE_ROM: action = "A: Load"; break; case ENTRY_TYPE_ROM: action = "A: Load"; break;
case ENTRY_TYPE_DISK: action = "A: Load"; break; case ENTRY_TYPE_DISK: action = "A: Load"; break;
case ENTRY_TYPE_IMAGE: action = "A: Show"; break; case ENTRY_TYPE_IMAGE: action = "A: Show"; break;
case ENTRY_TYPE_TEXT: action = "A: View"; break;
case ENTRY_TYPE_MUSIC: action = "A: Play"; break; case ENTRY_TYPE_MUSIC: action = "A: Play"; break;
default: action = "A: Info"; break; default: action = "A: Info"; break;
} }
@ -347,14 +376,14 @@ static void draw (menu_t *menu, surface_t *d) {
"%s\n" "%s\n"
"^%02XB: Back^00", "^%02XB: Back^00",
menu->browser.entries == 0 ? "" : action, menu->browser.entries == 0 ? "" : action,
path_is_root(menu->browser.directory) ? STL_UNKNOWN : STL_DEFAULT path_is_root(menu->browser.directory) ? STL_GRAY : STL_DEFAULT
); );
component_actions_bar_text_draw( component_actions_bar_text_draw(
ALIGN_RIGHT, VALIGN_TOP, ALIGN_RIGHT, VALIGN_TOP,
"Start: Settings\n" "Start: Settings\n"
"^%02XR: Options^00", "^%02XR: Options^00",
menu->browser.entries == 0 ? STL_UNKNOWN : STL_DEFAULT menu->browser.entries == 0 ? STL_GRAY : STL_DEFAULT
); );
if (menu->current_time >= 0) { if (menu->current_time >= 0) {
@ -380,24 +409,18 @@ 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;
} }
} }
if (menu->browser.reload) { if (menu->browser.reload) {
menu->browser.reload = false; menu->browser.reload = false;
int selected = menu->browser.selected; if (reload_directory(menu)) {
if (load_directory(menu)) {
menu_show_error(menu, "Error while reloading current directory"); menu_show_error(menu, "Error while reloading current directory");
menu->browser.valid = false; menu->browser.valid = false;
} else {
menu->browser.selected = selected;
if (menu->browser.selected >= menu->browser.entries) {
menu->browser.selected = menu->browser.entries - 1;
}
menu->browser.entry = menu->browser.selected >= 0 ? &menu->browser.list[menu->browser.selected] : NULL;
} }
} }
} }

View File

@ -2,7 +2,11 @@
#ifndef MENU_VERSION #ifndef MENU_VERSION
#define MENU_VERSION "0.0.0.6.ALPHA" #define MENU_VERSION "Unknown"
#endif
#ifndef BUILD_TIMESTAMP
#define BUILD_TIMESTAMP "Unknown"
#endif #endif
@ -28,7 +32,8 @@ static void draw (menu_t *menu, surface_t *d) {
ALIGN_LEFT, VALIGN_TOP, ALIGN_LEFT, VALIGN_TOP,
"\n" "\n"
"\n" "\n"
"Menu Revision: V%s\n" "Menu version: %s\n"
"Build timestamp: %s\n"
"\n" "\n"
"Github:\n" "Github:\n"
" https://github.com/Polprzewodnikowy/N64FlashcartMenu\n" " https://github.com/Polprzewodnikowy/N64FlashcartMenu\n"
@ -44,7 +49,8 @@ static void draw (menu_t *menu, surface_t *d) {
" mini.c (BSD 2-Clause License)\n" " mini.c (BSD 2-Clause License)\n"
" minimp3 (CC0 1.0 Universal)\n" " minimp3 (CC0 1.0 Universal)\n"
" miniz (MIT License)", " miniz (MIT License)",
MENU_VERSION MENU_VERSION,
BUILD_TIMESTAMP
); );
component_actions_bar_text_draw( component_actions_bar_text_draw(

View File

@ -50,5 +50,4 @@ void view_error_display (menu_t *menu, surface_t *display) {
void menu_show_error (menu_t *menu, char *error_message) { void menu_show_error (menu_t *menu, char *error_message) {
menu->next_mode = MENU_MODE_ERROR; menu->next_mode = MENU_MODE_ERROR;
menu->error_message = error_message; menu->error_message = error_message;
menu->browser.valid = false;
} }

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

@ -0,0 +1,46 @@
#include "views.h"
static void process (menu_t *menu) {
if (menu->actions.back) {
menu->next_mode = MENU_MODE_BROWSER;
}
}
static void draw (menu_t *menu, surface_t *d) {
rdpq_attach(d, NULL);
component_background_draw();
component_layout_draw();
component_main_text_draw(
ALIGN_CENTER, VALIGN_TOP,
"FLASHCART INFORMATION\n"
);
component_main_text_draw(
ALIGN_LEFT, VALIGN_TOP,
"\n"
"\n"
);
component_actions_bar_text_draw(
ALIGN_LEFT, VALIGN_TOP,
"\n"
"B: Back"
);
rdpq_detach_show();
}
void view_flashcart_info_init (menu_t *menu) {
// Nothing to initialize (yet)
}
void view_flashcart_info_display (menu_t *menu, surface_t *display) {
process(menu);
draw(menu, display);
}

View File

@ -126,9 +126,21 @@ static void load (menu_t *menu) {
} }
menu->next_mode = MENU_MODE_BOOT; menu->next_mode = MENU_MODE_BOOT;
menu->boot_params->device_type = load_rom ? BOOT_DEVICE_TYPE_ROM : BOOT_DEVICE_TYPE_64DD;
menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH; if (load_rom) {
menu->boot_params->device_type = BOOT_DEVICE_TYPE_ROM;
menu->boot_params->detect_cic_seed = rom_info_get_cic_seed(&menu->load.rom_info, &menu->boot_params->cic_seed);
switch (rom_info_get_tv_type(&menu->load.rom_info)) {
case ROM_TV_TYPE_PAL: menu->boot_params->tv_type = BOOT_TV_TYPE_PAL; break;
case ROM_TV_TYPE_NTSC: menu->boot_params->tv_type = BOOT_TV_TYPE_NTSC; break;
case ROM_TV_TYPE_MPAL: menu->boot_params->tv_type = BOOT_TV_TYPE_MPAL; break;
default: menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH; break;
}
} else {
menu->boot_params->device_type = BOOT_DEVICE_TYPE_64DD;
menu->boot_params->tv_type = BOOT_TV_TYPE_NTSC;
menu->boot_params->detect_cic_seed = true; menu->boot_params->detect_cic_seed = true;
}
} }
@ -142,7 +154,7 @@ void view_load_disk_init (menu_t *menu) {
menu->load.disk_path = path_clone_push(menu->browser.directory, menu->browser.entry->name); 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

@ -8,15 +8,15 @@ static bool load_pending;
static component_boxart_t *boxart; static component_boxart_t *boxart;
static char *convert_error_message (disk_err_t err) { static char *convert_error_message (rom_err_t err) {
switch (err) { switch (err) {
case ROM_ERR_IO: return "I/O error during loading ROM information"; case ROM_ERR_IO: return "I/O error during loading/storing ROM information";
case ROM_ERR_NO_FILE: return "Couldn't open ROM file"; case ROM_ERR_NO_FILE: return "Couldn't open ROM file";
default: return "Unknown ROM info load error"; default: return "Unknown ROM info load error";
} }
} }
static const char *format_rom_endianness (endianness_t endianness) { static const char *format_rom_endianness (rom_endianness_t endianness) {
switch (endianness) { switch (endianness) {
case ENDIANNESS_BIG: return "Big (default)"; case ENDIANNESS_BIG: return "Big (default)";
case ENDIANNESS_LITTLE: return "Little (unsupported)"; case ENDIANNESS_LITTLE: return "Little (unsupported)";
@ -25,7 +25,7 @@ static const char *format_rom_endianness (endianness_t endianness) {
} }
} }
static const char *format_rom_media_type (category_type_t media_type) { static const char *format_rom_media_type (rom_category_type_t media_type) {
switch (media_type) { switch (media_type) {
case N64_CART: return "Cartridge"; case N64_CART: return "Cartridge";
case N64_DISK: return "Disk"; case N64_DISK: return "Disk";
@ -36,7 +36,7 @@ static const char *format_rom_media_type (category_type_t media_type) {
} }
} }
static const char *format_rom_destination_market (destination_type_t market_type) { static const char *format_rom_destination_market (rom_destination_type_t market_type) {
// TODO: These are all assumptions and should be corrected if required. // TODO: These are all assumptions and should be corrected if required.
// From http://n64devkit.square7.ch/info/submission/pal/01-01.html // From http://n64devkit.square7.ch/info/submission/pal/01-01.html
switch (market_type) { switch (market_type) {
@ -87,7 +87,7 @@ static const char *format_rom_tv_type (rom_tv_type_t tv_type) {
} }
} }
static char *format_rom_expansion_pak_info (expansion_pak_t expansion_pak_info) { static const char *format_rom_expansion_pak_info (rom_expansion_pak_t expansion_pak_info) {
switch (expansion_pak_info) { switch (expansion_pak_info) {
case EXPANSION_PAK_REQUIRED: return "Required"; case EXPANSION_PAK_REQUIRED: return "Required";
case EXPANSION_PAK_RECOMMENDED: return "Recommended"; case EXPANSION_PAK_RECOMMENDED: return "Recommended";
@ -97,72 +97,94 @@ static char *format_rom_expansion_pak_info (expansion_pak_t expansion_pak_info)
} }
} }
static const char *format_cic_type (cic_type_t cic_type) { static const char *format_cic_type (rom_cic_type_t cic_type) {
switch (cic_type) { switch (cic_type) {
case CIC_5101: return "5101"; case ROM_CIC_TYPE_5101: return "5101";
case CIC_5167: return "5167"; case ROM_CIC_TYPE_5167: return "5167";
case CIC_6101: return "6101"; case ROM_CIC_TYPE_6101: return "6101";
case CIC_7102: return "7102"; case ROM_CIC_TYPE_7102: return "7102";
case CIC_6102_7101: return "6102 / 7101"; case ROM_CIC_TYPE_x102: return "6102 / 7101";
case CIC_x103: return "6103 / 7103"; case ROM_CIC_TYPE_x103: return "6103 / 7103";
case CIC_x105: return "6105 / 7105"; case ROM_CIC_TYPE_x105: return "6105 / 7105";
case CIC_x106: return "6106 / 7106"; case ROM_CIC_TYPE_x106: return "6106 / 7106";
case CIC_8301: return "8301"; case ROM_CIC_TYPE_8301: return "8301";
case CIC_8302: return "8302"; case ROM_CIC_TYPE_8302: return "8302";
case CIC_8303: return "8303"; case ROM_CIC_TYPE_8303: return "8303";
case CIC_8401: return "8401"; case ROM_CIC_TYPE_8401: return "8401";
case CIC_8501: return "8501"; case ROM_CIC_TYPE_8501: return "8501";
default: return "Unknown"; default: return "Unknown";
} }
} }
static void set_save_type (menu_t *menu, rom_save_type_t save_type) { static void set_cic_type (menu_t *menu, void *arg) {
rom_info_override_save_type(menu->load.rom_path, &menu->load.rom_info, save_type); rom_cic_type_t cic_type = (rom_cic_type_t) (arg);
rom_err_t err = rom_info_override_cic_type(menu->load.rom_path, &menu->load.rom_info, cic_type);
if (err != ROM_OK) {
menu_show_error(menu, convert_error_message(err));
}
menu->browser.reload = true; menu->browser.reload = true;
} }
static void set_save_type_automatic (menu_t *menu) { set_save_type(menu, SAVE_TYPE_AUTOMATIC); } static void set_save_type (menu_t *menu, void *arg) {
static void set_save_type_none (menu_t *menu) { set_save_type(menu, SAVE_TYPE_NONE); } rom_save_type_t save_type = (rom_save_type_t) (arg);
static void set_save_type_eeprom_4kbit (menu_t *menu) { set_save_type(menu, SAVE_TYPE_EEPROM_4K); } rom_err_t err = rom_info_override_save_type(menu->load.rom_path, &menu->load.rom_info, save_type);
static void set_save_type_eeprom_16kbit (menu_t *menu) { set_save_type(menu, SAVE_TYPE_EEPROM_16K); } if (err != ROM_OK) {
static void set_save_type_sram_256kbit (menu_t *menu) { set_save_type(menu, SAVE_TYPE_SRAM); } menu_show_error(menu, convert_error_message(err));
static void set_save_type_sram_768kbit (menu_t *menu) { set_save_type(menu, SAVE_TYPE_SRAM_BANKED); } }
static void set_save_type_sram_1mbit (menu_t *menu) { set_save_type(menu, SAVE_TYPE_SRAM_128K); } menu->browser.reload = true;
static void set_save_type_flash_ram_1mbit (menu_t *menu) { set_save_type(menu, SAVE_TYPE_FLASHRAM); } }
static component_context_menu_t set_save_type_context_menu = { .list = { static void set_tv_type (menu_t *menu, void *arg) {
{ .text = "Automatic", .action = set_save_type_automatic }, rom_tv_type_t tv_type = (rom_tv_type_t) (arg);
{ .text = "None", .action = set_save_type_none }, rom_err_t err = rom_info_override_tv_type(menu->load.rom_path, &menu->load.rom_info, tv_type);
{ .text = "EEPROM 4kbit", .action = set_save_type_eeprom_4kbit }, if (err != ROM_OK) {
{ .text = "EEPROM 16kbit", .action = set_save_type_eeprom_16kbit }, menu_show_error(menu, convert_error_message(err));
{ .text = "SRAM 256kbit", .action = set_save_type_sram_256kbit }, }
{ .text = "SRAM 768kbit", .action = set_save_type_sram_768kbit }, menu->browser.reload = true;
{ .text = "SRAM 1Mbit", .action = set_save_type_sram_1mbit }, }
{ .text = "FlashRAM 1Mbit", .action = set_save_type_flash_ram_1mbit },
static component_context_menu_t set_cic_type_context_menu = { .list = {
{.text = "Automatic", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_AUTOMATIC) },
{.text = "CIC-6101", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_6101) },
{.text = "CIC-7102", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_7102) },
{.text = "CIC-6102 / CIC-7101", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_x102) },
{.text = "CIC-6103 / CIC-7103", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_x103) },
{.text = "CIC-6105 / CIC-7105", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_x105) },
{.text = "CIC-6106 / CIC-7106", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_x106) },
{.text = "Aleck64 CIC-5101", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_5101) },
{.text = "64DD ROM conversion CIC-5167", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_5167) },
{.text = "NDDJ0 64DD IPL", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_8301) },
{.text = "NDDJ1 64DD IPL", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_8302) },
{.text = "NDDJ2 64DD IPL", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_8303) },
{.text = "NDXJ0 64DD IPL", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_8401) },
{.text = "NDDE0 64DD IPL", .action = set_cic_type, .arg = (void *) (ROM_CIC_TYPE_8501) },
COMPONENT_CONTEXT_MENU_LIST_END, COMPONENT_CONTEXT_MENU_LIST_END,
}}; }};
static void set_tv_type (menu_t *menu, rom_tv_type_t tv_type) { static component_context_menu_t set_save_type_context_menu = { .list = {
rom_info_override_tv_type(menu->load.rom_path, &menu->load.rom_info, tv_type); { .text = "Automatic", .action = set_save_type, .arg = (void *) (SAVE_TYPE_AUTOMATIC) },
menu->browser.reload = true; { .text = "None", .action = set_save_type, .arg = (void *) (SAVE_TYPE_NONE) },
} { .text = "EEPROM 4kbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_EEPROM_4K) },
{ .text = "EEPROM 16kbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_EEPROM_16K) },
static void set_tv_type_automatic (menu_t *menu) { set_tv_type(menu, ROM_TV_TYPE_AUTOMATIC); } { .text = "SRAM 256kbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_SRAM) },
static void set_tv_type_pal (menu_t *menu) { set_tv_type(menu, ROM_TV_TYPE_PAL); } { .text = "SRAM 768kbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_SRAM_BANKED) },
static void set_tv_type_ntsc (menu_t *menu) { set_tv_type(menu, ROM_TV_TYPE_NTSC); } { .text = "SRAM 1Mbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_SRAM_128K) },
static void set_tv_type_mpal (menu_t *menu) { set_tv_type(menu, ROM_TV_TYPE_MPAL); } { .text = "FlashRAM 1Mbit", .action = set_save_type, .arg = (void *) (SAVE_TYPE_FLASHRAM) },
COMPONENT_CONTEXT_MENU_LIST_END,
}};
static component_context_menu_t set_tv_type_context_menu = { .list = { static component_context_menu_t set_tv_type_context_menu = { .list = {
{ .text = "Automatic", .action = set_tv_type_automatic }, { .text = "Automatic", .action = set_tv_type, .arg = (void *) (ROM_TV_TYPE_AUTOMATIC) },
{ .text = "PAL", .action = set_tv_type_pal }, { .text = "PAL", .action = set_tv_type, .arg = (void *) (ROM_TV_TYPE_PAL) },
{ .text = "NTSC", .action = set_tv_type_ntsc }, { .text = "NTSC", .action = set_tv_type, .arg = (void *) (ROM_TV_TYPE_NTSC) },
{ .text = "MPAL", .action = set_tv_type_mpal }, { .text = "MPAL", .action = set_tv_type, .arg = (void *) (ROM_TV_TYPE_MPAL) },
COMPONENT_CONTEXT_MENU_LIST_END, COMPONENT_CONTEXT_MENU_LIST_END,
}}; }};
static component_context_menu_t options_context_menu = { .list = { static component_context_menu_t options_context_menu = { .list = {
{ .text = "Set save type", .submenu = &set_save_type_context_menu }, { .text = "Set CIC Type", .submenu = &set_cic_type_context_menu },
{ .text = "Set TV type", .submenu = &set_tv_type_context_menu }, { .text = "Set Save Type", .submenu = &set_save_type_context_menu },
{ .text = "Set TV Type", .submenu = &set_tv_type_context_menu },
COMPONENT_CONTEXT_MENU_LIST_END, COMPONENT_CONTEXT_MENU_LIST_END,
}}; }};
@ -230,7 +252,7 @@ static void draw (menu_t *menu, surface_t *d) {
format_rom_save_type(rom_info_get_save_type(&menu->load.rom_info)), format_rom_save_type(rom_info_get_save_type(&menu->load.rom_info)),
format_rom_tv_type(rom_info_get_tv_type(&menu->load.rom_info)), format_rom_tv_type(rom_info_get_tv_type(&menu->load.rom_info)),
format_rom_expansion_pak_info(menu->load.rom_info.features.expansion_pak), format_rom_expansion_pak_info(menu->load.rom_info.features.expansion_pak),
format_cic_type(menu->load.rom_info.cic_type), format_cic_type(rom_info_get_cic_type(&menu->load.rom_info)),
menu->load.rom_info.boot_address, menu->load.rom_info.boot_address,
(menu->load.rom_info.libultra.version / 10.0f), menu->load.rom_info.libultra.revision, (menu->load.rom_info.libultra.version / 10.0f), menu->load.rom_info.libultra.revision,
menu->load.rom_info.clock_rate menu->load.rom_info.clock_rate
@ -281,8 +303,8 @@ static void load (menu_t *menu) {
menu->next_mode = MENU_MODE_BOOT; menu->next_mode = MENU_MODE_BOOT;
menu->boot_params->device_type = BOOT_DEVICE_TYPE_ROM; menu->boot_params->device_type = BOOT_DEVICE_TYPE_ROM;
menu->boot_params->detect_cic_seed = true; menu->boot_params->detect_cic_seed = rom_info_get_cic_seed(&menu->load.rom_info, &menu->boot_params->cic_seed);
switch(rom_info_get_tv_type(&menu->load.rom_info)) { switch (rom_info_get_tv_type(&menu->load.rom_info)) {
case ROM_TV_TYPE_PAL: menu->boot_params->tv_type = BOOT_TV_TYPE_PAL; break; case ROM_TV_TYPE_PAL: menu->boot_params->tv_type = BOOT_TV_TYPE_PAL; break;
case ROM_TV_TYPE_NTSC: menu->boot_params->tv_type = BOOT_TV_TYPE_NTSC; break; case ROM_TV_TYPE_NTSC: menu->boot_params->tv_type = BOOT_TV_TYPE_NTSC; break;
case ROM_TV_TYPE_MPAL: menu->boot_params->tv_type = BOOT_TV_TYPE_MPAL; break; case ROM_TV_TYPE_MPAL: menu->boot_params->tv_type = BOOT_TV_TYPE_MPAL; break;
@ -296,21 +318,23 @@ static void deinit (void) {
void view_load_rom_init (menu_t *menu) { void view_load_rom_init (menu_t *menu) {
load_pending = false;
if (menu->load.rom_path) { if (menu->load.rom_path) {
path_free(menu->load.rom_path); path_free(menu->load.rom_path);
menu->load.rom_path = NULL;
} }
load_pending = false;
menu->load.rom_path = path_clone_push(menu->browser.directory, menu->browser.entry->name); menu->load.rom_path = path_clone_push(menu->browser.directory, menu->browser.entry->name);
rom_err_t err = rom_info_load(menu->load.rom_path, &menu->load.rom_info); rom_err_t err = rom_info_load(menu->load.rom_path, &menu->load.rom_info);
if (err != ROM_OK) { if (err != ROM_OK) {
path_free(menu->load.rom_path);
menu->load.rom_path = NULL;
menu_show_error(menu, convert_error_message(err)); menu_show_error(menu, convert_error_message(err));
return;
} }
boxart = component_boxart_init(menu->load.rom_info.game_code); boxart = component_boxart_init(menu->storage_prefix, menu->load.rom_info.game_code);
component_context_menu_init(&options_context_menu); component_context_menu_init(&options_context_menu);
} }

View File

@ -1,13 +1,14 @@
#include "views.h" #include "views.h"
static char *format_boolean_type (int state) {
static const char *format_switch (bool state) {
switch (state) { switch (state) {
case 0: return "Off"; case true: return "On";
case 1: return "On"; case false: return "Off";
default: return "Unknown";
} }
} }
static void process (menu_t *menu) { static void process (menu_t *menu) {
if (menu->actions.back) { if (menu->actions.back) {
menu->next_mode = MENU_MODE_BROWSER; menu->next_mode = MENU_MODE_BROWSER;
@ -34,22 +35,21 @@ 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_boolean_type(menu->settings.pal60_enabled), format_switch(menu->settings.pal60_enabled),
format_boolean_type(menu->settings.hidden_files_enabled), format_switch(menu->settings.show_protected_entries),
menu->settings.default_directory, menu->settings.default_directory,
format_boolean_type(menu->settings.use_saves_folder), format_switch(menu->settings.use_saves_folder),
format_boolean_type(menu->settings.bgm_enabled), format_switch(menu->settings.bgm_enabled),
format_boolean_type(menu->settings.sound_enabled), format_switch(menu->settings.sound_enabled),
format_boolean_type(menu->settings.rumble_enabled) format_switch(menu->settings.rumble_enabled)
); );
component_actions_bar_text_draw( component_actions_bar_text_draw(
ALIGN_LEFT, VALIGN_TOP, ALIGN_LEFT, VALIGN_TOP,
"\n" "\n"
@ -66,5 +66,6 @@ void view_settings_init (menu_t *menu) {
void view_settings_display (menu_t *menu, surface_t *display) { void view_settings_display (menu_t *menu, surface_t *display) {
process(menu); process(menu);
draw(menu, display); draw(menu, display);
} }

View File

@ -7,27 +7,25 @@ static int joypad[4];
static int accessory[4]; static int accessory[4];
static char *format_accessory (int joypad) { static const char *format_accessory (int joypad) {
switch (accessory[joypad]) { switch (accessory[joypad]) {
case JOYPAD_ACCESSORY_TYPE_RUMBLE_PAK: case JOYPAD_ACCESSORY_TYPE_RUMBLE_PAK: return "[Rumble Pak is inserted]";
return "[Rumble Pak is inserted]"; case JOYPAD_ACCESSORY_TYPE_CONTROLLER_PAK: return "[Controller Pak is inserted]";
case JOYPAD_ACCESSORY_TYPE_CONTROLLER_PAK: case JOYPAD_ACCESSORY_TYPE_TRANSFER_PAK: return "[Transfer Pak is inserted]";
return "[Controller Pak is inserted]"; case JOYPAD_ACCESSORY_TYPE_BIO_SENSOR: return "[BIO Sensor is inserted]";
case JOYPAD_ACCESSORY_TYPE_TRANSFER_PAK: case JOYPAD_ACCESSORY_TYPE_SNAP_STATION: return "[Snap Station is inserted]";
return "[Transfer Pak is inserted]"; case JOYPAD_ACCESSORY_TYPE_NONE: return "";
case JOYPAD_ACCESSORY_TYPE_BIO_SENSOR: default: return "[unknown accessory inserted]";
return "[BIO Sensor is inserted]";
case JOYPAD_ACCESSORY_TYPE_SNAP_STATION:
return "[Snap Station is inserted]";
case JOYPAD_ACCESSORY_TYPE_NONE:
return "";
default:
return "[unknown accessory inserted]";
} }
} }
static void process (menu_t *menu) { static void process (menu_t *menu) {
JOYPAD_PORT_FOREACH (port) {
joypad[port] = (joypad_get_style(port) != JOYPAD_STYLE_NONE);
accessory[port] = joypad_get_accessory_type(port);
}
if (menu->actions.back) { if (menu->actions.back) {
menu->next_mode = MENU_MODE_BROWSER; menu->next_mode = MENU_MODE_BROWSER;
} }
@ -53,10 +51,10 @@ static void draw (menu_t *menu, surface_t *d) {
"\n" "\n"
"Expansion PAK is %sinserted\n" "Expansion PAK is %sinserted\n"
"\n" "\n"
"JoyPad 1 is %sconnected %s\n" "Joypad 1 is %sconnected %s\n"
"JoyPad 2 is %sconnected %s\n" "Joypad 2 is %sconnected %s\n"
"JoyPad 3 is %sconnected %s\n" "Joypad 3 is %sconnected %s\n"
"JoyPad 4 is %sconnected %s\n", "Joypad 4 is %sconnected %s\n",
menu->current_time >= 0 ? ctime(&menu->current_time) : "Unknown\n", menu->current_time >= 0 ? ctime(&menu->current_time) : "Unknown\n",
is_memory_expanded() ? "" : "not ", is_memory_expanded() ? "" : "not ",
(joypad[0]) ? "" : "not ", format_accessory(0), (joypad[0]) ? "" : "not ", format_accessory(0),
@ -76,10 +74,7 @@ static void draw (menu_t *menu, surface_t *d) {
void view_system_info_init (menu_t *menu) { void view_system_info_init (menu_t *menu) {
JOYPAD_PORT_FOREACH (port) { // Nothing to initialize (yet)
joypad[port] = (joypad_get_style(port) != JOYPAD_STYLE_NONE);
accessory[port] = joypad_get_accessory_type(port);
}
} }
void view_system_info_display (menu_t *menu, surface_t *display) { void view_system_info_display (menu_t *menu, surface_t *display) {

View File

@ -0,0 +1,172 @@
#include <stdio.h>
#include <sys/stat.h>
#include "../components/constants.h"
#include "../fonts.h"
#include "utils/utils.h"
#include "views.h"
#define MAX_FILE_SIZE KiB(128)
typedef struct {
FILE *f;
char *contents;
size_t length;
int lines;
int current_line;
int offset;
bool vertical_scroll_possible;
} text_file_t;
static text_file_t *text;
static void perform_vertical_scroll (int lines) {
if (!text->vertical_scroll_possible) {
return;
}
int direction = (lines < 0) ? -1 : 1;
int next_offset = text->offset;
for (int i = 0; i < abs(lines); i++) {
while (true) {
next_offset += direction;
if (next_offset <= 0) {
text->current_line = 0;
text->offset = 0;
return;
}
if (next_offset > text->length) {
return;
}
if (text->contents[next_offset - 1] == '\n') {
break;
}
}
text->current_line += direction;
text->offset = next_offset;
}
}
static void process (menu_t *menu) {
if (menu->actions.back) {
menu->next_mode = MENU_MODE_BROWSER;
} else if (text) {
if (menu->actions.go_up) {
perform_vertical_scroll(menu->actions.go_fast ? -10 : -1);
} else if (menu->actions.go_down) {
perform_vertical_scroll(menu->actions.go_fast ? 10 : 1);
}
}
}
static void draw (menu_t *menu, surface_t *d) {
rdpq_attach(d, NULL);
component_background_draw();
component_layout_draw();
component_main_text_draw(
ALIGN_LEFT, VALIGN_TOP,
"%s\n",
text->contents + text->offset
);
component_list_scrollbar_draw(text->current_line, text->lines, LIST_ENTRIES);
component_actions_bar_text_draw(
ALIGN_LEFT, VALIGN_TOP,
"^%02XUp / Down: Scroll^00\n"
"B: Back",
text->vertical_scroll_possible ? STL_DEFAULT : STL_GRAY
);
rdpq_detach_show();
}
static void deinit (void) {
if (text) {
if (text->f) {
fclose(text->f);
}
if (text->contents) {
free(text->contents);
}
free(text);
text = NULL;
}
}
void view_text_viewer_init (menu_t *menu) {
if ((text = calloc(1, sizeof(text_file_t))) == NULL) {
return menu_show_error(menu, "Couldn't allocate memory for the text file");
}
path_t *path = path_clone_push(menu->browser.directory, menu->browser.entry->name);
text->f = fopen(path_get(path), "r");
path_free(path);
if (text->f == NULL) {
deinit();
return menu_show_error(menu, "Couldn't open text file");
}
struct stat st;
if (fstat(fileno(text->f), &st)) {
deinit();
return menu_show_error(menu, "Couldn't get text file size");
}
text->length = st.st_size;
if (text->length <= 0) {
deinit();
return menu_show_error(menu, "Text file is empty");
}
if (text->length > MAX_FILE_SIZE) {
deinit();
return menu_show_error(menu, "Text file is too big to be displayed");
}
if ((text->contents = malloc((text->length + 1) * sizeof(char))) == NULL) {
deinit();
return menu_show_error(menu, "Couldn't allocate memory for the text file contents");
}
if (fread(text->contents, text->length, 1, text->f) != 1) {
deinit();
return menu_show_error(menu, "Couldn't read text file contents");
}
text->contents[text->length] = '\0';
if (fclose(text->f)) {
deinit();
return menu_show_error(menu, "Couldn't close text file");
}
text->f = NULL;
text->lines = 1;
for (size_t i = 0; i < text->length; i++) {
if (text->contents[i] == '\n') {
text->lines += 1;
}
}
text->vertical_scroll_possible = (text->lines > LIST_ENTRIES);
}
void view_text_viewer_display (menu_t *menu, surface_t *display) {
process(menu);
draw(menu, display);
if (menu->next_mode != MENU_MODE_TEXT_VIEWER) {
deinit();
}
}

View File

@ -23,33 +23,39 @@ void view_startup_display (menu_t *menu, surface_t *display);
void view_browser_init (menu_t *menu); void view_browser_init (menu_t *menu);
void view_browser_display (menu_t *menu, surface_t *display); void view_browser_display (menu_t *menu, surface_t *display);
void view_system_info_init (menu_t *menu);
void view_system_info_display (menu_t *menu, surface_t *display);
void view_file_info_init (menu_t *menu); void view_file_info_init (menu_t *menu);
void view_file_info_display (menu_t *menu, surface_t *display); void view_file_info_display (menu_t *menu, surface_t *display);
void view_system_info_init (menu_t *menu);
void view_system_info_display (menu_t *menu, surface_t *display);
void view_image_viewer_init (menu_t *menu); void view_image_viewer_init (menu_t *menu);
void view_image_viewer_display (menu_t *menu, surface_t *display); void view_image_viewer_display (menu_t *menu, surface_t *display);
void view_text_viewer_init (menu_t *menu);
void view_text_viewer_display (menu_t *menu, surface_t *display);
void view_music_player_init (menu_t *menu); void view_music_player_init (menu_t *menu);
void view_music_player_display (menu_t *menu, surface_t *display); void view_music_player_display (menu_t *menu, surface_t *display);
void view_credits_init (menu_t *menu); void view_credits_init (menu_t *menu);
void view_credits_display (menu_t *menu, surface_t *display); void view_credits_display (menu_t *menu, surface_t *display);
void view_load_rom_init (menu_t *menu);
void view_load_rom_display (menu_t *menu, surface_t *display);
void view_load_disk_init (menu_t *menu);
void view_load_disk_display (menu_t *menu, surface_t *display);
void view_settings_init (menu_t *menu); void view_settings_init (menu_t *menu);
void view_settings_display (menu_t *menu, surface_t *display); void view_settings_display (menu_t *menu, surface_t *display);
void view_rtc_init (menu_t *menu); void view_rtc_init (menu_t *menu);
void view_rtc_display (menu_t *menu, surface_t *display); void view_rtc_display (menu_t *menu, surface_t *display);
void view_flashcart_info_init (menu_t *menu);
void view_flashcart_info_display (menu_t *menu, surface_t *display);
void view_load_rom_init (menu_t *menu);
void view_load_rom_display (menu_t *menu, surface_t *display);
void view_load_disk_init (menu_t *menu);
void view_load_disk_display (menu_t *menu, surface_t *display);
void view_load_emulator_init (menu_t *menu); void view_load_emulator_init (menu_t *menu);
void view_load_emulator_display (menu_t *menu, surface_t *display); void view_load_emulator_display (menu_t *menu, surface_t *display);

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);