Huge rewrite, not working yet :P

This commit is contained in:
XorTroll 2020-06-05 22:37:21 +02:00
parent 45ed6bf530
commit 12f183f306
88 changed files with 3109 additions and 3549 deletions

6
.gitmodules vendored
View file

@ -1,6 +1,6 @@
[submodule "Plutonium"] [submodule "Plutonium"]
path = Plutonium path = Plutonium
url = https://github.com/XorTroll/Plutonium url = https://github.com/XorTroll/Plutonium
[submodule "libstratosphere"] [submodule "Atmosphere-libs"]
path = libstratosphere path = Atmosphere-libs
url = https://github.com/Atmosphere-NX/libstratosphere url = https://github.com/Atmosphere-NX/Atmosphere-libs

1
Atmosphere-libs Submodule

@ -0,0 +1 @@
Subproject commit 6913aa52953f228f7abc7cc7617a6ae6baec1eca

View file

@ -4,11 +4,9 @@ export UL_MINOR := 3
export UL_MICRO := 0 export UL_MICRO := 0
export UL_VERSION := $(UL_MAJOR).$(UL_MINOR).$(UL_MICRO) export UL_VERSION := $(UL_MAJOR).$(UL_MINOR).$(UL_MICRO)
export UL_DEV := 0 export UL_DEFS := -DUL_MAJOR=$(UL_MAJOR) -DUL_MINOR=$(UL_MINOR) -DUL_MICRO=$(UL_MICRO) -DUL_VERSION=\"$(UL_VERSION)\"
export UL_DEFS := -DUL_DEV=$(UL_DEV) -DUL_MAJOR=$(UL_MAJOR) -DUL_MINOR=$(UL_MINOR) -DUL_MICRO=$(UL_MICRO) -DUL_VERSION=\"$(UL_VERSION)\" export UL_COMMON_SOURCES := ../uLaunch/source ../uLaunch/source/am ../uLaunch/source/dmi ../uLaunch/source/cfg ../uLaunch/source/db ../uLaunch/source/fs ../uLaunch/source/net ../uLaunch/source/os ../uLaunch/source/util
export UL_COMMON_SOURCES := ../uLaunch/source ../uLaunch/source/am ../uLaunch/source/cfg ../uLaunch/source/db ../uLaunch/source/fs ../uLaunch/source/net ../uLaunch/source/os ../uLaunch/source/util
export UL_COMMON_INCLUDES := ../uLaunch/include export UL_COMMON_INCLUDES := ../uLaunch/include
export UL_CXXFLAGS := -fno-rtti -fexceptions -std=gnu++17 export UL_CXXFLAGS := -fno-rtti -fexceptions -std=gnu++17
@ -36,7 +34,7 @@ make_hbtarget:
hbtarget: base make_hbtarget hbtarget: base make_hbtarget
make_daemon: make_daemon:
@$(MAKE) -C libstratosphere/ @$(MAKE) -C Atmosphere-libs/
@$(MAKE) -C uDaemon/ @$(MAKE) -C uDaemon/
@mkdir -p SdOut/atmosphere/contents/0100000000001000 @mkdir -p SdOut/atmosphere/contents/0100000000001000
@cp uDaemon/uDaemon.nsp SdOut/atmosphere/contents/0100000000001000/exefs.nsp @cp uDaemon/uDaemon.nsp SdOut/atmosphere/contents/0100000000001000/exefs.nsp

@ -1 +1 @@
Subproject commit a3c31497a7f5c7f9136fb612a9e1970500b3e8f6 Subproject commit 716afb53b69218613083133f128cbd4b64e1d707

View file

@ -92,13 +92,13 @@ Since launching this title should be impossible, it might involve ban risk. uLau
You will need devkitPro, devkitA64, libnx and all SDL2 libraries for switch development. You will need devkitPro, devkitA64, libnx and all SDL2 libraries for switch development.
Clone (**recursively!**) this repo (uses libstratosphere and Plutonium submodules) and hit `make` in root. It should build everything and generate a `titles` folder ready for use inside `SdOut`. Clone (**recursively!**) this repo (uses Atmosphere-libs and Plutonium submodules) and hit `make` in root. It should build everything and generate a `titles` folder ready for use inside `SdOut`.
Using `make dev` instead of regular `make` will compile uLaunch in debug mode, which makes the backend process display a debug console and a special menu before usual boot to perform special tasks. This is only meant to be used by developers. In order to only build a certain subproject, you can run `make` plus the subproject's name (`make daemon`, `make hbtarget` or `make menu`).
## Credits ## Credits
- SciresM for [libstratosphere](https://github.com/Atmosphere-NX/libstratosphere). - SciresM for [Atmosphere-libs](https://github.com/Atmosphere-NX/Atmosphere-libs).
- Switchbrew team for libnx and [nx-hbloader](https://github.com/switchbrew/nx-hbloader), the base of *QHbTarget processes (they're just simple wrappers of hbloader in the end) - Switchbrew team for libnx and [nx-hbloader](https://github.com/switchbrew/nx-hbloader), the base of *QHbTarget processes (they're just simple wrappers of hbloader in the end)

@ -1 +0,0 @@
Subproject commit da6eac986d7f67ca3dcc3a8df0c191ffbea4686f

View file

@ -1,7 +1,7 @@
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# pull in (modded) common stratosphere sysmodule configuration # pull in (modded) common stratosphere sysmodule configuration
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../libstratosphere/config/templates/stratosphere.mk include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../Atmosphere-libs/config/templates/stratosphere.mk
ARCH = -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE ARCH = -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE

View file

@ -1,36 +1,32 @@
#pragma once #pragma once
#include <stratosphere.hpp> #include <stratosphere.hpp>
#include <ul_Include.hpp>
#include <am/am_Application.hpp> #include <am/am_Application.hpp>
#include <algorithm>
namespace ecs namespace ecs {
{
Result Initialize(); Result Initialize();
void Exit(); void Exit();
Result RegisterExternalContent(u64 program_id, std::string exefs_path); Result RegisterExternalContent(u64 program_id, const std::string &exefs_path);
Result LaunchApplet(u64 program_id, u32 la_version, void *args, size_t args_size); Result LaunchApplet(u64 program_id, u32 la_version, void *args, size_t args_size);
Result LaunchSystemProcess(u64 program_id, std::string argv_str); Result LaunchSystemProcess(u64 program_id, const std::string &argv_str);
inline Result RegisterLaunchAsApplet(u64 program_id, u32 la_version, std::string exefs_path, void *args, size_t args_size) inline Result RegisterLaunchAsApplet(u64 program_id, u32 la_version, std::string exefs_path, void *args, size_t args_size) {
{ R_TRY(RegisterExternalContent(program_id, exefs_path));
auto rc = RegisterExternalContent(program_id, exefs_path); R_TRY(LaunchApplet(program_id, la_version, args, args_size));
if(R_SUCCEEDED(rc)) rc = LaunchApplet(program_id, la_version, args, args_size); return ResultSuccess;
return rc;
} }
inline Result RegisterLaunchAsApplication(u64 program_id, std::string exefs_path, void *args, size_t args_size, AccountUid uid) inline Result RegisterLaunchAsApplication(u64 program_id, const std::string &exefs_path, void *args, size_t args_size, AccountUid uid) {
{ R_TRY(RegisterExternalContent(program_id, exefs_path));
auto rc = RegisterExternalContent(program_id, exefs_path); R_TRY(am::ApplicationStart(program_id, false, uid, args, args_size));
if(R_SUCCEEDED(rc)) rc = am::ApplicationStart(program_id, false, uid, args, args_size); return ResultSuccess;
return rc;
} }
inline Result RegisterLaunchAsSystemProcess(u64 program_id, std::string exefs_path, std::string argv_str) inline Result RegisterLaunchAsSystemProcess(u64 program_id, const std::string &exefs_path, const std::string &argv_str) {
{ R_TRY(RegisterExternalContent(program_id, exefs_path));
auto rc = RegisterExternalContent(program_id, exefs_path); R_TRY(LaunchSystemProcess(program_id, argv_str));
if(R_SUCCEEDED(rc)) rc = LaunchSystemProcess(program_id, argv_str); return ResultSuccess;
return rc;
} }
} }

View file

@ -2,11 +2,11 @@
#pragma once #pragma once
#include <stratosphere.hpp> #include <stratosphere.hpp>
namespace ecs namespace ecs {
{
// Slightly modified version of ams's RemoteFileSystem // Slightly modified version of ams's RemoteFileSystem
class RemoteSdCardFileSystem : public ams::fs::fsa::IFileSystem { class RemoteSdCardFileSystem : public ams::fs::fsa::IFileSystem {
private: private:
FsFileSystem *base_fs; FsFileSystem *base_fs;
@ -90,5 +90,7 @@ namespace ecs
virtual ams::Result QueryEntryImpl(char *dst, size_t dst_size, const char *src, size_t src_size, ams::fs::fsa::QueryId query, const char *path) { virtual ams::Result QueryEntryImpl(char *dst, size_t dst_size, const char *src, size_t src_size, ams::fs::fsa::QueryId query, const char *path) {
return fsFsQueryEntry(this->base_fs, dst, dst_size, src, src_size, path, static_cast<FsFileSystemQueryId>(query)); return fsFsQueryEntry(this->base_fs, dst, dst_size, src, src_size, path, static_cast<FsFileSystemQueryId>(query));
} }
}; };
} }

View file

@ -3,26 +3,22 @@
#include <stratosphere.hpp> #include <stratosphere.hpp>
#include <ul_Include.hpp> #include <ul_Include.hpp>
namespace ipc namespace ipc {
{
class IPrivateService : public ams::sf::IServiceObject
{
private:
enum class CommandId class IPrivateService : public ams::sf::IServiceObject {
{
private:
enum class CommandId {
GetLatestMessage = 0 GetLatestMessage = 0
}; };
public: public:
ams::Result GetLatestMessage(const ams::sf::ClientProcessId &client_pid, ams::sf::Out<u32> out_msg);
ams::Result GetLatestMessage(ams::sf::Out<u32> msg, const ams::sf::ClientProcessId &client_pid);
public: public:
DEFINE_SERVICE_DISPATCH_TABLE {
DEFINE_SERVICE_DISPATCH_TABLE
{
MAKE_SERVICE_COMMAND_META(GetLatestMessage) MAKE_SERVICE_COMMAND_META(GetLatestMessage)
}; };
}; };
} }

View file

@ -3,12 +3,11 @@
#include <stratosphere.hpp> #include <stratosphere.hpp>
#include <ul_Include.hpp> #include <ul_Include.hpp>
namespace ipc namespace ipc {
{
class IPublicService : public ams::sf::IServiceObject
{
private:
class IPublicService : public ams::sf::IServiceObject {
private:
enum class CommandId enum class CommandId
{ {
}; };
@ -16,9 +15,9 @@ namespace ipc
public: public:
public: public:
DEFINE_SERVICE_DISPATCH_TABLE {
DEFINE_SERVICE_DISPATCH_TABLE
{
}; };
}; };
} }

View file

@ -10,33 +10,35 @@
#include <am/am_Application.hpp> #include <am/am_Application.hpp>
#include <am/am_LibraryApplet.hpp> #include <am/am_LibraryApplet.hpp>
#include <am/am_HomeMenu.hpp> #include <am/am_HomeMenu.hpp>
#include <am/am_DaemonMenuInteraction.hpp> #include <dmi/dmi_DaemonMenuInteraction.hpp>
#include <util/util_Convert.hpp> #include <util/util_Convert.hpp>
#include <cfg/cfg_Config.hpp> #include <cfg/cfg_Config.hpp>
extern "C" extern "C" {
{
u32 __nx_applet_type = AppletType_SystemApplet; u32 __nx_applet_type = AppletType_SystemApplet;
TimeServiceType __nx_time_service_type = TimeServiceType_System; TimeServiceType __nx_time_service_type = TimeServiceType_System;
#if UL_DEV
size_t __nx_heap_size = 0x3000000; // Dev builds use 48MB (still lower than official qlaunch) for debug console // uDaemon uses 8MB - while official qlaunch uses 56MB! That's 48 extra MB for other applets!
#else size_t __nx_heap_size = 0x800000;
size_t __nx_heap_size = 0x800000; // uDaemon uses 8MB - while official qlaunch uses 56MB! That's 48 extra MB for other applets
#endif
} }
// Needed by libstratosphere // Needed by libstratosphere
namespace ams
{ namespace ams {
ncm::ProgramId CurrentProgramId = ncm::SystemAppletId::Qlaunch; ncm::ProgramId CurrentProgramId = ncm::SystemAppletId::Qlaunch;
namespace result
{ namespace result {
bool CallFatalOnResultAssertion = true; bool CallFatalOnResultAssertion = true;
} }
} }
enum class USBMode : u32 enum class USBMode : u32 {
{
Invalid, Invalid,
RawRGBA, RawRGBA,
JPEG JPEG
@ -61,296 +63,234 @@ USBMode usb_mode = USBMode::Invalid;
static constexpr size_t USBPacketSize = RawRGBAScreenBufferSize + sizeof(u32); static constexpr size_t USBPacketSize = RawRGBAScreenBufferSize + sizeof(u32);
// This way the menu isn't loaded, even if nothing but the home menu is present, which outside of the debug menu is considered an invalid state ams::os::Mutex g_last_menu_msg_lock(false);
bool debug_menu = false; dmi::MenuMessage g_last_menu_msg = dmi::MenuMessage::Invalid;
ams::os::Mutex g_last_menu_msg_lock; dmi::DaemonStatus CreateStatus() {
am::MenuMessage g_last_menu_msg = am::MenuMessage::Invalid; dmi::DaemonStatus status = {};
status.selected_user = selected_uid;
am::DaemonStatus CreateStatus() if(am::ApplicationIsActive()) {
{ if(app_opened_hb) {
am::DaemonStatus status = {}; // Homebrew
memcpy(&status.selected_user, &selected_uid, sizeof(selected_uid)); status.params = hbapplaunch_flag_temp;
}
cfg::TitleType tmptype = cfg::TitleType::Invalid; else {
if(am::ApplicationIsActive()) // Regular title
{ status.app_id = am::ApplicationGetId();
tmptype = cfg::TitleType::Installed; }
if(app_opened_hb) tmptype = cfg::TitleType::Homebrew;
} }
if(tmptype == cfg::TitleType::Installed) status.app_id = am::ApplicationGetId();
else if(tmptype == cfg::TitleType::Homebrew) memcpy(&status.params, &hbapplaunch_flag_temp, sizeof(hbapplaunch_flag_temp));
return status; return status;
} }
void HandleSleep() void HandleSleep() {
{
appletStartSleepSequence(true); appletStartSleepSequence(true);
} }
Result LaunchMenu(am::MenuStartMode stmode, am::DaemonStatus status) Result LaunchMenu(dmi::MenuStartMode stmode, dmi::DaemonStatus status) {
{ R_TRY(ecs::RegisterLaunchAsApplet(config.menu_program_id, (u32)stmode, "/ulaunch/bin/uMenu/", &status, sizeof(status)));
if(debug_menu) return 0; return ResultSuccess;
return ecs::RegisterLaunchAsApplet(config.menu_program_id, (u32)stmode, "/ulaunch/bin/uMenu/", &status, sizeof(status));
} }
void HandleHomeButton() void HandleHomeButton() {
{
bool used_to_reopen_menu = false; bool used_to_reopen_menu = false;
if(am::LibraryAppletIsActive() && !am::LibraryAppletIsMenu()) if(am::LibraryAppletIsActive() && !am::LibraryAppletIsMenu()) {
{
am::LibraryAppletTerminate(); am::LibraryAppletTerminate();
auto status = CreateStatus(); auto status = CreateStatus();
UL_ASSERT(LaunchMenu(am::MenuStartMode::Menu, status)); UL_ASSERT(LaunchMenu(dmi::MenuStartMode::Menu, status));
return; return;
} }
if(am::ApplicationIsActive()) if(am::ApplicationIsActive()) {
{ if(am::ApplicationHasForeground()) {
if(am::ApplicationHasForeground())
{
am::HomeMenuSetForeground(); am::HomeMenuSetForeground();
auto status = CreateStatus(); auto status = CreateStatus();
UL_ASSERT(LaunchMenu(am::MenuStartMode::MenuApplicationSuspended, status)); UL_ASSERT(LaunchMenu(dmi::MenuStartMode::MenuApplicationSuspended, status));
used_to_reopen_menu = true; used_to_reopen_menu = true;
} }
} }
if(am::LibraryAppletIsMenu() && !used_to_reopen_menu) if(am::LibraryAppletIsMenu() && !used_to_reopen_menu) {
{ std::scoped_lock lk(g_last_menu_msg_lock);
std::scoped_lock _lock(g_last_menu_msg_lock); g_last_menu_msg = dmi::MenuMessage::HomeRequest;
g_last_menu_msg = am::MenuMessage::HomeRequest;
} }
} }
void HandleGeneralChannel() Result HandleGeneralChannel() {
{
AppletStorage sams_st; AppletStorage sams_st;
auto rc = appletPopFromGeneralChannel(&sams_st); R_TRY(appletPopFromGeneralChannel(&sams_st));
if(R_SUCCEEDED(rc)) UL_ON_SCOPE_EXIT({
{
os::SystemAppletMessage sams = {};
rc = appletStorageRead(&sams_st, 0, &sams, sizeof(sams));
appletStorageClose(&sams_st); appletStorageClose(&sams_st);
if(R_SUCCEEDED(rc)) });
{ os::SystemAppletMessage sams = {};
if(sams.magic == os::SAMSMagic) R_TRY(appletStorageRead(&sams_st, 0, &sams, sizeof(sams)));
{ if(sams.magic == os::SystemAppletMessage::Magic) {
os::GeneralChannelMessage msg = (os::GeneralChannelMessage)sams.message; auto msg = static_cast<os::GeneralChannelMessage>(sams.general_channel_message);
switch(msg) switch(msg) {
{ // Usually this doesn't happen, HOME is detected by applet messages...?
case os::GeneralChannelMessage::HomeButton: // Usually this doesn't happen, HOME is detected by applet messages...? case os::GeneralChannelMessage::HomeButton: {
{
HandleHomeButton();
break;
}
case os::GeneralChannelMessage::Shutdown:
{
appletStartShutdownSequence();
break;
}
case os::GeneralChannelMessage::Reboot:
{
appletStartRebootSequence();
break;
}
case os::GeneralChannelMessage::Sleep:
{
HandleSleep();
break;
}
default: // We don't have anything special to do for the rest
break;
}
}
}
}
}
void UpdateOperationMode()
{
// libnx, thank you so much for not exposing the actual code to get the mode via IPC ;)
// We're qlaunch, not using appletMainLoop, thus have to take care of this manually
u8 tmp_mode = 0;
UL_ASSERT(serviceDispatchOut(appletGetServiceSession_CommonStateGetter(), 5, tmp_mode));
console_mode = (AppletOperationMode)tmp_mode;
}
void HandleAppletMessage()
{
u32 nmsg = 0;
auto rc = appletGetMessage(&nmsg);
if(R_SUCCEEDED(rc))
{
os::AppletMessage msg = (os::AppletMessage)nmsg;
switch(msg)
{
case os::AppletMessage::HomeButton:
{
HandleHomeButton(); HandleHomeButton();
break; break;
} }
case os::AppletMessage::SdCardOut: case os::GeneralChannelMessage::Shutdown: {
{
// SD card out? Cya!
appletStartShutdownSequence(); appletStartShutdownSequence();
break; break;
} }
case os::AppletMessage::PowerButton: case os::GeneralChannelMessage::Reboot: {
{ appletStartRebootSequence();
break;
}
case os::GeneralChannelMessage::Sleep: {
HandleSleep(); HandleSleep();
break; break;
} }
case os::AppletMessage::ChangeOperationMode: // We don't have anything special to do for the rest
{
UpdateOperationMode();
break;
}
default: default:
break; break;
} }
} }
svcSleepThread(100'000'000L); return ResultSuccess;
}
Result UpdateOperationMode() {
// Thank you so much libnx for not exposing the actual call to get the mode via IPC :P
// We're qlaunch, not using appletMainLoop, thus we have to take care of this manually...
u8 tmp_mode = 0;
R_TRY(serviceDispatchOut(appletGetServiceSession_CommonStateGetter(), 5, tmp_mode));
console_mode = static_cast<AppletOperationMode>(tmp_mode);
return ResultSuccess;
}
Result HandleAppletMessage()
{
u32 raw_msg = 0;
R_TRY(appletGetMessage(&raw_msg));
auto msg = static_cast<os::AppletMessage>(raw_msg);
switch(msg) {
case os::AppletMessage::HomeButton: {
HandleHomeButton();
break;
}
case os::AppletMessage::SdCardOut: {
// Power off, since uMenu's UI relies on the SD card, so trying to use uMenu without the SD is quite risky...
appletStartShutdownSequence();
break;
}
case os::AppletMessage::PowerButton: {
HandleSleep();
break;
}
case os::AppletMessage::ChangeOperationMode: {
UpdateOperationMode();
break;
}
default:
break;
}
return ResultSuccess;
} }
void HandleMenuMessage() void HandleMenuMessage()
{ {
if(am::LibraryAppletIsMenu()) if(am::LibraryAppletIsMenu()) {
{ dmi::DaemonMessageReader reader;
am::DaemonCommandReader reader; if(reader) {
if(reader) switch(reader.GetValue()) {
{ case dmi::DaemonMessage::SetSelectedUser: {
switch(reader.GetMessage())
{
case am::DaemonMessage::SetSelectedUser:
{
selected_uid = reader.Read<AccountUid>(); selected_uid = reader.Read<AccountUid>();
reader.FinishRead(); reader.FinishRead();
break; break;
} }
case am::DaemonMessage::LaunchApplication: case dmi::DaemonMessage::LaunchApplication: {
{
auto app_id = reader.Read<u64>(); auto app_id = reader.Read<u64>();
reader.FinishRead(); reader.FinishRead();
if(am::ApplicationIsActive()) if(am::ApplicationIsActive()) {
{ dmi::DaemonResultWriter rc(RES_VALUE(Daemon, ApplicationActive));
am::DaemonCommandResultWriter res(RES_VALUE(Daemon, ApplicationActive));
res.FinishWrite();
} }
else if(!accountUidIsValid(&selected_uid)) else if(!accountUidIsValid(&selected_uid)) {
{ dmi::DaemonResultWriter rc(RES_VALUE(Daemon, InvalidSelectedUser));
am::DaemonCommandResultWriter res(RES_VALUE(Daemon, InvalidSelectedUser));
res.FinishWrite();
} }
else if(titlelaunch_flag > 0) else if(titlelaunch_flag > 0) {
{ dmi::DaemonResultWriter rc(RES_VALUE(Daemon, AlreadyQueued));
am::DaemonCommandResultWriter res(RES_VALUE(Daemon, AlreadyQueued));
res.FinishWrite();
} }
else else {
{
titlelaunch_flag = app_id; titlelaunch_flag = app_id;
am::DaemonCommandResultWriter res(0); dmi::DaemonResultWriter rc(ResultSuccess);
res.FinishWrite();
} }
break; break;
} }
case am::DaemonMessage::ResumeApplication: case dmi::DaemonMessage::ResumeApplication: {
{
reader.FinishRead(); reader.FinishRead();
if(!am::ApplicationIsActive()) if(!am::ApplicationIsActive()) {
{ dmi::DaemonResultWriter rc(RES_VALUE(Daemon, ApplicationNotActive));
am::DaemonCommandResultWriter res(RES_VALUE(Daemon, ApplicationNotActive));
res.FinishWrite();
} }
else else {
{
am::ApplicationSetForeground(); am::ApplicationSetForeground();
am::DaemonCommandResultWriter res(0); dmi::DaemonResultWriter rc(ResultSuccess);
res.FinishWrite();
} }
break; break;
} }
case am::DaemonMessage::TerminateApplication: case dmi::DaemonMessage::TerminateApplication: {
{
reader.FinishRead(); reader.FinishRead();
am::ApplicationTerminate(); am::ApplicationTerminate();
app_opened_hb = false; app_opened_hb = false;
break; break;
} }
case am::DaemonMessage::LaunchHomebrewLibraryApplet: case dmi::DaemonMessage::LaunchHomebrewLibraryApplet: {
{
hblaunch_flag = reader.Read<hb::HbTargetParams>(); hblaunch_flag = reader.Read<hb::HbTargetParams>();
reader.FinishRead(); reader.FinishRead();
break; break;
} }
case am::DaemonMessage::LaunchHomebrewApplication: case dmi::DaemonMessage::LaunchHomebrewApplication: {
{
auto app_id = reader.Read<u64>(); auto app_id = reader.Read<u64>();
auto ipt = reader.Read<hb::HbTargetParams>(); auto ipt = reader.Read<hb::HbTargetParams>();
reader.FinishRead(); reader.FinishRead();
if(am::ApplicationIsActive()) if(am::ApplicationIsActive()) {
{ dmi::DaemonResultWriter rc(RES_VALUE(Daemon, ApplicationActive));
am::DaemonCommandResultWriter res(RES_VALUE(Daemon, ApplicationActive));
res.FinishWrite();
} }
else if(!accountUidIsValid(&selected_uid)) else if(!accountUidIsValid(&selected_uid)) {
{ dmi::DaemonResultWriter rc(RES_VALUE(Daemon, InvalidSelectedUser));
am::DaemonCommandResultWriter res(RES_VALUE(Daemon, InvalidSelectedUser));
res.FinishWrite();
} }
else if(titlelaunch_flag > 0) else if(titlelaunch_flag > 0) {
{ dmi::DaemonResultWriter rc(RES_VALUE(Daemon, AlreadyQueued));
am::DaemonCommandResultWriter res(RES_VALUE(Daemon, AlreadyQueued));
res.FinishWrite();
} }
else else {
{ hbapplaunch_flag = ipt;
memcpy(&hbapplaunch_flag, &ipt, sizeof(ipt)); hbapplaunch_flag_temp = ipt;
memcpy(&hbapplaunch_flag_temp, &ipt, sizeof(ipt));
titlelaunch_flag = app_id; titlelaunch_flag = app_id;
am::DaemonCommandResultWriter res(0); dmi::DaemonResultWriter rc(ResultSuccess);
res.FinishWrite();
} }
break; break;
} }
case am::DaemonMessage::OpenWebPage: case dmi::DaemonMessage::OpenWebPage: {
{
webapplet_flag = reader.Read<WebCommonConfig>(); webapplet_flag = reader.Read<WebCommonConfig>();
reader.FinishRead(); reader.FinishRead();
break; break;
} }
case am::DaemonMessage::GetSelectedUser: case dmi::DaemonMessage::GetSelectedUser: {
{
reader.FinishRead(); reader.FinishRead();
am::DaemonCommandResultWriter res(0); dmi::DaemonResultWriter rc(ResultSuccess);
res.Write<AccountUid>(selected_uid); rc.Write<AccountUid>(selected_uid);
res.FinishWrite();
break; break;
} }
case am::DaemonMessage::OpenAlbum: case dmi::DaemonMessage::OpenAlbum:{
{
reader.FinishRead(); reader.FinishRead();
album_flag = true; album_flag = true;
break; break;
} }
case am::DaemonMessage::RestartMenu: case dmi::DaemonMessage::RestartMenu: {
{
reader.FinishRead(); reader.FinishRead();
menu_restart_flag = true;
menu_restart_flag = true;
break; break;
} }
default: default:
@ -360,10 +300,9 @@ void HandleMenuMessage()
} }
} }
namespace namespace {
{
struct ServerOptions struct ServerOptions {
{
static const size_t PointerBufferSize = 0x400; static const size_t PointerBufferSize = 0x400;
static const size_t MaxDomains = 0x40; static const size_t MaxDomains = 0x40;
static const size_t MaxDomainObjects = 0x40; static const size_t MaxDomainObjects = 0x40;
@ -380,10 +319,10 @@ namespace
ams::sf::hipc::ServerManager<NumServers, ServerOptions, MaxSessions> daemon_ipc_manager; ams::sf::hipc::ServerManager<NumServers, ServerOptions, MaxSessions> daemon_ipc_manager;
} }
static inline Result capsscCommand1204(void *buf, size_t buf_size, u64 *out_jpeg_size) static inline Result capsscCommand1204(void *buf, size_t buf_size, u64 *out_jpeg_size) {
{
struct { struct {
u32 a; u32 a;
u64 b; u64 b;
@ -394,19 +333,16 @@ static inline Result capsscCommand1204(void *buf, size_t buf_size, u64 *out_jpeg
); );
} }
namespace impl namespace impl {
{
void IPCManagerThread(void *arg) void IPCManagerThread(void *arg) {
{
UL_ASSERT(daemon_ipc_manager.RegisterServer<ipc::IPrivateService>(PrivateServiceName, MaxPrivateSessions).GetValue()); UL_ASSERT(daemon_ipc_manager.RegisterServer<ipc::IPrivateService>(PrivateServiceName, MaxPrivateSessions).GetValue());
UL_ASSERT(daemon_ipc_manager.RegisterServer<ipc::IPublicService>(PublicServiceName, MaxPublicSessions).GetValue()); UL_ASSERT(daemon_ipc_manager.RegisterServer<ipc::IPublicService>(PublicServiceName, MaxPublicSessions).GetValue());
daemon_ipc_manager.LoopProcess(); daemon_ipc_manager.LoopProcess();
} }
void USBViewerRGBAThread(void *arg) void USBViewerRGBAThread(void *arg) {
{ while(true) {
while(true)
{
bool tmp_flag; bool tmp_flag;
appletGetLastForegroundCaptureImageEx(usb_buf_read, RawRGBAScreenBufferSize, &tmp_flag); appletGetLastForegroundCaptureImageEx(usb_buf_read, RawRGBAScreenBufferSize, &tmp_flag);
appletUpdateLastForegroundCaptureImage(); appletUpdateLastForegroundCaptureImage();
@ -415,10 +351,8 @@ namespace impl
} }
} }
void USBViewerJPEGThread(void *arg) void USBViewerJPEGThread(void *arg) {
{ while(true) {
while(true)
{
u64 tmp_size; u64 tmp_size;
capsscCommand1204(usb_buf_read, RawRGBAScreenBufferSize, &tmp_size); capsscCommand1204(usb_buf_read, RawRGBAScreenBufferSize, &tmp_size);
usbCommsWrite(usb_buf, USBPacketSize); usbCommsWrite(usb_buf, USBPacketSize);
@ -426,53 +360,46 @@ namespace impl
} }
} }
void PrepareUSBViewer() void PrepareUSBViewer() {
{ usb_buf = new (std::align_val_t(0x1000), std::nothrow) u8[USBPacketSize]();
usb_buf = new (std::align_val_t(0x1000)) u8[USBPacketSize]();
usb_buf_read = usb_buf + sizeof(u32); usb_buf_read = usb_buf + sizeof(u32);
u64 tmp_size; u64 tmp_size;
auto rc = capsscCommand1204(usb_buf_read, RawRGBAScreenBufferSize, &tmp_size); if(R_SUCCEEDED(capsscCommand1204(usb_buf_read, RawRGBAScreenBufferSize, &tmp_size))) {
if(R_SUCCEEDED(rc)) usb_mode = USBMode::JPEG; usb_mode = USBMode::JPEG;
else }
{ else {
usb_mode = USBMode::RawRGBA; usb_mode = USBMode::RawRGBA;
capsscExit(); capsscExit();
} }
*(u32*)usb_buf = static_cast<u32>(usb_mode); *reinterpret_cast<u32*>(usb_buf) = static_cast<u32>(usb_mode);
} }
void LoopUpdate() void LoopUpdate() {
{
HandleGeneralChannel(); HandleGeneralChannel();
HandleAppletMessage(); HandleAppletMessage();
HandleMenuMessage(); HandleMenuMessage();
bool sth_done = false; bool sth_done = false;
if(webapplet_flag.version > 0) // A valid version in this config is always >= 0x20000 // A valid version in this config is always >= 0x20000
{ if(webapplet_flag.version > 0) {
if(!am::LibraryAppletIsActive()) if(!am::LibraryAppletIsActive()) {
{
UL_ASSERT(am::WebAppletStart(&webapplet_flag)); UL_ASSERT(am::WebAppletStart(&webapplet_flag));
sth_done = true; sth_done = true;
memset(&webapplet_flag, 0, sizeof(webapplet_flag)); webapplet_flag = {};
} }
} }
if(menu_restart_flag) if(menu_restart_flag) {
{ if(!am::LibraryAppletIsActive()) {
if(!am::LibraryAppletIsActive())
{
auto status = CreateStatus(); auto status = CreateStatus();
UL_ASSERT(LaunchMenu(am::MenuStartMode::StartupScreen, status)) UL_ASSERT(LaunchMenu(dmi::MenuStartMode::StartupScreen, status));
sth_done = true; sth_done = true;
menu_restart_flag = false; menu_restart_flag = false;
} }
} }
if(album_flag) if(album_flag) {
{ if(!am::LibraryAppletIsActive()) {
if(!am::LibraryAppletIsActive())
{
u8 albumflag = 2; u8 albumflag = 2;
UL_ASSERT(am::LibraryAppletStart(AppletId_photoViewer, 0x10000, &albumflag, sizeof(albumflag))); UL_ASSERT(am::LibraryAppletStart(AppletId_photoViewer, 0x10000, &albumflag, sizeof(albumflag)));
@ -480,12 +407,9 @@ namespace impl
album_flag = false; album_flag = false;
} }
} }
if(titlelaunch_flag > 0) if(titlelaunch_flag > 0) {
{ if(!am::LibraryAppletIsActive()) {
if(!am::LibraryAppletIsActive()) if(strlen(hbapplaunch_flag.nro_path)) {
{
if(strlen(hbapplaunch_flag.nro_path))
{
auto params = hb::HbTargetParams::Create(hbapplaunch_flag.nro_path, hbapplaunch_flag.nro_argv, false); auto params = hb::HbTargetParams::Create(hbapplaunch_flag.nro_path, hbapplaunch_flag.nro_argv, false);
UL_ASSERT(ecs::RegisterLaunchAsApplication(titlelaunch_flag, "/ulaunch/bin/uHbTarget/app/", &params, sizeof(params), selected_uid)); UL_ASSERT(ecs::RegisterLaunchAsApplication(titlelaunch_flag, "/ulaunch/bin/uHbTarget/app/", &params, sizeof(params), selected_uid));
sth_done = true; sth_done = true;
@ -493,81 +417,77 @@ namespace impl
titlelaunch_flag = 0; titlelaunch_flag = 0;
hbapplaunch_flag.nro_path[0] = '\0'; hbapplaunch_flag.nro_path[0] = '\0';
} }
else else {
{
UL_ASSERT(am::ApplicationStart(titlelaunch_flag, false, selected_uid)); UL_ASSERT(am::ApplicationStart(titlelaunch_flag, false, selected_uid));
sth_done = true; sth_done = true;
titlelaunch_flag = 0; titlelaunch_flag = 0;
} }
} }
} }
if(strlen(hblaunch_flag.nro_path)) if(strlen(hblaunch_flag.nro_path)) {
{ if(!am::LibraryAppletIsActive()) {
if(!am::LibraryAppletIsActive())
{
auto params = hb::HbTargetParams::Create(hblaunch_flag.nro_path, hblaunch_flag.nro_argv, false); auto params = hb::HbTargetParams::Create(hblaunch_flag.nro_path, hblaunch_flag.nro_argv, false);
UL_ASSERT(ecs::RegisterLaunchAsApplet(config.homebrew_applet_program_id, 0, "/ulaunch/bin/uHbTarget/applet/", &params, sizeof(params))); UL_ASSERT(ecs::RegisterLaunchAsApplet(config.homebrew_applet_program_id, 0, "/ulaunch/bin/uHbTarget/applet/", &params, sizeof(params)));
sth_done = true; sth_done = true;
hblaunch_flag.nro_path[0] = '\0'; hblaunch_flag.nro_path[0] = '\0';
} }
} }
if(!am::LibraryAppletIsActive()) if(!am::LibraryAppletIsActive()) {
{
auto cur_id = am::LibraryAppletGetId(); auto cur_id = am::LibraryAppletGetId();
if((cur_id == AppletId_web) || (cur_id == AppletId_photoViewer) || (cur_id == config.homebrew_applet_program_id)) if((cur_id == AppletId_web) || (cur_id == AppletId_photoViewer) || (cur_id == config.homebrew_applet_program_id)) {
{
auto status = CreateStatus(); auto status = CreateStatus();
UL_ASSERT(LaunchMenu(am::MenuStartMode::Menu, status)); UL_ASSERT(LaunchMenu(dmi::MenuStartMode::Menu, status));
sth_done = true; sth_done = true;
} }
} }
if(!sth_done && !debug_menu) if(!sth_done) {
{
// If nothing was done, but nothing is active... An application or applet might have crashed, terminated, failed to launch... // If nothing was done, but nothing is active... An application or applet might have crashed, terminated, failed to launch...
// No matter what is it, we reopen Menu in launch-error mode. // No matter what is it, we reopen Menu in launch-error mode.
if(!am::ApplicationIsActive() && !am::LibraryAppletIsActive()) if(!am::ApplicationIsActive() && !am::LibraryAppletIsActive()) {
{
auto status = CreateStatus(); auto status = CreateStatus();
UL_ASSERT(LaunchMenu(am::MenuStartMode::MenuLaunchFailure, status)); UL_ASSERT(LaunchMenu(dmi::MenuStartMode::MenuLaunchFailure, status));
} }
} }
svcSleepThread(10'000'000);
} }
Result LaunchIPCManagerThread() Result LaunchIPCManagerThread() {
{
R_TRY(threadCreate(&ipc_thr, &IPCManagerThread, nullptr, nullptr, 0x4000, 0x2b, -2)); R_TRY(threadCreate(&ipc_thr, &IPCManagerThread, nullptr, nullptr, 0x4000, 0x2b, -2));
R_TRY(threadStart(&ipc_thr)); R_TRY(threadStart(&ipc_thr));
return 0; return ResultSuccess;
} }
Result LaunchUSBViewerThread() Result LaunchUSBViewerThread() {
{ switch(usb_mode) {
switch(usb_mode) case USBMode::RawRGBA: {
{
case USBMode::RawRGBA:
R_TRY(threadCreate(&usb_thr, &USBViewerRGBAThread, nullptr, nullptr, 0x4000, 0x2b, -2)); R_TRY(threadCreate(&usb_thr, &USBViewerRGBAThread, nullptr, nullptr, 0x4000, 0x2b, -2));
break; break;
case USBMode::JPEG: }
case USBMode::JPEG: {
R_TRY(threadCreate(&usb_thr, &USBViewerJPEGThread, nullptr, nullptr, 0x4000, 0x2b, -2)); R_TRY(threadCreate(&usb_thr, &USBViewerJPEGThread, nullptr, nullptr, 0x4000, 0x2b, -2));
break; break;
}
default: default:
return 0; return ResultSuccess;
} }
R_TRY(threadStart(&usb_thr)); R_TRY(threadStart(&usb_thr));
return 0; return ResultSuccess;
} }
void Initialize() void Initialize() {
{ UL_ASSERT(setsysInitialize());
ams::hos::SetVersionForLibnx(); SetSysFirmwareVersion fwver = {};
UL_ASSERT(setsysGetFirmwareVersion(&fwver));
UL_ASSERT(nsInitialize()) hosversionSet(MAKEHOSVERSION(fwver.major, fwver.minor, fwver.micro));
UL_ASSERT(pminfoInitialize()) setsysExit();
UL_ASSERT(appletLoadAndApplyIdlePolicySettings())
UL_ASSERT(nsInitialize());
UL_ASSERT(pminfoInitialize());
UL_ASSERT(appletLoadAndApplyIdlePolicySettings());
UpdateOperationMode(); UpdateOperationMode();
UL_ASSERT(ecs::Initialize()) UL_ASSERT(ecs::Initialize());
UL_ASSERT(db::Mount()) UL_ASSERT(db::Mount());
// Remove old password files // Remove old password files
fs::DeleteDirectory(UL_BASE_DB_DIR "/user"); fs::DeleteDirectory(UL_BASE_DB_DIR "/user");
@ -587,76 +507,23 @@ namespace impl
config = cfg::EnsureConfig(); config = cfg::EnsureConfig();
am::LibraryAppletSetMenuAppletId(am::LibraryAppletGetAppletIdForProgramId(config.menu_program_id)); am::LibraryAppletSetMenuAppletId(am::LibraryAppletGetAppletIdForProgramId(config.menu_program_id));
if(config.viewer_usb_enabled) if(config.viewer_usb_enabled) {
{
UL_ASSERT(usbCommsInitialize()); UL_ASSERT(usbCommsInitialize());
UL_ASSERT(capsscInitialize()); UL_ASSERT(capsscInitialize());
PrepareUSBViewer(); PrepareUSBViewer();
UL_ASSERT(LaunchUSBViewerThread()); UL_ASSERT(LaunchUSBViewerThread());
} }
UL_ASSERT(LaunchIPCManagerThread()) UL_ASSERT(LaunchIPCManagerThread());
#if UL_DEV
// Debug testing mode
debug_menu = true;
consoleInit(nullptr);
CONSOLE_FMT("Welcome to uDaemon -> debug mode menu")
CONSOLE_FMT("")
CONSOLE_FMT("(A) -> Dump system save data to sd:/ulaunch/save_dump")
CONSOLE_FMT("(B) -> Delete uLaunch's saved content in save data (official HOME menu's content won't be touched)")
CONSOLE_FMT("(X) -> Reboot system")
CONSOLE_FMT("(Y) -> Continue to uMenu (launch normally)")
CONSOLE_FMT("")
while(true)
{
hidScanInput();
auto k = hidKeysDown(CONTROLLER_P1_AUTO);
if(k & KEY_A)
{
db::Mount();
fs::CopyDirectory(UL_DB_MOUNT_PATH, UL_BASE_SD_DIR "/save_dump");
db::Unmount();
CONSOLE_FMT(" - Dump done.")
}
else if(k & KEY_B)
{
db::Mount();
fs::DeleteDirectory(UL_BASE_DB_DIR);
db::Commit();
fs::CreateDirectory(UL_BASE_DB_DIR);
db::Commit();
db::Unmount();
CONSOLE_FMT(" - Cleanup done.")
}
else if(k & KEY_X)
{
CONSOLE_FMT(" - Rebooting...")
svcSleepThread(200'000'000);
appletStartRebootSequence();
}
else if(k & KEY_Y)
{
CONSOLE_FMT(" - Proceeding with launch...")
svcSleepThread(100'000'000);
break;
}
LoopUpdate();
svcSleepThread(10'000'000);
}
debug_menu = false;
#endif
} }
void Exit() void Exit() {
{ if(config.viewer_usb_enabled) {
if(config.viewer_usb_enabled)
{
usbCommsExit(); usbCommsExit();
if(usb_mode == USBMode::JPEG) capsscExit(); if(usb_mode == USBMode::JPEG) {
operator delete[](usb_buf, std::align_val_t(0x1000)); capsscExit();
}
operator delete[](usb_buf, std::align_val_t(0x1000), std::nothrow);
} }
nsExit(); nsExit();
@ -664,26 +531,25 @@ namespace impl
ecs::Exit(); ecs::Exit();
db::Unmount(); db::Unmount();
} }
} }
// Daemon handles basic qlaunch functionality and serves as a back-end for uLaunch, communicating with Menu front-end when neccessary. // uDaemon handles basic qlaunch functionality and serves as a back-end for uLaunch, communicating with uMenu front-end when neccessary.
int main()
{ int main() {
impl::Initialize(); impl::Initialize();
// Cache everything on startup // Cache everything on startup
cfg::CacheEverything(); cfg::CacheEverything();
auto status = CreateStatus(); auto status = CreateStatus();
UL_ASSERT(LaunchMenu(am::MenuStartMode::StartupScreen, status)) UL_ASSERT(LaunchMenu(dmi::MenuStartMode::StartupScreen, status));
while(true) // Loop forever - qlaunch should NEVER terminate
{ while(true) {
impl::LoopUpdate(); impl::LoopUpdate();
svcSleepThread(10'000'000);
} }
impl::Exit(); impl::Exit();
return 0; return 0;
} }

View file

@ -1,18 +1,15 @@
#include <ecs/ecs_ExternalContent.hpp> #include <ecs/ecs_ExternalContent.hpp>
#include <ecs/ecs_RemoteFileSystem.hpp> #include <ecs/ecs_RemoteFileSystem.hpp>
#include <map>
#include <stratosphere/fssrv/fssrv_interface_adapters.hpp> #include <stratosphere/fssrv/fssrv_interface_adapters.hpp>
#include <am/am_Application.hpp>
#include <am/am_LibraryApplet.hpp> #include <am/am_LibraryApplet.hpp>
#include <am/am_HomeMenu.hpp> #include <am/am_HomeMenu.hpp>
#include <am/am_DaemonMenuInteraction.hpp>
namespace ecs namespace ecs {
{
namespace namespace {
{
struct ServerOptions // fsp-srv's options, since we're hosting FileSystem sessions
{ struct ServerOptions {
static constexpr size_t PointerBufferSize = 0x800; static constexpr size_t PointerBufferSize = 0x800;
static constexpr size_t MaxDomains = 0x40; static constexpr size_t MaxDomains = 0x40;
static constexpr size_t MaxDomainObjects = 0x4000; static constexpr size_t MaxDomainObjects = 0x4000;
@ -23,53 +20,43 @@ namespace ecs
bool initialized = false; bool initialized = false;
Thread manager_process_thread; Thread manager_process_thread;
ams::sf::hipc::ServerManager<MaxServers, ServerOptions> manager_instance; ams::sf::hipc::ServerManager<MaxServers, ServerOptions> manager_instance;
} }
static void ECSManagerThread(void *arg) static void ECSManagerThread(void *arg) {
{
manager_instance.LoopProcess(); manager_instance.LoopProcess();
} }
Result Initialize() Result Initialize() {
{ if(initialized) {
if(initialized) return 0; return ResultSuccess;
auto rc = ldrShellInitialize();
if(R_SUCCEEDED(rc)) rc = pmshellInitialize();
initialized = R_SUCCEEDED(rc);
if(initialized)
{
R_TRY(threadCreate(&manager_process_thread, &ECSManagerThread, nullptr, nullptr, 0x8000, 0x2b, -2));
R_TRY(threadStart(&manager_process_thread));
} }
return rc; R_TRY(ldrShellInitialize());
R_TRY(pmshellInitialize());
R_TRY(threadCreate(&manager_process_thread, &ECSManagerThread, nullptr, nullptr, 0x8000, 0x2b, -2));
R_TRY(threadStart(&manager_process_thread));
return ResultSuccess;
} }
void Exit() void Exit() {
{ threadWaitForExit(&manager_process_thread);
if(initialized) pmshellExit();
{ ldrShellExit();
threadWaitForExit(&manager_process_thread);
pmshellExit();
ldrShellExit();
initialized = false;
}
} }
static inline Result ldrShellAtmosphereRegisterExternalCode(u64 app_id, Handle *out_h) static inline Result ldrShellAtmosphereRegisterExternalCode(u64 app_id, Handle *out_h) {
{
return serviceDispatchIn(ldrShellGetServiceSession(), 65000, app_id, return serviceDispatchIn(ldrShellGetServiceSession(), 65000, app_id,
.out_handle_attrs = { SfOutHandleAttr_HipcMove }, .out_handle_attrs = { SfOutHandleAttr_HipcMove },
.out_handles = out_h, .out_handles = out_h,
); );
} }
static inline Result ldrShellAtmosphereUnregisterExternalCode(u64 app_id) static inline Result ldrShellAtmosphereUnregisterExternalCode(u64 app_id) {
{
return serviceDispatchIn(ldrShellGetServiceSession(), 65001, app_id); return serviceDispatchIn(ldrShellGetServiceSession(), 65001, app_id);
} }
Result RegisterExternalContent(u64 app_id, std::string exefs_path) Result RegisterExternalContent(u64 app_id, const std::string &exefs_path) {
{
Handle move_h = INVALID_HANDLE; Handle move_h = INVALID_HANDLE;
ldrShellAtmosphereUnregisterExternalCode(app_id); ldrShellAtmosphereUnregisterExternalCode(app_id);
R_TRY(ldrShellAtmosphereRegisterExternalCode(app_id, &move_h)); R_TRY(ldrShellAtmosphereRegisterExternalCode(app_id, &move_h));
@ -81,29 +68,27 @@ namespace ecs
ams::sf::cmif::ServiceObjectHolder srv_holder(std::move(sd_ifs_ipc)); ams::sf::cmif::ServiceObjectHolder srv_holder(std::move(sd_ifs_ipc));
R_TRY(manager_instance.RegisterSession(move_h, std::move(srv_holder)).GetValue()); R_TRY(manager_instance.RegisterSession(move_h, std::move(srv_holder)).GetValue());
return 0; return ResultSuccess;
} }
Result LaunchApplet(u64 program_id, u32 la_version, void *args, size_t args_size) Result LaunchApplet(u64 program_id, u32 la_version, void *args, size_t args_size) {
{
Result rc = 0xdead;
auto appletid = am::LibraryAppletGetAppletIdForProgramId(program_id); auto appletid = am::LibraryAppletGetAppletIdForProgramId(program_id);
if(appletid != 0) rc = am::LibraryAppletStart(appletid, la_version, args, args_size); if(appletid == 0) {
return rc; return 0xDEAD;
}
R_TRY(am::LibraryAppletStart(appletid, la_version, args, args_size));
return ResultSuccess;
} }
Result LaunchSystemProcess(u64 program_id, std::string argv_str) Result LaunchSystemProcess(u64 program_id, const std::string &argv_str) {
{ R_TRY(ldrShellSetProgramArguments(program_id, argv_str.c_str(), argv_str.length()));
auto rc = ldrShellSetProgramArguments(program_id, argv_str.c_str(), argv_str.length()); NcmProgramLocation loc = {
if(R_SUCCEEDED(rc)) .program_id = program_id,
{ .storageID = NcmStorageId_BuiltInSystem,
NcmProgramLocation loc = { };
.program_id = program_id, u64 pid;
.storageID = NcmStorageId_BuiltInSystem, R_TRY(pmshellLaunchProgram(0, &loc, &pid));
}; return ResultSuccess;
u64 pid;
rc = pmshellLaunchProgram(0, &loc, &pid);
}
return rc;
} }
} }

View file

@ -1,26 +1,27 @@
#include <ipc/ipc_IPrivateService.hpp> #include <ipc/ipc_IPrivateService.hpp>
#include <am/am_DaemonMenuInteraction.hpp> #include <dmi/dmi_DaemonMenuInteraction.hpp>
#include <am/am_LibraryApplet.hpp> #include <am/am_LibraryApplet.hpp>
extern ams::os::Mutex g_last_menu_msg_lock; extern ams::os::Mutex g_last_menu_msg_lock;
extern am::MenuMessage g_last_menu_msg; extern dmi::MenuMessage g_last_menu_msg;
namespace ipc namespace ipc {
{
ams::Result IPrivateService::GetLatestMessage(ams::sf::Out<u32> msg, const ams::sf::ClientProcessId &client_pid) ams::Result IPrivateService::GetLatestMessage(const ams::sf::ClientProcessId &client_pid, ams::sf::Out<u32> out_msg) {
{
u64 program_id = 0; u64 program_id = 0;
UL_ASSERT(pminfoGetProgramId(&program_id, client_pid.process_id.value)); R_TRY(pminfoGetProgramId(&program_id, client_pid.process_id.value));
auto last_menu_program_id = am::LibraryAppletGetProgramIdForAppletId(am::LibraryAppletGetMenuAppletId()); auto last_menu_program_id = am::LibraryAppletGetProgramIdForAppletId(am::LibraryAppletGetMenuAppletId());
// If Menu hasn't been launched it's program ID will be 0/invalid, thus a != check wouldn't be enough // If Menu hasn't been launched it's program ID will be 0/invalid, thus a != check wouldn't be enough
// If any of the IDs is invalid, something unexpected is happening... // If any of the IDs is invalid, something unexpected is happening...
if((last_menu_program_id == 0) || (program_id == 0) || (program_id != last_menu_program_id)) return RES_VALUE(Daemon, PrivateServiceInvalidProcess); if((last_menu_program_id == 0) || (program_id == 0) || (program_id != last_menu_program_id)) {
return RES_VALUE(Daemon, PrivateServiceInvalidProcess);
}
std::scoped_lock _lock(g_last_menu_msg_lock); std::scoped_lock lk(g_last_menu_msg_lock);
msg.SetValue((u32)g_last_menu_msg); out_msg.SetValue(static_cast<u32>(g_last_menu_msg));
g_last_menu_msg = am::MenuMessage::Invalid; g_last_menu_msg = dmi::MenuMessage::Invalid;
return ams::ResultSuccess(); return ams::ResultSuccess();
} }
} }

View file

@ -1,6 +1,7 @@
#include <ipc/ipc_IPublicService.hpp> #include <ipc/ipc_IPublicService.hpp>
namespace ipc namespace ipc {
{
} }

View file

@ -2,8 +2,8 @@
#include <string> #include <string>
#include <algorithm> #include <algorithm>
extern "C" extern "C" {
{
u32 __nx_applet_type; u32 __nx_applet_type;
u32 __nx_applet_exit_mode = 2; u32 __nx_applet_exit_mode = 2;
@ -11,191 +11,266 @@ extern "C"
u32 __nx_fsdev_direntry_cache_size = 1; u32 __nx_fsdev_direntry_cache_size = 1;
bool __nx_fsdev_support_cwd = false; bool __nx_fsdev_support_cwd = false;
void hb_hbl_Target(const char *path, const char *argv, int do_once); void hb_hbl_Target(Handle process_handle, const char *path, const char *argv, int do_once);
} }
NX_CONSTEXPR bool IsApplet(u64 program_id) namespace {
{
return (0x0100000000001000 <= program_id) && (program_id <= 0x0100000000001FFF);
}
NX_CONSTEXPR bool IsApplication(u64 program_id) inline Result DoWithSmSession(std::function<Result()> fn) {
{ R_TRY(smInitialize());
return (0x0100000000010000 <= program_id); // (For forwarders :p) /* && (program_id <= 0x01FFFFFFFFFFFFFF); */ R_TRY(fn());
} smExit();
return ResultSuccess;
NX_CONSTEXPR bool IsSystemProcess(u64 program_id)
{
return (0x0100000000000000 <= program_id) && (program_id <= 0x01000000000007FF);
}
NX_CONSTEXPR AppletType DetectAppletType(u64 program_id)
{
if(IsApplet(program_id))
{
// OverlayApplet and SystemApplet are impossible in this case
return AppletType_LibraryApplet;
} }
else if(IsApplication(program_id))
{
// hbloader uses this instead of normal Application...
return AppletType_SystemApplication;
}
return AppletType_None;
}
bool ParseTargetParams(hb::HbTargetParams &params, u64 cur_program_id, int argc, char **argv) namespace impl {
{
bool ret = false;
// Load arguments from applet storages static Handle g_process_handle = INVALID_HANDLE;
if(IsApplet(cur_program_id)) static u64 g_process_id = 0;
{ static u64 g_program_id = 0;
// Initialize sm, initialize applet, get arguments, exit applet, exit sm
auto rc = smInitialize(); Result ProcessInfoReceiverImpl(Handle session) {
if(R_SUCCEEDED(rc)) auto base = armGetTls();
{ hipcMakeRequestInline(base);
rc = appletInitialize();
if(R_SUCCEEDED(rc)) s32 idx = 0;
{ R_TRY(svcReplyAndReceive(&idx, &session, 1, INVALID_HANDLE, UINT64_MAX));
AppletStorage common_args_st;
AppletStorage st; HipcParsedRequest r = hipcParseRequest(base);
rc = appletPopInData(&common_args_st); g_process_handle = r.data.copy_handles[0];
if(R_SUCCEEDED(rc)) g_process_id = r.pid;
{ svcCloseHandle(session);
appletStorageClose(&common_args_st); return ResultSuccess;
rc = appletPopInData(&st);
if(R_SUCCEEDED(rc))
{
hb::HbTargetParams params_st = {};
s64 st_size = 0;
appletStorageGetSize(&st, &st_size);
if((u64)st_size >= sizeof(params_st))
{
rc = appletStorageRead(&st, 0, &params_st, sizeof(params_st));
if(R_SUCCEEDED(rc))
{
if(params_st.magic == UL_HB_HBTARGET_MAGIC_U32)
{
params = params_st;
ret = true;
}
}
}
appletStorageClose(&st);
}
}
appletExit();
}
smExit();
} }
Result GetProgramIdImpl() {
auto rc = svcGetInfo(&g_program_id, InfoType_ProgramId, CUR_PROCESS_HANDLE, 0);
if(R_SUCCEEDED(rc)) {
return ResultSuccess;
}
// Let's try with pminfo
R_TRY(DoWithSmSession([&]() {
R_TRY(pminfoInitialize());
return ResultSuccess;
}));
UL_ON_SCOPE_EXIT({
pminfoExit();
});
R_TRY(pminfoGetProgramId(&g_program_id, impl::g_process_id));
return ResultSuccess;
}
void ProcessInfoReceiver(void *arg) {
auto session = static_cast<Handle>(reinterpret_cast<uintptr_t>(arg));
ProcessInfoReceiverImpl(session);
}
} }
// Load arguments from application launch parameter
else if(IsApplication(cur_program_id)) NX_CONSTEXPR bool IsApplet(u64 program_id) {
{ return (0x0100000000001000 <= program_id) && (program_id <= 0x0100000000001FFF);
// Initialize sm, initialize applet, get arguments, exit applet, exit sm }
auto rc = smInitialize();
if(R_SUCCEEDED(rc)) NX_CONSTEXPR bool IsApplication(u64 program_id) {
{ return (0x0100000000010000 <= program_id); // (For forwarders :p) /* && (program_id <= 0x01FFFFFFFFFFFFFF); */
rc = appletInitialize(); }
if(R_SUCCEEDED(rc))
{ NX_CONSTEXPR bool IsSystemProcess(u64 program_id) {
AppletStorage st; return (0x0100000000000000 <= program_id) && (program_id <= 0x01000000000007FF);
rc = appletPopLaunchParameter(&st, AppletLaunchParameterKind_UserChannel); }
if(R_SUCCEEDED(rc))
{ NX_CONSTEXPR AppletType DetectAppletType(u64 program_id) {
hb::HbTargetParams params_st = {}; if(IsApplet(program_id)) {
s64 st_size = 0; // OverlayApplet and SystemApplet are impossible in this case
appletStorageGetSize(&st, &st_size); return AppletType_LibraryApplet;
if((u64)st_size >= sizeof(params_st)) }
{ else if(IsApplication(program_id)) {
rc = appletStorageRead(&st, 0, &params_st, sizeof(params_st)); // hbloader uses this instead of normal Application...
if(R_SUCCEEDED(rc)) return AppletType_SystemApplication;
{ }
if(params_st.magic == UL_HB_HBTARGET_MAGIC_U32) return AppletType_None;
{ }
params = params_st;
inline bool TryParseTargetParamsFromStorage(AppletStorage *st, hb::HbTargetParams &params) {
// Ensure size is correct
s64 st_size = 0;
R_TRY(appletStorageGetSize(st, &st_size));
hb::HbTargetParams tmp_params;
if(static_cast<u64>(st_size) >= sizeof(tmp_params)) {
// Read params
R_TRY(appletStorageRead(st, 0, &tmp_params, sizeof(tmp_params)));
if(tmp_params.magic == UL_HB_HBTARGET_MAGIC_U32) {
params = tmp_params;
return true;
}
}
return false;
}
bool ParseTargetParams(hb::HbTargetParams &params, u64 cur_program_id, int argc, char **argv) {
bool ret = false;
// Load arguments from applet storages
if(IsApplet(cur_program_id)) {
// Initialize sm, initialize applet, exit sm
R_TRY(DoWithSmSession([&]() {
R_TRY(appletInitialize());
return ResultSuccess;
}));
// Ensure applet is exited in the end
UL_ON_SCOPE_EXIT({
appletExit();
});
// We don't make use of the common args storage
AppletStorage common_args_st;
R_TRY(appletPopInData(&common_args_st));
appletStorageClose(&common_args_st);
// Get our storage
AppletStorage hbtarget_st;
R_TRY(appletPopInData(&hbtarget_st));
UL_ON_SCOPE_EXIT({
appletStorageClose(&hbtarget_st);
});
// Try parse params
ret = TryParseTargetParamsFromStorage(&hbtarget_st, params);
}
// Load arguments from application launch parameter
else if(IsApplication(cur_program_id)) {
// Initialize sm, initialize applet, exit sm
R_TRY(DoWithSmSession([&]() {
R_TRY(appletInitialize());
return ResultSuccess;
}));
// Ensure applet is exited in the end
UL_ON_SCOPE_EXIT({
appletExit();
});
// Get our storage from user arguments
AppletStorage hbtarget_st;
R_TRY(appletPopLaunchParameter(&hbtarget_st, AppletLaunchParameterKind_UserChannel));
UL_ON_SCOPE_EXIT({
appletStorageClose(&hbtarget_st);
});
// Try parse params
ret = TryParseTargetParamsFromStorage(&hbtarget_st, params);
}
// Load arguments from system process argv
else if(IsSystemProcess(cur_program_id)) {
// Check that 4 strings are sent
if(argc >= 4) {
std::string magic = argv[0];
// NRO paths start with 'sdmc:/' and spaces are replaced with 0xFF
std::string nro_path = argv[1];
// Argv where ' ' spaces are replaced with 0xFF characters
std::string nro_argv = argv[2];
// This must be '0' or '1'.
std::string target_once = argv[3];
// Matches magic?
if(magic == UL_HB_HBTARGET_MAGIC) {
if(!nro_path.empty()) {
if(!nro_argv.empty()) {
bool target_once_v = true;
try {
target_once_v = (bool)std::stoi(target_once);
std::replace(nro_path.begin(), nro_path.end(), (char)0xFF, ' ');
std::replace(nro_argv.begin(), nro_argv.end(), (char)0xFF, ' ');
strcpy(params.nro_path, nro_path.c_str());
strcpy(params.nro_argv, nro_argv.c_str());
params.target_once = target_once_v;
ret = true; ret = true;
} }
} catch(...) {}
}
appletStorageClose(&st);
}
appletExit();
}
smExit();
}
}
// Load arguments from system process argv
else if(IsSystemProcess(cur_program_id))
{
// Check that 4 strings are sent
if(argc >= 4)
{
std::string magic = argv[0];
// NRO paths start with 'sdmc:/' and spaces are replaced with 0xFF
std::string nro_path = argv[1];
// Argv where ' ' spaces are replaced with 0xFF characters
std::string nro_argv = argv[2];
// This must be '0' or '1'.
std::string target_once = argv[3];
// Matches magic?
if(magic == UL_HB_HBTARGET_MAGIC)
{
if(!nro_path.empty())
{
if(!nro_argv.empty())
{
bool target_once_v = true;
try
{
target_once_v = (bool)std::stoi(target_once);
std::replace(nro_path.begin(), nro_path.end(), (char)0xFF, ' ');
std::replace(nro_argv.begin(), nro_argv.end(), (char)0xFF, ' ');
strcpy(params.nro_path, nro_path.c_str());
strcpy(params.nro_argv, nro_argv.c_str());
params.target_once = target_once_v;
ret = true;
}
catch(std::exception&)
{
} }
} }
} }
} }
} }
if(ret) {
if(strlen(params.nro_path) == 0) {
ret = false;
}
}
if(ret) {
if(strlen(params.nro_argv) == 0) {
strcpy(params.nro_argv, params.nro_path);
}
}
return ret;
} }
if(ret) Result LoadProcessInfo() {
{ Handle server_h;
if(strlen(params.nro_path) == 0) ret = false; Handle client_h;
// Create our own session, and close it on exit if success
R_TRY(svcCreateSession(&server_h, &client_h, 0, 0));
UL_ON_SCOPE_EXIT({
svcCloseHandle(client_h);
});
Thread receiver_thr;
auto thread_arg = reinterpret_cast<void*>(static_cast<uintptr_t>(server_h));
// Create a thread to handle our request, and close it on exit if success
R_TRY(threadCreate(&receiver_thr, &impl::ProcessInfoReceiver, thread_arg, nullptr, 0x1000, 0x2B, -2));
UL_ON_SCOPE_EXIT({
threadWaitForExit(&receiver_thr);
threadClose(&receiver_thr);
});
R_TRY(threadStart(&receiver_thr));
hipcMakeRequestInline(armGetTls(),
.send_pid = 1,
.num_copy_handles = 1,
).copy_handles[0] = CUR_PROCESS_HANDLE;
R_TRY(svcSendSyncRequest(client_h));
return ResultSuccess;
} }
if(ret)
{ Result LoadProgramId() {
if(strlen(params.nro_argv) == 0) strcpy(params.nro_argv, params.nro_path); return impl::GetProgramIdImpl();
} }
return ret; Handle GetOwnProcessHandle() {
return impl::g_process_handle;
}
u64 GetOwnProgramId() {
return impl::g_program_id;
}
} }
int main(int argc, char **argv) int main(int argc, char **argv) {
{ LoadProcessInfo();
u64 cur_program_id; LoadProgramId();
auto rc = svcGetInfo(&cur_program_id, InfoType_ProgramId, CUR_PROCESS_HANDLE, 0);
if(R_SUCCEEDED(rc))
{
__nx_applet_type = DetectAppletType(cur_program_id);
hb::HbTargetParams params = {}; auto program_id = GetOwnProgramId();
auto ok = ParseTargetParams(params, cur_program_id, argc, argv); auto process_handle = GetOwnProcessHandle();
if(ok) hb_hbl_Target(params.nro_path, params.nro_argv, (int)params.target_once);
__nx_applet_type = DetectAppletType(program_id);
hb::HbTargetParams params = {};
auto ok = ParseTargetParams(params, program_id, argc, argv);
if(ok) {
hb_hbl_Target(process_handle, params.nro_path, params.nro_argv, params.target_once);
} }
return 0; return 0;
} }

View file

@ -143,27 +143,6 @@ static void setupHbHeap(void)
static Handle g_procHandle; static Handle g_procHandle;
static void procHandleReceiveThread(void* arg)
{
Handle session = (Handle)(uintptr_t)arg;
Result rc;
void* base = armGetTls();
hipcMakeRequestInline(base);
s32 idx = 0;
rc = svcReplyAndReceive(&idx, &session, 1, INVALID_HANDLE, UINT64_MAX);
if (R_FAILED(rc))
fatalThrow(MAKERESULT(Module_HomebrewLoader, 15));
HipcParsedRequest r = hipcParseRequest(base);
if (r.meta.num_copy_handles != 1)
fatalThrow(MAKERESULT(Module_HomebrewLoader, 17));
g_procHandle = r.data.copy_handles[0];
svcCloseHandle(session);
}
//Gets the control.nacp for the current title id, and then sets g_isAutomaticGameplayRecording if less memory should be allocated. //Gets the control.nacp for the current title id, and then sets g_isAutomaticGameplayRecording if less memory should be allocated.
static void getIsAutomaticGameplayRecording(void) { static void getIsAutomaticGameplayRecording(void) {
if (hosversionAtLeast(5,0,0) && g_isApplication) { if (hosversionAtLeast(5,0,0) && g_isApplication) {
@ -191,35 +170,6 @@ static void getIsAutomaticGameplayRecording(void) {
} }
} }
static void getOwnProcessHandle(void)
{
Result rc;
Handle server_handle, client_handle;
rc = svcCreateSession(&server_handle, &client_handle, 0, 0);
if (R_FAILED(rc))
fatalThrow(MAKERESULT(Module_HomebrewLoader, 12));
Thread t;
rc = threadCreate(&t, &procHandleReceiveThread, (void*)(uintptr_t)server_handle, NULL, 0x1000, 0x20, -2);
if (R_FAILED(rc))
fatalThrow(MAKERESULT(Module_HomebrewLoader, 10));
rc = threadStart(&t);
if (R_FAILED(rc))
fatalThrow(MAKERESULT(Module_HomebrewLoader, 13));
hipcMakeRequestInline(armGetTls(),
.num_copy_handles = 1,
).copy_handles[0] = CUR_PROCESS_HANDLE;
svcSendSyncRequest(client_handle);
svcCloseHandle(client_handle);
threadWaitForExit(&t);
threadClose(&t);
}
Result sdInitMount() Result sdInitMount()
{ {
// When loading NRO: init sm, init fs, mount sd, unmount sd, close fs, close sm // When loading NRO: init sm, init fs, mount sd, unmount sd, close fs, close sm
@ -456,10 +406,12 @@ void loadNro(void)
hbTargetImpl((u64) entries, -1, entrypoint); hbTargetImpl((u64) entries, -1, entrypoint);
} }
void hb_hbl_Target(const char *path, const char *argv, int do_once) void hb_hbl_Target(Handle process_handle, const char *path, const char *argv, bool do_once) {
{ g_procHandle = process_handle;
g_targetCounter = -1; g_targetCounter = -1;
if(do_once) g_targetCounter = 1; if(do_once) {
g_targetCounter = 1;
}
g_isApplication = (__nx_applet_type == AppletType_Application) || (__nx_applet_type == AppletType_SystemApplication); g_isApplication = (__nx_applet_type == AppletType_Application) || (__nx_applet_type == AppletType_SystemApplication);
memcpy(g_savedTls, (u8*)armGetTls() + 0x100, 0x100); memcpy(g_savedTls, (u8*)armGetTls() + 0x100, 0x100);
strcpy(g_basePath, path); strcpy(g_basePath, path);
@ -467,6 +419,5 @@ void hb_hbl_Target(const char *path, const char *argv, int do_once)
getIsAutomaticGameplayRecording(); getIsAutomaticGameplayRecording();
setupHbHeap(); setupHbHeap();
getOwnProcessHandle();
loadNro(); loadNro();
} }

View file

@ -3,20 +3,28 @@
#include <ul_Include.hpp> #include <ul_Include.hpp>
#include <os/os_Titles.hpp> #include <os/os_Titles.hpp>
namespace am namespace am {
{
struct ApplicationSelectedUserArgument struct ApplicationSelectedUserArgument {
{
static constexpr u32 SelectedUserMagic = 0xC79497CA;
u32 magic; u32 magic;
u8 one; u8 unk_1;
u8 pad[3]; u8 pad[3];
AccountUid uid; AccountUid uid;
u8 unk2[0x400 - 0x18]; u8 unk2[0x3E8];
static inline constexpr ApplicationSelectedUserArgument Create(AccountUid uid) {
ApplicationSelectedUserArgument arg = {};
arg.magic = SelectedUserMagic;
arg.unk_1 = 1;
arg.uid = uid;
return arg;
}
}; };
static_assert(sizeof(ApplicationSelectedUserArgument) == 0x400, "ApplicationSelectedUserArgument");
static_assert(sizeof(ApplicationSelectedUserArgument) == 0x400, "ApplicationSelectedUserArgument must be 0x400!");
static constexpr u32 SelectedUserMagic = 0xC79497CA;
bool ApplicationIsActive(); bool ApplicationIsActive();
void ApplicationTerminate(); void ApplicationTerminate();
@ -27,4 +35,5 @@ namespace am
u64 ApplicationGetId(); u64 ApplicationGetId();
bool ApplicationNeedsUser(u64 app_id); bool ApplicationNeedsUser(u64 app_id);
} }

View file

@ -1,256 +0,0 @@
#pragma once
#include <ul_Include.hpp>
#include <hb/hb_Target.hpp>
#define AM_DAEMON_PRIVATE_SERVICE_NAME "qdmnsrv"
#define AM_DAEMON_PUBLIC_SERVICE_NAME "ulaunch"
namespace am
{
enum class MenuStartMode
{
Invalid,
StartupScreen,
Menu,
MenuApplicationSuspended,
MenuLaunchFailure
};
enum class MenuMessage
{
Invalid,
HomeRequest
};
enum class DaemonMessage
{
Invalid,
SetSelectedUser,
LaunchApplication,
ResumeApplication,
TerminateApplication,
LaunchHomebrewLibraryApplet,
LaunchHomebrewApplication,
OpenWebPage,
GetSelectedUser,
OpenAlbum,
RestartMenu,
};
struct DaemonStatus
{
AccountUid selected_user;
hb::HbTargetParams params; // Set if homebrew (via flog takeover) is suspended
u64 app_id; // Set if any title (other than flog) is suspended
};
ResultWith<MenuStartMode> Menu_ProcessInput();
Result Daemon_MenuWriteImpl(void *data, size_t size, bool wait);
Result Daemon_MenuReadImpl(void *data, size_t size, bool wait);
Result Menu_DaemonWriteImpl(void *data, size_t size, bool wait);
Result Menu_DaemonReadImpl(void *data, size_t size, bool wait);
typedef Result(*DMCommandRWFunction)(void*, size_t, bool);
struct DMCommandCommonHeader
{
u32 magic;
u32 val;
};
static constexpr u32 Magic = 0x434D4151;
static constexpr size_t BlockSize = 0x4000;
template<DMCommandRWFunction WriteFn>
class DMCommandWriter
{
private:
DMCommandCommonHeader request;
u8 *data_block;
size_t data_pos;
Result inner_rc;
bool write_done;
public:
DMCommandWriter(u32 value)
{
write_done = false;
request.magic = Magic;
request.val = value;
data_pos = 0;
data_block = new u8[BlockSize]();
inner_rc = WriteFn(&request, sizeof(request), false);
}
~DMCommandWriter()
{
FinishWrite();
}
void FinishWrite()
{
if(!write_done)
{
WriteFn(data_block, BlockSize, false);
if(data_block) delete[] data_block;
write_done = true;
}
}
Result GetResult()
{
return inner_rc;
}
operator bool()
{
return R_SUCCEEDED(inner_rc);
}
template<typename T>
void Write(T t)
{
memcpy(&data_block[data_pos], &t, sizeof(T));
data_pos += sizeof(T);
}
};
template<DMCommandRWFunction ReadFn>
class DMCommandReader
{
private:
DMCommandCommonHeader response;
u8 *data_block;
size_t data_pos;
Result inner_rc;
bool read_done;
bool fn_wait;
public:
DMCommandReader(bool wait = false)
{
fn_wait = wait;
read_done = false;
data_pos = 0;
data_block = new u8[BlockSize]();
inner_rc = ReadFn(&response, sizeof(DMCommandCommonHeader), fn_wait);
if(R_SUCCEEDED(inner_rc)) inner_rc = ReadFn(data_block, BlockSize, fn_wait);
}
~DMCommandReader()
{
FinishRead();
}
void FinishRead()
{
if(!read_done)
{
if(data_block) delete[] data_block;
read_done = true;
}
}
u32 GetValue()
{
return response.val;
}
Result GetResult()
{
return inner_rc;
}
operator bool()
{
return R_SUCCEEDED(inner_rc);
}
template<typename T>
T Read()
{
T t = *(T*)&data_block[data_pos];
data_pos += sizeof(T);
return t;
}
};
class DaemonCommandReader : public DMCommandReader<Daemon_MenuReadImpl>
{
public:
DaemonMessage GetMessage()
{
return (DaemonMessage)GetValue();
}
};
class DaemonCommandWriter : public DMCommandWriter<Daemon_MenuWriteImpl>
{
public:
DaemonCommandWriter(MenuMessage msg) : DMCommandWriter((u32)msg)
{
}
};
class DaemonCommandResultReader : public DMCommandReader<Daemon_MenuReadImpl>
{
public:
DaemonCommandResultReader() : DMCommandReader(true)
{
}
Result GetReadResult()
{
return GetValue();
}
};
class DaemonCommandResultWriter : public DMCommandWriter<Daemon_MenuWriteImpl>
{
public:
DaemonCommandResultWriter(Result rc) : DMCommandWriter(rc)
{
}
};
class MenuCommandReader : public DMCommandReader<Menu_DaemonReadImpl>
{
public:
MenuMessage GetMessage()
{
return (MenuMessage)GetValue();
}
};
class MenuCommandWriter : public DMCommandWriter<Menu_DaemonWriteImpl>
{
public:
MenuCommandWriter(DaemonMessage msg) : DMCommandWriter((u32)msg)
{
}
};
class MenuCommandResultReader : public DMCommandReader<Menu_DaemonReadImpl>
{
public:
MenuCommandResultReader() : DMCommandReader(true)
{
}
Result GetReadResult()
{
return GetValue();
}
};
class MenuCommandResultWriter : public DMCommandWriter<Menu_DaemonWriteImpl>
{
public:
MenuCommandResultWriter(Result rc) : DMCommandWriter(rc)
{
}
};
}

View file

@ -2,8 +2,9 @@
#pragma once #pragma once
#include <ul_Include.hpp> #include <ul_Include.hpp>
namespace am namespace am {
{
bool HomeMenuHasForeground(); bool HomeMenuHasForeground();
Result HomeMenuSetForeground(); Result HomeMenuSetForeground();
} }

View file

@ -1,10 +1,9 @@
#pragma once #pragma once
#include <ul_Include.hpp> #include <ul_Include.hpp>
#include <map>
namespace am namespace am {
{
bool LibraryAppletIsActive(); bool LibraryAppletIsActive();
void LibraryAppletSetMenuAppletId(AppletId id); void LibraryAppletSetMenuAppletId(AppletId id);
AppletId LibraryAppletGetMenuAppletId(); AppletId LibraryAppletGetMenuAppletId();
@ -13,12 +12,16 @@ namespace am
Result LibraryAppletStart(AppletId id, u32 la_version, void *in_data, size_t in_size); Result LibraryAppletStart(AppletId id, u32 la_version, void *in_data, size_t in_size);
Result LibraryAppletSend(void *data, size_t size); Result LibraryAppletSend(void *data, size_t size);
Result LibraryAppletRead(void *data, size_t size); Result LibraryAppletRead(void *data, size_t size);
Result WebAppletStart(WebCommonConfig *web);
inline Result WebAppletStart(WebCommonConfig *web) {
return LibraryAppletStart(AppletId_web, web->version, &web->arg, sizeof(web->arg));
}
u64 LibraryAppletGetProgramIdForAppletId(AppletId id); u64 LibraryAppletGetProgramIdForAppletId(AppletId id);
AppletId LibraryAppletGetAppletIdForProgramId(u64 id); AppletId LibraryAppletGetAppletIdForProgramId(u64 id);
AppletId LibraryAppletGetId(); AppletId LibraryAppletGetId();
static constexpr AppletId InvalidAppletId = (AppletId)0; static constexpr AppletId InvalidAppletId = static_cast<AppletId>(0);
} }

View file

@ -2,18 +2,17 @@
#pragma once #pragma once
#include <ul_Include.hpp> #include <ul_Include.hpp>
#include <hb/hb_Target.hpp> #include <hb/hb_Target.hpp>
#include <fs/fs_Stdio.hpp>
namespace cfg namespace cfg {
{
enum class TitleType : u32 enum class TitleType : u32 {
{
Invalid, Invalid,
Installed, Installed,
Homebrew Homebrew
}; };
struct TitleRecord struct TitleRecord {
{
std::string json_name; // Empty for non-SD, normal title records std::string json_name; // Empty for non-SD, normal title records
u32 title_type; u32 title_type;
std::string sub_folder; // Empty for root, name for a certain folder std::string sub_folder; // Empty for root, name for a certain folder
@ -28,20 +27,17 @@ namespace cfg
std::string version; std::string version;
}; };
struct TitleFolder struct TitleFolder {
{
std::string name; std::string name;
std::vector<TitleRecord> titles; std::vector<TitleRecord> titles;
}; };
struct TitleList struct TitleList {
{
TitleFolder root; TitleFolder root;
std::vector<TitleFolder> folders; std::vector<TitleFolder> folders;
}; };
struct ThemeManifest struct ThemeManifest {
{
std::string name; std::string name;
u32 format_version; u32 format_version;
std::string release; std::string release;
@ -49,22 +45,19 @@ namespace cfg
std::string author; std::string author;
}; };
struct Theme struct Theme {
{
std::string base_name; std::string base_name;
std::string path; std::string path;
ThemeManifest manifest; ThemeManifest manifest;
}; };
struct RecordStrings struct RecordStrings {
{
std::string name; std::string name;
std::string author; std::string author;
std::string version; std::string version;
}; };
struct RecordInformation struct RecordInformation {
{
RecordStrings strings; RecordStrings strings;
std::string icon_path; std::string icon_path;
}; };
@ -75,8 +68,7 @@ namespace cfg
// Take over parental controls applet by default // Take over parental controls applet by default
static constexpr u64 DefaultHomebrewAppletProgramId = 0x0100000000001001; static constexpr u64 DefaultHomebrewAppletProgramId = 0x0100000000001001;
struct Config struct Config {
{
std::string theme_name; std::string theme_name;
bool system_title_override_enabled; bool system_title_override_enabled;
bool viewer_usb_enabled; bool viewer_usb_enabled;
@ -88,6 +80,7 @@ namespace cfg
JSON default_lang; JSON default_lang;
Config() : system_title_override_enabled(false), viewer_usb_enabled(false), menu_program_id(DefaultMenuProgramId), homebrew_applet_program_id(DefaultHomebrewAppletProgramId), homebrew_title_application_id(0) {} Config() : system_title_override_enabled(false), viewer_usb_enabled(false), menu_program_id(DefaultMenuProgramId), homebrew_applet_program_id(DefaultHomebrewAppletProgramId), homebrew_title_application_id(0) {}
}; };
static constexpr u32 CurrentThemeFormatVersion = 1; static constexpr u32 CurrentThemeFormatVersion = 1;
@ -106,17 +99,28 @@ namespace cfg
std::vector<Theme> LoadThemes(); std::vector<Theme> LoadThemes();
std::string GetAssetByTheme(const Theme &base, const std::string &resource_base); std::string GetAssetByTheme(const Theme &base, const std::string &resource_base);
inline bool ThemeIsDefault(const Theme &base) inline bool ThemeIsDefault(const Theme &base) {
{
return base.base_name.empty(); return base.base_name.empty();
} }
std::string GetLanguageJSONPath(const std::string &lang); inline std::string GetLanguageJSONPath(const std::string &lang) {
return UL_BASE_SD_DIR "/lang/" + lang + ".json";
}
std::string GetLanguageString(const JSON &lang, const JSON &def, const std::string &name); std::string GetLanguageString(const JSON &lang, const JSON &def, const std::string &name);
Config CreateNewAndLoadConfig(); Config CreateNewAndLoadConfig();
Config LoadConfig(); Config LoadConfig();
Config EnsureConfig();
inline Config EnsureConfig() {
if(fs::ExistsFile(CFG_CONFIG_JSON)) {
return LoadConfig();
}
else {
return CreateNewAndLoadConfig();
}
}
void SaveConfig(const Config &cfg); void SaveConfig(const Config &cfg);
void SaveRecord(TitleRecord &record); void SaveRecord(TitleRecord &record);
@ -128,4 +132,5 @@ namespace cfg
std::string GetTitleCacheIconPath(u64 app_id); std::string GetTitleCacheIconPath(u64 app_id);
std::string GetNROCacheIconPath(const std::string &path); std::string GetNROCacheIconPath(const std::string &path);
} }

View file

@ -2,11 +2,12 @@
#pragma once #pragma once
#include <ul_Include.hpp> #include <ul_Include.hpp>
namespace db namespace db {
{
static constexpr u64 HomeMenuSaveDataId = 0x8000000000001010; static constexpr u64 HomeMenuSaveDataId = 0x8000000000001010;
Result Mount(); Result Mount();
void Unmount(); void Unmount();
void Commit(); void Commit();
} }

View file

@ -0,0 +1,189 @@
#pragma once
#include <ul_Include.hpp>
#include <hb/hb_Target.hpp>
#define AM_DAEMON_PRIVATE_SERVICE_NAME "qdmnsrv"
#define AM_DAEMON_PUBLIC_SERVICE_NAME "ulaunch"
namespace dmi {
enum class MenuStartMode {
Invalid,
StartupScreen,
Menu,
MenuApplicationSuspended,
MenuLaunchFailure
};
enum class MenuMessage {
Invalid,
HomeRequest
};
enum class DaemonMessage {
Invalid,
SetSelectedUser,
LaunchApplication,
ResumeApplication,
TerminateApplication,
LaunchHomebrewLibraryApplet,
LaunchHomebrewApplication,
OpenWebPage,
GetSelectedUser,
OpenAlbum,
RestartMenu,
};
struct DaemonStatus {
AccountUid selected_user;
hb::HbTargetParams params; // Set if homebrew (via flog takeover) is suspended
u64 app_id; // Set if any title (other than flog) is suspended
};
using CommandFunction = Result(*)(void*, size_t, bool);
struct CommandCommonHeader {
u32 magic;
u32 val;
};
static constexpr u32 Magic = 0x434D4151;
static constexpr size_t BlockSize = 0x4000;
namespace impl {
Result DaemonWriteImpl(void *data, size_t size, bool wait);
Result DaemonReadImpl(void *data, size_t size, bool wait);
Result MenuWriteImpl(void *data, size_t size, bool wait);
Result MenuReadImpl(void *data, size_t size, bool wait);
}
template<CommandFunction WriteFn, typename V, bool Wait>
class CommandWriter {
static_assert(sizeof(V) == sizeof(u32), "Invalid value type");
private:
CommandCommonHeader request;
u8 *data_block;
size_t data_pos;
Result inner_rc;
bool write_done;
public:
CommandWriter(V value) : request({ Magic, static_cast<u32>(value) }), data_block(new (std::nothrow) u8[BlockSize]()), data_pos(0), inner_rc(ResultSuccess), write_done(false) {
this->inner_rc = WriteFn(&this->request, sizeof(this->request), Wait);
}
~CommandWriter() {
this->FinishWrite();
}
inline void FinishWrite() {
if(!this->write_done) {
WriteFn(this->data_block, BlockSize, false);
if(this->data_block != nullptr) {
delete[] this->data_block;
this->data_block = nullptr;
}
this->write_done = true;
}
}
inline Result GetResult() {
return this->inner_rc;
}
inline operator bool() {
return R_SUCCEEDED(this->inner_rc);
}
template<typename T>
inline void Write(T t) {
memcpy(&this->data_block[this->data_pos], &t, sizeof(T));
this->data_pos += sizeof(T);
}
};
template<CommandFunction ReadFn, typename V, bool Wait>
class CommandReader {
static_assert(sizeof(V) == sizeof(u32), "Invalid value type");
private:
CommandCommonHeader response;
u8 *data_block;
size_t data_pos;
Result inner_rc;
bool read_done;
public:
CommandReader() : response(), data_block(new (std::nothrow) u8[BlockSize]()), data_pos(0), inner_rc(ResultSuccess), read_done(false) {
this->inner_rc = ReadFn(&this->response, sizeof(this->response), Wait);
if(R_SUCCEEDED(this->inner_rc)) {
this->inner_rc = ReadFn(this->data_block, BlockSize, Wait);
}
}
~CommandReader() {
this->FinishRead();
}
inline void FinishRead() {
if(!this->read_done) {
if(this->data_block != nullptr) {
delete[] this->data_block;
this->data_block = nullptr;
}
this->read_done = true;
}
}
inline V GetValue() {
return static_cast<V>(this->response.val);
}
inline Result GetResult() {
return this->inner_rc;
}
inline operator bool() {
return R_SUCCEEDED(this->inner_rc);
}
template<typename T>
inline T Read() {
auto t = *reinterpret_cast<T*>(this->data_block + data_pos);
data_pos += sizeof(T);
return t;
}
};
template<typename V, bool Wait>
using DaemonReader = CommandReader<&impl::DaemonReadImpl, V, Wait>;
template<typename V, bool Wait>
using DaemonWriter = CommandWriter<&impl::DaemonWriteImpl, V, Wait>;
using DaemonMessageReader = DaemonReader<DaemonMessage, false>;
using DaemonMessageWriter = DaemonWriter<MenuMessage, false>;
using DaemonResultReader = DaemonReader<Result, true>;
using DaemonResultWriter = DaemonWriter<Result, false>;
template<typename V, bool Wait>
using MenuReader = CommandReader<&impl::MenuReadImpl, V, Wait>;
template<typename V, bool Wait>
using MenuWriter = CommandWriter<&impl::MenuWriteImpl, V, Wait>;
using MenuMessageReader = MenuReader<MenuMessage, false>;
using MenuMessageWriter = MenuWriter<DaemonMessage, false>;
using MenuResultReader = MenuReader<Result, true>;
using MenuResultWriter = MenuWriter<Result, false>;
}

View file

@ -2,23 +2,52 @@
#pragma once #pragma once
#include <ul_Include.hpp> #include <ul_Include.hpp>
namespace fs namespace fs {
{
bool ExistsFile(const std::string &path);
bool ExistsDirectory(const std::string &path);
void CreateDirectory(const std::string &path); template<mode_t Mode>
void CreateFile(const std::string &path); inline bool ExistsBase(const std::string &path) {
void CreateConcatenationFile(const std::string &path); struct stat st;
if(stat(path.c_str(), &st) == 0) {
return st.st_mode & Mode;
}
return false;
}
void DeleteDirectory(const std::string &path); inline bool ExistsFile(const std::string &path) {
void DeleteFile(const std::string &path); return ExistsBase<S_IFREG>(path);
}
inline bool WriteFile(const std::string &path, void *data, size_t size, bool overwrite) inline bool ExistsDirectory(const std::string &path) {
{ return ExistsBase<S_IFDIR>(path);
FILE *f = fopen(path.c_str(), overwrite ? "wb" : "ab+"); }
if(f)
{ inline void CreateDirectory(const std::string &path) {
mkdir(path.c_str(), 777);
}
inline void CreateFileBase(const std::string &path, u32 opt) {
fsdevCreateFile(path.c_str(), 0, opt);
}
inline void CreateFile(const std::string &path) {
CreateFileBase(path, 0);
}
inline void CreateConcatenationFile(const std::string &path) {
CreateFileBase(path, FsCreateOption_BigFile);
}
inline void DeleteDirectory(const std::string &path) {
fsdevDeleteDirectoryRecursively(path.c_str());
}
inline void DeleteFile(const std::string &path) {
remove(path.c_str());
}
inline bool WriteFile(const std::string &path, void *data, size_t size, bool overwrite) {
auto f = fopen(path.c_str(), overwrite ? "wb" : "ab+");
if(f) {
fwrite(data, 1, size, f); fwrite(data, 1, size, f);
fclose(f); fclose(f);
return true; return true;
@ -26,11 +55,9 @@ namespace fs
return false; return false;
} }
inline bool ReadFile(const std::string &path, void *data, size_t size) inline bool ReadFile(const std::string &path, void *data, size_t size) {
{ auto f = fopen(path.c_str(), "rb");
FILE *f = fopen(path.c_str(), "rb"); if(f) {
if(f)
{
fread(data, 1, size, f); fread(data, 1, size, f);
fclose(f); fclose(f);
return true; return true;
@ -38,39 +65,28 @@ namespace fs
return false; return false;
} }
inline size_t GetFileSize(const std::string &path) inline size_t GetFileSize(const std::string &path) {
{ struct stat st;
FILE *f = fopen(path.c_str(), "rb"); if(stat(path.c_str(), &st) == 0) {
if(f) return st.st_size;
{
fseek(f, 0, SEEK_END);
size_t fsz = ftell(f);
rewind(f);
fclose(f);
return fsz;
} }
return 0; return 0;
} }
void MoveFile(const std::string &p1, const std::string &p2); #define UL_FS_FOR(dir, name_var, path_var, ...) ({ \
void CopyFile(const std::string &p, const std::string &np); auto dp = opendir(dir.c_str()); \
if(dp) { \
void MoveDirectory(const std::string &d, const std::string &nd); while(true) { \
void CopyDirectory(const std::string &d, const std::string &nd); auto dt = readdir(dp); \
if(dt == nullptr) { \
#define FS_FOR(dir, name_var, path_var, ...) \ break; \
DIR *dp = opendir(dir.c_str()); \ } \
if(dp) \
{ \
dirent *dt; \
while(true) \
{ \
dt = readdir(dp); \
if(dt == NULL) break; \
std::string name_var = dt->d_name; \ std::string name_var = dt->d_name; \
std::string path_var = dir + "/" + dt->d_name; \ std::string path_var = dir + "/" + dt->d_name; \
__VA_ARGS__ \ __VA_ARGS__ \
} \ } \
closedir(dp); \ closedir(dp); \
} } \
})
} }

View file

@ -5,27 +5,23 @@
#define UL_HB_HBTARGET_MAGIC "UHBT" #define UL_HB_HBTARGET_MAGIC "UHBT"
#define UL_HB_HBTARGET_MAGIC_U32 0x54424855 #define UL_HB_HBTARGET_MAGIC_U32 0x54424855
namespace hb namespace hb {
{
struct HbTargetParams struct HbTargetParams {
{
u32 magic; u32 magic;
char nro_path[FS_MAX_PATH]; char nro_path[FS_MAX_PATH];
char nro_argv[FS_MAX_PATH]; char nro_argv[FS_MAX_PATH];
bool target_once; bool target_once;
inline std::string GetNROPath() inline std::string GetNROPath() {
{
return this->nro_path; return this->nro_path;
} }
inline std::string GetNROArgv() inline std::string GetNROArgv() {
{
return this->nro_argv; return this->nro_argv;
} }
inline std::string FormatToArgvString() inline std::string FormatToArgvString() {
{
std::string argv = UL_HB_HBTARGET_MAGIC; std::string argv = UL_HB_HBTARGET_MAGIC;
std::string nro_path_copy = this->nro_path; std::string nro_path_copy = this->nro_path;
std::replace(nro_path_copy.begin(), nro_path_copy.end(), ' ', (char)0xFF); std::replace(nro_path_copy.begin(), nro_path_copy.end(), ' ', (char)0xFF);
@ -38,8 +34,7 @@ namespace hb
return argv; return argv;
} }
inline static HbTargetParams Create(const std::string &nro_path, const std::string &nro_argv, bool target_once) inline static HbTargetParams Create(const std::string &nro_path, const std::string &nro_argv, bool target_once) {
{
HbTargetParams params = {}; HbTargetParams params = {};
params.magic = UL_HB_HBTARGET_MAGIC_U32; params.magic = UL_HB_HBTARGET_MAGIC_U32;
strcpy(params.nro_path, nro_path.c_str()); strcpy(params.nro_path, nro_path.c_str());
@ -47,5 +42,7 @@ namespace hb
params.target_once = target_once; params.target_once = target_once;
return params; return params;
} }
}; };
} }

View file

@ -2,8 +2,8 @@
#pragma once #pragma once
#include <net/net_Types.hpp> #include <net/net_Types.hpp>
namespace net namespace net {
{
Result Initialize(); Result Initialize();
void Finalize(); void Finalize();
Result GetInternetConnectionStatus(NifmInternetConnectionStatus* status); Result GetInternetConnectionStatus(NifmInternetConnectionStatus* status);
@ -13,4 +13,5 @@ namespace net
std::string FormatMACAddress(u64 addr); std::string FormatMACAddress(u64 addr);
std::string GetConsoleIPAddress(); std::string GetConsoleIPAddress();
} }

View file

@ -2,15 +2,15 @@
#pragma once #pragma once
#include <ul_Include.hpp> #include <ul_Include.hpp>
namespace net namespace net {
{
struct NetworkProfileData struct NetworkProfileData
{ {
u8 unk[0x117]; u8 unk[0x117];
char wifi_name[0x20 + 1]; char wifi_name[0x20 + 1];
u8 unk2[3]; u8 unk2[3];
char wifi_password[0x40 + 1]; char wifi_password[0x40 + 1];
} PACKED; };
static_assert(sizeof(NetworkProfileData) == 0x17c, "Invalid NetworkProfileData"); static_assert(sizeof(NetworkProfileData) == 0x17c, "Invalid NetworkProfileData");
} }

View file

@ -2,9 +2,10 @@
#pragma once #pragma once
#include <ul_Include.hpp> #include <ul_Include.hpp>
namespace os namespace os {
{
std::string GetIconCacheImagePath(AccountUid user_id); std::string GetIconCacheImagePath(AccountUid user_id);
ResultWith<std::vector<AccountUid>> QuerySystemAccounts(bool dump_icon); Result QuerySystemAccounts(std::vector<AccountUid> &out_accounts, bool dump_icon);
ResultWith<std::string> GetAccountName(AccountUid user_id); Result GetAccountName(std::string &out_name, AccountUid user_id);
} }

View file

@ -2,10 +2,9 @@
#pragma once #pragma once
#include <ul_Include.hpp> #include <ul_Include.hpp>
namespace os namespace os {
{
enum class GeneralChannelMessage : u32 enum class GeneralChannelMessage : u32 {
{
Invalid, Invalid,
HomeButton = 2, HomeButton = 2,
Sleep = 3, Sleep = 3,
@ -18,20 +17,28 @@ namespace os
OverlayHidden = 17, OverlayHidden = 17,
}; };
struct SystemAppletMessage struct SystemAppletMessage {
{
static constexpr u32 Magic = 0x534D4153; // "SAMS" -> System applet message...?
u32 magic; u32 magic;
u32 unk; u32 unk;
u32 message; u32 general_channel_message;
u8 data[0x400 - (sizeof(u32) * 3)]; // 1024 bytes are usually sent, so let's read it all. u8 data[0x3F4];
} PACKED;
static constexpr u32 SAMSMagic = 0x534D4153; // "SAMS" -> System Applet message...? static inline constexpr SystemAppletMessage Create(GeneralChannelMessage msg) {
SystemAppletMessage sams = {};
sams.magic = Magic;
sams.general_channel_message = static_cast<u32>(msg);
return sams;
}
static_assert(sizeof(SystemAppletMessage) == 0x400, "System applet message must be 0x400!"); };
// 1024 bytes are always sent, so let's read it all.
static_assert(sizeof(SystemAppletMessage) == 0x400, "System applet message");
enum class AppletMessage : u32
{ enum class AppletMessage : u32 {
Invalid, Invalid,
Exit = 4, Exit = 4,
FocusStateChange = 15, FocusStateChange = 15,
@ -44,4 +51,5 @@ namespace os
}; };
Result PushSystemAppletMessage(SystemAppletMessage msg); Result PushSystemAppletMessage(SystemAppletMessage msg);
} }

View file

@ -2,10 +2,9 @@
#pragma once #pragma once
#include <ul_Include.hpp> #include <ul_Include.hpp>
namespace os namespace os {
{
enum class Language : u32 enum class Language : u32 {
{
Japanese, Japanese,
AmericanEnglish, AmericanEnglish,
French, French,
@ -31,4 +30,6 @@ namespace os
u32 GetBatteryLevel(); u32 GetBatteryLevel();
bool IsConsoleCharging(); bool IsConsoleCharging();
std::string GetFirmwareVersion(); std::string GetFirmwareVersion();
std::string GetCurrentTime();
} }

View file

@ -3,9 +3,25 @@
#include <ul_Include.hpp> #include <ul_Include.hpp>
#include <cfg/cfg_Config.hpp> #include <cfg/cfg_Config.hpp>
namespace os namespace os {
{
static constexpr u32 MaxInstalledCount = 64000; #define UL_OS_FOR_EACH_APP_RECORD(rec, ...) ({ \
NsApplicationRecord rec = {}; \
s32 offset = 0; \
s32 count = 0; \
while(true) { \
auto rc = nsListApplicationRecord(&rec, 1, offset, &count); \
if(R_FAILED(rc) || (count < 1)) { \
break; \
} \
if(rec.application_id != 0) { \
__VA_ARGS__ \
} \
offset++; \
rec = {}; \
} \
})
std::vector<cfg::TitleRecord> QueryInstalledTitles(); std::vector<cfg::TitleRecord> QueryInstalledTitles();
} }

View file

@ -32,6 +32,8 @@ using JSON = nlohmann::json;
#error uLaunch's release version isn't defined. #error uLaunch's release version isn't defined.
#endif #endif
#include <ul_Scope.hpp>
static constexpr size_t RawRGBAScreenBufferSize = 1280 * 720 * 4; static constexpr size_t RawRGBAScreenBufferSize = 1280 * 720 * 4;
#ifndef R_TRY #ifndef R_TRY
@ -75,19 +77,15 @@ inline constexpr ResultWith<Args...> SuccessResultWith(Args &&...args)
return MakeResultWith(0, args...); return MakeResultWith(0, args...);
} }
static constexpr Result ResultSuccess = 0;
static constexpr Mutex EmptyMutex = (Mutex)0; static constexpr Mutex EmptyMutex = (Mutex)0;
#include <ul_Results.hpp> #include <ul_Results.hpp>
#define UL_ASSERT(expr) { auto _tmp_rc = (expr); if(R_FAILED(_tmp_rc)) { fatalThrow(_tmp_rc); } } #define UL_ASSERT(expr) ({ \
const auto _tmp_rc = (expr); \
// Console (debug) if(R_FAILED(_tmp_rc)) { \
fatalThrow(_tmp_rc); \
#if UL_DEV } \
})
#include <iostream>
#define CONSOLE_OUT(...) { std::cout << __VA_ARGS__ << std::endl; consoleUpdate(NULL); }
#define CONSOLE_FMT(fmt, ...) { printf(fmt "\n", ##__VA_ARGS__); consoleUpdate(NULL); }
#endif

View file

@ -7,11 +7,12 @@
#define RES_VALUE(module, name) res::GetResultByModuleAndName(#module, #name) #define RES_VALUE(module, name) res::GetResultByModuleAndName(#module, #name)
#define RES_DESCRIPTION(rc) res::GetDescriptionByResult(rc) #define RES_DESCRIPTION(rc) res::GetDescriptionByResult(rc)
namespace res namespace res {
{
// All 2380-**** results are from uLaunch! // All 2380-**** results are from uLaunch!
static constexpr u32 Module = 380; static constexpr u32 Module = 380;
Result GetResultByModuleAndName(std::string mod, std::string name); Result GetResultByModuleAndName(std::string mod, std::string name);
std::string GetDescriptionByResult(Result rc); std::string GetDescriptionByResult(Result rc);
} }

View file

@ -0,0 +1,26 @@
#pragma once
#include <functional>
class OnScopeExit {
public:
using Fn = std::function<void()>;
private:
Fn exit_fn;
public:
OnScopeExit(Fn fn) : exit_fn(fn) {}
~OnScopeExit() {
(this->exit_fn)();
}
};
#define UL_CONCAT_IMPL(x,y) x##y
#define UL_CONCAT(x,y) UL_CONCAT_IMPL(x,y)
#define UL_UNIQUE_VAR_NAME(prefix) UL_CONCAT(prefix ## _, __COUNTER__)
#define UL_ON_SCOPE_EXIT(...) OnScopeExit UL_UNIQUE_VAR_NAME(on_scope_exit) ([&]() { __VA_ARGS__ })

View file

@ -2,10 +2,8 @@
#pragma once #pragma once
#include <ul_Include.hpp> #include <ul_Include.hpp>
namespace util namespace util {
{
ResultWith<JSON> LoadJSONFromFile(const std::string &path); Result LoadJSONFromFile(JSON &out_json, const std::string &path);
std::string GetCurrentTime();
u32 GetBatteryLevel();
bool IsCharging();
} }

View file

@ -2,11 +2,13 @@
#pragma once #pragma once
#include <ul_Include.hpp> #include <ul_Include.hpp>
namespace util namespace util {
{
inline bool StringEndsWith(const std::string &value, const std::string &ending) inline bool StringEndsWith(const std::string &value, const std::string &ending) {
{ if(ending.size() > value.size()) {
if(ending.size() > value.size()) return false; return false;
}
return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
} }
} }

View file

@ -6,79 +6,75 @@ namespace am
static AppletApplication g_application_holder; static AppletApplication g_application_holder;
static u64 g_last_app_id; static u64 g_last_app_id;
bool ApplicationIsActive() bool ApplicationIsActive() {
{ if(g_application_holder.StateChangedEvent.revent == INVALID_HANDLE) {
if(g_application_holder.StateChangedEvent.revent == INVALID_HANDLE) return false; return false;
if(!serviceIsActive(&g_application_holder.s)) return false; }
if(!serviceIsActive(&g_application_holder.s)) {
return false;
}
return !appletApplicationCheckFinished(&g_application_holder); return !appletApplicationCheckFinished(&g_application_holder);
} }
void ApplicationTerminate() void ApplicationTerminate() {
{
appletApplicationRequestExit(&g_application_holder); appletApplicationRequestExit(&g_application_holder);
if(!g_home_has_focus) g_home_has_focus = true; g_home_has_focus = true;
} }
Result ApplicationStart(u64 app_id, bool system, AccountUid user_id, void *data, size_t size) Result ApplicationStart(u64 app_id, bool system, AccountUid user_id, void *data, size_t size) {
{
appletApplicationClose(&g_application_holder); appletApplicationClose(&g_application_holder);
if(system) R_TRY(appletCreateSystemApplication(&g_application_holder, app_id)); if(system) {
else R_TRY(appletCreateApplication(&g_application_holder, app_id)); R_TRY(appletCreateSystemApplication(&g_application_holder, app_id));
}
else {
R_TRY(appletCreateApplication(&g_application_holder, app_id));
}
if(accountUidIsValid(&user_id)) if(accountUidIsValid(&user_id)) {
{ auto selected_user_arg = ApplicationSelectedUserArgument::Create(user_id);
ApplicationSelectedUserArgument arg = {}; R_TRY(ApplicationSend(&selected_user_arg, sizeof(selected_user_arg), AppletLaunchParameterKind_PreselectedUser));
arg.magic = SelectedUserMagic;
arg.one = 1;
memcpy(&arg.uid, &user_id, sizeof(user_id));
ApplicationSend(&arg, sizeof(arg), AppletLaunchParameterKind_PreselectedUser);
} }
if(size > 0) ApplicationSend(data, size); if(size > 0) {
R_TRY(ApplicationSend(data, size));
}
R_TRY(appletUnlockForeground()); R_TRY(appletUnlockForeground());
R_TRY(appletApplicationStart(&g_application_holder)); R_TRY(appletApplicationStart(&g_application_holder));
R_TRY(ApplicationSetForeground()); R_TRY(ApplicationSetForeground());
g_last_app_id = app_id; g_last_app_id = app_id;
return 0; return 0;
} }
bool ApplicationHasForeground() bool ApplicationHasForeground() {
{
return !g_home_has_focus; return !g_home_has_focus;
} }
Result ApplicationSetForeground() Result ApplicationSetForeground() {
{ R_TRY(appletApplicationRequestForApplicationToGetForeground(&g_application_holder));
auto rc = appletApplicationRequestForApplicationToGetForeground(&g_application_holder); g_home_has_focus = false;
if(R_SUCCEEDED(rc)) g_home_has_focus = false; return ResultSuccess;
return rc;
} }
Result ApplicationSend(void *data, size_t size, AppletLaunchParameterKind kind) Result ApplicationSend(void *data, size_t size, AppletLaunchParameterKind kind) {
{
AppletStorage st; AppletStorage st;
auto rc = appletCreateStorage(&st, size); R_TRY(appletCreateStorage(&st, size));
if(R_SUCCEEDED(rc)) UL_ON_SCOPE_EXIT({
{
rc = appletStorageWrite(&st, 0, data, size);
if(R_SUCCEEDED(rc)) rc = appletApplicationPushLaunchParameter(&g_application_holder, kind, &st);
appletStorageClose(&st); appletStorageClose(&st);
} });
return rc; R_TRY(appletStorageWrite(&st, 0, data, size));
R_TRY(appletApplicationPushLaunchParameter(&g_application_holder, kind, &st));
return ResultSuccess;
} }
u64 ApplicationGetId() u64 ApplicationGetId() {
{
return g_last_app_id; return g_last_app_id;
} }
bool ApplicationNeedsUser(u64 app_id) bool ApplicationNeedsUser(u64 app_id) {
{
NsApplicationControlData ctdata = {}; NsApplicationControlData ctdata = {};
nsGetApplicationControlData(NsApplicationControlSource_Storage, app_id, &ctdata, sizeof(ctdata), NULL); nsGetApplicationControlData(NsApplicationControlSource_Storage, app_id, &ctdata, sizeof(ctdata), nullptr);
return (ctdata.nacp.startup_user_account > 0); return ctdata.nacp.startup_user_account > 0;
} }
} }

View file

@ -1,64 +0,0 @@
#include <am/am_DaemonMenuInteraction.hpp>
#include <fs/fs_Stdio.hpp>
#include <am/am_LibraryApplet.hpp>
namespace am
{
#define UL_AM_LOOP_TRY_WITH(...) { \
Result rc = 0; \
do \
{ \
__VA_ARGS__ \
svcSleepThread(10'000'000); \
} while(R_FAILED(rc)); \
return rc; \
}
#define UL_AM_WAIT_WITH(...) { \
if(wait) { UL_AM_LOOP_TRY_WITH(__VA_ARGS__) } \
else \
{ \
Result rc = 0; \
__VA_ARGS__ \
return rc; \
} \
}
#define UL_AM_WAIT(expr) UL_AM_WAIT_WITH(rc = (expr);)
ResultWith<MenuStartMode> Menu_ProcessInput()
{
LibAppletArgs in_args;
auto rc = Menu_DaemonReadImpl(&in_args, sizeof(LibAppletArgs), false);
return MakeResultWith(rc, (MenuStartMode)in_args.LaVersion);
}
Result Daemon_MenuWriteImpl(void *data, size_t size, bool wait)
UL_AM_WAIT(LibraryAppletSend(data, size))
Result Daemon_MenuReadImpl(void *data, size_t size, bool wait)
UL_AM_WAIT(LibraryAppletRead(data, size))
Result Menu_DaemonWriteImpl(void *data, size_t size, bool wait)
UL_AM_WAIT_WITH(
AppletStorage st;
rc = appletCreateStorage(&st, size);
if(R_SUCCEEDED(rc))
{
rc = appletStorageWrite(&st, 0, data, size);
if(R_SUCCEEDED(rc)) rc = appletPushOutData(&st);
appletStorageClose(&st);
}
)
Result Menu_DaemonReadImpl(void *data, size_t size, bool wait)
UL_AM_WAIT_WITH(
AppletStorage st;
rc = appletPopInData(&st);
if(R_SUCCEEDED(rc))
{
rc = appletStorageRead(&st, 0, data, size);
appletStorageClose(&st);
}
)
}

View file

@ -1,20 +1,19 @@
#include <am/am_HomeMenu.hpp> #include <am/am_HomeMenu.hpp>
#include <am/am_LibraryApplet.hpp> #include <am/am_LibraryApplet.hpp>
namespace am namespace am {
{
bool g_home_has_focus = true; bool g_home_has_focus = true;
extern AppletHolder g_applet_holder; extern AppletHolder g_applet_holder;
bool HomeMenuHasForeground() bool HomeMenuHasForeground() {
{
return g_home_has_focus; return g_home_has_focus;
} }
Result HomeMenuSetForeground() Result HomeMenuSetForeground() {
{ R_TRY(appletRequestToGetForeground());
Result rc = appletRequestToGetForeground(); g_home_has_focus = true;
if(R_SUCCEEDED(rc)) g_home_has_focus = true; return ResultSuccess;
return rc;
} }
} }

View file

@ -1,4 +1,5 @@
#include <am/am_LibraryApplet.hpp> #include <am/am_LibraryApplet.hpp>
#include <map>
namespace am namespace am
{ {
@ -6,8 +7,7 @@ namespace am
static AppletId g_menu_applet_id = InvalidAppletId; static AppletId g_menu_applet_id = InvalidAppletId;
static AppletId g_last_applet_id = InvalidAppletId; static AppletId g_last_applet_id = InvalidAppletId;
static std::map<u64, AppletId> g_applet_id_table = static std::map<u64, AppletId> g_applet_id_table = {
{
{ 0x0100000000001001, AppletId_auth }, { 0x0100000000001001, AppletId_auth },
{ 0x0100000000001002, AppletId_cabinet }, { 0x0100000000001002, AppletId_cabinet },
{ 0x0100000000001003, AppletId_controller }, { 0x0100000000001003, AppletId_controller },
@ -27,69 +27,62 @@ namespace am
{ 0x0100000000001013, AppletId_myPage } { 0x0100000000001013, AppletId_myPage }
}; };
bool LibraryAppletIsActive() bool LibraryAppletIsActive() {
{ if(g_applet_holder.StateChangedEvent.revent == INVALID_HANDLE) {
if(g_applet_holder.StateChangedEvent.revent == INVALID_HANDLE) return false; return false;
if(!serviceIsActive(&g_applet_holder.s)) return false; }
if(!serviceIsActive(&g_applet_holder.s)) {
return false;
}
return !appletHolderCheckFinished(&g_applet_holder); return !appletHolderCheckFinished(&g_applet_holder);
} }
void LibraryAppletSetMenuAppletId(AppletId id) void LibraryAppletSetMenuAppletId(AppletId id) {
{
g_menu_applet_id = id; g_menu_applet_id = id;
} }
AppletId LibraryAppletGetMenuAppletId() AppletId LibraryAppletGetMenuAppletId() {
{
return g_menu_applet_id; return g_menu_applet_id;
} }
bool LibraryAppletIsMenu() bool LibraryAppletIsMenu() {
{ return LibraryAppletIsActive() && (g_menu_applet_id != InvalidAppletId) && (LibraryAppletGetId() == g_menu_applet_id);
return (LibraryAppletIsActive() && (g_menu_applet_id != InvalidAppletId) && (LibraryAppletGetId() == g_menu_applet_id));
} }
void LibraryAppletTerminate() void LibraryAppletTerminate() {
{
// Give it 15 seconds // Give it 15 seconds
appletHolderRequestExitOrTerminate(&g_applet_holder, 15'000'000'000ul); appletHolderRequestExitOrTerminate(&g_applet_holder, 15'000'000'000ul);
} }
Result LibraryAppletStart(AppletId id, u32 la_version, void *in_data, size_t in_size) Result LibraryAppletStart(AppletId id, u32 la_version, void *in_data, size_t in_size) {
{ if(LibraryAppletIsActive()) {
if(LibraryAppletIsActive()) LibraryAppletTerminate(); LibraryAppletTerminate();
}
appletHolderClose(&g_applet_holder); appletHolderClose(&g_applet_holder);
LibAppletArgs largs; LibAppletArgs largs;
libappletArgsCreate(&largs, la_version); libappletArgsCreate(&largs, la_version);
R_TRY(appletCreateLibraryApplet(&g_applet_holder, id, LibAppletMode_AllForeground)); R_TRY(appletCreateLibraryApplet(&g_applet_holder, id, LibAppletMode_AllForeground));
R_TRY(libappletArgsPush(&largs, &g_applet_holder)); R_TRY(libappletArgsPush(&largs, &g_applet_holder));
if(in_size > 0) if(in_size > 0) {
{
R_TRY(LibraryAppletSend(in_data, in_size)); R_TRY(LibraryAppletSend(in_data, in_size));
} }
R_TRY(appletHolderStart(&g_applet_holder)); R_TRY(appletHolderStart(&g_applet_holder));
g_last_applet_id = id; g_last_applet_id = id;
return 0; return ResultSuccess;
} }
Result LibraryAppletSend(void *data, size_t size) Result LibraryAppletSend(void *data, size_t size) {
{
return libappletPushInData(&g_applet_holder, data, size); return libappletPushInData(&g_applet_holder, data, size);
} }
Result LibraryAppletRead(void *data, size_t size) Result LibraryAppletRead(void *data, size_t size) {
{ return libappletPopOutData(&g_applet_holder, data, size, nullptr);
return libappletPopOutData(&g_applet_holder, data, size, NULL);
} }
Result WebAppletStart(WebCommonConfig *web) Result LibraryAppletDaemonLaunchWith(AppletId id, u32 la_version, std::function<void(AppletHolder*)> on_prepare, std::function<void(AppletHolder*)> on_finish, std::function<bool()> on_wait) {
{ if(LibraryAppletIsActive()) {
return LibraryAppletStart(AppletId_web, web->version, &web->arg, sizeof(web->arg)); LibraryAppletTerminate();
} }
Result LibraryAppletDaemonLaunchWith(AppletId id, u32 la_version, std::function<void(AppletHolder*)> on_prepare, std::function<void(AppletHolder*)> on_finish, std::function<bool()> on_wait)
{
if(LibraryAppletIsActive()) LibraryAppletTerminate();
appletHolderClose(&g_applet_holder); appletHolderClose(&g_applet_holder);
LibAppletArgs largs; LibAppletArgs largs;
libappletArgsCreate(&largs, la_version); libappletArgsCreate(&largs, la_version);
@ -97,11 +90,11 @@ namespace am
R_TRY(libappletArgsPush(&largs, &g_applet_holder)); R_TRY(libappletArgsPush(&largs, &g_applet_holder));
on_prepare(&g_applet_holder); on_prepare(&g_applet_holder);
R_TRY(appletHolderStart(&g_applet_holder)); R_TRY(appletHolderStart(&g_applet_holder));
while(true) while(true) {
{ if(!LibraryAppletIsActive()) {
if(!LibraryAppletIsActive()) break; break;
if(!on_wait()) }
{ if(!on_wait()) {
LibraryAppletTerminate(); LibraryAppletTerminate();
break; break;
} }
@ -109,31 +102,31 @@ namespace am
} }
on_finish(&g_applet_holder); on_finish(&g_applet_holder);
appletHolderClose(&g_applet_holder); appletHolderClose(&g_applet_holder);
return 0; return ResultSuccess;
} }
u64 LibraryAppletGetProgramIdForAppletId(AppletId id) u64 LibraryAppletGetProgramIdForAppletId(AppletId id) {
{ for(auto &[program_id, applet_id] : g_applet_id_table) {
for(auto &[program_id, applet_id] : g_applet_id_table) if(applet_id == id) {
{ return program_id;
if(applet_id == id) return program_id; }
} }
return 0; return 0;
} }
AppletId LibraryAppletGetAppletIdForProgramId(u64 id) AppletId LibraryAppletGetAppletIdForProgramId(u64 id) {
{ auto it = g_applet_id_table.find(id);
for(auto &[program_id, applet_id] : g_applet_id_table) if(it != g_applet_id_table.end()) {
{ return it->second;
if(program_id == id) return applet_id;
} }
return InvalidAppletId; return InvalidAppletId;
} }
AppletId LibraryAppletGetId() AppletId LibraryAppletGetId() {
{
auto idcopy = g_last_applet_id; auto idcopy = g_last_applet_id;
if(!LibraryAppletIsActive()) g_last_applet_id = InvalidAppletId; if(!LibraryAppletIsActive()) {
g_last_applet_id = InvalidAppletId;
}
return idcopy; return idcopy;
} }
} }

View file

@ -6,27 +6,21 @@
#include <db/db_Save.hpp> #include <db/db_Save.hpp>
#include <util/util_Convert.hpp> #include <util/util_Convert.hpp>
namespace cfg namespace cfg {
{
void CacheHomebrew(const std::string &nro_path) static void CacheHomebrew(const std::string &nro_path) {
{
auto nroimg = GetNROCacheIconPath(nro_path); auto nroimg = GetNROCacheIconPath(nro_path);
FILE *f = fopen(nro_path.c_str(), "rb"); auto f = fopen(nro_path.c_str(), "rb");
if(f) if(f) {
{
fseek(f, sizeof(NroStart), SEEK_SET); fseek(f, sizeof(NroStart), SEEK_SET);
NroHeader hdr = {}; NroHeader hdr = {};
if(fread(&hdr, sizeof(NroHeader), 1, f) == 1) if(fread(&hdr, sizeof(NroHeader), 1, f) == 1) {
{
fseek(f, hdr.size, SEEK_SET); fseek(f, hdr.size, SEEK_SET);
NroAssetHeader ahdr = {}; NroAssetHeader ahdr = {};
if(fread(&ahdr, sizeof(NroAssetHeader), 1, f) == 1) if(fread(&ahdr, sizeof(NroAssetHeader), 1, f) == 1) {
{ if(ahdr.magic == NROASSETHEADER_MAGIC) {
if(ahdr.magic == NROASSETHEADER_MAGIC) if((ahdr.icon.offset > 0) && (ahdr.icon.size > 0)) {
{ auto iconbuf = new u8[ahdr.icon.size]();
if((ahdr.icon.offset > 0) && (ahdr.icon.size > 0))
{
u8 *iconbuf = new u8[ahdr.icon.size]();
fseek(f, hdr.size + ahdr.icon.offset, SEEK_SET); fseek(f, hdr.size + ahdr.icon.offset, SEEK_SET);
fread(iconbuf, ahdr.icon.size, 1, f); fread(iconbuf, ahdr.icon.size, 1, f);
fs::WriteFile(nroimg, iconbuf, ahdr.icon.size, true); fs::WriteFile(nroimg, iconbuf, ahdr.icon.size, true);
@ -39,135 +33,105 @@ namespace cfg
} }
} }
std::vector<TitleRecord> QueryAllHomebrew(const std::string &base) static void CacheInstalledTitles() {
{ UL_OS_FOR_EACH_APP_RECORD(rec, {
std::vector<TitleRecord> nros; NsApplicationControlData control = {};
rc = nsGetApplicationControlData(NsApplicationControlSource_Storage, rec.application_id, &control, sizeof(control), nullptr);
FS_FOR(base, name, path, if(R_SUCCEEDED(rc)) {
{ auto fname = cfg::GetTitleCacheIconPath(rec.application_id);
if(dt->d_type & DT_DIR) fs::WriteFile(fname, control.icon, sizeof(control.icon), true);
{
auto hb = QueryAllHomebrew(path);
if(!hb.empty()) nros.insert(nros.begin(), hb.begin(), hb.end());
} }
else });
{
if(util::StringEndsWith(name, ".nro"))
{
TitleRecord rec = {};
rec.title_type = (u32)TitleType::Homebrew;
strcpy(rec.nro_target.nro_path, path.c_str());
nros.push_back(rec);
}
}
})
return nros;
} }
static void CacheInstalledTitles() static void CacheAllHomebrew(const std::string &hb_base_path) {
{ UL_FS_FOR(hb_base_path, name, path, {
NsApplicationRecord *recordbuf = new NsApplicationRecord[os::MaxInstalledCount](); if(dt->d_type & DT_DIR) {
s32 record_count = 0;
auto rc = nsListApplicationRecord(recordbuf, os::MaxInstalledCount, 0, &record_count);
if(R_SUCCEEDED(rc))
{
for(s32 i = 0; i < record_count; i++)
{
u64 appid = recordbuf[i].application_id;
NsApplicationControlData control = {};
size_t dummy;
auto rc = nsGetApplicationControlData(NsApplicationControlSource_Storage, appid, &control, sizeof(control), &dummy);
if(R_SUCCEEDED(rc))
{
auto fname = cfg::GetTitleCacheIconPath(appid);
fs::WriteFile(fname, control.icon, sizeof(control.icon), true);
}
}
}
delete[] recordbuf;
}
static void CacheAllHomebrew(const std::string &hb_base_path)
{
FS_FOR(hb_base_path, name, path,
{
if(dt->d_type & DT_DIR)
{
CacheAllHomebrew(path); CacheAllHomebrew(path);
} }
else else if(util::StringEndsWith(name, ".nro")) {
{ CacheHomebrew(path);
if(util::StringEndsWith(name, ".nro")) CacheHomebrew(path);
} }
}) });
} }
void CacheEverything(const std::string &hb_base_path) static void ProcessStringsFromNACP(RecordStrings &strs, NacpStruct *nacp) {
{ NacpLanguageEntry *lent = nullptr;
CacheInstalledTitles();
CacheAllHomebrew(hb_base_path);
}
std::string GetRecordIconPath(TitleRecord record)
{
std::string icon = record.icon;
if(icon.empty())
{
if((TitleType)record.title_type == TitleType::Homebrew) icon = GetNROCacheIconPath(record.nro_target.nro_path);
else if((TitleType)record.title_type == TitleType::Installed) icon = GetTitleCacheIconPath(record.app_id);
}
return icon;
}
static void ProcessStringsFromNACP(RecordStrings &strs, NacpStruct *nacp)
{
NacpLanguageEntry *lent = NULL;
nacpGetLanguageEntry(nacp, &lent); nacpGetLanguageEntry(nacp, &lent);
if(lent == NULL) if(lent == nullptr) {
{ for(u32 i = 0; i < 16; i++) {
for(u32 i = 0; i < 16; i++)
{
lent = &nacp->lang[i]; lent = &nacp->lang[i];
if(strlen(lent->name) && strlen(lent->author)) break; if((strlen(lent->name) > 0) && (strlen(lent->author) > 0)) {
lent = NULL; break;
}
lent = nullptr;
} }
} }
if(lent != NULL) if(lent != nullptr) {
{
strs.name = lent->name; strs.name = lent->name;
strs.author = lent->author; strs.author = lent->author;
strs.version = nacp->display_version; strs.version = nacp->display_version;
} }
} }
RecordInformation GetRecordInformation(TitleRecord record) std::vector<TitleRecord> QueryAllHomebrew(const std::string &base) {
{ std::vector<TitleRecord> nros;
UL_FS_FOR(base, name, path, {
if(dt->d_type & DT_DIR) {
auto hb = QueryAllHomebrew(path);
if(!hb.empty()) {
nros.insert(nros.begin(), hb.begin(), hb.end());
}
}
else {
if(util::StringEndsWith(name, ".nro")) {
TitleRecord rec = {};
rec.title_type = (u32)TitleType::Homebrew;
strcpy(rec.nro_target.nro_path, path.c_str());
nros.push_back(rec);
}
}
});
return nros;
}
void CacheEverything(const std::string &hb_base_path) {
CacheInstalledTitles();
CacheAllHomebrew(hb_base_path);
}
std::string GetRecordIconPath(TitleRecord record) {
std::string icon = record.icon;
if(icon.empty()) {
const auto type = static_cast<TitleType>(record.title_type);
if(type == TitleType::Homebrew) {
icon = GetNROCacheIconPath(record.nro_target.nro_path);
}
else if(type == TitleType::Installed) {
icon = GetTitleCacheIconPath(record.app_id);
}
}
return icon;
}
RecordInformation GetRecordInformation(TitleRecord record) {
RecordInformation info = {}; RecordInformation info = {};
info.icon_path = GetRecordIconPath(record); info.icon_path = GetRecordIconPath(record);
if((TitleType)record.title_type == TitleType::Homebrew) const auto type = static_cast<TitleType>(record.title_type);
{ if(type == TitleType::Homebrew) {
FILE *f = fopen(record.nro_target.nro_path, "rb"); auto f = fopen(record.nro_target.nro_path, "rb");
if(f) if(f) {
{
fseek(f, sizeof(NroStart), SEEK_SET); fseek(f, sizeof(NroStart), SEEK_SET);
NroHeader hdr = {}; NroHeader hdr = {};
if(fread(&hdr, 1, sizeof(NroHeader), f) == sizeof(NroHeader)) if(fread(&hdr, 1, sizeof(NroHeader), f) == sizeof(NroHeader)) {
{
fseek(f, hdr.size, SEEK_SET); fseek(f, hdr.size, SEEK_SET);
NroAssetHeader ahdr = {}; NroAssetHeader ahdr = {};
if(fread(&ahdr, 1, sizeof(NroAssetHeader), f) == sizeof(NroAssetHeader)) if(fread(&ahdr, 1, sizeof(NroAssetHeader), f) == sizeof(NroAssetHeader)) {
{ if(ahdr.magic == NROASSETHEADER_MAGIC) {
if(ahdr.magic == NROASSETHEADER_MAGIC) if(ahdr.nacp.size > 0) {
{
if(ahdr.nacp.size > 0)
{
NacpStruct nacp = {}; NacpStruct nacp = {};
fseek(f, hdr.size + ahdr.nacp.offset, SEEK_SET); fseek(f, hdr.size + ahdr.nacp.offset, SEEK_SET);
fread(&nacp, 1, ahdr.nacp.size, f); fread(&nacp, 1, ahdr.nacp.size, f);
ProcessStringsFromNACP(info.strings, &nacp); ProcessStringsFromNACP(info.strings, &nacp);
} }
} }
@ -176,78 +140,80 @@ namespace cfg
fclose(f); fclose(f);
} }
} }
else else {
{
NsApplicationControlData cdata = {}; NsApplicationControlData cdata = {};
nsGetApplicationControlData(NsApplicationControlSource_Storage, record.app_id, &cdata, sizeof(cdata), NULL); nsGetApplicationControlData(NsApplicationControlSource_Storage, record.app_id, &cdata, sizeof(cdata), nullptr);
ProcessStringsFromNACP(info.strings, &cdata.nacp); ProcessStringsFromNACP(info.strings, &cdata.nacp);
} }
if(!record.name.empty()) info.strings.name = record.name; if(!record.name.empty()) {
if(!record.author.empty()) info.strings.author = record.author; info.strings.name = record.name;
if(!record.version.empty()) info.strings.version = record.version; }
if(!record.author.empty()) {
info.strings.author = record.author;
}
if(!record.version.empty()) {
info.strings.version = record.version;
}
return info; return info;
} }
Theme LoadTheme(const std::string &base_name) Theme LoadTheme(const std::string &base_name) {
{
Theme theme = {}; Theme theme = {};
theme.base_name = base_name; theme.base_name = base_name;
auto themedir = std::string(UL_THEMES_PATH) + "/" + base_name; auto themedir = std::string(UL_THEMES_PATH) + "/" + base_name;
auto metajson = themedir + "/theme/Manifest.json"; auto metajson = themedir + "/theme/Manifest.json";
if(base_name.empty() || !fs::ExistsFile(metajson)) themedir = CFG_THEME_DEFAULT; if(base_name.empty() || !fs::ExistsFile(metajson)) {
themedir = CFG_THEME_DEFAULT;
}
metajson = themedir + "/theme/Manifest.json"; metajson = themedir + "/theme/Manifest.json";
auto [rc, meta] = util::LoadJSONFromFile(metajson); JSON meta;
if(R_SUCCEEDED(rc)) auto rc = util::LoadJSONFromFile(meta, metajson);
{ if(R_SUCCEEDED(rc)) {
theme.manifest.name = meta.value("name", "'" + base_name + "'"); theme.manifest.name = meta.value("name", "'" + base_name + "'");
theme.manifest.format_version = meta.value("format_version", 0); theme.manifest.format_version = meta.value("format_version", 0);
theme.manifest.release = meta.value("release", ""); theme.manifest.release = meta.value("release", "");
theme.manifest.description = meta.value("description", ""); theme.manifest.description = meta.value("description", "");
theme.manifest.author = meta.value("author", ""); theme.manifest.author = meta.value("author", "");
theme.path = themedir; theme.path = themedir;
return theme;
} }
else return LoadTheme(""); return LoadTheme("");
return theme;
} }
std::vector<Theme> LoadThemes() std::vector<Theme> LoadThemes() {
{
std::vector<Theme> themes; std::vector<Theme> themes;
UL_FS_FOR(std::string(UL_THEMES_PATH), name, path, {
FS_FOR(std::string(UL_THEMES_PATH), name, path, auto theme = LoadTheme(name);
{ if(!theme.path.empty()) {
Theme t = LoadTheme(name); themes.push_back(theme);
if(!t.path.empty()) themes.push_back(t); }
}) });
return themes; return themes;
} }
std::string GetAssetByTheme(const Theme &base, const std::string &resource_base) std::string GetAssetByTheme(const Theme &base, const std::string &resource_base) {
{
auto base_res = base.path + "/" + resource_base; auto base_res = base.path + "/" + resource_base;
if(fs::ExistsFile(base_res)) return base_res; if(fs::ExistsFile(base_res)) {
return base_res;
}
base_res = std::string(CFG_THEME_DEFAULT) + "/" + resource_base; base_res = std::string(CFG_THEME_DEFAULT) + "/" + resource_base;
if(fs::ExistsFile(base_res)) return base_res; if(fs::ExistsFile(base_res)) {
return base_res;
}
return ""; return "";
} }
std::string GetLanguageJSONPath(const std::string &lang) std::string GetLanguageString(const JSON &lang, const JSON &def, const std::string &name) {
{
return UL_BASE_SD_DIR "/lang/" + lang + ".json";
}
std::string GetLanguageString(const JSON &lang, const JSON &def, const std::string &name)
{
auto str = lang.value(name, ""); auto str = lang.value(name, "");
if(str.empty()) str = def.value(name, ""); if(str.empty()) {
str = def.value(name, "");
}
return str; return str;
} }
Config CreateNewAndLoadConfig() Config CreateNewAndLoadConfig() {
{
// Default constructor sets everything // Default constructor sets everything
Config cfg; Config cfg = {};
SaveConfig(cfg); SaveConfig(cfg);
return cfg; return cfg;
} }
@ -255,124 +221,139 @@ namespace cfg
Config LoadConfig() Config LoadConfig()
{ {
// Default constructor sets everything // Default constructor sets everything
Config cfg; Config cfg = {};
auto [rc, cfgjson] = util::LoadJSONFromFile(CFG_CONFIG_JSON); JSON cfgjson;
if(R_SUCCEEDED(rc)) auto rc = util::LoadJSONFromFile(cfgjson, CFG_CONFIG_JSON);
{ if(R_SUCCEEDED(rc)) {
cfg.theme_name = cfgjson.value("theme_name", ""); cfg.theme_name = cfgjson.value("theme_name", "");
cfg.system_title_override_enabled = cfgjson.value("system_title_override_enabled", false); cfg.system_title_override_enabled = cfgjson.value("system_title_override_enabled", false);
cfg.viewer_usb_enabled = cfgjson.value("viewer_usb_enabled", false); cfg.viewer_usb_enabled = cfgjson.value("viewer_usb_enabled", false);
auto menu_id_str = cfgjson.value("menu_program_id", ""); auto menu_id_str = cfgjson.value("menu_program_id", "");
if(!menu_id_str.empty()) cfg.menu_program_id = util::Get64FromString(menu_id_str); if(!menu_id_str.empty()) {
cfg.menu_program_id = util::Get64FromString(menu_id_str);
}
auto hb_applet_id_str = cfgjson.value("homebrew_applet_program_id", ""); auto hb_applet_id_str = cfgjson.value("homebrew_applet_program_id", "");
if(!hb_applet_id_str.empty()) cfg.homebrew_applet_program_id = util::Get64FromString(hb_applet_id_str); if(!hb_applet_id_str.empty()) {
cfg.homebrew_applet_program_id = util::Get64FromString(hb_applet_id_str);
}
auto hb_title_id_str = cfgjson.value("homebrew_title_application_id", ""); auto hb_title_id_str = cfgjson.value("homebrew_title_application_id", "");
if(!hb_title_id_str.empty()) cfg.homebrew_title_application_id = util::Get64FromString(hb_title_id_str); if(!hb_title_id_str.empty()) {
cfg.homebrew_title_application_id = util::Get64FromString(hb_title_id_str);
}
// Doing this saves any fields not set previously
SaveConfig(cfg);
return cfg;
} }
else fs::DeleteFile(CFG_CONFIG_JSON);
{ return CreateNewAndLoadConfig();
fs::DeleteFile(CFG_CONFIG_JSON);
return CreateNewAndLoadConfig();
}
// Doing this saves any fields not set previously
SaveConfig(cfg);
return cfg;
}
Config EnsureConfig()
{
if(!fs::ExistsFile(CFG_CONFIG_JSON)) return CreateNewAndLoadConfig();
return LoadConfig();
} }
void SaveConfig(const Config &cfg) void SaveConfig(const Config &cfg)
{ {
fs::DeleteFile(CFG_CONFIG_JSON); fs::DeleteFile(CFG_CONFIG_JSON);
JSON j = JSON::object(); auto json = JSON::object();
j["theme_name"] = cfg.theme_name; json["theme_name"] = cfg.theme_name;
j["viewer_usb_enabled"] = cfg.viewer_usb_enabled; json["viewer_usb_enabled"] = cfg.viewer_usb_enabled;
j["system_title_override_enabled"] = cfg.system_title_override_enabled; json["system_title_override_enabled"] = cfg.system_title_override_enabled;
j["menu_program_id"] = util::FormatApplicationId(cfg.menu_program_id); json["menu_program_id"] = util::FormatApplicationId(cfg.menu_program_id);
j["homebrew_applet_program_id"] = util::FormatApplicationId(cfg.homebrew_applet_program_id); json["homebrew_applet_program_id"] = util::FormatApplicationId(cfg.homebrew_applet_program_id);
j["homebrew_title_application_id"] = util::FormatApplicationId(cfg.homebrew_title_application_id); json["homebrew_title_application_id"] = util::FormatApplicationId(cfg.homebrew_title_application_id);
std::ofstream ofs(CFG_CONFIG_JSON); std::ofstream ofs(CFG_CONFIG_JSON);
ofs << std::setw(4) << j; ofs << std::setw(4) << json;
ofs.close(); ofs.close();
} }
void SaveRecord(TitleRecord &record) void SaveRecord(TitleRecord &record)
{ {
JSON entry = JSON::object(); auto entry = JSON::object();
entry["type"] = record.title_type; entry["type"] = record.title_type;
entry["folder"] = record.sub_folder; entry["folder"] = record.sub_folder;
if(!record.name.empty()) entry["name"] = record.name; if(!record.name.empty()) {
if(!record.author.empty()) entry["author"] = record.author; entry["name"] = record.name;
if(!record.version.empty()) entry["version"] = record.version; }
if(!record.icon.empty()) entry["icon"] = record.icon; if(!record.author.empty()) {
entry["author"] = record.author;
}
if(!record.version.empty()) {
entry["version"] = record.version;
}
if(!record.icon.empty()) {
entry["icon"] = record.icon;
}
// Prepare JSON path // Prepare JSON path
std::string basepath = UL_ENTRIES_PATH; std::string basepath = UL_ENTRIES_PATH;
std::string json = basepath; std::string json = basepath;
if((TitleType)record.title_type == TitleType::Homebrew) const auto type = static_cast<TitleType>(record.title_type);
{ if(type == TitleType::Homebrew) {
auto jsonname = std::to_string(fs::GetFileSize(record.nro_target.nro_path)) + ".json"; auto jsonname = std::to_string(fs::GetFileSize(record.nro_target.nro_path)) + ".json";
if(record.json_name.empty()) record.json_name = jsonname; if(record.json_name.empty()) {
record.json_name = jsonname;
}
json += "/" + jsonname; json += "/" + jsonname;
entry["nro_path"] = record.nro_target.nro_path; entry["nro_path"] = record.nro_target.nro_path;
if(strlen(record.nro_target.nro_argv)) if(strlen(record.nro_target.nro_argv)) {
{ if(strcasecmp(record.nro_target.nro_path, record.nro_target.nro_argv) != 0) {
if(strcasecmp(record.nro_target.nro_path, record.nro_target.nro_argv) != 0) entry["nro_argv"] = record.nro_target.nro_argv; entry["nro_argv"] = record.nro_target.nro_argv;
}
} }
} }
else if((TitleType)record.title_type == TitleType::Installed) else if(type == TitleType::Installed) {
{
auto strappid = util::FormatApplicationId(record.app_id); auto strappid = util::FormatApplicationId(record.app_id);
auto jsonname = strappid + ".json"; auto jsonname = strappid + ".json";
if(record.json_name.empty()) record.json_name = jsonname; if(record.json_name.empty()) {
record.json_name = jsonname;
}
json += "/" + jsonname; json += "/" + jsonname;
entry["application_id"] = strappid; entry["application_id"] = strappid;
} }
if(!record.json_name.empty()) json = basepath + "/" + record.json_name; if(!record.json_name.empty()) {
if(fs::ExistsFile(json)) fs::DeleteFile(json); json = basepath + "/" + record.json_name;
}
if(fs::ExistsFile(json)) {
fs::DeleteFile(json);
}
std::ofstream ofs(json); std::ofstream ofs(json);
ofs << std::setw(4) << entry; ofs << std::setw(4) << entry;
ofs.close(); ofs.close();
} }
void RemoveRecord(TitleRecord &record) void RemoveRecord(TitleRecord &record) {
{
// Prepare JSON path // Prepare JSON path
std::string basepath = UL_ENTRIES_PATH; std::string basepath = UL_ENTRIES_PATH;
std::string json = basepath; std::string json = basepath;
if((TitleType)record.title_type == TitleType::Homebrew) const auto type = static_cast<TitleType>(record.title_type);
{ if(type == TitleType::Homebrew) {
auto jsonname = std::to_string(fs::GetFileSize(record.nro_target.nro_path)) + ".json"; auto jsonname = std::to_string(fs::GetFileSize(record.nro_target.nro_path)) + ".json";
if(record.json_name.empty()) record.json_name = jsonname; if(record.json_name.empty()) {
record.json_name = jsonname;
}
json += "/" + jsonname; json += "/" + jsonname;
} }
else if((TitleType)record.title_type == TitleType::Installed) else if(type == TitleType::Installed) {
{
auto strappid = util::FormatApplicationId(record.app_id); auto strappid = util::FormatApplicationId(record.app_id);
auto jsonname = strappid + ".json"; auto jsonname = strappid + ".json";
if(record.json_name.empty()) record.json_name = jsonname; if(record.json_name.empty()) {
record.json_name = jsonname;
}
json += "/" + jsonname; json += "/" + jsonname;
} }
if(!record.json_name.empty()) json = basepath + "/" + record.json_name; if(!record.json_name.empty()) {
json = basepath + "/" + record.json_name;
}
fs::DeleteFile(json); fs::DeleteFile(json);
} }
bool MoveTitleToDirectory(TitleList &list, u64 app_id, const std::string &folder) bool MoveTitleToDirectory(TitleList &list, u64 app_id, const std::string &folder) {
{
bool title_found = false; bool title_found = false;
TitleRecord record_copy = {}; TitleRecord record_copy = {};
std::string recjson; std::string recjson;
// Search in root first // Search in root first
auto find = STL_FIND_IF(list.root.titles, tit, (tit.app_id == app_id)); auto find = STL_FIND_IF(list.root.titles, tit, (tit.app_id == app_id));
if(STL_FOUND(list.root.titles, find)) if(STL_FOUND(list.root.titles, find)) {
{
if(folder.empty()) return true; // It is already on root...? if(folder.empty()) return true; // It is already on root...?
recjson = STL_UNWRAP(find).json_name; recjson = STL_UNWRAP(find).json_name;
@ -380,14 +361,15 @@ namespace cfg
title_found = true; title_found = true;
} }
if(!title_found) // If not found yet, search on all dirs if the title is present // If not found yet, search on all dirs if the title is present
{ if(!title_found) {
for(auto &fld: list.folders) for(auto &fld: list.folders) {
{
auto find = STL_FIND_IF(fld.titles, entry, (entry.app_id == app_id)); auto find = STL_FIND_IF(fld.titles, entry, (entry.app_id == app_id));
if(STL_FOUND(fld.titles, find)) if(STL_FOUND(fld.titles, find)) {
{ // It is already on that folder...?
if(fld.name == folder) return true; // It is already on that folder...? if(fld.name == folder) {
return true;
}
recjson = STL_UNWRAP(find).json_name; recjson = STL_UNWRAP(find).json_name;
fld.titles.erase(find); fld.titles.erase(find);
@ -397,27 +379,24 @@ namespace cfg
} }
} }
if(title_found) if(title_found) {
{
TitleRecord title = {}; TitleRecord title = {};
title.app_id = app_id; title.app_id = app_id;
title.title_type = (u32)TitleType::Installed; title.title_type = (u32)TitleType::Installed;
title.json_name = recjson; title.json_name = recjson;
title.sub_folder = folder; title.sub_folder = folder;
if(folder.empty()) // Add (move) it to root again // Add (move) it to root again
{ if(folder.empty()) {
list.root.titles.push_back(title); list.root.titles.push_back(title);
} }
else // Add it to the new folder // Add it to the new folder
{ else {
auto find2 = STL_FIND_IF(list.folders, fld, (fld.name == folder)); auto find2 = STL_FIND_IF(list.folders, fld, (fld.name == folder));
if(STL_FOUND(list.folders, find2)) if(STL_FOUND(list.folders, find2)) {
{
STL_UNWRAP(find2).titles.push_back(title); STL_UNWRAP(find2).titles.push_back(title);
} }
else else {
{
TitleFolder fld = {}; TitleFolder fld = {};
fld.name = folder; fld.name = folder;
fld.titles.push_back(title); fld.titles.push_back(title);
@ -431,31 +410,33 @@ namespace cfg
return title_found; return title_found;
} }
bool MoveRecordTo(TitleList &list, TitleRecord record, const std::string &folder) bool MoveRecordTo(TitleList &list, TitleRecord record, const std::string &folder) {
{
bool title_found = false; bool title_found = false;
TitleRecord record_copy = {}; TitleRecord record_copy = {};
std::string recjson; std::string recjson;
const auto type = static_cast<TitleType>(record.title_type);
// Search in root first // Search in root first
if((TitleType)record.title_type == TitleType::Installed) if(type == TitleType::Installed) {
{
auto find = STL_FIND_IF(list.root.titles, tit, (tit.title_type == record.title_type) && (tit.app_id == record.app_id)); auto find = STL_FIND_IF(list.root.titles, tit, (tit.title_type == record.title_type) && (tit.app_id == record.app_id));
if(STL_FOUND(list.root.titles, find)) if(STL_FOUND(list.root.titles, find)) {
{ // It is already on root...?
if(folder.empty()) return true; // It is already on root...? if(folder.empty()) {
return true;
}
recjson = STL_UNWRAP(find).json_name; recjson = STL_UNWRAP(find).json_name;
list.root.titles.erase(find); list.root.titles.erase(find);
title_found = true; title_found = true;
} }
} }
else else {
{
auto find = STL_FIND_IF(list.root.titles, tit, (tit.title_type == record.title_type) && (tit.nro_target.nro_path == record.nro_target.nro_path)); auto find = STL_FIND_IF(list.root.titles, tit, (tit.title_type == record.title_type) && (tit.nro_target.nro_path == record.nro_target.nro_path));
if(STL_FOUND(list.root.titles, find)) if(STL_FOUND(list.root.titles, find)) {
{ // It is already on root...?
if(folder.empty()) return true; // It is already on root...? if(folder.empty()) {
return true;
}
recjson = STL_UNWRAP(find).json_name; recjson = STL_UNWRAP(find).json_name;
list.root.titles.erase(find); list.root.titles.erase(find);
@ -463,16 +444,16 @@ namespace cfg
} }
} }
if(!title_found) // If not found yet, search on all dirs if the title is present // If not found yet, search on all dirs if the title is present
{ if(!title_found) {
for(auto &fld: list.folders) for(auto &fld: list.folders) {
{ if(type == TitleType::Installed) {
if((TitleType)record.title_type == TitleType::Installed)
{
auto find = STL_FIND_IF(fld.titles, tit, (tit.title_type == record.title_type) && (tit.app_id == record.app_id)); auto find = STL_FIND_IF(fld.titles, tit, (tit.title_type == record.title_type) && (tit.app_id == record.app_id));
if(STL_FOUND(fld.titles, find)) if(STL_FOUND(fld.titles, find)) {
{ // It is already on that folder...?
if(fld.name == folder) return true; // It is already on that folder...? if(fld.name == folder) {
return true;
}
recjson = STL_UNWRAP(find).json_name; recjson = STL_UNWRAP(find).json_name;
fld.titles.erase(find); fld.titles.erase(find);
@ -480,12 +461,13 @@ namespace cfg
break; break;
} }
} }
else else {
{
auto find = STL_FIND_IF(fld.titles, tit, (tit.title_type == record.title_type) && (tit.nro_target.nro_path == record.nro_target.nro_path)); auto find = STL_FIND_IF(fld.titles, tit, (tit.title_type == record.title_type) && (tit.nro_target.nro_path == record.nro_target.nro_path));
if(STL_FOUND(fld.titles, find)) if(STL_FOUND(fld.titles, find)) {
{ // It is already on that folder...?
if(fld.name == folder) return true; // It is already on that folder...? if(fld.name == folder) {
return true;
}
recjson = STL_UNWRAP(find).json_name; recjson = STL_UNWRAP(find).json_name;
fld.titles.erase(find); fld.titles.erase(find);
@ -496,25 +478,22 @@ namespace cfg
} }
} }
if(title_found) if(title_found) {
{
TitleRecord title = record; TitleRecord title = record;
title.json_name = recjson; title.json_name = recjson;
title.sub_folder = folder; title.sub_folder = folder;
if(folder.empty()) // Add (move) it to root again // Add (move) it to root again
{ if(folder.empty()) {
list.root.titles.push_back(title); list.root.titles.push_back(title);
} }
else // Add it to the new folder // Add it to the new folder
{ else {
auto find2 = STL_FIND_IF(list.folders, fld, (fld.name == folder)); auto find2 = STL_FIND_IF(list.folders, fld, (fld.name == folder));
if(STL_FOUND(list.folders, find2)) if(STL_FOUND(list.folders, find2)) {
{
STL_UNWRAP(find2).titles.push_back(title); STL_UNWRAP(find2).titles.push_back(title);
} }
else else {
{
TitleFolder fld = {}; TitleFolder fld = {};
fld.name = folder; fld.name = folder;
fld.titles.push_back(title); fld.titles.push_back(title);
@ -530,25 +509,20 @@ namespace cfg
TitleFolder &FindFolderByName(TitleList &list, const std::string &name) TitleFolder &FindFolderByName(TitleList &list, const std::string &name)
{ {
if(!name.empty()) if(!name.empty()) {
{
auto f = STL_FIND_IF(list.folders, fld, (fld.name == name)); auto f = STL_FIND_IF(list.folders, fld, (fld.name == name));
if(STL_FOUND(list.folders, f)) if(STL_FOUND(list.folders, f)) {
{
return STL_UNWRAP(f); return STL_UNWRAP(f);
} }
} }
return list.root; return list.root;
} }
void RenameFolder(TitleList &list, const std::string &old_name, const std::string &new_name) void RenameFolder(TitleList &list, const std::string &old_name, const std::string &new_name) {
{
auto &folder = FindFolderByName(list, old_name); auto &folder = FindFolderByName(list, old_name);
if(!folder.name.empty()) if(!folder.name.empty()) {
{
folder.name = new_name; folder.name = new_name;
for(auto &entry: folder.titles) for(auto &entry: folder.titles) {
{
entry.sub_folder = new_name; entry.sub_folder = new_name;
SaveRecord(entry); SaveRecord(entry);
} }
@ -560,48 +534,42 @@ namespace cfg
bool title_found = false; bool title_found = false;
TitleRecord record_copy = {}; TitleRecord record_copy = {};
std::string recjson; std::string recjson;
const auto type = static_cast<TitleType>(record.title_type);
// Search in root first // Search in root first
if((TitleType)record.title_type == TitleType::Installed) if(type == TitleType::Installed) {
{
auto find = STL_FIND_IF(list.root.titles, tit, (tit.title_type == record.title_type) && (tit.app_id == record.app_id)); auto find = STL_FIND_IF(list.root.titles, tit, (tit.title_type == record.title_type) && (tit.app_id == record.app_id));
if(STL_FOUND(list.root.titles, find)) if(STL_FOUND(list.root.titles, find)) {
{ if(!STL_UNWRAP(find).json_name.empty()) {
if(!STL_UNWRAP(find).json_name.empty()) title_found = true; title_found = true;
}
} }
} }
else else {
{
auto find = STL_FIND_IF(list.root.titles, tit, (tit.title_type == record.title_type) && (tit.nro_target.nro_path == record.nro_target.nro_path)); auto find = STL_FIND_IF(list.root.titles, tit, (tit.title_type == record.title_type) && (tit.nro_target.nro_path == record.nro_target.nro_path));
if(STL_FOUND(list.root.titles, find)) if(STL_FOUND(list.root.titles, find)) {
{ if(!STL_UNWRAP(find).json_name.empty()) {
if(!STL_UNWRAP(find).json_name.empty()) title_found = true; title_found = true;
}
} }
} }
if(!title_found) // If not found yet, search on all dirs if the title is present // If not found yet, search on all dirs if the title is present
{ if(!title_found) {
for(auto &fld: list.folders) for(auto &fld: list.folders) {
{ if(type == TitleType::Installed) {
if((TitleType)record.title_type == TitleType::Installed)
{
auto find = STL_FIND_IF(fld.titles, tit, (tit.title_type == record.title_type) && (tit.app_id == record.app_id)); auto find = STL_FIND_IF(fld.titles, tit, (tit.title_type == record.title_type) && (tit.app_id == record.app_id));
if(STL_FOUND(fld.titles, find)) if(STL_FOUND(fld.titles, find)) {
{ if(!STL_UNWRAP(find).json_name.empty()) {
if(!STL_UNWRAP(find).json_name.empty())
{
title_found = true; title_found = true;
break; break;
} }
} }
} }
else else {
{
auto find = STL_FIND_IF(fld.titles, tit, (tit.title_type == record.title_type) && (tit.nro_target.nro_path == record.nro_target.nro_path)); auto find = STL_FIND_IF(fld.titles, tit, (tit.title_type == record.title_type) && (tit.nro_target.nro_path == record.nro_target.nro_path));
if(STL_FOUND(fld.titles, find)) if(STL_FOUND(fld.titles, find)) {
{ if(!STL_UNWRAP(find).json_name.empty()) {
if(!STL_UNWRAP(find).json_name.empty())
{
title_found = true; title_found = true;
break; break;
} }
@ -613,52 +581,43 @@ namespace cfg
return title_found; return title_found;
} }
TitleList LoadTitleList() TitleList LoadTitleList() {
{
TitleList list = {}; TitleList list = {};
// Installed titles first // Installed titles first
auto titles = os::QueryInstalledTitles(); auto titles = os::QueryInstalledTitles();
FS_FOR(std::string(UL_ENTRIES_PATH), name, path, UL_FS_FOR(std::string(UL_ENTRIES_PATH), name, path, {
{ JSON entry;
auto [rc, entry] = util::LoadJSONFromFile(path); auto rc = util::LoadJSONFromFile(entry, path);
if(R_SUCCEEDED(rc)) if(R_SUCCEEDED(rc)) {
{ auto type = static_cast<TitleType>(entry.value("type", 0));
TitleType type = entry.value("type", TitleType::Invalid); if(type == TitleType::Installed) {
if(type == TitleType::Installed)
{
std::string appidstr = entry.value("application_id", ""); std::string appidstr = entry.value("application_id", "");
if(!appidstr.empty()) if(!appidstr.empty()) {
{
std::string folder = entry.value("folder", ""); std::string folder = entry.value("folder", "");
u64 appid = util::Get64FromString(appidstr); auto appid = util::Get64FromString(appidstr);
if(appid > 0) if(appid > 0) {
{ if(!folder.empty()) {
if(!folder.empty())
{
TitleRecord rec = {}; TitleRecord rec = {};
rec.json_name = name; rec.json_name = name;
rec.app_id = appid; rec.app_id = appid;
rec.title_type = (u32)TitleType::Installed; rec.title_type = static_cast<u32>(TitleType::Installed);
rec.name = entry.value("name", ""); rec.name = entry.value("name", "");
rec.author = entry.value("author", ""); rec.author = entry.value("author", "");
rec.version = entry.value("version", ""); rec.version = entry.value("version", "");
rec.icon = entry.value("icon", ""); rec.icon = entry.value("icon", "");
auto find = STL_FIND_IF(titles, tit, (tit.app_id == appid)); auto find = STL_FIND_IF(titles, tit, (tit.app_id == appid));
if(STL_FOUND(titles, find)) if(STL_FOUND(titles, find)) {
{
titles.erase(find); titles.erase(find);
} }
auto find2 = STL_FIND_IF(list.folders, fld, (fld.name == folder)); auto find2 = STL_FIND_IF(list.folders, fld, (fld.name == folder));
if(STL_FOUND(list.folders, find2)) if(STL_FOUND(list.folders, find2)) {
{
STL_UNWRAP(find2).titles.push_back(rec); STL_UNWRAP(find2).titles.push_back(rec);
} }
else else {
{
TitleFolder fld = {}; TitleFolder fld = {};
fld.name = folder; fld.name = folder;
fld.titles.push_back(rec); fld.titles.push_back(rec);
@ -668,11 +627,9 @@ namespace cfg
} }
} }
} }
else if(type == TitleType::Homebrew) else if(type == TitleType::Homebrew) {
{
std::string nropath = entry.value("nro_path", ""); std::string nropath = entry.value("nro_path", "");
if(fs::ExistsFile(nropath)) if(fs::ExistsFile(nropath)) {
{
TitleRecord rec = {}; TitleRecord rec = {};
rec.json_name = name; rec.json_name = name;
rec.title_type = (u32)type; rec.title_type = (u32)type;
@ -682,18 +639,22 @@ namespace cfg
std::string argv = entry.value("nro_argv", ""); std::string argv = entry.value("nro_argv", "");
strcpy(rec.nro_target.nro_path, nropath.c_str()); strcpy(rec.nro_target.nro_path, nropath.c_str());
if(!argv.empty()) strcpy(rec.nro_target.nro_argv, argv.c_str()); if(!argv.empty()) {
strcpy(rec.nro_target.nro_argv, argv.c_str());
}
std::string folder = entry.value("folder", ""); std::string folder = entry.value("folder", "");
rec.sub_folder = folder; rec.sub_folder = folder;
rec.icon = entry.value("icon", ""); rec.icon = entry.value("icon", "");
if(folder.empty()) list.root.titles.push_back(rec); if(folder.empty()) {
else list.root.titles.push_back(rec);
{ }
else {
auto find = STL_FIND_IF(list.folders, fld, (fld.name == folder)); auto find = STL_FIND_IF(list.folders, fld, (fld.name == folder));
if(STL_FOUND(list.folders, find)) STL_UNWRAP(find).titles.push_back(rec); if(STL_FOUND(list.folders, find)) {
else STL_UNWRAP(find).titles.push_back(rec);
{ }
else {
TitleFolder fld = {}; TitleFolder fld = {};
fld.name = folder; fld.name = folder;
fld.titles.push_back(rec); fld.titles.push_back(rec);
@ -703,29 +664,28 @@ namespace cfg
} }
} }
} }
}) });
list.root.titles.insert(list.root.titles.end(), titles.begin(), titles.end()); list.root.titles.insert(list.root.titles.end(), titles.begin(), titles.end());
return list; return list;
} }
std::string GetTitleCacheIconPath(u64 app_id) std::string GetTitleCacheIconPath(u64 app_id) {
{
auto strappid = util::FormatApplicationId(app_id); auto strappid = util::FormatApplicationId(app_id);
return UL_BASE_SD_DIR "/title/" + strappid + ".jpg"; return UL_BASE_SD_DIR "/title/" + strappid + ".jpg";
} }
std::string GetNROCacheIconPath(const std::string &path) std::string GetNROCacheIconPath(const std::string &path) {
{
char pathcopy[FS_MAX_PATH] = {0}; char pathcopy[FS_MAX_PATH] = {0};
strcpy(pathcopy, path.c_str()); strcpy(pathcopy, path.c_str());
char hash[0x20] = {0}; u8 hash[0x20] = {0};
sha256CalculateHash(hash, pathcopy, FS_MAX_PATH); sha256CalculateHash(hash, pathcopy, FS_MAX_PATH);
std::string out = UL_BASE_SD_DIR "/nro/"; std::string out = UL_BASE_SD_DIR "/nro/";
std::stringstream strm; std::stringstream strm;
strm << out; strm << out;
for(u32 i = 0; i < 0x10; i++) strm << std::setw(2) << std::setfill('0') << std::hex << std::nouppercase << (int)hash[i]; for(u32 i = 0; i < 0x10; i++) {
strm << std::setw(2) << std::setfill('0') << std::hex << std::nouppercase << (u32)hash[i];
}
strm << ".jpg"; strm << ".jpg";
return strm.str(); return strm.str();
} }

View file

@ -1,29 +1,25 @@
#include <db/db_Save.hpp> #include <db/db_Save.hpp>
#include <fs/fs_Stdio.hpp>
#include <util/util_Convert.hpp>
namespace db namespace db {
{
Result Mount() Result Mount() {
{
FsFileSystem savefs; FsFileSystem savefs;
FsSaveDataAttribute attr = {}; FsSaveDataAttribute attr = {};
attr.system_save_data_id = HomeMenuSaveDataId; attr.system_save_data_id = HomeMenuSaveDataId;
attr.save_data_type = FsSaveDataType_System; attr.save_data_type = FsSaveDataType_System;
auto rc = fsOpenSaveDataFileSystemBySystemSaveDataId(&savefs, FsSaveDataSpaceId_System, &attr); R_TRY(fsOpenSaveDataFileSystemBySystemSaveDataId(&savefs, FsSaveDataSpaceId_System, &attr));
if(R_SUCCEEDED(rc)) fsdevMountDevice(UL_DB_MOUNT_NAME, savefs); fsdevMountDevice(UL_DB_MOUNT_NAME, savefs);
return rc; return ResultSuccess;
} }
void Unmount() void Unmount() {
{
fsdevUnmountDevice(UL_DB_MOUNT_NAME); fsdevUnmountDevice(UL_DB_MOUNT_NAME);
} }
void Commit() void Commit() {
{
fsdevCommitDevice(UL_DB_MOUNT_NAME); fsdevCommitDevice(UL_DB_MOUNT_NAME);
} }
} }

View file

@ -0,0 +1,73 @@
#include <dmi/dmi_DaemonMenuInteraction.hpp>
#include <fs/fs_Stdio.hpp>
#include <am/am_LibraryApplet.hpp>
namespace dmi {
namespace impl {
#define _UL_AM_DMI_IMPL_RC_TRY_LOOP(...) ({ \
auto rc = ResultSuccess; \
do { \
__VA_ARGS__ \
svcSleepThread(10'000'000); \
} while(R_FAILED(rc)); \
return rc; \
})
#define _UL_AM_DMI_IMPL_RC_TRY(...) ({ \
auto rc = ResultSuccess; \
__VA_ARGS__ \
return rc; \
})
#define _UL_AM_DMI_IMPL_RC(...) ({ \
if(wait) { \
_UL_AM_DMI_IMPL_RC_TRY_LOOP( __VA_ARGS__ ); \
} \
else { \
_UL_AM_DMI_IMPL_RC_TRY( __VA_ARGS__ ); \
} \
})
Result DaemonWriteImpl(void *data, size_t size, bool wait) {
_UL_AM_DMI_IMPL_RC(
rc = am::LibraryAppletSend(data, size);
);
}
Result DaemonReadImpl(void *data, size_t size, bool wait) {
_UL_AM_DMI_IMPL_RC(
rc = am::LibraryAppletRead(data, size);
);
}
Result MenuWriteImpl(void *data, size_t size, bool wait) {
_UL_AM_DMI_IMPL_RC(
AppletStorage st;
rc = appletCreateStorage(&st, size);
if(R_SUCCEEDED(rc))
{
rc = appletStorageWrite(&st, 0, data, size);
if(R_SUCCEEDED(rc)) {
rc = appletPushOutData(&st);
}
appletStorageClose(&st);
}
);
}
Result MenuReadImpl(void *data, size_t size, bool wait) {
_UL_AM_DMI_IMPL_RC(
AppletStorage st;
rc = appletPopInData(&st);
if(R_SUCCEEDED(rc)) {
rc = appletStorageRead(&st, 0, data, size);
}
appletStorageClose(&st);
);
}
}
}

View file

@ -1,114 +0,0 @@
#include <fs/fs_Stdio.hpp>
#include <util/util_String.hpp>
namespace fs
{
static bool ExistsImpl(size_t st_mode, const std::string &path)
{
struct stat st;
return (stat(path.c_str(), &st) == 0) && (st.st_mode & st_mode);
}
bool ExistsFile(const std::string &path)
{
return ExistsImpl(S_IFREG, path);
}
bool ExistsDirectory(const std::string &path)
{
return ExistsImpl(S_IFDIR, path);
}
void CreateDirectory(const std::string &path)
{
mkdir(path.c_str(), 777);
}
void CreateFile(const std::string &path)
{
fsdevCreateFile(path.c_str(), 0, 0);
}
void CreateConcatenationFile(const std::string &path)
{
fsdevCreateFile(path.c_str(), 0, FsCreateOption_BigFile);
}
void DeleteDirectory(const std::string &path)
{
fsdevDeleteDirectoryRecursively(path.c_str());
}
void DeleteFile(const std::string &path)
{
remove(path.c_str());
}
void MoveFile(const std::string &p1, const std::string &p2)
{
rename(p1.c_str(), p2.c_str());
}
void CopyFile(const std::string &p, const std::string &np)
{
FILE *inf = fopen(p.c_str(), "rb");
if(inf)
{
FILE *outf = fopen(np.c_str(), "wb");
if(outf)
{
fseek(inf, 0, SEEK_END);
u64 fsize = ftell(inf);
rewind(inf);
u64 readsize = 0x4000;
u64 tocopy = fsize;
u8 *tmp = new u8[readsize]();
memset(tmp, 0, readsize);
while(tocopy)
{
auto read = fread(tmp, 1, std::min(readsize, tocopy), inf);
fwrite(tmp, 1, read, outf);
tocopy -= read;
}
delete[] tmp;
fclose(outf);
}
fclose(inf);
}
}
static void HandleDirectoryImpl(bool copy, const std::string &d, const std::string &nd)
{
DIR *dp = opendir(d.c_str());
CreateDirectory(nd);
if(dp)
{
dirent *dt;
while(true)
{
dt = readdir(dp);
if(dt == NULL) break;
std::string fullp = d + "/" + std::string(dt->d_name);
std::string nfullp = nd + "/" + std::string(dt->d_name);
if(dt->d_type & DT_DIR) HandleDirectoryImpl(copy, fullp, nfullp);
else if(dt->d_type & DT_REG)
{
if(copy) CopyFile(fullp, nfullp);
else MoveFile(fullp, nfullp);
}
}
closedir(dp);
}
if(!copy) DeleteDirectory(d);
}
void MoveDirectory(const std::string &d, const std::string &nd)
{
return HandleDirectoryImpl(false, d, nd);
}
void CopyDirectory(const std::string &d, const std::string &nd)
{
return HandleDirectoryImpl(true, d, nd);
}
}

View file

@ -1,62 +1,55 @@
#include <net/net_Service.hpp> #include <net/net_Service.hpp>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h> #include <unistd.h>
namespace net namespace net {
{
Result Initialize() Result Initialize() {
{ R_TRY(nifmInitialize(NifmServiceType_System));
auto rc = nifmInitialize(NifmServiceType_System); R_TRY(wlaninfInitialize());
if(R_SUCCEEDED(rc)) rc = wlaninfInitialize(); return ResultSuccess;
return rc;
} }
void Finalize() void Finalize() {
{
wlaninfExit(); wlaninfExit();
nifmExit(); nifmExit();
} }
bool HasConnection() bool HasConnection() {
{ auto status = NifmInternetConnectionStatus_ConnectingUnknown1;
NifmInternetConnectionStatus status = NifmInternetConnectionStatus_ConnectingUnknown1; nifmGetInternetConnectionStatus(nullptr, nullptr, &status);
nifmGetInternetConnectionStatus(NULL, NULL, &status); return status == NifmInternetConnectionStatus_Connected;
return (status == NifmInternetConnectionStatus_Connected);
} }
Result GetCurrentNetworkProfile(NetworkProfileData *data) Result GetCurrentNetworkProfile(NetworkProfileData *data) {
{
return serviceDispatch(nifmGetServiceSession_GeneralService(), 5, return serviceDispatch(nifmGetServiceSession_GeneralService(), 5,
.buffer_attrs = { SfBufferAttr_FixedSize | SfBufferAttr_Out | SfBufferAttr_HipcPointer }, .buffer_attrs = { SfBufferAttr_FixedSize | SfBufferAttr_Out | SfBufferAttr_HipcPointer },
.buffers = { { data, sizeof(NetworkProfileData) } } .buffers = { { data, sizeof(NetworkProfileData) } }
); );
} }
Result GetMACAddress(u64 *out) Result GetMACAddress(u64 *out) {
{
return serviceDispatchOut(wlaninfGetServiceSession(), 2, *out); return serviceDispatchOut(wlaninfGetServiceSession(), 2, *out);
} }
std::string FormatMACAddress(u64 addr) std::string FormatMACAddress(u64 addr) {
{
std::stringstream strm; std::stringstream strm;
strm << std::hex << std::uppercase << addr; strm << std::hex << std::uppercase << addr;
std::string str; std::string str;
auto sstrm = strm.str(); auto sstrm = strm.str();
for(u32 i = 1; i < 7; i++) for(u32 i = 1; i < 7; i++) {
{
str += sstrm.substr((6 - i) * 2, 2); str += sstrm.substr((6 - i) * 2, 2);
if(i < 6) str += ":"; if(i < 6) {
str += ":";
}
} }
return str; return str;
} }
std::string GetConsoleIPAddress() std::string GetConsoleIPAddress() {
{
char ipaddr[0x20] = {0}; char ipaddr[0x20] = {0};
auto ip = gethostid(); auto ip = gethostid();
inet_ntop(AF_INET, &ip, ipaddr, sizeof(ipaddr)); sprintf(ipaddr, "%lu.%lu.%lu.%lu", (ip & 0xff000000) >> 24, (ip & 0x00ff0000) >> 16, (ip & 0x0000ff00) >> 8, (ip & 0x000000ff));
return ipaddr; return ipaddr;
} }
} }

View file

@ -3,63 +3,53 @@
#include <db/db_Save.hpp> #include <db/db_Save.hpp>
#include <fs/fs_Stdio.hpp> #include <fs/fs_Stdio.hpp>
namespace os namespace os {
{
std::string GetIconCacheImagePath(AccountUid user_id) std::string GetIconCacheImagePath(AccountUid user_id) {
{
auto uidstr = util::Format128NintendoStyle(user_id); auto uidstr = util::Format128NintendoStyle(user_id);
return UL_BASE_SD_DIR "/user/" + uidstr + ".jpg"; return UL_BASE_SD_DIR "/user/" + uidstr + ".jpg";
} }
ResultWith<std::vector<AccountUid>> QuerySystemAccounts(bool dump_icon) Result QuerySystemAccounts(std::vector<AccountUid> &out_accounts, bool dump_icon) {
{ AccountUid uids[ACC_USER_LIST_SIZE] = {0};
std::vector<AccountUid> uids;
AccountUid *uidbuf = new AccountUid[ACC_USER_LIST_SIZE]();
s32 acc_count = 0; s32 acc_count = 0;
auto rc = accountListAllUsers(uidbuf, ACC_USER_LIST_SIZE, &acc_count); R_TRY(accountListAllUsers(uids, ACC_USER_LIST_SIZE, &acc_count));
if(R_SUCCEEDED(rc)) for(s32 i = 0; i < acc_count; i++) {
{ out_accounts.push_back(uids[i]);
for(s32 i = 0; i < acc_count; i++) if(dump_icon) {
{ AccountProfile prof;
uids.push_back(uidbuf[i]); auto rc = accountGetProfile(&prof, uids[i]);
if(dump_icon) if(R_SUCCEEDED(rc)) {
{ u32 imgsz = 0;
AccountProfile prof; rc = accountProfileGetImageSize(&prof, &imgsz);
rc = accountGetProfile(&prof, uidbuf[i]); if(imgsz > 0) {
if(R_SUCCEEDED(rc)) auto imgbuf = new u8[imgsz]();
{ u32 tmpsz;
u32 imgsz = 0; rc = accountProfileLoadImage(&prof, imgbuf, imgsz, &tmpsz);
rc = accountProfileGetImageSize(&prof, &imgsz); if(R_SUCCEEDED(rc)) {
if(imgsz > 0) auto iconcache = GetIconCacheImagePath(uids[i]);
{ fs::WriteFile(iconcache, imgbuf, imgsz, true);
u8 *imgbuf = new u8[imgsz]();
u32 tmpsz;
rc = accountProfileLoadImage(&prof, imgbuf, imgsz, &tmpsz);
if(R_SUCCEEDED(rc))
{
auto iconcache = GetIconCacheImagePath(uidbuf[i]);
fs::WriteFile(iconcache, imgbuf, imgsz, true);
}
} }
accountProfileClose(&prof); delete[] imgbuf;
} }
accountProfileClose(&prof);
} }
} }
} }
delete[] uidbuf; return ResultSuccess;
return MakeResultWith(rc, uids);
} }
ResultWith<std::string> GetAccountName(AccountUid user_id) Result GetAccountName(std::string &out_name, AccountUid user_id) {
{
std::string name; std::string name;
AccountProfile prof; AccountProfile prof;
R_TRY_WITH(accountGetProfile(&prof, user_id), name); R_TRY(accountGetProfile(&prof, user_id));
UL_ON_SCOPE_EXIT({
accountProfileClose(&prof);
});
AccountProfileBase pbase; AccountProfileBase pbase;
AccountUserData udata; AccountUserData udata;
R_TRY_WITH(accountProfileGet(&prof, &udata, &pbase), name); R_TRY(accountProfileGet(&prof, &udata, &pbase));
name = pbase.nickname; out_name = pbase.nickname;
accountProfileClose(&prof); return ResultSuccess;
return SuccessResultWith(name);
} }
} }

View file

@ -1,17 +1,15 @@
#include <os/os_HomeMenu.hpp> #include <os/os_HomeMenu.hpp>
namespace os namespace os {
{
Result PushSystemAppletMessage(SystemAppletMessage msg) Result PushSystemAppletMessage(SystemAppletMessage msg) {
{
AppletStorage st; AppletStorage st;
auto rc = appletCreateStorage(&st, sizeof(msg)); R_TRY(appletCreateStorage(&st, sizeof(msg)));
if(R_SUCCEEDED(rc)) UL_ON_SCOPE_EXIT({
{
rc = appletStorageWrite(&st, 0, &msg, sizeof(msg));
if(R_SUCCEEDED(rc)) appletPushToGeneralChannel(&st);
appletStorageClose(&st); appletStorageClose(&st);
} });
return rc; R_TRY(appletStorageWrite(&st, 0, &msg, sizeof(msg)));
R_TRY(appletPushToGeneralChannel(&st));
return ResultSuccess;
} }
} }

View file

@ -1,9 +1,9 @@
#include <os/os_Misc.hpp> #include <os/os_Misc.hpp>
#include <ctime>
namespace os namespace os {
{
static std::vector<std::string> g_lang_names = static std::vector<std::string> g_lang_names = {
{
"Japanese", "Japanese",
"American English", "American English",
"Français", "Français",
@ -23,35 +23,44 @@ namespace os
"Chinese (traditional)" "Chinese (traditional)"
}; };
std::string GetLanguageName(u32 idx) std::string GetLanguageName(u32 idx) {
{ if(idx >= g_lang_names.size()) {
if(idx >= g_lang_names.size()) return ""; return "";
}
return g_lang_names[idx]; return g_lang_names[idx];
} }
std::vector<std::string> &GetLanguageNameList() std::vector<std::string> &GetLanguageNameList() {
{
return g_lang_names; return g_lang_names;
} }
u32 GetBatteryLevel() u32 GetBatteryLevel() {
{
u32 lvl = 0; u32 lvl = 0;
psmGetBatteryChargePercentage(&lvl); psmGetBatteryChargePercentage(&lvl);
return lvl; return lvl;
} }
bool IsConsoleCharging() bool IsConsoleCharging() {
{ auto cht = ChargerType_None;
ChargerType cht = ChargerType_None;
psmGetChargerType(&cht); psmGetChargerType(&cht);
return (cht > ChargerType_None); return cht > ChargerType_None;
} }
std::string GetFirmwareVersion() std::string GetFirmwareVersion() {
{
SetSysFirmwareVersion fwver; SetSysFirmwareVersion fwver;
setsysGetFirmwareVersion(&fwver); setsysGetFirmwareVersion(&fwver);
return fwver.display_version; return fwver.display_version;
} }
// Thanks Goldleaf
std::string GetCurrentTime() {
auto time_val = time(nullptr);
auto local_time = localtime(&time_val);
auto h = local_time->tm_hour;
auto min = local_time->tm_min;
char str[0x10] = {0};
sprintf(str, "%02d:%02d", h, min);
return str;
}
} }

View file

@ -1,27 +1,19 @@
#include <os/os_Titles.hpp> #include <os/os_Titles.hpp>
#include <db/db_Save.hpp>
#include <fs/fs_Stdio.hpp>
namespace os namespace os {
{
std::vector<cfg::TitleRecord> QueryInstalledTitles() std::vector<cfg::TitleRecord> QueryInstalledTitles() {
{
std::vector<cfg::TitleRecord> titles; std::vector<cfg::TitleRecord> titles;
NsApplicationRecord *recordbuf = new NsApplicationRecord[MaxInstalledCount](); UL_OS_FOR_EACH_APP_RECORD(record, {
s32 record_count = 0; cfg::TitleRecord rec = {};
auto rc = nsListApplicationRecord(recordbuf, MaxInstalledCount, 0, &record_count); rec.app_id = record.application_id;
if(R_SUCCEEDED(rc)) if(rec.app_id == 0) {
{ continue;
for(s32 i = 0; i < record_count; i++)
{
cfg::TitleRecord rec = {};
rec.app_id = recordbuf[i].application_id;
rec.title_type = (u32)cfg::TitleType::Installed;
if(rec.app_id == 0) continue;
titles.push_back(rec);
} }
} rec.title_type = static_cast<u32>(cfg::TitleType::Installed);
delete[] recordbuf; titles.push_back(rec);
});
return titles; return titles;
} }
} }

View file

@ -14,16 +14,14 @@
#define RES_BLOCK_END }; #define RES_BLOCK_END };
namespace res namespace res {
{
struct ResultImpl struct ResultImpl {
{
u32 res_desc; u32 res_desc;
std::string res_name; std::string res_name;
}; };
struct ResultModuleImpl struct ResultModuleImpl {
{
u32 mod_val; u32 mod_val;
std::string mod_name; std::string mod_name;
std::vector<ResultImpl> results; std::vector<ResultImpl> results;
@ -49,16 +47,11 @@ namespace res
RES_BLOCK_END RES_BLOCK_END
Result GetResultByModuleAndName(std::string mod, std::string name) Result GetResultByModuleAndName(std::string mod, std::string name) {
{ for(auto &module: g_result_impl_table) {
for(auto &module: g_result_impl_table) if(module.mod_name == mod) {
{ for(auto &res: module.results) {
if(module.mod_name == mod) if(res.res_name == name) {
{
for(auto &res: module.results)
{
if(res.res_name == name)
{
return MAKERESULT(Module, module.mod_val + res.res_desc); return MAKERESULT(Module, module.mod_val + res.res_desc);
} }
} }
@ -67,16 +60,15 @@ namespace res
return 0; return 0;
} }
std::string GetDescriptionByResult(Result rc) std::string GetDescriptionByResult(Result rc) {
{ for(auto &module: g_result_impl_table) {
for(auto &module: g_result_impl_table) for(auto &res: module.results) {
{ if(rc == MAKERESULT(Module, module.mod_val + res.res_desc)) {
for(auto &res: module.results) return module.mod_name + " - " + res.res_name;
{ }
auto resval = MAKERESULT(Module, module.mod_val + res.res_desc);
if(resval == rc) return module.mod_name + " - " + res.res_name;
} }
} }
return "Unknown result"; return "Unknown result";
} }
} }

View file

@ -2,60 +2,66 @@
namespace util namespace util
{ {
std::string Format128NintendoStyle(AccountUid value) std::string Format128NintendoStyle(AccountUid value) {
{ auto bytes_v = reinterpret_cast<u8*>(value.uid);
u8 *bytesval = (u8*)value.uid;
std::stringstream strm; std::stringstream strm;
strm << std::hex << std::setw(2) << std::setfill('0') << std::nouppercase << (int)bytesval[3]; #define _UL_TMP_PRINT(idx) strm << std::hex << std::setw(2) << std::setfill('0') << std::nouppercase << static_cast<u32>(bytes_v[idx]);
strm << std::hex << std::setw(2) << std::setfill('0') << std::nouppercase << (int)bytesval[2]; #define _UL_TMP_DASH strm << "-";
strm << std::hex << std::setw(2) << std::setfill('0') << std::nouppercase << (int)bytesval[1];
strm << std::hex << std::setw(2) << std::setfill('0') << std::nouppercase << (int)bytesval[0]; _UL_TMP_PRINT(3)
strm << "-"; _UL_TMP_PRINT(2)
strm << std::hex << std::setw(2) << std::setfill('0') << std::nouppercase << (int)bytesval[5]; _UL_TMP_PRINT(1)
strm << std::hex << std::setw(2) << std::setfill('0') << std::nouppercase << (int)bytesval[4]; _UL_TMP_PRINT(0)
strm << "-"; _UL_TMP_DASH
strm << std::hex << std::setw(2) << std::setfill('0') << std::nouppercase << (int)bytesval[7]; _UL_TMP_PRINT(5)
strm << std::hex << std::setw(2) << std::setfill('0') << std::nouppercase << (int)bytesval[6]; _UL_TMP_PRINT(4)
strm << "-"; _UL_TMP_DASH
strm << std::hex << std::setw(2) << std::setfill('0') << std::nouppercase << (int)bytesval[9]; _UL_TMP_PRINT(7)
strm << std::hex << std::setw(2) << std::setfill('0') << std::nouppercase << (int)bytesval[8]; _UL_TMP_PRINT(6)
strm << "-"; _UL_TMP_DASH
strm << std::hex << std::setw(2) << std::setfill('0') << std::nouppercase << (int)bytesval[15]; _UL_TMP_PRINT(9)
strm << std::hex << std::setw(2) << std::setfill('0') << std::nouppercase << (int)bytesval[14]; _UL_TMP_PRINT(8)
strm << std::hex << std::setw(2) << std::setfill('0') << std::nouppercase << (int)bytesval[13]; _UL_TMP_DASH
strm << std::hex << std::setw(2) << std::setfill('0') << std::nouppercase << (int)bytesval[12]; _UL_TMP_PRINT(15)
strm << std::hex << std::setw(2) << std::setfill('0') << std::nouppercase << (int)bytesval[11]; _UL_TMP_PRINT(14)
strm << std::hex << std::setw(2) << std::setfill('0') << std::nouppercase << (int)bytesval[10]; _UL_TMP_PRINT(13)
_UL_TMP_PRINT(12)
_UL_TMP_PRINT(11)
_UL_TMP_PRINT(10)
#undef _UL_TMP_DASH
#undef _UL_TMP_PRINT
return strm.str(); return strm.str();
} }
std::string FormatApplicationId(u64 app_id) std::string FormatApplicationId(u64 app_id) {
{
std::stringstream strm; std::stringstream strm;
strm << std::uppercase << std::setfill('0') << std::setw(16) << std::hex << app_id; strm << std::uppercase << std::setfill('0') << std::setw(16) << std::hex << app_id;
return strm.str(); return strm.str();
} }
std::string FormatResultDisplay(Result rc) std::string FormatResultDisplay(Result rc) {
{
char res[0x20] = {0}; char res[0x20] = {0};
sprintf(res, "%04d-%04d", R_MODULE(rc) + 2000, R_DESCRIPTION(rc)); sprintf(res, "%04d-%04d", R_MODULE(rc) + 2000, R_DESCRIPTION(rc));
return std::string(res); return std::string(res);
} }
std::string FormatResultHex(Result rc) std::string FormatResultHex(Result rc) {
{
char res[0x20] = {0}; char res[0x20] = {0};
sprintf(res, "0x%X", rc); sprintf(res, "0x%X", rc);
return std::string(res); return std::string(res);
} }
std::string FormatResult(Result rc) std::string FormatResult(Result rc) {
{
auto desc = RES_DESCRIPTION(rc); auto desc = RES_DESCRIPTION(rc);
if(desc.empty()) return desc; std::string fmt = "(" + FormatResultDisplay(rc) + ")";
return "(" + FormatResultDisplay(rc) + ") " + desc; if(!desc.empty()) {
fmt += " ";
fmt += desc;
}
return fmt;
} }
} }

View file

@ -3,46 +3,16 @@
namespace util namespace util
{ {
ResultWith<JSON> LoadJSONFromFile(const std::string &path) Result LoadJSONFromFile(JSON &out_json, const std::string &path) {
{ if(fs::ExistsFile(path)) {
JSON ret = JSON::object(); try {
if(fs::ExistsFile(path))
{
try
{
std::ifstream ifs(path); std::ifstream ifs(path);
ret = JSON::parse(ifs); out_json = JSON::parse(ifs);
return SuccessResultWith(ret); return ResultSuccess;
}
catch(std::exception&)
{
} }
catch(...) {}
} }
return MakeResultWith(RES_VALUE(Misc, InvalidJSONFile), ret); return RES_VALUE(Misc, InvalidJSONFile);
} }
std::string GetCurrentTime() // Thanks Goldleaf
{
time_t timet = time(NULL);
struct tm *times = localtime((const time_t*)&timet);
int h = times->tm_hour;
int min = times->tm_min;
char timestr[0x10] = {0};
sprintf(timestr, "%02d:%02d", h, min);
return std::string(timestr);
}
u32 GetBatteryLevel()
{
u32 perc = 0;
psmGetBatteryChargePercentage(&perc);
return perc;
}
bool IsCharging()
{
ChargerType ch = ChargerType_None;
psmGetChargerType(&ch);
return (ch > ChargerType_None);
}
} }

View file

@ -19,7 +19,7 @@ include $(DEVKITPRO)/libnx/switch_rules
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR)) TARGET := $(notdir $(CURDIR))
BUILD := build BUILD := build
SOURCES := source source/ui $(UL_COMMON_SOURCES) SOURCES := source source/ui source/am $(UL_COMMON_SOURCES)
DATA := data DATA := data
INCLUDES := include $(UL_COMMON_INCLUDES) INCLUDES := include $(UL_COMMON_INCLUDES)
EXEFS_SRC := exefs_src EXEFS_SRC := exefs_src
@ -39,13 +39,13 @@ CXXFLAGS := $(CFLAGS) $(UL_CXXFLAGS)
ASFLAGS := -g $(ARCH) ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lnx -lpu -lfreetype -lSDL2_mixer -lopusfile -lopus -lmodplug -lmpg123 -lvorbisidec -logg -lSDL2_ttf -lSDL2_gfx -lSDL2_image -lwebp -lpng -ljpeg `sdl2-config --libs` `freetype-config --libs` LIBS := -lpu -lfreetype -lSDL2_mixer -lopusfile -lopus -lmodplug -lmpg123 -lvorbisidec -logg -lSDL2_ttf -lSDL2_gfx -lSDL2_image -lSDL2 -lEGL -lGLESv2 -lglapi -ldrm_nouveau -lwebp -lpng -ljpeg `sdl2-config --libs` `freetype-config --libs` -lnx
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing # list of directories containing libraries, this must be the top level containing
# include and lib # include and lib
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(LIBNX) $(CURDIR)/../Plutonium/Plutonium/Output LIBDIRS := $(PORTLIBS) $(LIBNX) $(TOPDIR)/../Plutonium/Plutonium/Output
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
@ -134,13 +134,12 @@ all: $(BUILD)
$(BUILD): $(BUILD):
@[ -d $@ ] || mkdir -p $@ @[ -d $@ ] || mkdir -p $@
@$(MAKE) -C ../Plutonium/Plutonium/
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
clean: clean:
@echo clean ... @echo clean ...
@rm -fr $(BUILD) $(CURDIR)/Out $(TARGET).nsp $(TARGET).npdm $(TARGET).nso $(TARGET).elf @rm -fr $(BUILD) $(TARGET).nsp $(TARGET).npdm $(TARGET).nso $(TARGET).elf
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------

View file

@ -1,13 +1,14 @@
#pragma once #pragma once
#include <am/am_DaemonMenuInteraction.hpp> #include <dmi/dmi_DaemonMenuInteraction.hpp>
#include <functional> #include <functional>
namespace am namespace am {
{
using MessageDetectCallback = std::function<void()>; using MessageDetectCallback = std::function<void()>;
Result InitializeDaemonMessageHandler(); Result InitializeDaemonMessageHandler();
void ExitDaemonMessageHandler(); void ExitDaemonMessageHandler();
void RegisterOnMessageDetect(MessageDetectCallback callback, MenuMessage desired_msg); void RegisterOnMessageDetect(MessageDetectCallback callback, dmi::MenuMessage desired_msg);
} }

View file

@ -1,7 +1,8 @@
#pragma once #pragma once
namespace am namespace am {
{
void RegisterLibAppletHomeButtonDetection(); void RegisterLibAppletHomeButtonDetection();
} }

View file

@ -0,0 +1,10 @@
#pragma once
#include <dmi/dmi_DaemonMenuInteraction.hpp>
namespace am {
Result ReadDataFromStorage(void *data, size_t data_size);
Result ReadStartMode(dmi::MenuStartMode &out_start_mode);
}

View file

@ -1,10 +1,9 @@
#pragma once #pragma once
#include <ul_Include.hpp>
#include <cfg/cfg_Config.hpp> #include <cfg/cfg_Config.hpp>
namespace ui::actions namespace ui::actions {
{
void ShowAboutDialog(); void ShowAboutDialog();
void ShowSettingsMenu(); void ShowSettingsMenu();
void ShowThemesMenu(); void ShowThemesMenu();
@ -14,4 +13,5 @@ namespace ui::actions
void ShowHelpDialog(); void ShowHelpDialog();
void ShowAlbumApplet(); void ShowAlbumApplet();
void ShowPowerDialog(); void ShowPowerDialog();
} }

View file

@ -5,31 +5,20 @@
#include <ul_Include.hpp> #include <ul_Include.hpp>
#include <pu/Plutonium> #include <pu/Plutonium>
namespace ui namespace ui {
{
class ClickableImage : public pu::ui::elm::Element class ClickableImage : public pu::ui::elm::Element {
{
public: private:
ClickableImage(s32 X, s32 Y, pu::String Image); inline void Dispose() {
PU_SMART_CTOR(ClickableImage) if(this->ntex != nullptr) {
~ClickableImage(); pu::ui::render::DeleteTexture(this->ntex);
this->ntex = nullptr;
}
}
s32 GetX();
void SetX(s32 X);
s32 GetY();
void SetY(s32 Y);
s32 GetWidth();
void SetWidth(s32 Width);
s32 GetHeight();
void SetHeight(s32 Height);
pu::String GetImage();
void SetImage(pu::String Image);
bool IsImageValid();
void SetOnClick(std::function<void()> Callback);
void OnRender(pu::ui::render::Renderer::Ref &Drawer, s32 X, s32 Y);
void OnInput(u64 Down, u64 Up, u64 Held, pu::ui::Touch Pos);
protected: protected:
pu::String img; std::string img;
pu::sdl2::Texture ntex; pu::sdl2::Texture ntex;
s32 x; s32 x;
s32 y; s32 y;
@ -38,5 +27,33 @@ namespace ui
std::function<void()> cb; std::function<void()> cb;
std::chrono::steady_clock::time_point touchtp; std::chrono::steady_clock::time_point touchtp;
bool touched; bool touched;
public:
ClickableImage(s32 x, s32 y, const std::string &img) : Element(), ntex(nullptr), x(x), y(y), w(0), h(0), cb([&](){}), touched(false) {
this->SetImage(img);
}
~ClickableImage() {
this->Dispose();
}
PU_SMART_CTOR(ClickableImage)
s32 GetX();
void SetX(s32 x);
s32 GetY();
void SetY(s32 y);
s32 GetWidth();
void SetWidth(s32 w);
s32 GetHeight();
void SetHeight(s32 h);
std::string GetImage();
void SetImage(const std::string &img);
bool IsImageValid();
void SetOnClick(std::function<void()> cb);
void OnRender(pu::ui::render::Renderer::Ref &drawer, s32 x, s32 y);
void OnInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos);
}; };
} }

View file

@ -3,10 +3,10 @@
#include <pu/Plutonium> #include <pu/Plutonium>
#include <ul_Include.hpp> #include <ul_Include.hpp>
namespace ui namespace ui {
{
class IMenuLayout : public pu::ui::Layout class IMenuLayout : public pu::ui::Layout {
{
private: private:
Mutex home_press_lock; Mutex home_press_lock;
bool home_pressed; bool home_pressed;
@ -14,9 +14,11 @@ namespace ui
public: public:
IMenuLayout(); IMenuLayout();
void OnInput(u64 down, u64 up, u64 held, pu::ui::Touch pos); void OnInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos);
virtual void OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch pos) = 0; virtual void OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) = 0;
void DoOnHomeButtonPress(); void DoOnHomeButtonPress();
virtual void OnHomeButtonPress() = 0; virtual void OnHomeButtonPress() = 0;
}; };
} }

View file

@ -4,21 +4,24 @@
#include <ui/ui_IMenuLayout.hpp> #include <ui/ui_IMenuLayout.hpp>
#include <cfg/cfg_Config.hpp> #include <cfg/cfg_Config.hpp>
namespace ui namespace ui {
{
class LanguagesMenuLayout : public IMenuLayout class LanguagesMenuLayout : public IMenuLayout {
{
private:
pu::ui::elm::TextBlock::Ref infoText;
pu::ui::elm::Menu::Ref langsMenu;
public: public:
LanguagesMenuLayout(); LanguagesMenuLayout();
PU_SMART_CTOR(LanguagesMenuLayout) PU_SMART_CTOR(LanguagesMenuLayout)
void OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch pos) override; void OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) override;
void OnHomeButtonPress() override; void OnHomeButtonPress() override;
void Reload(); void Reload();
void lang_Click(u32 idx); void lang_Click(u32 idx);
private:
pu::ui::elm::TextBlock::Ref infoText;
pu::ui::elm::Menu::Ref langsMenu;
}; };
} }

View file

@ -5,12 +5,11 @@
#include <ui/ui_ThemeMenuLayout.hpp> #include <ui/ui_ThemeMenuLayout.hpp>
#include <ui/ui_SettingsMenuLayout.hpp> #include <ui/ui_SettingsMenuLayout.hpp>
#include <ui/ui_LanguagesMenuLayout.hpp> #include <ui/ui_LanguagesMenuLayout.hpp>
#include <am_DaemonMessages.hpp> #include <am/am_DaemonMessages.hpp>
namespace ui namespace ui {
{
enum class MenuType enum class MenuType {
{
Startup, Startup,
Main, Main,
Settings, Settings,
@ -18,16 +17,43 @@ namespace ui
Languages, Languages,
}; };
class MenuApplication : public pu::ui::Application void UiOnHomeButtonDetection();
{
class MenuApplication : public pu::ui::Application {
private:
dmi::MenuStartMode stmode;
StartupLayout::Ref startupLayout;
MenuLayout::Ref menuLayout;
ThemeMenuLayout::Ref themeMenuLayout;
SettingsMenuLayout::Ref settingsMenuLayout;
LanguagesMenuLayout::Ref languagesMenuLayout;
pu::ui::extras::Toast::Ref notifToast;
dmi::DaemonStatus status;
MenuType loaded_menu;
JSON uijson;
JSON bgmjson;
bool bgm_loop;
u32 bgm_fade_in_ms;
u32 bgm_fade_out_ms;
pu::audio::Music bgm;
public: public:
using Application::Application; using Application::Application;
~MenuApplication();
~MenuApplication() {
pu::audio::Delete(this->bgm);
}
PU_SMART_CTOR(MenuApplication) PU_SMART_CTOR(MenuApplication)
static inline void RegisterHomeButtonDetection() {
am::RegisterOnMessageDetect(&UiOnHomeButtonDetection, dmi::MenuMessage::HomeRequest);
}
void OnLoad() override; void OnLoad() override;
void SetInformation(am::MenuStartMode mode, am::DaemonStatus status, JSON ui_json); void SetInformation(dmi::MenuStartMode mode, dmi::DaemonStatus status, JSON ui_json);
void LoadMenu(); void LoadMenu();
void LoadStartupMenu(); void LoadStartupMenu();
void LoadThemeMenu(); void LoadThemeMenu();
@ -44,37 +70,31 @@ namespace ui
void ShowNotification(const std::string &text, u64 timeout = 1500); void ShowNotification(const std::string &text, u64 timeout = 1500);
template<typename T> template<typename T>
inline T GetUIConfigValue(const std::string &name, T def) inline T GetUIConfigValue(const std::string &name, T def) {
{
return this->uijson.value<T>(name, def); return this->uijson.value<T>(name, def);
} }
template<typename Elem> template<typename Elem>
void ApplyConfigForElement(const std::string &menu, const std::string &name, std::shared_ptr<Elem> &Ref, bool also_visible = true) inline void ApplyConfigForElement(const std::string &menu, const std::string &name, std::shared_ptr<Elem> &Ref, bool also_visible = true) {
{ if(this->uijson.count(menu)) {
if(this->uijson.count(menu))
{
auto jmenu = this->uijson[menu]; auto jmenu = this->uijson[menu];
if(jmenu.count(name)) if(jmenu.count(name)) {
{
auto jelem = jmenu[name]; auto jelem = jmenu[name];
bool set_coords = false; bool set_coords = false;
if(also_visible) if(also_visible) {
{
bool visible = jelem.value("visible", true); bool visible = jelem.value("visible", true);
Ref->SetVisible(visible); Ref->SetVisible(visible);
set_coords = visible; set_coords = visible;
} }
else set_coords = true; else {
if(set_coords) set_coords = true;
{ }
if(jelem.count("x")) if(set_coords) {
{ if(jelem.count("x")) {
s32 x = jelem["x"]; s32 x = jelem["x"];
Ref->SetX(x); Ref->SetX(x);
} }
if(jelem.count("y")) if(jelem.count("y")) {
{
s32 y = jelem["y"]; s32 y = jelem["y"];
Ref->SetY(y); Ref->SetY(y);
} }
@ -95,28 +115,7 @@ namespace ui
void SetSelectedUser(AccountUid user_id); void SetSelectedUser(AccountUid user_id);
AccountUid GetSelectedUser(); AccountUid GetSelectedUser();
MenuType GetCurrentLoadedMenu(); MenuType GetCurrentLoadedMenu();
private:
am::MenuStartMode stmode;
StartupLayout::Ref startupLayout;
MenuLayout::Ref menuLayout;
ThemeMenuLayout::Ref themeMenuLayout;
SettingsMenuLayout::Ref settingsMenuLayout;
LanguagesMenuLayout::Ref languagesMenuLayout;
pu::ui::extras::Toast::Ref notifToast;
am::DaemonStatus status;
MenuType loaded_menu;
JSON uijson;
JSON bgmjson;
bool bgm_loop;
u32 bgm_fade_in_ms;
u32 bgm_fade_out_ms;
pu::audio::Music bgm;
}; };
void UiOnHomeButtonDetection();
inline void RegisterHomeButtonDetection()
{
am::RegisterOnMessageDetect(&UiOnHomeButtonDetection, am::MenuMessage::HomeRequest);
}
} }

View file

@ -9,28 +9,10 @@
#include <ui/ui_Actions.hpp> #include <ui/ui_Actions.hpp>
#include <cfg/cfg_Config.hpp> #include <cfg/cfg_Config.hpp>
namespace ui namespace ui {
{
class MenuLayout : public IMenuLayout
{
public:
MenuLayout(void *raw, u8 min_alpha);
~MenuLayout();
PU_SMART_CTOR(MenuLayout)
void OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch pos) override; class MenuLayout : public IMenuLayout {
void OnHomeButtonPress() override;
void menu_Click(u64 down, u32 index);
void menu_OnSelected(u32 index);
void menuToggle_Click();
void MoveFolder(const std::string &name, bool fade);
void SetUser(AccountUid user);
void HandleCloseSuspended();
void HandleHomebrewLaunch(cfg::TitleRecord &rec);
void HandleMultiselectMoveToFolder(const std::string &folder);
void StopMultiselect();
void DoTerminateApplication();
private: private:
void *susptr; void *susptr;
bool last_hasconn; bool last_hasconn;
@ -66,5 +48,46 @@ namespace ui
s32 rawalpha; s32 rawalpha;
pu::audio::Sfx sfxTitleLaunch; pu::audio::Sfx sfxTitleLaunch;
pu::audio::Sfx sfxMenuToggle; pu::audio::Sfx sfxMenuToggle;
inline void ApplySuspendedRatio(bool increase) {
auto susp_w = this->bgSuspendedRaw->GetWidth();
auto susp_h = this->bgSuspendedRaw->GetHeight();
// Change size, 16:9 ratio
if(increase) {
susp_w += 16;
susp_h += 9;
}
else {
susp_w -= 16;
susp_h -= 9;
}
auto susp_x = (1280 - susp_w) / 2;
auto susp_y = (720 - susp_h) / 2;
this->bgSuspendedRaw->SetX(susp_x);
this->bgSuspendedRaw->SetY(susp_y);
this->bgSuspendedRaw->SetWidth(susp_w);
this->bgSuspendedRaw->SetHeight(susp_h);
}
public:
MenuLayout(void *raw, u8 min_alpha);
~MenuLayout();
PU_SMART_CTOR(MenuLayout)
void OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) override;
void OnHomeButtonPress() override;
void menu_Click(u64 down, u32 index);
void menu_OnSelected(u32 index);
void menuToggle_Click();
void MoveFolder(const std::string &name, bool fade);
void SetUser(AccountUid user);
void HandleCloseSuspended();
void HandleHomebrewLaunch(cfg::TitleRecord &rec);
void HandleMultiselectMoveToFolder(const std::string &folder);
void StopMultiselect();
void DoTerminateApplication();
}; };
} }

View file

@ -1,12 +1,11 @@
#pragma once #pragma once
#include <ul_Include.hpp> #include <am/am_DaemonMessages.hpp>
#include <pu/Plutonium> #include <pu/Plutonium>
namespace ui namespace ui {
{
enum class QuickMenuDirection enum class QuickMenuDirection {
{
Up, Up,
Down, Down,
Left, Left,
@ -18,26 +17,35 @@ namespace ui
None None
}; };
struct QuickMenuSubItem struct QuickMenuSubItem {
{
std::function<void()> on_select; std::function<void()> on_select;
pu::sdl2::Texture nicon; pu::sdl2::Texture nicon;
}; };
class QuickMenu : public pu::ui::elm::Element void QuickMenuOnHomeButtonDetection();
{
static constexpr s32 MainItemSize = 300;
static constexpr s32 SubItemsSize = 150;
static constexpr s32 CommonAreaSize = 50;
static constexpr s32 MainItemX = (1280 - MainItemSize) / 2; class QuickMenu : public pu::ui::elm::Element {
static constexpr s32 MainItemY = (720 - MainItemSize) / 2;
public:
static constexpr s32 MainItemSize = 300;
static constexpr s32 SubItemsSize = 150;
static constexpr s32 CommonAreaSize = 50;
static constexpr s32 MainItemX = (1280 - MainItemSize) / 2;
static constexpr s32 MainItemY = (720 - MainItemSize) / 2;
private:
bool on;
s32 bgalpha;
pu::ui::elm::Menu::Ref options_menu;
public: public:
QuickMenu(const std::string &main_icon); QuickMenu(const std::string &main_icon);
PU_SMART_CTOR(QuickMenu) PU_SMART_CTOR(QuickMenu)
static void RegisterHomeButtonDetection(); static inline void RegisterHomeButtonDetection() {
am::RegisterOnMessageDetect(&QuickMenuOnHomeButtonDetection, dmi::MenuMessage::HomeRequest);
}
s32 GetX(); s32 GetX();
s32 GetY(); s32 GetY();
@ -50,9 +58,6 @@ namespace ui
void OnRender(pu::ui::render::Renderer::Ref &Drawer, s32 X, s32 Y); void OnRender(pu::ui::render::Renderer::Ref &Drawer, s32 X, s32 Y);
void OnInput(u64 Down, u64 Up, u64 Held, pu::ui::Touch Pos); void OnInput(u64 Down, u64 Up, u64 Held, pu::ui::Touch Pos);
private:
bool on;
s32 bgalpha;
pu::ui::elm::Menu::Ref options_menu;
}; };
} }

View file

@ -3,26 +3,10 @@
#include <ul_Include.hpp> #include <ul_Include.hpp>
#include <pu/Plutonium> #include <pu/Plutonium>
namespace ui namespace ui {
{
class RawData : public pu::ui::elm::Element class RawData : public pu::ui::elm::Element {
{
public:
RawData(s32 X, s32 Y, void *raw, s32 Width, s32 Height, u32 PixNum);
PU_SMART_CTOR(RawData)
~RawData();
s32 GetX();
void SetX(s32 X);
s32 GetY();
void SetY(s32 Y);
s32 GetWidth();
void SetWidth(s32 Width);
s32 GetHeight();
void SetHeight(s32 Height);
void SetAlphaFactor(u8 Factor);
void OnRender(pu::ui::render::Renderer::Ref &Drawer, s32 X, s32 Y);
void OnInput(u64 Down, u64 Up, u64 Held, pu::ui::Touch Pos);
protected: protected:
s32 x; s32 x;
s32 y; s32 y;
@ -33,5 +17,24 @@ namespace ui
void *ptr; void *ptr;
size_t fullsz; size_t fullsz;
size_t pitch; size_t pitch;
public:
RawData(s32 x, s32 y, void *raw, s32 w, s32 h, u32 pix_num);
PU_SMART_CTOR(RawData)
~RawData();
s32 GetX();
void SetX(s32 x);
s32 GetY();
void SetY(s32 y);
s32 GetWidth();
void SetWidth(s32 w);
s32 GetHeight();
void SetHeight(s32 h);
void SetAlphaFactor(u8 Factor);
void OnRender(pu::ui::render::Renderer::Ref &drawer, s32 x, s32 y);
void OnInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos);
}; };
} }

View file

@ -4,22 +4,25 @@
#include <ui/ui_IMenuLayout.hpp> #include <ui/ui_IMenuLayout.hpp>
#include <cfg/cfg_Config.hpp> #include <cfg/cfg_Config.hpp>
namespace ui namespace ui {
{
class SettingsMenuLayout : public IMenuLayout class SettingsMenuLayout : public IMenuLayout {
{
private:
pu::ui::elm::TextBlock::Ref infoText;
pu::ui::elm::Menu::Ref settingsMenu;
public: public:
SettingsMenuLayout(); SettingsMenuLayout();
PU_SMART_CTOR(SettingsMenuLayout) PU_SMART_CTOR(SettingsMenuLayout)
void OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch pos) override; void OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) override;
void OnHomeButtonPress() override; void OnHomeButtonPress() override;
void Reload(); void Reload();
void PushSettingItem(const std::string &name, const std::string &value_display, int id); void PushSettingItem(const std::string &name, const std::string &value_display, int id);
void setting_Click(u32 id); void setting_Click(u32 id);
private:
pu::ui::elm::TextBlock::Ref infoText;
pu::ui::elm::Menu::Ref settingsMenu;
}; };
} }

View file

@ -3,51 +3,18 @@
#include <pu/Plutonium> #include <pu/Plutonium>
#include <map> #include <map>
namespace ui namespace ui {
{
class SideMenu : public pu::ui::elm::Element class SideMenu : public pu::ui::elm::Element {
{
static constexpr u32 ItemSize = 256;
static constexpr u32 Margin = 20;
static constexpr u32 ItemCount = 4;
static constexpr u32 FocusSize = 15;
static constexpr u32 FocusMargin = 5;
static constexpr u32 ExtraIconSize = ItemSize + (Margin * 2);
public: public:
SideMenu(pu::ui::Color suspended_clr, std::string cursor_path, std::string suspended_img_path, std::string multiselect_img_path, u32 txt_x, u32 txt_y, pu::String font_name, pu::ui::Color txt_clr, s32 y); static constexpr u32 ItemSize = 256;
PU_SMART_CTOR(SideMenu) static constexpr u32 Margin = 20;
~SideMenu(); static constexpr u32 ItemCount = 4;
static constexpr u32 FocusSize = 15;
static constexpr u32 FocusMargin = 5;
static constexpr u32 ExtraIconSize = ItemSize + (Margin * 2);
s32 GetX() override;
s32 GetY() override;
void SetX(s32 x); // Stubbed
void SetY(s32 y);
s32 GetWidth() override;
s32 GetHeight() override;
void OnRender(pu::ui::render::Renderer::Ref &drawer, s32 x, s32 y) override;
void OnInput(u64 down, u64 up, u64 held, pu::ui::Touch pos) override;
void SetOnItemSelected(std::function<void(u64, u32)> fn);
void SetOnSelectionChanged(std::function<void(u32)> fn);
void ClearItems();
void AddItem(const std::string &icon, const std::string &txt = "");
void SetSuspendedItem(u32 idx);
void UnsetSuspendedItem();
void SetSelectedItem(u32 idx);
void HandleMoveLeft();
void HandleMoveRight();
int GetSuspendedItem();
u32 GetSelectedItem();
u32 GetBaseItemIndex();
void SetBaseItemIndex(u32 idx);
void SetBasePositions(u32 selected_idx, u32 base_idx);
void ClearBorderIcons();
void UpdateBorderIcons();
void ResetMultiselections();
void SetItemMultiselected(u32 idx, bool selected);
bool IsItemMultiselected(u32 idx);
bool IsAnyMultiselected();
void SetEnabled(bool enabled);
private: private:
s32 y; s32 y;
u32 selitm; u32 selitm;
@ -79,8 +46,46 @@ namespace ui
u32 scrollflag; u32 scrollflag;
u32 scrolltpvalue; u32 scrolltpvalue;
u32 scrollcount; u32 scrollcount;
bool IsLeftFirst(); bool IsLeftFirst();
bool IsRightLast(); bool IsRightLast();
void ReloadIcons(u32 dir); void ReloadIcons(u32 dir);
public:
SideMenu(pu::ui::Color suspended_clr, std::string cursor_path, std::string suspended_img_path, std::string multiselect_img_path, u32 txt_x, u32 txt_y, pu::String font_name, pu::ui::Color txt_clr, s32 y);
PU_SMART_CTOR(SideMenu)
~SideMenu();
s32 GetX() override;
s32 GetY() override;
void SetX(s32 x); // Stubbed
void SetY(s32 y);
s32 GetWidth() override;
s32 GetHeight() override;
void OnRender(pu::ui::render::Renderer::Ref &drawer, s32 x, s32 y) override;
void OnInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) override;
void SetOnItemSelected(std::function<void(u64, u32)> fn);
void SetOnSelectionChanged(std::function<void(u32)> fn);
void ClearItems();
void AddItem(const std::string &icon, const std::string &txt = "");
void SetSuspendedItem(u32 idx);
void UnsetSuspendedItem();
void SetSelectedItem(u32 idx);
void HandleMoveLeft();
void HandleMoveRight();
int GetSuspendedItem();
u32 GetSelectedItem();
u32 GetBaseItemIndex();
void SetBaseItemIndex(u32 idx);
void SetBasePositions(u32 selected_idx, u32 base_idx);
void ClearBorderIcons();
void UpdateBorderIcons();
void ResetMultiselections();
void SetItemMultiselected(u32 idx, bool selected);
bool IsItemMultiselected(u32 idx);
bool IsAnyMultiselected();
void SetEnabled(bool enabled);
}; };
} }

View file

@ -1,27 +1,30 @@
#pragma once #pragma once
#include <ul_Include.hpp> #include <ul_Include.hpp>
#include <am/am_DaemonMenuInteraction.hpp> #include <dmi/dmi_DaemonMenuInteraction.hpp>
#include <db/db_Save.hpp> #include <db/db_Save.hpp>
#include <ui/ui_IMenuLayout.hpp> #include <ui/ui_IMenuLayout.hpp>
namespace ui namespace ui {
{
class StartupLayout : public IMenuLayout class StartupLayout : public IMenuLayout {
{
private:
bool loadmenu;
pu::ui::elm::TextBlock::Ref infoText;
pu::ui::elm::Menu::Ref usersMenu;
public: public:
StartupLayout(); StartupLayout();
PU_SMART_CTOR(StartupLayout) PU_SMART_CTOR(StartupLayout)
void OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch pos) override; void OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) override;
void OnHomeButtonPress() override; void OnHomeButtonPress() override;
void user_Click(AccountUid uid); void user_Click(AccountUid uid);
void create_Click(); void create_Click();
void ReloadMenu(); void ReloadMenu();
private:
bool loadmenu;
pu::ui::elm::TextBlock::Ref infoText;
pu::ui::elm::Menu::Ref usersMenu;
}; };
} }

View file

@ -4,19 +4,10 @@
#include <ui/ui_IMenuLayout.hpp> #include <ui/ui_IMenuLayout.hpp>
#include <cfg/cfg_Config.hpp> #include <cfg/cfg_Config.hpp>
namespace ui namespace ui {
{
class ThemeMenuLayout : public IMenuLayout
{
public:
ThemeMenuLayout();
PU_SMART_CTOR(ThemeMenuLayout)
void OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch pos) override; class ThemeMenuLayout : public IMenuLayout {
void OnHomeButtonPress() override;
void Reload();
void theme_Click();
private: private:
pu::ui::elm::Menu::Ref themesMenu; pu::ui::elm::Menu::Ref themesMenu;
pu::ui::elm::TextBlock::Ref curThemeText; pu::ui::elm::TextBlock::Ref curThemeText;
@ -26,5 +17,17 @@ namespace ui
pu::ui::elm::Image::Ref curThemeIcon; pu::ui::elm::Image::Ref curThemeIcon;
pu::ui::elm::Image::Ref curThemeBanner; pu::ui::elm::Image::Ref curThemeBanner;
std::vector<cfg::Theme> loadedThemes; std::vector<cfg::Theme> loadedThemes;
public:
ThemeMenuLayout();
PU_SMART_CTOR(ThemeMenuLayout)
void OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) override;
void OnHomeButtonPress() override;
void Reload();
void theme_Click();
}; };
} }

View file

@ -12,14 +12,16 @@
#include <os/os_HomeMenu.hpp> #include <os/os_HomeMenu.hpp>
#include <util/util_Convert.hpp> #include <util/util_Convert.hpp>
#include <am/am_LibraryApplet.hpp> #include <am/am_LibraryApplet.hpp>
#include <am_DaemonMessages.hpp> #include <am/am_DaemonMessages.hpp>
#include <am_LibAppletWrap.hpp> #include <am/am_LibAppletWrap.hpp>
#include <am/am_LibraryAppletUtils.hpp>
extern "C" {
extern "C"
{
u32 __nx_applet_type = AppletType_LibraryApplet; // Explicitly declare we're a library applet (need to do so for non-hbloader homebrew) u32 __nx_applet_type = AppletType_LibraryApplet; // Explicitly declare we're a library applet (need to do so for non-hbloader homebrew)
TimeServiceType __nx_time_service_type = TimeServiceType_System; TimeServiceType __nx_time_service_type = TimeServiceType_System;
size_t __nx_heap_size = 0xD000000; // 208MB heap size_t __nx_heap_size = 0x10000000; // 0xD000000; // 208MB heap
} }
#define MENU_ROMFS_BIN UL_BASE_SD_DIR "/bin/uMenu/romfs.bin" #define MENU_ROMFS_BIN UL_BASE_SD_DIR "/bin/uMenu/romfs.bin"
@ -33,32 +35,30 @@ cfg::Config g_ul_config;
cfg::Theme g_ul_theme; cfg::Theme g_ul_theme;
u8 *g_app_capture_buffer; u8 *g_app_capture_buffer;
namespace qmenu namespace impl {
{
void Initialize() void Initialize() {
{ UL_ASSERT(accountInitialize(AccountServiceType_System));
UL_ASSERT(accountInitialize(AccountServiceType_System)) UL_ASSERT(nsInitialize());
UL_ASSERT(nsInitialize()) UL_ASSERT(net::Initialize());
UL_ASSERT(net::Initialize()) UL_ASSERT(psmInitialize());
UL_ASSERT(psmInitialize()) UL_ASSERT(setsysInitialize());
UL_ASSERT(setsysInitialize()) UL_ASSERT(setInitialize());
UL_ASSERT(setInitialize())
// Register handlers for HOME button press detection // Register handlers for HOME button press detection
am::RegisterLibAppletHomeButtonDetection(); am::RegisterLibAppletHomeButtonDetection();
ui::RegisterHomeButtonDetection(); ui::MenuApplication::RegisterHomeButtonDetection();
ui::QuickMenu::RegisterHomeButtonDetection(); ui::QuickMenu::RegisterHomeButtonDetection();
// Initialize Daemon message handling // Initialize uDaemon message handling
UL_ASSERT(am::InitializeDaemonMessageHandler()); UL_ASSERT(am::InitializeDaemonMessageHandler());
// Load menu g_ul_config and theme // Load menu config and theme
g_ul_config = cfg::EnsureConfig(); g_ul_config = cfg::EnsureConfig();
g_ul_theme = cfg::LoadTheme(g_ul_config.theme_name); g_ul_theme = cfg::LoadTheme(g_ul_config.theme_name);
} }
void Exit() void Exit() {
{
am::ExitDaemonMessageHandler(); am::ExitDaemonMessageHandler();
setExit(); setExit();
@ -70,50 +70,47 @@ namespace qmenu
} }
} }
int main() int main() {
{ // First read sent storages, then init the renderer (UI, which also inits RomFs), then init everything else
auto [rc, smode] = am::Menu_ProcessInput(); auto smode = dmi::MenuStartMode::Invalid;
if(R_SUCCEEDED(rc))
{ auto rc = am::ReadStartMode(smode);
am::DaemonStatus status = {}; if(R_SUCCEEDED(rc)) {
dmi::DaemonStatus status = {};
// Information block sent as an extra storage to uMenu.
UL_ASSERT(am::ReadDataFromStorage(&status, sizeof(status)));
// Information block sent as an extra storage to Menu. if(smode != dmi::MenuStartMode::Invalid) {
am::Menu_DaemonReadImpl(&status, sizeof(status), false); // Check if our RomFs data exists...
if(!fs::ExistsFile(MENU_ROMFS_BIN)) {
UL_ASSERT(RES_VALUE(Menu, RomfsBinNotFound));
}
// First read sent storages, then init the renderer (UI, which also inits RomFs), then init everything else // Try to mount it
UL_ASSERT(romfsMountFromFsdev(MENU_ROMFS_BIN, 0, "romfs"));
if(smode != am::MenuStartMode::Invalid)
{
// Check if our RomFs file exists...
if(!fs::ExistsFile(MENU_ROMFS_BIN)) UL_ASSERT(RES_VALUE(Menu, RomfsBinNotFound))
// Try to mount RomFs from our binary
UL_ASSERT(romfsMountFromFsdev(MENU_ROMFS_BIN, 0, "romfs"))
// After initializing RomFs, start initializing the rest of stuff here // After initializing RomFs, start initializing the rest of stuff here
g_app_capture_buffer = new u8[RawRGBAScreenBufferSize](); g_app_capture_buffer = new (std::nothrow) u8[RawRGBAScreenBufferSize]();
qmenu::Initialize(); impl::Initialize();
g_entry_list = cfg::LoadTitleList(); g_entry_list = cfg::LoadTitleList();
// Get system language and load translations (default one if not present) // Get system language and load translations (default one if not present)
u64 lcode = 0; u64 lcode = 0;
setGetLanguageCode(&lcode); setGetLanguageCode(&lcode);
std::string syslang = (char*)&lcode; std::string syslang = reinterpret_cast<char*>(&lcode);
auto lpath = cfg::GetLanguageJSONPath(syslang); auto lpath = cfg::GetLanguageJSONPath(syslang);
auto [rc1, defjson] = util::LoadJSONFromFile(CFG_LANG_DEFAULT); UL_ASSERT(util::LoadJSONFromFile(g_ul_config.default_lang, CFG_LANG_DEFAULT));
UL_ASSERT(rc1); g_ul_config.main_lang = g_ul_config.default_lang;
g_ul_config.default_lang = defjson; if(fs::ExistsFile(lpath)) {
g_ul_config.main_lang = defjson; auto ljson = JSON::object();
if(fs::ExistsFile(lpath)) UL_ASSERT(util::LoadJSONFromFile(ljson, lpath));
{ g_ul_config.main_lang = ljson;
auto [rc2, ljson] = util::LoadJSONFromFile(lpath);
if(R_SUCCEEDED(rc2)) g_ul_config.main_lang = ljson;
} }
// Get the text sizes to initialize default fonts // Get the text sizes to initialize default fonts
auto [rc2, uijson] = util::LoadJSONFromFile(cfg::GetAssetByTheme(g_ul_theme, "ui/UI.json")); auto uijson = JSON::object();
UL_ASSERT(rc2); UL_ASSERT(util::LoadJSONFromFile(uijson, cfg::GetAssetByTheme(g_ul_theme, "ui/UI.json")));
auto menu_folder_txt_sz = uijson.value<u32>("menu_folder_text_size", 25); auto menu_folder_txt_sz = uijson.value<u32>("menu_folder_text_size", 25);
auto renderer = pu::ui::render::Renderer::New(pu::ui::render::RendererInitOptions(SDL_INIT_EVERYTHING, pu::ui::render::RendererHardwareFlags).WithIMG(pu::ui::render::IMGAllFlags).WithMixer(pu::ui::render::MixerAllFlags).WithTTF().WithDefaultFontSize(menu_folder_txt_sz)); auto renderer = pu::ui::render::Renderer::New(pu::ui::render::RendererInitOptions(SDL_INIT_EVERYTHING, pu::ui::render::RendererHardwareFlags).WithIMG(pu::ui::render::IMGAllFlags).WithMixer(pu::ui::render::MixerAllFlags).WithTTF().WithDefaultFontSize(menu_folder_txt_sz));
@ -122,14 +119,18 @@ int main()
g_menu_app_instance->SetInformation(smode, status, uijson); g_menu_app_instance->SetInformation(smode, status, uijson);
g_menu_app_instance->Prepare(); g_menu_app_instance->Prepare();
if(smode == am::MenuStartMode::MenuApplicationSuspended) g_menu_app_instance->Show(); if(smode == dmi::MenuStartMode::MenuApplicationSuspended) {
else g_menu_app_instance->ShowWithFadeIn(); g_menu_app_instance->Show();
}
else {
g_menu_app_instance->ShowWithFadeIn();
}
// Exit RomFs manually, as we initialized it manually too // Exit RomFs manually, since we also initialized it manually
romfsExit(); romfsExit();
delete[] g_app_capture_buffer; operator delete[](g_app_capture_buffer, std::nothrow);
qmenu::Exit(); impl::Exit();
} }
} }

View file

@ -1,34 +1,31 @@
#include <am_DaemonMessages.hpp> #include <am/am_DaemonMessages.hpp>
#include <thread>
#include <map> #include <map>
namespace am namespace am {
{
static Mutex g_receiver_lock = EmptyMutex; static Mutex g_receiver_lock = EmptyMutex;
static Service g_daemon_srv; static Service g_daemon_srv;
static bool g_init = false; static bool g_init = false;
static bool g_thr_should_stop = false; static bool g_thr_should_stop = false;
static Thread g_receiver_thr; static Thread g_receiver_thr;
static std::vector<std::pair<MessageDetectCallback, MenuMessage>> g_callback_table; static std::vector<std::pair<MessageDetectCallback, dmi::MenuMessage>> g_callback_table;
static void DaemonMessageReceiveThread(void *arg) static void DaemonMessageReceiveThread(void *arg) {
{ while(true) {
while(true)
{
mutexLock(&g_receiver_lock); mutexLock(&g_receiver_lock);
auto should_stop = g_thr_should_stop; auto should_stop = g_thr_should_stop;
mutexUnlock(&g_receiver_lock); mutexUnlock(&g_receiver_lock);
if(should_stop) break; if(should_stop) {
MenuMessage tmp_msg = MenuMessage::Invalid; break;
}
auto tmp_msg = dmi::MenuMessage::Invalid;
u64 pid_placeholder = 0; u64 pid_placeholder = 0;
UL_ASSERT(serviceDispatchInOut(&g_daemon_srv, 0, pid_placeholder, tmp_msg, UL_ASSERT(serviceDispatchInOut(&g_daemon_srv, 0, pid_placeholder, tmp_msg,
.in_send_pid = true, .in_send_pid = true,
)); ));
mutexLock(&g_receiver_lock); mutexLock(&g_receiver_lock);
for(auto &[cb, msg] : g_callback_table) for(auto &[cb, msg] : g_callback_table) {
{ if(msg == tmp_msg) {
if(msg == tmp_msg)
{
cb(); cb();
} }
} }
@ -37,26 +34,22 @@ namespace am
} }
} }
Result InitializeDaemonMessageHandler() Result InitializeDaemonMessageHandler() {
{ if(g_init) {
if(g_init) return 0; return ResultSuccess;
auto rc = smGetService(&g_daemon_srv, AM_DAEMON_PRIVATE_SERVICE_NAME);
if(R_SUCCEEDED(rc))
{
g_thr_should_stop = false;
rc = threadCreate(&g_receiver_thr, &DaemonMessageReceiveThread, nullptr, nullptr, 0x4000, 0x2b, -2);
if(R_SUCCEEDED(rc))
{
rc = threadStart(&g_receiver_thr);
if(R_SUCCEEDED(rc)) g_init = true;
}
} }
return rc; R_TRY(smGetService(&g_daemon_srv, AM_DAEMON_PRIVATE_SERVICE_NAME));
g_thr_should_stop = false;
R_TRY(threadCreate(&g_receiver_thr, &DaemonMessageReceiveThread, nullptr, nullptr, 0x1000, 0x2b, -2));
R_TRY(threadStart(&g_receiver_thr));
g_init = true;
return ResultSuccess;
} }
void ExitDaemonMessageHandler() void ExitDaemonMessageHandler() {
{ if(!g_init) {
if(!g_init) return; return;
}
mutexLock(&g_receiver_lock); mutexLock(&g_receiver_lock);
g_thr_should_stop = true; g_thr_should_stop = true;
mutexUnlock(&g_receiver_lock); mutexUnlock(&g_receiver_lock);
@ -66,8 +59,7 @@ namespace am
g_init = false; g_init = false;
} }
void RegisterOnMessageDetect(MessageDetectCallback callback, MenuMessage desired_msg) void RegisterOnMessageDetect(MessageDetectCallback callback, dmi::MenuMessage desired_msg) {
{
mutexLock(&g_receiver_lock); mutexLock(&g_receiver_lock);
g_callback_table.push_back(std::make_pair(callback, desired_msg)); g_callback_table.push_back(std::make_pair(callback, desired_msg));
mutexUnlock(&g_receiver_lock); mutexUnlock(&g_receiver_lock);

View file

@ -0,0 +1,88 @@
#include <dmi/dmi_DaemonMenuInteraction.hpp>
#include <am/am_LibAppletWrap.hpp>
#include <am/am_DaemonMessages.hpp>
static Mutex g_amwrap_detection_lock = EmptyMutex;
static bool g_is_applet_running = false;
static bool g_detection_home_pressed = false;
namespace am {
void OnHomeButtonDetection() {
mutexLock(&g_amwrap_detection_lock);
if(g_is_applet_running) {
g_detection_home_pressed = true;
}
mutexUnlock(&g_amwrap_detection_lock);
}
void RegisterLibAppletHomeButtonDetection() {
RegisterOnMessageDetect(&OnHomeButtonDetection, dmi::MenuMessage::HomeRequest);
}
}
extern "C" {
// Wrap libappletStart and libappletLaunch to use our custom waiting system
Result __wrap_libappletStart(AppletHolder *h) {
R_TRY(appletHolderStart(h));
mutexLock(&g_amwrap_detection_lock);
g_is_applet_running = true;
mutexUnlock(&g_amwrap_detection_lock);
while(true) {
if(appletHolderCheckFinished(h)) {
break;
}
if(!serviceIsActive(&h->s)) {
break;
}
mutexLock(&g_amwrap_detection_lock);
auto home_pressed = g_detection_home_pressed;
g_detection_home_pressed = false;
mutexUnlock(&g_amwrap_detection_lock);
if(home_pressed) {
appletHolderRequestExitOrTerminate(h, 15'000'000'000ul);
break;
}
svcSleepThread(10'000'000ul);
}
appletHolderJoin(h);
auto rc = ResultSuccess;
auto reason = appletHolderGetExitReason(h);
if(reason == LibAppletExitReason_Canceled || reason == LibAppletExitReason_Abnormal || reason == LibAppletExitReason_Unexpected) {
rc = MAKERESULT(Module_Libnx, LibnxError_LibAppletBadExit);
}
mutexLock(&g_amwrap_detection_lock);
g_is_applet_running = false;
mutexUnlock(&g_amwrap_detection_lock);
return rc;
}
Result __wrap_libappletLaunch(AppletId id, LibAppletArgs *commonargs, const void* arg, size_t arg_size, void* reply, size_t reply_size, size_t *out_reply_size) {
AppletHolder holder;
R_TRY(appletCreateLibraryApplet(&holder, id, LibAppletMode_AllForeground));
UL_ON_SCOPE_EXIT({
appletHolderClose(&holder);
});
R_TRY(libappletArgsPush(commonargs, &holder));
if(arg && arg_size) {
R_TRY(libappletPushInData(&holder, arg, arg_size));
}
R_TRY(__wrap_libappletStart(&holder));
if(reply && reply_size) {
R_TRY(libappletPopOutData(&holder, reply, reply_size, out_reply_size));
}
return ResultSuccess;
}
}

View file

@ -0,0 +1,22 @@
#include <am/am_LibraryAppletUtils.hpp>
namespace am {
Result ReadDataFromStorage(void *data, size_t data_size) {
AppletStorage st;
R_TRY(appletPopInData(&st));
UL_ON_SCOPE_EXIT({
appletStorageClose(&st);
});
R_TRY(appletStorageRead(&st, 0, data, data_size));
return ResultSuccess;
}
Result ReadStartMode(dmi::MenuStartMode &out_start_mode) {
LibAppletArgs args = {};
R_TRY(ReadDataFromStorage(&args, sizeof(args)));
out_start_mode = static_cast<dmi::MenuStartMode>(args.LaVersion);
return ResultSuccess;
}
}

View file

@ -1,89 +0,0 @@
#include <am/am_DaemonMenuInteraction.hpp>
#include <am_LibAppletWrap.hpp>
#include <am_DaemonMessages.hpp>
static Mutex g_amwrap_detection_lock = EmptyMutex;
static bool g_is_applet_running = false;
static bool g_detection_home_pressed = false;
namespace am
{
void OnHomeButtonDetection()
{
mutexLock(&g_amwrap_detection_lock);
if(g_is_applet_running) g_detection_home_pressed = true;
mutexUnlock(&g_amwrap_detection_lock);
}
void RegisterLibAppletHomeButtonDetection()
{
RegisterOnMessageDetect(&OnHomeButtonDetection, am::MenuMessage::HomeRequest);
}
}
extern "C"
{
// Wrap libappletStart and libappletLaunch to use our custom waiting system
Result __wrap_libappletStart(AppletHolder *h)
{
Result rc = appletHolderStart(h);
mutexLock(&g_amwrap_detection_lock);
g_is_applet_running = true;
mutexUnlock(&g_amwrap_detection_lock);
if(R_SUCCEEDED(rc))
{
while(true)
{
if(appletHolderCheckFinished(h)) break;
if(!serviceIsActive(&h->s)) break;
mutexLock(&g_amwrap_detection_lock);
auto home_pressed = g_detection_home_pressed;
g_detection_home_pressed = false;
mutexUnlock(&g_amwrap_detection_lock);
if(home_pressed)
{
appletHolderRequestExitOrTerminate(h, 15'000'000'000ul);
break;
}
svcSleepThread(10'000'000ul);
}
appletHolderJoin(h);
LibAppletExitReason reason = appletHolderGetExitReason(h);
if(reason == LibAppletExitReason_Canceled || reason == LibAppletExitReason_Abnormal || reason == LibAppletExitReason_Unexpected) rc = MAKERESULT(Module_Libnx, LibnxError_LibAppletBadExit);
}
mutexLock(&g_amwrap_detection_lock);
g_is_applet_running = false;
mutexUnlock(&g_amwrap_detection_lock);
return rc;
}
Result __wrap_libappletLaunch(AppletId id, LibAppletArgs *commonargs, const void* arg, size_t arg_size, void* reply, size_t reply_size, size_t *out_reply_size)
{
AppletHolder holder;
Result rc = appletCreateLibraryApplet(&holder, id, LibAppletMode_AllForeground);
if(R_FAILED(rc)) return rc;
rc = libappletArgsPush(commonargs, &holder);
if(R_SUCCEEDED(rc) && arg && arg_size) rc = libappletPushInData(&holder, arg, arg_size);
if(R_SUCCEEDED(rc)) rc = __wrap_libappletStart(&holder);
if(R_SUCCEEDED(rc) && reply && reply_size) rc = libappletPopOutData(&holder, reply, reply_size, out_reply_size);
appletHolderClose(&holder);
return rc;
}
}

View file

@ -15,48 +15,48 @@
extern ui::MenuApplication::Ref g_menu_app_instance; extern ui::MenuApplication::Ref g_menu_app_instance;
extern cfg::Config g_ul_config; extern cfg::Config g_ul_config;
namespace ui::actions namespace ui::actions {
{
void ShowAboutDialog() void ShowAboutDialog() {
{
g_menu_app_instance->CreateShowDialog(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "ulaunch_about"), "uLaunch v" + std::string(UL_VERSION) + "\n\n" + cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "ulaunch_desc") + ":\nhttps://github.com/XorTroll/uLaunch", { cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "ok") }, true, "romfs:/LogoLarge.png"); g_menu_app_instance->CreateShowDialog(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "ulaunch_about"), "uLaunch v" + std::string(UL_VERSION) + "\n\n" + cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "ulaunch_desc") + ":\nhttps://github.com/XorTroll/uLaunch", { cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "ok") }, true, "romfs:/LogoLarge.png");
} }
void ShowSettingsMenu() void ShowSettingsMenu() {
{
g_menu_app_instance->FadeOut(); g_menu_app_instance->FadeOut();
g_menu_app_instance->LoadSettingsMenu(); g_menu_app_instance->LoadSettingsMenu();
g_menu_app_instance->FadeIn(); g_menu_app_instance->FadeIn();
} }
void ShowThemesMenu() void ShowThemesMenu() {
{
g_menu_app_instance->FadeOut(); g_menu_app_instance->FadeOut();
g_menu_app_instance->LoadThemeMenu(); g_menu_app_instance->LoadThemeMenu();
g_menu_app_instance->FadeIn(); g_menu_app_instance->FadeIn();
} }
void ShowUserMenu() void ShowUserMenu() {
{
auto uid = g_menu_app_instance->GetSelectedUser(); auto uid = g_menu_app_instance->GetSelectedUser();
std::string name;
auto [_rc, name] = os::GetAccountName(uid); os::GetAccountName(name, uid);
auto sopt = g_menu_app_instance->CreateShowDialog(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "user_settings"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "user_selected") + ": " + name + "\n" + cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "user_option"), { cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "user_view_page"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "user_logoff"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "cancel") }, true, os::GetIconCacheImagePath(uid)); auto sopt = g_menu_app_instance->CreateShowDialog(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "user_settings"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "user_selected") + ": " + name + "\n" + cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "user_option"), { cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "user_view_page"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "user_logoff"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "cancel") }, true, os::GetIconCacheImagePath(uid));
if(sopt == 0) friendsLaShowMyProfileForHomeMenu(uid); if(sopt == 0) {
else if(sopt == 1) friendsLaShowMyProfileForHomeMenu(uid);
{ }
else if(sopt == 1) {
u32 logoff = 0; u32 logoff = 0;
if(g_menu_app_instance->IsSuspended()) if(g_menu_app_instance->IsSuspended()) {
{
auto sopt = g_menu_app_instance->CreateShowDialog(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "suspended_app"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "user_logoff_app_suspended"), { cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "yes"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "cancel") }, true); auto sopt = g_menu_app_instance->CreateShowDialog(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "suspended_app"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "user_logoff_app_suspended"), { cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "yes"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "cancel") }, true);
if(sopt == 0) logoff = 2; if(sopt == 0) {
logoff = 2;
}
} }
else logoff = 1; else {
logoff = 1;
if(logoff > 0) }
{ if(logoff > 0) {
auto &menu_lyt = g_menu_app_instance->GetMenuLayout(); auto &menu_lyt = g_menu_app_instance->GetMenuLayout();
if(logoff == 2) menu_lyt->DoTerminateApplication(); if(logoff == 2) {
menu_lyt->DoTerminateApplication();
}
g_menu_app_instance->FadeOut(); g_menu_app_instance->FadeOut();
menu_lyt->MoveFolder("", false); menu_lyt->MoveFolder("", false);
g_menu_app_instance->LoadStartupMenu(); g_menu_app_instance->LoadStartupMenu();
@ -65,40 +65,40 @@ namespace ui::actions
} }
} }
void ShowControllerSupport() void ShowControllerSupport() {
{
HidLaControllerSupportArg arg = {}; HidLaControllerSupportArg arg = {};
hidLaCreateControllerSupportArg(&arg); hidLaCreateControllerSupportArg(&arg);
arg.enable_explain_text = true; arg.enable_explain_text = true;
for(u32 i = 0; i < 8; i++) strcpy(arg.explain_text[i], "Test explain text"); for(u32 i = 0; i < 8; i++) {
strcpy(arg.explain_text[i], "Test explain text");
}
hidLaShowControllerSupportForSystem(nullptr, &arg, true); hidLaShowControllerSupportForSystem(nullptr, &arg, true);
} }
void ShowWebPage() void ShowWebPage() {
{
SwkbdConfig swkbd; SwkbdConfig swkbd;
swkbdCreate(&swkbd, 0); swkbdCreate(&swkbd, 0);
UL_ON_SCOPE_EXIT({
swkbdClose(&swkbd);
});
swkbdConfigSetGuideText(&swkbd, cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "swkbd_webpage_guide").c_str()); swkbdConfigSetGuideText(&swkbd, cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "swkbd_webpage_guide").c_str());
char url[500] = {0}; char url[500] = {0};
auto rc = swkbdShow(&swkbd, url, 500); swkbdShow(&swkbd, url, 500);
swkbdClose(&swkbd);
if(R_SUCCEEDED(rc)) WebCommonConfig web = {};
{ webPageCreate(&web, url);
WebCommonConfig web = {}; webConfigSetWhitelist(&web, ".*");
webPageCreate(&web, url);
webConfigSetWhitelist(&web, ".*");
am::MenuCommandWriter writer(am::DaemonMessage::OpenWebPage); dmi::MenuMessageWriter writer(dmi::DaemonMessage::OpenWebPage);
writer.Write<WebCommonConfig>(web); writer.Write<WebCommonConfig>(web);
writer.FinishWrite(); writer.FinishWrite();
g_menu_app_instance->StopPlayBGM(); g_menu_app_instance->StopPlayBGM();
g_menu_app_instance->CloseWithFadeOut(); g_menu_app_instance->CloseWithFadeOut();
}
} }
void ShowHelpDialog() void ShowHelpDialog() {
{
std::string msg; std::string msg;
msg += " - " + cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "help_launch") + "\n"; msg += " - " + cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "help_launch") + "\n";
msg += " - " + cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "help_close") + "\n"; msg += " - " + cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "help_close") + "\n";
@ -111,32 +111,32 @@ namespace ui::actions
g_menu_app_instance->CreateShowDialog(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "help_title"), msg, { cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "ok") }, true); g_menu_app_instance->CreateShowDialog(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "help_title"), msg, { cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "ok") }, true);
} }
void ShowAlbumApplet() void ShowAlbumApplet() {
{ dmi::MenuMessageWriter writer(dmi::DaemonMessage::OpenAlbum);
am::MenuCommandWriter writer(am::DaemonMessage::OpenAlbum);
writer.FinishWrite();
g_menu_app_instance->StopPlayBGM(); g_menu_app_instance->StopPlayBGM();
g_menu_app_instance->CloseWithFadeOut(); g_menu_app_instance->CloseWithFadeOut();
} }
void ShowPowerDialog() void ShowPowerDialog() {
{
auto msg = os::GeneralChannelMessage::Invalid; auto msg = os::GeneralChannelMessage::Invalid;
auto sopt = g_menu_app_instance->CreateShowDialog(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "power_dialog"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "power_dialog_info"), { cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "power_sleep"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "power_power_off"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "power_reboot"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "cancel") }, true); auto sopt = g_menu_app_instance->CreateShowDialog(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "power_dialog"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "power_dialog_info"), { cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "power_sleep"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "power_power_off"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "power_reboot"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "cancel") }, true);
if(sopt == 0) msg = os::GeneralChannelMessage::Sleep; if(sopt == 0) {
else if(sopt == 1) msg = os::GeneralChannelMessage::Shutdown; msg = os::GeneralChannelMessage::Sleep;
else if(sopt == 2) msg = os::GeneralChannelMessage::Reboot; }
else if(sopt == 1) {
msg = os::GeneralChannelMessage::Shutdown;
}
else if(sopt == 2) {
msg = os::GeneralChannelMessage::Reboot;
}
if(msg != os::GeneralChannelMessage::Invalid) if(msg != os::GeneralChannelMessage::Invalid) {
{
// Fade out on all cases // Fade out on all cases
g_menu_app_instance->FadeOut(); g_menu_app_instance->FadeOut();
os::SystemAppletMessage smsg = {};
smsg.magic = os::SAMSMagic; auto smsg = os::SystemAppletMessage::Create(msg);
smsg.message = (u32)msg;
os::PushSystemAppletMessage(smsg); os::PushSystemAppletMessage(smsg);
svcSleepThread(1'500'000'000L); svcSleepThread(1'500'000'000L);

View file

@ -4,125 +4,85 @@
#include <ui/ui_ClickableImage.hpp> #include <ui/ui_ClickableImage.hpp>
#include <fs/fs_Stdio.hpp> #include <fs/fs_Stdio.hpp>
namespace ui namespace ui {
{
ClickableImage::ClickableImage(s32 X, s32 Y, pu::String Image) : pu::ui::elm::Element::Element()
{
this->x = X;
this->y = Y;
this->w = 0;
this->h = 0;
this->ntex = nullptr;
this->touched = false;
this->cb = [&](){};
this->SetImage(Image);
}
ClickableImage::~ClickableImage() s32 ClickableImage::GetX() {
{
if(this->ntex != nullptr)
{
pu::ui::render::DeleteTexture(this->ntex);
this->ntex = nullptr;
}
}
s32 ClickableImage::GetX()
{
return this->x; return this->x;
} }
void ClickableImage::SetX(s32 X) void ClickableImage::SetX(s32 x) {
{ this->x = x;
this->x = X;
} }
s32 ClickableImage::GetY() s32 ClickableImage::GetY() {
{
return this->y; return this->y;
} }
void ClickableImage::SetY(s32 Y) void ClickableImage::SetY(s32 y) {
{ this->y = y;
this->y = Y;
} }
s32 ClickableImage::GetWidth() s32 ClickableImage::GetWidth() {
{
return this->w; return this->w;
} }
void ClickableImage::SetWidth(s32 Width) void ClickableImage::SetWidth(s32 w) {
{ this->w = w;
this->w = Width;
} }
s32 ClickableImage::GetHeight() s32 ClickableImage::GetHeight() {
{
return this->h; return this->h;
} }
void ClickableImage::SetHeight(s32 Height) void ClickableImage::SetHeight(s32 h) {
{ this->h = h;
this->h = Height;
} }
pu::String ClickableImage::GetImage() std::string ClickableImage::GetImage() {
{
return this->img; return this->img;
} }
void ClickableImage::SetImage(pu::String Image) void ClickableImage::SetImage(const std::string &img) {
{ this->Dispose();
if(this->ntex != nullptr) pu::ui::render::DeleteTexture(this->ntex); if(fs::ExistsFile(img)) {
this->ntex = nullptr; this->img = img;
if(fs::ExistsFile(Image.AsUTF8())) this->ntex = pu::ui::render::LoadImage(img);
{
this->img = Image;
this->ntex = pu::ui::render::LoadImage(Image.AsUTF8());
this->w = pu::ui::render::GetTextureWidth(this->ntex); this->w = pu::ui::render::GetTextureWidth(this->ntex);
this->h = pu::ui::render::GetTextureHeight(this->ntex); this->h = pu::ui::render::GetTextureHeight(this->ntex);
} }
} }
bool ClickableImage::IsImageValid() bool ClickableImage::IsImageValid() {
{ return (ntex != nullptr) && !this->img.empty();
return ((ntex != nullptr) && this->img.HasAny());
} }
void ClickableImage::SetOnClick(std::function<void()> Callback) void ClickableImage::SetOnClick(std::function<void()> cb) {
{ this->cb = cb;
cb = Callback;
} }
void ClickableImage::OnRender(pu::ui::render::Renderer::Ref &Drawer, s32 X, s32 Y) void ClickableImage::OnRender(pu::ui::render::Renderer::Ref &drawer, s32 x, s32 y) {
{ drawer->RenderTexture(this->ntex, x, y, { -1, w, h, -1 });
Drawer->RenderTexture(this->ntex, X, Y, { -1, w, h, -1 });
} }
void ClickableImage::OnInput(u64 Down, u64 Up, u64 Held, pu::ui::Touch Pos) void ClickableImage::OnInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) {
{ if(this->touched) {
if(touched)
{
auto tpnow = std::chrono::steady_clock::now(); auto tpnow = std::chrono::steady_clock::now();
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(tpnow - touchtp).count(); auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(tpnow - this->touchtp).count();
if(diff >= 200) if(diff >= 200) {
{ this->touched = false;
touched = false;
(this->cb)(); (this->cb)();
SDL_SetTextureColorMod(ntex, 255, 255, 255); SDL_SetTextureColorMod(this->ntex, 255, 255, 255);
} }
} }
else if(!Pos.IsEmpty()) else if(!touch_pos.IsEmpty()) {
{
touchPosition tch; touchPosition tch;
hidTouchRead(&tch, 0); hidTouchRead(&tch, 0);
if((Pos.X >= this->GetProcessedX()) && (Pos.X < (this->GetProcessedX() + w)) && (Pos.Y >= this->GetProcessedY()) && (Pos.Y < (this->GetProcessedY() + h))) if((touch_pos.X >= this->GetProcessedX()) && (touch_pos.X < (this->GetProcessedX() + w)) && (touch_pos.Y >= this->GetProcessedY()) && (touch_pos.Y < (this->GetProcessedY() + h))) {
{ this->touchtp = std::chrono::steady_clock::now();
touchtp = std::chrono::steady_clock::now(); this->touched = true;
touched = true; SDL_SetTextureColorMod(this->ntex, 200, 200, 255);
SDL_SetTextureColorMod(ntex, 200, 200, 255);
} }
} }
} }
} }

View file

@ -1,28 +1,30 @@
#include <ui/ui_IMenuLayout.hpp> #include <ui/ui_IMenuLayout.hpp>
#include <ui/ui_Actions.hpp> #include <ui/ui_Actions.hpp>
namespace ui namespace ui {
{
IMenuLayout::IMenuLayout() : home_press_lock(EmptyMutex), home_pressed(false) IMenuLayout::IMenuLayout() : home_press_lock(EmptyMutex), home_pressed(false) {
{
this->SetOnInput(std::bind(&IMenuLayout::OnInput, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); this->SetOnInput(std::bind(&IMenuLayout::OnInput, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
} }
void IMenuLayout::OnInput(u64 down, u64 up, u64 held, pu::ui::Touch pos) void IMenuLayout::OnInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) {
{
mutexLock(&this->home_press_lock); mutexLock(&this->home_press_lock);
auto home_press = this->home_pressed; auto home_press = this->home_pressed;
this->home_pressed = false; this->home_pressed = false;
mutexUnlock(&this->home_press_lock); mutexUnlock(&this->home_press_lock);
if(home_press) this->OnHomeButtonPress(); if(home_press) {
if(!hidIsControllerConnected(CONTROLLER_HANDHELD) && !hidIsControllerConnected(CONTROLLER_PLAYER_1)) actions::ShowControllerSupport(); this->OnHomeButtonPress();
this->OnMenuInput(down, up, held, pos); }
if(!hidIsControllerConnected(CONTROLLER_HANDHELD) && !hidIsControllerConnected(CONTROLLER_PLAYER_1)) {
actions::ShowControllerSupport();
}
this->OnMenuInput(down, up, held, touch_pos);
} }
void IMenuLayout::DoOnHomeButtonPress() void IMenuLayout::DoOnHomeButtonPress() {
{
mutexLock(&this->home_press_lock); mutexLock(&this->home_press_lock);
this->home_pressed = true; this->home_pressed = true;
mutexUnlock(&this->home_press_lock); mutexUnlock(&this->home_press_lock);
} }
} }

View file

@ -12,10 +12,9 @@ extern ui::MenuApplication::Ref g_menu_app_instance;
extern cfg::Theme g_ul_theme; extern cfg::Theme g_ul_theme;
extern cfg::Config g_ul_config; extern cfg::Config g_ul_config;
namespace ui namespace ui {
{
LanguagesMenuLayout::LanguagesMenuLayout() LanguagesMenuLayout::LanguagesMenuLayout() {
{
this->SetBackgroundImage(cfg::GetAssetByTheme(g_ul_theme, "ui/Background.png")); this->SetBackgroundImage(cfg::GetAssetByTheme(g_ul_theme, "ui/Background.png"));
pu::ui::Color textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff")); pu::ui::Color textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
@ -34,39 +33,36 @@ namespace ui
this->Add(this->langsMenu); this->Add(this->langsMenu);
} }
void LanguagesMenuLayout::OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch pos) void LanguagesMenuLayout::OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) {
{ if(down & KEY_B) {
if(down & KEY_B)
{
g_menu_app_instance->FadeOut(); g_menu_app_instance->FadeOut();
g_menu_app_instance->LoadSettingsMenu(); g_menu_app_instance->LoadSettingsMenu();
g_menu_app_instance->FadeIn(); g_menu_app_instance->FadeIn();
} }
} }
void LanguagesMenuLayout::OnHomeButtonPress() void LanguagesMenuLayout::OnHomeButtonPress() {
{
g_menu_app_instance->FadeOut(); g_menu_app_instance->FadeOut();
g_menu_app_instance->LoadMenu(); g_menu_app_instance->LoadMenu();
g_menu_app_instance->FadeIn(); g_menu_app_instance->FadeIn();
} }
void LanguagesMenuLayout::Reload() void LanguagesMenuLayout::Reload() {
{
this->langsMenu->ClearItems(); this->langsMenu->ClearItems();
this->langsMenu->SetSelectedIndex(0); this->langsMenu->SetSelectedIndex(0);
pu::ui::Color textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff")); auto textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
u64 lcode = 0; u64 lcode = 0;
SetLanguage ilang = SetLanguage_ENUS; auto ilang = SetLanguage_ENUS;
setGetLanguageCode(&lcode); setGetLanguageCode(&lcode);
setMakeLanguage(lcode, &ilang); setMakeLanguage(lcode, &ilang);
u32 idx = 0; u32 idx = 0;
for(auto &lang: os::GetLanguageNameList()) for(auto &lang: os::GetLanguageNameList()) {
{
auto name = lang; auto name = lang;
if((u32)ilang == idx) name += " " + cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "lang_selected"); if(static_cast<u32>(ilang) == idx) {
name += " " + cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "lang_selected");
}
auto litm = pu::ui::elm::MenuItem::New(name); auto litm = pu::ui::elm::MenuItem::New(name);
litm->SetColor(textclr); litm->SetColor(textclr);
litm->AddOnClick(std::bind(&LanguagesMenuLayout::lang_Click, this, idx)); litm->AddOnClick(std::bind(&LanguagesMenuLayout::lang_Click, this, idx));
@ -75,37 +71,34 @@ namespace ui
} }
} }
void LanguagesMenuLayout::lang_Click(u32 idx) void LanguagesMenuLayout::lang_Click(u32 idx) {
{
u64 lcode = 0; u64 lcode = 0;
SetLanguage ilang = SetLanguage_ENUS; SetLanguage ilang = SetLanguage_ENUS;
setGetLanguageCode(&lcode); setGetLanguageCode(&lcode);
setMakeLanguage(lcode, &ilang); setMakeLanguage(lcode, &ilang);
if((u32)ilang == idx) g_menu_app_instance->ShowNotification(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "lang_active_this")); if(static_cast<u32>(ilang) == idx) {
else g_menu_app_instance->ShowNotification(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "lang_active_this"));
{ }
else {
auto sopt = g_menu_app_instance->CreateShowDialog(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "lang_set"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "lang_set_conf"), { cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "yes"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "no") }, true); auto sopt = g_menu_app_instance->CreateShowDialog(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "lang_set"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "lang_set_conf"), { cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "yes"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "no") }, true);
if(sopt == 0) if(sopt == 0) {
{
u64 codes[16] = {0}; u64 codes[16] = {0};
s32 tmp; s32 tmp;
setGetAvailableLanguageCodes(&tmp, codes, 16); setGetAvailableLanguageCodes(&tmp, codes, 16);
u64 code = codes[this->langsMenu->GetSelectedIndex()]; auto code = codes[this->langsMenu->GetSelectedIndex()];
auto rc = setsysSetLanguageCode(code); auto rc = setsysSetLanguageCode(code);
g_menu_app_instance->CreateShowDialog(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "lang_set"), R_SUCCEEDED(rc) ? cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "lang_set_ok") : cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "lang_set_error") + ": " + util::FormatResult(rc), { cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "ok") }, true); g_menu_app_instance->CreateShowDialog(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "lang_set"), R_SUCCEEDED(rc) ? cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "lang_set_ok") : cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "lang_set_error") + ": " + util::FormatResult(rc), { cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "ok") }, true);
if(R_SUCCEEDED(rc)) if(R_SUCCEEDED(rc)) {
{
g_menu_app_instance->FadeOut(); g_menu_app_instance->FadeOut();
os::SystemAppletMessage smsg = {};
smsg.magic = os::SAMSMagic;
smsg.message = (u32)os::GeneralChannelMessage::Reboot;
auto smsg = os::SystemAppletMessage::Create(os::GeneralChannelMessage::Reboot);
os::PushSystemAppletMessage(smsg); os::PushSystemAppletMessage(smsg);
svcSleepThread(1'500'000'000L); svcSleepThread(1'500'000'000ul);
} }
} }
} }
} }
} }

View file

@ -5,61 +5,48 @@ extern u8 *g_app_capture_buffer;
extern cfg::Theme g_ul_theme; extern cfg::Theme g_ul_theme;
extern ui::MenuApplication::Ref g_menu_app_instance; extern ui::MenuApplication::Ref g_menu_app_instance;
namespace ui namespace ui {
{
void UiOnHomeButtonDetection() void UiOnHomeButtonDetection() {
{ switch(g_menu_app_instance->GetCurrentLoadedMenu()) {
switch(g_menu_app_instance->GetCurrentLoadedMenu()) case MenuType::Startup: {
{
case MenuType::Startup:
{
g_menu_app_instance->GetStartupLayout()->DoOnHomeButtonPress(); g_menu_app_instance->GetStartupLayout()->DoOnHomeButtonPress();
break; break;
} }
case MenuType::Main: case MenuType::Main: {
{
g_menu_app_instance->GetMenuLayout()->DoOnHomeButtonPress(); g_menu_app_instance->GetMenuLayout()->DoOnHomeButtonPress();
break; break;
} }
case MenuType::Settings: case MenuType::Settings: {
{
g_menu_app_instance->GetSettingsMenuLayout()->DoOnHomeButtonPress(); g_menu_app_instance->GetSettingsMenuLayout()->DoOnHomeButtonPress();
break; break;
} }
case MenuType::Theme: case MenuType::Theme: {
{
g_menu_app_instance->GetThemeMenuLayout()->DoOnHomeButtonPress(); g_menu_app_instance->GetThemeMenuLayout()->DoOnHomeButtonPress();
break; break;
} }
case MenuType::Languages: case MenuType::Languages: {
{
g_menu_app_instance->GetLanguagesMenuLayout()->DoOnHomeButtonPress(); g_menu_app_instance->GetLanguagesMenuLayout()->DoOnHomeButtonPress();
break; break;
} }
} }
} }
MenuApplication::~MenuApplication() void MenuApplication::OnLoad() {
{ if(this->IsSuspended()) {
pu::audio::Delete(this->bgm);
}
void MenuApplication::OnLoad()
{
if(this->IsSuspended())
{
bool flag; bool flag;
appletGetLastApplicationCaptureImageEx(g_app_capture_buffer, RawRGBAScreenBufferSize, &flag); appletGetLastApplicationCaptureImageEx(g_app_capture_buffer, RawRGBAScreenBufferSize, &flag);
} }
auto [_rc2, jbgm] = util::LoadJSONFromFile(cfg::GetAssetByTheme(g_ul_theme, "sound/BGM.json")); auto jbgm = JSON::object();
util::LoadJSONFromFile(jbgm, cfg::GetAssetByTheme(g_ul_theme, "sound/BGM.json"));
this->bgmjson = jbgm; this->bgmjson = jbgm;
this->bgm_loop = this->bgmjson.value("loop", true); this->bgm_loop = this->bgmjson.value("loop", true);
this->bgm_fade_in_ms = this->bgmjson.value("fade_in_ms", 1500); this->bgm_fade_in_ms = this->bgmjson.value("fade_in_ms", 1500);
this->bgm_fade_out_ms = this->bgmjson.value("fade_out_ms", 500); this->bgm_fade_out_ms = this->bgmjson.value("fade_out_ms", 500);
pu::ui::Color toasttextclr = pu::ui::Color::FromHex(GetUIConfigValue<std::string>("toast_text_color", "#e1e1e1ff")); auto toasttextclr = pu::ui::Color::FromHex(GetUIConfigValue<std::string>("toast_text_color", "#e1e1e1ff"));
pu::ui::Color toastbaseclr = pu::ui::Color::FromHex(GetUIConfigValue<std::string>("toast_base_color", "#282828ff")); auto toastbaseclr = pu::ui::Color::FromHex(GetUIConfigValue<std::string>("toast_base_color", "#282828ff"));
this->notifToast = pu::ui::extras::Toast::New("...", "DefaultFont@20", toasttextclr, toastbaseclr); this->notifToast = pu::ui::extras::Toast::New("...", "DefaultFont@20", toasttextclr, toastbaseclr);
this->bgm = pu::audio::Open(cfg::GetAssetByTheme(g_ul_theme, "sound/BGM.mp3")); this->bgm = pu::audio::Open(cfg::GetAssetByTheme(g_ul_theme, "sound/BGM.mp3"));
@ -70,162 +57,143 @@ namespace ui
this->settingsMenuLayout = SettingsMenuLayout::New(); this->settingsMenuLayout = SettingsMenuLayout::New();
this->languagesMenuLayout = LanguagesMenuLayout::New(); this->languagesMenuLayout = LanguagesMenuLayout::New();
switch(this->stmode) switch(this->stmode) {
{ case dmi::MenuStartMode::StartupScreen: {
case am::MenuStartMode::StartupScreen:
this->LoadStartupMenu(); this->LoadStartupMenu();
break; break;
default: }
default: {
this->StartPlayBGM(); this->StartPlayBGM();
this->LoadMenu(); this->LoadMenu();
break; break;
}
} }
} }
void MenuApplication::SetInformation(am::MenuStartMode mode, am::DaemonStatus status, JSON ui_json) void MenuApplication::SetInformation(dmi::MenuStartMode mode, dmi::DaemonStatus status, JSON ui_json) {
{
this->stmode = mode; this->stmode = mode;
this->status = status; this->status = status;
this->uijson = ui_json; this->uijson = ui_json;
} }
void MenuApplication::LoadMenu() void MenuApplication::LoadMenu() {
{
this->menuLayout->SetUser(this->status.selected_user); this->menuLayout->SetUser(this->status.selected_user);
this->LoadLayout(this->menuLayout); this->LoadLayout(this->menuLayout);
this->loaded_menu = MenuType::Main; this->loaded_menu = MenuType::Main;
} }
void MenuApplication::LoadStartupMenu() void MenuApplication::LoadStartupMenu() {
{
this->StopPlayBGM(); this->StopPlayBGM();
this->startupLayout->ReloadMenu(); this->startupLayout->ReloadMenu();
this->LoadLayout(this->startupLayout); this->LoadLayout(this->startupLayout);
this->loaded_menu = MenuType::Startup; this->loaded_menu = MenuType::Startup;
} }
void MenuApplication::LoadThemeMenu() void MenuApplication::LoadThemeMenu() {
{
this->themeMenuLayout->Reload(); this->themeMenuLayout->Reload();
this->LoadLayout(this->themeMenuLayout); this->LoadLayout(this->themeMenuLayout);
this->loaded_menu = MenuType::Theme; this->loaded_menu = MenuType::Theme;
} }
void MenuApplication::LoadSettingsMenu() void MenuApplication::LoadSettingsMenu() {
{
this->settingsMenuLayout->Reload(); this->settingsMenuLayout->Reload();
this->LoadLayout(this->settingsMenuLayout); this->LoadLayout(this->settingsMenuLayout);
this->loaded_menu = MenuType::Settings; this->loaded_menu = MenuType::Settings;
} }
void MenuApplication::LoadSettingsLanguagesMenu() void MenuApplication::LoadSettingsLanguagesMenu() {
{
this->languagesMenuLayout->Reload(); this->languagesMenuLayout->Reload();
this->LoadLayout(this->languagesMenuLayout); this->LoadLayout(this->languagesMenuLayout);
this->loaded_menu = MenuType::Languages; this->loaded_menu = MenuType::Languages;
} }
bool MenuApplication::IsSuspended() bool MenuApplication::IsSuspended() {
{
return this->IsTitleSuspended() || this->IsHomebrewSuspended(); return this->IsTitleSuspended() || this->IsHomebrewSuspended();
} }
bool MenuApplication::IsTitleSuspended() bool MenuApplication::IsTitleSuspended() {
{
return this->status.app_id > 0; return this->status.app_id > 0;
} }
bool MenuApplication::IsHomebrewSuspended() bool MenuApplication::IsHomebrewSuspended() {
{
return strlen(this->status.params.nro_path); return strlen(this->status.params.nro_path);
} }
bool MenuApplication::EqualsSuspendedHomebrewPath(const std::string &path) bool MenuApplication::EqualsSuspendedHomebrewPath(const std::string &path) {
{
return this->status.params.nro_path == path; return this->status.params.nro_path == path;
} }
u64 MenuApplication::GetSuspendedApplicationId() u64 MenuApplication::GetSuspendedApplicationId() {
{
return this->status.app_id; return this->status.app_id;
} }
void MenuApplication::NotifyEndSuspended() void MenuApplication::NotifyEndSuspended() {
{
// Blanking the whole status would also blank the selected user... // Blanking the whole status would also blank the selected user...
this->status.params = {}; this->status.params = {};
this->status.app_id = 0; this->status.app_id = 0;
} }
bool MenuApplication::LaunchFailed() bool MenuApplication::LaunchFailed() {
{ return this->stmode == dmi::MenuStartMode::MenuLaunchFailure;
return (this->stmode == am::MenuStartMode::MenuLaunchFailure);
} }
void MenuApplication::ShowNotification(const std::string &text, u64 timeout) void MenuApplication::ShowNotification(const std::string &text, u64 timeout) {
{
this->EndOverlay(); this->EndOverlay();
this->notifToast->SetText(text); this->notifToast->SetText(text);
this->StartOverlayWithTimeout(this->notifToast, timeout); this->StartOverlayWithTimeout(this->notifToast, timeout);
} }
void MenuApplication::StartPlayBGM() void MenuApplication::StartPlayBGM() {
{ if(this->bgm != nullptr) {
if(this->bgm != nullptr)
{
int loops = this->bgm_loop ? -1 : 1; int loops = this->bgm_loop ? -1 : 1;
if(this->bgm_fade_in_ms > 0) pu::audio::PlayWithFadeIn(this->bgm, loops, this->bgm_fade_in_ms); if(this->bgm_fade_in_ms > 0) {
pu::audio::PlayWithFadeIn(this->bgm, loops, this->bgm_fade_in_ms);
}
else pu::audio::Play(this->bgm, loops); else pu::audio::Play(this->bgm, loops);
} }
} }
void MenuApplication::StopPlayBGM() void MenuApplication::StopPlayBGM() {
{ if(this->bgm_fade_out_ms > 0) {
if(this->bgm_fade_out_ms > 0) pu::audio::FadeOut(this->bgm_fade_out_ms); pu::audio::FadeOut(this->bgm_fade_out_ms);
else pu::audio::Stop(); }
else {
pu::audio::Stop();
}
} }
StartupLayout::Ref &MenuApplication::GetStartupLayout() StartupLayout::Ref &MenuApplication::GetStartupLayout() {
{
return this->startupLayout; return this->startupLayout;
} }
MenuLayout::Ref &MenuApplication::GetMenuLayout() MenuLayout::Ref &MenuApplication::GetMenuLayout() {
{
return this->menuLayout; return this->menuLayout;
} }
ThemeMenuLayout::Ref &MenuApplication::GetThemeMenuLayout() ThemeMenuLayout::Ref &MenuApplication::GetThemeMenuLayout() {
{
return this->themeMenuLayout; return this->themeMenuLayout;
} }
SettingsMenuLayout::Ref &MenuApplication::GetSettingsMenuLayout() SettingsMenuLayout::Ref &MenuApplication::GetSettingsMenuLayout() {
{
return this->settingsMenuLayout; return this->settingsMenuLayout;
} }
LanguagesMenuLayout::Ref &MenuApplication::GetLanguagesMenuLayout() LanguagesMenuLayout::Ref &MenuApplication::GetLanguagesMenuLayout() {
{
return this->languagesMenuLayout; return this->languagesMenuLayout;
} }
void MenuApplication::SetSelectedUser(AccountUid user_id) void MenuApplication::SetSelectedUser(AccountUid user_id) {
{ this->status.selected_user = user_id;
am::MenuCommandWriter writer(am::DaemonMessage::SetSelectedUser); dmi::MenuMessageWriter writer(dmi::DaemonMessage::SetSelectedUser);
writer.Write<AccountUid>(user_id); writer.Write<AccountUid>(user_id);
writer.FinishWrite();
memcpy(&this->status.selected_user, &user_id, sizeof(user_id));
} }
AccountUid MenuApplication::GetSelectedUser() AccountUid MenuApplication::GetSelectedUser() {
{
return this->status.selected_user; return this->status.selected_user;
} }
MenuType MenuApplication::GetCurrentLoadedMenu() MenuType MenuApplication::GetCurrentLoadedMenu() {
{
return this->loaded_menu; return this->loaded_menu;
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -1,31 +1,23 @@
#include <ui/ui_QuickMenu.hpp> #include <ui/ui_QuickMenu.hpp>
#include <ui/ui_MenuApplication.hpp> #include <ui/ui_MenuApplication.hpp>
#include <cfg/cfg_Config.hpp> #include <cfg/cfg_Config.hpp>
#include <am_DaemonMessages.hpp> #include <am/am_DaemonMessages.hpp>
extern cfg::Theme g_ul_theme; extern cfg::Theme g_ul_theme;
extern ui::MenuApplication::Ref g_menu_app_instance; extern ui::MenuApplication::Ref g_menu_app_instance;
namespace ui namespace ui {
{
static Mutex g_quick_menu_home_lock = EmptyMutex; static Mutex g_quick_menu_home_lock = EmptyMutex;
static bool g_quick_menu_home_pressed = false; static bool g_quick_menu_home_pressed = false;
static void QuickMenuOnHomeButtonDetection() QuickMenu::QuickMenu(const std::string &main_icon) {
{
mutexLock(&g_quick_menu_home_lock);
g_quick_menu_home_pressed = true;
mutexUnlock(&g_quick_menu_home_lock);
}
QuickMenu::QuickMenu(const std::string &main_icon)
{
this->on = false; this->on = false;
this->bgalpha = 0; this->bgalpha = 0;
pu::ui::Color textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff")); auto textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
pu::ui::Color menufocusclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("menu_focus_color", "#5ebcffff")); auto menufocusclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("menu_focus_color", "#5ebcffff"));
pu::ui::Color menubgclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("menu_bg_color", "#0094ffff")); auto menubgclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("menu_bg_color", "#0094ffff"));
this->options_menu = pu::ui::elm::Menu::New(200, 115, 880, menubgclr, 60, 8); this->options_menu = pu::ui::elm::Menu::New(200, 115, 880, menubgclr, 60, 8);
this->options_menu->SetOnFocusColor(menufocusclr); this->options_menu->SetOnFocusColor(menufocusclr);
@ -80,92 +72,96 @@ namespace ui
this->options_menu->AddItem(opt_item); this->options_menu->AddItem(opt_item);
} }
void QuickMenu::RegisterHomeButtonDetection() s32 QuickMenu::GetX() {
{
am::RegisterOnMessageDetect(&QuickMenuOnHomeButtonDetection, am::MenuMessage::HomeRequest);
}
s32 QuickMenu::GetX()
{
return 0; return 0;
} }
s32 QuickMenu::GetY() s32 QuickMenu::GetY() {
{
return 0; return 0;
} }
s32 QuickMenu::GetWidth() s32 QuickMenu::GetWidth() {
{
return 1280; return 1280;
} }
s32 QuickMenu::GetHeight() s32 QuickMenu::GetHeight() {
{
return 720; return 720;
} }
void QuickMenu::Toggle() void QuickMenu::Toggle() {
{
this->on = !this->on; this->on = !this->on;
} }
bool QuickMenu::IsOn() bool QuickMenu::IsOn() {
{
return this->on && (this->bgalpha > 0); return this->on && (this->bgalpha > 0);
} }
void QuickMenu::OnRender(pu::ui::render::Renderer::Ref &Drawer, s32 X, s32 Y) void QuickMenu::OnRender(pu::ui::render::Renderer::Ref &drawer, s32 x, s32 y) {
{ if(!this->on) {
if(!this->on) if(this->bgalpha > 0) {
{
if(this->bgalpha > 0)
{
this->bgalpha -= 20; this->bgalpha -= 20;
if(this->bgalpha < 0) this->bgalpha = 0; if(this->bgalpha < 0) {
this->bgalpha = 0;
}
} }
} }
else else {
{ if(this->bgalpha < 220) {
if(this->bgalpha < 220)
{
this->bgalpha += 20; this->bgalpha += 20;
if(this->bgalpha > 220) this->bgalpha = 220; if(this->bgalpha > 220) {
this->bgalpha = 220;
}
} }
} }
this->options_menu->SetVisible(this->bgalpha != 0); this->options_menu->SetVisible(this->bgalpha != 0);
Drawer->RenderRectangleFill({ 50, 50, 50, (u8)this->bgalpha }, 0, 0, 1280, 720); auto bgalphau8 = static_cast<u8>(this->bgalpha);
if(this->bgalpha > 0) drawer->RenderRectangleFill({ 50, 50, 50, bgalphau8 }, 0, 0, 1280, 720);
{
if(this->bgalpha < 220) Drawer->SetBaseRenderAlpha((u8)this->bgalpha); if(this->bgalpha > 0) {
this->options_menu->OnRender(Drawer, this->options_menu->GetProcessedX(), this->options_menu->GetProcessedY()); if(this->bgalpha < 220) {
if(this->bgalpha < 220) Drawer->UnsetBaseRenderAlpha(); drawer->SetBaseRenderAlpha(bgalphau8);
}
this->options_menu->OnRender(drawer, this->options_menu->GetProcessedX(), this->options_menu->GetProcessedY());
if(this->bgalpha < 220) {
drawer->UnsetBaseRenderAlpha();
}
} }
} }
void QuickMenu::OnInput(u64 Down, u64 Up, u64 Held, pu::ui::Touch Pos) void QuickMenu::OnInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) {
{ if(this->on) {
if(this->on) this->options_menu->OnInput(Down, Up, Held, Pos); this->options_menu->OnInput(down, up, held, touch_pos);
if((Down & KEY_L) || (Down & KEY_R) || (Down & KEY_ZL) || (Down & KEY_ZR)) this->Toggle();
else if((Down & KEY_B) || (Down & KEY_A))
{
// B only valid for toggling off
// A = something selected in the menu :P
if(this->on) this->Toggle();
} }
else
{ if((down & KEY_L) || (down & KEY_R) || (down & KEY_ZL) || (down & KEY_ZR)) {
if(this->on) this->Toggle();
{ }
else if((down & KEY_B) || (down & KEY_A)) {
// B only valid for toggling off
// A = something selected in the menu
if(this->on) {
this->Toggle();
}
}
else {
if(this->on) {
mutexLock(&g_quick_menu_home_lock); mutexLock(&g_quick_menu_home_lock);
auto home_pressed = g_quick_menu_home_pressed; auto home_pressed = g_quick_menu_home_pressed;
g_quick_menu_home_pressed = false; g_quick_menu_home_pressed = false;
mutexUnlock(&g_quick_menu_home_lock); mutexUnlock(&g_quick_menu_home_lock);
if(home_pressed) this->Toggle(); if(home_pressed) {
this->Toggle();
}
} }
} }
} }
void QuickMenuOnHomeButtonDetection() {
mutexLock(&g_quick_menu_home_lock);
g_quick_menu_home_pressed = true;
mutexUnlock(&g_quick_menu_home_lock);
}
} }

View file

@ -1,91 +1,67 @@
#include <ui/ui_RawData.hpp> #include <ui/ui_RawData.hpp>
namespace ui namespace ui {
{
RawData::RawData(s32 X, s32 Y, void *raw, s32 Width, s32 Height, u32 PixNum) RawData::RawData(s32 x, s32 y, void *raw, s32 w, s32 h, u32 pix_num) : x(x), y(y), w(w), h(h), falpha(0xFF), ptr(raw), pitch(w * pix_num) {
{ if(this->ptr != nullptr) {
this->x = X; this->ntex = SDL_CreateTexture(pu::ui::render::GetMainRenderer(), SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, this->w, this->h);
this->y = Y; if(this->ntex != nullptr) {
this->w = Width; SDL_UpdateTexture(this->ntex, nullptr, this->ptr, this->pitch);
this->h = Height;
this->pitch = Width * PixNum;
this->ptr = raw;
this->falpha = 255;
if(raw != nullptr)
{
this->ntex = SDL_CreateTexture(pu::ui::render::GetMainRenderer(), SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, Width, Height);
if(this->ntex != nullptr)
{
SDL_UpdateTexture(this->ntex, nullptr, raw, this->pitch);
} }
} }
} }
RawData::~RawData() RawData::~RawData() {
{ if(this->ntex != nullptr) {
if(this->ntex != nullptr)
{
pu::ui::render::DeleteTexture(this->ntex); pu::ui::render::DeleteTexture(this->ntex);
this->ntex = nullptr; this->ntex = nullptr;
} }
} }
s32 RawData::GetX() s32 RawData::GetX() {
{
return this->x; return this->x;
} }
void RawData::SetX(s32 X) void RawData::SetX(s32 x) {
{ this->x = x;
this->x = X;
} }
s32 RawData::GetY() s32 RawData::GetY() {
{
return this->y; return this->y;
} }
void RawData::SetY(s32 Y) void RawData::SetY(s32 y) {
{ this->y = y;
this->y = Y;
} }
s32 RawData::GetWidth() s32 RawData::GetWidth() {
{
return this->w; return this->w;
} }
void RawData::SetWidth(s32 Width) void RawData::SetWidth(s32 w) {
{ this->w = w;
this->w = Width;
} }
s32 RawData::GetHeight() s32 RawData::GetHeight() {
{
return this->h; return this->h;
} }
void RawData::SetHeight(s32 Height) void RawData::SetHeight(s32 h) {
{ this->h = h;
this->h = Height;
} }
void RawData::SetAlphaFactor(u8 Factor) void RawData::SetAlphaFactor(u8 factor) {
{ this->falpha = factor;
this->falpha = Factor;
} }
void RawData::OnRender(pu::ui::render::Renderer::Ref &Drawer, s32 X, s32 Y) void RawData::OnRender(pu::ui::render::Renderer::Ref &drawer, s32 x, s32 y) {
{ if(this->ntex != nullptr) {
if(this->ntex != nullptr) drawer->SetBaseRenderAlpha(this->falpha);
{ drawer->RenderTexture(this->ntex, x, y, { -1, this->w, this->h, -1 });
Drawer->SetBaseRenderAlpha(this->falpha); drawer->UnsetBaseRenderAlpha();
Drawer->RenderTexture(this->ntex, X, Y, { -1, this->w, this->h, -1 });
Drawer->UnsetBaseRenderAlpha();
} }
} }
void RawData::OnInput(u64 Down, u64 Up, u64 Held, pu::ui::Touch Pos) void RawData::OnInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) {}
{
}
} }

View file

@ -11,34 +11,29 @@ extern ui::MenuApplication::Ref g_menu_app_instance;
extern cfg::Theme g_ul_theme; extern cfg::Theme g_ul_theme;
extern cfg::Config g_ul_config; extern cfg::Config g_ul_config;
namespace ui namespace ui {
{
template<typename T> template<typename T>
std::string EncodeForSettings(T t) inline std::string EncodeForSettings(T t) {
{
return cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_unknown_value"); return cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_unknown_value");
} }
template<> template<>
std::string EncodeForSettings<std::string>(std::string t) inline std::string EncodeForSettings<std::string>(std::string t) {
{
return "\"" + t + "\""; return "\"" + t + "\"";
} }
template<> template<>
std::string EncodeForSettings<u32>(u32 t) inline std::string EncodeForSettings<u32>(u32 t) {
{
return "\"" + std::to_string(t) + "\""; return "\"" + std::to_string(t) + "\"";
} }
template<> template<>
std::string EncodeForSettings<bool>(bool t) inline std::string EncodeForSettings<bool>(bool t) {
{
return t ? cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_true_value") : cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_false_value"); return t ? cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_true_value") : cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_false_value");
} }
SettingsMenuLayout::SettingsMenuLayout() SettingsMenuLayout::SettingsMenuLayout() {
{
this->SetBackgroundImage(cfg::GetAssetByTheme(g_ul_theme, "ui/Background.png")); this->SetBackgroundImage(cfg::GetAssetByTheme(g_ul_theme, "ui/Background.png"));
pu::ui::Color textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff")); pu::ui::Color textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
@ -57,25 +52,21 @@ namespace ui
this->Add(this->settingsMenu); this->Add(this->settingsMenu);
} }
void SettingsMenuLayout::OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch pos) void SettingsMenuLayout::OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch pos) {
{ if(down & KEY_B) {
if(down & KEY_B)
{
g_menu_app_instance->FadeOut(); g_menu_app_instance->FadeOut();
g_menu_app_instance->LoadMenu(); g_menu_app_instance->LoadMenu();
g_menu_app_instance->FadeIn(); g_menu_app_instance->FadeIn();
} }
} }
void SettingsMenuLayout::OnHomeButtonPress() void SettingsMenuLayout::OnHomeButtonPress() {
{
g_menu_app_instance->FadeOut(); g_menu_app_instance->FadeOut();
g_menu_app_instance->LoadMenu(); g_menu_app_instance->LoadMenu();
g_menu_app_instance->FadeIn(); g_menu_app_instance->FadeIn();
} }
void SettingsMenuLayout::Reload() void SettingsMenuLayout::Reload() {
{
this->settingsMenu->ClearItems(); this->settingsMenu->ClearItems();
this->settingsMenu->SetSelectedIndex(0); this->settingsMenu->SetSelectedIndex(0);
@ -87,9 +78,8 @@ namespace ui
this->PushSettingItem(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_console_timezone"), EncodeForSettings<std::string>(loc.name), -1); this->PushSettingItem(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_console_timezone"), EncodeForSettings<std::string>(loc.name), -1);
this->PushSettingItem(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_viewer_enabled"), EncodeForSettings(g_ul_config.viewer_usb_enabled), 1); this->PushSettingItem(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_viewer_enabled"), EncodeForSettings(g_ul_config.viewer_usb_enabled), 1);
this->PushSettingItem(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_flog_enabled"), EncodeForSettings(g_ul_config.system_title_override_enabled), 2); this->PushSettingItem(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_flog_enabled"), EncodeForSettings(g_ul_config.system_title_override_enabled), 2);
std::string connectednet = cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_wifi_none"); auto connectednet = cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_wifi_none");
if(net::HasConnection()) if(net::HasConnection()) {
{
net::NetworkProfileData data = {}; net::NetworkProfileData data = {};
net::GetCurrentNetworkProfile(&data); net::GetCurrentNetworkProfile(&data);
connectednet = data.wifi_name; connectednet = data.wifi_name;
@ -97,7 +87,7 @@ namespace ui
this->PushSettingItem(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_wifi_name"), EncodeForSettings(connectednet), 3); this->PushSettingItem(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_wifi_name"), EncodeForSettings(connectednet), 3);
u64 lcode = 0; u64 lcode = 0;
SetLanguage ilang = SetLanguage_ENUS; auto ilang = SetLanguage_ENUS;
setGetLanguageCode(&lcode); setGetLanguageCode(&lcode);
setMakeLanguage(lcode, &ilang); setMakeLanguage(lcode, &ilang);
this->PushSettingItem(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_console_lang"), EncodeForSettings(os::GetLanguageName(ilang)), 4); this->PushSettingItem(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_console_lang"), EncodeForSettings(os::GetLanguageName(ilang)), 4);
@ -122,20 +112,20 @@ namespace ui
bool nfc = false; bool nfc = false;
setsysGetNfcEnableFlag(&nfc); setsysGetNfcEnableFlag(&nfc);
this->PushSettingItem(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_nfc"), EncodeForSettings(nfc), 11); this->PushSettingItem(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_nfc"), EncodeForSettings(nfc), 11);
char serial[0x20] = {0}; SetSysSerialNumber serial = {};
setsysGetSerialNumber(serial); setsysGetSerialNumber(&serial);
this->PushSettingItem(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_serial_no"), EncodeForSettings<std::string>(serial), -1); this->PushSettingItem(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_serial_no"), EncodeForSettings<std::string>(serial.number), -1);
u64 mac = 0; u64 mac = 0;
net::GetMACAddress(&mac); net::GetMACAddress(&mac);
auto strmac = net::FormatMACAddress(mac); auto strmac = net::FormatMACAddress(mac);
this->PushSettingItem(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_mac_addr"), EncodeForSettings(strmac), -1); this->PushSettingItem(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_mac_addr"), EncodeForSettings(strmac), -1);
auto ipstr = net::GetConsoleIPAddress(); auto ipstr = net::GetConsoleIPAddress();
// TODO: strings
this->PushSettingItem("Console IP address", EncodeForSettings(ipstr), -1); this->PushSettingItem("Console IP address", EncodeForSettings(ipstr), -1);
} }
void SettingsMenuLayout::PushSettingItem(const std::string &name, const std::string &value_display, int id) void SettingsMenuLayout::PushSettingItem(const std::string &name, const std::string &value_display, int id) {
{ auto textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
pu::ui::Color textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
auto itm = pu::ui::elm::MenuItem::New(name + ": " + value_display); auto itm = pu::ui::elm::MenuItem::New(name + ": " + value_display);
itm->AddOnClick(std::bind(&SettingsMenuLayout::setting_Click, this, id)); itm->AddOnClick(std::bind(&SettingsMenuLayout::setting_Click, this, id));
itm->SetIcon(cfg::GetAssetByTheme(g_ul_theme, "ui/Setting" + std::string((id < 0) ? "No" : "") + "Editable.png")); itm->SetIcon(cfg::GetAssetByTheme(g_ul_theme, "ui/Setting" + std::string((id < 0) ? "No" : "") + "Editable.png"));
@ -143,13 +133,10 @@ namespace ui
this->settingsMenu->AddItem(itm); this->settingsMenu->AddItem(itm);
} }
void SettingsMenuLayout::setting_Click(u32 id) void SettingsMenuLayout::setting_Click(u32 id) {
{
bool reload_need = false; bool reload_need = false;
switch(id) switch(id) {
{ case 0: {
case 0:
{
SwkbdConfig swkbd; SwkbdConfig swkbd;
swkbdCreate(&swkbd, 0); swkbdCreate(&swkbd, 0);
swkbdConfigSetGuideText(&swkbd, cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "swkbd_console_nick_guide").c_str()); swkbdConfigSetGuideText(&swkbd, cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "swkbd_console_nick_guide").c_str());
@ -160,116 +147,104 @@ namespace ui
char name[SET_MAX_NICKNAME_SIZE] = {0}; char name[SET_MAX_NICKNAME_SIZE] = {0};
auto rc = swkbdShow(&swkbd, name, SET_MAX_NICKNAME_SIZE); auto rc = swkbdShow(&swkbd, name, SET_MAX_NICKNAME_SIZE);
swkbdClose(&swkbd); swkbdClose(&swkbd);
if(R_SUCCEEDED(rc)) if(R_SUCCEEDED(rc)) {
{
setsysSetDeviceNickname(name); setsysSetDeviceNickname(name);
reload_need = true; reload_need = true;
} }
break; break;
} }
case 1: case 1: {
{
auto sopt = g_menu_app_instance->CreateShowDialog(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_viewer_enabled"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_viewer_info") + "\n" + (g_ul_config.viewer_usb_enabled ? cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_disable_conf") : cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_enable_conf")), { cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "yes"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "cancel") }, true); auto sopt = g_menu_app_instance->CreateShowDialog(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_viewer_enabled"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_viewer_info") + "\n" + (g_ul_config.viewer_usb_enabled ? cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_disable_conf") : cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_enable_conf")), { cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "yes"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "cancel") }, true);
if(sopt == 0) if(sopt == 0) {
{
g_ul_config.viewer_usb_enabled = !g_ul_config.viewer_usb_enabled; g_ul_config.viewer_usb_enabled = !g_ul_config.viewer_usb_enabled;
reload_need = true; reload_need = true;
g_menu_app_instance->ShowNotification(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_changed_reboot")); g_menu_app_instance->ShowNotification(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_changed_reboot"));
} }
break; break;
} }
case 2: case 2: {
{
auto sopt = g_menu_app_instance->CreateShowDialog(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_flog_enabled"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_flog_info") + "\n" + (g_ul_config.viewer_usb_enabled ? cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_disable_conf") : cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_enable_conf")), { cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "yes"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "cancel") }, true); auto sopt = g_menu_app_instance->CreateShowDialog(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_flog_enabled"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_flog_info") + "\n" + (g_ul_config.viewer_usb_enabled ? cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_disable_conf") : cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_enable_conf")), { cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "yes"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "cancel") }, true);
if(sopt == 0) if(sopt == 0) {
{
g_ul_config.system_title_override_enabled = !g_ul_config.system_title_override_enabled; g_ul_config.system_title_override_enabled = !g_ul_config.system_title_override_enabled;
reload_need = true; reload_need = true;
} }
break; break;
} }
case 3: case 3: {
{
u8 in[28] = {0}; u8 in[28] = {0};
*(u32*)in = 1; // 0 = normal, 1 = qlaunch, 2 = starter? // 0 = normal, 1 = qlaunch, 2 = starter...?
*reinterpret_cast<u32*>(in) = 1;
u8 out[8] = {0}; u8 out[8] = {0};
LibAppletArgs netargs; LibAppletArgs netargs;
libappletArgsCreate(&netargs, 0); libappletArgsCreate(&netargs, 0);
auto rc = libappletLaunch(AppletId_netConnect, &netargs, in, sizeof(in), out, sizeof(out), nullptr); auto rc = libappletLaunch(AppletId_netConnect, &netargs, in, sizeof(in), out, sizeof(out), nullptr);
if(R_SUCCEEDED(rc)) if(R_SUCCEEDED(rc)) {
{ rc = *reinterpret_cast<Result*>(out);
rc = *(u32*)out; if(R_SUCCEEDED(rc)) {
if(R_SUCCEEDED(rc)) reload_need = true; reload_need = true;
}
} }
break; break;
} }
case 4: case 4: {
{
g_menu_app_instance->FadeOut(); g_menu_app_instance->FadeOut();
g_menu_app_instance->LoadSettingsLanguagesMenu(); g_menu_app_instance->LoadSettingsLanguagesMenu();
g_menu_app_instance->FadeIn(); g_menu_app_instance->FadeIn();
break; break;
} }
case 5: case 5: {
{ auto console_info_upload = false;
bool console_info_upload = false;
setsysGetConsoleInformationUploadFlag(&console_info_upload); setsysGetConsoleInformationUploadFlag(&console_info_upload);
setsysSetConsoleInformationUploadFlag(!console_info_upload); setsysSetConsoleInformationUploadFlag(!console_info_upload);
reload_need = true; reload_need = true;
break; break;
} }
case 6: case 6: {
{ auto auto_titles_dl = false;
bool auto_titles_dl = false;
setsysGetAutomaticApplicationDownloadFlag(&auto_titles_dl); setsysGetAutomaticApplicationDownloadFlag(&auto_titles_dl);
setsysSetAutomaticApplicationDownloadFlag(!auto_titles_dl); setsysSetAutomaticApplicationDownloadFlag(!auto_titles_dl);
reload_need = true; reload_need = true;
break; break;
} }
case 7: case 7: {
{ auto auto_update = false;
bool auto_update = false;
setsysGetAutoUpdateEnableFlag(&auto_update); setsysGetAutoUpdateEnableFlag(&auto_update);
setsysSetAutoUpdateEnableFlag(!auto_update); setsysSetAutoUpdateEnableFlag(!auto_update);
reload_need = true; reload_need = true;
break; break;
} }
case 8: case 8: {
{ auto wireless_lan = false;
bool wireless_lan = false;
setsysGetWirelessLanEnableFlag(&wireless_lan); setsysGetWirelessLanEnableFlag(&wireless_lan);
setsysSetWirelessLanEnableFlag(!wireless_lan); setsysSetWirelessLanEnableFlag(!wireless_lan);
reload_need = true; reload_need = true;
break; break;
} }
case 9: case 9: {
{ auto bluetooth = false;
bool bluetooth = false;
setsysGetBluetoothEnableFlag(&bluetooth); setsysGetBluetoothEnableFlag(&bluetooth);
setsysSetBluetoothEnableFlag(!bluetooth); setsysSetBluetoothEnableFlag(!bluetooth);
reload_need = true; reload_need = true;
break; break;
} }
case 10: case 10: {
{ auto usb_30 = false;
bool usb_30 = false;
setsysGetUsb30EnableFlag(&usb_30); setsysGetUsb30EnableFlag(&usb_30);
setsysSetUsb30EnableFlag(!usb_30); setsysSetUsb30EnableFlag(!usb_30);
reload_need = true; reload_need = true;
break; break;
} }
case 11: case 11: {
{ auto nfc = false;
bool nfc = false;
setsysGetNfcEnableFlag(&nfc); setsysGetNfcEnableFlag(&nfc);
setsysSetNfcEnableFlag(!nfc); setsysSetNfcEnableFlag(!nfc);
@ -277,10 +252,10 @@ namespace ui
break; break;
} }
} }
if(reload_need) if(reload_need) {
{
cfg::SaveConfig(g_ul_config); cfg::SaveConfig(g_ul_config);
this->Reload(); this->Reload();
} }
} }
} }

View file

@ -2,86 +2,59 @@
namespace ui namespace ui
{ {
SideMenu::SideMenu(pu::ui::Color suspended_clr, std::string cursor_path, std::string suspended_img_path, std::string multiselect_img_path, u32 txt_x, u32 txt_y, pu::String font_name, pu::ui::Color txt_clr, s32 y) SideMenu::SideMenu(pu::ui::Color suspended_clr, std::string cursor_path, std::string suspended_img_path, std::string multiselect_img_path, u32 txt_x, u32 txt_y, pu::String font_name, pu::ui::Color txt_clr, s32 y) : selitm(0), suspitm(-1), baseiconidx(0), movalpha(0), textx(txt_x), texty(txt_y), enabled(true), suspclr(suspended_clr), textclr(txt_clr), onselect([](u32, u64){}), onselch([](u32){}), leftbicon(nullptr), rightbicon(nullptr), textfont(font_name), scrollflag(0), scrolltpvalue(50), scrollcount(0) {
{
this->selitm = 0;
this->suspitm = -1;
this->suspclr = suspended_clr;
this->baseiconidx = 0;
this->scrollflag = 0;
this->movalpha = 0;
this->leftbicon = nullptr;
this->rightbicon = nullptr;
this->cursoricon = pu::ui::render::LoadImage(cursor_path); this->cursoricon = pu::ui::render::LoadImage(cursor_path);
this->suspicon = pu::ui::render::LoadImage(suspended_img_path); this->suspicon = pu::ui::render::LoadImage(suspended_img_path);
this->mselicon = pu::ui::render::LoadImage(multiselect_img_path); this->mselicon = pu::ui::render::LoadImage(multiselect_img_path);
this->textfont = font_name;
this->textx = txt_x;
this->texty = txt_y;
this->textclr = txt_clr;
this->onselect = [](u32,u64){};
this->onselch = [](u32){};
this->scrolltpvalue = 50;
this->scrollcount = 0;
this->enabled = true;
this->SetY(y); this->SetY(y);
} }
SideMenu::~SideMenu() SideMenu::~SideMenu() {
{ if(this->cursoricon != nullptr) {
if(this->cursoricon != nullptr)
{
pu::ui::render::DeleteTexture(this->cursoricon); pu::ui::render::DeleteTexture(this->cursoricon);
this->cursoricon = nullptr; this->cursoricon = nullptr;
} }
if(this->suspicon != nullptr) if(this->suspicon != nullptr) {
{
pu::ui::render::DeleteTexture(this->suspicon); pu::ui::render::DeleteTexture(this->suspicon);
this->suspicon = nullptr; this->suspicon = nullptr;
} }
this->ClearItems(); this->ClearItems();
} }
s32 SideMenu::GetX() s32 SideMenu::GetX() {
{
return 98; return 98;
} }
s32 SideMenu::GetY() s32 SideMenu::GetY() {
{
return this->y; return this->y;
} }
void SideMenu::SetX(s32 x) void SideMenu::SetX(s32 x) {}
{
}
void SideMenu::SetY(s32 y) void SideMenu::SetY(s32 y) {
{
this->y = y; this->y = y;
} }
s32 SideMenu::GetWidth() s32 SideMenu::GetWidth() {
{
return 1280; return 1280;
} }
s32 SideMenu::GetHeight() s32 SideMenu::GetHeight() {
{
return ItemSize + FocusSize + FocusMargin; return ItemSize + FocusSize + FocusMargin;
} }
void SideMenu::OnRender(pu::ui::render::Renderer::Ref &drawer, s32 x, s32 y) void SideMenu::OnRender(pu::ui::render::Renderer::Ref &drawer, s32 x, s32 y) {
{ if(this->icons.empty()) {
if(this->icons.empty()) return; return;
if(this->ricons.empty()) }
{ if(this->ricons.empty()) {
for(u32 i = 0; i < std::min((size_t)4, (this->icons.size() - this->baseiconidx)); i++) for(u32 i = 0; i < std::min(static_cast<size_t>(4), this->icons.size() - this->baseiconidx); i++) {
{
auto icon = pu::ui::render::LoadImage(this->icons[this->baseiconidx + i]); auto icon = pu::ui::render::LoadImage(this->icons[this->baseiconidx + i]);
auto text = this->icons_texts[this->baseiconidx + i]; auto text = this->icons_texts[this->baseiconidx + i];
this->ricons.push_back(icon); this->ricons.push_back(icon);
pu::sdl2::Texture ntext = nullptr; pu::sdl2::Texture ntext = nullptr;
if(!text.empty()) ntext = pu::ui::render::RenderText(this->textfont, text, this->textclr); if(!text.empty()) {
ntext = pu::ui::render::RenderText(this->textfont, text, this->textclr);
}
this->ricons_texts.push_back(ntext); this->ricons_texts.push_back(ntext);
} }
this->UpdateBorderIcons(); this->UpdateBorderIcons();
@ -90,76 +63,79 @@ namespace ui
u32 basex = x; u32 basex = x;
for(u32 i = 0; i < this->ricons.size(); i++) for(u32 i = 0; i < this->ricons.size(); i++) {
{
auto ricon = this->ricons[i]; auto ricon = this->ricons[i];
drawer->RenderTexture(ricon, basex, y, { -1, ItemSize, ItemSize, -1 }); drawer->RenderTexture(ricon, basex, y, { -1, ItemSize, ItemSize, -1 });
auto ntext = this->ricons_texts[i]; auto ntext = this->ricons_texts[i];
if(ntext != nullptr) drawer->RenderTexture(ntext, basex + this->textx, y + this->texty); if(ntext != nullptr) {
if(this->IsItemMultiselected(this->baseiconidx + i)) drawer->RenderTexture(this->mselicon, basex - Margin, y - Margin, { -1, ExtraIconSize, ExtraIconSize, -1 }); drawer->RenderTexture(ntext, basex + this->textx, y + this->texty);
if(this->suspitm >= 0) }
{ if(this->IsItemMultiselected(this->baseiconidx + i)) {
if((this->baseiconidx + i) == (u32)suspitm) drawer->RenderTexture(this->mselicon, basex - Margin, y - Margin, { -1, ExtraIconSize, ExtraIconSize, -1 });
{ }
if(this->suspicon != nullptr) drawer->RenderTexture(this->suspicon, basex - Margin, y - Margin, { -1, ExtraIconSize, ExtraIconSize, -1 }); if(this->suspitm >= 0) {
if((this->baseiconidx + i) == static_cast<u32>(suspitm)) {
if(this->suspicon != nullptr) {
drawer->RenderTexture(this->suspicon, basex - Margin, y - Margin, { -1, ExtraIconSize, ExtraIconSize, -1 });
}
} }
} }
if(this->cursoricon != nullptr) if(this->cursoricon != nullptr) {
{ if((this->baseiconidx + i) == selitm) {
if((this->baseiconidx + i) == selitm)
{
drawer->RenderTexture(this->cursoricon, basex - Margin, y - Margin, { 255 - movalpha, ExtraIconSize, ExtraIconSize, -1 }); drawer->RenderTexture(this->cursoricon, basex - Margin, y - Margin, { 255 - movalpha, ExtraIconSize, ExtraIconSize, -1 });
} }
else if((this->baseiconidx + i) == preselitm) else if((this->baseiconidx + i) == preselitm) {
{
drawer->RenderTexture(this->cursoricon, basex - Margin, y - Margin, { movalpha, ExtraIconSize, ExtraIconSize, -1 }); drawer->RenderTexture(this->cursoricon, basex - Margin, y - Margin, { movalpha, ExtraIconSize, ExtraIconSize, -1 });
} }
} }
basex += ItemSize + Margin; basex += ItemSize + Margin;
} }
if(leftbicon != nullptr) if(leftbicon != nullptr) {
{
drawer->RenderTexture(leftbicon, x - ItemSize - Margin, y, { -1, ItemSize, ItemSize, -1 }); drawer->RenderTexture(leftbicon, x - ItemSize - Margin, y, { -1, ItemSize, ItemSize, -1 });
} }
if(rightbicon != nullptr) {
if(rightbicon != nullptr)
{
drawer->RenderTexture(rightbicon, x + ((ItemSize + Margin) * 4), y, { -1, ItemSize, ItemSize, -1 }); drawer->RenderTexture(rightbicon, x + ((ItemSize + Margin) * 4), y, { -1, ItemSize, ItemSize, -1 });
} }
if(movalpha > 0) if(movalpha > 0) {
{
s32 tmpalpha = movalpha - 30; s32 tmpalpha = movalpha - 30;
if(tmpalpha < 0) tmpalpha = 0; if(tmpalpha < 0) {
movalpha = (u8)tmpalpha; tmpalpha = 0;
}
movalpha = static_cast<u8>(tmpalpha);
} }
} }
void SideMenu::OnInput(u64 down, u64 up, u64 held, pu::ui::Touch pos) void SideMenu::OnInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) {
{ if(this->ricons.empty()) {
if(this->ricons.empty()) return; return;
if(!this->enabled) return; }
if(!this->enabled) {
return;
}
if(down & KEY_LEFT) HandleMoveLeft(); if(down & KEY_LEFT) {
else if(down & KEY_RIGHT) HandleMoveRight(); HandleMoveLeft();
else if(!pos.IsEmpty()) }
{ else if(down & KEY_RIGHT) {
s32 basex = this->GetProcessedX(); HandleMoveRight();
s32 basey = this->GetProcessedY(); }
else if(!touch_pos.IsEmpty()) {
auto basex = this->GetProcessedX();
auto basey = this->GetProcessedY();
if(this->cursoricon != nullptr) if(this->cursoricon != nullptr) {
{ for(u32 i = 0; i < this->ricons.size(); i++) {
for(u32 i = 0; i < this->ricons.size(); i++) constexpr auto item_size = static_cast<s32>(ItemSize);
{ if((touch_pos.X >= basex) && (touch_pos.X < (basex + item_size)) && (touch_pos.Y >= basey) && (touch_pos.Y < (basey + item_size))) {
if((pos.X >= basex) && (pos.X < (basex + (s32)ItemSize)) && (pos.Y >= basey) && (pos.Y < (basey + (s32)ItemSize))) if((this->baseiconidx + i) == selitm) {
{ (this->onselect)(KEY_A, this->selitm);
if((this->baseiconidx + i) == selitm) (this->onselect)(KEY_A, this->selitm); }
else else {
{
preselitm = selitm; preselitm = selitm;
selitm = this->baseiconidx + i; selitm = this->baseiconidx + i;
movalpha = 255; movalpha = 0xFF;
(this->onselch)(this->selitm); (this->onselch)(this->selitm);
} }
break; break;
@ -168,23 +144,19 @@ namespace ui
} }
} }
} }
else (this->onselect)(down, this->selitm); else {
(this->onselect)(down, this->selitm);
}
if(held & KEY_LEFT) if(held & KEY_LEFT) {
{ if(this->scrollflag == 1) {
if(this->scrollflag == 1)
{
auto curtp = std::chrono::steady_clock::now(); auto curtp = std::chrono::steady_clock::now();
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(curtp - this->scrolltp).count(); auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(curtp - this->scrolltp).count();
if(diff >= 300) if(diff >= 300) {
{ if(this->scrollmoveflag) {
if(this->scrollmoveflag)
{
auto diff2 = std::chrono::duration_cast<std::chrono::milliseconds>(curtp - this->scrollmovetp).count(); auto diff2 = std::chrono::duration_cast<std::chrono::milliseconds>(curtp - this->scrollmovetp).count();
if(diff2 >= this->scrolltpvalue) if(diff2 >= this->scrolltpvalue) {
{ if(this->scrollcount >= 5) {
if(this->scrollcount >= 5)
{
this->scrollcount = 0; this->scrollcount = 0;
this->scrolltpvalue /= 2; this->scrolltpvalue /= 2;
} }
@ -193,34 +165,26 @@ namespace ui
this->scrollcount++; this->scrollcount++;
} }
} }
else else {
{
this->scrollmovetp = std::chrono::steady_clock::now(); this->scrollmovetp = std::chrono::steady_clock::now();
this->scrollmoveflag = true; this->scrollmoveflag = true;
} }
} }
} }
else else {
{
this->scrollflag = 1; this->scrollflag = 1;
this->scrolltp = std::chrono::steady_clock::now(); this->scrolltp = std::chrono::steady_clock::now();
} }
} }
else if(held & KEY_RIGHT) else if(held & KEY_RIGHT) {
{ if(this->scrollflag == 2) {
if(this->scrollflag == 2)
{
auto curtp = std::chrono::steady_clock::now(); auto curtp = std::chrono::steady_clock::now();
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(curtp - this->scrolltp).count(); auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(curtp - this->scrolltp).count();
if(diff >= 300) if(diff >= 300) {
{ if(this->scrollmoveflag) {
if(this->scrollmoveflag)
{
auto diff2 = std::chrono::duration_cast<std::chrono::milliseconds>(curtp - this->scrollmovetp).count(); auto diff2 = std::chrono::duration_cast<std::chrono::milliseconds>(curtp - this->scrollmovetp).count();
if(diff2 >= this->scrolltpvalue) if(diff2 >= this->scrolltpvalue) {
{ if(this->scrollcount >= 5) {
if(this->scrollcount >= 5)
{
this->scrollcount = 0; this->scrollcount = 0;
this->scrolltpvalue /= 2; this->scrolltpvalue /= 2;
} }
@ -229,50 +193,46 @@ namespace ui
this->scrollcount++; this->scrollcount++;
} }
} }
else else {
{
this->scrollmovetp = std::chrono::steady_clock::now(); this->scrollmovetp = std::chrono::steady_clock::now();
this->scrollmoveflag = true; this->scrollmoveflag = true;
} }
} }
} }
else else {
{
this->scrollflag = 2; this->scrollflag = 2;
this->scrolltp = std::chrono::steady_clock::now(); this->scrolltp = std::chrono::steady_clock::now();
} }
} }
else else {
{
this->scrollflag = 0; this->scrollflag = 0;
this->scrollcount = 0; this->scrollcount = 0;
this->scrolltpvalue = 50; this->scrolltpvalue = 50;
} }
} }
void SideMenu::SetOnItemSelected(std::function<void(u64, u32)> fn) void SideMenu::SetOnItemSelected(std::function<void(u64, u32)> fn) {
{
this->onselect = fn; this->onselect = fn;
} }
void SideMenu::SetOnSelectionChanged(std::function<void(u32)> fn) void SideMenu::SetOnSelectionChanged(std::function<void(u32)> fn) {
{
this->onselch = fn; this->onselch = fn;
} }
void SideMenu::ClearItems() void SideMenu::ClearItems() {
{
this->icons.clear(); this->icons.clear();
this->icons_texts.clear(); this->icons_texts.clear();
this->icons_mselected.clear(); this->icons_mselected.clear();
for(auto ricon: this->ricons) for(auto ricon: this->ricons) {
{ if(ricon != nullptr) {
if(ricon != nullptr) pu::ui::render::DeleteTexture(ricon); pu::ui::render::DeleteTexture(ricon);
}
} }
this->ricons.clear(); this->ricons.clear();
for(auto rtext: this->ricons_texts) for(auto rtext: this->ricons_texts) {
{ if(rtext != nullptr) {
if(rtext != nullptr) pu::ui::render::DeleteTexture(rtext); pu::ui::render::DeleteTexture(rtext);
}
} }
this->ricons_texts.clear(); this->ricons_texts.clear();
this->selitm = 0; this->selitm = 0;
@ -281,70 +241,66 @@ namespace ui
this->ClearBorderIcons(); this->ClearBorderIcons();
} }
void SideMenu::AddItem(const std::string &icon, const std::string &txt) void SideMenu::AddItem(const std::string &icon, const std::string &txt) {
{
this->icons.push_back(icon); this->icons.push_back(icon);
this->icons_texts.push_back(txt); this->icons_texts.push_back(txt);
this->icons_mselected.push_back(false); this->icons_mselected.push_back(false);
} }
void SideMenu::SetSuspendedItem(u32 Index) void SideMenu::SetSuspendedItem(u32 Index) {
{
this->suspitm = Index; this->suspitm = Index;
} }
void SideMenu::UnsetSuspendedItem() void SideMenu::UnsetSuspendedItem() {
{
this->suspitm = -1; this->suspitm = -1;
} }
void SideMenu::SetSelectedItem(u32 idx) void SideMenu::SetSelectedItem(u32 idx) {
{ if(idx < this->icons.size()) {
if(idx < this->icons.size()) this->selitm = idx; this->selitm = idx;
}
} }
void SideMenu::HandleMoveLeft() void SideMenu::HandleMoveLeft() {
{ if(selitm > 0) {
if(selitm > 0)
{
bool ilf = IsLeftFirst(); bool ilf = IsLeftFirst();
preselitm = selitm; preselitm = selitm;
selitm--; selitm--;
if(ilf) ReloadIcons(1); if(ilf) {
else movalpha = 255; ReloadIcons(1);
}
else movalpha = 0xFF;
(this->onselch)(this->selitm); (this->onselch)(this->selitm);
} }
} }
void SideMenu::HandleMoveRight() void SideMenu::HandleMoveRight() {
{ if((selitm + 1) < this->icons.size()) {
if((selitm + 1) < this->icons.size())
{
bool irl = IsRightLast(); bool irl = IsRightLast();
preselitm = selitm; preselitm = selitm;
selitm++; selitm++;
if(irl) ReloadIcons(2); if(irl) {
else movalpha = 255; ReloadIcons(2);
}
else movalpha = 0xFF;
(this->onselch)(this->selitm); (this->onselch)(this->selitm);
} }
} }
int SideMenu::GetSuspendedItem() int SideMenu::GetSuspendedItem() {
{
return this->suspitm; return this->suspitm;
} }
u32 SideMenu::GetSelectedItem() u32 SideMenu::GetSelectedItem() {
{
return this->selitm; return this->selitm;
} }
bool SideMenu::IsLeftFirst() bool SideMenu::IsLeftFirst() {
{ auto basex = GetProcessedX();
s32 basex = GetProcessedX(); for(u32 i = 0; i < this->ricons.size(); i++) {
for(u32 i = 0; i < this->ricons.size(); i++) if((basex == this->GetX()) && (this->selitm == (this->baseiconidx + i))) {
{ return true;
if((basex == this->GetX()) && (this->selitm == (this->baseiconidx + i))) return true; }
basex += ItemSize + Margin; basex += ItemSize + Margin;
} }
return false; return false;
@ -352,51 +308,57 @@ namespace ui
bool SideMenu::IsRightLast() bool SideMenu::IsRightLast()
{ {
if(this->selitm == (this->icons.size() - 1)) return true; if(this->selitm == (this->icons.size() - 1)) {
s32 basex = GetProcessedX(); return true;
for(u32 i = 0; i < this->ricons.size(); i++) }
{ auto basex = GetProcessedX();
if((basex == 926) && (this->selitm == (this->baseiconidx + i))) return true; for(u32 i = 0; i < this->ricons.size(); i++) {
if((basex == 926) && (this->selitm == (this->baseiconidx + i))) {
return true;
}
basex += ItemSize + Margin; basex += ItemSize + Margin;
} }
return false; return false;
} }
void SideMenu::ReloadIcons(u32 dir) void SideMenu::ReloadIcons(u32 dir) {
{
switch(dir) switch(dir)
{ {
case 1: // Left // Left
{ case 1: {
auto icon = pu::ui::render::LoadImage(this->icons[this->selitm]); auto icon = pu::ui::render::LoadImage(this->icons[this->selitm]);
this->ricons.insert(this->ricons.begin(), icon); this->ricons.insert(this->ricons.begin(), icon);
auto text = this->icons_texts[this->selitm]; auto text = this->icons_texts[this->selitm];
pu::sdl2::Texture ntext = nullptr; pu::sdl2::Texture ntext = nullptr;
if(!text.empty()) ntext = pu::ui::render::RenderText(this->textfont, text, this->textclr); if(!text.empty()) {
ntext = pu::ui::render::RenderText(this->textfont, text, this->textclr);
}
this->ricons_texts.insert(this->ricons_texts.begin(), ntext); this->ricons_texts.insert(this->ricons_texts.begin(), ntext);
this->baseiconidx--; this->baseiconidx--;
if(this->ricons.size() == 5) if(this->ricons.size() == 5) {
{
pu::ui::render::DeleteTexture(this->ricons.back()); pu::ui::render::DeleteTexture(this->ricons.back());
this->ricons.pop_back(); this->ricons.pop_back();
auto ntext = this->ricons_texts.back(); auto ntext = this->ricons_texts.back();
if(ntext != nullptr) pu::ui::render::DeleteTexture(ntext); if(ntext != nullptr) {
pu::ui::render::DeleteTexture(ntext);
}
this->ricons_texts.pop_back(); this->ricons_texts.pop_back();
} }
break; break;
} }
case 2: // Right // Right
{ case 2: {
auto icon = pu::ui::render::LoadImage(this->icons[this->selitm]); auto icon = pu::ui::render::LoadImage(this->icons[this->selitm]);
this->ricons.push_back(icon); this->ricons.push_back(icon);
auto ntext = pu::ui::render::RenderText(this->textfont, this->icons_texts[this->selitm], this->textclr); auto ntext = pu::ui::render::RenderText(this->textfont, this->icons_texts[this->selitm], this->textclr);
this->ricons_texts.push_back(ntext); this->ricons_texts.push_back(ntext);
if(this->ricons.size() == 5) if(this->ricons.size() == 5) {
{
pu::ui::render::DeleteTexture(this->ricons.front()); pu::ui::render::DeleteTexture(this->ricons.front());
this->ricons.erase(this->ricons.begin()); this->ricons.erase(this->ricons.begin());
auto ntext = this->ricons_texts.front(); auto ntext = this->ricons_texts.front();
if(ntext != nullptr) pu::ui::render::DeleteTexture(ntext); if(ntext != nullptr) {
pu::ui::render::DeleteTexture(ntext);
}
this->ricons_texts.erase(this->ricons_texts.begin()); this->ricons_texts.erase(this->ricons_texts.begin());
this->baseiconidx++; this->baseiconidx++;
} }
@ -406,64 +368,70 @@ namespace ui
this->UpdateBorderIcons(); this->UpdateBorderIcons();
} }
u32 SideMenu::GetBaseItemIndex() u32 SideMenu::GetBaseItemIndex() {
{
return this->baseiconidx; return this->baseiconidx;
} }
void SideMenu::SetBaseItemIndex(u32 index) void SideMenu::SetBaseItemIndex(u32 index) {
{
this->baseiconidx = index; this->baseiconidx = index;
} }
void SideMenu::SetBasePositions(u32 selected_idx, u32 base_idx) void SideMenu::SetBasePositions(u32 selected_idx, u32 base_idx) {
{ if(selected_idx < this->icons.size()) {
if(selected_idx < this->icons.size())
{
this->SetSelectedItem(selected_idx); this->SetSelectedItem(selected_idx);
this->SetBaseItemIndex(base_idx); this->SetBaseItemIndex(base_idx);
} }
} }
void SideMenu::ClearBorderIcons() void SideMenu::ClearBorderIcons() {
{ if(this->leftbicon != nullptr) {
if(this->leftbicon != nullptr) pu::ui::render::DeleteTexture(this->leftbicon); pu::ui::render::DeleteTexture(this->leftbicon);
}
this->leftbicon = nullptr; this->leftbicon = nullptr;
if(this->rightbicon != nullptr) pu::ui::render::DeleteTexture(this->rightbicon); if(this->rightbicon != nullptr) {
pu::ui::render::DeleteTexture(this->rightbicon);
}
this->rightbicon = nullptr; this->rightbicon = nullptr;
} }
void SideMenu::UpdateBorderIcons() void SideMenu::UpdateBorderIcons()
{ {
this->ClearBorderIcons(); this->ClearBorderIcons();
if(this->baseiconidx > 0) this->leftbicon = pu::ui::render::LoadImage(this->icons[this->baseiconidx - 1]); if(this->baseiconidx > 0) {
if((this->baseiconidx + 4) < this->icons.size()) this->rightbicon = pu::ui::render::LoadImage(this->icons[this->baseiconidx + 4]); this->leftbicon = pu::ui::render::LoadImage(this->icons[this->baseiconidx - 1]);
}
if((this->baseiconidx + 4) < this->icons.size()) {
this->rightbicon = pu::ui::render::LoadImage(this->icons[this->baseiconidx + 4]);
}
} }
void SideMenu::ResetMultiselections() void SideMenu::ResetMultiselections()
{ {
this->icons_mselected.clear(); this->icons_mselected.clear();
for(u32 i = 0; i < this->icons.size(); i++) this->icons_mselected.push_back(false); for(u32 i = 0; i < this->icons.size(); i++) {
this->icons_mselected.push_back(false);
}
} }
void SideMenu::SetItemMultiselected(u32 index, bool selected) void SideMenu::SetItemMultiselected(u32 index, bool selected) {
{ if(index < this->icons_mselected.size()) {
if(index < this->icons_mselected.size()) this->icons_mselected[index] = selected; this->icons_mselected[index] = selected;
}
} }
bool SideMenu::IsItemMultiselected(u32 index) bool SideMenu::IsItemMultiselected(u32 index)
{ {
if(index < this->icons_mselected.size()) return this->icons_mselected[index]; if(index < this->icons_mselected.size()) {
return this->icons_mselected[index];
}
return false; return false;
} }
bool SideMenu::IsAnyMultiselected() bool SideMenu::IsAnyMultiselected()
{ {
bool any = false; bool any = false;
for(auto msel: this->icons_mselected) for(auto msel: this->icons_mselected) {
{ if(msel) {
if(msel)
{
any = true; any = true;
break; break;
} }
@ -471,8 +439,8 @@ namespace ui
return any; return any;
} }
void SideMenu::SetEnabled(bool enabled) void SideMenu::SetEnabled(bool enabled) {
{
this->enabled = enabled; this->enabled = enabled;
} }
} }

View file

@ -8,16 +8,15 @@ extern ui::MenuApplication::Ref g_menu_app_instance;
extern cfg::Theme g_ul_theme; extern cfg::Theme g_ul_theme;
extern cfg::Config g_ul_config; extern cfg::Config g_ul_config;
namespace ui namespace ui {
{
StartupLayout::StartupLayout() StartupLayout::StartupLayout() {
{
this->SetBackgroundImage(cfg::GetAssetByTheme(g_ul_theme, "ui/Background.png")); this->SetBackgroundImage(cfg::GetAssetByTheme(g_ul_theme, "ui/Background.png"));
this->loadmenu = false; this->loadmenu = false;
pu::ui::Color textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff")); auto textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
pu::ui::Color menufocusclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("menu_focus_color", "#5ebcffff")); auto menufocusclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("menu_focus_color", "#5ebcffff"));
pu::ui::Color menubgclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("menu_bg_color", "#0094ffff")); auto menubgclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("menu_bg_color", "#0094ffff"));
this->infoText = pu::ui::elm::TextBlock::New(35, 635, cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "startup_welcome_info") + "\n" + cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "startup_control_info")); this->infoText = pu::ui::elm::TextBlock::New(35, 635, cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "startup_welcome_info") + "\n" + cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "startup_control_info"));
this->infoText->SetColor(textclr); this->infoText->SetColor(textclr);
@ -30,10 +29,8 @@ namespace ui
this->Add(this->usersMenu); this->Add(this->usersMenu);
} }
void StartupLayout::OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch pos) void StartupLayout::OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) {
{ if(this->loadmenu) {
if(this->loadmenu)
{
this->loadmenu = false; this->loadmenu = false;
g_menu_app_instance->StartPlayBGM(); g_menu_app_instance->StartPlayBGM();
g_menu_app_instance->FadeOut(); g_menu_app_instance->FadeOut();
@ -43,50 +40,44 @@ namespace ui
} }
} }
void StartupLayout::OnHomeButtonPress() void StartupLayout::OnHomeButtonPress() {
{
// ... // ...
} }
void StartupLayout::user_Click(AccountUid uid) void StartupLayout::user_Click(AccountUid uid) {
{
this->loadmenu = true; this->loadmenu = true;
g_menu_app_instance->SetSelectedUser(uid); g_menu_app_instance->SetSelectedUser(uid);
} }
void StartupLayout::create_Click() void StartupLayout::create_Click() {
{
auto rc = pselShowUserCreator(); auto rc = pselShowUserCreator();
if(R_SUCCEEDED(rc)) if(R_SUCCEEDED(rc)) {
{
g_menu_app_instance->FadeOut(); g_menu_app_instance->FadeOut();
this->ReloadMenu(); this->ReloadMenu();
g_menu_app_instance->FadeIn(); g_menu_app_instance->FadeIn();
} }
} }
void StartupLayout::ReloadMenu() void StartupLayout::ReloadMenu() {
{
this->usersMenu->ClearItems(); this->usersMenu->ClearItems();
this->usersMenu->SetSelectedIndex(0); this->usersMenu->SetSelectedIndex(0);
pu::ui::Color textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff")); auto textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
std::vector<AccountUid> users;
auto [rc, users] = os::QuerySystemAccounts(true); auto rc = os::QuerySystemAccounts(users, true);
if(R_SUCCEEDED(rc)) if(R_SUCCEEDED(rc)) {
{ for(auto &user: users) {
for(auto user: users) std::string name;
{ auto rc = os::GetAccountName(name, user);
auto [rc, name] = os::GetAccountName(user); if(R_SUCCEEDED(rc)) {
if(R_FAILED(rc)) continue; auto path = os::GetIconCacheImagePath(user);
auto uitm = pu::ui::elm::MenuItem::New(name);
uitm->SetIcon(path);
uitm->AddOnClick(std::bind(&StartupLayout::user_Click, this, user));
uitm->SetColor(textclr);
auto path = os::GetIconCacheImagePath(user); this->usersMenu->AddItem(uitm);
auto uitm = pu::ui::elm::MenuItem::New(name); }
uitm->SetIcon(path);
uitm->AddOnClick(std::bind(&StartupLayout::user_Click, this, user));
uitm->SetColor(textclr);
this->usersMenu->AddItem(uitm);
} }
} }
@ -95,4 +86,5 @@ namespace ui
citm->AddOnClick(std::bind(&StartupLayout::create_Click, this)); citm->AddOnClick(std::bind(&StartupLayout::create_Click, this));
this->usersMenu->AddItem(citm); this->usersMenu->AddItem(citm);
} }
} }

View file

@ -8,15 +8,14 @@ extern ui::MenuApplication::Ref g_menu_app_instance;
extern cfg::Theme g_ul_theme; extern cfg::Theme g_ul_theme;
extern cfg::Config g_ul_config; extern cfg::Config g_ul_config;
namespace ui namespace ui {
{
ThemeMenuLayout::ThemeMenuLayout() ThemeMenuLayout::ThemeMenuLayout() {
{
this->SetBackgroundImage(cfg::GetAssetByTheme(g_ul_theme, "ui/Background.png")); this->SetBackgroundImage(cfg::GetAssetByTheme(g_ul_theme, "ui/Background.png"));
pu::ui::Color textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff")); auto textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
pu::ui::Color menufocusclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("menu_focus_color", "#5ebcffff")); auto menufocusclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("menu_focus_color", "#5ebcffff"));
pu::ui::Color menubgclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("menu_bg_color", "#0094ffff")); auto menubgclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("menu_bg_color", "#0094ffff"));
this->curThemeBanner = pu::ui::elm::Image::New(0, 585, cfg::GetAssetByTheme(g_ul_theme, "ui/BannerTheme.png")); this->curThemeBanner = pu::ui::elm::Image::New(0, 585, cfg::GetAssetByTheme(g_ul_theme, "ui/BannerTheme.png"));
g_menu_app_instance->ApplyConfigForElement("themes_menu", "banner_image", this->curThemeBanner); g_menu_app_instance->ApplyConfigForElement("themes_menu", "banner_image", this->curThemeBanner);
@ -55,28 +54,23 @@ namespace ui
this->Add(this->curThemeIcon); this->Add(this->curThemeIcon);
} }
void ThemeMenuLayout::OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch pos) void ThemeMenuLayout::OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) {
{ if(down & KEY_B) {
if(down & KEY_B)
{
g_menu_app_instance->FadeOut(); g_menu_app_instance->FadeOut();
g_menu_app_instance->LoadMenu(); g_menu_app_instance->LoadMenu();
g_menu_app_instance->FadeIn(); g_menu_app_instance->FadeIn();
} }
} }
void ThemeMenuLayout::OnHomeButtonPress() void ThemeMenuLayout::OnHomeButtonPress() {
{
g_menu_app_instance->FadeOut(); g_menu_app_instance->FadeOut();
g_menu_app_instance->LoadMenu(); g_menu_app_instance->LoadMenu();
g_menu_app_instance->FadeIn(); g_menu_app_instance->FadeIn();
} }
void ThemeMenuLayout::Reload() void ThemeMenuLayout::Reload() {
{ auto textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
pu::ui::Color textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff")); if(cfg::ThemeIsDefault(g_ul_theme)) {
if(cfg::ThemeIsDefault(g_ul_theme))
{
this->curThemeText->SetText(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "theme_no_custom")); this->curThemeText->SetText(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "theme_no_custom"));
this->curThemeName->SetVisible(false); this->curThemeName->SetVisible(false);
this->curThemeAuthor->SetVisible(false); this->curThemeAuthor->SetVisible(false);
@ -84,8 +78,7 @@ namespace ui
this->curThemeBanner->SetVisible(false); this->curThemeBanner->SetVisible(false);
this->curThemeIcon->SetVisible(false); this->curThemeIcon->SetVisible(false);
} }
else else {
{
this->curThemeText->SetText(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "theme_current") + ":"); this->curThemeText->SetText(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "theme_current") + ":");
this->curThemeName->SetVisible(true); this->curThemeName->SetVisible(true);
this->curThemeName->SetText(g_ul_theme.manifest.name); this->curThemeName->SetText(g_ul_theme.manifest.name);
@ -111,8 +104,7 @@ namespace ui
ditm->SetIcon("romfs:/Logo.png"); ditm->SetIcon("romfs:/Logo.png");
this->themesMenu->AddItem(ditm); this->themesMenu->AddItem(ditm);
for(auto &ltheme: this->loadedThemes) for(auto &ltheme: this->loadedThemes) {
{
auto itm = pu::ui::elm::MenuItem::New(ltheme.manifest.name + " (v" + ltheme.manifest.release + ", " + cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "theme_by") + " " + ltheme.manifest.author + ")"); auto itm = pu::ui::elm::MenuItem::New(ltheme.manifest.name + " (v" + ltheme.manifest.release + ", " + cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "theme_by") + " " + ltheme.manifest.author + ")");
itm->AddOnClick(std::bind(&ThemeMenuLayout::theme_Click, this)); itm->AddOnClick(std::bind(&ThemeMenuLayout::theme_Click, this));
itm->SetColor(textclr); itm->SetColor(textclr);
@ -122,51 +114,47 @@ namespace ui
} }
} }
void ThemeMenuLayout::theme_Click() void ThemeMenuLayout::theme_Click() {
{
auto idx = this->themesMenu->GetSelectedIndex(); auto idx = this->themesMenu->GetSelectedIndex();
if(idx == 0) if(idx == 0) {
{ if(cfg::ThemeIsDefault(g_ul_theme)) {
if(cfg::ThemeIsDefault(g_ul_theme)) g_menu_app_instance->ShowNotification(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "theme_no_custom")); g_menu_app_instance->ShowNotification(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "theme_no_custom"));
else }
{ else {
auto sopt = g_menu_app_instance->CreateShowDialog(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "theme_reset"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "theme_reset_conf"), { cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "yes"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "cancel") }, true); auto sopt = g_menu_app_instance->CreateShowDialog(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "theme_reset"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "theme_reset_conf"), { cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "yes"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "cancel") }, true);
if(sopt == 0) if(sopt == 0) {
{
g_ul_config.theme_name = ""; g_ul_config.theme_name = "";
cfg::SaveConfig(g_ul_config); cfg::SaveConfig(g_ul_config);
am::MenuCommandWriter writer(am::DaemonMessage::RestartMenu);
writer.FinishWrite();
g_menu_app_instance->StopPlayBGM(); g_menu_app_instance->StopPlayBGM();
g_menu_app_instance->CloseWithFadeOut(); g_menu_app_instance->CloseWithFadeOut();
// g_menu_app_instance->ShowNotification(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "theme_changed")); // g_menu_app_instance->ShowNotification(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "theme_changed"));
dmi::MenuMessageWriter writer(dmi::DaemonMessage::RestartMenu);
} }
} }
} }
else else {
{
idx--; idx--;
auto seltheme = this->loadedThemes[idx]; auto seltheme = this->loadedThemes[idx];
auto iconpath = seltheme.path + "/theme/Icon.png"; auto iconpath = seltheme.path + "/theme/Icon.png";
if(seltheme.base_name == g_ul_theme.base_name) g_menu_app_instance->ShowNotification(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "theme_active_this")); if(seltheme.base_name == g_ul_theme.base_name) {
else g_menu_app_instance->ShowNotification(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "theme_active_this"));
{ }
else {
auto sopt = g_menu_app_instance->CreateShowDialog(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "theme_set"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "theme_set_conf"), { cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "yes"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "cancel") }, true, iconpath); auto sopt = g_menu_app_instance->CreateShowDialog(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "theme_set"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "theme_set_conf"), { cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "yes"), cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "cancel") }, true, iconpath);
if(sopt == 0) if(sopt == 0) {
{
g_ul_config.theme_name = seltheme.base_name; g_ul_config.theme_name = seltheme.base_name;
cfg::SaveConfig(g_ul_config); cfg::SaveConfig(g_ul_config);
am::MenuCommandWriter writer(am::DaemonMessage::RestartMenu);
writer.FinishWrite();
g_menu_app_instance->StopPlayBGM(); g_menu_app_instance->StopPlayBGM();
g_menu_app_instance->CloseWithFadeOut(); g_menu_app_instance->CloseWithFadeOut();
// g_menu_app_instance->ShowNotification(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "theme_changed")); // g_menu_app_instance->ShowNotification(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "theme_changed"));
dmi::MenuMessageWriter writer(dmi::DaemonMessage::RestartMenu);
} }
} }
} }
} }
} }

View file

@ -1,9 +1,9 @@
{ {
"name": "uMenu", "name": "uMenu",
"title_id": "0x010000000000100B", "title_id": "0x010000000000FFFF",
"title_id_range_min": "0x010000000000100B", "title_id_range_min": "0x010000000000FFFF",
"title_id_range_max": "0x010000000000100B", "title_id_range_max": "0x010000000000FFFF",
"main_thread_stack_size": "0x100000", "main_thread_stack_size": "0x4000",
"main_thread_priority": 44, "main_thread_priority": 44,
"default_cpu_id": 0, "default_cpu_id": 0,
"process_category": 0, "process_category": 0,

View file

@ -16,11 +16,11 @@ namespace uViewer
public byte[][] CaptureBackups = new byte[][] public byte[][] CaptureBackups = new byte[][]
{ {
new byte[ViewerMainForm.RawRGBAScreenBufferSize], // Backups (5) so that new captures made by the main form don't replace old ones new byte[ViewerMainForm.USBPacketSize], // Backups (5) so that new captures made by the main form don't replace old ones
new byte[ViewerMainForm.RawRGBAScreenBufferSize], new byte[ViewerMainForm.USBPacketSize],
new byte[ViewerMainForm.RawRGBAScreenBufferSize], new byte[ViewerMainForm.USBPacketSize],
new byte[ViewerMainForm.RawRGBAScreenBufferSize], new byte[ViewerMainForm.USBPacketSize],
new byte[ViewerMainForm.RawRGBAScreenBufferSize], new byte[ViewerMainForm.USBPacketSize],
}; };
public ScreenshotForm(ViewerMainForm main) public ScreenshotForm(ViewerMainForm main)