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
-21
View File
@@ -1,21 +0,0 @@
#pragma once
#include <pu/Plutonium>
#include <UsersLayout.hpp>
#include <TitlesLayout.hpp>
namespace ui {
class MainApplication : public pu::ui::Application {
public:
using Application::Application;
PU_SMART_CTOR(MainApplication)
void OnLoad() override;
// Layout instance
UsersLayout::Ref usersLayout;
TitlesLayout::Ref titlesLayout;
};
}
-76
View File
@@ -1,76 +0,0 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef ACCOUNT_HPP
#define ACCOUNT_HPP
#include <map>
#include <string.h>
#include <string>
#include <switch.h>
#include <vector>
#define USER_ICON_SIZE 64
namespace std {
template <>
struct hash<AccountUid> {
size_t operator()(const AccountUid& a) const { return ((hash<u64>()(a.uid[0]) ^ (hash<u64>()(a.uid[1]) << 1)) >> 1); }
};
}
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, u64 y)
{
return x.uid[0] == y && x.uid[1] == y;
}
inline bool operator<(const AccountUid& x, const AccountUid& y)
{
if (x.uid[0] != y.uid[0]) return x.uid[0] < y.uid[0];
return x.uid[1] < y.uid[1];
}
struct User {
AccountUid id;
std::string name;
};
namespace Account {
Result init(void);
void exit(void);
std::vector<AccountUid> ids(void);
AccountUid selectAccount(void);
std::string username(AccountUid id);
std::string iconPath(AccountUid id);
}
#endif
-13
View File
@@ -1,13 +0,0 @@
#include <string>
#include <switch.h>
int transfer_files(size_t index, AccountUid uid);
bool isClientTransferDone();
bool isClientTransferCancelled();
bool isClientConnectionFailed();
bool isClientProgressKnown();
bool isClientWorkersIdle();
void cancelClientTransfer();
double getClientProgress();
std::string getClientStatusText();
std::string getClientFailReason();
-65
View File
@@ -1,65 +0,0 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef COMMON_HPP
#define COMMON_HPP
#include <algorithm>
#include <arpa/inet.h>
#include <codecvt>
#include <cstdio>
#include <locale>
#include <memory>
#include <netinet/in.h>
#include <stdarg.h>
#include <string.h>
#include <string>
#include <sys/socket.h>
#include <time.h>
#include <unistd.h>
#define ATEXIT(func) atexit((void (*)())func)
namespace DateTime {
std::string timeStr(void);
std::string dateTimeStr(void);
std::string logDateTime(void);
}
namespace StringUtils {
bool containsInvalidChar(const std::string& str);
std::string escapeJson(const std::string& s);
std::string format(const std::string fmt_str, ...);
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);
}
char* getConsoleIP(void);
#endif
-6
View File
@@ -1,6 +0,0 @@
#pragma once
#include <Theme.hpp>
#define COLOR(hex) pu::ui::Color::FromHex(hex)
#define BACKGROUND_COLOR theme::color::BgBase
-58
View File
@@ -1,58 +0,0 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef DIRECTORY_HPP
#define DIRECTORY_HPP
#include <dirent.h>
#include <errno.h>
#include <string>
#include <switch.h>
#include <vector>
struct DirectoryEntry {
std::string name;
bool directory;
};
class Directory {
public:
Directory(const std::string& root);
~Directory() = default;
Result error(void);
std::string entry(size_t index);
bool folder(size_t index);
bool good(void);
size_t size(void);
private:
std::vector<struct DirectoryEntry> mList;
Result mError;
bool mGood;
};
#endif
-39
View File
@@ -1,39 +0,0 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef FILESYSTEM_HPP
#define FILESYSTEM_HPP
#include "account.hpp"
#include <switch.h>
namespace FileSystem {
Result mount(FsFileSystem* fileSystem, u64 titleID, AccountUid userID);
int mount(FsFileSystem fs);
void unmount(void);
}
#endif
-55
View File
@@ -1,55 +0,0 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef IO_HPP
#define IO_HPP
#include "account.hpp"
#include "directory.hpp"
#include "title.hpp"
#include "util.hpp"
#include <dirent.h>
#include <switch.h>
#include <sys/stat.h>
#include <tuple>
#include <unistd.h>
#include <utility>
#define BUFFER_SIZE 0x80000
namespace io {
std::tuple<bool, Result, std::string> backup(size_t index, AccountUid uid);
std::tuple<bool, Result, std::string> restore(size_t index, AccountUid uid, size_t cellIndex, const std::string& nameFromCell);
Result copyDirectory(const std::string& srcPath, const std::string& dstPath);
void copyFile(const std::string& srcPath, const std::string& dstPath);
Result createDirectory(const std::string& path);
Result deleteFolderRecursively(const std::string& path);
bool directoryExists(const std::string& path);
bool fileExists(const std::string& path);
}
#endif
-85
View File
@@ -1,85 +0,0 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef LOGGER_HPP
#define LOGGER_HPP
#include "common.hpp"
#include <stdio.h>
#include <string>
class Logger {
public:
static Logger& getInstance(void)
{
static Logger mLogger;
return mLogger;
}
inline static const std::string INFO = "[INFO]";
inline static const std::string DEBUG = "[DEBUG]";
inline static const std::string ERROR = "[ERROR]";
inline static const std::string WARN = "[WARN]";
template <typename... Args>
void log(const std::string& level, const std::string& format = {}, Args... args)
{
// buffer += StringUtils::format(("[" + DateTime::logDateTime() + "] " + level + " " + format + "\n").c_str(), args...);
// buffer += StringUtils::format("%s\n", format.c_str());
// buffer += StringUtils::format("%s\n",StringUtils::format("[" + DateTime::logDateTime() + "] " + level + " " + format + "\n").c_str(), args...);
printf(StringUtils::format("[" + DateTime::logDateTime() + "] " + level + " " + format + "\n").c_str(), args...);
}
void flush(void)
{
mFile = fopen(mPath.c_str(), "a");
if (mFile != NULL) {
fprintf(mFile, buffer.c_str());
fprintf(stderr, buffer.c_str());
fclose(mFile);
}
}
private:
Logger(void) { buffer = ""; }
~Logger(void) {}
Logger(Logger const&) = delete;
void operator=(Logger const&) = delete;
#if defined(__SWITCH__)
const std::string mPath = "/switch/NXST/log.log";
#else
const std::string mPath = "log.log";
#endif
FILE* mFile;
std::string buffer;
};
#endif
-26
View File
@@ -1,26 +0,0 @@
#ifndef MAIN_HPP
#define MAIN_HPP
#include <const.h>
#include "account.hpp"
#include "title.hpp"
#include "util.hpp"
#include <memory>
#include <switch.h>
#include "logger.hpp"
typedef enum { SORT_ALPHA, SORT_LAST_PLAYED, SORT_PLAY_TIME, SORT_MODES_COUNT } sort_t;
inline float g_currentTime = 0;
inline AccountUid g_currentUId;
inline bool g_backupScrollEnabled = 0;
inline bool g_notificationLedAvailable = false;
inline bool g_shouldExitNetworkLoop = false;
inline std::string g_selectedCheatKey;
inline std::vector<std::string> g_selectedCheatCodes;
inline u32 g_username_dotsize;
inline sort_t g_sortMode = SORT_ALPHA;
inline std::string g_currentFile = "";
inline bool g_isTransferringFile = false;
inline const std::string g_emptySave = "New...";
#endif
-18
View File
@@ -1,18 +0,0 @@
#pragma once
#include <unistd.h>
struct Socket {
int fd = -1;
Socket() = default;
explicit Socket(int fd) : fd(fd) {}
~Socket() { if (fd >= 0) close(fd); }
Socket(const Socket&) = delete;
Socket& operator=(const Socket&) = delete;
Socket(Socket&& o) : fd(o.fd) { o.fd = -1; }
operator int() const { return fd; }
bool valid() const { return fd >= 0; }
void release() { fd = -1; }
};
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include <pu/Plutonium>
#include <nxst/service/transfer_service.hpp>
#include <nxst/ui/titles_layout.hpp>
#include <nxst/ui/users_layout.hpp>
namespace ui {
class MainApplication : public pu::ui::Application {
public:
using Application::Application;
PU_SMART_CTOR(MainApplication)
void OnLoad() override;
UsersLayout::Ref users_layout;
TitlesLayout::Ref titles_layout;
nxst::TransferService transfer;
};
} // namespace ui
+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();
@@ -4,22 +4,22 @@
#include <string>
struct TransferState {
std::atomic<bool> done{false};
std::atomic<bool> cancelled{false};
std::atomic<bool> connection_failed{false};
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;
std::string status;
std::string fail_reason;
mutable std::mutex status_mutex;
void reset() {
done = false;
cancelled = false;
done = false;
cancelled = false;
connection_failed = false;
bytes_done = 0;
bytes_total = 0;
bytes_done = 0;
bytes_total = 0;
fail_reason.clear();
std::lock_guard<std::mutex> lock(status_mutex);
status.clear();
+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
+32
View File
@@ -0,0 +1,32 @@
// Copyright (C) 2024-2026 NXST contributors
#pragma once
#include <string>
#include <vector>
#include <switch.h>
class Directory {
public:
explicit Directory(const std::string& path);
bool good() const {
return m_good;
}
Result error() const {
return m_error;
}
size_t size() const {
return m_entries.size();
}
std::string entry(size_t i) const;
bool folder(size_t i) const;
private:
struct Entry {
std::string name;
bool is_dir;
};
std::vector<Entry> m_entries;
Result m_error{0};
bool m_good{false};
};
+9
View File
@@ -0,0 +1,9 @@
// Copyright (C) 2024-2026 NXST contributors
#pragma once
#include <switch.h>
namespace file_system {
Result mount(FsFileSystem* fs, u64 title_id, AccountUid uid);
int mount(FsFileSystem fs);
void unmount();
} // namespace file_system
+71
View File
@@ -0,0 +1,71 @@
#pragma once
#include <cstdio>
#include <switch.h>
namespace nxst {
// RAII wrapper for FsFileSystem — auto-closes on destruction.
struct FsFileSystemHandle {
FsFileSystem fs{};
bool valid{false};
FsFileSystemHandle() = default;
~FsFileSystemHandle() {
if (valid)
fsFsClose(&fs);
} // NOLINT(modernize-use-equals-default)
FsFileSystemHandle(const FsFileSystemHandle&) = delete;
FsFileSystemHandle& operator=(const FsFileSystemHandle&) = delete;
FsFileSystem* get() {
return &fs;
}
void release() {
valid = false;
} // transfer ownership to devfs
};
// RAII wrapper for FILE* — auto-fclose on destruction.
struct FileHandle {
FILE* ptr{nullptr};
explicit FileHandle(FILE* file) : ptr(file) {}
~FileHandle() {
if (ptr != nullptr)
fclose(ptr);
} // NOLINT(modernize-use-equals-default)
FileHandle(const FileHandle&) = delete;
FileHandle& operator=(const FileHandle&) = delete;
explicit operator bool() const {
return ptr != nullptr;
}
FILE* get() const {
return ptr;
}
};
// RAII wrapper for AccountProfile — auto-closes on destruction.
struct AccountProfileHandle {
AccountProfile profile{};
bool valid{false};
AccountProfileHandle() = default;
~AccountProfileHandle() {
if (valid)
accountProfileClose(&profile);
} // NOLINT(modernize-use-equals-default)
AccountProfileHandle(const AccountProfileHandle&) = delete;
AccountProfileHandle& operator=(const AccountProfileHandle&) = delete;
AccountProfile* get() {
return &profile;
}
};
} // namespace nxst
+19
View File
@@ -0,0 +1,19 @@
// Copyright (C) 2024-2026 NXST contributors
#pragma once
#include <string>
#include <switch.h>
#include <nxst/domain/result.hpp>
namespace io {
nxst::Result<std::string> backup(size_t index, AccountUid uid);
nxst::Result<std::string> restore(size_t index, AccountUid uid, const std::string& title_name);
Result copyDirectory(const std::string& src, const std::string& dst);
void copyFile(const std::string& src, const std::string& dst);
Result createDirectory(const std::string& path);
Result deleteFolderRecursively(const std::string& path);
bool directoryExists(const std::string& path);
bool fileExists(const std::string& path);
} // namespace io
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include <unistd.h>
struct Socket {
int fd = -1;
Socket() = default;
explicit Socket(int fd) : fd(fd) {}
~Socket() {
if (fd >= 0)
close(fd);
}
Socket(const Socket&) = delete;
Socket& operator=(const Socket&) = delete;
Socket(Socket&& o) : fd(o.fd) {
o.fd = -1;
}
operator int() const {
return fd;
}
bool valid() const {
return fd >= 0;
}
void release() {
fd = -1;
}
};
+14
View File
@@ -0,0 +1,14 @@
#pragma once
// New API — use these going forward.
namespace nxst::log {
enum class Level { Debug, Info, Warn, Error };
void write(Level level, const char* fmt, ...) __attribute__((format(printf, 2, 3)));
void debug(const char* fmt, ...) __attribute__((format(printf, 1, 2)));
void info(const char* fmt, ...) __attribute__((format(printf, 1, 2)));
void warn(const char* fmt, ...) __attribute__((format(printf, 1, 2)));
void error(const char* fmt, ...) __attribute__((format(printf, 1, 2)));
} // namespace nxst::log
+113
View File
@@ -0,0 +1,113 @@
#pragma once
#include <atomic>
#include <pthread.h>
#include <string>
#include <switch.h>
#include <nxst/domain/transfer_state.hpp>
namespace nxst {
class TransferService {
public:
int startSend(size_t title_index, AccountUid uid);
void cancelSend();
bool isSendDone() const {
return sender_state.done.load();
}
bool isSendCancelled() const {
return sender_state.cancelled.load();
}
bool isSendConnectionFailed() const {
return sender_state.connection_failed.load();
}
bool isSendProgressKnown() const {
return sender_state.bytes_total.load() > 0;
}
bool isSendWorkersIdle() const {
return !sender_active.load();
}
double sendProgress() const {
return sender_state.progress();
}
std::string sendStatusText() const {
return sender_state.getStatus();
}
std::string sendFailReason() const {
return sender_state.fail_reason;
}
int startReceive(size_t title_index, AccountUid uid, std::string title_name);
void cancelReceive();
bool isReceiveDone() const {
return receiver_state.done.load();
}
bool isReceiveCancelled() const {
return receiver_state.cancelled.load();
}
bool isReceiveWorkersIdle() const {
return !receiver_accept_active.load() && !receiver_broadcast_active.load();
}
double receiveProgress() const {
return receiver_state.progress();
}
std::string receiveStatusText() const {
return receiver_state.getStatus();
}
bool restoreSucceeded() const {
return restore_ok;
}
std::string restoreError() const {
return restore_error;
}
private:
// Sender
TransferState sender_state;
std::atomic<int> sender_udp_sock{-1};
std::atomic<int> sender_tcp_sock{-1};
std::atomic<bool> sender_active{false};
// Receiver
TransferState receiver_state;
std::atomic<int> receiver_client_sock{-1};
std::atomic<int> receiver_listen_sock{-1};
std::atomic<int> receiver_bcast_sock{-1};
std::atomic<bool> receiver_accept_active{false};
std::atomic<bool> receiver_broadcast_active{false};
pthread_t receiver_bcast_thread{};
// Stored at startReceive, read after network transfer completes
size_t restore_title_index{0};
AccountUid restore_uid{};
std::string restore_title_name;
bool restore_ok{false};
std::string restore_error;
// Sender thread
struct SenderArgs {
TransferService* svc;
size_t title_index;
AccountUid uid;
};
static void* senderEntry(void* arg);
void runSender(size_t title_index, AccountUid uid);
void failSend(const std::string& reason);
int findServer(char* out_ip);
// Receiver threads
struct AcceptArgs {
TransferService* svc;
int server_fd;
};
static void* broadcastEntry(void* arg);
static void* acceptEntry(void* arg);
void runBroadcast();
void runAccept(int server_fd);
std::string replaceUsername(const std::string& file_path) const;
};
} // namespace nxst
@@ -1,13 +1,13 @@
#pragma once
#include <pu/Plutonium>
#include <Theme.hpp>
#include <ui/UiContext.hpp>
#include <account.hpp>
#include <nxst/domain/account.hpp>
#include <nxst/ui/theme.hpp>
namespace ui {
class HeaderBar {
private:
private:
pu::ui::elm::Rectangle::Ref bg;
pu::ui::elm::Rectangle::Ref divider;
pu::ui::elm::TextBlock::Ref appName;
@@ -16,14 +16,12 @@ namespace ui {
pu::ui::elm::Image::Ref avatar;
pu::ui::elm::TextBlock::Ref userName;
public:
public:
HeaderBar(pu::ui::Layout* parent, const std::string& sub = "Save Transfer") {
using namespace theme;
bg = pu::ui::elm::Rectangle::New(
0, 0, layout::ScreenW, layout::HeaderH, color::BgSurface);
divider = pu::ui::elm::Rectangle::New(
0, layout::HeaderH - 1, layout::ScreenW, 1, color::Divider);
bg = pu::ui::elm::Rectangle::New(0, 0, layout::ScreenW, layout::HeaderH, color::BgSurface);
divider = pu::ui::elm::Rectangle::New(0, layout::HeaderH - 1, layout::ScreenW, 1, color::Divider);
appName = pu::ui::elm::TextBlock::New(space::lg, 8, "NXST");
appName->SetFont(type::font(type::Title));
@@ -35,9 +33,7 @@ namespace ui {
const int chipW = 280;
const int chipX = layout::ScreenW - chipW - space::lg;
chipBg = pu::ui::elm::Rectangle::New(
chipX, 16, chipW, 40,
color::BgSurface2, radius::pill);
chipBg = pu::ui::elm::Rectangle::New(chipX, 16, chipW, 40, color::BgSurface2, radius::pill);
chipBg->SetVisible(false);
avatar = pu::ui::elm::Image::New(chipX + 4, 20, "");
@@ -65,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);
@@ -83,4 +79,4 @@ namespace ui {
subtitle->SetText(text);
}
};
}
} // namespace ui
@@ -1,8 +1,10 @@
#pragma once
#include <pu/Plutonium>
#include <Theme.hpp>
#include <vector>
#include <string>
#include <vector>
#include <pu/Plutonium>
#include <nxst/ui/theme.hpp>
namespace ui {
@@ -12,28 +14,27 @@ namespace ui {
};
class HintBar {
private:
private:
pu::ui::Layout* parent;
pu::ui::elm::Rectangle::Ref bg;
pu::ui::elm::Rectangle::Ref divider;
std::vector<pu::ui::elm::TextBlock::Ref> labels;
public:
public:
HintBar(pu::ui::Layout* p) : parent(p) {
using namespace theme;
bg = pu::ui::elm::Rectangle::New(
0, layout::ScreenH - layout::HintH,
layout::ScreenW, layout::HintH, color::BgSurface);
divider = pu::ui::elm::Rectangle::New(
0, layout::ScreenH - layout::HintH,
layout::ScreenW, 1, color::Divider);
bg = pu::ui::elm::Rectangle::New(0, layout::ScreenH - layout::HintH, layout::ScreenW,
layout::HintH, color::BgSurface);
divider = pu::ui::elm::Rectangle::New(0, layout::ScreenH - layout::HintH, layout::ScreenW, 1,
color::Divider);
parent->Add(bg);
parent->Add(divider);
}
void SetHints(const std::vector<Hint>& hints) {
using namespace theme;
for (auto& l : labels) l->SetVisible(false);
for (auto& l : labels)
l->SetVisible(false);
labels.clear();
int x = layout::ScreenW - space::lg;
@@ -52,4 +53,4 @@ namespace ui {
}
}
};
}
} // namespace ui
@@ -1,8 +1,9 @@
#pragma once
#include <pu/Plutonium>
#include <string>
#include <pu/Plutonium>
namespace theme {
using pu::ui::Color;
@@ -26,7 +27,7 @@ namespace theme {
constexpr Color Divider{0x2A, 0x33, 0x42, 0xFF};
constexpr Color FocusRing{0xE2, 0x4B, 0x55, 0xFF};
}
} // namespace color
namespace space {
constexpr int xs = 4;
@@ -35,14 +36,14 @@ namespace theme {
constexpr int lg = 24;
constexpr int xl = 32;
constexpr int xxl = 48;
}
} // namespace space
namespace radius {
constexpr int sm = 6;
constexpr int md = 12;
constexpr int lg = 20;
constexpr int pill = 9999;
}
} // namespace radius
namespace type {
constexpr int Display = 38;
@@ -54,7 +55,7 @@ namespace theme {
inline std::string font(int size) {
return "DefaultFont@" + std::to_string(size);
}
}
} // namespace type
namespace layout {
constexpr int ScreenW = 1280;
@@ -63,16 +64,16 @@ namespace theme {
constexpr int HintH = 56;
constexpr int ContentTop = HeaderH;
constexpr int ContentH = ScreenH - HeaderH - HintH;
}
} // namespace layout
namespace motion {
constexpr int FadeFrames = 20;
constexpr int SlideFrames = 14;
constexpr int SpinnerFrames = 72;
}
} // namespace motion
namespace font {
constexpr const char* Default = "Inter";
constexpr const char* Medium = "InterMedium";
}
}
} // namespace font
} // namespace theme
@@ -1,12 +1,14 @@
#include <pu/Plutonium>
#include <const.h>
#include <title.hpp>
#include <account.hpp>
#pragma once
#include <memory>
#include <unordered_map>
#include <vector>
#include <memory>
#include <ui/HeaderBar.hpp>
#include <ui/HintBar.hpp>
#include <pu/Plutonium>
#include <nxst/domain/account.hpp>
#include <nxst/domain/title.hpp>
#include <nxst/ui/header_bar.hpp>
#include <nxst/ui/hint_bar.hpp>
namespace ui {
@@ -14,8 +16,7 @@ namespace ui {
enum class TitlesAction { Transfer, Receive };
class TitlesLayout : public pu::ui::Layout {
private:
private:
pu::ui::elm::Menu::Ref titlesMenu;
std::unordered_map<AccountUid, std::vector<pu::ui::elm::MenuItem::Ref>> menuCache;
bool m_inputLocked = false;
@@ -33,6 +34,7 @@ namespace ui {
pu::ui::elm::TextBlock::Ref emptyText;
pu::ui::elm::TextBlock::Ref emptySub;
AccountUid current_uid{};
TitlesFocus focus = TitlesFocus::List;
TitlesAction action = TitlesAction::Transfer;
int lockedListIndex = 0;
@@ -43,15 +45,18 @@ namespace ui {
void runTransfer(int index, Title& title);
void runReceive(int index, Title& title);
public:
public:
TitlesLayout();
void InitTitles();
void LockInput() { m_inputLocked = true; }
void UnlockInput() { m_inputLocked = false; }
void InitTitles(AccountUid uid);
void LockInput() {
m_inputLocked = true;
}
void UnlockInput() {
m_inputLocked = false;
}
void onInput(u64 Down, u64 Up, u64 Held, pu::ui::TouchPoint Pos);
PU_SMART_CTOR(TitlesLayout)
};
}
} // namespace ui
@@ -1,12 +1,13 @@
#pragma once
#include <pu/Plutonium>
#include <Theme.hpp>
#include <util.hpp>
#include <nxst/domain/util.hpp>
#include <nxst/ui/theme.hpp>
namespace ui {
class TransferOverlay : public pu::ui::Overlay {
private:
private:
pu::ui::elm::Rectangle::Ref card;
pu::ui::elm::TextBlock::Ref titleText;
pu::ui::elm::TextBlock::Ref statusText;
@@ -20,24 +21,18 @@ namespace ui {
static constexpr int CardX = (theme::layout::ScreenW - CardW) / 2;
static constexpr int CardY = (theme::layout::ScreenH - CardH) / 2;
public:
TransferOverlay(const std::string &title)
: Overlay(0, 0, theme::layout::ScreenW, theme::layout::ScreenH, theme::color::Scrim)
{
public:
TransferOverlay(const std::string& title)
: Overlay(0, 0, theme::layout::ScreenW, theme::layout::ScreenH, theme::color::Scrim) {
using namespace theme;
card = pu::ui::elm::Rectangle::New(
CardX, CardY, CardW, CardH, color::BgSurface, radius::lg);
card = pu::ui::elm::Rectangle::New(CardX, CardY, CardW, CardH, color::BgSurface, radius::lg);
titleText = pu::ui::elm::TextBlock::New(
CardX + space::lg, CardY + space::lg, title);
titleText = pu::ui::elm::TextBlock::New(CardX + space::lg, CardY + space::lg, title);
titleText->SetFont(type::font(type::Title));
titleText->SetColor(color::TextPrimary);
statusText = pu::ui::elm::TextBlock::New(
CardX + space::lg,
CardY + space::lg + 56,
"");
statusText = pu::ui::elm::TextBlock::New(CardX + space::lg, CardY + space::lg + 56, "");
statusText->SetFont(type::font(type::Body));
statusText->SetColor(color::TextSecondary);
@@ -45,24 +40,19 @@ namespace ui {
int barY = CardY + space::lg + 56 + 56;
int barW = CardW - 2 * space::lg;
progressTrack = pu::ui::elm::Rectangle::New(
barX, barY, barW, 8, color::Divider, radius::sm);
progressTrack = pu::ui::elm::Rectangle::New(barX, barY, barW, 8, color::Divider, radius::sm);
progressBar = pu::ui::elm::ProgressBar::New(
barX, barY, barW, 8, 100.0);
progressBar = pu::ui::elm::ProgressBar::New(barX, barY, barW, 8, 100.0);
progressBar->SetProgressColor(color::Primary);
progressBar->SetBackgroundColor(color::Divider);
indeterminateText = pu::ui::elm::TextBlock::New(
barX, barY - 4, "Preparing transfer...");
indeterminateText = pu::ui::elm::TextBlock::New(barX, barY - 4, "Preparing transfer...");
indeterminateText->SetFont(type::font(type::Body));
indeterminateText->SetColor(color::TextMuted);
indeterminateText->SetVisible(false);
hintText = pu::ui::elm::TextBlock::New(
CardX + space::lg,
CardY + CardH - space::lg - 18,
"B to cancel");
hintText =
pu::ui::elm::TextBlock::New(CardX + space::lg, CardY + CardH - space::lg - 18, "B to cancel");
hintText->SetFont(type::font(type::Caption));
hintText->SetColor(color::TextMuted);
@@ -76,8 +66,8 @@ namespace ui {
}
PU_SMART_CTOR(TransferOverlay)
void SetStatus(const std::string &status) {
statusText->SetText(StringUtils::elide(status, 56));
void SetStatus(const std::string& status) {
statusText->SetText(string_utils::elide(status, 56));
}
void SetProgress(double val) {
@@ -91,4 +81,4 @@ namespace ui {
}
};
}
} // namespace ui
@@ -1,22 +1,21 @@
#include <pu/Plutonium>
#include <const.h>
#include <ui/HeaderBar.hpp>
#include <ui/HintBar.hpp>
#include <memory>
#include <pu/Plutonium>
#include <nxst/ui/header_bar.hpp>
#include <nxst/ui/hint_bar.hpp>
namespace ui {
class UsersLayout : public pu::ui::Layout {
private:
private:
pu::ui::elm::Menu::Ref usersMenu;
pu::ui::elm::Rectangle::Ref loadingBg;
pu::ui::elm::TextBlock::Ref loadingText;
std::unique_ptr<HeaderBar> header;
std::unique_ptr<HintBar> hints;
public:
public:
UsersLayout();
void onInput(u64 Down, u64 Up, u64 Held, pu::ui::TouchPoint Pos);
@@ -24,6 +23,5 @@ namespace ui {
int32_t GetCurrentIndex();
PU_SMART_CTOR(UsersLayout)
};
}
} // namespace ui
-17
View File
@@ -1,17 +0,0 @@
#pragma once
#include <cstdint>
namespace proto {
constexpr uint16_t TCP_PORT = 8080;
constexpr uint16_t MULTICAST_PORT = 8081;
constexpr char MULTICAST_GROUP[] = "239.0.0.1";
constexpr size_t BUF_SIZE = 65536;
constexpr uint32_t MAX_FILENAME = 4096;
constexpr uint32_t EOF_SENTINEL = 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]
}
-8
View File
@@ -1,8 +0,0 @@
#include <string>
int startSendingThread();
bool isServerTransferDone();
bool isServerTransferCancelled();
bool isServerWorkersIdle();
void cancelServerTransfer();
double getServerProgress();
std::string getServerStatusText();
-91
View File
@@ -1,91 +0,0 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef TITLE_HPP
#define TITLE_HPP
#include "account.hpp"
#include "filesystem.hpp"
#include "io.hpp"
#include <algorithm>
#include <stdlib.h>
#include <string>
#include <switch.h>
#include <unordered_map>
#include <utility>
#include <vector>
class Title {
public:
void init(u8 saveDataType, u64 titleid, AccountUid userID, const std::string& name, const std::string& author);
~Title() = default;
std::string author(void);
std::pair<std::string, std::string> displayName(void);
u64 id(void);
std::string name(void);
std::string path(void);
u64 playTimeNanoseconds(void);
std::string playTime(void);
void playTimeNanoseconds(u64 playTimeNanoseconds);
u32 lastPlayedTimestamp(void);
void lastPlayedTimestamp(u32 lastPlayedTimestamp);
std::string fullPath(size_t index);
void refreshDirectories(void);
u64 saveId();
void saveId(u64 id);
std::vector<std::string> saves(void);
u8 saveDataType(void);
AccountUid userId(void);
std::string userName(void);
private:
u64 mId;
u64 mSaveId;
AccountUid mUserId;
std::string mUserName;
std::string mName;
std::string mSafeName;
std::string mAuthor;
std::string mPath;
std::vector<std::string> mSaves;
std::vector<std::string> mFullSavePaths;
u8 mSaveDataType;
std::pair<std::string, std::string> mDisplayName;
u64 mPlayTimeNanoseconds;
u32 mLastPlayedTimestamp;
};
void getTitle(Title& dst, AccountUid uid, size_t i);
size_t getTitleCount(AccountUid uid);
void loadTitles(void);
bool areTitlesLoaded(void);
void sortTitles(void);
void rotateSortMode(void);
void refreshDirectories(u64 id);
std::unordered_map<std::string, std::string> getCompleteTitleList(void);
#endif
-18
View File
@@ -1,18 +0,0 @@
#pragma once
#include <pu/Plutonium>
#include <Theme.hpp>
namespace ui {
class Card {
public:
pu::ui::elm::Rectangle::Ref bg;
Card(pu::ui::Layout* parent, int x, int y, int w, int h,
pu::ui::Color color = theme::color::BgSurface,
int rad = theme::radius::lg) {
bg = pu::ui::elm::Rectangle::New(x, y, w, h, color, rad);
parent->Add(bg);
}
};
}
-12
View File
@@ -1,12 +0,0 @@
#pragma once
#include <string>
#include <optional>
#include <switch.h>
#include <account.hpp>
namespace ui {
struct UiContext {
std::optional<AccountUid> selectedUser;
std::string selectedUserName;
};
}
-53
View File
@@ -1,53 +0,0 @@
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef UTIL_HPP
#define UTIL_HPP
#include "account.hpp"
#include "common.hpp"
#include "io.hpp"
#include <switch.h>
#include <sys/stat.h>
// debug
#include <arpa/inet.h>
#include <sys/errno.h>
#include <sys/socket.h>
void servicesExit(void);
Result servicesInit(void);
HidsysNotificationLedPattern blinkLedPattern(u8 times);
void blinkLed(u8 times);
namespace StringUtils {
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 maxChars);
}
#endif