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"]
path = Plutonium
url = https://github.com/XorTroll/Plutonium
[submodule "libstratosphere"]
path = libstratosphere
url = https://github.com/Atmosphere-NX/libstratosphere
[submodule "Atmosphere-libs"]
path = Atmosphere-libs
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_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/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/dmi ../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_CXXFLAGS := -fno-rtti -fexceptions -std=gnu++17
@ -36,7 +34,7 @@ make_hbtarget:
hbtarget: base make_hbtarget
make_daemon:
@$(MAKE) -C libstratosphere/
@$(MAKE) -C Atmosphere-libs/
@$(MAKE) -C uDaemon/
@mkdir -p SdOut/atmosphere/contents/0100000000001000
@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.
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
- 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)

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

View file

@ -1,7 +1,7 @@
#---------------------------------------------------------------------------------
# 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

View file

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

View file

@ -2,11 +2,11 @@
#pragma once
#include <stratosphere.hpp>
namespace ecs
{
namespace ecs {
// Slightly modified version of ams's RemoteFileSystem
class RemoteSdCardFileSystem : public ams::fs::fsa::IFileSystem {
private:
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) {
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 <ul_Include.hpp>
namespace ipc
{
class IPrivateService : public ams::sf::IServiceObject
{
private:
namespace ipc {
enum class CommandId
{
class IPrivateService : public ams::sf::IServiceObject {
private:
enum class CommandId {
GetLatestMessage = 0
};
public:
ams::Result GetLatestMessage(ams::sf::Out<u32> msg, const ams::sf::ClientProcessId &client_pid);
ams::Result GetLatestMessage(const ams::sf::ClientProcessId &client_pid, ams::sf::Out<u32> out_msg);
public:
DEFINE_SERVICE_DISPATCH_TABLE
{
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(GetLatestMessage)
};
};
}

View file

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

View file

@ -10,33 +10,35 @@
#include <am/am_Application.hpp>
#include <am/am_LibraryApplet.hpp>
#include <am/am_HomeMenu.hpp>
#include <am/am_DaemonMenuInteraction.hpp>
#include <dmi/dmi_DaemonMenuInteraction.hpp>
#include <util/util_Convert.hpp>
#include <cfg/cfg_Config.hpp>
extern "C"
{
extern "C" {
u32 __nx_applet_type = AppletType_SystemApplet;
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
#else
size_t __nx_heap_size = 0x800000; // uDaemon uses 8MB - while official qlaunch uses 56MB! That's 48 extra MB for other applets
#endif
// uDaemon uses 8MB - while official qlaunch uses 56MB! That's 48 extra MB for other applets!
size_t __nx_heap_size = 0x800000;
}
// Needed by libstratosphere
namespace ams
{
namespace ams {
ncm::ProgramId CurrentProgramId = ncm::SystemAppletId::Qlaunch;
namespace result
{
namespace result {
bool CallFatalOnResultAssertion = true;
}
}
enum class USBMode : u32
{
enum class USBMode : u32 {
Invalid,
RawRGBA,
JPEG
@ -61,296 +63,234 @@ USBMode usb_mode = USBMode::Invalid;
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
bool debug_menu = false;
ams::os::Mutex g_last_menu_msg_lock(false);
dmi::MenuMessage g_last_menu_msg = dmi::MenuMessage::Invalid;
ams::os::Mutex g_last_menu_msg_lock;
am::MenuMessage g_last_menu_msg = am::MenuMessage::Invalid;
dmi::DaemonStatus CreateStatus() {
dmi::DaemonStatus status = {};
status.selected_user = selected_uid;
am::DaemonStatus CreateStatus()
{
am::DaemonStatus status = {};
memcpy(&status.selected_user, &selected_uid, sizeof(selected_uid));
cfg::TitleType tmptype = cfg::TitleType::Invalid;
if(am::ApplicationIsActive())
{
tmptype = cfg::TitleType::Installed;
if(app_opened_hb) tmptype = cfg::TitleType::Homebrew;
if(am::ApplicationIsActive()) {
if(app_opened_hb) {
// Homebrew
status.params = hbapplaunch_flag_temp;
}
else {
// Regular title
status.app_id = am::ApplicationGetId();
}
}
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;
}
void HandleSleep()
{
void HandleSleep() {
appletStartSleepSequence(true);
}
Result LaunchMenu(am::MenuStartMode stmode, am::DaemonStatus status)
{
if(debug_menu) return 0;
return ecs::RegisterLaunchAsApplet(config.menu_program_id, (u32)stmode, "/ulaunch/bin/uMenu/", &status, sizeof(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)));
return ResultSuccess;
}
void HandleHomeButton()
{
void HandleHomeButton() {
bool used_to_reopen_menu = false;
if(am::LibraryAppletIsActive() && !am::LibraryAppletIsMenu())
{
if(am::LibraryAppletIsActive() && !am::LibraryAppletIsMenu()) {
am::LibraryAppletTerminate();
auto status = CreateStatus();
UL_ASSERT(LaunchMenu(am::MenuStartMode::Menu, status));
UL_ASSERT(LaunchMenu(dmi::MenuStartMode::Menu, status));
return;
}
if(am::ApplicationIsActive())
{
if(am::ApplicationHasForeground())
{
if(am::ApplicationIsActive()) {
if(am::ApplicationHasForeground()) {
am::HomeMenuSetForeground();
auto status = CreateStatus();
UL_ASSERT(LaunchMenu(am::MenuStartMode::MenuApplicationSuspended, status));
UL_ASSERT(LaunchMenu(dmi::MenuStartMode::MenuApplicationSuspended, status));
used_to_reopen_menu = true;
}
}
if(am::LibraryAppletIsMenu() && !used_to_reopen_menu)
{
std::scoped_lock _lock(g_last_menu_msg_lock);
g_last_menu_msg = am::MenuMessage::HomeRequest;
if(am::LibraryAppletIsMenu() && !used_to_reopen_menu) {
std::scoped_lock lk(g_last_menu_msg_lock);
g_last_menu_msg = dmi::MenuMessage::HomeRequest;
}
}
void HandleGeneralChannel()
{
Result HandleGeneralChannel() {
AppletStorage sams_st;
auto rc = appletPopFromGeneralChannel(&sams_st);
if(R_SUCCEEDED(rc))
{
os::SystemAppletMessage sams = {};
rc = appletStorageRead(&sams_st, 0, &sams, sizeof(sams));
R_TRY(appletPopFromGeneralChannel(&sams_st));
UL_ON_SCOPE_EXIT({
appletStorageClose(&sams_st);
if(R_SUCCEEDED(rc))
{
if(sams.magic == os::SAMSMagic)
{
os::GeneralChannelMessage msg = (os::GeneralChannelMessage)sams.message;
switch(msg)
{
case os::GeneralChannelMessage::HomeButton: // Usually this doesn't happen, HOME is detected by applet messages...?
{
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:
{
});
os::SystemAppletMessage sams = {};
R_TRY(appletStorageRead(&sams_st, 0, &sams, sizeof(sams)));
if(sams.magic == os::SystemAppletMessage::Magic) {
auto msg = static_cast<os::GeneralChannelMessage>(sams.general_channel_message);
switch(msg) {
// Usually this doesn't happen, HOME is detected by applet messages...?
case os::GeneralChannelMessage::HomeButton: {
HandleHomeButton();
break;
}
case os::AppletMessage::SdCardOut:
{
// SD card out? Cya!
case os::GeneralChannelMessage::Shutdown: {
appletStartShutdownSequence();
break;
}
case os::AppletMessage::PowerButton:
{
case os::GeneralChannelMessage::Reboot: {
appletStartRebootSequence();
break;
}
case os::GeneralChannelMessage::Sleep: {
HandleSleep();
break;
}
case os::AppletMessage::ChangeOperationMode:
{
UpdateOperationMode();
break;
}
// We don't have anything special to do for the rest
default:
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()
{
if(am::LibraryAppletIsMenu())
{
am::DaemonCommandReader reader;
if(reader)
{
switch(reader.GetMessage())
{
case am::DaemonMessage::SetSelectedUser:
{
if(am::LibraryAppletIsMenu()) {
dmi::DaemonMessageReader reader;
if(reader) {
switch(reader.GetValue()) {
case dmi::DaemonMessage::SetSelectedUser: {
selected_uid = reader.Read<AccountUid>();
reader.FinishRead();
break;
}
case am::DaemonMessage::LaunchApplication:
{
case dmi::DaemonMessage::LaunchApplication: {
auto app_id = reader.Read<u64>();
reader.FinishRead();
if(am::ApplicationIsActive())
{
am::DaemonCommandResultWriter res(RES_VALUE(Daemon, ApplicationActive));
res.FinishWrite();
if(am::ApplicationIsActive()) {
dmi::DaemonResultWriter rc(RES_VALUE(Daemon, ApplicationActive));
}
else if(!accountUidIsValid(&selected_uid))
{
am::DaemonCommandResultWriter res(RES_VALUE(Daemon, InvalidSelectedUser));
res.FinishWrite();
else if(!accountUidIsValid(&selected_uid)) {
dmi::DaemonResultWriter rc(RES_VALUE(Daemon, InvalidSelectedUser));
}
else if(titlelaunch_flag > 0)
{
am::DaemonCommandResultWriter res(RES_VALUE(Daemon, AlreadyQueued));
res.FinishWrite();
else if(titlelaunch_flag > 0) {
dmi::DaemonResultWriter rc(RES_VALUE(Daemon, AlreadyQueued));
}
else
{
else {
titlelaunch_flag = app_id;
am::DaemonCommandResultWriter res(0);
res.FinishWrite();
dmi::DaemonResultWriter rc(ResultSuccess);
}
break;
}
case am::DaemonMessage::ResumeApplication:
{
case dmi::DaemonMessage::ResumeApplication: {
reader.FinishRead();
if(!am::ApplicationIsActive())
{
am::DaemonCommandResultWriter res(RES_VALUE(Daemon, ApplicationNotActive));
res.FinishWrite();
if(!am::ApplicationIsActive()) {
dmi::DaemonResultWriter rc(RES_VALUE(Daemon, ApplicationNotActive));
}
else
{
else {
am::ApplicationSetForeground();
am::DaemonCommandResultWriter res(0);
res.FinishWrite();
dmi::DaemonResultWriter rc(ResultSuccess);
}
break;
}
case am::DaemonMessage::TerminateApplication:
{
case dmi::DaemonMessage::TerminateApplication: {
reader.FinishRead();
am::ApplicationTerminate();
app_opened_hb = false;
break;
}
case am::DaemonMessage::LaunchHomebrewLibraryApplet:
{
case dmi::DaemonMessage::LaunchHomebrewLibraryApplet: {
hblaunch_flag = reader.Read<hb::HbTargetParams>();
reader.FinishRead();
break;
}
case am::DaemonMessage::LaunchHomebrewApplication:
{
case dmi::DaemonMessage::LaunchHomebrewApplication: {
auto app_id = reader.Read<u64>();
auto ipt = reader.Read<hb::HbTargetParams>();
reader.FinishRead();
if(am::ApplicationIsActive())
{
am::DaemonCommandResultWriter res(RES_VALUE(Daemon, ApplicationActive));
res.FinishWrite();
if(am::ApplicationIsActive()) {
dmi::DaemonResultWriter rc(RES_VALUE(Daemon, ApplicationActive));
}
else if(!accountUidIsValid(&selected_uid))
{
am::DaemonCommandResultWriter res(RES_VALUE(Daemon, InvalidSelectedUser));
res.FinishWrite();
else if(!accountUidIsValid(&selected_uid)) {
dmi::DaemonResultWriter rc(RES_VALUE(Daemon, InvalidSelectedUser));
}
else if(titlelaunch_flag > 0)
{
am::DaemonCommandResultWriter res(RES_VALUE(Daemon, AlreadyQueued));
res.FinishWrite();
else if(titlelaunch_flag > 0) {
dmi::DaemonResultWriter rc(RES_VALUE(Daemon, AlreadyQueued));
}
else
{
memcpy(&hbapplaunch_flag, &ipt, sizeof(ipt));
memcpy(&hbapplaunch_flag_temp, &ipt, sizeof(ipt));
else {
hbapplaunch_flag = ipt;
hbapplaunch_flag_temp = ipt;
titlelaunch_flag = app_id;
am::DaemonCommandResultWriter res(0);
res.FinishWrite();
dmi::DaemonResultWriter rc(ResultSuccess);
}
break;
}
case am::DaemonMessage::OpenWebPage:
{
case dmi::DaemonMessage::OpenWebPage: {
webapplet_flag = reader.Read<WebCommonConfig>();
reader.FinishRead();
break;
}
case am::DaemonMessage::GetSelectedUser:
{
case dmi::DaemonMessage::GetSelectedUser: {
reader.FinishRead();
am::DaemonCommandResultWriter res(0);
res.Write<AccountUid>(selected_uid);
res.FinishWrite();
dmi::DaemonResultWriter rc(ResultSuccess);
rc.Write<AccountUid>(selected_uid);
break;
}
case am::DaemonMessage::OpenAlbum:
{
case dmi::DaemonMessage::OpenAlbum:{
reader.FinishRead();
album_flag = true;
break;
}
case am::DaemonMessage::RestartMenu:
{
case dmi::DaemonMessage::RestartMenu: {
reader.FinishRead();
menu_restart_flag = true;
menu_restart_flag = true;
break;
}
default:
@ -360,10 +300,9 @@ void HandleMenuMessage()
}
}
namespace
{
struct ServerOptions
{
namespace {
struct ServerOptions {
static const size_t PointerBufferSize = 0x400;
static const size_t MaxDomains = 0x40;
static const size_t MaxDomainObjects = 0x40;
@ -380,10 +319,10 @@ namespace
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 {
u32 a;
u64 b;
@ -394,19 +333,16 @@ static inline Result capsscCommand1204(void *buf, size_t buf_size, u64 *out_jpeg
);
}
namespace impl
{
void IPCManagerThread(void *arg)
{
namespace impl {
void IPCManagerThread(void *arg) {
UL_ASSERT(daemon_ipc_manager.RegisterServer<ipc::IPrivateService>(PrivateServiceName, MaxPrivateSessions).GetValue());
UL_ASSERT(daemon_ipc_manager.RegisterServer<ipc::IPublicService>(PublicServiceName, MaxPublicSessions).GetValue());
daemon_ipc_manager.LoopProcess();
}
void USBViewerRGBAThread(void *arg)
{
while(true)
{
void USBViewerRGBAThread(void *arg) {
while(true) {
bool tmp_flag;
appletGetLastForegroundCaptureImageEx(usb_buf_read, RawRGBAScreenBufferSize, &tmp_flag);
appletUpdateLastForegroundCaptureImage();
@ -415,10 +351,8 @@ namespace impl
}
}
void USBViewerJPEGThread(void *arg)
{
while(true)
{
void USBViewerJPEGThread(void *arg) {
while(true) {
u64 tmp_size;
capsscCommand1204(usb_buf_read, RawRGBAScreenBufferSize, &tmp_size);
usbCommsWrite(usb_buf, USBPacketSize);
@ -426,53 +360,46 @@ namespace impl
}
}
void PrepareUSBViewer()
{
usb_buf = new (std::align_val_t(0x1000)) u8[USBPacketSize]();
void PrepareUSBViewer() {
usb_buf = new (std::align_val_t(0x1000), std::nothrow) u8[USBPacketSize]();
usb_buf_read = usb_buf + sizeof(u32);
u64 tmp_size;
auto rc = capsscCommand1204(usb_buf_read, RawRGBAScreenBufferSize, &tmp_size);
if(R_SUCCEEDED(rc)) usb_mode = USBMode::JPEG;
else
{
if(R_SUCCEEDED(capsscCommand1204(usb_buf_read, RawRGBAScreenBufferSize, &tmp_size))) {
usb_mode = USBMode::JPEG;
}
else {
usb_mode = USBMode::RawRGBA;
capsscExit();
}
*(u32*)usb_buf = static_cast<u32>(usb_mode);
*reinterpret_cast<u32*>(usb_buf) = static_cast<u32>(usb_mode);
}
void LoopUpdate()
{
void LoopUpdate() {
HandleGeneralChannel();
HandleAppletMessage();
HandleMenuMessage();
bool sth_done = false;
if(webapplet_flag.version > 0) // A valid version in this config is always >= 0x20000
{
if(!am::LibraryAppletIsActive())
{
// A valid version in this config is always >= 0x20000
if(webapplet_flag.version > 0) {
if(!am::LibraryAppletIsActive()) {
UL_ASSERT(am::WebAppletStart(&webapplet_flag));
sth_done = true;
memset(&webapplet_flag, 0, sizeof(webapplet_flag));
webapplet_flag = {};
}
}
if(menu_restart_flag)
{
if(!am::LibraryAppletIsActive())
{
if(menu_restart_flag) {
if(!am::LibraryAppletIsActive()) {
auto status = CreateStatus();
UL_ASSERT(LaunchMenu(am::MenuStartMode::StartupScreen, status))
UL_ASSERT(LaunchMenu(dmi::MenuStartMode::StartupScreen, status));
sth_done = true;
menu_restart_flag = false;
}
}
if(album_flag)
{
if(!am::LibraryAppletIsActive())
{
if(album_flag) {
if(!am::LibraryAppletIsActive()) {
u8 albumflag = 2;
UL_ASSERT(am::LibraryAppletStart(AppletId_photoViewer, 0x10000, &albumflag, sizeof(albumflag)));
@ -480,12 +407,9 @@ namespace impl
album_flag = false;
}
}
if(titlelaunch_flag > 0)
{
if(!am::LibraryAppletIsActive())
{
if(strlen(hbapplaunch_flag.nro_path))
{
if(titlelaunch_flag > 0) {
if(!am::LibraryAppletIsActive()) {
if(strlen(hbapplaunch_flag.nro_path)) {
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));
sth_done = true;
@ -493,81 +417,77 @@ namespace impl
titlelaunch_flag = 0;
hbapplaunch_flag.nro_path[0] = '\0';
}
else
{
else {
UL_ASSERT(am::ApplicationStart(titlelaunch_flag, false, selected_uid));
sth_done = true;
titlelaunch_flag = 0;
}
}
}
if(strlen(hblaunch_flag.nro_path))
{
if(!am::LibraryAppletIsActive())
{
if(strlen(hblaunch_flag.nro_path)) {
if(!am::LibraryAppletIsActive()) {
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)));
sth_done = true;
hblaunch_flag.nro_path[0] = '\0';
}
}
if(!am::LibraryAppletIsActive())
{
if(!am::LibraryAppletIsActive()) {
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();
UL_ASSERT(LaunchMenu(am::MenuStartMode::Menu, status));
UL_ASSERT(LaunchMenu(dmi::MenuStartMode::Menu, status));
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...
// 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();
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(threadStart(&ipc_thr));
return 0;
return ResultSuccess;
}
Result LaunchUSBViewerThread()
{
switch(usb_mode)
{
case USBMode::RawRGBA:
Result LaunchUSBViewerThread() {
switch(usb_mode) {
case USBMode::RawRGBA: {
R_TRY(threadCreate(&usb_thr, &USBViewerRGBAThread, nullptr, nullptr, 0x4000, 0x2b, -2));
break;
case USBMode::JPEG:
}
case USBMode::JPEG: {
R_TRY(threadCreate(&usb_thr, &USBViewerJPEGThread, nullptr, nullptr, 0x4000, 0x2b, -2));
break;
}
default:
return 0;
return ResultSuccess;
}
R_TRY(threadStart(&usb_thr));
return 0;
return ResultSuccess;
}
void Initialize()
{
ams::hos::SetVersionForLibnx();
UL_ASSERT(nsInitialize())
UL_ASSERT(pminfoInitialize())
UL_ASSERT(appletLoadAndApplyIdlePolicySettings())
void Initialize() {
UL_ASSERT(setsysInitialize());
SetSysFirmwareVersion fwver = {};
UL_ASSERT(setsysGetFirmwareVersion(&fwver));
hosversionSet(MAKEHOSVERSION(fwver.major, fwver.minor, fwver.micro));
setsysExit();
UL_ASSERT(nsInitialize());
UL_ASSERT(pminfoInitialize());
UL_ASSERT(appletLoadAndApplyIdlePolicySettings());
UpdateOperationMode();
UL_ASSERT(ecs::Initialize())
UL_ASSERT(ecs::Initialize());
UL_ASSERT(db::Mount())
UL_ASSERT(db::Mount());
// Remove old password files
fs::DeleteDirectory(UL_BASE_DB_DIR "/user");
@ -587,76 +507,23 @@ namespace impl
config = cfg::EnsureConfig();
am::LibraryAppletSetMenuAppletId(am::LibraryAppletGetAppletIdForProgramId(config.menu_program_id));
if(config.viewer_usb_enabled)
{
if(config.viewer_usb_enabled) {
UL_ASSERT(usbCommsInitialize());
UL_ASSERT(capsscInitialize());
PrepareUSBViewer();
UL_ASSERT(LaunchUSBViewerThread());
}
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
UL_ASSERT(LaunchIPCManagerThread());
}
void Exit()
{
if(config.viewer_usb_enabled)
{
void Exit() {
if(config.viewer_usb_enabled) {
usbCommsExit();
if(usb_mode == USBMode::JPEG) capsscExit();
operator delete[](usb_buf, std::align_val_t(0x1000));
if(usb_mode == USBMode::JPEG) {
capsscExit();
}
operator delete[](usb_buf, std::align_val_t(0x1000), std::nothrow);
}
nsExit();
@ -664,26 +531,25 @@ namespace impl
ecs::Exit();
db::Unmount();
}
}
// Daemon handles basic qlaunch functionality and serves as a back-end for uLaunch, communicating with Menu front-end when neccessary.
int main()
{
// uDaemon handles basic qlaunch functionality and serves as a back-end for uLaunch, communicating with uMenu front-end when neccessary.
int main() {
impl::Initialize();
// Cache everything on startup
cfg::CacheEverything();
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();
svcSleepThread(10'000'000);
}
impl::Exit();
return 0;
}

View file

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

View file

@ -1,26 +1,27 @@
#include <ipc/ipc_IPrivateService.hpp>
#include <am/am_DaemonMenuInteraction.hpp>
#include <dmi/dmi_DaemonMenuInteraction.hpp>
#include <am/am_LibraryApplet.hpp>
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
{
ams::Result IPrivateService::GetLatestMessage(ams::sf::Out<u32> msg, const ams::sf::ClientProcessId &client_pid)
{
namespace ipc {
ams::Result IPrivateService::GetLatestMessage(const ams::sf::ClientProcessId &client_pid, ams::sf::Out<u32> out_msg) {
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());
// 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((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);
msg.SetValue((u32)g_last_menu_msg);
g_last_menu_msg = am::MenuMessage::Invalid;
std::scoped_lock lk(g_last_menu_msg_lock);
out_msg.SetValue(static_cast<u32>(g_last_menu_msg));
g_last_menu_msg = dmi::MenuMessage::Invalid;
return ams::ResultSuccess();
}
}

View file

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

View file

@ -2,8 +2,8 @@
#include <string>
#include <algorithm>
extern "C"
{
extern "C" {
u32 __nx_applet_type;
u32 __nx_applet_exit_mode = 2;
@ -11,191 +11,266 @@ extern "C"
u32 __nx_fsdev_direntry_cache_size = 1;
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)
{
return (0x0100000000001000 <= program_id) && (program_id <= 0x0100000000001FFF);
}
namespace {
NX_CONSTEXPR bool IsApplication(u64 program_id)
{
return (0x0100000000010000 <= program_id); // (For forwarders :p) /* && (program_id <= 0x01FFFFFFFFFFFFFF); */
}
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;
inline Result DoWithSmSession(std::function<Result()> fn) {
R_TRY(smInitialize());
R_TRY(fn());
smExit();
return ResultSuccess;
}
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)
{
bool ret = false;
namespace impl {
// Load arguments from applet storages
if(IsApplet(cur_program_id))
{
// Initialize sm, initialize applet, get arguments, exit applet, exit sm
auto rc = smInitialize();
if(R_SUCCEEDED(rc))
{
rc = appletInitialize();
if(R_SUCCEEDED(rc))
{
AppletStorage common_args_st;
AppletStorage st;
rc = appletPopInData(&common_args_st);
if(R_SUCCEEDED(rc))
{
appletStorageClose(&common_args_st);
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();
static Handle g_process_handle = INVALID_HANDLE;
static u64 g_process_id = 0;
static u64 g_program_id = 0;
Result ProcessInfoReceiverImpl(Handle session) {
auto base = armGetTls();
hipcMakeRequestInline(base);
s32 idx = 0;
R_TRY(svcReplyAndReceive(&idx, &session, 1, INVALID_HANDLE, UINT64_MAX));
HipcParsedRequest r = hipcParseRequest(base);
g_process_handle = r.data.copy_handles[0];
g_process_id = r.pid;
svcCloseHandle(session);
return ResultSuccess;
}
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))
{
// Initialize sm, initialize applet, get arguments, exit applet, exit sm
auto rc = smInitialize();
if(R_SUCCEEDED(rc))
{
rc = appletInitialize();
if(R_SUCCEEDED(rc))
{
AppletStorage st;
rc = appletPopLaunchParameter(&st, AppletLaunchParameterKind_UserChannel);
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;
NX_CONSTEXPR bool IsApplet(u64 program_id) {
return (0x0100000000001000 <= program_id) && (program_id <= 0x0100000000001FFF);
}
NX_CONSTEXPR bool IsApplication(u64 program_id) {
return (0x0100000000010000 <= program_id); // (For forwarders :p) /* && (program_id <= 0x01FFFFFFFFFFFFFF); */
}
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;
}
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;
}
}
}
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&)
{
catch(...) {}
}
}
}
}
}
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)
{
if(strlen(params.nro_path) == 0) ret = false;
Result LoadProcessInfo() {
Handle server_h;
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)
{
if(strlen(params.nro_argv) == 0) strcpy(params.nro_argv, params.nro_path);
Result LoadProgramId() {
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)
{
u64 cur_program_id;
auto rc = svcGetInfo(&cur_program_id, InfoType_ProgramId, CUR_PROCESS_HANDLE, 0);
if(R_SUCCEEDED(rc))
{
__nx_applet_type = DetectAppletType(cur_program_id);
int main(int argc, char **argv) {
LoadProcessInfo();
LoadProgramId();
hb::HbTargetParams params = {};
auto ok = ParseTargetParams(params, cur_program_id, argc, argv);
if(ok) hb_hbl_Target(params.nro_path, params.nro_argv, (int)params.target_once);
auto program_id = GetOwnProgramId();
auto process_handle = GetOwnProcessHandle();
__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;
}

View file

@ -143,27 +143,6 @@ static void setupHbHeap(void)
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.
static void getIsAutomaticGameplayRecording(void) {
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()
{
// 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);
}
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;
if(do_once) g_targetCounter = 1;
if(do_once) {
g_targetCounter = 1;
}
g_isApplication = (__nx_applet_type == AppletType_Application) || (__nx_applet_type == AppletType_SystemApplication);
memcpy(g_savedTls, (u8*)armGetTls() + 0x100, 0x100);
strcpy(g_basePath, path);
@ -467,6 +419,5 @@ void hb_hbl_Target(const char *path, const char *argv, int do_once)
getIsAutomaticGameplayRecording();
setupHbHeap();
getOwnProcessHandle();
loadNro();
}

View file

@ -3,20 +3,28 @@
#include <ul_Include.hpp>
#include <os/os_Titles.hpp>
namespace am
{
struct ApplicationSelectedUserArgument
{
namespace am {
struct ApplicationSelectedUserArgument {
static constexpr u32 SelectedUserMagic = 0xC79497CA;
u32 magic;
u8 one;
u8 unk_1;
u8 pad[3];
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 must be 0x400!");
static constexpr u32 SelectedUserMagic = 0xC79497CA;
static_assert(sizeof(ApplicationSelectedUserArgument) == 0x400, "ApplicationSelectedUserArgument");
bool ApplicationIsActive();
void ApplicationTerminate();
@ -27,4 +35,5 @@ namespace am
u64 ApplicationGetId();
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
#include <ul_Include.hpp>
namespace am
{
namespace am {
bool HomeMenuHasForeground();
Result HomeMenuSetForeground();
}

View file

@ -1,10 +1,9 @@
#pragma once
#include <ul_Include.hpp>
#include <map>
namespace am
{
namespace am {
bool LibraryAppletIsActive();
void LibraryAppletSetMenuAppletId(AppletId id);
AppletId LibraryAppletGetMenuAppletId();
@ -13,12 +12,16 @@ namespace am
Result LibraryAppletStart(AppletId id, u32 la_version, void *in_data, size_t in_size);
Result LibraryAppletSend(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);
AppletId LibraryAppletGetAppletIdForProgramId(u64 id);
AppletId LibraryAppletGetId();
static constexpr AppletId InvalidAppletId = (AppletId)0;
static constexpr AppletId InvalidAppletId = static_cast<AppletId>(0);
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3,9 +3,25 @@
#include <ul_Include.hpp>
#include <cfg/cfg_Config.hpp>
namespace os
{
static constexpr u32 MaxInstalledCount = 64000;
namespace os {
#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();
}

View file

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

View file

@ -7,11 +7,12 @@
#define RES_VALUE(module, name) res::GetResultByModuleAndName(#module, #name)
#define RES_DESCRIPTION(rc) res::GetDescriptionByResult(rc)
namespace res
{
namespace res {
// All 2380-**** results are from uLaunch!
static constexpr u32 Module = 380;
Result GetResultByModuleAndName(std::string mod, std::string name);
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
#include <ul_Include.hpp>
namespace util
{
ResultWith<JSON> LoadJSONFromFile(const std::string &path);
std::string GetCurrentTime();
u32 GetBatteryLevel();
bool IsCharging();
namespace util {
Result LoadJSONFromFile(JSON &out_json, const std::string &path);
}

View file

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

View file

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

View file

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

View file

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

View file

@ -1,29 +1,25 @@
#include <db/db_Save.hpp>
#include <fs/fs_Stdio.hpp>
#include <util/util_Convert.hpp>
namespace db
{
Result Mount()
{
namespace db {
Result Mount() {
FsFileSystem savefs;
FsSaveDataAttribute attr = {};
attr.system_save_data_id = HomeMenuSaveDataId;
attr.save_data_type = FsSaveDataType_System;
auto rc = fsOpenSaveDataFileSystemBySystemSaveDataId(&savefs, FsSaveDataSpaceId_System, &attr);
if(R_SUCCEEDED(rc)) fsdevMountDevice(UL_DB_MOUNT_NAME, savefs);
R_TRY(fsOpenSaveDataFileSystemBySystemSaveDataId(&savefs, FsSaveDataSpaceId_System, &attr));
fsdevMountDevice(UL_DB_MOUNT_NAME, savefs);
return rc;
return ResultSuccess;
}
void Unmount()
{
void Unmount() {
fsdevUnmountDevice(UL_DB_MOUNT_NAME);
}
void Commit()
{
void Commit() {
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 <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
namespace net
{
Result Initialize()
{
auto rc = nifmInitialize(NifmServiceType_System);
if(R_SUCCEEDED(rc)) rc = wlaninfInitialize();
return rc;
namespace net {
Result Initialize() {
R_TRY(nifmInitialize(NifmServiceType_System));
R_TRY(wlaninfInitialize());
return ResultSuccess;
}
void Finalize()
{
void Finalize() {
wlaninfExit();
nifmExit();
}
bool HasConnection()
{
NifmInternetConnectionStatus status = NifmInternetConnectionStatus_ConnectingUnknown1;
nifmGetInternetConnectionStatus(NULL, NULL, &status);
return (status == NifmInternetConnectionStatus_Connected);
bool HasConnection() {
auto status = NifmInternetConnectionStatus_ConnectingUnknown1;
nifmGetInternetConnectionStatus(nullptr, nullptr, &status);
return status == NifmInternetConnectionStatus_Connected;
}
Result GetCurrentNetworkProfile(NetworkProfileData *data)
{
Result GetCurrentNetworkProfile(NetworkProfileData *data) {
return serviceDispatch(nifmGetServiceSession_GeneralService(), 5,
.buffer_attrs = { SfBufferAttr_FixedSize | SfBufferAttr_Out | SfBufferAttr_HipcPointer },
.buffers = { { data, sizeof(NetworkProfileData) } }
);
}
Result GetMACAddress(u64 *out)
{
Result GetMACAddress(u64 *out) {
return serviceDispatchOut(wlaninfGetServiceSession(), 2, *out);
}
std::string FormatMACAddress(u64 addr)
{
std::string FormatMACAddress(u64 addr) {
std::stringstream strm;
strm << std::hex << std::uppercase << addr;
std::string 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);
if(i < 6) str += ":";
if(i < 6) {
str += ":";
}
}
return str;
}
std::string GetConsoleIPAddress()
{
std::string GetConsoleIPAddress() {
char ipaddr[0x20] = {0};
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;
}
}

View file

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

View file

@ -1,17 +1,15 @@
#include <os/os_HomeMenu.hpp>
namespace os
{
Result PushSystemAppletMessage(SystemAppletMessage msg)
{
namespace os {
Result PushSystemAppletMessage(SystemAppletMessage msg) {
AppletStorage st;
auto rc = appletCreateStorage(&st, sizeof(msg));
if(R_SUCCEEDED(rc))
{
rc = appletStorageWrite(&st, 0, &msg, sizeof(msg));
if(R_SUCCEEDED(rc)) appletPushToGeneralChannel(&st);
R_TRY(appletCreateStorage(&st, sizeof(msg)));
UL_ON_SCOPE_EXIT({
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 <ctime>
namespace os
{
static std::vector<std::string> g_lang_names =
{
namespace os {
static std::vector<std::string> g_lang_names = {
"Japanese",
"American English",
"Français",
@ -23,35 +23,44 @@ namespace os
"Chinese (traditional)"
};
std::string GetLanguageName(u32 idx)
{
if(idx >= g_lang_names.size()) return "";
std::string GetLanguageName(u32 idx) {
if(idx >= g_lang_names.size()) {
return "";
}
return g_lang_names[idx];
}
std::vector<std::string> &GetLanguageNameList()
{
std::vector<std::string> &GetLanguageNameList() {
return g_lang_names;
}
u32 GetBatteryLevel()
{
u32 GetBatteryLevel() {
u32 lvl = 0;
psmGetBatteryChargePercentage(&lvl);
return lvl;
}
bool IsConsoleCharging()
{
ChargerType cht = ChargerType_None;
bool IsConsoleCharging() {
auto cht = ChargerType_None;
psmGetChargerType(&cht);
return (cht > ChargerType_None);
return cht > ChargerType_None;
}
std::string GetFirmwareVersion()
{
std::string GetFirmwareVersion() {
SetSysFirmwareVersion fwver;
setsysGetFirmwareVersion(&fwver);
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 <db/db_Save.hpp>
#include <fs/fs_Stdio.hpp>
namespace os
{
std::vector<cfg::TitleRecord> QueryInstalledTitles()
{
namespace os {
std::vector<cfg::TitleRecord> QueryInstalledTitles() {
std::vector<cfg::TitleRecord> titles;
NsApplicationRecord *recordbuf = new NsApplicationRecord[MaxInstalledCount]();
s32 record_count = 0;
auto rc = nsListApplicationRecord(recordbuf, MaxInstalledCount, 0, &record_count);
if(R_SUCCEEDED(rc))
{
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);
UL_OS_FOR_EACH_APP_RECORD(record, {
cfg::TitleRecord rec = {};
rec.app_id = record.application_id;
if(rec.app_id == 0) {
continue;
}
}
delete[] recordbuf;
rec.title_type = static_cast<u32>(cfg::TitleType::Installed);
titles.push_back(rec);
});
return titles;
}
}

View file

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

View file

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

View file

@ -3,46 +3,16 @@
namespace util
{
ResultWith<JSON> LoadJSONFromFile(const std::string &path)
{
JSON ret = JSON::object();
if(fs::ExistsFile(path))
{
try
{
Result LoadJSONFromFile(JSON &out_json, const std::string &path) {
if(fs::ExistsFile(path)) {
try {
std::ifstream ifs(path);
ret = JSON::parse(ifs);
return SuccessResultWith(ret);
}
catch(std::exception&)
{
out_json = JSON::parse(ifs);
return ResultSuccess;
}
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))
BUILD := build
SOURCES := source source/ui $(UL_COMMON_SOURCES)
SOURCES := source source/ui source/am $(UL_COMMON_SOURCES)
DATA := data
INCLUDES := include $(UL_COMMON_INCLUDES)
EXEFS_SRC := exefs_src
@ -39,13 +39,13 @@ CXXFLAGS := $(CFLAGS) $(UL_CXXFLAGS)
ASFLAGS := -g $(ARCH)
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
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(LIBNX) $(CURDIR)/../Plutonium/Plutonium/Output
LIBDIRS := $(PORTLIBS) $(LIBNX) $(TOPDIR)/../Plutonium/Plutonium/Output
#---------------------------------------------------------------------------------
@ -134,13 +134,12 @@ all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) -C ../Plutonium/Plutonium/
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
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
#include <am/am_DaemonMenuInteraction.hpp>
#include <dmi/dmi_DaemonMenuInteraction.hpp>
#include <functional>
namespace am
{
namespace am {
using MessageDetectCallback = std::function<void()>;
Result InitializeDaemonMessageHandler();
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
namespace am
{
namespace am {
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
#include <ul_Include.hpp>
#include <cfg/cfg_Config.hpp>
namespace ui::actions
{
namespace ui::actions {
void ShowAboutDialog();
void ShowSettingsMenu();
void ShowThemesMenu();
@ -14,4 +13,5 @@ namespace ui::actions
void ShowHelpDialog();
void ShowAlbumApplet();
void ShowPowerDialog();
}

View file

@ -5,31 +5,20 @@
#include <ul_Include.hpp>
#include <pu/Plutonium>
namespace ui
{
class ClickableImage : public pu::ui::elm::Element
{
public:
ClickableImage(s32 X, s32 Y, pu::String Image);
PU_SMART_CTOR(ClickableImage)
~ClickableImage();
namespace ui {
class ClickableImage : public pu::ui::elm::Element {
private:
inline void Dispose() {
if(this->ntex != nullptr) {
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:
pu::String img;
std::string img;
pu::sdl2::Texture ntex;
s32 x;
s32 y;
@ -38,5 +27,33 @@ namespace ui
std::function<void()> cb;
std::chrono::steady_clock::time_point touchtp;
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 <ul_Include.hpp>
namespace ui
{
class IMenuLayout : public pu::ui::Layout
{
namespace ui {
class IMenuLayout : public pu::ui::Layout {
private:
Mutex home_press_lock;
bool home_pressed;
@ -14,9 +14,11 @@ namespace ui
public:
IMenuLayout();
void OnInput(u64 down, u64 up, u64 held, pu::ui::Touch pos);
virtual void OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch pos) = 0;
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 touch_pos) = 0;
void DoOnHomeButtonPress();
virtual void OnHomeButtonPress() = 0;
};
}

View file

@ -4,21 +4,24 @@
#include <ui/ui_IMenuLayout.hpp>
#include <cfg/cfg_Config.hpp>
namespace ui
{
class LanguagesMenuLayout : public IMenuLayout
{
namespace ui {
class LanguagesMenuLayout : public IMenuLayout {
private:
pu::ui::elm::TextBlock::Ref infoText;
pu::ui::elm::Menu::Ref langsMenu;
public:
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 Reload();
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_SettingsMenuLayout.hpp>
#include <ui/ui_LanguagesMenuLayout.hpp>
#include <am_DaemonMessages.hpp>
#include <am/am_DaemonMessages.hpp>
namespace ui
{
enum class MenuType
{
namespace ui {
enum class MenuType {
Startup,
Main,
Settings,
@ -18,16 +17,43 @@ namespace ui
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:
using Application::Application;
~MenuApplication();
~MenuApplication() {
pu::audio::Delete(this->bgm);
}
PU_SMART_CTOR(MenuApplication)
static inline void RegisterHomeButtonDetection() {
am::RegisterOnMessageDetect(&UiOnHomeButtonDetection, dmi::MenuMessage::HomeRequest);
}
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 LoadStartupMenu();
void LoadThemeMenu();
@ -44,37 +70,31 @@ namespace ui
void ShowNotification(const std::string &text, u64 timeout = 1500);
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);
}
template<typename Elem>
void ApplyConfigForElement(const std::string &menu, const std::string &name, std::shared_ptr<Elem> &Ref, bool also_visible = true)
{
if(this->uijson.count(menu))
{
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)) {
auto jmenu = this->uijson[menu];
if(jmenu.count(name))
{
if(jmenu.count(name)) {
auto jelem = jmenu[name];
bool set_coords = false;
if(also_visible)
{
if(also_visible) {
bool visible = jelem.value("visible", true);
Ref->SetVisible(visible);
set_coords = visible;
}
else set_coords = true;
if(set_coords)
{
if(jelem.count("x"))
{
else {
set_coords = true;
}
if(set_coords) {
if(jelem.count("x")) {
s32 x = jelem["x"];
Ref->SetX(x);
}
if(jelem.count("y"))
{
if(jelem.count("y")) {
s32 y = jelem["y"];
Ref->SetY(y);
}
@ -95,28 +115,7 @@ namespace ui
void SetSelectedUser(AccountUid user_id);
AccountUid GetSelectedUser();
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 <cfg/cfg_Config.hpp>
namespace ui
{
class MenuLayout : public IMenuLayout
{
public:
MenuLayout(void *raw, u8 min_alpha);
~MenuLayout();
PU_SMART_CTOR(MenuLayout)
namespace ui {
void OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch pos) override;
void OnHomeButtonPress() override;
class MenuLayout : public IMenuLayout {
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:
void *susptr;
bool last_hasconn;
@ -66,5 +48,46 @@ namespace ui
s32 rawalpha;
pu::audio::Sfx sfxTitleLaunch;
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
#include <ul_Include.hpp>
#include <am/am_DaemonMessages.hpp>
#include <pu/Plutonium>
namespace ui
{
enum class QuickMenuDirection
{
namespace ui {
enum class QuickMenuDirection {
Up,
Down,
Left,
@ -18,26 +17,35 @@ namespace ui
None
};
struct QuickMenuSubItem
{
struct QuickMenuSubItem {
std::function<void()> on_select;
pu::sdl2::Texture nicon;
};
class QuickMenu : public pu::ui::elm::Element
{
static constexpr s32 MainItemSize = 300;
static constexpr s32 SubItemsSize = 150;
static constexpr s32 CommonAreaSize = 50;
void QuickMenuOnHomeButtonDetection();
static constexpr s32 MainItemX = (1280 - MainItemSize) / 2;
static constexpr s32 MainItemY = (720 - MainItemSize) / 2;
class QuickMenu : public pu::ui::elm::Element {
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:
QuickMenu(const std::string &main_icon);
PU_SMART_CTOR(QuickMenu)
static void RegisterHomeButtonDetection();
static inline void RegisterHomeButtonDetection() {
am::RegisterOnMessageDetect(&QuickMenuOnHomeButtonDetection, dmi::MenuMessage::HomeRequest);
}
s32 GetX();
s32 GetY();
@ -50,9 +58,6 @@ namespace ui
void OnRender(pu::ui::render::Renderer::Ref &Drawer, s32 X, s32 Y);
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 <pu/Plutonium>
namespace ui
{
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();
namespace ui {
class RawData : public pu::ui::elm::Element {
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:
s32 x;
s32 y;
@ -33,5 +17,24 @@ namespace ui
void *ptr;
size_t fullsz;
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 <cfg/cfg_Config.hpp>
namespace ui
{
class SettingsMenuLayout : public IMenuLayout
{
namespace ui {
class SettingsMenuLayout : public IMenuLayout {
private:
pu::ui::elm::TextBlock::Ref infoText;
pu::ui::elm::Menu::Ref settingsMenu;
public:
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 Reload();
void PushSettingItem(const std::string &name, const std::string &value_display, int 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 <map>
namespace ui
{
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);
namespace ui {
class SideMenu : public pu::ui::elm::Element {
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();
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);
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:
s32 y;
u32 selitm;
@ -79,8 +46,46 @@ namespace ui
u32 scrollflag;
u32 scrolltpvalue;
u32 scrollcount;
bool IsLeftFirst();
bool IsRightLast();
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
#include <ul_Include.hpp>
#include <am/am_DaemonMenuInteraction.hpp>
#include <dmi/dmi_DaemonMenuInteraction.hpp>
#include <db/db_Save.hpp>
#include <ui/ui_IMenuLayout.hpp>
namespace ui
{
class StartupLayout : public IMenuLayout
{
namespace ui {
class StartupLayout : public IMenuLayout {
private:
bool loadmenu;
pu::ui::elm::TextBlock::Ref infoText;
pu::ui::elm::Menu::Ref usersMenu;
public:
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 user_Click(AccountUid uid);
void create_Click();
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 <cfg/cfg_Config.hpp>
namespace ui
{
class ThemeMenuLayout : public IMenuLayout
{
public:
ThemeMenuLayout();
PU_SMART_CTOR(ThemeMenuLayout)
namespace ui {
void OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch pos) override;
void OnHomeButtonPress() override;
class ThemeMenuLayout : public IMenuLayout {
void Reload();
void theme_Click();
private:
pu::ui::elm::Menu::Ref themesMenu;
pu::ui::elm::TextBlock::Ref curThemeText;
@ -26,5 +17,17 @@ namespace ui
pu::ui::elm::Image::Ref curThemeIcon;
pu::ui::elm::Image::Ref curThemeBanner;
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 <util/util_Convert.hpp>
#include <am/am_LibraryApplet.hpp>
#include <am_DaemonMessages.hpp>
#include <am_LibAppletWrap.hpp>
#include <am/am_DaemonMessages.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)
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"
@ -33,32 +35,30 @@ cfg::Config g_ul_config;
cfg::Theme g_ul_theme;
u8 *g_app_capture_buffer;
namespace qmenu
{
void Initialize()
{
UL_ASSERT(accountInitialize(AccountServiceType_System))
UL_ASSERT(nsInitialize())
UL_ASSERT(net::Initialize())
UL_ASSERT(psmInitialize())
UL_ASSERT(setsysInitialize())
UL_ASSERT(setInitialize())
namespace impl {
void Initialize() {
UL_ASSERT(accountInitialize(AccountServiceType_System));
UL_ASSERT(nsInitialize());
UL_ASSERT(net::Initialize());
UL_ASSERT(psmInitialize());
UL_ASSERT(setsysInitialize());
UL_ASSERT(setInitialize());
// Register handlers for HOME button press detection
am::RegisterLibAppletHomeButtonDetection();
ui::RegisterHomeButtonDetection();
ui::MenuApplication::RegisterHomeButtonDetection();
ui::QuickMenu::RegisterHomeButtonDetection();
// Initialize Daemon message handling
// Initialize uDaemon message handling
UL_ASSERT(am::InitializeDaemonMessageHandler());
// Load menu g_ul_config and theme
// Load menu config and theme
g_ul_config = cfg::EnsureConfig();
g_ul_theme = cfg::LoadTheme(g_ul_config.theme_name);
}
void Exit()
{
void Exit() {
am::ExitDaemonMessageHandler();
setExit();
@ -70,50 +70,47 @@ namespace qmenu
}
}
int main()
{
auto [rc, smode] = am::Menu_ProcessInput();
if(R_SUCCEEDED(rc))
{
am::DaemonStatus status = {};
int main() {
// First read sent storages, then init the renderer (UI, which also inits RomFs), then init everything else
auto smode = dmi::MenuStartMode::Invalid;
auto rc = am::ReadStartMode(smode);
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.
am::Menu_DaemonReadImpl(&status, sizeof(status), false);
if(smode != dmi::MenuStartMode::Invalid) {
// 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
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"))
// Try to mount it
UL_ASSERT(romfsMountFromFsdev(MENU_ROMFS_BIN, 0, "romfs"));
// After initializing RomFs, start initializing the rest of stuff here
g_app_capture_buffer = new u8[RawRGBAScreenBufferSize]();
qmenu::Initialize();
g_app_capture_buffer = new (std::nothrow) u8[RawRGBAScreenBufferSize]();
impl::Initialize();
g_entry_list = cfg::LoadTitleList();
// Get system language and load translations (default one if not present)
u64 lcode = 0;
setGetLanguageCode(&lcode);
std::string syslang = (char*)&lcode;
std::string syslang = reinterpret_cast<char*>(&lcode);
auto lpath = cfg::GetLanguageJSONPath(syslang);
auto [rc1, defjson] = util::LoadJSONFromFile(CFG_LANG_DEFAULT);
UL_ASSERT(rc1);
g_ul_config.default_lang = defjson;
g_ul_config.main_lang = defjson;
if(fs::ExistsFile(lpath))
{
auto [rc2, ljson] = util::LoadJSONFromFile(lpath);
if(R_SUCCEEDED(rc2)) g_ul_config.main_lang = ljson;
UL_ASSERT(util::LoadJSONFromFile(g_ul_config.default_lang, CFG_LANG_DEFAULT));
g_ul_config.main_lang = g_ul_config.default_lang;
if(fs::ExistsFile(lpath)) {
auto ljson = JSON::object();
UL_ASSERT(util::LoadJSONFromFile(ljson, lpath));
g_ul_config.main_lang = ljson;
}
// Get the text sizes to initialize default fonts
auto [rc2, uijson] = util::LoadJSONFromFile(cfg::GetAssetByTheme(g_ul_theme, "ui/UI.json"));
UL_ASSERT(rc2);
auto uijson = JSON::object();
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 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->Prepare();
if(smode == am::MenuStartMode::MenuApplicationSuspended) g_menu_app_instance->Show();
else g_menu_app_instance->ShowWithFadeIn();
if(smode == dmi::MenuStartMode::MenuApplicationSuspended) {
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();
delete[] g_app_capture_buffer;
qmenu::Exit();
operator delete[](g_app_capture_buffer, std::nothrow);
impl::Exit();
}
}

View file

@ -1,34 +1,31 @@
#include <am_DaemonMessages.hpp>
#include <thread>
#include <am/am_DaemonMessages.hpp>
#include <map>
namespace am
{
namespace am {
static Mutex g_receiver_lock = EmptyMutex;
static Service g_daemon_srv;
static bool g_init = false;
static bool g_thr_should_stop = false;
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)
{
while(true)
{
static void DaemonMessageReceiveThread(void *arg) {
while(true) {
mutexLock(&g_receiver_lock);
auto should_stop = g_thr_should_stop;
mutexUnlock(&g_receiver_lock);
if(should_stop) break;
MenuMessage tmp_msg = MenuMessage::Invalid;
if(should_stop) {
break;
}
auto tmp_msg = dmi::MenuMessage::Invalid;
u64 pid_placeholder = 0;
UL_ASSERT(serviceDispatchInOut(&g_daemon_srv, 0, pid_placeholder, tmp_msg,
.in_send_pid = true,
));
mutexLock(&g_receiver_lock);
for(auto &[cb, msg] : g_callback_table)
{
if(msg == tmp_msg)
{
for(auto &[cb, msg] : g_callback_table) {
if(msg == tmp_msg) {
cb();
}
}
@ -37,26 +34,22 @@ namespace am
}
}
Result InitializeDaemonMessageHandler()
{
if(g_init) return 0;
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;
}
Result InitializeDaemonMessageHandler() {
if(g_init) {
return ResultSuccess;
}
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()
{
if(!g_init) return;
void ExitDaemonMessageHandler() {
if(!g_init) {
return;
}
mutexLock(&g_receiver_lock);
g_thr_should_stop = true;
mutexUnlock(&g_receiver_lock);
@ -66,8 +59,7 @@ namespace am
g_init = false;
}
void RegisterOnMessageDetect(MessageDetectCallback callback, MenuMessage desired_msg)
{
void RegisterOnMessageDetect(MessageDetectCallback callback, dmi::MenuMessage desired_msg) {
mutexLock(&g_receiver_lock);
g_callback_table.push_back(std::make_pair(callback, desired_msg));
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 cfg::Config g_ul_config;
namespace ui::actions
{
void ShowAboutDialog()
{
namespace ui::actions {
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");
}
void ShowSettingsMenu()
{
void ShowSettingsMenu() {
g_menu_app_instance->FadeOut();
g_menu_app_instance->LoadSettingsMenu();
g_menu_app_instance->FadeIn();
}
void ShowThemesMenu()
{
void ShowThemesMenu() {
g_menu_app_instance->FadeOut();
g_menu_app_instance->LoadThemeMenu();
g_menu_app_instance->FadeIn();
}
void ShowUserMenu()
{
void ShowUserMenu() {
auto uid = g_menu_app_instance->GetSelectedUser();
auto [_rc, name] = os::GetAccountName(uid);
std::string name;
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));
if(sopt == 0) friendsLaShowMyProfileForHomeMenu(uid);
else if(sopt == 1)
{
if(sopt == 0) {
friendsLaShowMyProfileForHomeMenu(uid);
}
else if(sopt == 1) {
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);
if(sopt == 0) logoff = 2;
if(sopt == 0) {
logoff = 2;
}
}
else logoff = 1;
if(logoff > 0)
{
else {
logoff = 1;
}
if(logoff > 0) {
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();
menu_lyt->MoveFolder("", false);
g_menu_app_instance->LoadStartupMenu();
@ -65,40 +65,40 @@ namespace ui::actions
}
}
void ShowControllerSupport()
{
void ShowControllerSupport() {
HidLaControllerSupportArg arg = {};
hidLaCreateControllerSupportArg(&arg);
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);
}
void ShowWebPage()
{
void ShowWebPage() {
SwkbdConfig swkbd;
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());
char url[500] = {0};
auto rc = swkbdShow(&swkbd, url, 500);
swkbdClose(&swkbd);
if(R_SUCCEEDED(rc))
{
WebCommonConfig web = {};
webPageCreate(&web, url);
webConfigSetWhitelist(&web, ".*");
swkbdShow(&swkbd, url, 500);
WebCommonConfig web = {};
webPageCreate(&web, url);
webConfigSetWhitelist(&web, ".*");
am::MenuCommandWriter writer(am::DaemonMessage::OpenWebPage);
writer.Write<WebCommonConfig>(web);
writer.FinishWrite();
dmi::MenuMessageWriter writer(dmi::DaemonMessage::OpenWebPage);
writer.Write<WebCommonConfig>(web);
writer.FinishWrite();
g_menu_app_instance->StopPlayBGM();
g_menu_app_instance->CloseWithFadeOut();
}
g_menu_app_instance->StopPlayBGM();
g_menu_app_instance->CloseWithFadeOut();
}
void ShowHelpDialog()
{
void ShowHelpDialog() {
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_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);
}
void ShowAlbumApplet()
{
am::MenuCommandWriter writer(am::DaemonMessage::OpenAlbum);
writer.FinishWrite();
void ShowAlbumApplet() {
dmi::MenuMessageWriter writer(dmi::DaemonMessage::OpenAlbum);
g_menu_app_instance->StopPlayBGM();
g_menu_app_instance->CloseWithFadeOut();
}
void ShowPowerDialog()
{
void ShowPowerDialog() {
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);
if(sopt == 0) msg = os::GeneralChannelMessage::Sleep;
else if(sopt == 1) msg = os::GeneralChannelMessage::Shutdown;
else if(sopt == 2) msg = os::GeneralChannelMessage::Reboot;
if(sopt == 0) {
msg = os::GeneralChannelMessage::Sleep;
}
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
g_menu_app_instance->FadeOut();
os::SystemAppletMessage smsg = {};
smsg.magic = os::SAMSMagic;
smsg.message = (u32)msg;
auto smsg = os::SystemAppletMessage::Create(msg);
os::PushSystemAppletMessage(smsg);
svcSleepThread(1'500'000'000L);

View file

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

View file

@ -1,28 +1,30 @@
#include <ui/ui_IMenuLayout.hpp>
#include <ui/ui_Actions.hpp>
namespace ui
{
IMenuLayout::IMenuLayout() : home_press_lock(EmptyMutex), home_pressed(false)
{
namespace ui {
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));
}
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);
auto home_press = this->home_pressed;
this->home_pressed = false;
mutexUnlock(&this->home_press_lock);
if(home_press) this->OnHomeButtonPress();
if(!hidIsControllerConnected(CONTROLLER_HANDHELD) && !hidIsControllerConnected(CONTROLLER_PLAYER_1)) actions::ShowControllerSupport();
this->OnMenuInput(down, up, held, pos);
if(home_press) {
this->OnHomeButtonPress();
}
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);
this->home_pressed = true;
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::Config g_ul_config;
namespace ui
{
LanguagesMenuLayout::LanguagesMenuLayout()
{
namespace ui {
LanguagesMenuLayout::LanguagesMenuLayout() {
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"));
@ -34,39 +33,36 @@ namespace ui
this->Add(this->langsMenu);
}
void LanguagesMenuLayout::OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch pos)
{
if(down & KEY_B)
{
void LanguagesMenuLayout::OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) {
if(down & KEY_B) {
g_menu_app_instance->FadeOut();
g_menu_app_instance->LoadSettingsMenu();
g_menu_app_instance->FadeIn();
}
}
void LanguagesMenuLayout::OnHomeButtonPress()
{
void LanguagesMenuLayout::OnHomeButtonPress() {
g_menu_app_instance->FadeOut();
g_menu_app_instance->LoadMenu();
g_menu_app_instance->FadeIn();
}
void LanguagesMenuLayout::Reload()
{
void LanguagesMenuLayout::Reload() {
this->langsMenu->ClearItems();
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;
SetLanguage ilang = SetLanguage_ENUS;
auto ilang = SetLanguage_ENUS;
setGetLanguageCode(&lcode);
setMakeLanguage(lcode, &ilang);
u32 idx = 0;
for(auto &lang: os::GetLanguageNameList())
{
for(auto &lang: os::GetLanguageNameList()) {
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);
litm->SetColor(textclr);
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;
SetLanguage ilang = SetLanguage_ENUS;
setGetLanguageCode(&lcode);
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"));
else
{
if(static_cast<u32>(ilang) == idx) {
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);
if(sopt == 0)
{
if(sopt == 0) {
u64 codes[16] = {0};
s32 tmp;
setGetAvailableLanguageCodes(&tmp, codes, 16);
u64 code = codes[this->langsMenu->GetSelectedIndex()];
auto code = codes[this->langsMenu->GetSelectedIndex()];
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);
if(R_SUCCEEDED(rc))
{
if(R_SUCCEEDED(rc)) {
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);
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 ui::MenuApplication::Ref g_menu_app_instance;
namespace ui
{
void UiOnHomeButtonDetection()
{
switch(g_menu_app_instance->GetCurrentLoadedMenu())
{
case MenuType::Startup:
{
namespace ui {
void UiOnHomeButtonDetection() {
switch(g_menu_app_instance->GetCurrentLoadedMenu()) {
case MenuType::Startup: {
g_menu_app_instance->GetStartupLayout()->DoOnHomeButtonPress();
break;
}
case MenuType::Main:
{
case MenuType::Main: {
g_menu_app_instance->GetMenuLayout()->DoOnHomeButtonPress();
break;
}
case MenuType::Settings:
{
case MenuType::Settings: {
g_menu_app_instance->GetSettingsMenuLayout()->DoOnHomeButtonPress();
break;
}
case MenuType::Theme:
{
case MenuType::Theme: {
g_menu_app_instance->GetThemeMenuLayout()->DoOnHomeButtonPress();
break;
}
case MenuType::Languages:
{
case MenuType::Languages: {
g_menu_app_instance->GetLanguagesMenuLayout()->DoOnHomeButtonPress();
break;
}
}
}
MenuApplication::~MenuApplication()
{
pu::audio::Delete(this->bgm);
}
void MenuApplication::OnLoad()
{
if(this->IsSuspended())
{
void MenuApplication::OnLoad() {
if(this->IsSuspended()) {
bool 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->bgm_loop = this->bgmjson.value("loop", true);
this->bgm_fade_in_ms = this->bgmjson.value("fade_in_ms", 1500);
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"));
pu::ui::Color toastbaseclr = pu::ui::Color::FromHex(GetUIConfigValue<std::string>("toast_base_color", "#282828ff"));
auto toasttextclr = pu::ui::Color::FromHex(GetUIConfigValue<std::string>("toast_text_color", "#e1e1e1ff"));
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->bgm = pu::audio::Open(cfg::GetAssetByTheme(g_ul_theme, "sound/BGM.mp3"));
@ -70,162 +57,143 @@ namespace ui
this->settingsMenuLayout = SettingsMenuLayout::New();
this->languagesMenuLayout = LanguagesMenuLayout::New();
switch(this->stmode)
{
case am::MenuStartMode::StartupScreen:
switch(this->stmode) {
case dmi::MenuStartMode::StartupScreen: {
this->LoadStartupMenu();
break;
default:
}
default: {
this->StartPlayBGM();
this->LoadMenu();
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->status = status;
this->uijson = ui_json;
}
void MenuApplication::LoadMenu()
{
void MenuApplication::LoadMenu() {
this->menuLayout->SetUser(this->status.selected_user);
this->LoadLayout(this->menuLayout);
this->loaded_menu = MenuType::Main;
}
void MenuApplication::LoadStartupMenu()
{
void MenuApplication::LoadStartupMenu() {
this->StopPlayBGM();
this->startupLayout->ReloadMenu();
this->LoadLayout(this->startupLayout);
this->loaded_menu = MenuType::Startup;
}
void MenuApplication::LoadThemeMenu()
{
void MenuApplication::LoadThemeMenu() {
this->themeMenuLayout->Reload();
this->LoadLayout(this->themeMenuLayout);
this->loaded_menu = MenuType::Theme;
}
void MenuApplication::LoadSettingsMenu()
{
void MenuApplication::LoadSettingsMenu() {
this->settingsMenuLayout->Reload();
this->LoadLayout(this->settingsMenuLayout);
this->loaded_menu = MenuType::Settings;
}
void MenuApplication::LoadSettingsLanguagesMenu()
{
void MenuApplication::LoadSettingsLanguagesMenu() {
this->languagesMenuLayout->Reload();
this->LoadLayout(this->languagesMenuLayout);
this->loaded_menu = MenuType::Languages;
}
bool MenuApplication::IsSuspended()
{
bool MenuApplication::IsSuspended() {
return this->IsTitleSuspended() || this->IsHomebrewSuspended();
}
bool MenuApplication::IsTitleSuspended()
{
bool MenuApplication::IsTitleSuspended() {
return this->status.app_id > 0;
}
bool MenuApplication::IsHomebrewSuspended()
{
bool MenuApplication::IsHomebrewSuspended() {
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;
}
u64 MenuApplication::GetSuspendedApplicationId()
{
u64 MenuApplication::GetSuspendedApplicationId() {
return this->status.app_id;
}
void MenuApplication::NotifyEndSuspended()
{
void MenuApplication::NotifyEndSuspended() {
// Blanking the whole status would also blank the selected user...
this->status.params = {};
this->status.app_id = 0;
}
bool MenuApplication::LaunchFailed()
{
return (this->stmode == am::MenuStartMode::MenuLaunchFailure);
bool MenuApplication::LaunchFailed() {
return this->stmode == dmi::MenuStartMode::MenuLaunchFailure;
}
void MenuApplication::ShowNotification(const std::string &text, u64 timeout)
{
void MenuApplication::ShowNotification(const std::string &text, u64 timeout) {
this->EndOverlay();
this->notifToast->SetText(text);
this->StartOverlayWithTimeout(this->notifToast, timeout);
}
void MenuApplication::StartPlayBGM()
{
if(this->bgm != nullptr)
{
void MenuApplication::StartPlayBGM() {
if(this->bgm != nullptr) {
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);
}
}
void MenuApplication::StopPlayBGM()
{
if(this->bgm_fade_out_ms > 0) pu::audio::FadeOut(this->bgm_fade_out_ms);
else pu::audio::Stop();
void MenuApplication::StopPlayBGM() {
if(this->bgm_fade_out_ms > 0) {
pu::audio::FadeOut(this->bgm_fade_out_ms);
}
else {
pu::audio::Stop();
}
}
StartupLayout::Ref &MenuApplication::GetStartupLayout()
{
StartupLayout::Ref &MenuApplication::GetStartupLayout() {
return this->startupLayout;
}
MenuLayout::Ref &MenuApplication::GetMenuLayout()
{
MenuLayout::Ref &MenuApplication::GetMenuLayout() {
return this->menuLayout;
}
ThemeMenuLayout::Ref &MenuApplication::GetThemeMenuLayout()
{
ThemeMenuLayout::Ref &MenuApplication::GetThemeMenuLayout() {
return this->themeMenuLayout;
}
SettingsMenuLayout::Ref &MenuApplication::GetSettingsMenuLayout()
{
SettingsMenuLayout::Ref &MenuApplication::GetSettingsMenuLayout() {
return this->settingsMenuLayout;
}
LanguagesMenuLayout::Ref &MenuApplication::GetLanguagesMenuLayout()
{
LanguagesMenuLayout::Ref &MenuApplication::GetLanguagesMenuLayout() {
return this->languagesMenuLayout;
}
void MenuApplication::SetSelectedUser(AccountUid user_id)
{
am::MenuCommandWriter writer(am::DaemonMessage::SetSelectedUser);
void MenuApplication::SetSelectedUser(AccountUid user_id) {
this->status.selected_user = user_id;
dmi::MenuMessageWriter writer(dmi::DaemonMessage::SetSelectedUser);
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;
}
MenuType MenuApplication::GetCurrentLoadedMenu()
{
MenuType MenuApplication::GetCurrentLoadedMenu() {
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_MenuApplication.hpp>
#include <cfg/cfg_Config.hpp>
#include <am_DaemonMessages.hpp>
#include <am/am_DaemonMessages.hpp>
extern cfg::Theme g_ul_theme;
extern ui::MenuApplication::Ref g_menu_app_instance;
namespace ui
{
namespace ui {
static Mutex g_quick_menu_home_lock = EmptyMutex;
static bool g_quick_menu_home_pressed = false;
static void QuickMenuOnHomeButtonDetection()
{
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)
{
QuickMenu::QuickMenu(const std::string &main_icon) {
this->on = false;
this->bgalpha = 0;
pu::ui::Color 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"));
pu::ui::Color menubgclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("menu_bg_color", "#0094ffff"));
auto textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
auto menufocusclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("menu_focus_color", "#5ebcffff"));
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->SetOnFocusColor(menufocusclr);
@ -80,92 +72,96 @@ namespace ui
this->options_menu->AddItem(opt_item);
}
void QuickMenu::RegisterHomeButtonDetection()
{
am::RegisterOnMessageDetect(&QuickMenuOnHomeButtonDetection, am::MenuMessage::HomeRequest);
}
s32 QuickMenu::GetX()
{
s32 QuickMenu::GetX() {
return 0;
}
s32 QuickMenu::GetY()
{
s32 QuickMenu::GetY() {
return 0;
}
s32 QuickMenu::GetWidth()
{
s32 QuickMenu::GetWidth() {
return 1280;
}
s32 QuickMenu::GetHeight()
{
s32 QuickMenu::GetHeight() {
return 720;
}
void QuickMenu::Toggle()
{
void QuickMenu::Toggle() {
this->on = !this->on;
}
bool QuickMenu::IsOn()
{
bool QuickMenu::IsOn() {
return this->on && (this->bgalpha > 0);
}
void QuickMenu::OnRender(pu::ui::render::Renderer::Ref &Drawer, s32 X, s32 Y)
{
if(!this->on)
{
if(this->bgalpha > 0)
{
void QuickMenu::OnRender(pu::ui::render::Renderer::Ref &drawer, s32 x, s32 y) {
if(!this->on) {
if(this->bgalpha > 0) {
this->bgalpha -= 20;
if(this->bgalpha < 0) this->bgalpha = 0;
if(this->bgalpha < 0) {
this->bgalpha = 0;
}
}
}
else
{
if(this->bgalpha < 220)
{
else {
if(this->bgalpha < 220) {
this->bgalpha += 20;
if(this->bgalpha > 220) this->bgalpha = 220;
if(this->bgalpha > 220) {
this->bgalpha = 220;
}
}
}
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)
{
if(this->bgalpha < 220) Drawer->SetBaseRenderAlpha((u8)this->bgalpha);
this->options_menu->OnRender(Drawer, this->options_menu->GetProcessedX(), this->options_menu->GetProcessedY());
if(this->bgalpha < 220) Drawer->UnsetBaseRenderAlpha();
drawer->RenderRectangleFill({ 50, 50, 50, bgalphau8 }, 0, 0, 1280, 720);
if(this->bgalpha > 0) {
if(this->bgalpha < 220) {
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)
{
if(this->on) this->options_menu->OnInput(Down, Up, Held, 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();
void QuickMenu::OnInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) {
if(this->on) {
this->options_menu->OnInput(down, up, held, touch_pos);
}
else
{
if(this->on)
{
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
if(this->on) {
this->Toggle();
}
}
else {
if(this->on) {
mutexLock(&g_quick_menu_home_lock);
auto home_pressed = g_quick_menu_home_pressed;
g_quick_menu_home_pressed = false;
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>
namespace ui
{
RawData::RawData(s32 X, s32 Y, void *raw, s32 Width, s32 Height, u32 PixNum)
{
this->x = X;
this->y = Y;
this->w = Width;
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);
namespace ui {
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->ntex = SDL_CreateTexture(pu::ui::render::GetMainRenderer(), SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, this->w, this->h);
if(this->ntex != nullptr) {
SDL_UpdateTexture(this->ntex, nullptr, this->ptr, this->pitch);
}
}
}
RawData::~RawData()
{
if(this->ntex != nullptr)
{
RawData::~RawData() {
if(this->ntex != nullptr) {
pu::ui::render::DeleteTexture(this->ntex);
this->ntex = nullptr;
}
}
s32 RawData::GetX()
{
s32 RawData::GetX() {
return this->x;
}
void RawData::SetX(s32 X)
{
this->x = X;
void RawData::SetX(s32 x) {
this->x = x;
}
s32 RawData::GetY()
{
s32 RawData::GetY() {
return this->y;
}
void RawData::SetY(s32 Y)
{
this->y = Y;
void RawData::SetY(s32 y) {
this->y = y;
}
s32 RawData::GetWidth()
{
s32 RawData::GetWidth() {
return this->w;
}
void RawData::SetWidth(s32 Width)
{
this->w = Width;
void RawData::SetWidth(s32 w) {
this->w = w;
}
s32 RawData::GetHeight()
{
s32 RawData::GetHeight() {
return this->h;
}
void RawData::SetHeight(s32 Height)
{
this->h = Height;
void RawData::SetHeight(s32 h) {
this->h = h;
}
void RawData::SetAlphaFactor(u8 Factor)
{
this->falpha = Factor;
void RawData::SetAlphaFactor(u8 factor) {
this->falpha = factor;
}
void RawData::OnRender(pu::ui::render::Renderer::Ref &Drawer, s32 X, s32 Y)
{
if(this->ntex != nullptr)
{
Drawer->SetBaseRenderAlpha(this->falpha);
Drawer->RenderTexture(this->ntex, X, Y, { -1, this->w, this->h, -1 });
Drawer->UnsetBaseRenderAlpha();
void RawData::OnRender(pu::ui::render::Renderer::Ref &drawer, s32 x, s32 y) {
if(this->ntex != nullptr) {
drawer->SetBaseRenderAlpha(this->falpha);
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::Config g_ul_config;
namespace ui
{
namespace ui {
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");
}
template<>
std::string EncodeForSettings<std::string>(std::string t)
{
inline std::string EncodeForSettings<std::string>(std::string t) {
return "\"" + t + "\"";
}
template<>
std::string EncodeForSettings<u32>(u32 t)
{
inline std::string EncodeForSettings<u32>(u32 t) {
return "\"" + std::to_string(t) + "\"";
}
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");
}
SettingsMenuLayout::SettingsMenuLayout()
{
SettingsMenuLayout::SettingsMenuLayout() {
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"));
@ -57,25 +52,21 @@ namespace ui
this->Add(this->settingsMenu);
}
void SettingsMenuLayout::OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch pos)
{
if(down & KEY_B)
{
void SettingsMenuLayout::OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch pos) {
if(down & KEY_B) {
g_menu_app_instance->FadeOut();
g_menu_app_instance->LoadMenu();
g_menu_app_instance->FadeIn();
}
}
void SettingsMenuLayout::OnHomeButtonPress()
{
void SettingsMenuLayout::OnHomeButtonPress() {
g_menu_app_instance->FadeOut();
g_menu_app_instance->LoadMenu();
g_menu_app_instance->FadeIn();
}
void SettingsMenuLayout::Reload()
{
void SettingsMenuLayout::Reload() {
this->settingsMenu->ClearItems();
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_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);
std::string connectednet = cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_wifi_none");
if(net::HasConnection())
{
auto connectednet = cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_wifi_none");
if(net::HasConnection()) {
net::NetworkProfileData data = {};
net::GetCurrentNetworkProfile(&data);
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);
u64 lcode = 0;
SetLanguage ilang = SetLanguage_ENUS;
auto ilang = SetLanguage_ENUS;
setGetLanguageCode(&lcode);
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);
@ -122,20 +112,20 @@ namespace ui
bool nfc = false;
setsysGetNfcEnableFlag(&nfc);
this->PushSettingItem(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_nfc"), EncodeForSettings(nfc), 11);
char serial[0x20] = {0};
setsysGetSerialNumber(serial);
this->PushSettingItem(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_serial_no"), EncodeForSettings<std::string>(serial), -1);
SetSysSerialNumber serial = {};
setsysGetSerialNumber(&serial);
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;
net::GetMACAddress(&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);
auto ipstr = net::GetConsoleIPAddress();
// TODO: strings
this->PushSettingItem("Console IP address", EncodeForSettings(ipstr), -1);
}
void SettingsMenuLayout::PushSettingItem(const std::string &name, const std::string &value_display, int id)
{
pu::ui::Color textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
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"));
auto itm = pu::ui::elm::MenuItem::New(name + ": " + value_display);
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"));
@ -143,13 +133,10 @@ namespace ui
this->settingsMenu->AddItem(itm);
}
void SettingsMenuLayout::setting_Click(u32 id)
{
void SettingsMenuLayout::setting_Click(u32 id) {
bool reload_need = false;
switch(id)
{
case 0:
{
switch(id) {
case 0: {
SwkbdConfig swkbd;
swkbdCreate(&swkbd, 0);
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};
auto rc = swkbdShow(&swkbd, name, SET_MAX_NICKNAME_SIZE);
swkbdClose(&swkbd);
if(R_SUCCEEDED(rc))
{
if(R_SUCCEEDED(rc)) {
setsysSetDeviceNickname(name);
reload_need = true;
}
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);
if(sopt == 0)
{
if(sopt == 0) {
g_ul_config.viewer_usb_enabled = !g_ul_config.viewer_usb_enabled;
reload_need = true;
g_menu_app_instance->ShowNotification(cfg::GetLanguageString(g_ul_config.main_lang, g_ul_config.default_lang, "set_changed_reboot"));
}
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);
if(sopt == 0)
{
if(sopt == 0) {
g_ul_config.system_title_override_enabled = !g_ul_config.system_title_override_enabled;
reload_need = true;
}
break;
}
case 3:
{
case 3: {
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};
LibAppletArgs netargs;
libappletArgsCreate(&netargs, 0);
auto rc = libappletLaunch(AppletId_netConnect, &netargs, in, sizeof(in), out, sizeof(out), nullptr);
if(R_SUCCEEDED(rc))
{
rc = *(u32*)out;
if(R_SUCCEEDED(rc)) reload_need = true;
if(R_SUCCEEDED(rc)) {
rc = *reinterpret_cast<Result*>(out);
if(R_SUCCEEDED(rc)) {
reload_need = true;
}
}
break;
}
case 4:
{
case 4: {
g_menu_app_instance->FadeOut();
g_menu_app_instance->LoadSettingsLanguagesMenu();
g_menu_app_instance->FadeIn();
break;
}
case 5:
{
bool console_info_upload = false;
case 5: {
auto console_info_upload = false;
setsysGetConsoleInformationUploadFlag(&console_info_upload);
setsysSetConsoleInformationUploadFlag(!console_info_upload);
reload_need = true;
break;
}
case 6:
{
bool auto_titles_dl = false;
case 6: {
auto auto_titles_dl = false;
setsysGetAutomaticApplicationDownloadFlag(&auto_titles_dl);
setsysSetAutomaticApplicationDownloadFlag(!auto_titles_dl);
reload_need = true;
break;
}
case 7:
{
bool auto_update = false;
case 7: {
auto auto_update = false;
setsysGetAutoUpdateEnableFlag(&auto_update);
setsysSetAutoUpdateEnableFlag(!auto_update);
reload_need = true;
break;
}
case 8:
{
bool wireless_lan = false;
case 8: {
auto wireless_lan = false;
setsysGetWirelessLanEnableFlag(&wireless_lan);
setsysSetWirelessLanEnableFlag(!wireless_lan);
reload_need = true;
break;
}
case 9:
{
bool bluetooth = false;
case 9: {
auto bluetooth = false;
setsysGetBluetoothEnableFlag(&bluetooth);
setsysSetBluetoothEnableFlag(!bluetooth);
reload_need = true;
break;
}
case 10:
{
bool usb_30 = false;
case 10: {
auto usb_30 = false;
setsysGetUsb30EnableFlag(&usb_30);
setsysSetUsb30EnableFlag(!usb_30);
reload_need = true;
break;
}
case 11:
{
bool nfc = false;
case 11: {
auto nfc = false;
setsysGetNfcEnableFlag(&nfc);
setsysSetNfcEnableFlag(!nfc);
@ -277,10 +252,10 @@ namespace ui
break;
}
}
if(reload_need)
{
if(reload_need) {
cfg::SaveConfig(g_ul_config);
this->Reload();
}
}
}

View file

@ -2,86 +2,59 @@
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)
{
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;
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->cursoricon = pu::ui::render::LoadImage(cursor_path);
this->suspicon = pu::ui::render::LoadImage(suspended_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);
}
SideMenu::~SideMenu()
{
if(this->cursoricon != nullptr)
{
SideMenu::~SideMenu() {
if(this->cursoricon != nullptr) {
pu::ui::render::DeleteTexture(this->cursoricon);
this->cursoricon = nullptr;
}
if(this->suspicon != nullptr)
{
if(this->suspicon != nullptr) {
pu::ui::render::DeleteTexture(this->suspicon);
this->suspicon = nullptr;
}
this->ClearItems();
}
s32 SideMenu::GetX()
{
s32 SideMenu::GetX() {
return 98;
}
s32 SideMenu::GetY()
{
s32 SideMenu::GetY() {
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;
}
s32 SideMenu::GetWidth()
{
s32 SideMenu::GetWidth() {
return 1280;
}
s32 SideMenu::GetHeight()
{
s32 SideMenu::GetHeight() {
return ItemSize + FocusSize + FocusMargin;
}
void SideMenu::OnRender(pu::ui::render::Renderer::Ref &drawer, s32 x, s32 y)
{
if(this->icons.empty()) return;
if(this->ricons.empty())
{
for(u32 i = 0; i < std::min((size_t)4, (this->icons.size() - this->baseiconidx)); i++)
{
void SideMenu::OnRender(pu::ui::render::Renderer::Ref &drawer, s32 x, s32 y) {
if(this->icons.empty()) {
return;
}
if(this->ricons.empty()) {
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 text = this->icons_texts[this->baseiconidx + i];
this->ricons.push_back(icon);
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->UpdateBorderIcons();
@ -90,76 +63,79 @@ namespace ui
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];
drawer->RenderTexture(ricon, basex, y, { -1, ItemSize, ItemSize, -1 });
auto ntext = this->ricons_texts[i];
if(ntext != nullptr) drawer->RenderTexture(ntext, basex + this->textx, y + this->texty);
if(this->IsItemMultiselected(this->baseiconidx + i)) drawer->RenderTexture(this->mselicon, basex - Margin, y - Margin, { -1, ExtraIconSize, ExtraIconSize, -1 });
if(this->suspitm >= 0)
{
if((this->baseiconidx + i) == (u32)suspitm)
{
if(this->suspicon != nullptr) drawer->RenderTexture(this->suspicon, basex - Margin, y - Margin, { -1, ExtraIconSize, ExtraIconSize, -1 });
if(ntext != nullptr) {
drawer->RenderTexture(ntext, basex + this->textx, y + this->texty);
}
if(this->IsItemMultiselected(this->baseiconidx + i)) {
drawer->RenderTexture(this->mselicon, 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->baseiconidx + i) == selitm)
{
if(this->cursoricon != nullptr) {
if((this->baseiconidx + i) == selitm) {
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 });
}
}
basex += ItemSize + Margin;
}
if(leftbicon != nullptr)
{
if(leftbicon != nullptr) {
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 });
}
if(movalpha > 0)
{
if(movalpha > 0) {
s32 tmpalpha = movalpha - 30;
if(tmpalpha < 0) tmpalpha = 0;
movalpha = (u8)tmpalpha;
if(tmpalpha < 0) {
tmpalpha = 0;
}
movalpha = static_cast<u8>(tmpalpha);
}
}
void SideMenu::OnInput(u64 down, u64 up, u64 held, pu::ui::Touch pos)
{
if(this->ricons.empty()) return;
if(!this->enabled) return;
void SideMenu::OnInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) {
if(this->ricons.empty()) {
return;
}
if(!this->enabled) {
return;
}
if(down & KEY_LEFT) HandleMoveLeft();
else if(down & KEY_RIGHT) HandleMoveRight();
else if(!pos.IsEmpty())
{
s32 basex = this->GetProcessedX();
s32 basey = this->GetProcessedY();
if(down & KEY_LEFT) {
HandleMoveLeft();
}
else if(down & KEY_RIGHT) {
HandleMoveRight();
}
else if(!touch_pos.IsEmpty()) {
auto basex = this->GetProcessedX();
auto basey = this->GetProcessedY();
if(this->cursoricon != nullptr)
{
for(u32 i = 0; i < this->ricons.size(); i++)
{
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);
else
{
if(this->cursoricon != nullptr) {
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((this->baseiconidx + i) == selitm) {
(this->onselect)(KEY_A, this->selitm);
}
else {
preselitm = selitm;
selitm = this->baseiconidx + i;
movalpha = 255;
movalpha = 0xFF;
(this->onselch)(this->selitm);
}
break;
@ -168,23 +144,19 @@ namespace ui
}
}
}
else (this->onselect)(down, this->selitm);
else {
(this->onselect)(down, this->selitm);
}
if(held & KEY_LEFT)
{
if(this->scrollflag == 1)
{
if(held & KEY_LEFT) {
if(this->scrollflag == 1) {
auto curtp = std::chrono::steady_clock::now();
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(curtp - this->scrolltp).count();
if(diff >= 300)
{
if(this->scrollmoveflag)
{
if(diff >= 300) {
if(this->scrollmoveflag) {
auto diff2 = std::chrono::duration_cast<std::chrono::milliseconds>(curtp - this->scrollmovetp).count();
if(diff2 >= this->scrolltpvalue)
{
if(this->scrollcount >= 5)
{
if(diff2 >= this->scrolltpvalue) {
if(this->scrollcount >= 5) {
this->scrollcount = 0;
this->scrolltpvalue /= 2;
}
@ -193,34 +165,26 @@ namespace ui
this->scrollcount++;
}
}
else
{
else {
this->scrollmovetp = std::chrono::steady_clock::now();
this->scrollmoveflag = true;
}
}
}
else
{
else {
this->scrollflag = 1;
this->scrolltp = std::chrono::steady_clock::now();
}
}
else if(held & KEY_RIGHT)
{
if(this->scrollflag == 2)
{
else if(held & KEY_RIGHT) {
if(this->scrollflag == 2) {
auto curtp = std::chrono::steady_clock::now();
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(curtp - this->scrolltp).count();
if(diff >= 300)
{
if(this->scrollmoveflag)
{
if(diff >= 300) {
if(this->scrollmoveflag) {
auto diff2 = std::chrono::duration_cast<std::chrono::milliseconds>(curtp - this->scrollmovetp).count();
if(diff2 >= this->scrolltpvalue)
{
if(this->scrollcount >= 5)
{
if(diff2 >= this->scrolltpvalue) {
if(this->scrollcount >= 5) {
this->scrollcount = 0;
this->scrolltpvalue /= 2;
}
@ -229,50 +193,46 @@ namespace ui
this->scrollcount++;
}
}
else
{
else {
this->scrollmovetp = std::chrono::steady_clock::now();
this->scrollmoveflag = true;
}
}
}
else
{
else {
this->scrollflag = 2;
this->scrolltp = std::chrono::steady_clock::now();
}
}
else
{
else {
this->scrollflag = 0;
this->scrollcount = 0;
this->scrolltpvalue = 50;
}
}
void SideMenu::SetOnItemSelected(std::function<void(u64, u32)> fn)
{
void SideMenu::SetOnItemSelected(std::function<void(u64, u32)> fn) {
this->onselect = fn;
}
void SideMenu::SetOnSelectionChanged(std::function<void(u32)> fn)
{
void SideMenu::SetOnSelectionChanged(std::function<void(u32)> fn) {
this->onselch = fn;
}
void SideMenu::ClearItems()
{
void SideMenu::ClearItems() {
this->icons.clear();
this->icons_texts.clear();
this->icons_mselected.clear();
for(auto ricon: this->ricons)
{
if(ricon != nullptr) pu::ui::render::DeleteTexture(ricon);
for(auto ricon: this->ricons) {
if(ricon != nullptr) {
pu::ui::render::DeleteTexture(ricon);
}
}
this->ricons.clear();
for(auto rtext: this->ricons_texts)
{
if(rtext != nullptr) pu::ui::render::DeleteTexture(rtext);
for(auto rtext: this->ricons_texts) {
if(rtext != nullptr) {
pu::ui::render::DeleteTexture(rtext);
}
}
this->ricons_texts.clear();
this->selitm = 0;
@ -281,70 +241,66 @@ namespace ui
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_texts.push_back(txt);
this->icons_mselected.push_back(false);
}
void SideMenu::SetSuspendedItem(u32 Index)
{
void SideMenu::SetSuspendedItem(u32 Index) {
this->suspitm = Index;
}
void SideMenu::UnsetSuspendedItem()
{
void SideMenu::UnsetSuspendedItem() {
this->suspitm = -1;
}
void SideMenu::SetSelectedItem(u32 idx)
{
if(idx < this->icons.size()) this->selitm = idx;
void SideMenu::SetSelectedItem(u32 idx) {
if(idx < this->icons.size()) {
this->selitm = idx;
}
}
void SideMenu::HandleMoveLeft()
{
if(selitm > 0)
{
void SideMenu::HandleMoveLeft() {
if(selitm > 0) {
bool ilf = IsLeftFirst();
preselitm = selitm;
selitm--;
if(ilf) ReloadIcons(1);
else movalpha = 255;
if(ilf) {
ReloadIcons(1);
}
else movalpha = 0xFF;
(this->onselch)(this->selitm);
}
}
void SideMenu::HandleMoveRight()
{
if((selitm + 1) < this->icons.size())
{
void SideMenu::HandleMoveRight() {
if((selitm + 1) < this->icons.size()) {
bool irl = IsRightLast();
preselitm = selitm;
selitm++;
if(irl) ReloadIcons(2);
else movalpha = 255;
if(irl) {
ReloadIcons(2);
}
else movalpha = 0xFF;
(this->onselch)(this->selitm);
}
}
int SideMenu::GetSuspendedItem()
{
int SideMenu::GetSuspendedItem() {
return this->suspitm;
}
u32 SideMenu::GetSelectedItem()
{
u32 SideMenu::GetSelectedItem() {
return this->selitm;
}
bool SideMenu::IsLeftFirst()
{
s32 basex = GetProcessedX();
for(u32 i = 0; i < this->ricons.size(); i++)
{
if((basex == this->GetX()) && (this->selitm == (this->baseiconidx + i))) return true;
bool SideMenu::IsLeftFirst() {
auto basex = GetProcessedX();
for(u32 i = 0; i < this->ricons.size(); i++) {
if((basex == this->GetX()) && (this->selitm == (this->baseiconidx + i))) {
return true;
}
basex += ItemSize + Margin;
}
return false;
@ -352,51 +308,57 @@ namespace ui
bool SideMenu::IsRightLast()
{
if(this->selitm == (this->icons.size() - 1)) return true;
s32 basex = GetProcessedX();
for(u32 i = 0; i < this->ricons.size(); i++)
{
if((basex == 926) && (this->selitm == (this->baseiconidx + i))) return true;
if(this->selitm == (this->icons.size() - 1)) {
return true;
}
auto basex = GetProcessedX();
for(u32 i = 0; i < this->ricons.size(); i++) {
if((basex == 926) && (this->selitm == (this->baseiconidx + i))) {
return true;
}
basex += ItemSize + Margin;
}
return false;
}
void SideMenu::ReloadIcons(u32 dir)
{
void SideMenu::ReloadIcons(u32 dir) {
switch(dir)
{
case 1: // Left
{
// Left
case 1: {
auto icon = pu::ui::render::LoadImage(this->icons[this->selitm]);
this->ricons.insert(this->ricons.begin(), icon);
auto text = this->icons_texts[this->selitm];
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->baseiconidx--;
if(this->ricons.size() == 5)
{
if(this->ricons.size() == 5) {
pu::ui::render::DeleteTexture(this->ricons.back());
this->ricons.pop_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();
}
break;
}
case 2: // Right
{
// Right
case 2: {
auto icon = pu::ui::render::LoadImage(this->icons[this->selitm]);
this->ricons.push_back(icon);
auto ntext = pu::ui::render::RenderText(this->textfont, this->icons_texts[this->selitm], this->textclr);
this->ricons_texts.push_back(ntext);
if(this->ricons.size() == 5)
{
if(this->ricons.size() == 5) {
pu::ui::render::DeleteTexture(this->ricons.front());
this->ricons.erase(this->ricons.begin());
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->baseiconidx++;
}
@ -406,64 +368,70 @@ namespace ui
this->UpdateBorderIcons();
}
u32 SideMenu::GetBaseItemIndex()
{
u32 SideMenu::GetBaseItemIndex() {
return this->baseiconidx;
}
void SideMenu::SetBaseItemIndex(u32 index)
{
void SideMenu::SetBaseItemIndex(u32 index) {
this->baseiconidx = index;
}
void SideMenu::SetBasePositions(u32 selected_idx, u32 base_idx)
{
if(selected_idx < this->icons.size())
{
void SideMenu::SetBasePositions(u32 selected_idx, u32 base_idx) {
if(selected_idx < this->icons.size()) {
this->SetSelectedItem(selected_idx);
this->SetBaseItemIndex(base_idx);
}
}
void SideMenu::ClearBorderIcons()
{
if(this->leftbicon != nullptr) pu::ui::render::DeleteTexture(this->leftbicon);
void SideMenu::ClearBorderIcons() {
if(this->leftbicon != nullptr) {
pu::ui::render::DeleteTexture(this->leftbicon);
}
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;
}
void SideMenu::UpdateBorderIcons()
{
this->ClearBorderIcons();
if(this->baseiconidx > 0) 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]);
if(this->baseiconidx > 0) {
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()
{
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)
{
if(index < this->icons_mselected.size()) this->icons_mselected[index] = selected;
void SideMenu::SetItemMultiselected(u32 index, bool selected) {
if(index < this->icons_mselected.size()) {
this->icons_mselected[index] = selected;
}
}
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;
}
bool SideMenu::IsAnyMultiselected()
{
bool any = false;
for(auto msel: this->icons_mselected)
{
if(msel)
{
for(auto msel: this->icons_mselected) {
if(msel) {
any = true;
break;
}
@ -471,8 +439,8 @@ namespace ui
return any;
}
void SideMenu::SetEnabled(bool enabled)
{
void SideMenu::SetEnabled(bool 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::Config g_ul_config;
namespace ui
{
StartupLayout::StartupLayout()
{
namespace ui {
StartupLayout::StartupLayout() {
this->SetBackgroundImage(cfg::GetAssetByTheme(g_ul_theme, "ui/Background.png"));
this->loadmenu = false;
pu::ui::Color 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"));
pu::ui::Color menubgclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("menu_bg_color", "#0094ffff"));
auto textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
auto menufocusclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("menu_focus_color", "#5ebcffff"));
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->SetColor(textclr);
@ -30,10 +29,8 @@ namespace ui
this->Add(this->usersMenu);
}
void StartupLayout::OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch pos)
{
if(this->loadmenu)
{
void StartupLayout::OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) {
if(this->loadmenu) {
this->loadmenu = false;
g_menu_app_instance->StartPlayBGM();
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;
g_menu_app_instance->SetSelectedUser(uid);
}
void StartupLayout::create_Click()
{
void StartupLayout::create_Click() {
auto rc = pselShowUserCreator();
if(R_SUCCEEDED(rc))
{
if(R_SUCCEEDED(rc)) {
g_menu_app_instance->FadeOut();
this->ReloadMenu();
g_menu_app_instance->FadeIn();
}
}
void StartupLayout::ReloadMenu()
{
void StartupLayout::ReloadMenu() {
this->usersMenu->ClearItems();
this->usersMenu->SetSelectedIndex(0);
pu::ui::Color textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
auto [rc, users] = os::QuerySystemAccounts(true);
if(R_SUCCEEDED(rc))
{
for(auto user: users)
{
auto [rc, name] = os::GetAccountName(user);
if(R_FAILED(rc)) continue;
auto textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
std::vector<AccountUid> users;
auto rc = os::QuerySystemAccounts(users, true);
if(R_SUCCEEDED(rc)) {
for(auto &user: users) {
std::string name;
auto rc = os::GetAccountName(name, user);
if(R_SUCCEEDED(rc)) {
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);
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);
this->usersMenu->AddItem(uitm);
}
}
}
@ -95,4 +86,5 @@ namespace ui
citm->AddOnClick(std::bind(&StartupLayout::create_Click, this));
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::Config g_ul_config;
namespace ui
{
ThemeMenuLayout::ThemeMenuLayout()
{
namespace ui {
ThemeMenuLayout::ThemeMenuLayout() {
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 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 textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
auto menufocusclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("menu_focus_color", "#5ebcffff"));
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"));
g_menu_app_instance->ApplyConfigForElement("themes_menu", "banner_image", this->curThemeBanner);
@ -55,28 +54,23 @@ namespace ui
this->Add(this->curThemeIcon);
}
void ThemeMenuLayout::OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch pos)
{
if(down & KEY_B)
{
void ThemeMenuLayout::OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) {
if(down & KEY_B) {
g_menu_app_instance->FadeOut();
g_menu_app_instance->LoadMenu();
g_menu_app_instance->FadeIn();
}
}
void ThemeMenuLayout::OnHomeButtonPress()
{
void ThemeMenuLayout::OnHomeButtonPress() {
g_menu_app_instance->FadeOut();
g_menu_app_instance->LoadMenu();
g_menu_app_instance->FadeIn();
}
void ThemeMenuLayout::Reload()
{
pu::ui::Color textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
if(cfg::ThemeIsDefault(g_ul_theme))
{
void ThemeMenuLayout::Reload() {
auto textclr = pu::ui::Color::FromHex(g_menu_app_instance->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
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->curThemeName->SetVisible(false);
this->curThemeAuthor->SetVisible(false);
@ -84,8 +78,7 @@ namespace ui
this->curThemeBanner->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->curThemeName->SetVisible(true);
this->curThemeName->SetText(g_ul_theme.manifest.name);
@ -111,8 +104,7 @@ namespace ui
ditm->SetIcon("romfs:/Logo.png");
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 + ")");
itm->AddOnClick(std::bind(&ThemeMenuLayout::theme_Click, this));
itm->SetColor(textclr);
@ -122,51 +114,47 @@ namespace ui
}
}
void ThemeMenuLayout::theme_Click()
{
void ThemeMenuLayout::theme_Click() {
auto idx = this->themesMenu->GetSelectedIndex();
if(idx == 0)
{
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"));
else
{
if(idx == 0) {
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"));
}
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);
if(sopt == 0)
{
if(sopt == 0) {
g_ul_config.theme_name = "";
cfg::SaveConfig(g_ul_config);
am::MenuCommandWriter writer(am::DaemonMessage::RestartMenu);
writer.FinishWrite();
g_menu_app_instance->StopPlayBGM();
g_menu_app_instance->CloseWithFadeOut();
// 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--;
auto seltheme = this->loadedThemes[idx];
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"));
else
{
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"));
}
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);
if(sopt == 0)
{
if(sopt == 0) {
g_ul_config.theme_name = seltheme.base_name;
cfg::SaveConfig(g_ul_config);
am::MenuCommandWriter writer(am::DaemonMessage::RestartMenu);
writer.FinishWrite();
g_menu_app_instance->StopPlayBGM();
g_menu_app_instance->CloseWithFadeOut();
// 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",
"title_id": "0x010000000000100B",
"title_id_range_min": "0x010000000000100B",
"title_id_range_max": "0x010000000000100B",
"main_thread_stack_size": "0x100000",
"title_id": "0x010000000000FFFF",
"title_id_range_min": "0x010000000000FFFF",
"title_id_range_max": "0x010000000000FFFF",
"main_thread_stack_size": "0x4000",
"main_thread_priority": 44,
"default_cpu_id": 0,
"process_category": 0,

View file

@ -16,11 +16,11 @@ namespace uViewer
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.RawRGBAScreenBufferSize],
new byte[ViewerMainForm.RawRGBAScreenBufferSize],
new byte[ViewerMainForm.RawRGBAScreenBufferSize],
new byte[ViewerMainForm.RawRGBAScreenBufferSize],
new byte[ViewerMainForm.USBPacketSize], // Backups (5) so that new captures made by the main form don't replace old ones
new byte[ViewerMainForm.USBPacketSize],
new byte[ViewerMainForm.USBPacketSize],
new byte[ViewerMainForm.USBPacketSize],
new byte[ViewerMainForm.USBPacketSize],
};
public ScreenshotForm(ViewerMainForm main)