Added a extremely basic and buggy file browser.

More documentation and bug fixes.
This commit is contained in:
profi200 2020-07-01 23:55:27 +02:00
parent 83a4258bc0
commit 7879ce1597
No known key found for this signature in database
GPG Key ID: 17B42AE5911139F3
13 changed files with 293 additions and 39 deletions

View File

@ -0,0 +1,7 @@
#pragma once
#include "error_codes.h"
Result browseFiles(const char *const basePath, char selected[512]);

View File

@ -34,3 +34,7 @@
void LGYFB_init(void);
void LGYFB_processFrame(void);
void LGYFB_deinit(void);
#ifndef NDEBUG
void LGYFB_dbgDumpFrame(void);
#endif

View File

@ -15,28 +15,29 @@ enum
RES_OK = 0u,
RES_SD_CARD_REMOVED = 1u,
RES_INVALID_ARG = 2u,
RES_OUT_OF_MEM = 3u,
// fatfs errors.
// Caution: Update fs.c on ARM9 if this changes!
RES_FR_DISK_ERR = 3u, /* (1) A hard error occurred in the low level disk I/O layer */
RES_FR_INT_ERR = 4u, /* (2) Assertion failed */
RES_FR_NOT_READY = 5u, /* (3) The physical drive cannot work */
RES_FR_NO_FILE = 6u, /* (4) Could not find the file */
RES_FR_NO_PATH = 7u, /* (5) Could not find the path */
RES_FR_INVALID_NAME = 8u, /* (6) The path name format is invalid */
RES_FR_DENIED = 9u, /* (7) Access denied due to prohibited access or directory full */
RES_FR_EXIST = 10u, /* (8) Access denied due to prohibited access */
RES_FR_INVALID_OBJECT = 11u, /* (9) The file/directory object is invalid */
RES_FR_WRITE_PROTECTED = 12u, /* (10) The physical drive is write protected */
RES_FR_INVALID_DRIVE = 13u, /* (11) The logical drive number is invalid */
RES_FR_NOT_ENABLED = 14u, /* (12) The volume has no work area */
RES_FR_NO_FILESYSTEM = 15u, /* (13) There is no valid FAT volume */
RES_FR_MKFS_ABORTED = 16u, /* (14) The f_mkfs() aborted due to any problem */
RES_FR_TIMEOUT = 17u, /* (15) Could not get a grant to access the volume within defined period */
RES_FR_LOCKED = 18u, /* (16) The operation is rejected according to the file sharing policy */
RES_FR_NOT_ENOUGH_CORE = 19u, /* (17) LFN working buffer could not be allocated */
RES_FR_TOO_MANY_OPEN_FILES = 20u, /* (18) Number of open files > FF_FS_LOCK */
RES_FR_INVALID_PARAMETER = 21u, /* (19) Given parameter is invalid */
// Caution: Update fres2Res() in fs.c on ARM9 if this changes!
RES_FR_DISK_ERR = 4u, /* (1) A hard error occurred in the low level disk I/O layer */
RES_FR_INT_ERR = 5u, /* (2) Assertion failed */
RES_FR_NOT_READY = 6u, /* (3) The physical drive cannot work */
RES_FR_NO_FILE = 7u, /* (4) Could not find the file */
RES_FR_NO_PATH = 8u, /* (5) Could not find the path */
RES_FR_INVALID_NAME = 9u, /* (6) The path name format is invalid */
RES_FR_DENIED = 10u, /* (7) Access denied due to prohibited access or directory full */
RES_FR_EXIST = 11u, /* (8) Access denied due to prohibited access */
RES_FR_INVALID_OBJECT = 12u, /* (9) The file/directory object is invalid */
RES_FR_WRITE_PROTECTED = 13u, /* (10) The physical drive is write protected */
RES_FR_INVALID_DRIVE = 14u, /* (11) The logical drive number is invalid */
RES_FR_NOT_ENABLED = 15u, /* (12) The volume has no work area */
RES_FR_NO_FILESYSTEM = 16u, /* (13) There is no valid FAT volume */
RES_FR_MKFS_ABORTED = 17u, /* (14) The f_mkfs() aborted due to any problem */
RES_FR_TIMEOUT = 18u, /* (15) Could not get a grant to access the volume within defined period */
RES_FR_LOCKED = 19u, /* (16) The operation is rejected according to the file sharing policy */
RES_FR_NOT_ENOUGH_CORE = 20u, /* (17) LFN working buffer could not be allocated */
RES_FR_TOO_MANY_OPEN_FILES = 21u, /* (18) Number of open files > FF_FS_LOCK */
RES_FR_INVALID_PARAMETER = 22u, /* (19) Given parameter is invalid */
// Custom errors.
RES_ROM_TOO_BIG = MAKE_CUSTOM_ERR(0),

View File

@ -107,7 +107,7 @@ Result LGY_setGbaRtc(const GbaRtc rtc);
Result LGY_getGbaRtc(GbaRtc *const out);
Result LGY_backupGbaSave(void);
#ifdef ARM11
Result LGY_prepareGbaMode(bool biosIntro, const char *const romPath, const char *const savePath);
Result LGY_prepareGbaMode(bool biosIntro, char *const romPath);
void LGY_switchMode(void);
void LGY_handleEvents(void);
void LGY_deinit(void);

194
source/arm11/filebrowser.c Normal file
View File

@ -0,0 +1,194 @@
#include <stdlib.h>
#include <string.h>
#include "types.h"
#include "error_codes.h"
#include "fs.h"
#include "arm11/hardware/hid.h"
#include "arm11/fmt.h"
#include "hardware/gfx.h"
#define MAX_DIR_ENTRIES (510u)
#define DIR_READ_BLOCKS (10u)
#define SCREEN_COLS (52u)
#define SCREEN_ROWS (24u)
typedef struct
{
u32 num;
const char *strPtrs[MAX_DIR_ENTRIES];
u8 entTypes[MAX_DIR_ENTRIES]; // 0 = file, 1 = dir
char strBufs[MAX_DIR_ENTRIES][256];
} DirList;
// num including null terminator.
size_t safeStrcpy(char *const dst, const char *const src, size_t num)
{
if(num == 0) return 0;
const size_t len = strlen(src) + 1;
if(len > num)
{
*dst = '\0';
return 1;
}
strcpy(dst, src);
return len;
}
static Result scanDir(const char *const path, DirList *const dList)
{
FILINFO *const fi = (FILINFO*)malloc(DIR_READ_BLOCKS * sizeof(FILINFO));
if(fi == NULL) return RES_OUT_OF_MEM;
memset(dList, 0, sizeof(DirList));
Result res;
DHandle dh;
if((res = fOpenDir(&dh, path)) == RES_OK)
{
u32 read;
u32 totalRead = 0;
do
{
if((res = fReadDir(dh, fi, DIR_READ_BLOCKS, &read)) != RES_OK) break;
if(totalRead + read > MAX_DIR_ENTRIES) break;
for(u32 i = 0; i < read; i++)
{
const u32 dListPos = totalRead + i;
dList->strPtrs[dListPos] = dList->strBufs[dListPos];
// Mark as dir.
if(fi[i].fattrib & AM_DIR) dList->entTypes[dListPos] = 1;
safeStrcpy(dList->strBufs[dListPos], fi[i].fname, 256);
}
totalRead += read;
} while(read == DIR_READ_BLOCKS);
dList->num = totalRead;
fCloseDir(dh);
}
free(fi);
return res;
}
static void showDirList(const DirList *const dList, u32 start)
{
// Clear screen.
ee_printf("\x1b[2J");
const u32 listLength = (dList->num - start > SCREEN_ROWS ? start + SCREEN_ROWS : dList->num);
for(u32 i = start; i < listLength; i++)
{
const char *const printStr =
(dList->entTypes[i] == 0 ? "\x1b[%lu;H\x1b[37m %.51s" : "\x1b[%lu;H\x1b[33m %.51s");
ee_printf(printStr, i - start, dList->strPtrs[i]);
}
}
// TODO: Handle empty dirs.
Result browseFiles(const char *const basePath, char selected[512])
{
if(basePath == NULL || selected == NULL) return RES_INVALID_ARG;
char *curDir = (char*)malloc(512);
if(curDir == NULL) return RES_OUT_OF_MEM;
safeStrcpy(curDir, basePath, 512);
DirList *const dList = (DirList*)malloc(sizeof(DirList));
if(dList == NULL) return RES_OUT_OF_MEM;
Result res;
if((res = scanDir(curDir, dList)) != RES_OK) goto end;
showDirList(dList, 0);
s32 cursorPos = 0; // Within the entire list.
u32 windowPos = 0; // Window start position within the list.
s32 oldCursorPos = 0;
while(1)
{
ee_printf("\x1b[%lu;H ", oldCursorPos - windowPos); // Clear old cursor.
ee_printf("\x1b[%lu;H\x1b[37m>", cursorPos - windowPos); // Draw cursor.
u32 kDown;
do
{
GFX_waitForVBlank0();
hidScanInput();
if(hidGetExtraKeys(0) & KEY_POWER) goto end;
kDown = hidKeysDown();
} while(kDown == 0);
oldCursorPos = cursorPos;
if(kDown & KEY_DRIGHT) cursorPos += SCREEN_ROWS;
if(kDown & KEY_DLEFT) cursorPos -= SCREEN_ROWS;
if(kDown & KEY_DUP) cursorPos -= 1;
if(kDown & KEY_DDOWN) cursorPos += 1;
if(cursorPos < 0) cursorPos = dList->num - 1; // Wrap to end of list.
if((u32)cursorPos > (dList->num - 1)) cursorPos = 0; // Wrap to start of list.
if((u32)cursorPos < windowPos)
{
windowPos = cursorPos;
showDirList(dList, windowPos);
}
if((u32)cursorPos >= windowPos + SCREEN_ROWS)
{
windowPos = cursorPos - (SCREEN_ROWS - 1);
showDirList(dList, windowPos);
}
if(kDown & (KEY_A | KEY_B))
{
if(kDown & KEY_A)
{
u32 pathLen = strlen(curDir);
// TODO: !!! Insecure !!!
if(curDir[pathLen - 1] != '/') curDir[pathLen++] = '/';
safeStrcpy(curDir + pathLen, dList->strPtrs[cursorPos], 512);
if(dList->entTypes[cursorPos] == 0)
{
safeStrcpy(selected, curDir, 512);
break;
}
}
if(kDown & KEY_B)
{
const u32 pathLen = strlen(curDir);
if(curDir[pathLen - 2] != ':')
{
char *tmpPathPtr = curDir + pathLen;
while(*--tmpPathPtr != '/');
*tmpPathPtr = '\0';
}
}
if((res = scanDir(curDir, dList)) != RES_OK) break;
cursorPos = 0;
windowPos = 0;
showDirList(dList, 0);
}
}
end:
free(dList);
free(curDir);
// Clear screen.
ee_printf("\x1b[2J");
return res;
}

