flow improvements

This commit is contained in:
2026-04-25 16:02:39 +03:00
parent 22b669fae0
commit 4ffa6ed970
21 changed files with 150 additions and 59 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

+4 -3
View File
@@ -38,8 +38,7 @@ EXEFS_SRC := exefs_src
APP_TITLE := NXST
APP_AUTHOR := DragonSpirit
APP_VERSION := 04.24.2026
ICON := icon.jpg
ICON := icon.png
#---------------------------------------------------------------------------------
# options for code generation
@@ -51,7 +50,7 @@ CFLAGS += -g -O2 -ffunction-sections \
CFLAGS += $(INCLUDE) -D__SWITCH__ -D_GNU_SOURCE=1
CXXFLAGS:= $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
CXXFLAGS:= $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17 -g
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
@@ -155,6 +154,8 @@ clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf $(TARGET).lst
cleanbuild: clean all
#---------------------------------------------------------------------------------
send: $(BUILD)
@nxlink $(TARGET).nro
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

+3
View File
@@ -1,12 +1,15 @@
#include <pu/Plutonium>
#include <const.h>
#include <title.hpp>
#include <account.hpp>
#include <unordered_map>
namespace ui {
class TitlesLayout : public pu::ui::Layout {
private:
pu::ui::elm::Menu::Ref titlesMenu;
std::unordered_map<AccountUid, pu::ui::elm::Menu::Ref> menuCache;
bool m_inputLocked = false;
public:
+14
View File
@@ -51,6 +51,20 @@ namespace ui {
void SetProgress(double val) {
progressBar->SetProgress(val);
}
void SetProgressVisible(bool visible) {
progressBar->SetVisible(visible);
if (visible) {
this->SetHeight(OvlH);
this->SetY(OvlY);
hintText->SetY(195);
} else {
static constexpr int SmallH = 160;
this->SetHeight(SmallH);
this->SetY((720 - SmallH) / 2);
hintText->SetY(120);
}
}
};
}
+2
View File
@@ -7,6 +7,8 @@ namespace ui {
private:
pu::ui::elm::Menu::Ref usersMenu;
pu::ui::elm::Rectangle::Ref loadingBg;
pu::ui::elm::TextBlock::Ref loadingText;
public:
+2 -1
View File
@@ -54,7 +54,8 @@ inline bool operator==(const AccountUid& x, u64 y)
inline bool operator<(const AccountUid& x, const AccountUid& y)
{
return x.uid[0] < y.uid[0] && x.uid[1] == y.uid[1];
if (x.uid[0] != y.uid[0]) return x.uid[0] < y.uid[0];
return x.uid[1] < y.uid[1];
}
struct User {
+1
View File
@@ -5,6 +5,7 @@ int transfer_files(size_t index, AccountUid uid);
bool isClientTransferDone();
bool isClientTransferCancelled();
bool isClientConnectionFailed();
bool isClientProgressKnown();
void cancelClientTransfer();
double getClientProgress();
std::string getClientStatusText();
+4 -3
View File
@@ -48,9 +48,10 @@ public:
template <typename... Args>
void log(const std::string& level, const std::string& format = {}, Args... args)
{
// xcbuffer += StringUtils::format(("[" + DateTime::logDateTime() + "] " + level + " " + format + "\n").c_str(), args...);
printf("%s\n", format.c_str());
// printf("%s\n",StringUtils::format("[" + DateTime::logDateTime() + "] " + level + " " + format + "\n").c_str(), 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)
+1
View File
@@ -82,6 +82,7 @@ private:
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);
+13 -13
View File
@@ -41,25 +41,25 @@ int main() {
exit(res);
}
loadTitles();
printf("main");
// First create our renderer, where one can customize SDL or other stuff's
// initialization.
auto renderer_opts = pu::ui::render::RendererInitOptions(
SDL_INIT_EVERYTHING, pu::ui::render::RendererHardwareFlags);
renderer_opts.UseImage(pu::ui::render::IMGAllFlags);
renderer_opts.UseAudio(pu::ui::render::MixerAllFlags);
renderer_opts.UseTTF();
// First create our renderer, where one can customize SDL or other stuff's
// initialization.
auto renderer_opts = pu::ui::render::RendererInitOptions(
SDL_INIT_EVERYTHING, pu::ui::render::RendererHardwareFlags);
renderer_opts.UseImage(pu::ui::render::IMGAllFlags);
renderer_opts.UseAudio(pu::ui::render::MixerAllFlags);
renderer_opts.UseTTF();
auto renderer = pu::ui::render::Renderer::New(renderer_opts);
// Create our main application from the renderer
auto main = ui::MainApplication::New(renderer);
// Create our main application from the renderer
auto main = ui::MainApplication::New(renderer);
main->Prepare();
main->Prepare();
main->Show();
main->Show();
servicesExit();
servicesExit();
return 0;
}
+23 -7
View File
@@ -11,15 +11,25 @@ static std::vector<uint64_t> accSids, devSids, bcatSids, cacheSids;
namespace ui {
extern MainApplication *mainApp;
void TitlesLayout::InitTitles() {
this->titlesMenu = pu::ui::elm::Menu::New(0, 0, 1280, COLOR("#67000000"), COLOR("#170909FF"), 94, 7);
for (size_t i = 0; i < getTitleCount(g_currentUId); i++) {
Title title;
getTitle(title, g_currentUId, i);
auto titleItem = pu::ui::elm::MenuItem::New(title.name().c_str());
titleItem->SetColor(COLOR("#FFFFFFFF"));
this->titlesMenu->AddItem(titleItem);
Logger::getInstance().log(Logger::INFO, "InitTitles");
auto it = this->menuCache.find(g_currentUId);
if (it != this->menuCache.end()) {
this->titlesMenu = it->second;
} else {
auto menu = pu::ui::elm::Menu::New(0, 0, 1280, COLOR("#67000000"), COLOR("#170909FF"), 94, 7);
for (size_t i = 0; i < getTitleCount(g_currentUId); i++) {
Title title;
getTitle(title, g_currentUId, i);
auto titleItem = pu::ui::elm::MenuItem::New(title.name().c_str());
titleItem->SetColor(COLOR("#FFFFFFFF"));
menu->AddItem(titleItem);
}
this->menuCache.emplace(g_currentUId, menu);
this->titlesMenu = menu;
}
this->Clear();
this->Add(this->titlesMenu);
this->SetBackgroundColor(BACKGROUND_COLOR);
@@ -28,6 +38,11 @@ namespace ui {
void TitlesLayout::onInput(u64 Down, u64 Up, u64 Held, pu::ui::TouchPoint Pos) {
if (m_inputLocked) return;
if (Down & HidNpadButton_B) {
mainApp->LoadLayout(mainApp->usersLayout);
return;
}
if (Down & HidNpadButton_Plus) {
cancelClientTransfer();
cancelServerTransfer();
@@ -60,6 +75,7 @@ namespace ui {
}
while (!isClientTransferDone()) {
ovl->SetStatus(getClientStatusText());
ovl->SetProgressVisible(isClientProgressKnown());
ovl->SetProgress(getClientProgress());
mainApp->CallForRender();
if (mainApp->GetButtonsDown() & HidNpadButton_B) {
+27 -4
View File
@@ -10,12 +10,22 @@ namespace ui {
this->usersMenu->SetScrollbarColor(COLOR("#170909FF"));
for (AccountUid const& uid : Account::ids()) {
auto username = pu::ui::elm::MenuItem::New(Account::username(uid) + ": " + std::to_string(getTitleCount(uid)));
username->SetColor(COLOR("#FFFFFFFF"));
this->usersMenu->AddItem(username);
auto item = pu::ui::elm::MenuItem::New(Account::username(uid));
item->SetColor(COLOR("#FFFFFFFF"));
this->usersMenu->AddItem(item);
}
this->loadingBg = pu::ui::elm::Rectangle::New(0, 0, 1280, 720, pu::ui::Color(30, 30, 30, 220));
this->loadingBg->SetVisible(false);
this->loadingText = pu::ui::elm::TextBlock::New(480, 340, "Reading game list...");
this->loadingText->SetColor(pu::ui::Color(255, 255, 255, 255));
this->loadingText->SetVisible(false);
this->SetBackgroundColor(BACKGROUND_COLOR);
this->Add(this->usersMenu);
this->Add(this->loadingBg);
this->Add(this->loadingText);
}
int32_t UsersLayout::GetCurrentIndex() {
@@ -29,8 +39,21 @@ namespace ui {
}
if (Down & HidNpadButton_A) {
printf("current index is %i\n", this->usersMenu->GetSelectedIndex());
g_currentUId = Account::ids().at(this->usersMenu->GetSelectedIndex());
if (!areTitlesLoaded()) {
this->usersMenu->SetVisible(false);
this->loadingBg->SetVisible(true);
this->loadingText->SetVisible(true);
mainApp->CallForRender();
loadTitles();
this->loadingBg->SetVisible(false);
this->loadingText->SetVisible(false);
this->usersMenu->SetVisible(true);
}
mainApp->titlesLayout->InitTitles();
mainApp->LoadLayout(mainApp->titlesLayout);
}
+15 -5
View File
@@ -31,7 +31,16 @@ static std::map<AccountUid, User> mUsers;
Result Account::init(void)
{
return accountInitialize(AccountServiceType_Application);
Result res = accountInitialize(AccountServiceType_Application);
if (R_FAILED(res)) return res;
AccountUid uids[8];
s32 count = 0;
accountListAllUsers(uids, 8, &count);
for (s32 i = 0; i < count; i++) {
Account::username(uids[i]); // populates mUsers as side effect
}
return 0;
}
void Account::exit(void)
@@ -55,11 +64,12 @@ static User getUser(AccountUid id)
AccountProfileBase profilebase;
memset(&profilebase, 0, sizeof(profilebase));
if (R_SUCCEEDED(accountGetProfile(&profile, id)) && R_SUCCEEDED(accountProfileGet(&profile, NULL, &profilebase))) {
user.name = std::string(profilebase.nickname);
if (R_SUCCEEDED(accountGetProfile(&profile, id))) {
if (R_SUCCEEDED(accountProfileGet(&profile, NULL, &profilebase))) {
user.name = std::string(profilebase.nickname);
}
accountProfileClose(&profile);
}
accountProfileClose(&profile);
return user;
}
+1
View File
@@ -30,6 +30,7 @@ static TransferState g_client_state;
bool isClientTransferDone() { return g_client_state.done.load(); }
bool isClientTransferCancelled() { return g_client_state.cancelled.load(); }
bool isClientConnectionFailed() { return g_client_state.connection_failed.load(); }
bool isClientProgressKnown() { return g_client_state.bytes_total.load() > 0; }
void cancelClientTransfer() { g_client_state.cancelled.store(true); }
double getClientProgress() { return g_client_state.progress(); }
std::string getClientStatusText() { return g_client_state.getStatus(); }
+2 -3
View File
@@ -45,10 +45,9 @@ Directory::Directory(const std::string& root)
struct DirectoryEntry de = {name, directory};
mList.push_back(de);
}
closedir(dir);
mGood = true;
}
closedir(dir);
mGood = true;
}
Result Directory::error(void)
+6
View File
@@ -62,6 +62,10 @@ void io::copyFile(const std::string& srcPath, const std::string& dstPath)
while (offset < sz) {
u32 count = fread((char*)buf, 1, BUFFER_SIZE, src);
if (count == 0) {
Logger::getInstance().log(Logger::ERROR, "fread returned 0 for file {} at offset {}/{} with errno {}. Aborting copy.", srcPath, offset, sz, errno);
break;
}
fwrite((char*)buf, 1, count, dst);
offset += count;
}
@@ -161,6 +165,7 @@ std::tuple<bool, Result, std::string> io::backup(size_t index, AccountUid uid)
if (R_SUCCEEDED(res)) {
int rc = FileSystem::mount(fileSystem);
if (rc == -1) {
fsFsClose(&fileSystem);
FileSystem::unmount();
Logger::getInstance().log(Logger::ERROR, "Failed to mount filesystem during backup. Title id: 0x%016lX; User id: 0x%lX%lX.", title.id(),
title.userId().uid[1], title.userId().uid[0]);
@@ -257,6 +262,7 @@ std::tuple<bool, Result, std::string> io::restore(size_t index, AccountUid uid,
if (R_SUCCEEDED(res)) {
int rc = FileSystem::mount(fileSystem);
if (rc == -1) {
fsFsClose(&fileSystem);
FileSystem::unmount();
Logger::getInstance().log(Logger::ERROR, "Failed to mount filesystem during restore. Title id: 0x%016lX; User id: 0x%lX%lX.", title.id(),
uid.uid[1], uid.uid[0]);
+9 -2
View File
@@ -123,7 +123,7 @@ static void receive_file(int sock, const std::string& relative_path, uint64_t fi
static void* handle_client(void* socket_desc) {
int client_socket = *(int*)socket_desc;
free(socket_desc);
delete static_cast<int*>(socket_desc);
while (true) {
uint32_t filename_len = 0;
@@ -186,7 +186,11 @@ static void* accept_and_handle(void* arg) {
if (client_socket >= 0) {
g_server_client_sock.store(client_socket);
int* pclient = new (std::nothrow) int(client_socket);
if (pclient) handle_client(pclient);
if (pclient) {
handle_client(pclient);
} else {
close(client_socket);
}
g_server_client_sock.store(-1);
}
@@ -203,6 +207,9 @@ static void* broadcast_listener(void* arg) {
g_broadcast_sock.store(udp.fd);
struct timeval tv{0, 100000}; // 100ms poll so cancel is detected quickly
setsockopt(udp, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
+9 -15
View File
@@ -28,6 +28,12 @@
#include "main.hpp"
static std::unordered_map<AccountUid, std::vector<Title>> titles;
static bool s_titlesLoaded = false;
bool areTitlesLoaded(void)
{
return s_titlesLoaded;
}
void Title::init(u8 saveDataType, u64 id, AccountUid userID, const std::string& name, const std::string& author)
{
@@ -176,25 +182,13 @@ void Title::refreshDirectories(void)
else {
Logger::getInstance().log(Logger::ERROR, "Couldn't retrieve the extdata directory list for the title " + name());
}
// save backups from configuration
// std::vector<std::string> additionalFolders = Configuration::getInstance().additionalSaveFolders(mId);
// for (std::vector<std::string>::const_iterator it = additionalFolders.begin(); it != additionalFolders.end(); ++it) {
// we have other folders to parse
// Directory list(*it);
// if (list.good()) {
// for (size_t i = 0, sz = list.size(); i < sz; i++) {
// if (list.folder(i)) {
// mSaves.push_back(list.entry(i));
// mFullSavePaths.push_back(*it + "/" + list.entry(i));
// }
// }
// }
// }
}
void loadTitles(void)
{
if (s_titlesLoaded) return;
s_titlesLoaded = true;
titles.clear();
FsSaveDataInfoReader reader;
+14 -3
View File
@@ -43,10 +43,8 @@ Result servicesInit(void)
io::createDirectory("sdmc:/switch/NXST");
io::createDirectory("sdmc:/switch/NXST/saves");
Logger::getInstance().log(Logger::INFO, "Starting Checkpoint loading...");
if (appletGetAppletType() != AppletType_Application) {
Logger::getInstance().log(Logger::WARN, "Please do not run Checkpoint in applet mode.");
Logger::getInstance().log(Logger::WARN, "Please do not run NXST in applet mode.");
}
// Result socinit = 0;
@@ -76,6 +74,19 @@ Result servicesInit(void)
return res;
}
if (R_FAILED(res = nsInitialize())) {
Logger::getInstance().log(Logger::ERROR, "nsInitialize failed. Result code 0x{:08X}.", res);
return res;
}
if (R_SUCCEEDED(res = hidsysInitialize())) {
g_notificationLedAvailable = true;
}
else {
Logger::getInstance().log(Logger::INFO, "Notification led not available. Result code 0x{:08X}.", res);
}
Logger::getInstance().log(Logger::INFO, "NXST loading completed!");
return 0;