From 9ee6942e79a0674384d7ad948bbae16c5c16a43c Mon Sep 17 00:00:00 2001 From: Cameron Cawley Date: Wed, 22 Sep 2021 14:01:00 +0100 Subject: [PATCH] Improve RISC OS implementations of SDL_GetBasePath and SDL_GetPrefPath --- src/filesystem/riscos/SDL_sysfilesystem.c | 170 ++++++++++++++++++---- 1 file changed, 144 insertions(+), 26 deletions(-) diff --git a/src/filesystem/riscos/SDL_sysfilesystem.c b/src/filesystem/riscos/SDL_sysfilesystem.c index a16effffe..125470bb2 100644 --- a/src/filesystem/riscos/SDL_sysfilesystem.c +++ b/src/filesystem/riscos/SDL_sysfilesystem.c @@ -25,28 +25,144 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* System dependent filesystem routines */ -#include -#include +#include +#include +#include #include "SDL_error.h" #include "SDL_stdinc.h" #include "SDL_filesystem.h" -#include "SDL_rwops.h" + +/* Wrapper around __unixify_std that uses SDL's memory allocators */ +static char * +SDL_unixify_std(const char *ro_path, char *buffer, size_t buf_len, int filetype) +{ + const char *const in_buf = buffer; /* = NULL if we malloc the buffer. */ + + if (!buffer) { + /* This matches the logic in __unixify, with an additional byte for the + * extra path separator. + */ + buf_len = SDL_strlen(ro_path) + 14 + 1; + buffer = SDL_malloc(buf_len); + + if (!buffer) { + SDL_OutOfMemory(); + return NULL; + } + } + + if (!__unixify_std(ro_path, buffer, buf_len, filetype)) { + if (!in_buf) + SDL_free(buffer); + + SDL_SetError("Could not convert '%s' to a Unix-style path", ro_path); + return NULL; + } + + /* HACK: It's necessary to add an extra path separator here since SDL's API + * requires it, however paths with trailing separators aren't normally valid + * on RISC OS. + */ + if (__get_riscosify_control() & __RISCOSIFY_NO_PROCESS) + SDL_strlcat(buffer, ".", buf_len); + else + SDL_strlcat(buffer, "/", buf_len); + + return buffer; +} + +static char * +canonicalisePath(const char *path, const char *pathVar) +{ + _kernel_oserror *error; + _kernel_swi_regs regs; + char *buf; + + regs.r[0] = 37; + regs.r[1] = (int)path; + regs.r[2] = 0; + regs.r[3] = (int)pathVar; + regs.r[4] = 0; + regs.r[5] = 0; + error = _kernel_swi(OS_FSControl, ®s, ®s); + if (error) { + SDL_SetError("Couldn't canonicalise path: %s", error->errmess); + return NULL; + } + + regs.r[5] = 1 - regs.r[5]; + buf = SDL_malloc(regs.r[5]); + if (!buf) { + SDL_OutOfMemory(); + return NULL; + } + regs.r[2] = (int)buf; + error = _kernel_swi(OS_FSControl, ®s, ®s); + if (error) { + SDL_SetError("Couldn't canonicalise path: %s", error->errmess); + SDL_free(buf); + return NULL; + } + + return buf; +} + +static _kernel_oserror * +createDirectoryRecursive(char *path) +{ + char *ptr = NULL; + _kernel_oserror *error; + _kernel_swi_regs regs; + regs.r[0] = 8; + regs.r[1] = (int)path; + regs.r[2] = 0; + + for (ptr = path+1; *ptr; ptr++) { + if (*ptr == '.') { + *ptr = '\0'; + error = _kernel_swi(OS_File, ®s, ®s); + *ptr = '.'; + if (error != NULL) + return error; + } + } + return _kernel_swi(OS_File, ®s, ®s); +} char * SDL_GetBasePath(void) { - SDL_Unsupported(); - return NULL; + _kernel_swi_regs regs; + _kernel_oserror *error; + char *canon, *ptr, *retval; + + error = _kernel_swi(OS_GetEnv, ®s, ®s); + if (error) { + return NULL; + } + + canon = canonicalisePath((const char *)regs.r[0], "Run$Path"); + if (!canon) { + return NULL; + } + + /* chop off filename. */ + ptr = SDL_strrchr(canon, '.'); + if (ptr != NULL) + *ptr = '\0'; + + retval = SDL_unixify_std(canon, NULL, 0, __RISCOSIFY_FILETYPE_NOTSPECIFIED); + SDL_free(canon); + return retval; } char * SDL_GetPrefPath(const char *org, const char *app) { - const char *prefix = "//"; - char *retval = NULL; - char *ptr = NULL; - size_t len = 0; + char *canon, *dir, *retval; + size_t len; + _kernel_oserror *error; if (!app) { SDL_InvalidParamError("app"); @@ -56,34 +172,36 @@ SDL_GetPrefPath(const char *org, const char *app) org = ""; } - len = SDL_strlen(prefix) + SDL_strlen(org) + SDL_strlen(app) + 3; - retval = (char *) SDL_malloc(len); - if (!retval) { + canon = canonicalisePath("", "Run$Path"); + if (!canon) { + return NULL; + } + + len = SDL_strlen(canon) + SDL_strlen(org) + SDL_strlen(app) + 4; + dir = (char *) SDL_malloc(len); + if (!dir) { SDL_OutOfMemory(); + free(canon); return NULL; } if (*org) { - SDL_snprintf(retval, len, "%s%s/%s/", prefix, org, app); + SDL_snprintf(dir, len, "%s.%s.%s", canon, org, app); } else { - SDL_snprintf(retval, len, "%s%s/", prefix, app); + SDL_snprintf(dir, len, "%s.%s", canon, app); } - for (ptr = retval+1; *ptr; ptr++) { - if (*ptr == '/') { - *ptr = '\0'; - if (mkdir(retval, 0700) != 0 && errno != EEXIST) - goto error; - *ptr = '/'; - } - } - if (mkdir(retval, 0700) != 0 && errno != EEXIST) { -error: - SDL_SetError("Couldn't create directory '%s': '%s'", retval, strerror(errno)); - SDL_free(retval); + SDL_free(canon); + + error = createDirectoryRecursive(dir); + if (error != NULL) { + SDL_SetError("Couldn't create directory: %s", error->errmess); + SDL_free(dir); return NULL; } + retval = SDL_unixify_std(dir, NULL, 0, __RISCOSIFY_FILETYPE_NOTSPECIFIED); + SDL_free(dir); return retval; }