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
This commit is contained in:
Mauricio Garrido 2026-01-09 17:20:29 -06:00 committed by Solomon Peachy
parent 0777add596
commit 752467dee4
6 changed files with 240 additions and 78 deletions

View file

@ -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

View file

@ -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};
}

View file

@ -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_ */

View file

@ -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:

View file

@ -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")) {

View file

@ -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;
}