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] == ':';