View File

@ -84,9 +84,10 @@ static u16 checkSaveOverride(u32 gameCode)
u16 saveType;
} overrideLut[] =
{
{"\0\0\0\0", SAVE_TYPE_SRAM_256k}, // Homebrew.
{"\0\0\0\0", SAVE_TYPE_SRAM_256k}, // Homebrew. TODO: Set WAITCNT to 0x4014?
{"GMB\0", SAVE_TYPE_SRAM_256k}, // Goomba Color (Homebrew).
{"AA2\0", SAVE_TYPE_EEPROM_64k}, // Super Mario Advance 2.
{"A3A\0", SAVE_TYPE_EEPROM_64k}, // Super Mario Advance 3.
};
for(u32 i = 0; i < sizeof(overrideLut) / sizeof(*overrideLut); i++)
@ -131,7 +132,7 @@ static u16 tryDetectSaveType(u32 romSize)
{"EEPROM_V111", SAVE_TYPE_EEPROM_8k}, // Actually EEPROM 4k.
{"EEPROM_V120", SAVE_TYPE_EEPROM_8k}, // Confirmed.
{"EEPROM_V121", SAVE_TYPE_EEPROM_64k}, // Confirmed.
{"EEPROM_V122", SAVE_TYPE_EEPROM_8k}, // Confirmed. Except Super Mario Advance 2.
{"EEPROM_V122", SAVE_TYPE_EEPROM_8k}, // Confirmed. Except Super Mario Advance 2/3.
{"EEPROM_V124", SAVE_TYPE_EEPROM_64k}, // Confirmed.
{"EEPROM_V125", SAVE_TYPE_EEPROM_8k}, // Confirmed.
{"EEPROM_V126", SAVE_TYPE_EEPROM_8k}, // Confirmed.
@ -198,7 +199,7 @@ static void setupFcramForGbaMode(void)
while(REG_PDN_FCRAM_CNT & PDN_FCRAM_CNT_CLK_E_ACK); // Wait until clock is disabled.
}
Result LGY_prepareGbaMode(bool biosIntro, const char *const romPath, const char *const savePath)
Result LGY_prepareGbaMode(bool biosIntro, char *const romPath)
{
// Load the ROM image.
u32 romSize;
@ -209,9 +210,12 @@ Result LGY_prepareGbaMode(bool biosIntro, const char *const romPath, const char
const u16 saveType = tryDetectSaveType(romSize);
// Prepare ARM9 for GBA mode + settings and save loading.
const u32 romPathLen = strlen(romPath);
strcpy(romPath + romPathLen - 4, ".sav");
u32 cmdBuf[4];
cmdBuf[0] = (u32)savePath;
cmdBuf[1] = strlen(savePath) + 1;
cmdBuf[0] = (u32)romPath;
cmdBuf[1] = romPathLen + 1;
cmdBuf[2] = biosIntro;
cmdBuf[3] = saveType;
res = PXI_sendCmd(IPC_CMD9_PREPARE_GBA, cmdBuf, 4);
@ -256,6 +260,8 @@ void LGY_switchMode(void)
}
#ifndef NDEBUG
#include "arm11/hardware/gx.h"
#include "arm11/hardware/gpu_regs.h"
void debugTests(void)
{
const u32 kDown = hidKeysDown();
@ -265,7 +271,20 @@ void debugTests(void)
{
GbaRtc rtc; LGY_getGbaRtc(&rtc);
ee_printf("RTC: %02X.%02X.%04X %02X:%02X:%02X\n", rtc.d, rtc.mon, rtc.y + 0x2000u, rtc.h, rtc.min, rtc.s);
/*static u8 filter = 1;
filter ^= 1;
u32 texEnvSource = 0x000F000F;
u32 texEnvCombiner = 0x00000000;
if(filter == 1)
{
texEnvSource = 0x00FF00FFu;
texEnvCombiner = 0x00010001u;
}
REG_GX_P3D(GPUREG_TEXENV1_SOURCE) = texEnvSource;
REG_GX_P3D(GPUREG_TEXENV1_COMBINER) = texEnvCombiner;*/
}
if(kDown & KEY_Y) LGYFB_dbgDumpFrame();
}
#endif

View File

@ -63,13 +63,20 @@ void LGYFB_init(void)
REG_LGYFB_TOP_SIZE = LGYFB_SIZE(256u, 160u);
REG_LGYFB_TOP_STAT = LGYFB_IRQ_MASK;
REG_LGYFB_TOP_IRQ = 0;
// With RGB8 output solid red and blue are converted to 0xF8 and green to 0xFA.
// So either the hardware uses RGB565 internally or RGB666 with different conversion for green.
// Some results:
// RGBA8: Same as RGB8 but with useless alpha component.
// RGB8: Observed best format. No dithering and best color accuracy (if we ignore the lazy conversion).
// RGB565: A little dithering. Good color accuracy.
// RGB5551: Lots of dithering. Good color accuracy (a little worse than 565).
REG_LGYFB_TOP_ALPHA = 0xFF;
REG_LGYFB_TOP_CNT = LGYFB_DMA_E | LGYFB_OUT_SWIZZLE | LGYFB_OUT_FMT_8880 | LGYFB_ENABLE;
IRQ_registerIsr(IRQ_CDMA_EVENT0, 13, 0, lgyFbDmaIrqHandler);
}
void rotateFrame(void)
static void rotateFrame(void)
{
// With dark filter.
alignas(16) static const u8 firstList[1136] =
@ -122,7 +129,7 @@ alignas(16) static const u8 firstList[1136] =
0x18, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
0x81, 0x00, 0x4F, 0x80, 0x00, 0x01, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0x01, 0x00, 0x00, 0x00, // Last 4 bytes: Texture format.
0x8E, 0x00, 0x0F, 0x00, 0x01, 0x10, 0x01, 0x00, 0x80, 0x00, 0x0B, 0x00,
0x00, 0x00, 0x01, 0x00, 0x80, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00,
0x8B, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x07, 0x00,
@ -397,3 +404,13 @@ void LGYFB_deinit(void)
IRQ_unregisterIsr(IRQ_CDMA_EVENT0);
}
#ifndef NDEBUG
#include "fsutil.h"
void LGYFB_dbgDumpFrame(void)
{
GX_displayTransfer((u32*)0x18200000, 160u<<16 | 256u, (u32*)0x18400000, 160u<<16 | 256u, 1u<<12 | 1u<<8);
GFX_waitForEvent(GFX_EVENT_PPF, false);
fsQuickWrite((void*)0x18400000, "sdmc:/lgyfb_dbg_frame.bgr", 256 * 160 * 3);
}
#endif

