mirror of
https://github.com/Polprzewodnikowy/N64FlashcartMenu.git
synced 2024-11-22 02:29:19 +01:00
Browser rewrite / Boot bug fixes
This commit is contained in:
parent
1288d4fdcd
commit
e0f0734e18
@ -1,4 +1,4 @@
|
||||
FROM ubuntu:latest
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get upgrade -y && \
|
||||
@ -6,8 +6,9 @@ RUN apt-get update && \
|
||||
wget https://github.com/DragonMinded/libdragon/releases/download/toolchain-continuous-prerelease/gcc-toolchain-mips64-x86_64.deb && \
|
||||
dpkg -i gcc-toolchain-mips64-x86_64.deb && \
|
||||
rm gcc-toolchain-mips64-x86_64.deb && \
|
||||
wget https://github.com/Polprzewodnikowy/SummerCart64/releases/download/v2.15.1/sc64-deployer-linux-v2.15.1.tar.gz && \
|
||||
wget https://github.com/Polprzewodnikowy/SummerCart64/releases/download/v2.16.0/sc64-deployer-linux-v2.16.0.tar.gz && \
|
||||
tar -xf sc64-deployer-linux-v2.15.1.tar.gz -C /usr/local/bin && \
|
||||
rm sc64-deployer-linux-v2.15.1.tar.gz && \
|
||||
git config --global --add safe.directory "*" && \
|
||||
SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" && \
|
||||
echo "$SNIPPET" >> "/root/.bashrc"
|
||||
|
@ -6,7 +6,7 @@
|
||||
"mounts": [
|
||||
"source=n64flashcartmenu-bashhistory,target=/commandhistory,type=volume"
|
||||
],
|
||||
"postCreateCommand": "cd ./libdragon && ./build.sh",
|
||||
"postCreateCommand": "git submodule update --init && cd ./libdragon && ./build.sh",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
|
20
Makefile
20
Makefile
@ -12,20 +12,26 @@ include $(N64_INST)/include/n64.mk
|
||||
N64_CFLAGS += -iquote $(SOURCE_DIR)
|
||||
|
||||
SRCS = \
|
||||
main.c \
|
||||
boot/boot.c \
|
||||
boot/crc32.c \
|
||||
boot/ipl2.S \
|
||||
flashcart/flashcart.c \
|
||||
flashcart/sc64/sc64_internal.c \
|
||||
flashcart/sc64/sc64.c \
|
||||
menu/menu_main.c \
|
||||
menu/menu_info.c \
|
||||
menu/menu_fileinfo.c \
|
||||
menu/settings.c \
|
||||
menu/rom_database.c \
|
||||
utils/fs.c \
|
||||
libs/toml/toml.c \
|
||||
main.c
|
||||
menu/actions.c \
|
||||
menu/menu.c \
|
||||
menu/path.c \
|
||||
menu/rom_database.c \
|
||||
menu/settings.c \
|
||||
menu/views/browser.c \
|
||||
menu/views/credits.c \
|
||||
menu/views/error.c \
|
||||
menu/views/file_info.c \
|
||||
menu/views/init.c \
|
||||
menu/views/load.c \
|
||||
utils/fs.c
|
||||
|
||||
OBJS = $(addprefix $(BUILD_DIR)/, $(addsuffix .o,$(basename $(SRCS))))
|
||||
|
||||
|
@ -17,7 +17,7 @@ static const ipl3_crc32_t ipl3_crc32[] = {
|
||||
{ .crc32 = 0x587BD543, .seed = 0xAC }, // 5101
|
||||
{ .crc32 = 0x6170A4A1, .seed = 0x3F }, // 6101
|
||||
{ .crc32 = 0x009E9EA3, .seed = 0x3F }, // 7102
|
||||
{ .crc32 = 0x90BB6CB5, .seed = 0x3F }, // x102
|
||||
{ .crc32 = 0x90BB6CB5, .seed = 0x3F }, // 6102/7101
|
||||
{ .crc32 = 0x0B050EE0, .seed = 0x78 }, // x103
|
||||
{ .crc32 = 0x98BC2C86, .seed = 0x91 }, // x105
|
||||
{ .crc32 = 0xACC8580A, .seed = 0x85 }, // x106
|
||||
@ -38,33 +38,6 @@ static io32_t *boot_get_device_base (boot_params_t *params) {
|
||||
return device_base_address;
|
||||
}
|
||||
|
||||
static bool boot_detect_tv_type (boot_params_t *params) {
|
||||
io32_t *base = boot_get_device_base(params);
|
||||
|
||||
char region = ((io_read((uint32_t) (&base[15])) >> 8) & 0xFF);
|
||||
|
||||
switch (region) {
|
||||
case 'P':
|
||||
case 'U':
|
||||
params->tv_type = BOOT_TV_TYPE_PAL;
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
case 'J':
|
||||
params->tv_type = BOOT_TV_TYPE_NTSC;
|
||||
break;
|
||||
|
||||
case 'B':
|
||||
params->tv_type = BOOT_TV_TYPE_MPAL;
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool boot_detect_cic_seed (boot_params_t *params) {
|
||||
io32_t *base = boot_get_device_base(params);
|
||||
|
||||
@ -92,11 +65,9 @@ bool boot_is_warm (void) {
|
||||
}
|
||||
|
||||
void boot (boot_params_t *params) {
|
||||
if (params->detect_tv_type) {
|
||||
if (!boot_detect_tv_type(params)) {
|
||||
if (params->tv_type == BOOT_TV_TYPE_PASSTHROUGH) {
|
||||
params->tv_type = OS_INFO->tv_type;
|
||||
}
|
||||
}
|
||||
|
||||
if (params->detect_cic_seed) {
|
||||
if (!boot_detect_cic_seed(params)) {
|
||||
@ -104,6 +75,12 @@ void boot (boot_params_t *params) {
|
||||
}
|
||||
}
|
||||
|
||||
// asm volatile (
|
||||
// "li $t1, %[status] \n"
|
||||
// "mtc0 $t1, $12 \n" ::
|
||||
// [status] "i" (C0_SR_CU1 | C0_SR_CU0 | C0_SR_FR)
|
||||
// );
|
||||
|
||||
OS_INFO->mem_size_6105 = OS_INFO->mem_size;
|
||||
|
||||
while (!(cpu_io_read(&SP->SR) & SP_SR_HALT));
|
||||
@ -119,8 +96,21 @@ void boot (boot_params_t *params) {
|
||||
cpu_io_write(&AI->MADDR, 0);
|
||||
cpu_io_write(&AI->LEN, 0);
|
||||
|
||||
io32_t *base = boot_get_device_base(params);
|
||||
while (cpu_io_read(&SP->SR) & SP_SR_DMA_BUSY);
|
||||
|
||||
uint32_t *ipl2_src = &ipl2;
|
||||
io32_t *ipl2_dst = SP_MEM->IMEM;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
cpu_io_write(&ipl2_dst[i], ipl2_src[i]);
|
||||
}
|
||||
|
||||
cpu_io_write(&PI->DOM[0].LAT, 0xFF);
|
||||
cpu_io_write(&PI->DOM[0].PWD, 0xFF);
|
||||
cpu_io_write(&PI->DOM[0].PGS, 0x0F);
|
||||
cpu_io_write(&PI->DOM[0].RLS, 0x03);
|
||||
|
||||
io32_t *base = boot_get_device_base(params);
|
||||
uint32_t pi_config = io_read((uint32_t) (base));
|
||||
|
||||
cpu_io_write(&PI->DOM[0].LAT, pi_config & 0xFF);
|
||||
@ -132,13 +122,6 @@ void boot (boot_params_t *params) {
|
||||
while (cpu_io_read(&DPC->SR) & DPC_SR_PIPE_BUSY);
|
||||
}
|
||||
|
||||
uint32_t *ipl2_src = &ipl2;
|
||||
io32_t *ipl2_dst = SP_MEM->IMEM;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
cpu_io_write(&ipl2_dst[i], ipl2_src[i]);
|
||||
}
|
||||
|
||||
io32_t *ipl3_src = base;
|
||||
io32_t *ipl3_dst = SP_MEM->DMEM;
|
||||
|
||||
@ -159,7 +142,7 @@ void boot (boot_params_t *params) {
|
||||
tv_type = (params->tv_type & 0x03);
|
||||
reset_type = (params->reset_type & 0x01);
|
||||
cic_seed = (params->cic_seed & 0xFF);
|
||||
version = 1;
|
||||
version = (params->tv_type == BOOT_TV_TYPE_PAL) ? 6 : 1;
|
||||
stack_pointer = (void *) UNCACHED(&SP_MEM->IMEM[1020]);
|
||||
|
||||
asm volatile (
|
||||
|
@ -20,6 +20,7 @@ typedef enum {
|
||||
BOOT_TV_TYPE_PAL = 0,
|
||||
BOOT_TV_TYPE_NTSC = 1,
|
||||
BOOT_TV_TYPE_MPAL = 2,
|
||||
BOOT_TV_TYPE_PASSTHROUGH = 3,
|
||||
} boot_tv_type_t;
|
||||
|
||||
|
||||
@ -28,7 +29,6 @@ typedef struct {
|
||||
boot_reset_type_t reset_type;
|
||||
boot_tv_type_t tv_type;
|
||||
uint8_t cic_seed;
|
||||
bool detect_tv_type;
|
||||
bool detect_cic_seed;
|
||||
} boot_params_t;
|
||||
|
||||
|
@ -87,7 +87,12 @@ static flashcart_error_t sc64_load_rom (char *rom_path) {
|
||||
return FLASHCART_ERROR_LOAD;
|
||||
}
|
||||
|
||||
size_t rom_size = ALIGN(f_size(&fil), FS_SECTOR_SIZE);
|
||||
// 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);
|
||||
|
||||
size_t rom_size = f_size(&fil);
|
||||
|
||||
if (rom_size > MiB(78)) {
|
||||
f_close(&fil);
|
||||
|
10
src/main.c
10
src/main.c
@ -5,8 +5,8 @@
|
||||
|
||||
#include "boot/boot.h"
|
||||
#include "flashcart/flashcart.h"
|
||||
#include "menu/menu.h"
|
||||
#include "menu/settings.h"
|
||||
#include "menu/menu_main.h"
|
||||
|
||||
|
||||
static void init (void) {
|
||||
@ -19,15 +19,12 @@ static void init (void) {
|
||||
assertf(error == FLASHCART_OK, "Unknown error while initializing flashcart");
|
||||
|
||||
controller_init();
|
||||
|
||||
display_close();
|
||||
display_init(RESOLUTION_640x240, DEPTH_16_BPP, 3, GAMMA_NONE, ANTIALIAS_RESAMPLE);
|
||||
display_init(RESOLUTION_640x240, DEPTH_16_BPP, 2, GAMMA_NONE, ANTIALIAS_RESAMPLE);
|
||||
graphics_set_color(0xFFFFFFFF, 0x00000000);
|
||||
graphics_set_default_font();
|
||||
}
|
||||
|
||||
static void deinit (void) {
|
||||
display_close();
|
||||
flashcart_deinit();
|
||||
rdpq_close();
|
||||
rspq_close();
|
||||
@ -38,7 +35,6 @@ static void deinit (void) {
|
||||
|
||||
|
||||
int main (void) {
|
||||
|
||||
init();
|
||||
|
||||
settings_t settings;
|
||||
@ -49,7 +45,7 @@ int main (void) {
|
||||
// menu_restore(&settings);
|
||||
// }
|
||||
|
||||
menu_main_init(&settings);
|
||||
menu_run(&settings);
|
||||
|
||||
deinit();
|
||||
|
||||
|
66
src/menu/actions.c
Normal file
66
src/menu/actions.c
Normal file
@ -0,0 +1,66 @@
|
||||
#include <libdragon.h>
|
||||
|
||||
#include "actions.h"
|
||||
|
||||
|
||||
#define ACTIONS_REPEAT_DELAY 20
|
||||
#define ACTIONS_REPEAT_RATE 2
|
||||
|
||||
|
||||
void actions_update (menu_t *menu) {
|
||||
menu->actions.go_up = false;
|
||||
menu->actions.go_down = false;
|
||||
menu->actions.fast = false;
|
||||
menu->actions.enter = false;
|
||||
menu->actions.back = false;
|
||||
menu->actions.info = false;
|
||||
menu->actions.settings = false;
|
||||
|
||||
controller_scan();
|
||||
struct controller_data down = get_keys_down();
|
||||
struct controller_data held = get_keys_held();
|
||||
|
||||
if (down.c[0].err != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (down.c[0].up || down.c[0].C_up) {
|
||||
menu->actions.go_up = true;
|
||||
menu->actions.held_counter = 0;
|
||||
if (down.c[0].C_up) {
|
||||
menu->actions.fast = true;
|
||||
}
|
||||
} else if (down.c[0].down || down.c[0].C_down) {
|
||||
menu->actions.go_down = true;
|
||||
menu->actions.held_counter = 0;
|
||||
if (down.c[0].C_down) {
|
||||
menu->actions.fast = true;
|
||||
}
|
||||
} else if (held.c[0].up || held.c[0].C_up) {
|
||||
menu->actions.held_counter += 1;
|
||||
if ((menu->actions.held_counter >= ACTIONS_REPEAT_DELAY) && (menu->actions.held_counter % ACTIONS_REPEAT_RATE)) {
|
||||
menu->actions.go_up = true;
|
||||
if (held.c[0].C_up) {
|
||||
menu->actions.fast = true;
|
||||
}
|
||||
}
|
||||
} else if (held.c[0].down || held.c[0].C_down) {
|
||||
menu->actions.held_counter += 1;
|
||||
if ((menu->actions.held_counter >= ACTIONS_REPEAT_DELAY) && (menu->actions.held_counter % ACTIONS_REPEAT_RATE)) {
|
||||
menu->actions.go_down = true;
|
||||
if (held.c[0].C_down) {
|
||||
menu->actions.fast = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (down.c[0].A) {
|
||||
menu->actions.enter = true;
|
||||
} else if (down.c[0].B) {
|
||||
menu->actions.back = true;
|
||||
} else if (down.c[0].Z) {
|
||||
menu->actions.info = true;
|
||||
} else if (down.c[0].start) {
|
||||
menu->actions.settings = true;
|
||||
}
|
||||
}
|
11
src/menu/actions.h
Normal file
11
src/menu/actions.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef ACTIONS_H__
|
||||
#define ACTIONS_H__
|
||||
|
||||
|
||||
#include "menu.h"
|
||||
|
||||
|
||||
void actions_update (menu_t *menu);
|
||||
|
||||
|
||||
#endif
|
104
src/menu/menu.c
Normal file
104
src/menu/menu.c
Normal file
@ -0,0 +1,104 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <libdragon.h>
|
||||
|
||||
#include "actions.h"
|
||||
#include "menu.h"
|
||||
#include "views.h"
|
||||
|
||||
|
||||
static menu_t *menu_init (settings_t *settings) {
|
||||
menu_t *menu = calloc(1, sizeof(menu_t));
|
||||
|
||||
menu->mode = MENU_MODE_INIT;
|
||||
menu->next_mode = MENU_MODE_BROWSER;
|
||||
|
||||
menu->browser.valid = false;
|
||||
menu->browser.directory = path_init(NULL); // TODO: load starting directory from settings
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
static void menu_deinit (menu_t *menu) {
|
||||
path_free(menu->browser.directory);
|
||||
free(menu);
|
||||
}
|
||||
|
||||
|
||||
void menu_run (settings_t *settings) {
|
||||
menu_t *menu = menu_init(settings);
|
||||
|
||||
bool running = true;
|
||||
|
||||
while (running) {
|
||||
surface_t *display = display_try_get();
|
||||
|
||||
if (display != NULL) {
|
||||
actions_update(menu);
|
||||
|
||||
switch (menu->mode) {
|
||||
case MENU_MODE_INIT:
|
||||
view_init_display(menu, display);
|
||||
break;
|
||||
|
||||
case MENU_MODE_BROWSER:
|
||||
view_browser_display(menu, display);
|
||||
break;
|
||||
|
||||
case MENU_MODE_FILE_INFO:
|
||||
view_file_info_display(menu, display);
|
||||
break;
|
||||
|
||||
case MENU_MODE_CREDITS:
|
||||
view_credits_display(menu, display);
|
||||
break;
|
||||
|
||||
case MENU_MODE_LOAD:
|
||||
view_load_display(menu, display);
|
||||
break;
|
||||
|
||||
case MENU_MODE_ERROR:
|
||||
view_error_display(menu, display);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
while (menu->mode != menu->next_mode) {
|
||||
menu->mode = menu->next_mode;
|
||||
|
||||
switch (menu->next_mode) {
|
||||
case MENU_MODE_BROWSER:
|
||||
view_browser_init(menu);
|
||||
break;
|
||||
|
||||
case MENU_MODE_FILE_INFO:
|
||||
view_file_info_init(menu);
|
||||
break;
|
||||
|
||||
case MENU_MODE_CREDITS:
|
||||
view_credits_init(menu);
|
||||
break;
|
||||
|
||||
case MENU_MODE_LOAD:
|
||||
view_load_init(menu);
|
||||
break;
|
||||
|
||||
case MENU_MODE_ERROR:
|
||||
view_error_init(menu);
|
||||
break;
|
||||
|
||||
case MENU_MODE_BOOT:
|
||||
running = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
menu_deinit(menu);
|
||||
}
|
62
src/menu/menu.h
Normal file
62
src/menu/menu.h
Normal file
@ -0,0 +1,62 @@
|
||||
#ifndef MENU_H__
|
||||
#define MENU_H__
|
||||
|
||||
|
||||
#include "path.h"
|
||||
#include "settings.h"
|
||||
|
||||
|
||||
#define BROWSER_LIST_SIZE 4096
|
||||
|
||||
|
||||
typedef enum {
|
||||
MENU_MODE_INIT,
|
||||
MENU_MODE_BROWSER,
|
||||
MENU_MODE_FILE_INFO,
|
||||
MENU_MODE_CREDITS,
|
||||
MENU_MODE_LOAD,
|
||||
MENU_MODE_ERROR,
|
||||
MENU_MODE_BOOT,
|
||||
} menu_mode_t;
|
||||
|
||||
typedef enum {
|
||||
ENTRY_TYPE_DIR,
|
||||
ENTRY_TYPE_ROM,
|
||||
ENTRY_TYPE_SAVE,
|
||||
ENTRY_TYPE_UNKNOWN,
|
||||
} entry_type_t;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
entry_type_t type;
|
||||
} entry_t;
|
||||
|
||||
typedef struct {
|
||||
menu_mode_t mode;
|
||||
menu_mode_t next_mode;
|
||||
|
||||
struct {
|
||||
bool go_up;
|
||||
bool go_down;
|
||||
bool fast;
|
||||
bool enter;
|
||||
bool back;
|
||||
bool info;
|
||||
bool settings;
|
||||
int held_counter;
|
||||
} actions;
|
||||
|
||||
struct {
|
||||
bool valid;
|
||||
path_t *directory;
|
||||
entry_t list[BROWSER_LIST_SIZE];
|
||||
int entries;
|
||||
int selected;
|
||||
} browser;
|
||||
} menu_t;
|
||||
|
||||
|
||||
void menu_run (settings_t *settings);
|
||||
|
||||
|
||||
#endif
|
@ -1,114 +0,0 @@
|
||||
#include <libdragon.h>
|
||||
#include "menu_res_setup.h"
|
||||
#include "menu_fileinfo.h"
|
||||
#include "../utils/str_utils.h"
|
||||
|
||||
|
||||
static char *get_file_type(FILINFO current_fileinfo) {
|
||||
// TODO: should be at least a switch statement!
|
||||
if (str_endswith(current_fileinfo.fname, ".z64") || str_endswith(current_fileinfo.fname, ".n64") || str_endswith(current_fileinfo.fname, ".v64") || str_endswith(current_fileinfo.fname, ".rom")) {
|
||||
// TODO: check the necessary bytes in the header to ensure!
|
||||
return "N64 ROM";
|
||||
}
|
||||
else if (str_endswith(current_fileinfo.fname, ".txt")) {
|
||||
return "Text File";
|
||||
}
|
||||
else if (str_endswith(current_fileinfo.fname, ".ini")) {
|
||||
return "INI File";
|
||||
}
|
||||
else if (str_endswith(current_fileinfo.fname, ".yml") || str_endswith(current_fileinfo.fname, ".yaml")) {
|
||||
return "YAML File";
|
||||
}
|
||||
else if (str_endswith(current_fileinfo.fname, ".toml")) {
|
||||
return "TOML File";
|
||||
}
|
||||
else if (str_endswith(current_fileinfo.fname, ".sav")) {
|
||||
return "Save File";
|
||||
}
|
||||
else if (str_endswith(current_fileinfo.fname, ".emu")) {
|
||||
return "Emulator File";
|
||||
}
|
||||
else {
|
||||
return "Unknown File";
|
||||
}
|
||||
}
|
||||
|
||||
void menu_fileinfo_draw_n64_rom_info(surface_t *disp) {
|
||||
// rom_header_t temp_header = file_read_rom_header(tmp_buffer);
|
||||
|
||||
// sprintf(buff, "ROM checksum: %llu\n", temp_header.checksum);
|
||||
// sprintf(buff, "ROM title: %s\n", temp_header.title);
|
||||
// sprintf(buff, "ROM media type: %c\n", temp_header.metadata.media_type);
|
||||
// sprintf(buff, "ROM unique id: %.2s\n", (char*)&(temp_header.metadata.unique_identifier));
|
||||
// sprintf(buff, "ROM dest market: %c\n", temp_header.metadata.destination_market);
|
||||
// sprintf(buff, "ROM version: %hhu\n", temp_header.version);
|
||||
}
|
||||
|
||||
void menu_fileinfo_draw_header(surface_t *disp) {
|
||||
|
||||
graphics_draw_text(disp, (disp->width / 2) - 64, vertical_start_position, "FILE INFORMATION"); // centre = numchars * font_horizontal_pixels / 2
|
||||
graphics_draw_line(disp, 0, 30, disp->width, 30, 0xff);
|
||||
|
||||
}
|
||||
|
||||
void menu_fileinfo_draw_footer(surface_t *disp) {
|
||||
|
||||
graphics_draw_line(disp, 0, disp->height - overscan_vertical_pixels - font_vertical_pixels, disp->width,disp->height - overscan_vertical_pixels - font_vertical_pixels, 0xff);
|
||||
graphics_draw_text(disp, (disp->width / 2) - 80,disp->height - overscan_vertical_pixels, "Press (B) to return!"); // centre = numchars * font_horizontal_pixels / 2
|
||||
|
||||
}
|
||||
|
||||
void menu_fileinfo(FILINFO current_fileinfo) {
|
||||
|
||||
char str_buffer[1024];
|
||||
|
||||
surface_t *disp = display_try_get();
|
||||
graphics_fill_screen(disp, 0x00);
|
||||
menu_fileinfo_draw_header(disp);
|
||||
|
||||
int16_t vertical_position = 40;
|
||||
|
||||
graphics_draw_text(disp, horizontal_start_position, vertical_position, "Name:");
|
||||
graphics_draw_text(disp, horizontal_indent, vertical_position += font_vertical_pixels, current_fileinfo.fname);
|
||||
vertical_position += (font_vertical_pixels * 2);
|
||||
|
||||
graphics_draw_text(disp, horizontal_start_position, vertical_position, "Size:");
|
||||
sprintf(str_buffer, "%d %s", (int)current_fileinfo.fsize, "Bytes");
|
||||
graphics_draw_text(disp, horizontal_indent, vertical_position += font_vertical_pixels, str_buffer);
|
||||
vertical_position += (font_vertical_pixels * 2);
|
||||
|
||||
graphics_draw_text(disp, horizontal_start_position, vertical_position, "Attributes:");
|
||||
sprintf(str_buffer, "%s%s%s%s%s\n",
|
||||
((current_fileinfo.fattrib & AM_DIR) ? "Directory" : "File"),
|
||||
((current_fileinfo.fattrib & AM_RDO) ? " | ReadOnly" : ""),
|
||||
((current_fileinfo.fattrib & AM_SYS) ? " | System" : ""),
|
||||
((current_fileinfo.fattrib & AM_ARC) ? " | Archive" : ""),
|
||||
((current_fileinfo.fattrib & AM_HID) ? " | Hidden" : "")
|
||||
);
|
||||
|
||||
graphics_draw_text(disp, horizontal_indent, vertical_position += font_vertical_pixels, str_buffer);
|
||||
vertical_position += (font_vertical_pixels * 2);
|
||||
|
||||
graphics_draw_text(disp, horizontal_start_position, vertical_position, "Modified Timestamp:");
|
||||
sprintf(str_buffer, "%u-%02u-%02u, %02u:%02u", (current_fileinfo.fdate >> 9) + 1980, current_fileinfo.fdate >> 5 & 15, current_fileinfo.fdate & 31, current_fileinfo.ftime >> 11, current_fileinfo.ftime >> 5 & 63);
|
||||
graphics_draw_text(disp, horizontal_indent, vertical_position += font_vertical_pixels, str_buffer);
|
||||
vertical_position += (font_vertical_pixels * 2);
|
||||
|
||||
graphics_draw_text(disp, horizontal_start_position, vertical_position, "Type:");
|
||||
graphics_draw_text(disp, horizontal_indent, vertical_position += font_vertical_pixels, get_file_type(current_fileinfo));
|
||||
|
||||
//menu_fileinfo_draw_n64_rom_info(disp);
|
||||
|
||||
menu_fileinfo_draw_footer(disp);
|
||||
|
||||
display_show(disp);
|
||||
|
||||
for (;;) {
|
||||
controller_scan();
|
||||
struct controller_data ckeys = get_keys_down();
|
||||
|
||||
if (ckeys.c[0].B) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
#ifndef MENU_FILEINFO_H__
|
||||
#define MENU_FILEINFO_H__
|
||||
|
||||
#include <fatfs/ff.h>
|
||||
|
||||
void menu_fileinfo(FILINFO current_fileinfo);
|
||||
|
||||
#endif
|
@ -1,55 +0,0 @@
|
||||
#include <libdragon.h>
|
||||
#include "menu_info.h"
|
||||
#include "menu_res_setup.h"
|
||||
|
||||
void menu_info_draw_header(surface_t *disp) {
|
||||
|
||||
graphics_draw_text(disp, (disp->width / 2) - 64, vertical_start_position, "MENU INFORMATION"); // centre = numchars * font_horizontal_pixels / 2
|
||||
graphics_draw_line(disp, 0, 30, disp->width, 30, 0xff);
|
||||
|
||||
}
|
||||
|
||||
void menu_info_draw_footer(surface_t *disp) {
|
||||
|
||||
graphics_draw_line(disp, 0, disp->height - overscan_vertical_pixels - font_vertical_pixels, disp->width,disp->height - overscan_vertical_pixels - font_vertical_pixels, 0xff);
|
||||
graphics_draw_text(disp, (disp->width / 2) - 80,disp->height - overscan_vertical_pixels, "Press (B) to return!"); // centre = numchars * font_horizontal_pixels / 2
|
||||
|
||||
}
|
||||
|
||||
void menu_info(void) {
|
||||
|
||||
surface_t *disp = display_try_get();
|
||||
graphics_fill_screen(disp, 0x00);
|
||||
menu_info_draw_header(disp);
|
||||
|
||||
int16_t vertical_position = 40;
|
||||
|
||||
graphics_draw_text(disp, horizontal_start_position, vertical_position, "Menu Version:");
|
||||
graphics_draw_text(disp, horizontal_indent,vertical_position += font_vertical_pixels, MENU_VERSION);
|
||||
vertical_position += (font_vertical_pixels * 2);
|
||||
graphics_draw_text(disp, horizontal_start_position, vertical_position, "Authors:");
|
||||
graphics_draw_text(disp, horizontal_indent, vertical_position += font_vertical_pixels, "JonesAlmighty / NetworkFusion");
|
||||
graphics_draw_text(disp, horizontal_indent, vertical_position += font_vertical_pixels, "korgeaux / Polprzewodnikowy");
|
||||
vertical_position += (font_vertical_pixels * 2);
|
||||
graphics_draw_text(disp, horizontal_start_position, vertical_position += font_vertical_pixels, "Github:");
|
||||
// graphics_draw_text(disp, horizontal_indent, vertical_position += font_vertical_pixels, "https://github.com/Polprzewodnikowy/SummerCart64");
|
||||
graphics_draw_text(disp, horizontal_indent, vertical_position += font_vertical_pixels, "https://github.com/Polprzewodnikowy/N64FlashcartMenu");
|
||||
graphics_draw_text(disp, horizontal_indent, vertical_position += font_vertical_pixels, "https://github.com/NetworkFusion/N64FlashcartMenu");
|
||||
vertical_position += (font_vertical_pixels * 2);
|
||||
graphics_draw_text(disp, horizontal_start_position, vertical_position, "OSS licenses used:");
|
||||
graphics_draw_text(disp, horizontal_indent,vertical_position += font_vertical_pixels, "UNLICENSE");
|
||||
graphics_draw_text(disp, horizontal_indent,vertical_position += font_vertical_pixels, "MIT");
|
||||
|
||||
menu_info_draw_footer(disp);
|
||||
|
||||
display_show(disp);
|
||||
|
||||
for (;;) {
|
||||
controller_scan();
|
||||
struct controller_data ckeys = get_keys_down();
|
||||
|
||||
if (ckeys.c[0].B) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
#ifndef MENU_INFO_H__
|
||||
#define MENU_INFO_H__
|
||||
|
||||
#define MENU_VERSION "V0.0.0.3"
|
||||
|
||||
void menu_info(void);
|
||||
|
||||
#endif
|
@ -1,265 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // TODO: does this work... will unlock qsort!
|
||||
#include <string.h>
|
||||
|
||||
#include <libdragon.h>
|
||||
#include <fatfs/ff.h>
|
||||
|
||||
#include "flashcart/flashcart.h"
|
||||
|
||||
#include "settings.h"
|
||||
#include "menu_main.h"
|
||||
#include "menu_info.h"
|
||||
#include "menu_fileinfo.h"
|
||||
#include "rom_database.h"
|
||||
#include "menu_res_setup.h"
|
||||
|
||||
#include "../utils/str_utils.h"
|
||||
|
||||
//const int max_files_on_screen = 16;
|
||||
static int scroll_menu_position = 1;
|
||||
static int items_in_dir = 1;
|
||||
|
||||
static FILINFO current_fileinfo;
|
||||
|
||||
|
||||
void load_n64_rom() {
|
||||
|
||||
console_init();
|
||||
console_clear();
|
||||
|
||||
char sd_path_buffer[1024];
|
||||
sprintf(sd_path_buffer, "sd:/%s", current_fileinfo.fname);
|
||||
|
||||
rom_header_t temp_header = file_read_rom_header(sd_path_buffer);
|
||||
|
||||
printf("Loading N64 ROM type...\n");
|
||||
printf("%s\n\n", current_fileinfo.fname);
|
||||
|
||||
printf("ROM title: %s\n\n", temp_header.title);
|
||||
|
||||
printf("ROM media type: %c\n", temp_header.metadata.media_type);
|
||||
printf("ROM unique id: %.2s\n", (char*)&(temp_header.metadata.unique_identifier));
|
||||
printf("uid as int: %d\n\n", temp_header.metadata.unique_identifier);
|
||||
|
||||
printf("ROM destination market: %c\n\n", temp_header.metadata.destination_market);
|
||||
|
||||
printf("ROM version: %hhu\n", temp_header.version);
|
||||
printf("ROM checksum: %llu\n\n", temp_header.checksum);
|
||||
|
||||
// FIXME: if the ROM header does not make sense, it is an invalid ROM.
|
||||
wait_ms(5000); // wait used for debugging. Can be removed later.
|
||||
|
||||
|
||||
uint8_t save_type = rom_db_match_save_type(temp_header);
|
||||
|
||||
printf("save type: %d\n", save_type);
|
||||
|
||||
wait_ms(5000); // wait used for debugging. Can be removed later.
|
||||
|
||||
sprintf(sd_path_buffer, "%s.%llu.sav", current_fileinfo.fname, temp_header.checksum);
|
||||
wait_ms(5000); // wait used for debugging. Can be removed later.
|
||||
|
||||
assertf(flashcart_load_save(sd_path_buffer, (flashcart_save_type_t)save_type, true) == FLASHCART_OK, "ROM load save error");
|
||||
|
||||
assertf(flashcart_load_rom(current_fileinfo.fname) == FLASHCART_OK, "ROM load error");
|
||||
|
||||
}
|
||||
|
||||
// FIXME: use newlib rather than fatfs to do this!
|
||||
FRESULT scan_file_path (surface_t *disp, char *path) {
|
||||
|
||||
FRESULT res;
|
||||
DIR dir;
|
||||
char sfno[1024];
|
||||
int counter = 0; //FIXME: we dont account for an empty dir or only one valid file!
|
||||
int16_t vertical_position = 56;
|
||||
char str_buffer[1036];
|
||||
|
||||
res = f_opendir(&dir, path);
|
||||
|
||||
if (res == FR_OK) {
|
||||
for (;;) {
|
||||
FILINFO file_info;
|
||||
|
||||
res = f_readdir(&dir, &file_info);
|
||||
|
||||
if ((res != FR_OK) || (file_info.fname[0] == 0)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (file_info.fattrib & AM_SYS) {
|
||||
continue; // we do not want to show system files ever...
|
||||
}
|
||||
|
||||
counter ++;
|
||||
|
||||
// TODO: convert these to icons...
|
||||
sprintf(sfno, "| %c%c%c | %10d | %s",
|
||||
((file_info.fattrib & AM_DIR) ? 'D' : '-'),
|
||||
((file_info.fattrib & AM_RDO) ? 'R' : '-'),
|
||||
((file_info.fattrib & AM_HID) ? 'H' : '-'),
|
||||
(int)file_info.fsize, file_info.fname);
|
||||
|
||||
if (scroll_menu_position == counter) {
|
||||
sprintf(str_buffer, "-> %s\n", sfno);
|
||||
graphics_draw_text(disp, horizontal_indent, vertical_position += font_vertical_pixels, str_buffer);
|
||||
current_fileinfo = file_info;
|
||||
}
|
||||
else {
|
||||
sprintf(str_buffer, " %s\n", sfno);
|
||||
graphics_draw_text(disp, horizontal_indent, vertical_position += font_vertical_pixels, str_buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
items_in_dir = counter;
|
||||
f_closedir(&dir);
|
||||
return res;
|
||||
}
|
||||
|
||||
void menu_main_draw_header(surface_t *disp) {
|
||||
|
||||
graphics_draw_text(disp, (disp->width / 2) - 36, vertical_start_position, "FILE MENU"); // centre = numchars * font_horizontal_pixels / 2
|
||||
graphics_draw_line(disp,0,30,disp->width,30, 0xff);
|
||||
}
|
||||
|
||||
void menu_main_draw_footer(char *dir_path, surface_t *disp) {
|
||||
|
||||
graphics_draw_line(disp,0,disp->height - overscan_vertical_pixels - font_vertical_pixels, disp->width,disp->height - overscan_vertical_pixels - font_vertical_pixels, 0xff);
|
||||
|
||||
char str_buffer[1024];
|
||||
sprintf(str_buffer, "Current Directory: SD:%s\nFile: %d of %d\n\n", dir_path, scroll_menu_position, items_in_dir);
|
||||
|
||||
graphics_draw_text(disp, (disp->width / 2) - 160,disp->height - overscan_vertical_pixels, str_buffer); // centre = numchars * font_horizontal_pixels / 2
|
||||
|
||||
}
|
||||
|
||||
void menu_main_refresh (char *dir_path) {
|
||||
|
||||
surface_t *disp = display_get();
|
||||
graphics_fill_screen(disp, 0);
|
||||
|
||||
menu_main_draw_header(disp);
|
||||
|
||||
int16_t vertical_position = 40;
|
||||
|
||||
char str_buffer[1024];
|
||||
sprintf(str_buffer, " | DRH | FILE SIZE | FILE NAME\n");
|
||||
graphics_draw_text(disp, horizontal_indent, vertical_position += font_vertical_pixels, str_buffer);
|
||||
sprintf(str_buffer, " |-----|------------|----------\n");
|
||||
graphics_draw_text(disp, horizontal_indent, vertical_position += font_vertical_pixels, str_buffer);
|
||||
|
||||
scan_file_path(disp, dir_path);
|
||||
|
||||
menu_main_draw_footer(dir_path, disp);
|
||||
|
||||
display_show(disp);
|
||||
|
||||
}
|
||||
|
||||
void menu_main_init (settings_t *settings) {
|
||||
|
||||
|
||||
|
||||
char *current_dir = settings->last_state.current_directory;
|
||||
char *last_dir = current_dir;
|
||||
struct controller_data joypad = get_keys_down();
|
||||
|
||||
if ((settings->last_state.auto_load_last_rom) && !(joypad.c[0].B)) { // FIXME: the B button on any controller!
|
||||
|
||||
console_init();
|
||||
console_clear();
|
||||
|
||||
printf("Loading last ROM: %s\n", settings->last_rom.rom_path);
|
||||
assertf(flashcart_load_rom(settings->last_rom.rom_path) == FLASHCART_OK, "ROM load error");
|
||||
|
||||
printf("Loading save: %s, type: %d, writeback: %d\n", settings->last_rom.save_path, settings->last_rom.save_type, settings->last_rom.save_writeback);
|
||||
assertf(flashcart_load_save(settings->last_rom.save_path, settings->last_rom.save_type, settings->last_rom.save_writeback) == FLASHCART_OK, "Save load error");
|
||||
}
|
||||
else {
|
||||
settings->last_state.auto_load_last_rom = false;
|
||||
menu_main_refresh(current_dir);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
controller_scan();
|
||||
joypad = get_keys_down();
|
||||
|
||||
if (joypad.c[0].up) {
|
||||
if (scroll_menu_position > 1 && scroll_menu_position <= items_in_dir) {
|
||||
scroll_menu_position --;
|
||||
}
|
||||
else {
|
||||
scroll_menu_position = items_in_dir;
|
||||
}
|
||||
menu_main_refresh(current_dir);
|
||||
}
|
||||
|
||||
if (joypad.c[0].down) {
|
||||
if (scroll_menu_position < items_in_dir) {
|
||||
scroll_menu_position ++;
|
||||
}
|
||||
else {
|
||||
scroll_menu_position = 1;
|
||||
}
|
||||
menu_main_refresh(current_dir);
|
||||
}
|
||||
|
||||
if (joypad.c[0].A) {
|
||||
// TODO: move this to a function and check that the ROM is valid by checking the header...
|
||||
if (str_endswith(current_fileinfo.fname, ".z64")) {
|
||||
|
||||
load_n64_rom();
|
||||
|
||||
break; //required!
|
||||
}
|
||||
else if (str_endswith(current_fileinfo.fname, ".n64") || str_endswith(current_fileinfo.fname, ".v64") || str_endswith(current_fileinfo.fname, ".rom")) {
|
||||
console_init();
|
||||
console_clear();
|
||||
|
||||
printf("ROM : %s\n", current_fileinfo.fname);
|
||||
printf("Not loading due to potential conversion issue.\n");
|
||||
wait_ms(10000); // wait used for debugging. Can be removed later.
|
||||
menu_main_refresh(current_dir);
|
||||
}
|
||||
else if (str_endswith(current_fileinfo.fname, ".zip")) {
|
||||
console_init();
|
||||
console_clear();
|
||||
|
||||
printf("ZIP : %s\n", current_fileinfo.fname);
|
||||
printf("Not loading due to potential conversion issue.\n");
|
||||
wait_ms(10000); // wait used for debugging. Can be removed later.
|
||||
menu_main_refresh(current_dir);
|
||||
}
|
||||
else {
|
||||
if (current_fileinfo.fattrib & AM_DIR) {
|
||||
// if this is a directory we need to transverse to it!
|
||||
items_in_dir = 1;
|
||||
scroll_menu_position = 1;
|
||||
last_dir = current_dir;
|
||||
current_dir = current_fileinfo.fname;
|
||||
menu_main_refresh(current_dir);
|
||||
}
|
||||
else {
|
||||
|
||||
menu_fileinfo(current_fileinfo);
|
||||
menu_main_refresh(current_dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (joypad.c[0].B) {
|
||||
menu_main_refresh(current_dir);
|
||||
current_dir = last_dir;
|
||||
}
|
||||
if (joypad.c[0].start) { // FIXME: the START button on any controller!
|
||||
menu_info();
|
||||
menu_main_refresh(current_dir);
|
||||
}
|
||||
if (joypad.c[0].Z) {
|
||||
menu_fileinfo(current_fileinfo);
|
||||
menu_main_refresh(current_dir);
|
||||
}
|
||||
}
|
||||
// TODO: write menu state to SD card
|
||||
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
#ifndef MENU_MAIN_H__
|
||||
#define MENU_MAIN_H__
|
||||
|
||||
void menu_main_init (settings_t *settings);
|
||||
|
||||
#endif
|
102
src/menu/path.c
Normal file
102
src/menu/path.c
Normal file
@ -0,0 +1,102 @@
|
||||
#include <libdragon.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "path.h"
|
||||
|
||||
|
||||
#define PATH_CAPACITY_INITIAL 255
|
||||
#define PATH_CAPACITY_ALIGNMENT 32
|
||||
|
||||
|
||||
static void path_resize (path_t *path, size_t min_length) {
|
||||
path->capacity = min_length > PATH_CAPACITY_INITIAL ? min_length : PATH_CAPACITY_INITIAL;
|
||||
size_t alignment = path->capacity % PATH_CAPACITY_ALIGNMENT;
|
||||
if (alignment != (PATH_CAPACITY_ALIGNMENT - 1)) {
|
||||
path->capacity += PATH_CAPACITY_ALIGNMENT - alignment;
|
||||
}
|
||||
path->buffer = realloc(path->buffer, (path->capacity + 1) * sizeof(char));
|
||||
}
|
||||
|
||||
path_t *path_init (char *string) {
|
||||
if (string == NULL) {
|
||||
string = "";
|
||||
}
|
||||
path_t *path = calloc(1, sizeof(path_t));
|
||||
path_resize(path, strlen(string));
|
||||
memset(path->buffer, 0, path->capacity + 1);
|
||||
strcpy(path->buffer, string);
|
||||
return path;
|
||||
}
|
||||
|
||||
void path_free (path_t *path) {
|
||||
free(path->buffer);
|
||||
free(path);
|
||||
}
|
||||
|
||||
path_t *path_clone (path_t *path) {
|
||||
return path_init(path->buffer);
|
||||
}
|
||||
|
||||
char *path_get (path_t *path) {
|
||||
return path->buffer;
|
||||
}
|
||||
|
||||
char *path_last_get (path_t *path) {
|
||||
char *last_slash = strrchr(path->buffer, '/');
|
||||
return (last_slash == NULL) ? path->buffer : (last_slash + 1);
|
||||
}
|
||||
|
||||
void path_append (path_t *path, char *string) {
|
||||
size_t buffer_length = strlen(path->buffer);
|
||||
size_t string_length = strlen(string);
|
||||
size_t new_path_length = buffer_length + string_length;
|
||||
if (new_path_length > path->capacity) {
|
||||
path_resize(path, new_path_length);
|
||||
}
|
||||
strcat(path->buffer, string);
|
||||
}
|
||||
|
||||
void path_concat (path_t *dst, path_t *src) {
|
||||
path_append(dst, src->buffer);
|
||||
}
|
||||
|
||||
void path_push (path_t *path, char *string) {
|
||||
if (path->buffer[strlen(path->buffer) - 1] != '/') {
|
||||
path_append(path, "/");
|
||||
}
|
||||
if (string[0] == '/') {
|
||||
string += 1;
|
||||
}
|
||||
path_append(path, string);
|
||||
}
|
||||
|
||||
void path_pop (path_t *path) {
|
||||
char *last_slash = strrchr(path->buffer, '/');
|
||||
if (last_slash != NULL) {
|
||||
*last_slash = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
char *path_ext_get (path_t *path) {
|
||||
char *buffer = path_last_get(path);
|
||||
char *last_dot = strrchr(buffer, '.');
|
||||
if (last_dot != NULL) {
|
||||
return last_dot + 1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void path_ext_remove (path_t *path) {
|
||||
char *buffer = path_last_get(path);
|
||||
char *last_dot = strrchr(buffer, '.');
|
||||
if (last_dot != NULL) {
|
||||
*last_dot = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
void path_ext_replace (path_t *path, char *ext) {
|
||||
path_ext_remove(path);
|
||||
path_append(path, ".");
|
||||
path_append(path, ext);
|
||||
}
|
25
src/menu/path.h
Normal file
25
src/menu/path.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef PAHT_H__
|
||||
#define PATH_H__
|
||||
|
||||
|
||||
typedef struct {
|
||||
char *buffer;
|
||||
size_t capacity;
|
||||
} path_t;
|
||||
|
||||
|
||||
path_t *path_init (char *string);
|
||||
void path_free (path_t *path);
|
||||
path_t *path_clone (path_t *string);
|
||||
char *path_get (path_t *path);
|
||||
char *path_last_get (path_t *path);
|
||||
void path_append (path_t *path, char *string);
|
||||
void path_concat (path_t *dst, path_t *str);
|
||||
void path_push (path_t *path, char *string);
|
||||
void path_pop (path_t *path);
|
||||
char *path_ext_get (path_t *path);
|
||||
void path_ext_remove (path_t *path);
|
||||
void path_ext_replace (path_t *path, char *ext);
|
||||
|
||||
|
||||
#endif
|
@ -1,7 +1,9 @@
|
||||
#include <libdragon.h>
|
||||
#include "rom_database.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
uint8_t extract_homebrew_setting(uint8_t setting, uint8_t bit_position) {
|
||||
return (setting & (1 << bit_position)) ? 1 : 0;
|
||||
@ -29,10 +31,14 @@ uint8_t extract_homebrew_save_type(uint8_t save_type) {
|
||||
}
|
||||
|
||||
rom_header_t file_read_rom_header(char *path) {
|
||||
FILE *fp = fopen(path, "rb");
|
||||
printf("loading path: %s\n", path);
|
||||
char *sd_path = calloc(4 + strlen(path) + 1, sizeof(char));
|
||||
sprintf(sd_path, "sd:/%s", path);
|
||||
|
||||
FILE *fp = fopen(sd_path, "rb");
|
||||
|
||||
debugf("loading path: %s\n", sd_path);
|
||||
if (!fp) {
|
||||
printf("Error loading rom file header\n");
|
||||
debugf("Error loading rom file header\n");
|
||||
}
|
||||
|
||||
rom_header_t *rom_header = malloc(sizeof(rom_header_t));
|
||||
@ -53,6 +59,8 @@ rom_header_t file_read_rom_header(char *path) {
|
||||
|
||||
fclose(fp);
|
||||
|
||||
free(sd_path);
|
||||
|
||||
return *rom_header;
|
||||
}
|
||||
|
||||
|
@ -162,7 +162,7 @@ void settings_load_default_state(settings_t *settings) {
|
||||
|
||||
settings->boot_params.device_type = BOOT_DEVICE_TYPE_ROM;
|
||||
settings->boot_params.reset_type = BOOT_RESET_TYPE_NMI;
|
||||
settings->boot_params.detect_tv_type = true;
|
||||
settings->boot_params.tv_type = BOOT_TV_TYPE_PASSTHROUGH;
|
||||
settings->boot_params.detect_cic_seed = true;
|
||||
|
||||
// Initialize other default settings...
|
||||
|
28
src/menu/views.h
Normal file
28
src/menu/views.h
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef VIEWS_H__
|
||||
#define VIEWS_H__
|
||||
|
||||
|
||||
#include <surface.h>
|
||||
|
||||
#include "menu.h"
|
||||
|
||||
|
||||
void view_init_display (menu_t *menu, surface_t *display);
|
||||
|
||||
void view_browser_init (menu_t *menu);
|
||||
void view_browser_display (menu_t *menu, surface_t *display);
|
||||
|
||||
void view_file_info_init (menu_t *menu);
|
||||
void view_file_info_display (menu_t *menu, surface_t *display);
|
||||
|
||||
void view_credits_init (menu_t *menu);
|
||||
void view_credits_display (menu_t *menu, surface_t *display);
|
||||
|
||||
void view_load_init (menu_t *menu);
|
||||
void view_load_display (menu_t *menu, surface_t *display);
|
||||
|
||||
void view_error_init (menu_t *menu);
|
||||
void view_error_display (menu_t *menu, surface_t *display);
|
||||
|
||||
|
||||
#endif
|
222
src/menu/views/browser.c
Normal file
222
src/menu/views/browser.c
Normal file
@ -0,0 +1,222 @@
|
||||
#include <fatfs/ff.h>
|
||||
#include <libdragon.h>
|
||||
#include <stdlib.h>
|
||||
#include "../menu.h"
|
||||
#include "../menu_res_setup.h"
|
||||
#include "../../utils/str_utils.h"
|
||||
|
||||
|
||||
#define BROWSER_LIST_ROWS 21
|
||||
|
||||
|
||||
static int compare_entry (const void *pa, const void *pb) {
|
||||
entry_t *a = (entry_t *) (pa);
|
||||
entry_t *b = (entry_t *) (pb);
|
||||
|
||||
if (a->type != b->type) {
|
||||
if (a->type == ENTRY_TYPE_DIR) {
|
||||
return -1;
|
||||
} else if (b->type == ENTRY_TYPE_DIR) {
|
||||
return 1;
|
||||
} else if (a->type == ENTRY_TYPE_ROM) {
|
||||
return -1;
|
||||
} else if (b->type == ENTRY_TYPE_ROM) {
|
||||
return 1;
|
||||
} else if (a->type == ENTRY_TYPE_SAVE) {
|
||||
return -1;
|
||||
} else if (b->type == ENTRY_TYPE_SAVE) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return strcasecmp((const char *) (a->name), (const char *) (b->name));
|
||||
}
|
||||
|
||||
static void load_directory (menu_t *menu) {
|
||||
DIR dir;
|
||||
FILINFO info;
|
||||
|
||||
for (int i = 0; i < menu->browser.entries; i++) {
|
||||
free(menu->browser.list[i].name);
|
||||
}
|
||||
menu->browser.entries = 0;
|
||||
menu->browser.selected = -1;
|
||||
|
||||
if (f_opendir(&dir, path_get(menu->browser.directory)) != FR_OK) {
|
||||
menu->next_mode = MENU_MODE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (f_readdir(&dir, &info) != FR_OK) {
|
||||
menu->next_mode = MENU_MODE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t length = strlen(info.fname);
|
||||
if (length == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (info.fattrib & AM_SYS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
entry_t *entry = &menu->browser.list[menu->browser.entries];
|
||||
|
||||
entry->name = malloc((length + 1) * sizeof(char));
|
||||
strcpy(entry->name, info.fname);
|
||||
|
||||
if (info.fattrib & AM_DIR) {
|
||||
entry->type = ENTRY_TYPE_DIR;
|
||||
// TODO: use something like `ext_is_n64_rom(info.fname)` instead of `str_endswith(info.fname, ".xxx")`
|
||||
} else if (str_endswith(info.fname, ".n64") || str_endswith(info.fname, ".z64") || str_endswith(info.fname, ".v64") || str_endswith(info.fname, ".N64")) {
|
||||
entry->type = ENTRY_TYPE_ROM;
|
||||
} else if (str_endswith(info.fname, ".sav")) {
|
||||
entry->type = ENTRY_TYPE_SAVE;
|
||||
} else {
|
||||
entry->type = ENTRY_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
menu->browser.entries += 1;
|
||||
if (menu->browser.entries == BROWSER_LIST_SIZE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
f_closedir(&dir);
|
||||
|
||||
if (menu->browser.entries > 0) {
|
||||
menu->browser.selected = 0;
|
||||
}
|
||||
|
||||
qsort(menu->browser.list, menu->browser.entries, sizeof(entry_t), compare_entry);
|
||||
}
|
||||
|
||||
void push_directory (menu_t *menu, char *directory) {
|
||||
path_push(menu->browser.directory, directory);
|
||||
load_directory(menu);
|
||||
}
|
||||
|
||||
void pop_directory (menu_t *menu) {
|
||||
path_t *current_directory = path_clone(menu->browser.directory);
|
||||
path_pop(menu->browser.directory);
|
||||
load_directory(menu);
|
||||
for (int i = 0; i < menu->browser.entries; i++) {
|
||||
if (strcmp(menu->browser.list[i].name, path_last_get(current_directory)) == 0) {
|
||||
menu->browser.selected = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
path_free(current_directory);
|
||||
}
|
||||
|
||||
|
||||
static void process (menu_t *menu) {
|
||||
int scroll_speed = menu->actions.fast ? BROWSER_LIST_ROWS : 1;
|
||||
|
||||
if (menu->browser.entries > 1) {
|
||||
if (menu->actions.go_up) {
|
||||
menu->browser.selected -= scroll_speed;
|
||||
if (menu->browser.selected < 0) {
|
||||
menu->browser.selected = 0;
|
||||
}
|
||||
} else if (menu->actions.go_down) {
|
||||
menu->browser.selected += scroll_speed;
|
||||
if (menu->browser.selected >= menu->browser.entries) {
|
||||
menu->browser.selected = menu->browser.entries - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (menu->actions.enter) {
|
||||
entry_t *entry = &menu->browser.list[menu->browser.selected];
|
||||
|
||||
switch (entry->type) {
|
||||
case ENTRY_TYPE_DIR:
|
||||
push_directory(menu, entry->name);
|
||||
break;
|
||||
|
||||
case ENTRY_TYPE_ROM:
|
||||
menu->next_mode = MENU_MODE_LOAD;
|
||||
break;
|
||||
|
||||
default:
|
||||
menu->next_mode = MENU_MODE_FILE_INFO;
|
||||
break;
|
||||
}
|
||||
} else if (menu->actions.back) {
|
||||
pop_directory(menu);
|
||||
} else if (menu->actions.info) {
|
||||
menu->next_mode = MENU_MODE_FILE_INFO;
|
||||
} else if (menu->actions.settings) {
|
||||
menu->next_mode = MENU_MODE_CREDITS;
|
||||
}
|
||||
}
|
||||
|
||||
static void draw (menu_t *menu, surface_t *d) {
|
||||
int x = 24;
|
||||
int y = 35;
|
||||
|
||||
int starting_position = 0;
|
||||
int entries_drawn = 0;
|
||||
|
||||
if (menu->browser.entries > BROWSER_LIST_ROWS && menu->browser.selected >= (BROWSER_LIST_ROWS / 2)) {
|
||||
starting_position = menu->browser.selected - (BROWSER_LIST_ROWS / 2);
|
||||
if (starting_position >= menu->browser.entries - BROWSER_LIST_ROWS) {
|
||||
starting_position = menu->browser.entries - BROWSER_LIST_ROWS;
|
||||
}
|
||||
}
|
||||
|
||||
graphics_fill_screen(d, graphics_make_color(0, 0, 0, 255));
|
||||
|
||||
graphics_draw_text(d, (d->width / 2) - 36, vertical_start_position, "FILE MENU");
|
||||
graphics_draw_line(d, 0, 30, d->width, 30, 0xff);
|
||||
|
||||
char str_buffer[1024];
|
||||
|
||||
for (int i = starting_position; i < menu->browser.entries; i++) {
|
||||
if (i == menu->browser.selected) {
|
||||
uint32_t color;
|
||||
switch (menu->browser.list[i].type) {
|
||||
case ENTRY_TYPE_ROM:
|
||||
color = graphics_make_color(0, 64, 0, 0);
|
||||
break;
|
||||
default:
|
||||
color = graphics_make_color(64, 64, 64, 0);
|
||||
break;
|
||||
}
|
||||
graphics_draw_box(d, x, y, (640 - x * 2), font_vertical_pixels, color);
|
||||
}
|
||||
snprintf(str_buffer, 1024, "%.74s", menu->browser.list[i].name);
|
||||
graphics_draw_text(d, x, y, str_buffer);
|
||||
|
||||
y += font_vertical_pixels;
|
||||
|
||||
entries_drawn += 1;
|
||||
if (entries_drawn == BROWSER_LIST_ROWS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
graphics_draw_line(d, 0, d->height - overscan_vertical_pixels - font_vertical_pixels, d->width, d->height - overscan_vertical_pixels - font_vertical_pixels, 0xff);
|
||||
|
||||
sprintf(str_buffer, "Current Directory: SD:%s\nFile: %d of %d\n\n", path_get(menu->browser.directory), menu->browser.selected + 1, menu->browser.entries);
|
||||
|
||||
graphics_draw_text(d, (d->width / 2) - 160, d->height - overscan_vertical_pixels, str_buffer);
|
||||
|
||||
display_show(d);
|
||||
}
|
||||
|
||||
|
||||
void view_browser_init (menu_t *menu) {
|
||||
if (!menu->browser.valid) {
|
||||
menu->browser.valid = true;
|
||||
load_directory(menu);
|
||||
}
|
||||
}
|
||||
|
||||
void view_browser_display (menu_t *menu, surface_t *display) {
|
||||
process(menu);
|
||||
draw(menu, display);
|
||||
}
|
53
src/menu/views/credits.c
Normal file
53
src/menu/views/credits.c
Normal file
@ -0,0 +1,53 @@
|
||||
#include <libdragon.h>
|
||||
#include "../menu.h"
|
||||
#include "../menu_res_setup.h"
|
||||
|
||||
|
||||
#define MENU_VERSION "V0.0.0.3"
|
||||
|
||||
|
||||
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) {
|
||||
graphics_fill_screen(d, 0x00);
|
||||
|
||||
graphics_draw_text(d, (d->width / 2) - 64, vertical_start_position, "MENU INFORMATION"); // centre = numchars * font_horizontal_pixels / 2
|
||||
graphics_draw_line(d, 0, 30, d->width, 30, 0xff);
|
||||
|
||||
int16_t vertical_position = 40;
|
||||
|
||||
graphics_draw_text(d, horizontal_start_position, vertical_position, "Menu Version:");
|
||||
graphics_draw_text(d, horizontal_indent,vertical_position += font_vertical_pixels, MENU_VERSION);
|
||||
vertical_position += (font_vertical_pixels * 2);
|
||||
graphics_draw_text(d, horizontal_start_position, vertical_position, "Authors:");
|
||||
graphics_draw_text(d, horizontal_indent, vertical_position += font_vertical_pixels, "JonesAlmighty / NetworkFusion");
|
||||
graphics_draw_text(d, horizontal_indent, vertical_position += font_vertical_pixels, "korgeaux / Polprzewodnikowy");
|
||||
vertical_position += (font_vertical_pixels * 2);
|
||||
graphics_draw_text(d, horizontal_start_position, vertical_position += font_vertical_pixels, "Github:");
|
||||
// graphics_draw_text(d, horizontal_indent, vertical_position += font_vertical_pixels, "https://github.com/Polprzewodnikowy/SummerCart64");
|
||||
graphics_draw_text(d, horizontal_indent, vertical_position += font_vertical_pixels, "https://github.com/Polprzewodnikowy/N64FlashcartMenu");
|
||||
graphics_draw_text(d, horizontal_indent, vertical_position += font_vertical_pixels, "https://github.com/NetworkFusion/N64FlashcartMenu");
|
||||
vertical_position += (font_vertical_pixels * 2);
|
||||
graphics_draw_text(d, horizontal_start_position, vertical_position, "OSS licenses used:");
|
||||
graphics_draw_text(d, horizontal_indent,vertical_position += font_vertical_pixels, "UNLICENSE");
|
||||
graphics_draw_text(d, horizontal_indent,vertical_position += font_vertical_pixels, "MIT");
|
||||
|
||||
graphics_draw_line(d, 0, d->height - overscan_vertical_pixels - font_vertical_pixels, d->width,d->height - overscan_vertical_pixels - font_vertical_pixels, 0xff);
|
||||
graphics_draw_text(d, (d->width / 2) - 80,d->height - overscan_vertical_pixels, "Press (B) to return!"); // centre = numchars * font_horizontal_pixels / 2
|
||||
|
||||
display_show(d);
|
||||
}
|
||||
|
||||
|
||||
void view_credits_init (menu_t *menu) {
|
||||
// Nothing to initialize (yet)
|
||||
}
|
||||
|
||||
void view_credits_display (menu_t *menu, surface_t *display) {
|
||||
process(menu);
|
||||
draw(menu, display);
|
||||
}
|
27
src/menu/views/error.c
Normal file
27
src/menu/views/error.c
Normal file
@ -0,0 +1,27 @@
|
||||
#include <libdragon.h>
|
||||
#include "../menu.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) {
|
||||
graphics_fill_screen(d, graphics_make_color(0, 0, 0, 255));
|
||||
|
||||
graphics_draw_text(d, 24, 36, "ERROR!");
|
||||
|
||||
display_show(d);
|
||||
}
|
||||
|
||||
|
||||
void view_error_init (menu_t *menu) {
|
||||
// Nothing to initialize (yet)
|
||||
}
|
||||
|
||||
void view_error_display (menu_t *menu, surface_t *display) {
|
||||
process(menu);
|
||||
draw(menu, display);
|
||||
}
|
106
src/menu/views/file_info.c
Normal file
106
src/menu/views/file_info.c
Normal file
@ -0,0 +1,106 @@
|
||||
#include <fatfs/ff.h>
|
||||
#include <libdragon.h>
|
||||
#include "../menu.h"
|
||||
#include "../menu_res_setup.h"
|
||||
#include "../../utils/str_utils.h"
|
||||
|
||||
|
||||
static FILINFO info;
|
||||
|
||||
|
||||
static char *get_file_type (void) {
|
||||
// TODO: should be at least a switch statement!
|
||||
if (str_endswith(info.fname, ".z64") || str_endswith(info.fname, ".n64") || str_endswith(info.fname, ".v64") || str_endswith(info.fname, ".rom")) {
|
||||
// TODO: check the necessary bytes in the header to ensure!
|
||||
return "N64 ROM";
|
||||
}
|
||||
else if (str_endswith(info.fname, ".txt")) {
|
||||
return "Text File";
|
||||
}
|
||||
else if (str_endswith(info.fname, ".ini")) {
|
||||
return "INI File";
|
||||
}
|
||||
else if (str_endswith(info.fname, ".yml") || str_endswith(info.fname, ".yaml")) {
|
||||
return "YAML File";
|
||||
}
|
||||
else if (str_endswith(info.fname, ".toml")) {
|
||||
return "TOML File";
|
||||
}
|
||||
else if (str_endswith(info.fname, ".sav")) {
|
||||
return "Save File";
|
||||
}
|
||||
else if (str_endswith(info.fname, ".emu")) {
|
||||
return "Emulator File";
|
||||
}
|
||||
else {
|
||||
return "Unknown File";
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
char str_buffer[1024];
|
||||
|
||||
graphics_fill_screen(d, 0x00);
|
||||
|
||||
graphics_draw_text(d, (d->width / 2) - 64, vertical_start_position, "FILE INFORMATION"); // centre = numchars * font_horizontal_pixels / 2
|
||||
graphics_draw_line(d, 0, 30, d->width, 30, 0xff);
|
||||
|
||||
int16_t vertical_position = 40;
|
||||
|
||||
graphics_draw_text(d, horizontal_start_position, vertical_position, "Name:");
|
||||
graphics_draw_text(d, horizontal_indent, vertical_position += font_vertical_pixels, info.fname);
|
||||
vertical_position += (font_vertical_pixels * 2);
|
||||
|
||||
graphics_draw_text(d, horizontal_start_position, vertical_position, "Size:");
|
||||
sprintf(str_buffer, "%d %s", (int)info.fsize, "Bytes");
|
||||
graphics_draw_text(d, horizontal_indent, vertical_position += font_vertical_pixels, str_buffer);
|
||||
vertical_position += (font_vertical_pixels * 2);
|
||||
|
||||
graphics_draw_text(d, horizontal_start_position, vertical_position, "Attributes:");
|
||||
sprintf(str_buffer, "%s%s%s%s%s\n",
|
||||
((info.fattrib & AM_DIR) ? "Directory" : "File"),
|
||||
((info.fattrib & AM_RDO) ? " | ReadOnly" : ""),
|
||||
((info.fattrib & AM_SYS) ? " | System" : ""),
|
||||
((info.fattrib & AM_ARC) ? " | Archive" : ""),
|
||||
((info.fattrib & AM_HID) ? " | Hidden" : "")
|
||||
);
|
||||
|
||||
graphics_draw_text(d, horizontal_indent, vertical_position += font_vertical_pixels, str_buffer);
|
||||
vertical_position += (font_vertical_pixels * 2);
|
||||
|
||||
graphics_draw_text(d, horizontal_start_position, vertical_position, "Modified Timestamp:");
|
||||
sprintf(str_buffer, "%u-%02u-%02u, %02u:%02u", (info.fdate >> 9) + 1980, info.fdate >> 5 & 15, info.fdate & 31, info.ftime >> 11, info.ftime >> 5 & 63);
|
||||
graphics_draw_text(d, horizontal_indent, vertical_position += font_vertical_pixels, str_buffer);
|
||||
vertical_position += (font_vertical_pixels * 2);
|
||||
|
||||
graphics_draw_text(d, horizontal_start_position, vertical_position, "Type:");
|
||||
graphics_draw_text(d, horizontal_indent, vertical_position += font_vertical_pixels, get_file_type());
|
||||
|
||||
//menu_fileinfo_draw_n64_rom_info(d);
|
||||
|
||||
graphics_draw_line(d, 0, d->height - overscan_vertical_pixels - font_vertical_pixels, d->width,d->height - overscan_vertical_pixels - font_vertical_pixels, 0xff);
|
||||
graphics_draw_text(d, (d->width / 2) - 80,d->height - overscan_vertical_pixels, "Press (B) to return!"); // centre = numchars * font_horizontal_pixels / 2
|
||||
|
||||
display_show(d);
|
||||
}
|
||||
|
||||
|
||||
void view_file_info_init (menu_t *menu) {
|
||||
path_t *file = path_clone(menu->browser.directory);
|
||||
path_push(file, menu->browser.list[menu->browser.selected].name);
|
||||
if (f_stat(path_get(file), &info) != FR_OK) {
|
||||
menu->next_mode = MENU_MODE_ERROR;
|
||||
}
|
||||
path_free(file);
|
||||
}
|
||||
|
||||
void view_file_info_display (menu_t *menu, surface_t *display) {
|
||||
process(menu);
|
||||
draw(menu, display);
|
||||
}
|
13
src/menu/views/init.c
Normal file
13
src/menu/views/init.c
Normal file
@ -0,0 +1,13 @@
|
||||
#include <libdragon.h>
|
||||
#include "../menu.h"
|
||||
|
||||
|
||||
static void draw (menu_t *menu, surface_t *d) {
|
||||
graphics_fill_screen(d, graphics_make_color(0, 0, 0, 255));
|
||||
|
||||
display_show(d);
|
||||
}
|
||||
|
||||
void view_init_display (menu_t *menu, surface_t *display) {
|
||||
draw(menu, display);
|
||||
}
|
53
src/menu/views/load.c
Normal file
53
src/menu/views/load.c
Normal file
@ -0,0 +1,53 @@
|
||||
#include <libdragon.h>
|
||||
#include "../menu.h"
|
||||
#include "../rom_database.h"
|
||||
#include "../../flashcart/flashcart.h"
|
||||
|
||||
|
||||
static void draw (menu_t *menu, surface_t *d) {
|
||||
int x = 24;
|
||||
int y = 36;
|
||||
|
||||
graphics_fill_screen(d, graphics_make_color(0, 0, 0, 255));
|
||||
|
||||
graphics_draw_text(d, x, y, "booting...");
|
||||
|
||||
display_show(d);
|
||||
}
|
||||
|
||||
static void load (menu_t *menu) {
|
||||
menu->next_mode = MENU_MODE_BOOT;
|
||||
|
||||
path_t *path = path_clone(menu->browser.directory);
|
||||
|
||||
path_push(path, menu->browser.list[menu->browser.selected].name);
|
||||
|
||||
rom_header_t temp_header = file_read_rom_header(path_get(path));
|
||||
|
||||
uint8_t save_type = rom_db_match_save_type(temp_header);
|
||||
|
||||
if (flashcart_load_rom(path_get(path)) != FLASHCART_OK) {
|
||||
menu->next_mode = MENU_MODE_ERROR;
|
||||
path_free(path);
|
||||
return;
|
||||
}
|
||||
|
||||
path_ext_replace(path, "sav");
|
||||
if (flashcart_load_save(path_get(path), (flashcart_save_type_t) (save_type), true) != FLASHCART_OK) {
|
||||
menu->next_mode = MENU_MODE_ERROR;
|
||||
path_free(path);
|
||||
return;
|
||||
}
|
||||
|
||||
path_free(path);
|
||||
}
|
||||
|
||||
|
||||
void view_load_init (menu_t *menu) {
|
||||
// Nothing to initialize (yet)
|
||||
}
|
||||
|
||||
void view_load_display (menu_t *menu, surface_t *display) {
|
||||
draw(menu, display);
|
||||
load(menu);
|
||||
}
|
Loading…
Reference in New Issue
Block a user