mirror of
https://github.com/XorTroll/uLaunch
synced 2024-11-10 06:24:12 +00:00
Huge rewrite, not working yet :P
This commit is contained in:
parent
45ed6bf530
commit
12f183f306
88 changed files with 3109 additions and 3549 deletions
6
.gitmodules
vendored
6
.gitmodules
vendored
|
@ -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
1
Atmosphere-libs
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 6913aa52953f228f7abc7cc7617a6ae6baec1eca
|
8
Makefile
8
Makefile
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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)
|
||||
};
|
||||
};
|
||||
|
||||
}
|
|
@ -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 {
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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/", ¶ms, 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/", ¶ms, 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
#include <ipc/ipc_IPublicService.hpp>
|
||||
|
||||
namespace ipc
|
||||
{
|
||||
namespace ipc {
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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 ¶ms, 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, ¶ms_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, ¶ms_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 ¶ms) {
|
||||
// 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 ¶ms, 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;
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
|
@ -2,8 +2,9 @@
|
|||
#pragma once
|
||||
#include <ul_Include.hpp>
|
||||
|
||||
namespace am
|
||||
{
|
||||
namespace am {
|
||||
|
||||
bool HomeMenuHasForeground();
|
||||
Result HomeMenuSetForeground();
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
189
uLaunch/include/dmi/dmi_DaemonMenuInteraction.hpp
Normal file
189
uLaunch/include/dmi/dmi_DaemonMenuInteraction.hpp
Normal 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>;
|
||||
|
||||
}
|
|
@ -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); \
|
||||
}
|
||||
} \
|
||||
})
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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");
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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); \
|
||||
} \
|
||||
})
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
26
uLaunch/include/ul_Scope.hpp
Normal file
26
uLaunch/include/ul_Scope.hpp
Normal 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__ })
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
)
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
73
uLaunch/source/dmi/dmi_DaemonMenuInteraction.cpp
Normal file
73
uLaunch/source/dmi/dmi_DaemonMenuInteraction.cpp
Normal 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);
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
namespace am
|
||||
{
|
||||
namespace am {
|
||||
|
||||
void RegisterLibAppletHomeButtonDetection();
|
||||
|
||||
}
|
10
uMenu/include/am/am_LibraryAppletUtils.hpp
Normal file
10
uMenu/include/am/am_LibraryAppletUtils.hpp
Normal 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);
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
88
uMenu/source/am/am_LibAppletWrap.cpp
Normal file
88
uMenu/source/am/am_LibAppletWrap.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
22
uMenu/source/am/am_LibraryAppletUtils.cpp
Normal file
22
uMenu/source/am/am_LibraryAppletUtils.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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) {}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <heme: this->loadedThemes)
|
||||
{
|
||||
for(auto <heme: 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue