mirror of
https://github.com/Polprzewodnikowy/N64FlashcartMenu.git
synced 2024-11-21 18:19:19 +01:00
Added USB debug commands handling + file_get_sectors refactor
This commit is contained in:
parent
c44ff7f752
commit
f063f05fe6
1
Makefile
1
Makefile
@ -42,6 +42,7 @@ SRCS = \
|
|||||||
menu/rom_database.c \
|
menu/rom_database.c \
|
||||||
menu/settings.c \
|
menu/settings.c \
|
||||||
menu/sound.c \
|
menu/sound.c \
|
||||||
|
menu/usb_comm.c \
|
||||||
menu/views/browser.c \
|
menu/views/browser.c \
|
||||||
menu/views/credits.c \
|
menu/views/credits.c \
|
||||||
menu/views/error.c \
|
menu/views/error.c \
|
||||||
|
@ -21,8 +21,14 @@ echo !!! Now toggle power to the N64 !!!
|
|||||||
echo:
|
echo:
|
||||||
echo:
|
echo:
|
||||||
|
|
||||||
if not "%1" == "/d" goto :exit
|
if not "%1" == "/d" goto :not_d
|
||||||
|
|
||||||
%~dp0tools\sc64\sc64deployer debug --no-writeback
|
%~dp0tools\sc64\sc64deployer debug --no-writeback
|
||||||
|
|
||||||
:exit
|
:not_d
|
||||||
|
|
||||||
|
if not "%1" == "/du" goto :not_du
|
||||||
|
|
||||||
|
%~dp0tools\sc64\sc64deployer debug --no-writeback --init "send-file /sc64menu.n64 @output/sc64menu.n64@;reboot"
|
||||||
|
|
||||||
|
:not_du
|
||||||
|
@ -4,28 +4,12 @@ set -e
|
|||||||
|
|
||||||
REMOTE="--remote ${REMOTE:-host.docker.internal:9064}"
|
REMOTE="--remote ${REMOTE:-host.docker.internal:9064}"
|
||||||
|
|
||||||
## FIXME: this does not work!
|
|
||||||
# Make sure we are connected
|
|
||||||
#echo Detecting SC64...
|
|
||||||
#sc64deployer $REMOTE list
|
|
||||||
|
|
||||||
# Get the information
|
|
||||||
echo SC64 info...:
|
|
||||||
sc64deployer $REMOTE info
|
|
||||||
echo
|
|
||||||
echo
|
|
||||||
|
|
||||||
# Load the ROM
|
|
||||||
echo Loading ROM...:
|
|
||||||
sc64deployer $REMOTE upload ./output/N64FlashcartMenu.n64
|
sc64deployer $REMOTE upload ./output/N64FlashcartMenu.n64
|
||||||
|
|
||||||
echo
|
|
||||||
echo
|
|
||||||
# Toggle the power of the N64 to boot the ROM.
|
|
||||||
echo !!! Now toggle power to the N64 !!!
|
|
||||||
echo
|
|
||||||
echo
|
|
||||||
|
|
||||||
if [ "$1" = "-d" ]; then
|
if [ "$1" = "-d" ]; then
|
||||||
sc64deployer $REMOTE debug --no-writeback
|
sc64deployer $REMOTE debug --no-writeback
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "$1" = "-du" ]; then
|
||||||
|
sc64deployer $REMOTE debug --no-writeback --init "send-file /sc64menu.n64 @output/sc64menu.n64@;reboot"
|
||||||
|
fi
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
#include "sc64/sc64.h"
|
#include "sc64/sc64.h"
|
||||||
|
|
||||||
|
|
||||||
#define WRITEBACK_MAX_SECTORS (256)
|
#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] = {
|
||||||
@ -24,6 +24,9 @@ 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_error_t dummy_init (void) {
|
static flashcart_error_t dummy_init (void) {
|
||||||
return FLASHCART_OK;
|
return FLASHCART_OK;
|
||||||
}
|
}
|
||||||
@ -95,6 +98,7 @@ flashcart_error_t flashcart_deinit (void) {
|
|||||||
if (flashcart->deinit) {
|
if (flashcart->deinit) {
|
||||||
return flashcart->deinit();
|
return flashcart->deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
return FLASHCART_OK;
|
return FLASHCART_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,9 +128,21 @@ flashcart_error_t flashcart_load_file (char *file_path, uint32_t rom_offset, uin
|
|||||||
return flashcart->load_file(file_path, rom_offset, file_offset);
|
return flashcart->load_file(file_path, rom_offset, file_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void save_writeback_sectors_callback (uint32_t sector_count, uint32_t file_sector, uint32_t cluster_sector, uint32_t cluster_size) {
|
||||||
|
for (uint32_t i = 0; i < cluster_size; i++) {
|
||||||
|
uint32_t offset = file_sector + i;
|
||||||
|
uint32_t sector = cluster_sector + i;
|
||||||
|
|
||||||
|
if ((offset > SAVE_WRITEBACK_MAX_SECTORS) || (offset > sector_count)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
save_writeback_sectors[offset] = sector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
flashcart_error_t flashcart_load_save (char *save_path, flashcart_save_type_t save_type) {
|
flashcart_error_t flashcart_load_save (char *save_path, flashcart_save_type_t save_type) {
|
||||||
flashcart_error_t error;
|
flashcart_error_t error;
|
||||||
uint32_t sectors[WRITEBACK_MAX_SECTORS] __attribute__((aligned(8)));
|
|
||||||
|
|
||||||
if (save_type >= __FLASHCART_SAVE_TYPE_END) {
|
if (save_type >= __FLASHCART_SAVE_TYPE_END) {
|
||||||
return FLASHCART_ERROR_ARGS;
|
return FLASHCART_ERROR_ARGS;
|
||||||
@ -158,10 +174,13 @@ flashcart_error_t flashcart_load_save (char *save_path, flashcart_save_type_t sa
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (flashcart->set_save_writeback) {
|
if (flashcart->set_save_writeback) {
|
||||||
if (file_get_sectors(save_path, sectors, WRITEBACK_MAX_SECTORS)) {
|
for (int i = 0; i < SAVE_WRITEBACK_MAX_SECTORS; i++) {
|
||||||
|
save_writeback_sectors[i] = 0;
|
||||||
|
}
|
||||||
|
if (file_get_sectors(save_path, save_writeback_sectors_callback)) {
|
||||||
return FLASHCART_ERROR_LOAD;
|
return FLASHCART_ERROR_LOAD;
|
||||||
}
|
}
|
||||||
if ((error = flashcart->set_save_writeback(sectors)) != FLASHCART_OK) {
|
if ((error = flashcart->set_save_writeback(save_writeback_sectors)) != FLASHCART_OK) {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ static flashcart_error_t sc64_init (void) {
|
|||||||
uint32_t value;
|
uint32_t value;
|
||||||
} default_config[] = {
|
} default_config[] = {
|
||||||
{ CFG_ID_BOOTLOADER_SWITCH, false },
|
{ CFG_ID_BOOTLOADER_SWITCH, false },
|
||||||
{ CFG_ID_ROM_WRITE_ENABLE, false },
|
{ CFG_ID_ROM_WRITE_ENABLE, true },
|
||||||
{ CFG_ID_ROM_SHADOW_ENABLE, false },
|
{ CFG_ID_ROM_SHADOW_ENABLE, false },
|
||||||
{ CFG_ID_DD_MODE, DD_MODE_DISABLED },
|
{ CFG_ID_DD_MODE, DD_MODE_DISABLED },
|
||||||
{ CFG_ID_ISV_ADDRESS, 0x00000000 },
|
{ CFG_ID_ISV_ADDRESS, 0x00000000 },
|
||||||
@ -110,6 +110,8 @@ static flashcart_error_t sc64_init (void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static flashcart_error_t sc64_deinit (void) {
|
static flashcart_error_t sc64_deinit (void) {
|
||||||
|
sc64_ll_set_config(CFG_ID_ROM_WRITE_ENABLE, false);
|
||||||
|
|
||||||
sc64_ll_lock();
|
sc64_ll_lock();
|
||||||
|
|
||||||
return FLASHCART_OK;
|
return FLASHCART_OK;
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "png_decoder.h"
|
#include "png_decoder.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "sound.h"
|
#include "sound.h"
|
||||||
|
#include "usb_comm.h"
|
||||||
#include "utils/fs.h"
|
#include "utils/fs.h"
|
||||||
#include "views/views.h"
|
#include "views/views.h"
|
||||||
|
|
||||||
@ -180,6 +181,8 @@ void menu_run (boot_params_t *boot_params) {
|
|||||||
sound_poll();
|
sound_poll();
|
||||||
|
|
||||||
png_decoder_poll();
|
png_decoder_poll();
|
||||||
|
|
||||||
|
usb_comm_poll(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
menu_deinit(menu);
|
menu_deinit(menu);
|
||||||
|
163
src/menu/usb_comm.c
Normal file
163
src/menu/usb_comm.c
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
// NOTE: This code doesn't implement EverDrive-64 USB protocol.
|
||||||
|
// Main use of these functions is to aid menu development
|
||||||
|
// (for example replace files on the SD card or reboot menu).
|
||||||
|
|
||||||
|
#include <fatfs/ff.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <usb.h>
|
||||||
|
|
||||||
|
#include "usb_comm.h"
|
||||||
|
#include "utils/utils.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define MAX_FILE_SIZE MiB(4)
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *id;
|
||||||
|
void (*op) (menu_t *menu);
|
||||||
|
} usb_comm_command_t;
|
||||||
|
|
||||||
|
|
||||||
|
static int usb_comm_get_char (void) {
|
||||||
|
char c;
|
||||||
|
|
||||||
|
if (USBHEADER_GETSIZE(usb_poll()) <= 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
usb_read(&c, sizeof(c));
|
||||||
|
|
||||||
|
return (int) (c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool usb_comm_read_string (char *string, int length, char end) {
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
int c = usb_comm_get_char();
|
||||||
|
|
||||||
|
if (c < 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string[i] = (char) (c);
|
||||||
|
|
||||||
|
if (c == '\0' || c == end) {
|
||||||
|
string[i] = '\0';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == (length - 1)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usb_comm_send_error (const char *message) {
|
||||||
|
usb_purge();
|
||||||
|
usb_write(DATATYPE_TEXT, message, strlen(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void command_reboot (menu_t *menu) {
|
||||||
|
menu->next_mode = MENU_MODE_BOOT;
|
||||||
|
|
||||||
|
menu->boot_params->device_type = BOOT_DEVICE_TYPE_ROM;
|
||||||
|
menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH;
|
||||||
|
menu->boot_params->detect_cic_seed = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void command_send_file (menu_t *menu) {
|
||||||
|
char path[256];
|
||||||
|
char length[8];
|
||||||
|
|
||||||
|
FIL f;
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usb_comm_get_char() != '@') {
|
||||||
|
return usb_comm_send_error("Invalid argument\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usb_comm_read_string(length, sizeof(length), '@')) {
|
||||||
|
return usb_comm_send_error("Invalid file length argument\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f_open(&f, path, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK) {
|
||||||
|
return usb_comm_send_error("Couldn't create file\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
remaining = atoi(length);
|
||||||
|
|
||||||
|
if (remaining > MAX_FILE_SIZE) {
|
||||||
|
return usb_comm_send_error("File size too big\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (remaining > 0) {
|
||||||
|
int block_size = MIN(remaining, sizeof(data));
|
||||||
|
usb_read(data, block_size);
|
||||||
|
if (f_write(&f, data, block_size, &bytes_written) != FR_OK) {
|
||||||
|
f_close(&f);
|
||||||
|
return usb_comm_send_error("Couldn't write data to the file\n");
|
||||||
|
}
|
||||||
|
if (bytes_written != block_size) {
|
||||||
|
f_close(&f);
|
||||||
|
return usb_comm_send_error("Couldn't write all required data to the file\n");
|
||||||
|
}
|
||||||
|
remaining -= block_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f_close(&f) != FR_OK) {
|
||||||
|
return usb_comm_send_error("Couldn't flush data to the file\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usb_comm_get_char() != '\0') {
|
||||||
|
return usb_comm_send_error("Invalid token at the end of data stream\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static usb_comm_command_t commands[] = {
|
||||||
|
{ .id = "reboot", .op = command_reboot },
|
||||||
|
{ .id = "send-file", .op = command_send_file },
|
||||||
|
{ .id = NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void usb_comm_poll (menu_t *menu) {
|
||||||
|
uint32_t header = usb_poll();
|
||||||
|
|
||||||
|
if (USBHEADER_GETTYPE(header) != DATATYPE_TEXT) {
|
||||||
|
usb_purge();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (USBHEADER_GETSIZE(header) > 0) {
|
||||||
|
char cmd_id[32];
|
||||||
|
|
||||||
|
if (usb_comm_read_string(cmd_id, sizeof(cmd_id), ' ')) {
|
||||||
|
usb_comm_send_error("Command id too long\n");
|
||||||
|
} else {
|
||||||
|
usb_comm_command_t *cmd = commands;
|
||||||
|
|
||||||
|
while (cmd->id != NULL) {
|
||||||
|
if (strcmp(cmd->id, cmd_id) == 0) {
|
||||||
|
cmd->op(menu);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cmd++;
|
||||||
|
}
|
||||||
|
|
||||||
|
usb_purge();
|
||||||
|
|
||||||
|
if (cmd->id == NULL) {
|
||||||
|
usb_comm_send_error("Unknown command\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
src/menu/usb_comm.h
Normal file
21
src/menu/usb_comm.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @file usb_comm.h
|
||||||
|
* @brief USB communication subsystem
|
||||||
|
* @ingroup menu
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef USB_COMM_H__
|
||||||
|
#define USB_COMM_H__
|
||||||
|
|
||||||
|
|
||||||
|
#include "menu_state.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
void usb_comm_poll (menu_t *menu);
|
||||||
|
#else
|
||||||
|
#define usb_comm_poll(menu)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
@ -13,7 +13,7 @@ char *strip_sd_prefix (char *path) {
|
|||||||
|
|
||||||
char *found = strstr(path, prefix);
|
char *found = strstr(path, prefix);
|
||||||
if (found) {
|
if (found) {
|
||||||
return found + strlen(prefix);
|
return found + strlen(prefix) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
@ -106,13 +106,13 @@ bool file_fill (char *path, uint8_t value) {
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool file_get_sectors (char *path, uint32_t *sectors, size_t entries) {
|
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;
|
FATFS *fs;
|
||||||
FIL fil;
|
FIL fil;
|
||||||
bool error = false;
|
bool error = false;
|
||||||
|
|
||||||
for (int i = 0; i < entries; i++) {
|
if (!callback) {
|
||||||
sectors[i] = 0;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f_open(&fil, strip_sd_prefix(path), FA_READ) != FR_OK) {
|
if (f_open(&fil, strip_sd_prefix(path), FA_READ) != FR_OK) {
|
||||||
@ -121,13 +121,11 @@ bool file_get_sectors (char *path, uint32_t *sectors, size_t entries) {
|
|||||||
|
|
||||||
fs = fil.obj.fs;
|
fs = fil.obj.fs;
|
||||||
|
|
||||||
uint32_t file_sector_entries = (ALIGN(f_size(&fil), FS_SECTOR_SIZE) / FS_SECTOR_SIZE);
|
uint32_t sector_count = (ALIGN(f_size(&fil), FS_SECTOR_SIZE) / FS_SECTOR_SIZE);
|
||||||
file_sector_entries = (file_sector_entries > entries) ? entries : file_sector_entries;
|
|
||||||
|
|
||||||
uint32_t cluster_sector = 0;
|
uint32_t cluster_sector = 0;
|
||||||
|
|
||||||
for (int file_sector = 0; file_sector < file_sector_entries; file_sector++) {
|
for (int file_sector = 0; file_sector < sector_count; file_sector += fs->csize) {
|
||||||
if ((file_sector % fs->csize) == 0) {
|
|
||||||
if ((f_lseek(&fil, (file_sector * FS_SECTOR_SIZE) + (FS_SECTOR_SIZE / 2))) != FR_OK) {
|
if ((f_lseek(&fil, (file_sector * FS_SECTOR_SIZE) + (FS_SECTOR_SIZE / 2))) != FR_OK) {
|
||||||
error = true;
|
error = true;
|
||||||
break;
|
break;
|
||||||
@ -138,8 +136,7 @@ bool file_get_sectors (char *path, uint32_t *sectors, size_t entries) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cluster_sector = (fs->database + ((LBA_t) (fs->csize) * (cluster - 2)));
|
cluster_sector = (fs->database + ((LBA_t) (fs->csize) * (cluster - 2)));
|
||||||
}
|
callback(sector_count, file_sector, cluster_sector, fs->csize);
|
||||||
*sectors++ = (cluster_sector + (file_sector % fs->csize));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f_close(&fil) != FR_OK) {
|
if (f_close(&fil) != FR_OK) {
|
||||||
|
@ -17,7 +17,7 @@ size_t file_get_size (char *path);
|
|||||||
bool file_delete (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, uint32_t *sectors, size_t entries);
|
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);
|
||||||
|
Loading…
Reference in New Issue
Block a user