Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 49efcde301 |
@@ -27,10 +27,10 @@ struct User {
|
||||
std::string name;
|
||||
};
|
||||
|
||||
namespace Account {
|
||||
namespace account {
|
||||
Result init();
|
||||
void exit();
|
||||
std::vector<AccountUid> ids();
|
||||
std::string username(AccountUid id);
|
||||
std::string iconPath(AccountUid id);
|
||||
} // namespace Account
|
||||
} // namespace account
|
||||
|
||||
@@ -7,7 +7,7 @@ void servicesExit();
|
||||
Result servicesInit();
|
||||
void blinkLed(u8 times);
|
||||
|
||||
namespace StringUtils {
|
||||
namespace string_utils {
|
||||
bool containsInvalidChar(const std::string& str);
|
||||
std::string format(const char* fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||
std::string removeForbiddenCharacters(std::string src);
|
||||
@@ -19,4 +19,4 @@ namespace StringUtils {
|
||||
std::string removeNotAscii(std::string str);
|
||||
std::u16string UTF8toUTF16(const char* src);
|
||||
std::string elide(const std::string& s, size_t max_chars);
|
||||
} // namespace StringUtils
|
||||
} // namespace string_utils
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
namespace FileSystem {
|
||||
namespace file_system {
|
||||
Result mount(FsFileSystem* fs, u64 title_id, AccountUid uid);
|
||||
int mount(FsFileSystem fs);
|
||||
void unmount();
|
||||
} // namespace FileSystem
|
||||
} // namespace file_system
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
// New API — use these going forward.
|
||||
namespace nxst::log {
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace ui {
|
||||
userName->SetVisible(show);
|
||||
if (show) {
|
||||
userName->SetText(name);
|
||||
std::string path = Account::iconPath(*uid);
|
||||
std::string path = account::iconPath(*uid);
|
||||
if (!path.empty()) {
|
||||
avatar->SetImage(path);
|
||||
avatar->SetWidth(32);
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace ui {
|
||||
PU_SMART_CTOR(TransferOverlay)
|
||||
|
||||
void SetStatus(const std::string& status) {
|
||||
statusText->SetText(StringUtils::elide(status, 56));
|
||||
statusText->SetText(string_utils::elide(status, 56));
|
||||
}
|
||||
|
||||
void SetProgress(double val) {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
static std::map<AccountUid, User> s_users;
|
||||
|
||||
Result Account::init() {
|
||||
Result account::init() {
|
||||
Result res = accountInitialize(AccountServiceType_Application);
|
||||
if (R_FAILED(res))
|
||||
return res;
|
||||
@@ -24,11 +24,11 @@ Result Account::init() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Account::exit() {
|
||||
void account::exit() {
|
||||
accountExit();
|
||||
}
|
||||
|
||||
std::vector<AccountUid> Account::ids() {
|
||||
std::vector<AccountUid> account::ids() {
|
||||
std::vector<AccountUid> result;
|
||||
result.reserve(s_users.size());
|
||||
for (const auto& pair : s_users) {
|
||||
@@ -51,7 +51,7 @@ static User fetchUser(AccountUid id) {
|
||||
return user;
|
||||
}
|
||||
|
||||
std::string Account::username(AccountUid id) {
|
||||
std::string account::username(AccountUid id) {
|
||||
auto it = s_users.find(id);
|
||||
if (it == s_users.end()) {
|
||||
User user = fetchUser(id);
|
||||
@@ -61,7 +61,7 @@ std::string Account::username(AccountUid id) {
|
||||
return it->second.name;
|
||||
}
|
||||
|
||||
std::string Account::iconPath(AccountUid id) {
|
||||
std::string account::iconPath(AccountUid id) {
|
||||
char path[128];
|
||||
snprintf(path, sizeof(path), "sdmc:/switch/NXST/cache/%016lX%016lX.jpg", id.uid[0], id.uid[1]);
|
||||
|
||||
|
||||
+11
-12
@@ -4,7 +4,6 @@
|
||||
#include <vector>
|
||||
|
||||
#include "nxst/domain/account.hpp"
|
||||
#include <nxst/domain/util.hpp>
|
||||
#include <nxst/domain/title.hpp>
|
||||
#include <nxst/domain/util.hpp>
|
||||
#include <nxst/infra/fs/directory.hpp>
|
||||
@@ -22,22 +21,22 @@ void Title::init(u8 save_data_type, u64 title_id, AccountUid uid, const std::str
|
||||
m_id = title_id;
|
||||
m_uid = uid;
|
||||
m_save_data_type = save_data_type;
|
||||
m_user_name = Account::username(uid);
|
||||
m_user_name = account::username(uid);
|
||||
m_author = author;
|
||||
m_name = name;
|
||||
m_safe_name = StringUtils::containsInvalidChar(name) ? StringUtils::format("0x%016llX", m_id)
|
||||
: StringUtils::removeForbiddenCharacters(name);
|
||||
m_path = "sdmc:/switch/NXST/saves/" + StringUtils::format("0x%016llX", m_id) + " " + m_safe_name;
|
||||
m_safe_name = string_utils::containsInvalidChar(name) ? string_utils::format("0x%016llX", m_id)
|
||||
: string_utils::removeForbiddenCharacters(name);
|
||||
m_path = "sdmc:/switch/NXST/saves/" + string_utils::format("0x%016llX", m_id) + " " + m_safe_name;
|
||||
|
||||
std::string aname = StringUtils::removeAccents(m_name);
|
||||
std::string aname = string_utils::removeAccents(m_name);
|
||||
m_display_name = {aname, ""};
|
||||
|
||||
size_t colon = aname.rfind(':');
|
||||
if (colon != std::string::npos) {
|
||||
std::string head = aname.substr(0, colon);
|
||||
std::string tail = aname.substr(colon + 1);
|
||||
StringUtils::trim(head);
|
||||
StringUtils::trim(tail);
|
||||
string_utils::trim(head);
|
||||
string_utils::trim(tail);
|
||||
m_display_name = {head, tail};
|
||||
} else {
|
||||
size_t open = aname.rfind('(');
|
||||
@@ -45,8 +44,8 @@ void Title::init(u8 save_data_type, u64 title_id, AccountUid uid, const std::str
|
||||
if (open != std::string::npos && close != std::string::npos && close > open) {
|
||||
std::string head = aname.substr(0, open);
|
||||
std::string paren = aname.substr(open + 1, close - open - 1);
|
||||
StringUtils::trim(head);
|
||||
StringUtils::trim(paren);
|
||||
string_utils::trim(head);
|
||||
string_utils::trim(paren);
|
||||
m_display_name = {head, paren};
|
||||
}
|
||||
}
|
||||
@@ -105,7 +104,7 @@ void Title::lastPlayedTimestamp(u32 ts) {
|
||||
|
||||
std::string Title::playTime() const {
|
||||
const u64 minutes = m_play_time_ns / 60000000000ULL;
|
||||
return StringUtils::format("%d", minutes / 60) + ":" + StringUtils::format("%02d", minutes % 60) +
|
||||
return string_utils::format("%d", minutes / 60) + ":" + string_utils::format("%02d", minutes % 60) +
|
||||
" hours";
|
||||
}
|
||||
|
||||
@@ -237,7 +236,7 @@ std::unordered_map<std::string, std::string> getCompleteTitleList() {
|
||||
std::unordered_map<std::string, std::string> map;
|
||||
for (const auto& pair : titles) {
|
||||
for (const auto& title : pair.second) {
|
||||
map.emplace(StringUtils::format("0x%016llX", title.id()), title.name());
|
||||
map.emplace(string_utils::format("0x%016llX", title.id()), title.name());
|
||||
}
|
||||
}
|
||||
return map;
|
||||
|
||||
+15
-15
@@ -14,7 +14,7 @@
|
||||
static bool s_notification_led_available = false;
|
||||
|
||||
void servicesExit() {
|
||||
Account::exit();
|
||||
account::exit();
|
||||
plExit();
|
||||
romfsExit();
|
||||
}
|
||||
@@ -37,8 +37,8 @@ Result servicesInit() {
|
||||
nxst::log::error("plInitialize failed. Result code 0x%08X.", res);
|
||||
return res;
|
||||
}
|
||||
if (R_FAILED(res = Account::init())) {
|
||||
nxst::log::error("Account::init failed. Result code 0x%08X.", res);
|
||||
if (R_FAILED(res = account::init())) {
|
||||
nxst::log::error("account::init failed. Result code 0x%08X.", res);
|
||||
return res;
|
||||
}
|
||||
if (R_FAILED(res = nsInitialize())) {
|
||||
@@ -55,7 +55,7 @@ Result servicesInit() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool StringUtils::containsInvalidChar(const std::string& str) {
|
||||
bool string_utils::containsInvalidChar(const std::string& str) {
|
||||
for (unsigned char c : str) {
|
||||
if (!isascii(c))
|
||||
return true;
|
||||
@@ -63,7 +63,7 @@ bool StringUtils::containsInvalidChar(const std::string& str) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string StringUtils::format(const char* fmt, ...) {
|
||||
std::string string_utils::format(const char* fmt, ...) {
|
||||
va_list a1, a2;
|
||||
va_start(a1, fmt);
|
||||
va_copy(a2, a1);
|
||||
@@ -79,7 +79,7 @@ std::string StringUtils::format(const char* fmt, ...) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
std::string StringUtils::removeForbiddenCharacters(std::string src) {
|
||||
std::string string_utils::removeForbiddenCharacters(std::string src) {
|
||||
static constexpr std::string_view kForbidden = ".,!\\/:?*\"<>|";
|
||||
for (char& c : src) {
|
||||
if (kForbidden.find(c) != std::string_view::npos)
|
||||
@@ -114,7 +114,7 @@ static size_t encodeUtf8(char* out, char32_t cp) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
std::string StringUtils::UTF16toUTF8(const std::u16string& src) {
|
||||
std::string string_utils::UTF16toUTF8(const std::u16string& src) {
|
||||
std::string result;
|
||||
result.reserve(src.size() * 2);
|
||||
for (size_t i = 0; i < src.size(); ++i) {
|
||||
@@ -128,13 +128,13 @@ std::string StringUtils::UTF16toUTF8(const std::u16string& src) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void StringUtils::ltrim(std::string& s) {
|
||||
void string_utils::ltrim(std::string& s) {
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char c) {
|
||||
return !std::isspace(c);
|
||||
}));
|
||||
}
|
||||
|
||||
void StringUtils::rtrim(std::string& s) {
|
||||
void string_utils::rtrim(std::string& s) {
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(),
|
||||
[](unsigned char c) {
|
||||
return !std::isspace(c);
|
||||
@@ -143,13 +143,13 @@ void StringUtils::rtrim(std::string& s) {
|
||||
s.end());
|
||||
}
|
||||
|
||||
void StringUtils::trim(std::string& s) {
|
||||
void string_utils::trim(std::string& s) {
|
||||
ltrim(s);
|
||||
rtrim(s);
|
||||
}
|
||||
|
||||
// Decodes a UTF-8 string to UTF-16, handling surrogate pairs for codepoints > U+FFFF.
|
||||
std::u16string StringUtils::UTF8toUTF16(const char* src) {
|
||||
std::u16string string_utils::UTF8toUTF16(const char* src) {
|
||||
std::u16string result;
|
||||
while (*src != '\0') {
|
||||
char32_t cp = 0;
|
||||
@@ -181,7 +181,7 @@ std::u16string StringUtils::UTF8toUTF16(const char* src) {
|
||||
}
|
||||
|
||||
// Replaces Latin characters with diacritics with their ASCII base equivalents.
|
||||
std::string StringUtils::removeAccents(std::string str) {
|
||||
std::string string_utils::removeAccents(std::string str) {
|
||||
static const std::unordered_map<char16_t, char16_t> kMap = {
|
||||
{u'À', u'A'}, {u'Á', u'A'}, {u'Â', u'A'}, {u'Ã', u'A'}, {u'Ä', u'A'}, {u'Å', u'A'}, {u'Æ', u'E'},
|
||||
{u'Ç', u'C'}, {u'È', u'E'}, {u'É', u'E'}, {u'Ê', u'E'}, {u'Ë', u'E'}, {u'Ì', u'I'}, {u'Í', u'I'},
|
||||
@@ -199,10 +199,10 @@ std::string StringUtils::removeAccents(std::string str) {
|
||||
if (it != kMap.end())
|
||||
ch = it->second;
|
||||
}
|
||||
return StringUtils::UTF16toUTF8(wide);
|
||||
return string_utils::UTF16toUTF8(wide);
|
||||
}
|
||||
|
||||
std::string StringUtils::removeNotAscii(std::string str) {
|
||||
std::string string_utils::removeNotAscii(std::string str) {
|
||||
for (char& c : str) {
|
||||
if (!isascii(static_cast<unsigned char>(c)))
|
||||
c = ' ';
|
||||
@@ -210,7 +210,7 @@ std::string StringUtils::removeNotAscii(std::string str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string StringUtils::elide(const std::string& s, size_t max_chars) {
|
||||
std::string string_utils::elide(const std::string& s, size_t max_chars) {
|
||||
if (s.size() <= max_chars || max_chars < 6)
|
||||
return s;
|
||||
size_t budget = max_chars - 3;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
// Copyright (C) 2024-2026 NXST contributors
|
||||
#include <nxst/infra/fs/filesystem.hpp>
|
||||
|
||||
Result FileSystem::mount(FsFileSystem* fs, u64 title_id, AccountUid uid) {
|
||||
Result file_system::mount(FsFileSystem* fs, u64 title_id, AccountUid uid) {
|
||||
return fsOpen_SaveData(fs, title_id, uid);
|
||||
}
|
||||
|
||||
int FileSystem::mount(FsFileSystem fs) {
|
||||
int file_system::mount(FsFileSystem fs) {
|
||||
return fsdevMountDevice("save", fs);
|
||||
}
|
||||
|
||||
void FileSystem::unmount() {
|
||||
void file_system::unmount() {
|
||||
fsdevUnmountDevice("save");
|
||||
}
|
||||
|
||||
+17
-16
@@ -136,7 +136,7 @@ nxst::Result<std::string> io::backup(size_t index, AccountUid uid) {
|
||||
title.id(), title.userId().uid[1], title.userId().uid[0]);
|
||||
|
||||
nxst::FsFileSystemHandle fs_handle;
|
||||
Result res = FileSystem::mount(fs_handle.get(), title.id(), title.userId());
|
||||
Result res = file_system::mount(fs_handle.get(), title.id(), title.userId());
|
||||
if (R_FAILED(res)) {
|
||||
nxst::log::error("Failed to mount filesystem during backup with result 0x%08X. "
|
||||
"Title id: 0x%016lX; User id: 0x%lX%lX.",
|
||||
@@ -145,16 +145,16 @@ nxst::Result<std::string> io::backup(size_t index, AccountUid uid) {
|
||||
}
|
||||
fs_handle.valid = true;
|
||||
|
||||
if (FileSystem::mount(*fs_handle.get()) == -1) {
|
||||
if (file_system::mount(*fs_handle.get()) == -1) {
|
||||
nxst::log::error("Failed to mount devfs during backup. Title id: 0x%016lX; User id: 0x%lX%lX.",
|
||||
title.id(), title.userId().uid[1], title.userId().uid[0]);
|
||||
FileSystem::unmount();
|
||||
file_system::unmount();
|
||||
return nxst::Result<std::string>::failure("Failed to mount save.");
|
||||
}
|
||||
fs_handle.release(); // devfs now owns the kernel handle
|
||||
|
||||
std::string suggestion =
|
||||
StringUtils::removeNotAscii(StringUtils::removeAccents(Account::username(title.userId())));
|
||||
string_utils::removeNotAscii(string_utils::removeAccents(account::username(title.userId())));
|
||||
|
||||
io::createDirectory(title.path());
|
||||
std::string dst_path = title.path() + "/" + suggestion;
|
||||
@@ -166,13 +166,13 @@ nxst::Result<std::string> io::backup(size_t index, AccountUid uid) {
|
||||
}
|
||||
res = io::createDirectory(tmp_path);
|
||||
if (R_FAILED(res)) {
|
||||
FileSystem::unmount();
|
||||
file_system::unmount();
|
||||
nxst::log::error("Failed to create tmp dir %s.", tmp_path.c_str());
|
||||
return nxst::Result<std::string>::failure("Failed to create tmp directory.");
|
||||
}
|
||||
res = copyDirectory("save:/", tmp_path + "/");
|
||||
if (R_FAILED(res)) {
|
||||
FileSystem::unmount();
|
||||
file_system::unmount();
|
||||
io::deleteFolderRecursively(tmp_path + "/");
|
||||
nxst::log::error("Failed to copy directory to %s with result 0x%08X.", tmp_path.c_str(), res);
|
||||
return nxst::Result<std::string>::failure("Failed to backup save.");
|
||||
@@ -183,13 +183,13 @@ nxst::Result<std::string> io::backup(size_t index, AccountUid uid) {
|
||||
nxst::log::warn("Failed to remove old backup at %s.", dst_path.c_str());
|
||||
}
|
||||
if (rename(tmp_path.c_str(), dst_path.c_str()) != 0) {
|
||||
FileSystem::unmount();
|
||||
file_system::unmount();
|
||||
nxst::log::error("Failed to rename temp backup to %s.", dst_path.c_str());
|
||||
return nxst::Result<std::string>::failure("Failed to finalise backup.");
|
||||
}
|
||||
|
||||
refreshDirectories(title.id());
|
||||
FileSystem::unmount();
|
||||
file_system::unmount();
|
||||
|
||||
nxst::log::info("Backup succeeded.");
|
||||
return nxst::Result<std::string>::success(dst_path);
|
||||
@@ -268,7 +268,7 @@ nxst::Result<std::string> io::restore(size_t index, AccountUid uid, const std::s
|
||||
createSaveIfNeeded(title.id(), uid);
|
||||
|
||||
nxst::FsFileSystemHandle fs_handle;
|
||||
Result res = FileSystem::mount(fs_handle.get(), title.id(), uid);
|
||||
Result res = file_system::mount(fs_handle.get(), title.id(), uid);
|
||||
if (R_FAILED(res)) {
|
||||
nxst::log::error("Failed to mount filesystem during restore with result 0x%08X. "
|
||||
"Title id: 0x%016lX; User id: 0x%lX%lX.",
|
||||
@@ -277,22 +277,23 @@ nxst::Result<std::string> io::restore(size_t index, AccountUid uid, const std::s
|
||||
}
|
||||
fs_handle.valid = true;
|
||||
|
||||
if (FileSystem::mount(*fs_handle.get()) == -1) {
|
||||
if (file_system::mount(*fs_handle.get()) == -1) {
|
||||
nxst::log::error("Failed to mount devfs during restore. Title id: 0x%016lX; User id: 0x%lX%lX.",
|
||||
title.id(), uid.uid[1], uid.uid[0]);
|
||||
FileSystem::unmount();
|
||||
file_system::unmount();
|
||||
return nxst::Result<std::string>::failure("Failed to mount save.");
|
||||
}
|
||||
fs_handle.release(); // devfs now owns the kernel handle
|
||||
|
||||
std::string suggestion = StringUtils::removeNotAscii(StringUtils::removeAccents(Account::username(uid)));
|
||||
std::string suggestion =
|
||||
string_utils::removeNotAscii(string_utils::removeAccents(account::username(uid)));
|
||||
std::string src_path = title.path() + "/" + suggestion + "/";
|
||||
const std::string dst_path = "save:/";
|
||||
|
||||
{
|
||||
Directory src_check(src_path);
|
||||
if (!src_check.good() || src_check.size() == 0) {
|
||||
FileSystem::unmount();
|
||||
file_system::unmount();
|
||||
nxst::log::error("Restore source is empty or missing: %s", src_path.c_str());
|
||||
return nxst::Result<std::string>::failure("Restore source is empty or missing.");
|
||||
}
|
||||
@@ -300,18 +301,18 @@ nxst::Result<std::string> io::restore(size_t index, AccountUid uid, const std::s
|
||||
|
||||
auto clear_res = clearSaveRoot(dst_path);
|
||||
if (!clear_res.isOk()) {
|
||||
FileSystem::unmount();
|
||||
file_system::unmount();
|
||||
return nxst::Result<std::string>::failure(clear_res.error());
|
||||
}
|
||||
|
||||
auto extract_res = extractAndCommit(src_path, dst_path);
|
||||
if (!extract_res.isOk()) {
|
||||
FileSystem::unmount();
|
||||
file_system::unmount();
|
||||
return nxst::Result<std::string>::failure(extract_res.error());
|
||||
}
|
||||
|
||||
blinkLed(4);
|
||||
FileSystem::unmount();
|
||||
file_system::unmount();
|
||||
|
||||
nxst::log::info("Restore succeeded.");
|
||||
return nxst::Result<std::string>::success(title_name + "\nhas been restored successfully.");
|
||||
|
||||
@@ -319,7 +319,7 @@ namespace nxst {
|
||||
std::string TransferService::replaceUsername(const std::string& file_path) const {
|
||||
#ifdef __SWITCH__
|
||||
std::string username =
|
||||
StringUtils::removeNotAscii(StringUtils::removeAccents(Account::username(restore_uid)));
|
||||
string_utils::removeNotAscii(string_utils::removeAccents(account::username(restore_uid)));
|
||||
size_t last_slash = file_path.rfind('/');
|
||||
if (last_slash == std::string::npos)
|
||||
return file_path;
|
||||
|
||||
@@ -135,7 +135,7 @@ namespace ui {
|
||||
this->refreshButtons();
|
||||
this->updateHints();
|
||||
|
||||
this->header->SetUser(uid, Account::username(uid));
|
||||
this->header->SetUser(uid, account::username(uid));
|
||||
}
|
||||
|
||||
void TitlesLayout::refreshPanel() {
|
||||
@@ -144,7 +144,7 @@ namespace ui {
|
||||
int idx = this->titlesMenu->GetSelectedIndex();
|
||||
Title title;
|
||||
getTitle(title, this->current_uid, idx);
|
||||
this->panelTitle->SetText(StringUtils::elide(title.name(), 24));
|
||||
this->panelTitle->SetText(string_utils::elide(title.name(), 24));
|
||||
}
|
||||
|
||||
void TitlesLayout::refreshButtons() {
|
||||
|
||||
@@ -11,8 +11,8 @@ namespace ui {
|
||||
this->usersMenu->SetScrollbarColor(color::Primary);
|
||||
this->usersMenu->SetItemsFocusColor(color::BgSurface2);
|
||||
|
||||
for (AccountUid const& uid : Account::ids()) {
|
||||
auto item = pu::ui::elm::MenuItem::New(Account::username(uid));
|
||||
for (AccountUid const& uid : account::ids()) {
|
||||
auto item = pu::ui::elm::MenuItem::New(account::username(uid));
|
||||
item->SetColor(color::TextPrimary);
|
||||
this->usersMenu->AddItem(item);
|
||||
}
|
||||
@@ -47,7 +47,7 @@ namespace ui {
|
||||
}
|
||||
|
||||
if (Down & HidNpadButton_A) {
|
||||
AccountUid uid = Account::ids().at(this->usersMenu->GetSelectedIndex());
|
||||
AccountUid uid = account::ids().at(this->usersMenu->GetSelectedIndex());
|
||||
|
||||
if (!areTitlesLoaded()) {
|
||||
this->usersMenu->SetVisible(false);
|
||||
|
||||
Reference in New Issue
Block a user