From 752467dee4e2cda61c605a7b4a615e76817f28ba Mon Sep 17 00:00:00 2001 From: Mauricio Garrido Date: Fri, 9 Jan 2026 17:20:29 -0600 Subject: [PATCH] 3ds: Various fixes, optimizations. This commit does the following changes: - Fix mkdir implementation not reporting EEXIST error. - Fix database build feature. - Small speed-up when parsing directories and files. - Fix buffering thread hogging cpu and preventing other threads to run. - Fix sdl plugins not compiling by re-adding ctru specific cflags in sdl.make. Change-Id: I507c0dcc85cdbcc607ab9c9c6d0b42e6a80caa5a --- apps/plugins/sdl/sdl.make | 6 + firmware/target/hosted/ctru/lib/bfile/bfile.c | 192 ++++++++++++++++-- firmware/target/hosted/ctru/lib/bfile/bfile.h | 15 +- firmware/target/hosted/ctru/lib/sys_dir.c | 47 +++-- firmware/target/hosted/ctru/lib/sys_file.c | 47 +---- firmware/target/hosted/ctru/lib/sys_thread.c | 11 +- 6 files changed, 240 insertions(+), 78 deletions(-) diff --git a/apps/plugins/sdl/sdl.make b/apps/plugins/sdl/sdl.make index 3b3bf819cf..9d666bd732 100644 --- a/apps/plugins/sdl/sdl.make +++ b/apps/plugins/sdl/sdl.make @@ -39,6 +39,12 @@ SDLFLAGS = -I$(SDL_SRCDIR)/include $(filter-out -O%,$(PLUGINFLAGS)) \ #-ffast-math -funroll-loops -fomit-frame-pointer -fexpensive-optimizations \ #-D_GNU_SOURCE=1 -D_REENTRANT -DSDL -DELF +# CTRU does need these to avoid compiler errors +ifeq ($(APP_TYPE),ctru-app) +SDLFLAGS += -Wno-int-conversion -Wno-incompatible-pointer-types \ +-Wno-implicit-function-declaration -Wno-implicit-int +endif + ifndef APP_TYPE ### no target has a big enough plugin buffer ROCKS += $(SDL_OBJDIR)/duke3d.ovl diff --git a/firmware/target/hosted/ctru/lib/bfile/bfile.c b/firmware/target/hosted/ctru/lib/bfile/bfile.c index e63ecf008b..e7a8625e71 100644 --- a/firmware/target/hosted/ctru/lib/bfile/bfile.c +++ b/firmware/target/hosted/ctru/lib/bfile/bfile.c @@ -6,6 +6,12 @@ #include "bfile.h" +/* Define LOGF_ENABLE to enable logf output in this file */ +#if 0 +#define LOGF_ENABLE +#endif +#include "logf.h" + // rw mutex implementation void sync_RWMutexInit(sync_RWMutex *m) { LightLock_Init(&m->shared); @@ -63,12 +69,14 @@ void sync_RWMutexUnlock(sync_RWMutex *m) { file_error_t readPage(PageReader *p, u64 offset) { u32 read_bytes = 0; - if (R_FAILED(FSFILE_Read(p->file, - &read_bytes, - offset, - p->buffer, - p->size))) { - printf("readPage: FSFILE_Read failed (I/O Error)\n"); + Result res; + res = FSFILE_Read(p->file, + &read_bytes, + offset, + p->buffer, + p->size); + if (R_FAILED(res)) { + logf("readPage: 0x%lx\n", R_DESCRIPTION(res)); return "I/O Error"; } @@ -81,7 +89,7 @@ file_error_t readPage(PageReader *p, u64 offset) p->start = offset; p->end = offset + p->size; - /* printf("page: 0x%llx, 0x%llx, %lld (last_page: %s)\n", p->start, p->end, p->size, p->isLastPage == true ? "true": "false"); */ + logf("page: 0x%llx, 0x%llx, %lld (last_page: %s)\n", p->start, p->end, p->size, p->isLastPage == true ? "true": "false"); return NULL; } @@ -89,18 +97,18 @@ PageReader* NewPageReader(Handle file, s64 pageSize) { PageReader *p = (PageReader *) malloc(sizeof(PageReader)); if (p == NULL) { - printf("NewPageReader: memory error\n"); + logf("NewPageReader: memory error\n"); return NULL; } - p->buffer = (u8 *) malloc(sizeof(u8) * pageSize); + p->buffer = malloc(sizeof(u8) * pageSize); if (p->buffer == NULL) { - printf("NewPagereader: memory error (buffer)\n"); + logf("NewPagereader: memory error (buffer)\n"); return NULL; } // clear buffer data - memset(p->buffer, 0x0, sizeof(u8) + pageSize); + memset(p->buffer, 0x0, sizeof(u8) * pageSize); p->start = 0; p->end = 0; @@ -140,6 +148,27 @@ int_error_t PageReader_ReadAt(PageReader *p, u8 *buffer, size_t size, off_t offs { bool eof = false; + // only use page_reader if buffer size is smaller than page size + if (size > p->size) { + // direct file access (fs.h) + u32 bytes_read = 0; + Result res = FSFILE_Read(p->file, + &bytes_read, + offset, + buffer, + size); + if (R_FAILED(res)) { + return (int_error_t) { R_DESCRIPTION(res), "I/O Error" }; + } + + // io.EOF + if (bytes_read == 0) { + return (int_error_t) { 0, "io.EOF" }; + } + + return (int_error_t) { bytes_read, NULL }; + } + // higher probability, buffer is in cached page range if ((offset >= p->start) && ((offset + size) < p->end)) { copyPage(p, buffer, size, offset); @@ -147,16 +176,14 @@ int_error_t PageReader_ReadAt(PageReader *p, u8 *buffer, size_t size, off_t offs } // less higher probability, read new page at offset - if (p->isLastPage == false) { - file_error_t err = readPage(p, offset); - if (err != NULL) { - return (int_error_t) { -1, "I/O Error" }; - } - - // we could have reached last page here so continue to - // last page handling + file_error_t err = readPage(p, offset); + if (err != NULL) { + return (int_error_t) { -1, "I/O Error" }; } + // we could have reached last page here so continue to + // last page handling + // lower probability, we are at the last page // trying to read past end of file @@ -173,3 +200,130 @@ int_error_t PageReader_ReadAt(PageReader *p, u8 *buffer, size_t size, off_t offs copyPage(p, buffer, size, offset); return (int_error_t) { size, eof == true ? "io.EOF" : NULL }; } + +int_error_t PageReader_WriteAt(PageReader *p, u8 *buffer, size_t size, off_t offset, bool flush_cache) +{ + // we will use direct file access for writing (fs.h) + u32 written = 0; + Result res = FSFILE_Write(p->file, + &written, + offset, + buffer, + size, + FS_WRITE_FLUSH); + if (R_FAILED(res)) { + return (int_error_t) { -1, "I/O Error" }; + } + + if (flush_cache) { + /* logf("PageReader_WriteAt: flush cache\n"); */ + // reload current page for O_RDWR files + // read ahead first page buffer from file + file_error_t err = readPage(p, p->start); + if (err != NULL) { + return (int_error_t) { -1, "I/O Error" }; + } + } + + return (int_error_t) { written, NULL }; +} + +// page reader implementation for directory entries +file_error_t readPageDirectory(PageReader *p) +{ + u32 entries_read = 0; + FS_DirectoryEntry *dirEntries = (FS_DirectoryEntry *) p->buffer; + Result result = FSDIR_Read(p->file, + &entries_read, + p->size, + dirEntries); + + if (R_FAILED(result)) { + logf("FSDIR_Read: 0x%lx\n", R_DESCRIPTION(result)); + return "I/O Error"; + } + + // we reached directory end + if (entries_read == 0) { + return "io.EOF"; + } + + if (entries_read < p->size) { + p->isLastPage = true; + } + + p->size = entries_read; + p->start = 0; + p->end = p->size; + + /* logf("page: 0x%llx, 0x%llx, %lld (last_page: %s)\n", p->start, p->end, p->size, p->isLastPage == true ? "true": "false"); */ + return NULL; +} + +PageReader *NewPageReaderDirectory(Handle file, s64 pageSize) +{ + PageReader *p = (PageReader *) malloc(sizeof(PageReader)); + if (p == NULL) { + logf("NewPageReaderDirectory: memory error\n"); + return NULL; + } + + // round buffer size to sizeof(FS_DirectoryEntry) + int maxDirEntries = pageSize / sizeof(FS_DirectoryEntry); + + p->buffer = malloc(sizeof(FS_DirectoryEntry) * maxDirEntries); + if (p->buffer == NULL) { + logf("NewPageReaderDirectory: memory error (buffer)\n"); + return NULL; + } + + // clear buffer data + memset(p->buffer, 0x0, sizeof(FS_DirectoryEntry) * maxDirEntries); + + p->start = 0; + p->end = 0; + p->isLastPage = false; + p->size = maxDirEntries; + p->file = file; + + // read ahead first page buffer from file + file_error_t err = readPageDirectory(p); + if (err != NULL) { + free(p); + return NULL; + } + + return p; +} + +static inline void copyEntry(PageReader *p, FS_DirectoryEntry *entry) +{ + const FS_DirectoryEntry *dirEntries = (FS_DirectoryEntry *) p->buffer; + memcpy(entry, &dirEntries[p->start], sizeof(FS_DirectoryEntry)); + p->start ++; +} + +int_error_t PageReader_ReadDir(PageReader *p, FS_DirectoryEntry *entry) +{ + // higher probability, entry is in cached page range + if (p->start < p->end) { + copyEntry(p, entry); + return (int_error_t) { 1, NULL}; + } + + // less higher probability, read a new page + file_error_t err = readPageDirectory(p); + + // end of directory? + if (err != NULL) { + if (!strcmp(err, "io.EOF")) { + return (int_error_t) { 0, "io.EOF" }; + } + else { + return (int_error_t) { -1, "I/O Error" }; + } + } + + copyEntry(p, entry); + return (int_error_t) { 1, NULL}; +} diff --git a/firmware/target/hosted/ctru/lib/bfile/bfile.h b/firmware/target/hosted/ctru/lib/bfile/bfile.h index dab6c69309..bdaeb3810e 100644 --- a/firmware/target/hosted/ctru/lib/bfile/bfile.h +++ b/firmware/target/hosted/ctru/lib/bfile/bfile.h @@ -3,15 +3,24 @@ #include "bfile-internal.h" -static const int defaultPageSize = 0x80000; // default page buffer size, 512 kB +static const int defaultPageSize = 0x20000; // default page buffer size, 128 kB -// NewPageReader creates a new PageReader struct with pageSize +// NewPageReader creates a new PageReader struct with pageSize. PageReader *NewPageReader(Handle file, s64 pageSize); -// Free all memory associated to the PageReader pointer +// Free all memory associated to the PageReader pointer. void PageReader_Free(PageReader *p); // ReadAt reads size bytes from the PageReader starting at offset. int_error_t PageReader_ReadAt(PageReader *p, u8 *buffer, size_t size, off_t offset); +// WriteAt writes size bytes from the PageReader starting at offset. +int_error_t PageReader_WriteAt(PageReader *p, u8 *buffer, size_t size, off_t offset, bool flush_cache); + +// NewPageReader creates a new PageReader struct with pageSize, for directory use. +PageReader *NewPageReaderDirectory(Handle file, s64 pageSize); + +// ReadAt reads one directory entry from memory. +int_error_t PageReader_ReadDir(PageReader *p, FS_DirectoryEntry *entry); + #endif /* _B_FILE_H_ */ diff --git a/firmware/target/hosted/ctru/lib/sys_dir.c b/firmware/target/hosted/ctru/lib/sys_dir.c index 5571d40c8e..b997178900 100644 --- a/firmware/target/hosted/ctru/lib/sys_dir.c +++ b/firmware/target/hosted/ctru/lib/sys_dir.c @@ -46,7 +46,7 @@ static struct dirstr_desc struct dirent entry; /* current parsed entry information */ } open_streams[MAX_OPEN_DIRS] = { - [0 ... MAX_OPEN_FILES-1] = { .stream = { .handle = 0 } } + [0 ... MAX_OPEN_FILES-1] = { .stream = { .cache = NULL } } }; extern FS_Archive sdmcArchive; @@ -58,7 +58,7 @@ static struct dirstr_desc * get_dirstr(DIR *dirp) if (!PTR_IN_ARRAY(open_streams, dir, MAX_OPEN_DIRS)) dir = NULL; - else if (dir->stream.handle != 0) + else if (dir->stream.cache != NULL) return dir; int errnum; @@ -103,7 +103,7 @@ static struct dirstr_desc * alloc_dirstr(void) for (unsigned int dd = 0; dd < MAX_OPEN_DIRS; dd++) { struct dirstr_desc *dir = &open_streams[dd]; - if (dir->stream.handle == 0) + if (dir->stream.cache == NULL) return dir; } @@ -151,6 +151,11 @@ DIR * ctru_opendir(const char *dirname) FILE_ERROR(EMFILE, -1); } + dir->stream.cache = NewPageReaderDirectory(dir->stream.handle, defaultPageSize); + if (dir->stream.cache == NULL) { + FILE_ERROR(ERRNO, -2); + } + dir->stream.size = 0; dir->stream.flags = 0; @@ -159,6 +164,10 @@ DIR * ctru_opendir(const char *dirname) dirp = (DIR *)dir; file_error: + if (dir->stream.handle != 0) { + FSDIR_Close(dir->stream.handle); + dir->stream.handle = 0; + } file_internal_unlock_WRITER(); return dirp; } @@ -177,12 +186,15 @@ int ctru_closedir(DIR *dirp) logf("closedir(dirname=\"%s\")\n", dir->stream.path); - if (dir->stream.handle == 0) + if (dir->stream.cache == NULL) { logf("dir #%d: dir not open\n", (int)(dir - open_streams)); FILE_ERROR(EBADF, -2); } + PageReader_Free(dir->stream.cache); + dir->stream.cache = NULL; + Result res = FSDIR_Close(dir->stream.handle); if (R_FAILED(res)) FILE_ERROR(ERRNO, -3); @@ -233,16 +245,12 @@ struct dirent * ctru_readdir(DIR *dirp) logf("readdir(dirname=\"%s\")\n", dir->stream.path); - u32 dataRead = 0; FS_DirectoryEntry dirEntry; - Result result = FSDIR_Read(dir->stream.handle, - &dataRead, - 1, - &dirEntry); - if (R_FAILED(result)) + int_error_t n_err = PageReader_ReadDir(dir->stream.cache, &dirEntry); + if (n_err.n < 0) FILE_ERROR(EIO, _RC); - if (dataRead == 0) { + if (n_err.n == 0) { /* directory end. return NULL value, no errno */ res = NULL; goto file_error; @@ -274,6 +282,15 @@ file_error: return res; } +/* libctru result.h description values appear to be missing + FSUSER_CreateDirectory return values */ + +enum +{ + RD_DIRECTORY_ALREADY_EXISTS = 0xBE, + RD_MISSING_PARENT_DIRECTORY = 0x78, +}; + /* make a directory */ int ctru_mkdir(const char *path) { @@ -286,8 +303,14 @@ int ctru_mkdir(const char *path) Result res = FSUSER_CreateDirectory(sdmcArchive, fsMakePath(PATH_ASCII, path), 0); - if (R_FAILED(res)) + if (R_FAILED(res)) { + if (R_DESCRIPTION(res) == RD_DIRECTORY_ALREADY_EXISTS) + FILE_ERROR(ERRNO, EEXIST); + if (R_DESCRIPTION(res) == RD_MISSING_PARENT_DIRECTORY) + FILE_ERROR(ERRNO, ENOENT); + /* Unknown error */ FILE_ERROR(ERRNO, -1); + } rc = 0; file_error: diff --git a/firmware/target/hosted/ctru/lib/sys_file.c b/firmware/target/hosted/ctru/lib/sys_file.c index fe884b2531..4370627362 100644 --- a/firmware/target/hosted/ctru/lib/sys_file.c +++ b/firmware/target/hosted/ctru/lib/sys_file.c @@ -235,46 +235,17 @@ static ssize_t readwrite(struct filestr_desc *file, void *buf, size_t nbyte, int_error_t n_err; if (write) { - // we will use direct file access for writing - u32 written = 0; - Result res = FSFILE_Write(file->stream.handle, - &written, - AtomicLoad(&file->offset), - buf, - nbyte, - FS_WRITE_FLUSH); - if (R_FAILED(res)) { - n_err.err = "I/O error"; - } - - n_err.n = written; + n_err = PageReader_WriteAt(file->stream.cache, + buf, + nbyte, + AtomicLoad(&file->offset), + file->stream.flags & O_RDWR ? true : false); } else { - // only use page_reader if buffer size is smaller than page size - if (nbyte < file->stream.cache->size) { - n_err = PageReader_ReadAt(file->stream.cache, - (u8 *) buf, - nbyte, - AtomicLoad(&file->offset)); - } - else { - u32 bytes_read = 0; - Result res = FSFILE_Read(file->stream.handle, - &bytes_read, - AtomicLoad(&file->offset), - buf, - nbyte); - if (R_FAILED(res)) { - n_err.err = "I/O error"; - } - - // io.EOF - if (bytes_read == 0) { - n_err.err = "io.EOF"; - } - - n_err.n = bytes_read; - } + n_err = PageReader_ReadAt(file->stream.cache, + buf, + nbyte, + AtomicLoad(&file->offset)); } if ((n_err.err != NULL) && strcmp(n_err.err, "io.EOF")) { diff --git a/firmware/target/hosted/ctru/lib/sys_thread.c b/firmware/target/hosted/ctru/lib/sys_thread.c index 4d6ba2ff99..04e80b75ac 100644 --- a/firmware/target/hosted/ctru/lib/sys_thread.c +++ b/firmware/target/hosted/ctru/lib/sys_thread.c @@ -78,12 +78,12 @@ int get_ctru_thread_priority(int priority) (priority == PRIORITY_REALTIME)) return 0x18; else if (priority == PRIORITY_BUFFERING) - return 0x18; /* Highest */ + return 0x2F; else if ((priority == PRIORITY_USER_INTERFACE) || (priority == PRIORITY_RECORDING) || (priority == PRIORITY_PLAYBACK)) - return 0x30; - else if (priority == PRIORITY_PLAYBACK_MAX) return 0x2F; + else if (priority == PRIORITY_PLAYBACK_MAX) + return 0x20; else if (priority == PRIORITY_SYSTEM) return 0x30; else if (priority == PRIORITY_BACKGROUND) @@ -208,8 +208,8 @@ sysThread *sys_create_thread(int(*fn)(void *), const char *name, const size_t st thread->userdata = data; thread->stacksize = stacksize; - int cpu = -1; - if (name && (strncmp(name, "tagcache", 8) == 0) && R_SUCCEEDED(APT_SetAppCpuTimeLimit(30))) { + int cpu = 0; + if (name && (strncmp(name, "buffering", 9) == 0) && R_SUCCEEDED(APT_SetAppCpuTimeLimit(30))) { cpu = 1; printf("thread: %s, running in cpu 1\n", name); } @@ -400,4 +400,3 @@ int sys_cond_wait(sysCond *cond, RecursiveLock *mutex) return 0; } -