mirror of
https://github.com/Polprzewodnikowy/N64FlashcartMenu.git
synced 2024-12-23 08:51:47 +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/settings.c \
|
||||
menu/sound.c \
|
||||
menu/usb_comm.c \
|
||||
menu/views/browser.c \
|
||||
menu/views/credits.c \
|
||||
menu/views/error.c \
|
||||
|
@ -21,8 +21,14 @@ echo !!! Now toggle power to the N64 !!!
|
||||
echo:
|
||||
echo:
|
||||
|
||||
if not "%1" == "/d" goto :exit
|
||||
if not "%1" == "/d" goto :not_d
|
||||
|
||||
%~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}"
|
||||
|
||||
## 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
|
||||
|
||||
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
|
||||
sc64deployer $REMOTE debug --no-writeback
|
||||
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"
|
||||
|
||||
|
||||
#define WRITEBACK_MAX_SECTORS (256)
|
||||
#define SAVE_WRITEBACK_MAX_SECTORS (256)
|
||||
|
||||
|
||||
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),
|
||||
};
|
||||
|
||||
static uint32_t save_writeback_sectors[SAVE_WRITEBACK_MAX_SECTORS] __attribute__((aligned(8)));
|
||||
|
||||
|
||||
static flashcart_error_t dummy_init (void) {
|
||||
return FLASHCART_OK;
|
||||
}
|
||||
@ -95,6 +98,7 @@ flashcart_error_t flashcart_deinit (void) {
|
||||
if (flashcart->deinit) {
|
||||
return flashcart->deinit();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 error;
|
||||
uint32_t sectors[WRITEBACK_MAX_SECTORS] __attribute__((aligned(8)));
|
||||
|
||||
if (save_type >= __FLASHCART_SAVE_TYPE_END) {
|
||||
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 (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;
|
||||
}
|
||||
if ((error = flashcart->set_save_writeback(sectors)) != FLASHCART_OK) {
|
||||
if ((error = flashcart->set_save_writeback(save_writeback_sectors)) != FLASHCART_OK) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ static flashcart_error_t sc64_init (void) {
|
||||
uint32_t value;
|
||||
} default_config[] = {
|
||||
{ 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_DD_MODE, DD_MODE_DISABLED },
|
||||
{ CFG_ID_ISV_ADDRESS, 0x00000000 },
|
||||
@ -110,6 +110,8 @@ static flashcart_error_t sc64_init (void) {
|
||||
}
|
||||
|
||||
static flashcart_error_t sc64_deinit (void) {
|
||||
sc64_ll_set_config(CFG_ID_ROM_WRITE_ENABLE, false);
|
||||
|
||||
sc64_ll_lock();
|
||||
|
||||
return FLASHCART_OK;
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "png_decoder.h"
|
||||
#include "settings.h"
|
||||
#include "sound.h"
|
||||
#include "usb_comm.h"
|
||||
#include "utils/fs.h"
|
||||
#include "views/views.h"
|
||||
|
||||
@ -180,6 +181,8 @@ void menu_run (boot_params_t *boot_params) {
|
||||
sound_poll();
|
||||
|
||||
png_decoder_poll();
|
||||
|
||||
usb_comm_poll(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);
|
||||
if (found) {
|
||||
return found + strlen(prefix);
|
||||
return found + strlen(prefix) - 1;
|
||||
}
|
||||
|
||||
return path;
|
||||
@ -106,13 +106,13 @@ bool file_fill (char *path, uint8_t value) {
|
||||
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;
|
||||
FIL fil;
|
||||
bool error = false;
|
||||
|
||||
for (int i = 0; i < entries; i++) {
|
||||
sectors[i] = 0;
|
||||
if (!callback) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (f_open(&fil, strip_sd_prefix(path), FA_READ) != FR_OK) {
|
||||
@ -121,25 +121,22 @@ bool file_get_sectors (char *path, uint32_t *sectors, size_t entries) {
|
||||
|
||||
fs = fil.obj.fs;
|
||||
|
||||
uint32_t file_sector_entries = (ALIGN(f_size(&fil), FS_SECTOR_SIZE) / FS_SECTOR_SIZE);
|
||||
file_sector_entries = (file_sector_entries > entries) ? entries : file_sector_entries;
|
||||
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 < file_sector_entries; file_sector++) {
|
||||
if ((file_sector % fs->csize) == 0) {
|
||||
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)));
|
||||
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;
|
||||
}
|
||||
*sectors++ = (cluster_sector + (file_sector % fs->csize));
|
||||
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) {
|
||||
|
@ -17,7 +17,7 @@ size_t file_get_size (char *path);
|
||||
bool file_delete (char *path);
|
||||
bool file_allocate (char *path, size_t size);
|
||||
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 directory_exists (char *path);
|
||||
|
Loading…
Reference in New Issue
Block a user