View File

@ -16,7 +16,9 @@ LPFE
# LgyFb sometimes (at the end of each scanline?) sends
# single requests. Since we can transfer all 8 scanlines
# with bursts only we will ignore them.
LP 47
# LP 31 # RGB5551 and RGB565
LP 47 # RGB8
# LP 63 # RGBA8
LDB
STB
LPENDB

View File

@ -16,6 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include "types.h"
#include "arm11/hardware/hid.h"
#include "arm11/hardware/codec.h"
@ -25,6 +26,7 @@
#include "arm11/power.h"
#include "hardware/gfx.h"
#include "fs.h"
#include "arm11/filebrowser.h"
#include "arm.h"
@ -35,11 +37,15 @@ int main(void)
GFX_setBrightness(DEFAULT_BRIGHTNESS, DEFAULT_BRIGHTNESS);
consoleInit(SCREEN_BOT, NULL);
//CODEC_init();
fMount(FS_DRIVE_SDMC);
Result res;
char *romPath = (char*)malloc(512);
*romPath = '\0';
if((res = fMount(FS_DRIVE_SDMC)) != RES_OK || (res = browseFiles("sdmc:/", romPath)) != RES_OK || *romPath == '\0')
goto end;
ee_puts("Reading ROM and save...");
Result res;
if((res = LGY_prepareGbaMode(false, "sdmc:/rom.gba", "sdmc:/rom.sav")) == RES_OK)
if((res = LGY_prepareGbaMode(false, romPath)) == RES_OK)
{
#ifdef NDEBUG
GFX_setForceBlack(false, true);
@ -59,7 +65,10 @@ int main(void)
__wfi();
} while(1);
}
else printErrorWaitInput(res, 0);
end:
free(romPath);
if(res != RES_OK) printErrorWaitInput(res, 0);
LGY_deinit();
fUnmount(FS_DRIVE_SDMC);

