diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_path_utils.cpp b/stratosphere/ams_mitm/source/fs_mitm/fs_path_utils.cpp index d200ef4bc..9dc0c8595 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fs_path_utils.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_path_utils.cpp @@ -14,6 +14,7 @@ * along with this program. If not, see . */ #include +#include #include #include "fs_path_utils.hpp" #include "fs_results.hpp" @@ -162,7 +163,99 @@ Result FsPathUtils::IsNormalized(bool *out, const char *path) { return 0; } -Result FsPathUtils::Normalize(char *out, size_t max_out_size, const char *src, size_t *out_size) { - /* TODO */ - return ResultFsNotImplemented; +Result FsPathUtils::Normalize(char *out, size_t max_out_size, const char *src, size_t *out_len) { + /* Paths must start with / */ + if (src[0] != '/') { + return ResultFsInvalidPathFormat; + } + + bool skip_next_sep = false; + size_t i = 0; + size_t len = 0; + + while (src[i] != 0) { + if (src[i] == '/') { + /* Swallow separators. */ + while (src[++i] == '/') { } + if (src[i] == 0) { + break; + } + + /* Handle skip if needed */ + if (!skip_next_sep) { + if (len + 1 == max_out_size) { + out[len] = 0; + *out_len = len; + return ResultFsTooLongPath; + } + + out[len++] = '/'; + + /* TODO: N has some weird windows support stuff here under a bool. */ + /* Boolean is normally false though? */ + } + skip_next_sep = false; + } + + /* See length of current dir. */ + size_t dir_len = 0; + while (src[i+dir_len] != '/' && src[i+dir_len] != 0) { + dir_len++; + } + + if (FsPathUtils::IsCurrentDirectory(&src[i])) { + skip_next_sep = true; + } else if (FsPathUtils::IsParentDirectory(&src[i])) { + if (len == 1) { + return ResultFsDirectoryUnobtainable; + } + + /* Walk up a directory. */ + len -= 2; + while (out[len] != '/') { + len--; + } + } else { + /* Copy, possibly truncating. */ + if (len + dir_len + 1 <= max_out_size) { + for (size_t j = 0; j < dir_len; j++) { + out[len++] = src[i+j]; + } + } else { + const size_t copy_len = max_out_size - 1 - len; + for (size_t j = 0; j < copy_len; j++) { + out[len++] = src[i+j]; + } + out[len] = 0; + *out_len = len; + return ResultFsTooLongPath; + } + } + + i += dir_len; + } + + if (skip_next_sep) { + len--; + } + + if (len == 0 && max_out_size) { + out[len++] = '/'; + } + + if (max_out_size < len - 1) { + return ResultFsTooLongPath; + } + + /* NULL terminate. */ + out[len] = 0; + *out_len = len; + + /* Assert normalized. */ + bool normalized = false; + if (R_FAILED(FsPathUtils::IsNormalized(&normalized, out)) || !normalized) { + std::abort(); + } + + return 0; } diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_path_utils.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_path_utils.hpp index fbd073c13..142fd25b7 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fs_path_utils.hpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_path_utils.hpp @@ -33,6 +33,14 @@ class FsPathUtils { return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); } + static bool IsCurrentDirectory(const char *path) { + return path[0] == '.' && (path[1] == 0 || path[1] == '/'); + } + + static bool IsParentDirectory(const char *path) { + return path[0] == '.' && path[1] == '.' && (path[2] == 0 || path[2] == '/'); + } + static bool IsWindowsAbsolutePath(const char *path) { /* Nintendo uses this in path comparisons... */ return IsWindowsDriveLetter(path[0]) && path[1] == ':';