refactoring
CI / Build NRO (push) Successful in 28s
CI / Format check (push) Successful in 33s
CI / Layering check (push) Successful in 1s

Co-authored-by: n.fedorov <mail@nfedorov.dev>
Co-committed-by: n.fedorov <mail@nfedorov.dev>
This commit was merged in pull request #1.
This commit is contained in:
2026-05-14 23:34:13 +03:00
committed by DragonSpirit
parent 844093e3e7
commit 1111f691c6
81 changed files with 3685 additions and 3036 deletions
+36
View File
@@ -0,0 +1,36 @@
// Copyright (C) 2024-2026 NXST contributors
#pragma once
#include <string>
#include <vector>
#include <switch.h>
// Hash and comparison support for AccountUid as map/unordered_map key.
namespace std {
template <> struct hash<AccountUid> {
size_t operator()(const AccountUid& a) const noexcept {
return (hash<u64>()(a.uid[0]) ^ (hash<u64>()(a.uid[1]) << 1)) >> 1;
}
};
} // namespace std
inline bool operator==(const AccountUid& x, const AccountUid& y) {
return x.uid[0] == y.uid[0] && x.uid[1] == y.uid[1];
}
inline bool operator<(const AccountUid& x, const AccountUid& y) {
return x.uid[0] != y.uid[0] ? x.uid[0] < y.uid[0] : x.uid[1] < y.uid[1];
}
struct User {
AccountUid id;
std::string name;
};
namespace account {
Result init();
void exit();
std::vector<AccountUid> ids();
std::string username(AccountUid id);
std::string iconPath(AccountUid id);
} // namespace account
+16
View File
@@ -0,0 +1,16 @@
#pragma once
namespace proto {
constexpr uint16_t kTcpPort = 8080;
constexpr uint16_t kMulticastPort = 8081;
constexpr char kMulticastGroup[] = "239.0.0.1";
constexpr size_t kBufSize = 65536;
constexpr uint32_t kMaxFilename = 4096;
constexpr uint32_t kEofSentinel = 0;
// Wire layout per file:
// [filename_len : uint32_t LE] — 0 == end-of-stream
// [filename : filename_len bytes]
// [file_size : uint64_t LE]
// [file_data : file_size bytes]
} // namespace proto
+108
View File
@@ -0,0 +1,108 @@
#pragma once
#include <string>
#include <utility>
namespace nxst {
template <class T, class E = std::string> class Result {
bool ok;
alignas(T) alignas(E) unsigned char storage[sizeof(T) > sizeof(E) ? sizeof(T) : sizeof(E)];
Result() = default;
public:
static Result success(T val) {
Result res;
res.ok = true;
new (res.storage) T(std::move(val));
return res;
}
static Result failure(E err) {
Result res;
res.ok = false;
new (res.storage) E(std::move(err));
return res;
}
~Result() {
if (ok)
reinterpret_cast<T*>(storage)->~T();
else
reinterpret_cast<E*>(storage)->~E();
}
Result(const Result& other) : ok(other.ok) {
if (ok)
new (storage) T(*reinterpret_cast<const T*>(other.storage));
else
new (storage) E(*reinterpret_cast<const E*>(other.storage));
}
Result(Result&& other) : ok(other.ok) {
if (ok)
new (storage) T(std::move(*reinterpret_cast<T*>(other.storage)));
else
new (storage) E(std::move(*reinterpret_cast<E*>(other.storage)));
}
Result& operator=(const Result&) = delete;
bool isOk() const noexcept {
return ok;
}
const T& value() const {
return *reinterpret_cast<const T*>(storage);
}
const E& error() const {
return *reinterpret_cast<const E*>(storage);
}
};
// Specialisation for Result<void>
template <class E> class Result<void, E> {
bool ok;
alignas(E) unsigned char storage[sizeof(E)];
Result() = default;
public:
static Result success() {
Result res;
res.ok = true;
return res;
}
static Result failure(E err) {
Result res;
res.ok = false;
new (res.storage) E(std::move(err));
return res;
}
~Result() {
if (!ok)
reinterpret_cast<E*>(storage)->~E();
}
Result(const Result& other) : ok(other.ok) {
if (!ok)
new (storage) E(*reinterpret_cast<const E*>(other.storage));
}
Result(Result&& other) : ok(other.ok) {
if (!ok)
new (storage) E(std::move(*reinterpret_cast<E*>(other.storage)));
}
Result& operator=(const Result&) = delete;
bool isOk() const noexcept {
return ok;
}
const E& error() const {
return *reinterpret_cast<const E*>(storage);
}
};
} // namespace nxst
+58
View File
@@ -0,0 +1,58 @@
// Copyright (C) 2024-2026 NXST contributors
#pragma once
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include <switch.h>
class Title {
public:
void init(u8 save_data_type, u64 title_id, AccountUid uid, const std::string& name,
const std::string& author);
std::string author() const;
std::pair<std::string, std::string> displayName() const;
u64 id() const;
std::string name() const;
std::string path() const;
u64 playTimeNanoseconds() const;
std::string playTime() const;
void playTimeNanoseconds(u64 ns);
u32 lastPlayedTimestamp() const;
void lastPlayedTimestamp(u32 ts);
std::string fullPath(size_t index) const;
void refreshDirectories();
u64 saveId() const;
void saveId(u64 id);
std::vector<std::string> saves() const;
u8 saveDataType() const;
AccountUid userId() const;
std::string userName() const;
private:
u64 m_id{0};
u64 m_save_id{0};
AccountUid m_uid{};
std::string m_user_name;
std::string m_name;
std::string m_safe_name;
std::string m_author;
std::string m_path;
std::vector<std::string> m_saves;
std::vector<std::string> m_full_save_paths;
u8 m_save_data_type{0};
std::pair<std::string, std::string> m_display_name;
u64 m_play_time_ns{0};
u32 m_last_played_ts{0};
};
bool areTitlesLoaded();
void loadTitles();
void sortTitles();
void rotateSortMode();
void getTitle(Title& dst, AccountUid uid, size_t i);
size_t getTitleCount(AccountUid uid);
void refreshDirectories(u64 id);
std::unordered_map<std::string, std::string> getCompleteTitleList();
+42
View File
@@ -0,0 +1,42 @@
#pragma once
#include <atomic>
#include <mutex>
#include <string>
struct TransferState {
std::atomic<bool> done{false};
std::atomic<bool> cancelled{false};
std::atomic<bool> connection_failed{false};
std::atomic<uint64_t> bytes_done{0};
std::atomic<uint64_t> bytes_total{0};
std::string status;
std::string fail_reason;
mutable std::mutex status_mutex;
void reset() {
done = false;
cancelled = false;
connection_failed = false;
bytes_done = 0;
bytes_total = 0;
fail_reason.clear();
std::lock_guard<std::mutex> lock(status_mutex);
status.clear();
}
double progress() const {
uint64_t t = bytes_total.load();
return t ? (double)bytes_done.load() / (double)t * 100.0 : 0.0;
}
std::string getStatus() const {
std::lock_guard<std::mutex> lock(status_mutex);
return status;
}
void setStatus(const std::string& s) {
std::lock_guard<std::mutex> lock(status_mutex);
status = s;
}
};
+22
View File
@@ -0,0 +1,22 @@
// Copyright (C) 2024-2026 NXST contributors
#pragma once
#include <switch.h>
void servicesExit();
Result servicesInit();
void blinkLed(u8 times);
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);
std::string UTF16toUTF8(const std::u16string& src);
void ltrim(std::string& s);
void rtrim(std::string& s);
void trim(std::string& s);
std::string removeAccents(std::string str);
std::string removeNotAscii(std::string str);
std::u16string UTF8toUTF16(const char* src);
std::string elide(const std::string& s, size_t max_chars);
} // namespace string_utils