View File

@ -11,11 +11,11 @@ BEGIN_ASM_FUNC _arm7_stub_start
mov r0, #PSR_INT_OFF | PSR_SVC_MODE
adr r1, _arm7_stub_start + 0x200 @ 0x3008000
msr CPSR_cxsf, r0
mov r0, #PSR_INT_OFF | PSR_IRQ_MODE
@mov r0, #PSR_INT_OFF | PSR_IRQ_MODE
mov sp, r1
msr CPSR_cxsf, r0
@msr CPSR_cxsf, r0
mov r0, #PSR_INT_OFF | PSR_SYS_MODE
sub sp, r1, #0x60 @ 0x3007FA0
@sub sp, r1, #0x60 @ 0x3007FA0
msr CPSR_cxsf, r0
mov r3, #0x4700000
adr r2, _arm7_stub_16 + 1

View File

@ -44,7 +44,7 @@ static struct
static Result fres2Res(FRESULT fr)
{
if(fr != FR_OK) return fr + 2;
if(fr != FR_OK) return fr + 3;
else return RES_OK;
}

View File

@ -27,7 +27,7 @@
static u32 g_saveSize = 0;
static u32 g_saveHash[8] = {0};
static char g_savePath[256] = {0};
static char g_savePath[512] = {0};
@ -82,7 +82,7 @@ Result LGY_prepareGbaMode(bool biosIntro, u16 saveType, const char *const savePa
setupBiosOverlay(biosIntro);
setupSaveType(saveType);
strncpy_s(g_savePath, savePath, 255, 256);
strncpy_s(g_savePath, savePath, 511, 512);
Result res = RES_OK;
if(g_saveSize != 0)

View File

@ -16,6 +16,7 @@ void printError(Result res)
"OK",
"SD card removed",
"Invalid argument",
"Out of memory",
// fatfs errors.
"fatfs disk error",