mirror of
https://github.com/XorTroll/uLaunch
synced 2024-11-21 19:33:11 +00:00
Update for new Plutonium, huge code cleanup/rewrite
This commit is contained in:
parent
033afd2267
commit
4d915bc653
75 changed files with 5794 additions and 9293 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,6 +1,6 @@
|
|||
build/
|
||||
.vscode/
|
||||
Out/
|
||||
lib/
|
||||
SdOut/
|
||||
*.elf
|
||||
*.npdm
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 470139aaedd7d1051d9f7abdffb8536347daa7aa
|
||||
Subproject commit 0777e00e65958c82e0b87de3dc9fcde8eca609b0
|
|
@ -2,28 +2,28 @@
|
|||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include <am/am_Application.hpp>
|
||||
#include <am/am_LibraryApplet.hpp>
|
||||
|
||||
namespace ecs {
|
||||
|
||||
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, const std::string &argv_str);
|
||||
Result RegisterExternalContent(const u64 program_id, const std::string &exefs_path);
|
||||
Result LaunchSystemProcess(const u64 program_id, const std::string &argv_str);
|
||||
|
||||
inline Result RegisterLaunchAsApplet(u64 program_id, u32 la_version, const 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));
|
||||
inline Result RegisterLaunchAsApplet(const u64 program_id, const u32 la_version, const std::string &exefs_path, const void *args, const size_t args_size) {
|
||||
UL_RC_TRY(RegisterExternalContent(program_id, exefs_path));
|
||||
UL_RC_TRY(am::LibraryAppletStart(am::LibraryAppletGetAppletIdForProgramId(program_id), la_version, args, args_size));
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
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));
|
||||
inline Result RegisterLaunchAsApplication(const u64 program_id, const std::string &exefs_path, const void *args, const size_t args_size, const AccountUid uid) {
|
||||
UL_RC_TRY(RegisterExternalContent(program_id, exefs_path));
|
||||
UL_RC_TRY(am::ApplicationStart(program_id, false, uid, args, args_size));
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
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));
|
||||
inline Result RegisterLaunchAsSystemProcess(const u64 program_id, const std::string &exefs_path, const std::string &argv_str) {
|
||||
UL_RC_TRY(RegisterExternalContent(program_id, exefs_path));
|
||||
UL_RC_TRY(LaunchSystemProcess(program_id, argv_str));
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#include <stratosphere.hpp>
|
||||
#include <dmi/dmi_DaemonMenuInteraction.hpp>
|
||||
#include <functional>
|
||||
|
||||
namespace ipc {
|
||||
|
||||
|
@ -41,12 +40,13 @@ namespace ipc {
|
|||
};
|
||||
|
||||
Result Initialize();
|
||||
ServerManager &GetGlobalManager();
|
||||
Allocator &GetServerAllocator();
|
||||
Allocator &GetManagerAllocator();
|
||||
|
||||
ams::Result RegisterSession(const ams::os::NativeHandle session_handle, ams::sf::cmif::ServiceObjectHolder &&obj);
|
||||
|
||||
template<typename Impl, typename T, typename ...Args>
|
||||
inline auto MakeShared(Args ...args) {
|
||||
return ObjectFactory::CreateSharedEmplaced<Impl, T>(std::addressof(GetServerAllocator()), args...);
|
||||
return ObjectFactory::CreateSharedEmplaced<Impl, T>(std::addressof(GetManagerAllocator()), args...);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
#include <ecs/ecs_ExternalContent.hpp>
|
||||
#include <ipc/ipc_GlobalManager.hpp>
|
||||
#include <ipc/ipc_Manager.hpp>
|
||||
#include <db/db_Save.hpp>
|
||||
#include <os/os_Titles.hpp>
|
||||
#include <os/os_HomeMenu.hpp>
|
||||
|
@ -19,17 +19,19 @@ extern "C" {
|
|||
u32 __nx_fsdev_direntry_cache_size = 0;
|
||||
|
||||
// Needed by libnx's usbcomms to allocate internal buffers...
|
||||
// Note: operator new(size_t, std::align_val_t) isn't redefined in ams, so we need to stick with C-style memory functions
|
||||
// (by default that operator internally calls _memalign_r, which isn't redefined by ams either, so it leads to crashes)
|
||||
|
||||
void *__libnx_alloc(size_t size) {
|
||||
return operator new(size);
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void *__libnx_aligned_alloc(size_t align, size_t size) {
|
||||
return operator new(size, std::align_val_t(align));
|
||||
return aligned_alloc(align, size);
|
||||
}
|
||||
|
||||
void __libnx_free(void *ptr) {
|
||||
return operator delete(ptr);
|
||||
return free(ptr);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -43,8 +45,8 @@ namespace {
|
|||
|
||||
enum class UsbMode : u32 {
|
||||
Invalid,
|
||||
RawRGBA,
|
||||
JPEG
|
||||
PlainRgba,
|
||||
Jpeg
|
||||
};
|
||||
|
||||
AccountUid g_SelectedUser = {};
|
||||
|
@ -61,14 +63,17 @@ namespace {
|
|||
u8 *g_UsbViewerBuffer = nullptr;
|
||||
u8 *g_UsbViewerReadBuffer = nullptr;
|
||||
cfg::Config g_Config = {};
|
||||
|
||||
constexpr size_t UsbViewerThreadStackSize = 16_KB;
|
||||
ams::os::ThreadType g_UsbViewerThread;
|
||||
alignas(ams::os::ThreadStackAlignment) u8 g_UsbViewerThreadStack[0x4000];
|
||||
alignas(ams::os::ThreadStackAlignment) u8 g_UsbViewerThreadStack[UsbViewerThreadStackSize];
|
||||
|
||||
UsbMode g_UsbViewerMode = UsbMode::Invalid;
|
||||
SetSysFirmwareVersion g_FwVersion = {};
|
||||
|
||||
// In the USB packet, the first u32 stores the USB mode (raw RGBA or JPEG, depending on what the console supports)
|
||||
// In the USB packet, the first u32 stores the USB mode (plain RGBA or JPEG, depending on what the console supports)
|
||||
|
||||
constexpr size_t UsbPacketSize = RawRGBAScreenBufferSize + sizeof(UsbMode);
|
||||
constexpr size_t UsbPacketSize = PlainRgbaScreenBufferSize + sizeof(UsbMode);
|
||||
|
||||
constexpr size_t HeapSize = 10_MB;
|
||||
alignas(ams::os::MemoryPageSize) constinit u8 g_HeapBuffer[HeapSize];
|
||||
|
@ -78,8 +83,9 @@ namespace {
|
|||
namespace {
|
||||
|
||||
dmi::DaemonStatus CreateStatus() {
|
||||
dmi::DaemonStatus status = {};
|
||||
status.selected_user = g_SelectedUser;
|
||||
dmi::DaemonStatus status = {
|
||||
.selected_user = g_SelectedUser
|
||||
};
|
||||
|
||||
memcpy(status.fw_version, g_FwVersion.display_version, sizeof(status.fw_version));
|
||||
|
||||
|
@ -101,24 +107,20 @@ namespace {
|
|||
appletStartSleepSequence(true);
|
||||
}
|
||||
|
||||
inline Result LaunchMenu(dmi::MenuStartMode st_mode, dmi::DaemonStatus status) {
|
||||
u64 menu_program_id;
|
||||
UL_ASSERT_TRUE(g_Config.GetEntry(cfg::ConfigEntryId::MenuTakeoverProgramId, menu_program_id));
|
||||
return ecs::RegisterLaunchAsApplet(menu_program_id, static_cast<u32>(st_mode), "/ulaunch/bin/uMenu", &status, sizeof(status));
|
||||
inline Result LaunchMenu(const dmi::MenuStartMode st_mode, const dmi::DaemonStatus &status) {
|
||||
return ecs::RegisterLaunchAsApplet(am::LibraryAppletGetMenuProgramId(), static_cast<u32>(st_mode), "/ulaunch/bin/uMenu", std::addressof(status), sizeof(status));
|
||||
}
|
||||
|
||||
void HandleHomeButton() {
|
||||
if(am::LibraryAppletIsActive() && !am::LibraryAppletIsMenu()) {
|
||||
// An applet is opened (which is not our menu), thus close it and reopen the menu
|
||||
am::LibraryAppletTerminate();
|
||||
auto status = CreateStatus();
|
||||
UL_ASSERT(LaunchMenu(dmi::MenuStartMode::Menu, status));
|
||||
UL_RC_ASSERT(LaunchMenu(dmi::MenuStartMode::Menu, CreateStatus()));
|
||||
}
|
||||
else if(am::ApplicationIsActive() && am::ApplicationHasForeground()) {
|
||||
// Hide the application currently on focus and open our menu
|
||||
am::HomeMenuSetForeground();
|
||||
auto status = CreateStatus();
|
||||
UL_ASSERT(LaunchMenu(dmi::MenuStartMode::MenuApplicationSuspended, status));
|
||||
UL_RC_ASSERT(LaunchMenu(dmi::MenuStartMode::MenuApplicationSuspended, CreateStatus()));
|
||||
}
|
||||
else if(am::LibraryAppletIsMenu()) {
|
||||
// Send a message to our menu to handle itself the home press
|
||||
|
@ -129,11 +131,11 @@ namespace {
|
|||
|
||||
Result HandleGeneralChannel() {
|
||||
AppletStorage sams_st;
|
||||
R_TRY(appletPopFromGeneralChannel(&sams_st));
|
||||
UL_RC_TRY(appletPopFromGeneralChannel(&sams_st));
|
||||
UL_ON_SCOPE_EXIT({ appletStorageClose(&sams_st); });
|
||||
|
||||
os::SystemAppletMessage sams = {};
|
||||
R_TRY(appletStorageRead(&sams_st, 0, &sams, sizeof(sams)));
|
||||
UL_RC_TRY(appletStorageRead(&sams_st, 0, &sams, sizeof(sams)));
|
||||
if(sams.magic == os::SystemAppletMessage::Magic) {
|
||||
switch(sams.general_channel_message) {
|
||||
// Usually this doesn't happen, HOME is detected by applet messages...?
|
||||
|
@ -167,7 +169,7 @@ namespace {
|
|||
// 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));
|
||||
UL_RC_TRY(serviceDispatchOut(appletGetServiceSession_CommonStateGetter(), 5, tmp_mode));
|
||||
|
||||
g_OperationMode = static_cast<AppletOperationMode>(tmp_mode);
|
||||
return ResultSuccess;
|
||||
|
@ -175,7 +177,7 @@ namespace {
|
|||
|
||||
Result HandleAppletMessage() {
|
||||
u32 raw_msg = 0;
|
||||
R_TRY(appletGetMessage(&raw_msg));
|
||||
UL_RC_TRY(appletGetMessage(&raw_msg));
|
||||
|
||||
const auto msg = static_cast<os::AppletMessage>(raw_msg);
|
||||
switch(msg) {
|
||||
|
@ -207,15 +209,15 @@ namespace {
|
|||
if(am::LibraryAppletIsMenu()) {
|
||||
char web_url[500] = {};
|
||||
u64 app_id = 0;
|
||||
hb::HbTargetParams ipt = {};
|
||||
dmi::dmn::ReceiveCommand([&](dmi::DaemonMessage msg, dmi::dmn::DaemonScopedStorageReader &reader) -> Result {
|
||||
hb::HbTargetParams params = {};
|
||||
dmi::dmn::ReceiveCommand([&](const dmi::DaemonMessage msg, dmi::dmn::DaemonScopedStorageReader &reader) -> Result {
|
||||
switch(msg) {
|
||||
case dmi::DaemonMessage::SetSelectedUser: {
|
||||
R_TRY(reader.Pop(g_SelectedUser));
|
||||
UL_RC_TRY(reader.Pop(g_SelectedUser));
|
||||
break;
|
||||
}
|
||||
case dmi::DaemonMessage::LaunchApplication: {
|
||||
R_TRY(reader.Pop(app_id));
|
||||
UL_RC_TRY(reader.Pop(app_id));
|
||||
break;
|
||||
}
|
||||
case dmi::DaemonMessage::ResumeApplication: {
|
||||
|
@ -227,16 +229,16 @@ namespace {
|
|||
break;
|
||||
}
|
||||
case dmi::DaemonMessage::LaunchHomebrewLibraryApplet: {
|
||||
R_TRY(reader.Pop(g_HbTargetLaunchFlag));
|
||||
UL_RC_TRY(reader.Pop(g_HbTargetLaunchFlag));
|
||||
break;
|
||||
}
|
||||
case dmi::DaemonMessage::LaunchHomebrewApplication: {
|
||||
R_TRY(reader.Pop(app_id));
|
||||
R_TRY(reader.Pop(ipt));
|
||||
UL_RC_TRY(reader.Pop(app_id));
|
||||
UL_RC_TRY(reader.Pop(params));
|
||||
break;
|
||||
}
|
||||
case dmi::DaemonMessage::OpenWebPage: {
|
||||
R_TRY(reader.PopData(web_url, sizeof(web_url)));
|
||||
UL_RC_TRY(reader.PopData(web_url, sizeof(web_url)));
|
||||
break;
|
||||
}
|
||||
case dmi::DaemonMessage::OpenAlbum: {
|
||||
|
@ -254,7 +256,7 @@ namespace {
|
|||
}
|
||||
return ResultSuccess;
|
||||
},
|
||||
[&](dmi::DaemonMessage msg, dmi::dmn::DaemonScopedStorageWriter &writer) -> Result {
|
||||
[&](const dmi::DaemonMessage msg, dmi::dmn::DaemonScopedStorageWriter &writer) -> Result {
|
||||
switch(msg) {
|
||||
case dmi::DaemonMessage::SetSelectedUser: {
|
||||
// ...
|
||||
|
@ -302,8 +304,8 @@ namespace {
|
|||
return dmn::ResultAlreadyQueued;
|
||||
}
|
||||
|
||||
g_HbTargetApplicationLaunchFlag = ipt;
|
||||
g_HbTargetApplicationLaunchFlagCopy = ipt;
|
||||
g_HbTargetApplicationLaunchFlag = params;
|
||||
g_HbTargetApplicationLaunchFlagCopy = params;
|
||||
g_ApplicationLaunchFlag = app_id;
|
||||
break;
|
||||
}
|
||||
|
@ -330,10 +332,10 @@ namespace {
|
|||
}
|
||||
}
|
||||
|
||||
void UsbViewerRGBAThread(void*) {
|
||||
void UsbViewerRgbaThread(void*) {
|
||||
while(true) {
|
||||
bool tmp_flag;
|
||||
appletGetLastForegroundCaptureImageEx(g_UsbViewerReadBuffer, RawRGBAScreenBufferSize, &tmp_flag);
|
||||
appletGetLastForegroundCaptureImageEx(g_UsbViewerReadBuffer, PlainRgbaScreenBufferSize, &tmp_flag);
|
||||
appletUpdateLastForegroundCaptureImage();
|
||||
usbCommsWrite(g_UsbViewerBuffer, UsbPacketSize);
|
||||
}
|
||||
|
@ -342,21 +344,23 @@ namespace {
|
|||
void UsbViewerJPEGThread(void*) {
|
||||
while(true) {
|
||||
u64 tmp_size;
|
||||
capsscCaptureJpegScreenShot(&tmp_size, g_UsbViewerReadBuffer, RawRGBAScreenBufferSize, ViLayerStack_Default, UINT64_MAX);
|
||||
capsscCaptureJpegScreenShot(&tmp_size, g_UsbViewerReadBuffer, PlainRgbaScreenBufferSize, ViLayerStack_Default, UINT64_MAX);
|
||||
usbCommsWrite(g_UsbViewerBuffer, UsbPacketSize);
|
||||
}
|
||||
}
|
||||
|
||||
void PrepareUsbViewer() {
|
||||
g_UsbViewerBuffer = new (std::align_val_t(0x1000)) u8[UsbPacketSize]();
|
||||
g_UsbViewerBuffer = reinterpret_cast<u8*>(__libnx_aligned_alloc(ams::os::MemoryPageSize, UsbPacketSize));
|
||||
memset(g_UsbViewerBuffer, 0, UsbPacketSize);
|
||||
|
||||
// Skip the first u32 of the buffer, since the mode is stored there
|
||||
g_UsbViewerReadBuffer = g_UsbViewerBuffer + sizeof(UsbMode);
|
||||
u64 tmp_size;
|
||||
if(R_SUCCEEDED(capsscCaptureJpegScreenShot(&tmp_size, g_UsbViewerReadBuffer, RawRGBAScreenBufferSize, ViLayerStack_Default, UINT64_MAX))) {
|
||||
g_UsbViewerMode = UsbMode::JPEG;
|
||||
if(R_SUCCEEDED(capsscCaptureJpegScreenShot(&tmp_size, g_UsbViewerReadBuffer, PlainRgbaScreenBufferSize, ViLayerStack_Default, UINT64_MAX))) {
|
||||
g_UsbViewerMode = UsbMode::Jpeg;
|
||||
}
|
||||
else {
|
||||
g_UsbViewerMode = UsbMode::RawRGBA;
|
||||
g_UsbViewerMode = UsbMode::PlainRgba;
|
||||
capsscExit();
|
||||
}
|
||||
*reinterpret_cast<UsbMode*>(g_UsbViewerBuffer) = g_UsbViewerMode;
|
||||
|
@ -367,12 +371,12 @@ namespace {
|
|||
HandleAppletMessage();
|
||||
HandleMenuMessage();
|
||||
|
||||
bool sth_done = false;
|
||||
auto sth_done = false;
|
||||
// A valid version will always be >= 0x20000
|
||||
if(g_WebAppletLaunchFlag.version > 0) {
|
||||
if(!am::LibraryAppletIsActive()) {
|
||||
// TODO: applet startup sound?
|
||||
UL_ASSERT(am::WebAppletStart(&g_WebAppletLaunchFlag));
|
||||
UL_RC_ASSERT(am::WebAppletStart(&g_WebAppletLaunchFlag));
|
||||
|
||||
sth_done = true;
|
||||
g_WebAppletLaunchFlag = {};
|
||||
|
@ -380,8 +384,7 @@ namespace {
|
|||
}
|
||||
if(g_MenuRestartFlag) {
|
||||
if(!am::LibraryAppletIsActive()) {
|
||||
auto status = CreateStatus();
|
||||
UL_ASSERT(LaunchMenu(dmi::MenuStartMode::StartupScreen, status));
|
||||
UL_RC_ASSERT(LaunchMenu(dmi::MenuStartMode::StartupScreen, CreateStatus()));
|
||||
|
||||
sth_done = true;
|
||||
g_MenuRestartFlag = false;
|
||||
|
@ -393,7 +396,7 @@ namespace {
|
|||
u8 album_arg;
|
||||
} album_data = { AlbumLaArg_ShowAllAlbumFilesForHomeMenu };
|
||||
// TODO: applet startup sound?
|
||||
UL_ASSERT(am::LibraryAppletStart(AppletId_LibraryAppletPhotoViewer, 0x10000, &album_data, sizeof(album_data)));
|
||||
UL_RC_ASSERT(am::LibraryAppletStart(AppletId_LibraryAppletPhotoViewer, 0x10000, &album_data, sizeof(album_data)));
|
||||
|
||||
sth_done = true;
|
||||
g_AlbumAppletLaunchFlag = false;
|
||||
|
@ -402,16 +405,16 @@ namespace {
|
|||
if(g_ApplicationLaunchFlag > 0) {
|
||||
if(!am::LibraryAppletIsActive()) {
|
||||
if(strlen(g_HbTargetApplicationLaunchFlag.nro_path)) {
|
||||
auto params = hb::HbTargetParams::Create(g_HbTargetApplicationLaunchFlag.nro_path, g_HbTargetApplicationLaunchFlag.nro_argv, false);
|
||||
UL_ASSERT(ecs::RegisterLaunchAsApplication(g_ApplicationLaunchFlag, "/ulaunch/bin/uHbTarget/app", ¶ms, sizeof(params), g_SelectedUser));
|
||||
const auto params = hb::HbTargetParams::Create(g_HbTargetApplicationLaunchFlag.nro_path, g_HbTargetApplicationLaunchFlag.nro_argv, false);
|
||||
UL_RC_ASSERT(ecs::RegisterLaunchAsApplication(g_ApplicationLaunchFlag, "/ulaunch/bin/uHbTarget/app", ¶ms, sizeof(params), g_SelectedUser));
|
||||
|
||||
g_HbTargetOpenedAsApplication = true;
|
||||
g_HbTargetApplicationLaunchFlag.nro_path[0] = '\0';
|
||||
}
|
||||
else {
|
||||
// Ensure the application is launchable
|
||||
UL_ASSERT(nsTouchApplication(g_ApplicationLaunchFlag));
|
||||
UL_ASSERT(am::ApplicationStart(g_ApplicationLaunchFlag, false, g_SelectedUser));
|
||||
UL_RC_ASSERT(nsTouchApplication(g_ApplicationLaunchFlag));
|
||||
UL_RC_ASSERT(am::ApplicationStart(g_ApplicationLaunchFlag, false, g_SelectedUser));
|
||||
}
|
||||
sth_done = true;
|
||||
g_ApplicationLaunchFlag = 0;
|
||||
|
@ -419,22 +422,21 @@ namespace {
|
|||
}
|
||||
if(strlen(g_HbTargetLaunchFlag.nro_path)) {
|
||||
if(!am::LibraryAppletIsActive()) {
|
||||
auto params = hb::HbTargetParams::Create(g_HbTargetLaunchFlag.nro_path, g_HbTargetLaunchFlag.nro_argv, false);
|
||||
const auto params = hb::HbTargetParams::Create(g_HbTargetLaunchFlag.nro_path, g_HbTargetLaunchFlag.nro_argv, false);
|
||||
u64 homebrew_applet_program_id;
|
||||
UL_ASSERT_TRUE(g_Config.GetEntry(cfg::ConfigEntryId::HomebrewAppletTakeoverProgramId, homebrew_applet_program_id));
|
||||
UL_ASSERT(ecs::RegisterLaunchAsApplet(homebrew_applet_program_id, 0, "/ulaunch/bin/uHbTarget/applet", ¶ms, sizeof(params)));
|
||||
UL_RC_ASSERT(ecs::RegisterLaunchAsApplet(homebrew_applet_program_id, 0, "/ulaunch/bin/uHbTarget/applet", ¶ms, sizeof(params)));
|
||||
|
||||
sth_done = true;
|
||||
g_HbTargetLaunchFlag.nro_path[0] = '\0';
|
||||
}
|
||||
}
|
||||
if(!am::LibraryAppletIsActive()) {
|
||||
auto cur_id = am::LibraryAppletGetId();
|
||||
const auto cur_id = am::LibraryAppletGetId();
|
||||
u64 homebrew_applet_program_id;
|
||||
UL_ASSERT_TRUE(g_Config.GetEntry(cfg::ConfigEntryId::HomebrewAppletTakeoverProgramId, homebrew_applet_program_id));
|
||||
if((cur_id == AppletId_LibraryAppletWeb) || (cur_id == AppletId_LibraryAppletPhotoViewer) || (cur_id == homebrew_applet_program_id)) {
|
||||
auto status = CreateStatus();
|
||||
UL_ASSERT(LaunchMenu(dmi::MenuStartMode::Menu, status));
|
||||
UL_RC_ASSERT(LaunchMenu(dmi::MenuStartMode::Menu, CreateStatus()));
|
||||
|
||||
sth_done = true;
|
||||
}
|
||||
|
@ -448,26 +450,26 @@ namespace {
|
|||
// Throw the application's result if it actually ended with a result
|
||||
auto terminate_rc = ResultSuccess;
|
||||
if(R_SUCCEEDED(nsGetApplicationTerminateResult(am::ApplicationGetId(), &terminate_rc))) {
|
||||
UL_ASSERT(terminate_rc);
|
||||
UL_RC_ASSERT(terminate_rc);
|
||||
}
|
||||
|
||||
// Reopen uMenu in launch-error mode
|
||||
auto status = CreateStatus();
|
||||
UL_ASSERT(LaunchMenu(dmi::MenuStartMode::MenuLaunchFailure, status));
|
||||
UL_RC_ASSERT(LaunchMenu(dmi::MenuStartMode::MenuLaunchFailure, CreateStatus()));
|
||||
g_HbTargetOpenedAsApplication = false;
|
||||
}
|
||||
}
|
||||
|
||||
svcSleepThread(10'000'000ul);
|
||||
}
|
||||
|
||||
Result LaunchUsbViewerThread() {
|
||||
void(*thread_entry)(void*) = nullptr;
|
||||
switch(g_UsbViewerMode) {
|
||||
case UsbMode::RawRGBA: {
|
||||
thread_entry = &UsbViewerRGBAThread;
|
||||
case UsbMode::PlainRgba: {
|
||||
thread_entry = &UsbViewerRgbaThread;
|
||||
break;
|
||||
}
|
||||
case UsbMode::JPEG: {
|
||||
case UsbMode::Jpeg: {
|
||||
thread_entry = &UsbViewerJPEGThread;
|
||||
break;
|
||||
}
|
||||
|
@ -475,19 +477,19 @@ namespace {
|
|||
return ResultSuccess;
|
||||
}
|
||||
|
||||
R_TRY(ams::os::CreateThread(&g_UsbViewerThread, thread_entry, nullptr, g_UsbViewerThreadStack, sizeof(g_UsbViewerThreadStack), 10).GetValue());
|
||||
UL_RC_TRY(ams::os::CreateThread(&g_UsbViewerThread, thread_entry, nullptr, g_UsbViewerThreadStack, sizeof(g_UsbViewerThreadStack), 10));
|
||||
ams::os::StartThread(&g_UsbViewerThread);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void Initialize() {
|
||||
UL_ASSERT(appletLoadAndApplyIdlePolicySettings());
|
||||
UL_RC_ASSERT(appletLoadAndApplyIdlePolicySettings());
|
||||
UpdateOperationMode();
|
||||
|
||||
UL_ASSERT(setsysGetFirmwareVersion(&g_FwVersion));
|
||||
UL_RC_ASSERT(setsysGetFirmwareVersion(&g_FwVersion));
|
||||
|
||||
UL_ASSERT(db::Mount());
|
||||
UL_RC_ASSERT(db::Mount());
|
||||
|
||||
// Remove last present error report
|
||||
fs::DeleteFile(UL_ASSERTION_LOG_FILE);
|
||||
|
@ -515,25 +517,24 @@ namespace {
|
|||
bool viewer_usb_enabled;
|
||||
UL_ASSERT_TRUE(g_Config.GetEntry(cfg::ConfigEntryId::ViewerUsbEnabled, viewer_usb_enabled));
|
||||
if(viewer_usb_enabled) {
|
||||
UL_ASSERT(usbCommsInitialize());
|
||||
UL_ASSERT(capsscInitialize());
|
||||
UL_RC_ASSERT(usbCommsInitialize());
|
||||
UL_RC_ASSERT(capsscInitialize());
|
||||
|
||||
PrepareUsbViewer();
|
||||
UL_ASSERT(LaunchUsbViewerThread());
|
||||
UL_RC_ASSERT(LaunchUsbViewerThread());
|
||||
}
|
||||
|
||||
UL_ASSERT(ipc::Initialize());
|
||||
UL_RC_ASSERT(ipc::Initialize());
|
||||
}
|
||||
|
||||
void Finalize() {
|
||||
bool viewer_usb_enabled;
|
||||
UL_ASSERT_TRUE(g_Config.GetEntry(cfg::ConfigEntryId::ViewerUsbEnabled, viewer_usb_enabled));
|
||||
if(viewer_usb_enabled) {
|
||||
if(g_UsbViewerBuffer != nullptr) {
|
||||
usbCommsExit();
|
||||
if(g_UsbViewerMode == UsbMode::JPEG) {
|
||||
if(g_UsbViewerMode == UsbMode::Jpeg) {
|
||||
capsscExit();
|
||||
}
|
||||
operator delete[](g_UsbViewerBuffer, std::align_val_t(0x1000));
|
||||
__libnx_free(g_UsbViewerBuffer);
|
||||
g_UsbViewerBuffer = nullptr;
|
||||
}
|
||||
|
||||
nsExit();
|
||||
|
@ -553,20 +554,17 @@ namespace ams {
|
|||
|
||||
void InitializeSystemModule() {
|
||||
__nx_applet_type = AppletType_SystemApplet;
|
||||
UL_AMS_ASSERT(sm::Initialize());
|
||||
UL_RC_ASSERT(sm::Initialize());
|
||||
|
||||
UL_ASSERT(appletInitialize());
|
||||
UL_ASSERT(fsInitialize());
|
||||
UL_ASSERT(nsInitialize());
|
||||
UL_ASSERT(pminfoInitialize());
|
||||
UL_ASSERT(ldrShellInitialize());
|
||||
UL_ASSERT(pmshellInitialize());
|
||||
UL_ASSERT(setsysInitialize());
|
||||
UL_RC_ASSERT(appletInitialize());
|
||||
UL_RC_ASSERT(fsInitialize());
|
||||
UL_RC_ASSERT(nsInitialize());
|
||||
UL_RC_ASSERT(pminfoInitialize());
|
||||
UL_RC_ASSERT(ldrShellInitialize());
|
||||
UL_RC_ASSERT(pmshellInitialize());
|
||||
UL_RC_ASSERT(setsysInitialize());
|
||||
|
||||
fsdevMountSdmc();
|
||||
|
||||
// TODO: disabling this doesn't really avoid ams aborting with new fws, shall we try to avoid that in a different way?
|
||||
/* ams::CheckApiVersion(); */
|
||||
}
|
||||
|
||||
void FinalizeSystemModule() {}
|
||||
|
@ -574,6 +572,8 @@ namespace ams {
|
|||
void Startup() {
|
||||
// Initialize the global malloc-free/new-delete allocator
|
||||
init::InitializeAllocator(g_HeapBuffer, HeapSize);
|
||||
|
||||
os::SetThreadNamePointer(os::GetCurrentThread(), "ul.daemon.Main");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -590,8 +590,7 @@ namespace ams {
|
|||
Initialize();
|
||||
|
||||
// After having initialized everything, launch our menu
|
||||
auto status = CreateStatus();
|
||||
UL_ASSERT(LaunchMenu(dmi::MenuStartMode::StartupScreen, status));
|
||||
UL_RC_ASSERT(LaunchMenu(dmi::MenuStartMode::StartupScreen, CreateStatus()));
|
||||
|
||||
// Loop forever, since qlaunch should NEVER terminate (AM would crash in that case)
|
||||
while(true) {
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
#include <ecs/ecs_ExternalContent.hpp>
|
||||
#include <ipc/ipc_GlobalManager.hpp>
|
||||
#include <am/am_LibraryApplet.hpp>
|
||||
#include <am/am_HomeMenu.hpp>
|
||||
#include <ipc/ipc_Manager.hpp>
|
||||
#include <stratosphere/fssrv/fssrv_interface_adapters.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
inline Result ldrShellAtmosphereRegisterExternalCode(u64 app_id, Handle *out_h) {
|
||||
inline Result ldrShellAtmosphereRegisterExternalCode(const u64 app_id, Handle *out_h) {
|
||||
return serviceDispatchIn(ldrShellGetServiceSession(), 65000, app_id,
|
||||
.out_handle_attrs = { SfOutHandleAttr_HipcMove },
|
||||
.out_handles = out_h,
|
||||
|
@ -17,38 +15,29 @@ namespace {
|
|||
|
||||
namespace ecs {
|
||||
|
||||
Result RegisterExternalContent(u64 app_id, const std::string &exefs_path) {
|
||||
Result RegisterExternalContent(const u64 app_id, const std::string &exefs_path) {
|
||||
auto move_h = INVALID_HANDLE;
|
||||
R_TRY(ldrShellAtmosphereRegisterExternalCode(app_id, &move_h));
|
||||
UL_RC_TRY(ldrShellAtmosphereRegisterExternalCode(app_id, &move_h));
|
||||
|
||||
FsFileSystem sd_fs;
|
||||
R_TRY(fsOpenSdCardFileSystem(&sd_fs));
|
||||
UL_RC_TRY(fsOpenSdCardFileSystem(&sd_fs));
|
||||
std::unique_ptr<ams::fs::fsa::IFileSystem> remote_sd_fs = std::make_unique<ams::fs::RemoteFileSystem>(sd_fs);
|
||||
auto subdir_fs = std::make_shared<ams::fssystem::SubDirectoryFileSystem>(std::move(remote_sd_fs), exefs_path.c_str());
|
||||
auto sd_ifs_ipc = ipc::MakeShared<ams::fssrv::sf::IFileSystem, ams::fssrv::impl::FileSystemInterfaceAdapter>(std::move(subdir_fs), false);
|
||||
|
||||
R_TRY(ipc::GetGlobalManager().RegisterSession(move_h, ams::sf::cmif::ServiceObjectHolder(std::move(sd_ifs_ipc))).GetValue());
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result LaunchApplet(u64 program_id, u32 la_version, void *args, size_t args_size) {
|
||||
auto appletid = am::LibraryAppletGetAppletIdForProgramId(program_id);
|
||||
if(appletid == 0) {
|
||||
return 0xDEAD;
|
||||
}
|
||||
R_TRY(am::LibraryAppletStart(appletid, la_version, args, args_size));
|
||||
UL_RC_TRY(ipc::RegisterSession(move_h, ams::sf::cmif::ServiceObjectHolder(std::move(sd_ifs_ipc))));
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result LaunchSystemProcess(u64 program_id, const std::string &argv_str) {
|
||||
R_TRY(ldrShellSetProgramArguments(program_id, argv_str.c_str(), argv_str.length()));
|
||||
Result LaunchSystemProcess(const u64 program_id, const std::string &argv_str) {
|
||||
UL_RC_TRY(ldrShellSetProgramArguments(program_id, argv_str.c_str(), argv_str.length()));
|
||||
NcmProgramLocation loc = {
|
||||
.program_id = program_id,
|
||||
.storageID = NcmStorageId_BuiltInSystem,
|
||||
.storageID = NcmStorageId_BuiltInSystem
|
||||
};
|
||||
|
||||
u64 pid;
|
||||
R_TRY(pmshellLaunchProgram(0, &loc, &pid));
|
||||
UL_RC_TRY(pmshellLaunchProgram(0, &loc, &pid));
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
#include <ipc/ipc_GlobalManager.hpp>
|
||||
#include <ipc/ipc_IPrivateService.hpp>
|
||||
// IPublicService...
|
||||
|
||||
namespace {
|
||||
|
||||
ipc::ServerManager g_GlobalManager;
|
||||
ams::os::ThreadType g_GlobalManagerThread;
|
||||
alignas(ams::os::ThreadStackAlignment) u8 g_GlobalManagerThreadStack[0x8000];
|
||||
ams::os::Mutex g_ServerAllocatorLock(false);
|
||||
|
||||
void GlobalManagerThread(void*) {
|
||||
UL_AMS_ASSERT(g_GlobalManager.RegisterServer(ipc::Port_PrivateService, ipc::PrivateServiceName, ipc::MaxPrivateSessions));
|
||||
// UL_ASSERT(g_GlobalManager.RegisterServer<ipc::IPublicService>(PublicServiceName, MaxPublicSessions).GetValue());
|
||||
|
||||
g_GlobalManager.LoopProcess();
|
||||
}
|
||||
|
||||
alignas(0x40) constinit u8 g_server_allocator_buffer[0x8000];
|
||||
ams::lmem::HeapHandle g_server_heap_handle;
|
||||
ipc::Allocator g_server_allocator;
|
||||
|
||||
void InitializeHeap() {
|
||||
g_server_heap_handle = ams::lmem::CreateExpHeap(g_server_allocator_buffer, sizeof(g_server_allocator_buffer), ams::lmem::CreateOption_None);
|
||||
g_server_allocator.Attach(g_server_heap_handle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace ipc {
|
||||
|
||||
ams::Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
|
||||
switch(port_index) {
|
||||
case Port_PrivateService: {
|
||||
return this->AcceptImpl(server, MakeShared<ams::sf::ul::IPrivateService, ipc::PrivateService>());
|
||||
}
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
::Result Initialize() {
|
||||
InitializeHeap();
|
||||
R_TRY(ams::os::CreateThread(&g_GlobalManagerThread, &GlobalManagerThread, nullptr, g_GlobalManagerThreadStack, sizeof(g_GlobalManagerThreadStack), 10).GetValue());
|
||||
ams::os::StartThread(&g_GlobalManagerThread);
|
||||
|
||||
return ::ResultSuccess;
|
||||
}
|
||||
|
||||
ServerManager &GetGlobalManager() {
|
||||
return g_GlobalManager;
|
||||
}
|
||||
|
||||
Allocator &GetServerAllocator() {
|
||||
std::scoped_lock lk(g_ServerAllocatorLock);
|
||||
return g_server_allocator;
|
||||
}
|
||||
|
||||
}
|
|
@ -10,15 +10,15 @@ namespace ipc {
|
|||
ams::Result PrivateService::Initialize(const ams::sf::ClientProcessId &client_pid) {
|
||||
if(!this->initialized) {
|
||||
u64 program_id = 0;
|
||||
R_TRY(pminfoGetProgramId(&program_id, client_pid.process_id.value));
|
||||
|
||||
const auto last_menu_program_id = am::LibraryAppletGetProgramIdForAppletId(am::LibraryAppletGetMenuAppletId());
|
||||
UL_RC_TRY(pminfoGetProgramId(&program_id, client_pid.process_id.value));
|
||||
|
||||
const auto last_menu_program_id = am::LibraryAppletGetMenuProgramId();
|
||||
// If Menu hasn't been launched it's program ID will be 0 (invalid), thus a single (program_id != last_menu_program_id) check isn't 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 ipc::ResultInvalidProcess;
|
||||
}
|
||||
|
||||
|
||||
this->initialized = true;
|
||||
}
|
||||
|
||||
|
|
63
uDaemon/source/ipc/ipc_Manager.cpp
Normal file
63
uDaemon/source/ipc/ipc_Manager.cpp
Normal file
|
@ -0,0 +1,63 @@
|
|||
#include <ipc/ipc_Manager.hpp>
|
||||
#include <ipc/ipc_IPrivateService.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
ipc::ServerManager g_Manager;
|
||||
|
||||
constexpr size_t IpcManagerThreadStackSize = 32_KB;
|
||||
ams::os::ThreadType g_ManagerThread;
|
||||
alignas(ams::os::ThreadStackAlignment) u8 g_ManagerThreadStack[IpcManagerThreadStackSize];
|
||||
|
||||
ams::os::Mutex g_ManagerAllocatorLock(false);
|
||||
|
||||
constexpr size_t ServerAllocatorHeapSize = 32_KB;
|
||||
alignas(0x40) constinit u8 g_ManagerAllocatorHeap[ServerAllocatorHeapSize];
|
||||
ams::lmem::HeapHandle g_ManagerAllocatorHeapHandle;
|
||||
ipc::Allocator g_ManagerAllocator;
|
||||
|
||||
void IpcManagerThread(void*) {
|
||||
ams::os::SetThreadNamePointer(ams::os::GetCurrentThread(), "ul.daemon.IpcManager");
|
||||
|
||||
UL_RC_ASSERT(g_Manager.RegisterServer(ipc::Port_PrivateService, ipc::PrivateServiceName, ipc::MaxPrivateSessions));
|
||||
// UL_RC_ASSERT(g_Manager.RegisterServer<ipc::IPublicService>(PublicServiceName, MaxPublicSessions));
|
||||
|
||||
g_Manager.LoopProcess();
|
||||
}
|
||||
|
||||
void InitializeHeap() {
|
||||
g_ManagerAllocatorHeapHandle = ams::lmem::CreateExpHeap(g_ManagerAllocatorHeap, sizeof(g_ManagerAllocatorHeap), ams::lmem::CreateOption_None);
|
||||
g_ManagerAllocator.Attach(g_ManagerAllocatorHeapHandle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace ipc {
|
||||
|
||||
ams::Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
|
||||
switch(port_index) {
|
||||
case Port_PrivateService: {
|
||||
return this->AcceptImpl(server, MakeShared<ams::sf::ul::IPrivateService, ipc::PrivateService>());
|
||||
}
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
Result Initialize() {
|
||||
InitializeHeap();
|
||||
UL_RC_TRY(ams::os::CreateThread(&g_ManagerThread, &IpcManagerThread, nullptr, g_ManagerThreadStack, sizeof(g_ManagerThreadStack), 10));
|
||||
ams::os::StartThread(&g_ManagerThread);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Allocator &GetManagerAllocator() {
|
||||
std::scoped_lock lk(g_ManagerAllocatorLock);
|
||||
return g_ManagerAllocator;
|
||||
}
|
||||
|
||||
ams::Result RegisterSession(const ams::os::NativeHandle session_handle, ams::sf::cmif::ServiceObjectHolder &&obj) {
|
||||
return g_Manager.RegisterSession(session_handle, std::move(obj));
|
||||
}
|
||||
|
||||
}
|
|
@ -11,79 +11,25 @@ extern "C" {
|
|||
u32 __nx_fsdev_direntry_cache_size = 1;
|
||||
bool __nx_fsdev_support_cwd = false;
|
||||
|
||||
void hb_hbl_Target(Handle process_handle, const char *path, const char *argv, int do_once);
|
||||
void hb_hbl_Target(const Handle process_handle, const char *path, const char *argv, const int do_once);
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
inline Result DoWithSmSession(std::function<Result()> fn) {
|
||||
R_TRY(smInitialize());
|
||||
R_TRY(fn());
|
||||
smExit();
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
namespace impl {
|
||||
|
||||
Handle g_process_handle = INVALID_HANDLE;
|
||||
u64 g_process_id = 0;
|
||||
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));
|
||||
|
||||
auto 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
NX_CONSTEXPR bool IsApplet(u64 program_id) {
|
||||
inline constexpr bool IsApplet(const 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); */
|
||||
inline constexpr bool IsApplication(const u64 program_id) {
|
||||
return (0x0100000000010000 <= program_id);
|
||||
}
|
||||
|
||||
NX_CONSTEXPR bool IsSystemProcess(u64 program_id) {
|
||||
inline constexpr bool IsSystemProcess(const u64 program_id) {
|
||||
return (0x0100000000000000 <= program_id) && (program_id <= 0x01000000000007FF);
|
||||
}
|
||||
|
||||
NX_CONSTEXPR AppletType DetectAppletType(u64 program_id) {
|
||||
inline constexpr AppletType DetectAppletType(const u64 program_id) {
|
||||
if(IsApplet(program_id)) {
|
||||
// OverlayApplet and SystemApplet are impossible in this case
|
||||
return AppletType_LibraryApplet;
|
||||
|
@ -92,17 +38,87 @@ namespace {
|
|||
// hbloader uses this instead of normal Application...
|
||||
return AppletType_SystemApplication;
|
||||
}
|
||||
return AppletType_None;
|
||||
else {
|
||||
return AppletType_None;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool TryParseTargetParamsFromStorage(AppletStorage *st, hb::HbTargetParams ¶ms) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
Handle g_ProcessHandle = INVALID_HANDLE;
|
||||
u64 g_ProcessId = 0;
|
||||
u64 g_ProgramId = 0;
|
||||
|
||||
void ProcessInfoReceiver(void *arg) {
|
||||
const auto session = static_cast<Handle>(reinterpret_cast<uintptr_t>(arg));
|
||||
|
||||
auto base = armGetTls();
|
||||
hipcMakeRequestInline(base);
|
||||
|
||||
s32 idx = 0;
|
||||
svcReplyAndReceive(&idx, &session, 1, INVALID_HANDLE, UINT64_MAX);
|
||||
|
||||
const auto request = hipcParseRequest(base);
|
||||
g_ProcessHandle = request.data.copy_handles[0];
|
||||
g_ProcessId = request.pid;
|
||||
svcCloseHandle(session);
|
||||
}
|
||||
|
||||
Result LoadProcessInfo() {
|
||||
Handle server_h;
|
||||
Handle client_h;
|
||||
// Create our own session, and close it on exit if success
|
||||
UL_RC_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
|
||||
UL_RC_TRY(threadCreate(&receiver_thr, &ProcessInfoReceiver, thread_arg, nullptr, 0x1000, 0x2B, -2));
|
||||
UL_ON_SCOPE_EXIT({
|
||||
threadWaitForExit(&receiver_thr);
|
||||
threadClose(&receiver_thr);
|
||||
});
|
||||
|
||||
UL_RC_TRY(threadStart(&receiver_thr));
|
||||
|
||||
hipcMakeRequestInline(armGetTls(),
|
||||
.send_pid = 1,
|
||||
.num_copy_handles = 1,
|
||||
).copy_handles[0] = CUR_PROCESS_HANDLE;
|
||||
|
||||
// Will always fail, since we never actually respond to the request
|
||||
UL_RC_TRY(svcSendSyncRequest(client_h));
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result LoadProgramId() {
|
||||
if(R_SUCCEEDED(svcGetInfo(&g_ProgramId, InfoType_ProgramId, CUR_PROCESS_HANDLE, 0))) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
// Let's try with pminfo
|
||||
UL_RC_TRY(pminfoInitialize());
|
||||
UL_ON_SCOPE_EXIT({ pminfoExit(); });
|
||||
|
||||
UL_RC_TRY(pminfoGetProgramId(&g_ProgramId, g_ProcessId));
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool TryParseTargetParamsFromStorage(AppletStorage *st, hb::HbTargetParams ¶ms) {
|
||||
// Ensure size is correct
|
||||
s64 st_size = 0;
|
||||
R_TRY(appletStorageGetSize(st, &st_size));
|
||||
UL_RC_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)));
|
||||
UL_RC_TRY(appletStorageRead(st, 0, &tmp_params, sizeof(tmp_params)));
|
||||
if(tmp_params.magic == UL_HB_HBTARGET_MAGIC_U32) {
|
||||
params = tmp_params;
|
||||
return true;
|
||||
|
@ -111,65 +127,51 @@ namespace {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ParseTargetParams(hb::HbTargetParams ¶ms, u64 cur_program_id, int argc, char **argv) {
|
||||
bool ret = false;
|
||||
bool ParseTargetParams(const u64 cur_program_id, const int argc, char **argv, hb::HbTargetParams &out_params) {
|
||||
auto 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;
|
||||
}));
|
||||
UL_RC_TRY(appletInitialize());
|
||||
|
||||
// Ensure applet is exited in the end
|
||||
UL_ON_SCOPE_EXIT({
|
||||
appletExit();
|
||||
});
|
||||
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));
|
||||
UL_RC_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);
|
||||
});
|
||||
UL_RC_TRY(appletPopInData(&hbtarget_st));
|
||||
UL_ON_SCOPE_EXIT({ appletStorageClose(&hbtarget_st); });
|
||||
|
||||
// Try parse params
|
||||
ret = TryParseTargetParamsFromStorage(&hbtarget_st, params);
|
||||
ret = TryParseTargetParamsFromStorage(&hbtarget_st, out_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;
|
||||
}));
|
||||
UL_RC_TRY(appletInitialize());
|
||||
|
||||
// Ensure applet is exited in the end
|
||||
UL_ON_SCOPE_EXIT({
|
||||
appletExit();
|
||||
});
|
||||
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);
|
||||
});
|
||||
UL_RC_TRY(appletPopLaunchParameter(&hbtarget_st, AppletLaunchParameterKind_UserChannel));
|
||||
UL_ON_SCOPE_EXIT({ appletStorageClose(&hbtarget_st); });
|
||||
|
||||
// Try parse params
|
||||
ret = TryParseTargetParamsFromStorage(&hbtarget_st, params);
|
||||
ret = TryParseTargetParamsFromStorage(&hbtarget_st, out_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];
|
||||
const std::string magic = argv[0];
|
||||
|
||||
// NRO paths start with 'sdmc:/' and spaces are replaced with 0xFF
|
||||
std::string nro_path = argv[1];
|
||||
|
@ -178,99 +180,61 @@ namespace {
|
|||
std::string nro_argv = argv[2];
|
||||
|
||||
// This must be '0' or '1'.
|
||||
std::string target_once = argv[3];
|
||||
const 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);
|
||||
if((magic == UL_HB_HBTARGET_MAGIC) && !nro_path.empty() && !nro_argv.empty()) {
|
||||
auto target_once_v = true;
|
||||
try {
|
||||
target_once_v = static_cast<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, ' ');
|
||||
std::replace(nro_path.begin(), nro_path.end(), static_cast<char>(0xFF), ' ');
|
||||
std::replace(nro_argv.begin(), nro_argv.end(), static_cast<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(...) {}
|
||||
}
|
||||
strcpy(out_params.nro_path, nro_path.c_str());
|
||||
strcpy(out_params.nro_argv, nro_argv.c_str());
|
||||
out_params.target_once = target_once_v;
|
||||
ret = true;
|
||||
}
|
||||
catch(...) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(ret) {
|
||||
if(strlen(params.nro_path) == 0) {
|
||||
if(strlen(out_params.nro_path) == 0) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
if(ret) {
|
||||
if(strlen(params.nro_argv) == 0) {
|
||||
strcpy(params.nro_argv, params.nro_path);
|
||||
if(strlen(out_params.nro_argv) == 0) {
|
||||
strcpy(out_params.nro_argv, out_params.nro_path);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Result LoadProgramId() {
|
||||
return impl::GetProgramIdImpl();
|
||||
}
|
||||
|
||||
Handle GetOwnProcessHandle() {
|
||||
return impl::g_process_handle;
|
||||
}
|
||||
|
||||
u64 GetOwnProgramId() {
|
||||
return impl::g_program_id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// Avoid checking the result (will always fail), we just care about the process handle
|
||||
LoadProcessInfo();
|
||||
LoadProgramId();
|
||||
if(g_ProcessHandle == INVALID_HANDLE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto program_id = GetOwnProgramId();
|
||||
auto process_handle = GetOwnProcessHandle();
|
||||
UL_RC_ASSERT(smInitialize());
|
||||
UL_RC_ASSERT(LoadProgramId());
|
||||
|
||||
__nx_applet_type = DetectAppletType(program_id);
|
||||
__nx_applet_type = DetectAppletType(g_ProgramId);
|
||||
|
||||
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);
|
||||
const auto params_ok = ParseTargetParams(g_ProgramId, argc, argv, params);
|
||||
smExit();
|
||||
|
||||
if(params_ok) {
|
||||
hb_hbl_Target(g_ProcessHandle, params.nro_path, params.nro_argv, params.target_once);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -6,25 +6,23 @@
|
|||
namespace am {
|
||||
|
||||
struct ApplicationSelectedUserArgument {
|
||||
|
||||
static constexpr u32 SelectedUserMagic = 0xC79497CA;
|
||||
static constexpr u32 Magic = 0xC79497CA;
|
||||
|
||||
u32 magic;
|
||||
u8 unk_1;
|
||||
u8 pad[3];
|
||||
AccountUid uid;
|
||||
u8 unk2[0x3E8];
|
||||
u8 unk_2[0x3E8];
|
||||
|
||||
static inline constexpr ApplicationSelectedUserArgument Create(const AccountUid uid) {
|
||||
ApplicationSelectedUserArgument arg = {};
|
||||
arg.magic = SelectedUserMagic;
|
||||
arg.unk_1 = 1;
|
||||
arg.uid = uid;
|
||||
return arg;
|
||||
return {
|
||||
.magic = Magic,
|
||||
.unk_1 = 1,
|
||||
.uid = uid
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
static_assert(sizeof(ApplicationSelectedUserArgument) == 0x400, "ApplicationSelectedUserArgument");
|
||||
static_assert(sizeof(ApplicationSelectedUserArgument) == 0x400);
|
||||
|
||||
bool ApplicationIsActive();
|
||||
void ApplicationTerminate();
|
||||
|
|
|
@ -5,9 +5,6 @@
|
|||
namespace am {
|
||||
|
||||
bool LibraryAppletIsActive();
|
||||
void LibraryAppletSetMenuAppletId(const AppletId id);
|
||||
AppletId LibraryAppletGetMenuAppletId();
|
||||
bool LibraryAppletIsMenu();
|
||||
void LibraryAppletTerminate();
|
||||
Result LibraryAppletStart(const AppletId id, const u32 la_version, const void *in_data, const size_t in_size);
|
||||
Result LibraryAppletSend(const void *data, const size_t size);
|
||||
|
@ -24,4 +21,16 @@ namespace am {
|
|||
|
||||
AppletId LibraryAppletGetId();
|
||||
|
||||
bool LibraryAppletIsMenu();
|
||||
void LibraryAppletSetMenuAppletId(const AppletId id);
|
||||
AppletId LibraryAppletGetMenuAppletId();
|
||||
|
||||
inline void LibraryAppletSetMenuProgramId(const u64 id) {
|
||||
LibraryAppletSetMenuAppletId(LibraryAppletGetAppletIdForProgramId(id));
|
||||
}
|
||||
|
||||
inline u64 LibraryAppletGetMenuProgramId() {
|
||||
return LibraryAppletGetProgramIdForAppletId(LibraryAppletGetMenuAppletId());
|
||||
}
|
||||
|
||||
}
|
|
@ -15,21 +15,21 @@ namespace cfg {
|
|||
|
||||
struct TitleRecord {
|
||||
std::string json_name; // Empty for non-SD, normal title records
|
||||
u32 title_type;
|
||||
TitleType title_type; // Title type
|
||||
std::string sub_folder; // Empty for root, name for a certain folder
|
||||
std::string icon; // Custom icon, if specified
|
||||
|
||||
u64 app_id; // TitleType::Installed
|
||||
hb::HbTargetParams nro_target; // TitleType::Homebrew
|
||||
u64 app_id; // For TitleType::Installed
|
||||
hb::HbTargetParams nro_target; // For TitleType::Homebrew
|
||||
|
||||
// Optional NACP params
|
||||
std::string name;
|
||||
std::string author;
|
||||
std::string version;
|
||||
|
||||
inline bool Equals(const TitleRecord &other) {
|
||||
inline bool Equals(const TitleRecord &other) const {
|
||||
if(this->title_type == other.title_type) {
|
||||
switch(static_cast<TitleType>(this->title_type)) {
|
||||
switch(this->title_type) {
|
||||
case TitleType::Installed: {
|
||||
return this->app_id == other.app_id;
|
||||
}
|
||||
|
@ -67,6 +67,10 @@ namespace cfg {
|
|||
std::string base_name;
|
||||
std::string path;
|
||||
ThemeManifest manifest;
|
||||
|
||||
inline bool IsDefault() {
|
||||
return this->base_name.empty();
|
||||
}
|
||||
};
|
||||
|
||||
struct RecordStrings {
|
||||
|
@ -108,7 +112,7 @@ namespace cfg {
|
|||
std::string str_value;
|
||||
|
||||
template<typename T>
|
||||
bool Get(T &out_t) const {
|
||||
inline bool Get(T &out_t) const {
|
||||
switch(this->header.type) {
|
||||
case ConfigEntryType::Bool: {
|
||||
if constexpr(std::is_same_v<T, bool>) {
|
||||
|
@ -142,7 +146,7 @@ namespace cfg {
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
bool Set(const T &t) {
|
||||
inline bool Set(const T &t) {
|
||||
switch(this->header.type) {
|
||||
case ConfigEntryType::Bool: {
|
||||
if constexpr(std::is_same_v<T, bool>) {
|
||||
|
@ -188,7 +192,7 @@ namespace cfg {
|
|||
std::vector<ConfigEntry> entries;
|
||||
|
||||
template<typename T>
|
||||
bool SetEntry(const ConfigEntryId id, const T &t) {
|
||||
inline bool SetEntry(const ConfigEntryId id, const T &t) {
|
||||
for(auto &entry : this->entries) {
|
||||
if(entry.header.id == id) {
|
||||
return entry.Set(t);
|
||||
|
@ -242,12 +246,13 @@ namespace cfg {
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
bool GetEntry(const ConfigEntryId id, T &out_t) const {
|
||||
inline bool GetEntry(const ConfigEntryId id, T &out_t) const {
|
||||
for(const auto &entry : this->entries) {
|
||||
if(entry.header.id == id) {
|
||||
return entry.Get(out_t);
|
||||
}
|
||||
}
|
||||
|
||||
// Default values
|
||||
switch(id) {
|
||||
case ConfigEntryId::MenuTakeoverProgramId: {
|
||||
|
@ -305,7 +310,7 @@ namespace cfg {
|
|||
}
|
||||
};
|
||||
|
||||
static constexpr u32 CurrentThemeFormatVersion = 1;
|
||||
constexpr u32 CurrentThemeFormatVersion = 1;
|
||||
|
||||
#define CFG_THEME_DEFAULT "romfs:/default"
|
||||
#define CFG_LANG_DEFAULT "romfs:/LangDefault.json"
|
||||
|
@ -314,17 +319,15 @@ namespace cfg {
|
|||
TitleList LoadTitleList();
|
||||
std::vector<TitleRecord> QueryAllHomebrew(const std::string &base = "sdmc:/switch");
|
||||
void CacheEverything(const std::string &hb_base_path = "sdmc:/switch");
|
||||
std::string GetRecordIconPath(TitleRecord record);
|
||||
RecordInformation GetRecordInformation(TitleRecord record);
|
||||
|
||||
std::string GetRecordIconPath(const TitleRecord &record);
|
||||
std::string GetRecordJsonPath(const TitleRecord &record);
|
||||
RecordInformation GetRecordInformation(const TitleRecord &record);
|
||||
|
||||
Theme LoadTheme(const std::string &base_name);
|
||||
std::vector<Theme> LoadThemes();
|
||||
std::string GetAssetByTheme(const Theme &base, const std::string &resource_base);
|
||||
|
||||
inline bool ThemeIsDefault(const Theme &base) {
|
||||
return base.base_name.empty();
|
||||
}
|
||||
|
||||
inline std::string GetLanguageJSONPath(const std::string &lang) {
|
||||
return UL_BASE_SD_DIR "/lang/" + lang + ".json";
|
||||
}
|
||||
|
@ -336,17 +339,21 @@ namespace cfg {
|
|||
|
||||
void SaveConfig(const Config &cfg);
|
||||
|
||||
void SaveRecord(TitleRecord &record);
|
||||
void RemoveRecord(TitleRecord &record);
|
||||
bool MoveRecordTo(TitleList &list, TitleRecord record, const std::string &folder);
|
||||
TitleFolder &FindFolderByName(TitleList &list, const std::string &name);
|
||||
void RenameFolder(TitleList &list, const std::string &old_name, const std::string &new_name);
|
||||
bool ExistsRecord(TitleList &list, TitleRecord record);
|
||||
|
||||
inline std::string GetTitleCacheIconPath(u64 app_id) {
|
||||
return UL_BASE_SD_DIR "/title/" + util::FormatApplicationId(app_id) + ".jpg";
|
||||
void SaveRecord(const TitleRecord &record);
|
||||
|
||||
inline void RemoveRecord(const TitleRecord &record) {
|
||||
fs::DeleteFile(cfg::GetRecordJsonPath(record));
|
||||
}
|
||||
|
||||
std::string GetNROCacheIconPath(const std::string &path);
|
||||
bool MoveRecordTo(TitleList &list, const TitleRecord &record, const std::string &folder);
|
||||
TitleFolder &FindFolderByName(TitleList &list, const std::string &name);
|
||||
void RenameFolder(TitleList &list, const std::string &old_name, const std::string &new_name);
|
||||
bool ExistsRecord(const TitleList &list, const TitleRecord &record);
|
||||
|
||||
inline std::string GetTitleCacheIconPath(const u64 app_id) {
|
||||
return UL_TITLE_CACHE_PATH "/" + util::FormatApplicationId(app_id) + ".jpg";
|
||||
}
|
||||
|
||||
std::string GetNroCacheIconPath(const std::string &path);
|
||||
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
namespace db {
|
||||
|
||||
static constexpr u64 HomeMenuSaveDataId = 0x8000000000001010;
|
||||
constexpr u64 HomeMenuSaveDataId = 0x8000000000001010;
|
||||
|
||||
Result Mount();
|
||||
void Unmount();
|
||||
|
|
|
@ -8,7 +8,7 @@ constexpr const char PrivateServiceName[] = "ulsf:p";
|
|||
|
||||
namespace dmi {
|
||||
|
||||
enum class MenuStartMode {
|
||||
enum class MenuStartMode : u32 {
|
||||
Invalid,
|
||||
StartupScreen,
|
||||
Menu,
|
||||
|
@ -21,7 +21,7 @@ namespace dmi {
|
|||
HomeRequest
|
||||
};
|
||||
|
||||
enum class DaemonMessage {
|
||||
enum class DaemonMessage : u32 {
|
||||
Invalid,
|
||||
SetSelectedUser,
|
||||
LaunchApplication,
|
||||
|
@ -31,7 +31,7 @@ namespace dmi {
|
|||
LaunchHomebrewApplication,
|
||||
OpenWebPage,
|
||||
OpenAlbum,
|
||||
RestartMenu,
|
||||
RestartMenu
|
||||
};
|
||||
|
||||
struct DaemonStatus {
|
||||
|
@ -41,7 +41,7 @@ namespace dmi {
|
|||
char fw_version[0x18]; // System version (sent by uDaemon so that it contains Atmosphere/EmuMMC info)
|
||||
};
|
||||
|
||||
using CommandFunction = Result(*)(void*, size_t, bool);
|
||||
using CommandFunction = Result(*)(void*, const size_t, const bool);
|
||||
|
||||
struct CommandCommonHeader {
|
||||
u32 magic;
|
||||
|
@ -53,7 +53,7 @@ namespace dmi {
|
|||
|
||||
namespace impl {
|
||||
|
||||
using PopStorageFunction = Result(*)(AppletStorage*, bool);
|
||||
using PopStorageFunction = Result(*)(AppletStorage*, const bool);
|
||||
using PushStorageFunction = Result(*)(AppletStorage*);
|
||||
|
||||
template<PushStorageFunction PushStorageFn>
|
||||
|
@ -63,33 +63,35 @@ namespace dmi {
|
|||
size_t cur_offset;
|
||||
|
||||
public:
|
||||
ScopedStorageWriterBase() : st({}), cur_offset(0) {}
|
||||
ScopedStorageWriterBase() : st(), cur_offset(0) {}
|
||||
|
||||
~ScopedStorageWriterBase() {
|
||||
UL_ASSERT(this->PushStorage(&this->st));
|
||||
UL_RC_ASSERT(PushStorage(&this->st));
|
||||
appletStorageClose(&this->st);
|
||||
}
|
||||
|
||||
Result PushStorage(AppletStorage *st) {
|
||||
static inline Result PushStorage(AppletStorage *st) {
|
||||
return PushStorageFn(st);
|
||||
}
|
||||
|
||||
void Initialize(AppletStorage st) {
|
||||
inline void Initialize(const AppletStorage &st) {
|
||||
this->st = st;
|
||||
}
|
||||
|
||||
Result PushData(void *data, size_t size) {
|
||||
inline Result PushData(const void *data, const size_t size) {
|
||||
if((cur_offset + size) <= CommandStorageSize) {
|
||||
R_TRY(appletStorageWrite(&this->st, this->cur_offset, data, size));
|
||||
UL_RC_TRY(appletStorageWrite(&this->st, this->cur_offset, data, size));
|
||||
this->cur_offset += size;
|
||||
return ResultSuccess;
|
||||
}
|
||||
return 0xBAFF;
|
||||
else {
|
||||
return ResultOutOfPushSpace;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Result Push(T t) {
|
||||
return PushData(&t, sizeof(T));
|
||||
inline Result Push(const T t) {
|
||||
return this->PushData(&t, sizeof(T));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -100,39 +102,41 @@ namespace dmi {
|
|||
size_t cur_offset;
|
||||
|
||||
public:
|
||||
ScopedStorageReaderBase() : st({}), cur_offset(0) {}
|
||||
ScopedStorageReaderBase() : st(), cur_offset(0) {}
|
||||
|
||||
~ScopedStorageReaderBase() {
|
||||
appletStorageClose(&this->st);
|
||||
}
|
||||
|
||||
Result PopStorage(AppletStorage *st, bool wait) {
|
||||
static inline Result PopStorage(AppletStorage *st, const bool wait) {
|
||||
return PopStorageFn(st, wait);
|
||||
}
|
||||
|
||||
void Initialize(AppletStorage st) {
|
||||
inline void Initialize(const AppletStorage &st) {
|
||||
this->st = st;
|
||||
}
|
||||
|
||||
Result PopData(void *out_data, size_t size) {
|
||||
inline Result PopData(void *out_data, const size_t size) {
|
||||
if((cur_offset + size) <= CommandStorageSize) {
|
||||
R_TRY(appletStorageRead(&this->st, this->cur_offset, out_data, size));
|
||||
UL_RC_TRY(appletStorageRead(&this->st, this->cur_offset, out_data, size));
|
||||
this->cur_offset += size;
|
||||
return ResultSuccess;
|
||||
}
|
||||
return 0xBAFF;
|
||||
else {
|
||||
return ResultOutOfPopSpace;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Result Pop(T &out_t) {
|
||||
return PopData(&out_t, sizeof(T));
|
||||
inline Result Pop(T &out_t) {
|
||||
return this->PopData(std::addressof(out_t), sizeof(T));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename StorageReader>
|
||||
inline Result OpenStorageReader(StorageReader &reader, bool wait) {
|
||||
inline Result OpenStorageReader(StorageReader &reader, const bool wait) {
|
||||
AppletStorage st = {};
|
||||
R_TRY(reader.PopStorage(&st, wait));
|
||||
UL_RC_TRY(StorageReader::PopStorage(&st, wait));
|
||||
|
||||
reader.Initialize(st);
|
||||
return ResultSuccess;
|
||||
|
@ -141,62 +145,69 @@ namespace dmi {
|
|||
template<typename StorageWriter>
|
||||
inline Result OpenStorageWriter(StorageWriter &writer) {
|
||||
AppletStorage st = {};
|
||||
R_TRY(appletCreateStorage(&st, CommandStorageSize));
|
||||
UL_RC_TRY(appletCreateStorage(&st, CommandStorageSize));
|
||||
|
||||
writer.Initialize(st);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
template<typename StorageWriter, typename StorageReader, typename MessageType>
|
||||
Result SendCommandImpl(MessageType msg_type, std::function<Result(StorageWriter&)> push_fn, std::function<Result(StorageReader&)> pop_fn) {
|
||||
CommandCommonHeader header = { CommandMagic, static_cast<u32>(msg_type) };
|
||||
inline Result SendCommandImpl(const MessageType msg_type, std::function<Result(StorageWriter&)> push_fn, std::function<Result(StorageReader&)> pop_fn) {
|
||||
{
|
||||
StorageWriter writer;
|
||||
R_TRY(OpenStorageWriter(writer));
|
||||
R_TRY(writer.Push(header));
|
||||
const CommandCommonHeader in_header = {
|
||||
.magic = CommandMagic,
|
||||
.val = static_cast<u32>(msg_type)
|
||||
};
|
||||
|
||||
R_TRY(push_fn(writer));
|
||||
StorageWriter writer;
|
||||
UL_RC_TRY(OpenStorageWriter(writer));
|
||||
UL_RC_TRY(writer.Push(in_header));
|
||||
|
||||
UL_RC_TRY(push_fn(writer));
|
||||
}
|
||||
|
||||
{
|
||||
CommandCommonHeader out_header = {};
|
||||
|
||||
StorageReader reader;
|
||||
R_TRY(OpenStorageReader(reader, true));
|
||||
R_TRY(reader.Pop(header));
|
||||
if(header.magic != CommandMagic) {
|
||||
return 0xB0FA;
|
||||
UL_RC_TRY(OpenStorageReader(reader, true));
|
||||
UL_RC_TRY(reader.Pop(out_header));
|
||||
if(out_header.magic != CommandMagic) {
|
||||
return ResultInvalidOutHeaderMagic;
|
||||
}
|
||||
|
||||
R_TRY(header.val);
|
||||
UL_RC_TRY(out_header.val);
|
||||
|
||||
R_TRY(pop_fn(reader));
|
||||
UL_RC_TRY(pop_fn(reader));
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
template<typename StorageWriter, typename StorageReader, typename MessageType>
|
||||
Result ReceiveCommandImpl(std::function<Result(MessageType, StorageReader&)> pop_fn, std::function<Result(MessageType, StorageWriter&)> push_fn) {
|
||||
CommandCommonHeader header = {};
|
||||
inline Result ReceiveCommandImpl(std::function<Result(const MessageType, StorageReader&)> pop_fn, std::function<Result(const MessageType, StorageWriter&)> push_fn) {
|
||||
CommandCommonHeader in_out_header = {};
|
||||
auto msg_type = MessageType();
|
||||
|
||||
{
|
||||
StorageReader reader;
|
||||
R_TRY(OpenStorageReader(reader, false));
|
||||
R_TRY(reader.Pop(header));
|
||||
if(header.magic != CommandMagic) {
|
||||
return 0xBAFA;
|
||||
UL_RC_TRY(OpenStorageReader(reader, false));
|
||||
UL_RC_TRY(reader.Pop(in_out_header));
|
||||
if(in_out_header.magic != CommandMagic) {
|
||||
return dmi::ResultInvalidInHeaderMagic;
|
||||
}
|
||||
msg_type = static_cast<MessageType>(header.val);
|
||||
|
||||
header.val = pop_fn(msg_type, reader);
|
||||
msg_type = static_cast<MessageType>(in_out_header.val);
|
||||
in_out_header.val = pop_fn(msg_type, reader);
|
||||
}
|
||||
|
||||
{
|
||||
StorageWriter writer;
|
||||
R_TRY(OpenStorageWriter(writer));
|
||||
R_TRY(writer.Push(header));
|
||||
UL_RC_TRY(OpenStorageWriter(writer));
|
||||
UL_RC_TRY(writer.Push(in_out_header));
|
||||
|
||||
if(R_SUCCEEDED(header.val)) {
|
||||
R_TRY(push_fn(msg_type, writer));
|
||||
if(R_SUCCEEDED(in_out_header.val)) {
|
||||
UL_RC_TRY(push_fn(msg_type, writer));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -207,18 +218,15 @@ namespace dmi {
|
|||
|
||||
namespace dmn {
|
||||
|
||||
Result PopStorage(AppletStorage *st, bool wait);
|
||||
Result PopStorage(AppletStorage *st, const bool wait);
|
||||
Result PushStorage(AppletStorage *st);
|
||||
|
||||
using DaemonScopedStorageReader = impl::ScopedStorageReaderBase<&PopStorage>;
|
||||
|
||||
using DaemonScopedStorageWriter = impl::ScopedStorageWriterBase<&PushStorage>;
|
||||
|
||||
inline Result SendCommand(MenuMessage msg, std::function<Result(DaemonScopedStorageWriter&)> push_fn, std::function<Result(DaemonScopedStorageReader&)> pop_fn) {
|
||||
return impl::SendCommandImpl(msg, push_fn, pop_fn);
|
||||
}
|
||||
// Daemon only receives commands from Menu
|
||||
|
||||
inline Result ReceiveCommand(std::function<Result(DaemonMessage, DaemonScopedStorageReader&)> pop_fn, std::function<Result(DaemonMessage, DaemonScopedStorageWriter&)> push_fn) {
|
||||
inline Result ReceiveCommand(std::function<Result(const DaemonMessage, DaemonScopedStorageReader&)> pop_fn, std::function<Result(const DaemonMessage, DaemonScopedStorageWriter&)> push_fn) {
|
||||
return impl::ReceiveCommandImpl(pop_fn, push_fn);
|
||||
}
|
||||
|
||||
|
@ -226,19 +234,16 @@ namespace dmi {
|
|||
|
||||
namespace menu {
|
||||
|
||||
Result PopStorage(AppletStorage *st, bool wait);
|
||||
Result PopStorage(AppletStorage *st, const bool wait);
|
||||
Result PushStorage(AppletStorage *st);
|
||||
|
||||
using MenuScopedStorageReader = impl::ScopedStorageReaderBase<&PopStorage>;
|
||||
|
||||
using MenuScopedStorageWriter = impl::ScopedStorageWriterBase<&PushStorage>;
|
||||
|
||||
inline Result SendCommand(DaemonMessage msg, std::function<Result(MenuScopedStorageWriter&)> push_fn, std::function<Result(MenuScopedStorageReader&)> pop_fn) {
|
||||
return impl::SendCommandImpl(msg, push_fn, pop_fn);
|
||||
}
|
||||
// Menu only sends commands to Daemon
|
||||
|
||||
inline Result ReceiveCommand(std::function<Result(MenuMessage, MenuScopedStorageReader&)> pop_fn, std::function<Result(MenuMessage, MenuScopedStorageWriter&)> push_fn) {
|
||||
return impl::ReceiveCommandImpl(pop_fn, push_fn);
|
||||
inline Result SendCommand(const DaemonMessage msg, std::function<Result(MenuScopedStorageWriter&)> push_fn, std::function<Result(MenuScopedStorageReader&)> pop_fn) {
|
||||
return impl::SendCommandImpl(msg, push_fn, pop_fn);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,7 +10,9 @@ namespace fs {
|
|||
if(stat(path.c_str(), &st) == 0) {
|
||||
return st.st_mode & Mode;
|
||||
}
|
||||
return false;
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool ExistsFile(const std::string &path) {
|
||||
|
@ -45,24 +47,33 @@ namespace fs {
|
|||
remove(path.c_str());
|
||||
}
|
||||
|
||||
inline bool WriteFile(const std::string &path, const void *data, size_t size, bool overwrite) {
|
||||
inline void CleanDirectory(const std::string &path) {
|
||||
DeleteDirectory(path);
|
||||
CreateDirectory(path);
|
||||
}
|
||||
|
||||
inline bool WriteFile(const std::string &path, const void *data, const size_t size, const bool overwrite) {
|
||||
auto f = fopen(path.c_str(), overwrite ? "wb" : "ab+");
|
||||
if(f) {
|
||||
fwrite(data, 1, size, f);
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool ReadFile(const std::string &path, void *data, size_t size) {
|
||||
inline bool ReadFile(const std::string &path, void *data, const size_t size) {
|
||||
auto f = fopen(path.c_str(), "rb");
|
||||
if(f) {
|
||||
fread(data, 1, size, f);
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline size_t GetFileSize(const std::string &path) {
|
||||
|
@ -70,19 +81,22 @@ namespace fs {
|
|||
if(stat(path.c_str(), &st) == 0) {
|
||||
return st.st_size;
|
||||
}
|
||||
return 0;
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#define UL_FS_FOR(dir, name_var, path_var, ...) ({ \
|
||||
auto dp = opendir(dir.c_str()); \
|
||||
const std::string dir_str = dir; \
|
||||
auto dp = opendir(dir_str.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; \
|
||||
const std::string name_var = dt->d_name; \
|
||||
const std::string path_var = dir_str + "/" + dt->d_name; \
|
||||
__VA_ARGS__ \
|
||||
} \
|
||||
closedir(dp); \
|
||||
|
|
|
@ -29,20 +29,21 @@ namespace hb {
|
|||
std::string nro_argv_copy = this->nro_argv;
|
||||
std::replace(nro_argv_copy.begin(), nro_argv_copy.end(), ' ', (char)0xFF);
|
||||
argv += " " + nro_argv_copy;
|
||||
u32 num_val = this->target_once ? 1 : 0;
|
||||
argv += " " + std::to_string(num_val);
|
||||
const auto target_once_num = this->target_once ? 1 : 0;
|
||||
argv += " " + std::to_string(target_once_num);
|
||||
return argv;
|
||||
}
|
||||
|
||||
static inline HbTargetParams Create(const std::string &nro_path, const std::string &nro_argv, bool target_once) {
|
||||
HbTargetParams params = {};
|
||||
params.magic = UL_HB_HBTARGET_MAGIC_U32;
|
||||
static inline HbTargetParams Create(const std::string &nro_path, const std::string &nro_argv, const bool target_once) {
|
||||
HbTargetParams params = {
|
||||
.magic = UL_HB_HBTARGET_MAGIC_U32,
|
||||
.target_once = target_once
|
||||
};
|
||||
|
||||
strcpy(params.nro_path, nro_path.c_str());
|
||||
strcpy(params.nro_argv, nro_argv.c_str());
|
||||
params.target_once = target_once;
|
||||
return params;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -4,6 +4,7 @@
|
|||
|
||||
namespace net {
|
||||
|
||||
// TODO: use libnx's nifm stuff?
|
||||
struct NetworkProfileData {
|
||||
u8 unk[0x117];
|
||||
char wifi_name[0x20 + 1];
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
namespace os {
|
||||
|
||||
std::string GetIconCacheImagePath(AccountUid user_id);
|
||||
Result QuerySystemAccounts(std::vector<AccountUid> &out_accounts, bool dump_icon);
|
||||
Result GetAccountName(std::string &out_name, AccountUid user_id);
|
||||
std::string GetIconCacheImagePath(const AccountUid user_id);
|
||||
Result QuerySystemAccounts(const bool dump_icon, std::vector<AccountUid> &out_accounts);
|
||||
Result GetAccountName(const AccountUid user_id, std::string &out_name);
|
||||
|
||||
}
|
|
@ -14,28 +14,25 @@ namespace os {
|
|||
OverlayAutoBrightnessChanged = 14,
|
||||
OverlayAirplaneModeChanged = 15,
|
||||
HomeButtonHold = 16,
|
||||
OverlayHidden = 17,
|
||||
OverlayHidden = 17
|
||||
};
|
||||
|
||||
struct SystemAppletMessage {
|
||||
|
||||
static constexpr u32 Magic = 0x534D4153; // "SAMS" -> System applet message...?
|
||||
|
||||
u32 magic;
|
||||
u32 unk;
|
||||
GeneralChannelMessage general_channel_message;
|
||||
u8 data[0x3F4];
|
||||
u8 unk_reserved[0x3F4];
|
||||
|
||||
static inline constexpr SystemAppletMessage Create(GeneralChannelMessage msg) {
|
||||
return SystemAppletMessage {
|
||||
static inline constexpr SystemAppletMessage Create(const GeneralChannelMessage msg) {
|
||||
return {
|
||||
.magic = Magic,
|
||||
.general_channel_message = msg
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
// 1024 bytes are always sent, so let's read it all.
|
||||
static_assert(sizeof(SystemAppletMessage) == 0x400, "System applet message");
|
||||
static_assert(sizeof(SystemAppletMessage) == 0x400);
|
||||
|
||||
// TODO: actual names N uses?
|
||||
enum class AppletMessage : u32 {
|
||||
|
@ -47,9 +44,9 @@ namespace os {
|
|||
BackFromSleep = 26,
|
||||
ChangeOperationMode = 30,
|
||||
ChangePerformanceMode = 31,
|
||||
SdCardOut = 33,
|
||||
SdCardOut = 33
|
||||
};
|
||||
|
||||
Result PushSystemAppletMessage(SystemAppletMessage msg);
|
||||
Result PushSystemAppletMessage(const SystemAppletMessage msg);
|
||||
|
||||
}
|
|
@ -46,6 +46,14 @@ namespace os {
|
|||
};
|
||||
constexpr size_t LanguageNameCount = sizeof(LanguageNameList) / sizeof(const char*);
|
||||
|
||||
inline SetLanguage GetSystemLanguage() {
|
||||
u64 lang_code = 0;
|
||||
auto lang = SetLanguage_ENUS;
|
||||
setGetLanguageCode(&lang_code);
|
||||
setMakeLanguage(lang_code, &lang);
|
||||
return lang;
|
||||
}
|
||||
|
||||
u32 GetBatteryLevel();
|
||||
bool IsConsoleCharging();
|
||||
std::string GetCurrentTime();
|
||||
|
|
|
@ -5,23 +5,6 @@
|
|||
|
||||
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 = {}; \
|
||||
} \
|
||||
})
|
||||
|
||||
constexpr u32 MaxTitleCount = 64000;
|
||||
|
||||
std::vector<cfg::TitleRecord> QueryInstalledTitles();
|
||||
|
|
|
@ -17,9 +17,13 @@
|
|||
#include <functional>
|
||||
#include <thread>
|
||||
|
||||
// JSON utils
|
||||
|
||||
#include <json.hpp>
|
||||
using JSON = nlohmann::json;
|
||||
|
||||
// Library defines
|
||||
|
||||
#define UL_BASE_DIR "ulaunch"
|
||||
#define UL_BASE_SD_DIR "sdmc:/" UL_BASE_DIR
|
||||
#define UL_DB_MOUNT_NAME "ul_save"
|
||||
|
@ -27,21 +31,21 @@ using JSON = nlohmann::json;
|
|||
#define UL_BASE_DB_DIR UL_DB_MOUNT_PATH UL_BASE_DIR
|
||||
#define UL_ENTRIES_PATH UL_BASE_SD_DIR "/entries"
|
||||
#define UL_THEMES_PATH UL_BASE_SD_DIR "/themes"
|
||||
#define UL_NRO_CACHE_PATH UL_BASE_SD_DIR "/nro"
|
||||
#define UL_TITLE_CACHE_PATH UL_BASE_SD_DIR "/titles"
|
||||
#define UL_ASSERTION_LOG_FILE UL_BASE_SD_DIR "/err.log"
|
||||
|
||||
#ifndef UL_VERSION
|
||||
#error uLaunch's build version isn't defined
|
||||
#endif
|
||||
|
||||
// Scope utils
|
||||
|
||||
#include <ul_Scope.hpp>
|
||||
|
||||
static constexpr size_t RawRGBAScreenBufferSize = 1280 * 720 * 4;
|
||||
// Size utils
|
||||
|
||||
#define STL_FIND_IF(stl_item, var_name, cond) std::find_if(stl_item.begin(), stl_item.end(), [&](const auto &var_name){ return (cond); })
|
||||
#define STL_FOUND(stl_item, find_ret) (find_ret != stl_item.end())
|
||||
#define STL_UNWRAP(find_ret) (*find_ret)
|
||||
#define STL_REMOVE_IF(stl_item, var_name, cond) stl_item.erase(std::remove_if(stl_item.begin(), stl_item.end(), [&](const auto &var_name){ return (cond); }), stl_item.end())
|
||||
|
||||
#include <ul_Result.hpp>
|
||||
static constexpr size_t PlainRgbaScreenBufferSize = 1280 * 720 * 4;
|
||||
|
||||
inline constexpr size_t operator ""_KB(unsigned long long n) {
|
||||
return n * 0x400;
|
||||
|
@ -55,36 +59,40 @@ inline constexpr size_t operator ""_GB(unsigned long long n) {
|
|||
return operator ""_MB(n) * 0x400;
|
||||
}
|
||||
|
||||
#define UL_ASSERTION_LOG_FILE UL_BASE_SD_DIR "/err.log"
|
||||
// STL utils
|
||||
|
||||
inline void NORETURN OnAssertionFailed(const char *log_buf, size_t log_buf_len, const Result rc) {
|
||||
#define STL_FIND_IF(stl_item, var_name, cond) std::find_if(stl_item.begin(), stl_item.end(), [&](const auto &var_name){ return (cond); })
|
||||
#define STL_FOUND(stl_item, find_ret) (find_ret != stl_item.end())
|
||||
#define STL_UNWRAP(find_ret) (*find_ret)
|
||||
#define STL_REMOVE_IF(stl_item, var_name, cond) stl_item.erase(std::remove_if(stl_item.begin(), stl_item.end(), [&](const auto &var_name){ return (cond); }), stl_item.end())
|
||||
|
||||
// Result and assertion utils
|
||||
|
||||
#include <ul_Result.hpp>
|
||||
|
||||
template<typename ...Args>
|
||||
inline void NORETURN OnAssertionFailed(const char *log_fmt, Args &&...args) {
|
||||
// TODO: unique log file for each assertion faial (a la crash report?)
|
||||
auto log_f = fopen(UL_ASSERTION_LOG_FILE, "wb"); \
|
||||
if(log_f) {
|
||||
fwrite(log_buf, 1, log_buf_len, log_f);
|
||||
fprintf(log_f, log_fmt, args...);
|
||||
fprintf(log_f, "\n");
|
||||
fclose(log_f);
|
||||
}
|
||||
|
||||
fatalThrow(rc);
|
||||
fatalThrow(misc::ResultAssertionFailed);
|
||||
}
|
||||
|
||||
#define UL_ASSERT_LOG_LEN 0x400
|
||||
|
||||
#define UL_ASSERT(expr) ({ \
|
||||
const auto _tmp_rc = (expr); \
|
||||
#define UL_RC_ASSERT(expr) ({ \
|
||||
const auto _tmp_rc = res::TransformIntoResult(expr); \
|
||||
if(R_FAILED(_tmp_rc)) { \
|
||||
char log_buf[UL_ASSERT_LOG_LEN] = {}; \
|
||||
const auto log_buf_len = std::sprintf(log_buf, "%s asserted 0x%X...", #expr, _tmp_rc); \
|
||||
OnAssertionFailed(log_buf, log_buf_len, _tmp_rc); \
|
||||
OnAssertionFailed("%s asserted 0x%X...", #expr, _tmp_rc); \
|
||||
} \
|
||||
})
|
||||
|
||||
#define UL_ASSERT_TRUE(expr) ({ \
|
||||
const auto _tmp_expr = (expr); \
|
||||
if(!_tmp_expr) { \
|
||||
char logbuf[UL_ASSERT_LOG_LEN] = {}; \
|
||||
const auto log_buf_len = sprintf(logbuf, "%s failed...", #expr); \
|
||||
OnAssertionFailed(logbuf, log_buf_len, 0xDEAD); \
|
||||
OnAssertionFailed("%s asserted to be false...", #expr); \
|
||||
} \
|
||||
})
|
||||
|
||||
#define UL_AMS_ASSERT(expr) UL_ASSERT(ams::Result(expr).GetValue())
|
||||
|
|
|
@ -1,64 +1,94 @@
|
|||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <string>
|
||||
|
||||
#ifdef ATMOSPHERE
|
||||
#include <stratosphere.hpp>
|
||||
#endif
|
||||
|
||||
// All 2380-**** results are from us
|
||||
|
||||
constexpr u32 Module = 380;
|
||||
constexpr u32 SubmoduleOffset = 100;
|
||||
|
||||
#define RES_DEFINE_SUBMODULE(val) constexpr u32 Submodule = val
|
||||
|
||||
#define RES_DEFINE(name, val) constexpr Result Result ## name = MAKERESULT(Module + Submodule * SubmoduleOffset, val)
|
||||
|
||||
#ifndef R_TRY
|
||||
|
||||
#define R_TRY(res_expr) ({ \
|
||||
const auto _tmp_r_try_rc = static_cast<Result>(res_expr); \
|
||||
if (R_FAILED(_tmp_r_try_rc)) { \
|
||||
return _tmp_r_try_rc; \
|
||||
} \
|
||||
})
|
||||
|
||||
#endif
|
||||
#define UL_RC_DEFINE_SUBMODULE(val) constexpr u32 Submodule = val
|
||||
#define UL_RC_DEFINE(name, val) constexpr Result Result ## name = MAKERESULT(Module, Submodule * SubmoduleOffset + val)
|
||||
|
||||
constexpr Result ResultSuccess = 0;
|
||||
|
||||
namespace misc {
|
||||
|
||||
RES_DEFINE_SUBMODULE(0);
|
||||
RES_DEFINE(InvalidJsonFile, 1);
|
||||
UL_RC_DEFINE_SUBMODULE(0);
|
||||
UL_RC_DEFINE(AssertionFailed, 1);
|
||||
UL_RC_DEFINE(UnexpectedTransform, 2);
|
||||
UL_RC_DEFINE(InvalidJsonFile, 3);
|
||||
|
||||
}
|
||||
|
||||
namespace dmn {
|
||||
|
||||
RES_DEFINE_SUBMODULE(1);
|
||||
RES_DEFINE(ApplicationActive, 1);
|
||||
RES_DEFINE(InvalidSelectedUser, 2);
|
||||
RES_DEFINE(AlreadyQueued, 3);
|
||||
RES_DEFINE(ApplicationNotActive, 4);
|
||||
UL_RC_DEFINE_SUBMODULE(1);
|
||||
UL_RC_DEFINE(ApplicationActive, 1);
|
||||
UL_RC_DEFINE(InvalidSelectedUser, 2);
|
||||
UL_RC_DEFINE(AlreadyQueued, 3);
|
||||
UL_RC_DEFINE(ApplicationNotActive, 4);
|
||||
|
||||
}
|
||||
|
||||
namespace menu {
|
||||
|
||||
RES_DEFINE_SUBMODULE(2);
|
||||
RES_DEFINE(RomfsFileNotFound, 1);
|
||||
UL_RC_DEFINE_SUBMODULE(2);
|
||||
UL_RC_DEFINE(RomfsFileNotFound, 1);
|
||||
|
||||
}
|
||||
|
||||
namespace ipc {
|
||||
|
||||
RES_DEFINE_SUBMODULE(3);
|
||||
RES_DEFINE(InvalidProcess, 1);
|
||||
UL_RC_DEFINE_SUBMODULE(3);
|
||||
UL_RC_DEFINE(InvalidProcess, 1);
|
||||
|
||||
}
|
||||
|
||||
namespace dmi {
|
||||
|
||||
UL_RC_DEFINE_SUBMODULE(4);
|
||||
UL_RC_DEFINE(OutOfPushSpace, 1);
|
||||
UL_RC_DEFINE(OutOfPopSpace, 2);
|
||||
UL_RC_DEFINE(InvalidInHeaderMagic, 3);
|
||||
UL_RC_DEFINE(InvalidOutHeaderMagic, 4);
|
||||
UL_RC_DEFINE(WaitTimeout, 5);
|
||||
|
||||
}
|
||||
|
||||
namespace res {
|
||||
|
||||
template<typename T>
|
||||
inline ::Result TransformIntoResult(const T t) {
|
||||
return misc::ResultUnexpectedTransform;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline ::Result TransformIntoResult<::Result>(const ::Result rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef ATMOSPHERE
|
||||
template<>
|
||||
inline ::Result TransformIntoResult<ams::Result>(const ams::Result ams_rc) {
|
||||
return ams_rc.GetValue();
|
||||
}
|
||||
#endif
|
||||
|
||||
Result GetResultByModuleAndName(const std::string &mod, const std::string &name);
|
||||
std::string GetModuleByResult(const Result rc);
|
||||
std::string GetNameByResult(const Result rc);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#define UL_RC_TRY(res_expr) ({ \
|
||||
const auto _tmp_rc = res::TransformIntoResult(res_expr); \
|
||||
if (R_FAILED(_tmp_rc)) { \
|
||||
return _tmp_rc; \
|
||||
} \
|
||||
})
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <functional>
|
||||
|
||||
class OnScopeExit {
|
||||
|
||||
public:
|
||||
using Fn = std::function<void()>;
|
||||
|
||||
|
@ -16,11 +16,24 @@ class OnScopeExit {
|
|||
~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__ })
|
||||
#define UL_ON_SCOPE_EXIT(...) OnScopeExit UL_UNIQUE_VAR_NAME(on_scope_exit) ([&]() { __VA_ARGS__ })
|
||||
|
||||
class ScopedLock {
|
||||
private:
|
||||
Mutex &lock;
|
||||
|
||||
public:
|
||||
ScopedLock(Mutex &lock) : lock(lock) {
|
||||
mutexLock(std::addressof(lock));
|
||||
}
|
||||
|
||||
~ScopedLock() {
|
||||
mutexUnlock(std::addressof(this->lock));
|
||||
}
|
||||
};
|
|
@ -37,24 +37,24 @@ namespace am {
|
|||
appletApplicationClose(&g_ApplicationHolder);
|
||||
|
||||
if(system) {
|
||||
R_TRY(appletCreateSystemApplication(&g_ApplicationHolder, app_id));
|
||||
UL_RC_TRY(appletCreateSystemApplication(&g_ApplicationHolder, app_id));
|
||||
}
|
||||
else {
|
||||
R_TRY(appletCreateApplication(&g_ApplicationHolder, app_id));
|
||||
UL_RC_TRY(appletCreateApplication(&g_ApplicationHolder, app_id));
|
||||
}
|
||||
|
||||
if(accountUidIsValid(&user_id)) {
|
||||
auto selected_user_arg = ApplicationSelectedUserArgument::Create(user_id);
|
||||
R_TRY(ApplicationSend(&selected_user_arg, sizeof(selected_user_arg), AppletLaunchParameterKind_PreselectedUser));
|
||||
const auto selected_user_arg = ApplicationSelectedUserArgument::Create(user_id);
|
||||
UL_RC_TRY(ApplicationSend(&selected_user_arg, sizeof(selected_user_arg), AppletLaunchParameterKind_PreselectedUser));
|
||||
}
|
||||
|
||||
if(size > 0) {
|
||||
R_TRY(ApplicationSend(data, size));
|
||||
UL_RC_TRY(ApplicationSend(data, size));
|
||||
}
|
||||
|
||||
R_TRY(appletUnlockForeground());
|
||||
R_TRY(appletApplicationStart(&g_ApplicationHolder));
|
||||
R_TRY(ApplicationSetForeground());
|
||||
UL_RC_TRY(appletUnlockForeground());
|
||||
UL_RC_TRY(appletApplicationStart(&g_ApplicationHolder));
|
||||
UL_RC_TRY(ApplicationSetForeground());
|
||||
|
||||
g_LastApplicationId = app_id;
|
||||
return 0;
|
||||
|
@ -65,19 +65,18 @@ namespace am {
|
|||
}
|
||||
|
||||
Result ApplicationSetForeground() {
|
||||
R_TRY(appletApplicationRequestForApplicationToGetForeground(&g_ApplicationHolder));
|
||||
UL_RC_TRY(appletApplicationRequestForApplicationToGetForeground(&g_ApplicationHolder));
|
||||
g_DaemonHasFocus = false;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ApplicationSend(const void *data, const size_t size, const AppletLaunchParameterKind kind) {
|
||||
AppletStorage st;
|
||||
R_TRY(appletCreateStorage(&st, size));
|
||||
UL_ON_SCOPE_EXIT({
|
||||
appletStorageClose(&st);
|
||||
});
|
||||
R_TRY(appletStorageWrite(&st, 0, data, size));
|
||||
R_TRY(appletApplicationPushLaunchParameter(&g_ApplicationHolder, kind, &st));
|
||||
UL_RC_TRY(appletCreateStorage(&st, size));
|
||||
UL_ON_SCOPE_EXIT({ appletStorageClose(&st); });
|
||||
|
||||
UL_RC_TRY(appletStorageWrite(&st, 0, data, size));
|
||||
UL_RC_TRY(appletApplicationPushLaunchParameter(&g_ApplicationHolder, kind, &st));
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
|
@ -86,8 +85,11 @@ namespace am {
|
|||
}
|
||||
|
||||
bool ApplicationNeedsUser(const u64 app_id) {
|
||||
NsApplicationControlData ctdata = {};
|
||||
nsGetApplicationControlData(NsApplicationControlSource_Storage, app_id, &ctdata, sizeof(ctdata), nullptr);
|
||||
return ctdata.nacp.startup_user_account > 0;
|
||||
auto control_data = new NsApplicationControlData();
|
||||
nsGetApplicationControlData(NsApplicationControlSource_Storage, app_id, control_data, sizeof(NsApplicationControlData), nullptr);
|
||||
|
||||
const auto needs_user = control_data->nacp.startup_user_account > 0;
|
||||
delete control_data;
|
||||
return needs_user;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
#include <am/am_HomeMenu.hpp>
|
||||
#include <am/am_LibraryApplet.hpp>
|
||||
|
||||
namespace am {
|
||||
|
||||
|
@ -10,7 +9,7 @@ namespace am {
|
|||
}
|
||||
|
||||
Result HomeMenuSetForeground() {
|
||||
R_TRY(appletRequestToGetForeground());
|
||||
UL_RC_TRY(appletRequestToGetForeground());
|
||||
g_DaemonHasFocus = true;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
|
|
@ -46,18 +46,6 @@ namespace am {
|
|||
return !appletHolderCheckFinished(&g_AppletHolder);
|
||||
}
|
||||
|
||||
void LibraryAppletSetMenuAppletId(AppletId id) {
|
||||
g_MenuAppletId = id;
|
||||
}
|
||||
|
||||
AppletId LibraryAppletGetMenuAppletId() {
|
||||
return g_MenuAppletId;
|
||||
}
|
||||
|
||||
bool LibraryAppletIsMenu() {
|
||||
return LibraryAppletIsActive() && (g_MenuAppletId != AppletId_None) && (LibraryAppletGetId() == g_MenuAppletId);
|
||||
}
|
||||
|
||||
void LibraryAppletTerminate() {
|
||||
// Give it 15 seconds
|
||||
appletHolderRequestExitOrTerminate(&g_AppletHolder, 15'000'000'000ul);
|
||||
|
@ -68,14 +56,15 @@ namespace am {
|
|||
LibraryAppletTerminate();
|
||||
}
|
||||
appletHolderClose(&g_AppletHolder);
|
||||
|
||||
LibAppletArgs la_args;
|
||||
libappletArgsCreate(&la_args, la_version);
|
||||
R_TRY(appletCreateLibraryApplet(&g_AppletHolder, id, LibAppletMode_AllForeground));
|
||||
R_TRY(libappletArgsPush(&la_args, &g_AppletHolder));
|
||||
UL_RC_TRY(appletCreateLibraryApplet(&g_AppletHolder, id, LibAppletMode_AllForeground));
|
||||
UL_RC_TRY(libappletArgsPush(&la_args, &g_AppletHolder));
|
||||
if(in_size > 0) {
|
||||
R_TRY(LibraryAppletSend(in_data, in_size));
|
||||
UL_RC_TRY(LibraryAppletSend(in_data, in_size));
|
||||
}
|
||||
R_TRY(appletHolderStart(&g_AppletHolder));
|
||||
UL_RC_TRY(appletHolderStart(&g_AppletHolder));
|
||||
g_LastAppletId = id;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
@ -125,4 +114,17 @@ namespace am {
|
|||
}
|
||||
return last_id_copy;
|
||||
}
|
||||
|
||||
bool LibraryAppletIsMenu() {
|
||||
return LibraryAppletIsActive() && (g_MenuAppletId != AppletId_None) && (LibraryAppletGetId() == g_MenuAppletId);
|
||||
}
|
||||
|
||||
void LibraryAppletSetMenuAppletId(const AppletId id) {
|
||||
g_MenuAppletId = id;
|
||||
}
|
||||
|
||||
AppletId LibraryAppletGetMenuAppletId() {
|
||||
return g_MenuAppletId;
|
||||
}
|
||||
|
||||
}
|
|
@ -8,23 +8,23 @@ namespace cfg {
|
|||
|
||||
namespace {
|
||||
|
||||
void CacheHomebrew(const std::string &nro_path) {
|
||||
auto nroimg = GetNROCacheIconPath(nro_path);
|
||||
void DoCacheHomebrew(const std::string &nro_path) {
|
||||
const auto cache_nro_icon_path = GetNroCacheIconPath(nro_path);
|
||||
auto f = fopen(nro_path.c_str(), "rb");
|
||||
if(f) {
|
||||
fseek(f, sizeof(NroStart), SEEK_SET);
|
||||
NroHeader hdr = {};
|
||||
if(fread(&hdr, sizeof(hdr), 1, f) == 1) {
|
||||
fseek(f, hdr.size, SEEK_SET);
|
||||
NroAssetHeader ahdr = {};
|
||||
if(fread(&ahdr, sizeof(ahdr), 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);
|
||||
delete[] iconbuf;
|
||||
NroHeader nro_header = {};
|
||||
if(fread(&nro_header, sizeof(nro_header), 1, f) == 1) {
|
||||
fseek(f, nro_header.size, SEEK_SET);
|
||||
NroAssetHeader asset_header = {};
|
||||
if(fread(&asset_header, sizeof(asset_header), 1, f) == 1) {
|
||||
if(asset_header.magic == NROASSETHEADER_MAGIC) {
|
||||
if((asset_header.icon.offset > 0) && (asset_header.icon.size > 0)) {
|
||||
auto icon_buf = new u8[asset_header.icon.size]();
|
||||
fseek(f, nro_header.size + asset_header.icon.offset, SEEK_SET);
|
||||
fread(icon_buf, asset_header.icon.size, 1, f);
|
||||
fs::WriteFile(cache_nro_icon_path, icon_buf, asset_header.icon.size, true);
|
||||
delete[] icon_buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,46 +33,46 @@ namespace cfg {
|
|||
}
|
||||
}
|
||||
|
||||
void CacheInstalledTitles() {
|
||||
UL_OS_FOR_EACH_APP_RECORD(rec, {
|
||||
auto fname = cfg::GetTitleCacheIconPath(rec.application_id);
|
||||
if(!fs::ExistsFile(fname)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void CacheAllHomebrew(const std::string &hb_base_path) {
|
||||
void CacheHomebrew(const std::string &hb_base_path) {
|
||||
UL_FS_FOR(hb_base_path, name, path, {
|
||||
if(dt->d_type & DT_DIR) {
|
||||
CacheAllHomebrew(path);
|
||||
CacheHomebrew(path);
|
||||
}
|
||||
else if(util::StringEndsWith(name, ".nro")) {
|
||||
CacheHomebrew(path);
|
||||
DoCacheHomebrew(path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ProcessStringsFromNACP(RecordStrings &strs, NacpStruct *nacp) {
|
||||
NacpLanguageEntry *lent = nullptr;
|
||||
nacpGetLanguageEntry(nacp, &lent);
|
||||
if(lent == nullptr) {
|
||||
for(u32 i = 0; i < 16; i++) {
|
||||
lent = &nacp->lang[i];
|
||||
if((strlen(lent->name) > 0) && (strlen(lent->author) > 0)) {
|
||||
break;
|
||||
}
|
||||
lent = nullptr;
|
||||
void CacheInstalledTitles() {
|
||||
const auto titles = os::QueryInstalledTitles();
|
||||
|
||||
auto control_data = new NsApplicationControlData();
|
||||
for(const auto &title: titles) {
|
||||
const auto cache_icon_path = cfg::GetTitleCacheIconPath(title.app_id);
|
||||
if(R_SUCCEEDED(nsGetApplicationControlData(NsApplicationControlSource_Storage, title.app_id, control_data, sizeof(NsApplicationControlData), nullptr))) {
|
||||
fs::WriteFile(cache_icon_path, control_data->icon, sizeof(control_data->icon), true);
|
||||
}
|
||||
}
|
||||
if(lent != nullptr) {
|
||||
strs.name = lent->name;
|
||||
strs.author = lent->author;
|
||||
delete control_data;
|
||||
}
|
||||
|
||||
void ProcessStringsFromNacp(RecordStrings &strs, NacpStruct *nacp) {
|
||||
NacpLanguageEntry *lang_entry = nullptr;
|
||||
nacpGetLanguageEntry(nacp, &lang_entry);
|
||||
if(lang_entry == nullptr) {
|
||||
for(u32 i = 0; i < 16; i++) {
|
||||
lang_entry = &nacp->lang[i];
|
||||
if((strlen(lang_entry->name) > 0) && (strlen(lang_entry->author) > 0)) {
|
||||
break;
|
||||
}
|
||||
lang_entry = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if(lang_entry != nullptr) {
|
||||
strs.name = lang_entry->name;
|
||||
strs.author = lang_entry->author;
|
||||
strs.version = nacp->display_version;
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ namespace cfg {
|
|||
std::vector<TitleRecord> nros;
|
||||
UL_FS_FOR(base, name, path, {
|
||||
if(dt->d_type & DT_DIR) {
|
||||
auto hb = QueryAllHomebrew(path);
|
||||
const auto hb = QueryAllHomebrew(path);
|
||||
if(!hb.empty()) {
|
||||
nros.insert(nros.begin(), hb.begin(), hb.end());
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ namespace cfg {
|
|||
else {
|
||||
if(util::StringEndsWith(name, ".nro")) {
|
||||
TitleRecord rec = {};
|
||||
rec.title_type = (u32)TitleType::Homebrew;
|
||||
rec.title_type = TitleType::Homebrew;
|
||||
strcpy(rec.nro_target.nro_path, path.c_str());
|
||||
nros.push_back(rec);
|
||||
}
|
||||
|
@ -101,29 +101,44 @@ namespace cfg {
|
|||
}
|
||||
|
||||
void CacheEverything(const std::string &hb_base_path) {
|
||||
fs::CleanDirectory(UL_TITLE_CACHE_PATH);
|
||||
CacheInstalledTitles();
|
||||
CacheAllHomebrew(hb_base_path);
|
||||
|
||||
fs::CleanDirectory(UL_NRO_CACHE_PATH);
|
||||
CacheHomebrew(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);
|
||||
std::string GetRecordIconPath(const TitleRecord &record) {
|
||||
auto icon_path = record.icon;
|
||||
if(icon_path.empty()) {
|
||||
if(record.title_type == TitleType::Homebrew) {
|
||||
icon_path = GetNroCacheIconPath(record.nro_target.nro_path);
|
||||
}
|
||||
else if(type == TitleType::Installed) {
|
||||
icon = GetTitleCacheIconPath(record.app_id);
|
||||
else if(record.title_type == TitleType::Installed) {
|
||||
icon_path = GetTitleCacheIconPath(record.app_id);
|
||||
}
|
||||
}
|
||||
return icon;
|
||||
return icon_path;
|
||||
}
|
||||
|
||||
RecordInformation GetRecordInformation(TitleRecord record) {
|
||||
std::string GetRecordJsonPath(const TitleRecord &record) {
|
||||
auto json_name = record.json_name;
|
||||
if(json_name.empty()) {
|
||||
if(record.title_type == TitleType::Homebrew) {
|
||||
json_name = std::to_string(fs::GetFileSize(record.nro_target.nro_path)) + ".json";
|
||||
}
|
||||
else if(record.title_type == TitleType::Installed) {
|
||||
const auto app_id_str = util::FormatApplicationId(record.app_id);
|
||||
json_name = app_id_str + ".json";
|
||||
}
|
||||
}
|
||||
return UL_ENTRIES_PATH "/" + json_name;
|
||||
}
|
||||
|
||||
RecordInformation GetRecordInformation(const TitleRecord &record) {
|
||||
RecordInformation info = {};
|
||||
info.icon_path = GetRecordIconPath(record);
|
||||
const auto type = static_cast<TitleType>(record.title_type);
|
||||
if(type == TitleType::Homebrew) {
|
||||
if(record.title_type == TitleType::Homebrew) {
|
||||
auto f = fopen(record.nro_target.nro_path, "rb");
|
||||
if(f) {
|
||||
fseek(f, sizeof(NroStart), SEEK_SET);
|
||||
|
@ -137,7 +152,7 @@ namespace cfg {
|
|||
NacpStruct nacp = {};
|
||||
fseek(f, hdr.size + ahdr.nacp.offset, SEEK_SET);
|
||||
fread(&nacp, 1, ahdr.nacp.size, f);
|
||||
ProcessStringsFromNACP(info.strings, &nacp);
|
||||
ProcessStringsFromNacp(info.strings, &nacp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -146,9 +161,10 @@ namespace cfg {
|
|||
}
|
||||
}
|
||||
else {
|
||||
NsApplicationControlData cdata = {};
|
||||
nsGetApplicationControlData(NsApplicationControlSource_Storage, record.app_id, &cdata, sizeof(cdata), nullptr);
|
||||
ProcessStringsFromNACP(info.strings, &cdata.nacp);
|
||||
auto control_data = new NsApplicationControlData();
|
||||
nsGetApplicationControlData(NsApplicationControlSource_Storage, record.app_id, control_data, sizeof(NsApplicationControlData), nullptr);
|
||||
ProcessStringsFromNacp(info.strings, &control_data->nacp);
|
||||
delete control_data;
|
||||
}
|
||||
if(!record.name.empty()) {
|
||||
info.strings.name = record.name;
|
||||
|
@ -163,32 +179,34 @@ namespace cfg {
|
|||
}
|
||||
|
||||
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;
|
||||
Theme theme = {
|
||||
.base_name = base_name
|
||||
};
|
||||
auto theme_dir = UL_THEMES_PATH "/" + base_name;
|
||||
auto manifest_path = theme_dir + "/theme/Manifest.json";
|
||||
if(base_name.empty() || !fs::ExistsFile(manifest_path)) {
|
||||
theme_dir = CFG_THEME_DEFAULT;
|
||||
}
|
||||
metajson = themedir + "/theme/Manifest.json";
|
||||
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;
|
||||
manifest_path = theme_dir + "/theme/Manifest.json";
|
||||
|
||||
JSON manifest_json;
|
||||
if(R_SUCCEEDED(util::LoadJSONFromFile(manifest_json, manifest_path))) {
|
||||
theme.manifest.name = manifest_json.value("name", "'" + base_name + "'");
|
||||
theme.manifest.format_version = manifest_json.value("format_version", 0);
|
||||
theme.manifest.release = manifest_json.value("release", "");
|
||||
theme.manifest.description = manifest_json.value("description", "");
|
||||
theme.manifest.author = manifest_json.value("author", "");
|
||||
theme.path = theme_dir;
|
||||
return theme;
|
||||
}
|
||||
|
||||
return LoadTheme("");
|
||||
}
|
||||
|
||||
std::vector<Theme> LoadThemes() {
|
||||
std::vector<Theme> themes;
|
||||
UL_FS_FOR(std::string(UL_THEMES_PATH), name, path, {
|
||||
auto theme = LoadTheme(name);
|
||||
UL_FS_FOR(UL_THEMES_PATH, name, path, {
|
||||
const auto theme = LoadTheme(name);
|
||||
if(!theme.path.empty()) {
|
||||
themes.push_back(theme);
|
||||
}
|
||||
|
@ -201,10 +219,12 @@ namespace cfg {
|
|||
if(fs::ExistsFile(base_res)) {
|
||||
return base_res;
|
||||
}
|
||||
base_res = std::string(CFG_THEME_DEFAULT) + "/" + resource_base;
|
||||
|
||||
base_res = CFG_THEME_DEFAULT "/" + resource_base;
|
||||
if(fs::ExistsFile(base_res)) {
|
||||
return base_res;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -305,9 +325,9 @@ namespace cfg {
|
|||
}
|
||||
}
|
||||
|
||||
void SaveRecord(TitleRecord &record) {
|
||||
void SaveRecord(const TitleRecord &record) {
|
||||
auto entry = JSON::object();
|
||||
entry["type"] = record.title_type;
|
||||
entry["type"] = static_cast<u32>(record.title_type);
|
||||
entry["folder"] = record.sub_folder;
|
||||
|
||||
if(!record.name.empty()) {
|
||||
|
@ -323,16 +343,7 @@ namespace cfg {
|
|||
entry["icon"] = record.icon;
|
||||
}
|
||||
|
||||
// Prepare JSON path
|
||||
std::string basepath = UL_ENTRIES_PATH;
|
||||
std::string json = basepath;
|
||||
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;
|
||||
}
|
||||
json += "/" + jsonname;
|
||||
if(record.title_type == TitleType::Homebrew) {
|
||||
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) {
|
||||
|
@ -340,147 +351,50 @@ namespace cfg {
|
|||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
json += "/" + jsonname;
|
||||
entry["application_id"] = strappid;
|
||||
else if(record.title_type == TitleType::Installed) {
|
||||
entry["application_id"] = util::FormatApplicationId(record.app_id);
|
||||
}
|
||||
if(!record.json_name.empty()) {
|
||||
json = basepath + "/" + record.json_name;
|
||||
}
|
||||
if(fs::ExistsFile(json)) {
|
||||
fs::DeleteFile(json);
|
||||
|
||||
const auto json_path = GetRecordJsonPath(record);
|
||||
if(fs::ExistsFile(json_path)) {
|
||||
fs::DeleteFile(json_path);
|
||||
}
|
||||
|
||||
std::ofstream ofs(json);
|
||||
std::ofstream ofs(json_path);
|
||||
ofs << std::setw(4) << entry;
|
||||
ofs.close();
|
||||
}
|
||||
|
||||
void RemoveRecord(TitleRecord &record) {
|
||||
// Prepare JSON path
|
||||
std::string basepath = UL_ENTRIES_PATH;
|
||||
std::string json = basepath;
|
||||
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;
|
||||
}
|
||||
json += "/" + jsonname;
|
||||
}
|
||||
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;
|
||||
}
|
||||
json += "/" + jsonname;
|
||||
}
|
||||
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 MoveRecordTo(TitleList &list, const TitleRecord &record, const std::string &folder_name) {
|
||||
bool title_found = false;
|
||||
TitleRecord record_copy = {};
|
||||
std::string recjson;
|
||||
std::string record_json_name;
|
||||
|
||||
// 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(folder.empty()) return true; // It is already on root...?
|
||||
recjson = STL_UNWRAP(find).json_name;
|
||||
|
||||
list.root.titles.erase(find);
|
||||
title_found = true;
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
// It is already on that folder...?
|
||||
if(fld.name == folder) {
|
||||
return true;
|
||||
}
|
||||
recjson = STL_UNWRAP(find).json_name;
|
||||
|
||||
fld.titles.erase(find);
|
||||
title_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(title_found) {
|
||||
TitleRecord title = {};
|
||||
title.app_id = app_id;
|
||||
title.title_type = (u32)TitleType::Installed;
|
||||
title.json_name = recjson;
|
||||
title.sub_folder = folder;
|
||||
|
||||
// Add (move) it to root again
|
||||
if(folder.empty()) {
|
||||
list.root.titles.push_back(title);
|
||||
}
|
||||
// Add it to the new folder
|
||||
else {
|
||||
auto find2 = STL_FIND_IF(list.folders, fld, (fld.name == folder));
|
||||
if(STL_FOUND(list.folders, find2)) {
|
||||
STL_UNWRAP(find2).titles.push_back(title);
|
||||
}
|
||||
else {
|
||||
TitleFolder fld = {};
|
||||
fld.name = folder;
|
||||
fld.titles.push_back(title);
|
||||
list.folders.push_back(fld);
|
||||
}
|
||||
}
|
||||
|
||||
SaveRecord(title);
|
||||
}
|
||||
|
||||
return title_found;
|
||||
}
|
||||
|
||||
bool MoveRecordTo(TitleList &list, TitleRecord record, 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, record.Equals(tit));
|
||||
if(STL_FOUND(list.root.titles, find)) {
|
||||
const auto find_in_root = STL_FIND_IF(list.root.titles, title_item, record.Equals(title_item));
|
||||
if(STL_FOUND(list.root.titles, find_in_root)) {
|
||||
// It is already on root...?
|
||||
if(folder.empty()) {
|
||||
if(folder_name.empty()) {
|
||||
return true;
|
||||
}
|
||||
recjson = STL_UNWRAP(find).json_name;
|
||||
record_json_name = STL_UNWRAP(find_in_root).json_name;
|
||||
|
||||
list.root.titles.erase(find);
|
||||
list.root.titles.erase(find_in_root);
|
||||
title_found = true;
|
||||
}
|
||||
|
||||
// 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, tit, record.Equals(tit));
|
||||
if(STL_FOUND(fld.titles, find)) {
|
||||
for(auto &folder: list.folders) {
|
||||
const auto find_in_folder = STL_FIND_IF(folder.titles, title_item, record.Equals(title_item));
|
||||
if(STL_FOUND(folder.titles, find_in_folder)) {
|
||||
// It is already on that folder...?
|
||||
if(fld.name == folder) {
|
||||
if(folder.name == folder_name) {
|
||||
return true;
|
||||
}
|
||||
recjson = STL_UNWRAP(find).json_name;
|
||||
record_json_name = STL_UNWRAP(find_in_folder).json_name;
|
||||
|
||||
fld.titles.erase(find);
|
||||
folder.titles.erase(find_in_folder);
|
||||
title_found = true;
|
||||
break;
|
||||
}
|
||||
|
@ -489,24 +403,24 @@ namespace cfg {
|
|||
|
||||
if(title_found) {
|
||||
TitleRecord title = record;
|
||||
title.json_name = recjson;
|
||||
title.sub_folder = folder;
|
||||
title.json_name = record_json_name;
|
||||
title.sub_folder = folder_name;
|
||||
|
||||
// Add (move) it to root again
|
||||
if(folder.empty()) {
|
||||
if(folder_name.empty()) {
|
||||
list.root.titles.push_back(title);
|
||||
}
|
||||
// Add it to the new folder
|
||||
else {
|
||||
auto find2 = STL_FIND_IF(list.folders, fld, (fld.name == folder));
|
||||
if(STL_FOUND(list.folders, find2)) {
|
||||
STL_UNWRAP(find2).titles.push_back(title);
|
||||
const auto find_folder = STL_FIND_IF(list.folders, folder_item, (folder_item.name == folder_name));
|
||||
if(STL_FOUND(list.folders, find_folder)) {
|
||||
STL_UNWRAP(find_folder).titles.push_back(title);
|
||||
}
|
||||
else {
|
||||
TitleFolder fld = {};
|
||||
fld.name = folder;
|
||||
fld.titles.push_back(title);
|
||||
list.folders.push_back(fld);
|
||||
TitleFolder folder = {};
|
||||
folder.name = folder_name;
|
||||
folder.titles.push_back(title);
|
||||
list.folders.push_back(folder);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -537,25 +451,25 @@ namespace cfg {
|
|||
}
|
||||
}
|
||||
|
||||
bool ExistsRecord(TitleList &list, TitleRecord record) {
|
||||
bool ExistsRecord(const TitleList &list, const TitleRecord &record) {
|
||||
auto title_found = false;
|
||||
TitleRecord record_copy = {};
|
||||
std::string recjson;
|
||||
std::string record_json_name;
|
||||
|
||||
// Search in root first
|
||||
auto find = STL_FIND_IF(list.root.titles, tit, record.Equals(tit));
|
||||
if(STL_FOUND(list.root.titles, find)) {
|
||||
if(!STL_UNWRAP(find).json_name.empty()) {
|
||||
const auto find_in_root = STL_FIND_IF(list.root.titles, title_item, record.Equals(title_item));
|
||||
if(STL_FOUND(list.root.titles, find_in_root)) {
|
||||
if(!STL_UNWRAP(find_in_root).json_name.empty()) {
|
||||
title_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 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, tit, record.Equals(tit));
|
||||
if(STL_FOUND(fld.titles, find)) {
|
||||
if(!STL_UNWRAP(find).json_name.empty()) {
|
||||
for(auto &folder: list.folders) {
|
||||
const auto find_in_folder = STL_FIND_IF(folder.titles, title_item, record.Equals(title_item));
|
||||
if(STL_FOUND(folder.titles, find_in_folder)) {
|
||||
if(!STL_UNWRAP(find_in_folder).json_name.empty()) {
|
||||
title_found = true;
|
||||
break;
|
||||
}
|
||||
|
@ -572,78 +486,84 @@ namespace cfg {
|
|||
// Installed titles first
|
||||
auto titles = os::QueryInstalledTitles();
|
||||
|
||||
UL_FS_FOR(std::string(UL_ENTRIES_PATH), name, path, {
|
||||
UL_FS_FOR(UL_ENTRIES_PATH, name, path, {
|
||||
JSON entry;
|
||||
auto rc = util::LoadJSONFromFile(entry, path);
|
||||
const auto rc = util::LoadJSONFromFile(entry, path);
|
||||
if(R_SUCCEEDED(rc)) {
|
||||
auto type = static_cast<TitleType>(entry.value("type", 0));
|
||||
const auto type = static_cast<TitleType>(entry.value("type", static_cast<u32>(TitleType::Invalid)));
|
||||
if(type == TitleType::Installed) {
|
||||
std::string appidstr = entry.value("application_id", "");
|
||||
if(!appidstr.empty()) {
|
||||
std::string folder = entry.value("folder", "");
|
||||
auto appid = util::Get64FromString(appidstr);
|
||||
if(appid > 0) {
|
||||
const std::string app_id_str = entry.value("application_id", "");
|
||||
if(!app_id_str.empty()) {
|
||||
const std::string folder = entry.value("folder", "");
|
||||
const auto app_id = util::Get64FromString(app_id_str);
|
||||
if(app_id > 0) {
|
||||
if(!folder.empty()) {
|
||||
TitleRecord rec = {};
|
||||
rec.json_name = name;
|
||||
rec.app_id = appid;
|
||||
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", "");
|
||||
TitleRecord rec = {
|
||||
.json_name = name,
|
||||
.title_type = type,
|
||||
.sub_folder = folder,
|
||||
.icon = entry.value("icon", ""),
|
||||
.app_id = app_id,
|
||||
.name = entry.value("name", ""),
|
||||
.author = entry.value("author", ""),
|
||||
.version = entry.value("version", "")
|
||||
};
|
||||
|
||||
auto find = STL_FIND_IF(titles, tit, (tit.app_id == appid));
|
||||
if(STL_FOUND(titles, find)) {
|
||||
titles.erase(find);
|
||||
const auto find_title = STL_FIND_IF(titles, title_item, title_item.app_id == app_id);
|
||||
if(STL_FOUND(titles, find_title)) {
|
||||
titles.erase(find_title);
|
||||
}
|
||||
|
||||
auto find2 = STL_FIND_IF(list.folders, fld, (fld.name == folder));
|
||||
if(STL_FOUND(list.folders, find2)) {
|
||||
STL_UNWRAP(find2).titles.push_back(rec);
|
||||
const auto find_folder = STL_FIND_IF(list.folders, folder_item, folder_item.name == folder);
|
||||
if(STL_FOUND(list.folders, find_folder)) {
|
||||
STL_UNWRAP(find_folder).titles.push_back(rec);
|
||||
}
|
||||
else {
|
||||
TitleFolder fld = {};
|
||||
fld.name = folder;
|
||||
fld.titles.push_back(rec);
|
||||
list.folders.push_back(fld);
|
||||
TitleFolder title_folder = {
|
||||
.name = folder
|
||||
};
|
||||
title_folder.titles.push_back(rec);
|
||||
|
||||
list.folders.push_back(title_folder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(type == TitleType::Homebrew) {
|
||||
std::string nropath = entry.value("nro_path", "");
|
||||
if(fs::ExistsFile(nropath)) {
|
||||
TitleRecord rec = {};
|
||||
rec.json_name = name;
|
||||
rec.title_type = (u32)type;
|
||||
rec.name = entry.value("name", "");
|
||||
rec.author = entry.value("author", "");
|
||||
rec.version = entry.value("version", "");
|
||||
|
||||
std::string argv = entry.value("nro_argv", "");
|
||||
strcpy(rec.nro_target.nro_path, nropath.c_str());
|
||||
const std::string nro_path = entry.value("nro_path", "");
|
||||
if(fs::ExistsFile(nro_path)) {
|
||||
const std::string folder = entry.value("folder", "");
|
||||
TitleRecord rec = {
|
||||
.json_name = name,
|
||||
.title_type = type,
|
||||
.sub_folder = folder,
|
||||
.icon = entry.value("icon", ""),
|
||||
.name = entry.value("name", ""),
|
||||
.author = entry.value("author", ""),
|
||||
.version = entry.value("version", "")
|
||||
};
|
||||
const std::string argv = entry.value("nro_argv", "");
|
||||
strcpy(rec.nro_target.nro_path, nro_path.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 {
|
||||
auto find = STL_FIND_IF(list.folders, fld, (fld.name == folder));
|
||||
if(STL_FOUND(list.folders, find)) {
|
||||
STL_UNWRAP(find).titles.push_back(rec);
|
||||
const auto find_folder = STL_FIND_IF(list.folders, folder_item, folder_item.name == folder);
|
||||
if(STL_FOUND(list.folders, find_folder)) {
|
||||
STL_UNWRAP(find_folder).titles.push_back(rec);
|
||||
}
|
||||
else {
|
||||
TitleFolder fld = {};
|
||||
fld.name = folder;
|
||||
fld.titles.push_back(rec);
|
||||
list.folders.push_back(fld);
|
||||
TitleFolder title_folder = {
|
||||
.name = folder
|
||||
};
|
||||
title_folder.titles.push_back(rec);
|
||||
|
||||
list.folders.push_back(title_folder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -651,21 +571,26 @@ namespace cfg {
|
|||
}
|
||||
});
|
||||
|
||||
// Add cached icons to non-custom title entries
|
||||
for(auto &title: titles) {
|
||||
title.icon = cfg::GetTitleCacheIconPath(title.app_id);
|
||||
}
|
||||
|
||||
list.root.titles.insert(list.root.titles.end(), titles.begin(), titles.end());
|
||||
return list;
|
||||
}
|
||||
|
||||
std::string GetNROCacheIconPath(const std::string &path) {
|
||||
char pathcopy[FS_MAX_PATH] = {0};
|
||||
strcpy(pathcopy, path.c_str());
|
||||
std::string GetNroCacheIconPath(const std::string &path) {
|
||||
char path_copy[FS_MAX_PATH] = {};
|
||||
strcpy(path_copy, path.c_str());
|
||||
u8 hash[0x20] = {0};
|
||||
sha256CalculateHash(hash, pathcopy, FS_MAX_PATH);
|
||||
std::string out = UL_BASE_SD_DIR "/nro/";
|
||||
sha256CalculateHash(hash, path_copy, FS_MAX_PATH);
|
||||
|
||||
std::stringstream strm;
|
||||
strm << out;
|
||||
strm << UL_NRO_CACHE_PATH "/";
|
||||
// Use the first half of the hash, like N does with NCAs.
|
||||
for(u32 i = 0; i < sizeof(hash) / 2; i++) {
|
||||
strm << std::setw(2) << std::setfill('0') << std::hex << std::nouppercase << (u32)hash[i];
|
||||
strm << std::setw(2) << std::setfill('0') << std::hex << std::nouppercase << static_cast<u32>(hash[i]);
|
||||
}
|
||||
strm << ".jpg";
|
||||
return strm.str();
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace db {
|
|||
attr.system_save_data_id = HomeMenuSaveDataId;
|
||||
attr.save_data_type = FsSaveDataType_System;
|
||||
|
||||
R_TRY(fsOpenSaveDataFileSystemBySystemSaveDataId(&savefs, FsSaveDataSpaceId_System, &attr));
|
||||
UL_RC_TRY(fsOpenSaveDataFileSystemBySystemSaveDataId(&savefs, FsSaveDataSpaceId_System, &attr));
|
||||
fsdevMountDevice(UL_DB_MOUNT_NAME, savefs);
|
||||
|
||||
return ResultSuccess;
|
||||
|
|
|
@ -8,22 +8,20 @@ namespace dmi {
|
|||
|
||||
constexpr u32 MaxRetryCount = 10000;
|
||||
|
||||
inline Result LoopWait(Result(*cmd_fn)(AppletStorage*), AppletStorage *st, bool wait) {
|
||||
inline Result LoopWait(Result(*cmd_fn)(AppletStorage*), AppletStorage *st, const bool wait) {
|
||||
if(!wait) {
|
||||
return cmd_fn(st);
|
||||
}
|
||||
|
||||
u32 count = 0;
|
||||
|
||||
while(true) {
|
||||
auto rc = cmd_fn(st);
|
||||
if(R_SUCCEEDED(rc)) {
|
||||
if(R_SUCCEEDED(cmd_fn(st))) {
|
||||
break;
|
||||
}
|
||||
|
||||
count++;
|
||||
if(count > MaxRetryCount) {
|
||||
return 0xCAFA;
|
||||
return ResultWaitTimeout;
|
||||
}
|
||||
|
||||
svcSleepThread(10'000'000);
|
||||
|
@ -40,7 +38,7 @@ namespace dmi {
|
|||
return LoopWait(&am::LibraryAppletPush, st, false);
|
||||
}
|
||||
|
||||
Result PopStorage(AppletStorage *st, bool wait) {
|
||||
Result PopStorage(AppletStorage *st, const bool wait) {
|
||||
return LoopWait(&am::LibraryAppletPop, st, wait);
|
||||
}
|
||||
|
||||
|
@ -52,7 +50,7 @@ namespace dmi {
|
|||
return LoopWait(&appletPushOutData, st, false);
|
||||
}
|
||||
|
||||
Result PopStorage(AppletStorage *st, bool wait) {
|
||||
Result PopStorage(AppletStorage *st, const bool wait) {
|
||||
return LoopWait(&appletPopInData, st, wait);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
namespace net {
|
||||
|
||||
Result Initialize() {
|
||||
R_TRY(nifmInitialize(NifmServiceType_System));
|
||||
R_TRY(wlaninfInitialize());
|
||||
UL_RC_TRY(nifmInitialize(NifmServiceType_System));
|
||||
UL_RC_TRY(wlaninfInitialize());
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,33 +5,31 @@
|
|||
|
||||
namespace os {
|
||||
|
||||
std::string GetIconCacheImagePath(AccountUid user_id) {
|
||||
auto uidstr = util::Format128NintendoStyle(user_id);
|
||||
return UL_BASE_SD_DIR "/user/" + uidstr + ".jpg";
|
||||
std::string GetIconCacheImagePath(const AccountUid user_id) {
|
||||
const auto uid_str = util::Format128NintendoStyle(user_id);
|
||||
return UL_BASE_SD_DIR "/user/" + uid_str + ".jpg";
|
||||
}
|
||||
|
||||
Result QuerySystemAccounts(std::vector<AccountUid> &out_accounts, bool dump_icon) {
|
||||
Result QuerySystemAccounts(const bool dump_icon, std::vector<AccountUid> &out_accounts) {
|
||||
AccountUid uids[ACC_USER_LIST_SIZE] = {};
|
||||
s32 acc_count = 0;
|
||||
R_TRY(accountListAllUsers(uids, ACC_USER_LIST_SIZE, &acc_count));
|
||||
UL_RC_TRY(accountListAllUsers(uids, ACC_USER_LIST_SIZE, &acc_count));
|
||||
for(s32 i = 0; i < acc_count; i++) {
|
||||
auto uid = uids[i];
|
||||
const auto uid = uids[i];
|
||||
out_accounts.push_back(uid);
|
||||
if(dump_icon) {
|
||||
AccountProfile prof;
|
||||
auto rc = accountGetProfile(&prof, uid);
|
||||
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(uid);
|
||||
fs::WriteFile(iconcache, imgbuf, imgsz, true);
|
||||
if(R_SUCCEEDED(accountGetProfile(&prof, uid))) {
|
||||
u32 img_size = 0;
|
||||
accountProfileGetImageSize(&prof, &img_size);
|
||||
if(img_size > 0) {
|
||||
auto img_buf = new u8[img_size]();
|
||||
u32 tmp_size;
|
||||
if(R_SUCCEEDED(accountProfileLoadImage(&prof, img_buf, img_size, &tmp_size))) {
|
||||
const auto cache_icon_path = GetIconCacheImagePath(uid);
|
||||
fs::WriteFile(cache_icon_path, img_buf, img_size, true);
|
||||
}
|
||||
delete[] imgbuf;
|
||||
delete[] img_buf;
|
||||
}
|
||||
accountProfileClose(&prof);
|
||||
}
|
||||
|
@ -40,16 +38,15 @@ namespace os {
|
|||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result GetAccountName(std::string &out_name, AccountUid user_id) {
|
||||
std::string name;
|
||||
Result GetAccountName(const AccountUid user_id, std::string &out_name) {
|
||||
AccountProfile prof;
|
||||
R_TRY(accountGetProfile(&prof, user_id));
|
||||
UL_ON_SCOPE_EXIT({
|
||||
accountProfileClose(&prof);
|
||||
});
|
||||
UL_RC_TRY(accountGetProfile(&prof, user_id));
|
||||
UL_ON_SCOPE_EXIT({ accountProfileClose(&prof); });
|
||||
|
||||
AccountProfileBase pbase;
|
||||
AccountUserData udata;
|
||||
R_TRY(accountProfileGet(&prof, &udata, &pbase));
|
||||
UL_RC_TRY(accountProfileGet(&prof, &udata, &pbase));
|
||||
|
||||
out_name = pbase.nickname;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
|
|
@ -2,14 +2,13 @@
|
|||
|
||||
namespace os {
|
||||
|
||||
Result PushSystemAppletMessage(SystemAppletMessage msg) {
|
||||
Result PushSystemAppletMessage(const SystemAppletMessage msg) {
|
||||
AppletStorage st;
|
||||
R_TRY(appletCreateStorage(&st, sizeof(msg)));
|
||||
UL_ON_SCOPE_EXIT({
|
||||
appletStorageClose(&st);
|
||||
});
|
||||
R_TRY(appletStorageWrite(&st, 0, &msg, sizeof(msg)));
|
||||
R_TRY(appletPushToGeneralChannel(&st));
|
||||
UL_RC_TRY(appletCreateStorage(&st, sizeof(msg)));
|
||||
UL_ON_SCOPE_EXIT({ appletStorageClose(&st); });
|
||||
|
||||
UL_RC_TRY(appletStorageWrite(&st, 0, &msg, sizeof(msg)));
|
||||
UL_RC_TRY(appletPushToGeneralChannel(&st));
|
||||
return ResultSuccess;
|
||||
}
|
||||
}
|
|
@ -16,10 +16,11 @@ namespace os {
|
|||
}
|
||||
|
||||
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;
|
||||
const auto time_val = time(nullptr);
|
||||
const auto local_time = localtime(&time_val);
|
||||
const auto h = local_time->tm_hour;
|
||||
const auto min = local_time->tm_min;
|
||||
|
||||
char str[0x10] = {};
|
||||
sprintf(str, "%02d:%02d", h, min);
|
||||
return str;
|
||||
|
|
|
@ -6,15 +6,15 @@ namespace os {
|
|||
std::vector<cfg::TitleRecord> titles;
|
||||
auto records_buf = new NsApplicationRecord[MaxTitleCount]();
|
||||
s32 record_count;
|
||||
UL_ASSERT(nsListApplicationRecord(records_buf, MaxTitleCount, 0, &record_count));
|
||||
UL_RC_ASSERT(nsListApplicationRecord(records_buf, MaxTitleCount, 0, &record_count));
|
||||
for(s32 i = 0; i < record_count; i++) {
|
||||
const auto &record = records_buf[i];
|
||||
if(record.application_id == 0) {
|
||||
continue;
|
||||
}
|
||||
const cfg::TitleRecord rec = {
|
||||
.title_type = static_cast<u32>(cfg::TitleType::Installed),
|
||||
.app_id = record.application_id,
|
||||
.title_type = cfg::TitleType::Installed,
|
||||
.app_id = record.application_id
|
||||
};
|
||||
titles.push_back(std::move(rec));
|
||||
}
|
||||
|
|
|
@ -11,23 +11,30 @@ namespace res {
|
|||
const char *name;
|
||||
};
|
||||
|
||||
#define _RES_INFO_DEFINE(mod, name) { mod::Result ## name, #mod, #name }
|
||||
#define _UL_RC_INFO_DEFINE(mod, name) { mod::Result ## name, #mod, #name }
|
||||
constexpr ResultInfoImpl g_ResultInfoTableImpl[] = {
|
||||
_UL_RC_INFO_DEFINE(misc, AssertionFailed),
|
||||
_UL_RC_INFO_DEFINE(misc, UnexpectedTransform),
|
||||
_UL_RC_INFO_DEFINE(misc, InvalidJsonFile),
|
||||
|
||||
static ResultInfoImpl g_ResultInfoTableImpl[] = {
|
||||
_RES_INFO_DEFINE(misc, InvalidJsonFile),
|
||||
_UL_RC_INFO_DEFINE(dmn, ApplicationActive),
|
||||
_UL_RC_INFO_DEFINE(dmn, InvalidSelectedUser),
|
||||
_UL_RC_INFO_DEFINE(dmn, AlreadyQueued),
|
||||
_UL_RC_INFO_DEFINE(dmn, ApplicationNotActive),
|
||||
|
||||
_RES_INFO_DEFINE(dmn, ApplicationActive),
|
||||
_RES_INFO_DEFINE(dmn, InvalidSelectedUser),
|
||||
_RES_INFO_DEFINE(dmn, AlreadyQueued),
|
||||
_RES_INFO_DEFINE(dmn, ApplicationNotActive),
|
||||
_UL_RC_INFO_DEFINE(menu, RomfsFileNotFound),
|
||||
|
||||
_RES_INFO_DEFINE(menu, RomfsFileNotFound),
|
||||
_UL_RC_INFO_DEFINE(ipc, InvalidProcess),
|
||||
|
||||
_RES_INFO_DEFINE(ipc, InvalidProcess),
|
||||
_UL_RC_INFO_DEFINE(dmi, OutOfPushSpace),
|
||||
_UL_RC_INFO_DEFINE(dmi, OutOfPopSpace),
|
||||
_UL_RC_INFO_DEFINE(dmi, InvalidInHeaderMagic),
|
||||
_UL_RC_INFO_DEFINE(dmi, InvalidOutHeaderMagic),
|
||||
};
|
||||
#undef _UL_RC_INFO_DEFINE
|
||||
constexpr size_t ResultInfoTableImplCount = sizeof(g_ResultInfoTableImpl) / sizeof(ResultInfoImpl);
|
||||
|
||||
inline constexpr ResultInfoImpl &GetResultInfo(const u32 idx) {
|
||||
inline constexpr ResultInfoImpl GetResultInfo(const u32 idx) {
|
||||
return g_ResultInfoTableImpl[idx];
|
||||
}
|
||||
|
||||
|
@ -53,7 +60,7 @@ namespace res {
|
|||
}
|
||||
}
|
||||
|
||||
return "Unknown module";
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string GetNameByResult(const Result rc) {
|
||||
|
@ -64,7 +71,7 @@ namespace res {
|
|||
}
|
||||
}
|
||||
|
||||
return "Unknown name";
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
|
@ -27,7 +27,7 @@ EXEFS_SRC := exefs_src
|
|||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE -Wl,-wrap,libappletStart -Wl,-wrap,libappletLaunch
|
||||
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE -Wl,-wrap,libappletLaunch -Wl,-wrap,libappletStart
|
||||
|
||||
CFLAGS := -g -Wall -O2 -ffunction-sections \
|
||||
$(ARCH) $(DEFINES)
|
||||
|
@ -45,7 +45,7 @@ LIBS := -lpu -lfreetype -lSDL2_mixer -lopusfile -lopus -lmodplug -lmpg123 -lvorb
|
|||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(PORTLIBS) $(LIBNX) $(TOPDIR)/../Plutonium/Plutonium/out
|
||||
LIBDIRS := $(PORTLIBS) $(LIBNX) $(CURDIR)/../Plutonium/Plutonium
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
|
|
|
@ -9,6 +9,6 @@ namespace am {
|
|||
|
||||
Result InitializeDaemonMessageHandler();
|
||||
void ExitDaemonMessageHandler();
|
||||
void RegisterOnMessageDetect(OnMessageCallback callback, dmi::MenuMessage desired_msg);
|
||||
void RegisterOnMessageDetect(OnMessageCallback callback, const dmi::MenuMessage desired_msg);
|
||||
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
namespace am {
|
||||
|
||||
void RegisterLibAppletHomeButtonDetection();
|
||||
|
||||
}
|
8
uMenu/include/am/am_LibnxLibappletWrap.hpp
Normal file
8
uMenu/include/am/am_LibnxLibappletWrap.hpp
Normal file
|
@ -0,0 +1,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
namespace am {
|
||||
|
||||
void RegisterLibnxLibappletHomeButtonDetection();
|
||||
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
namespace am {
|
||||
|
||||
Result ReadDataFromStorage(void *data, size_t data_size);
|
||||
Result ReadDataFromStorage(void *out_data, const size_t data_size);
|
||||
Result ReadStartMode(dmi::MenuStartMode &out_start_mode);
|
||||
|
||||
}
|
|
@ -8,52 +8,82 @@
|
|||
namespace ui {
|
||||
|
||||
class ClickableImage : public pu::ui::elm::Element {
|
||||
public:
|
||||
using OnClickCallback = std::function<void()>;
|
||||
|
||||
private:
|
||||
inline void Dispose() {
|
||||
if(this->ntex != nullptr) {
|
||||
pu::ui::render::DeleteTexture(this->ntex);
|
||||
this->ntex = nullptr;
|
||||
}
|
||||
}
|
||||
static constexpr u64 TouchActionTimeMilliseconds = 200;
|
||||
|
||||
static constexpr u8 RedColorMod = 0xC8;
|
||||
static constexpr u8 GreenColorMod = 0xC8;
|
||||
static constexpr u8 BlueColorMod = 0xFF;
|
||||
|
||||
protected:
|
||||
std::string img;
|
||||
pu::sdl2::Texture ntex;
|
||||
pu::sdl2::Texture img_tex;
|
||||
s32 x;
|
||||
s32 y;
|
||||
s32 w;
|
||||
s32 h;
|
||||
std::function<void()> cb;
|
||||
std::chrono::steady_clock::time_point touchtp;
|
||||
OnClickCallback cb;
|
||||
std::chrono::steady_clock::time_point touch_tp;
|
||||
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) {
|
||||
ClickableImage(const s32 x, const s32 y, const std::string &img) : Element(), img_tex(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);
|
||||
~ClickableImage();
|
||||
|
||||
inline s32 GetX() override {
|
||||
return this->x;
|
||||
}
|
||||
|
||||
inline void SetX(const s32 x) {
|
||||
this->x = x;
|
||||
}
|
||||
|
||||
inline s32 GetY() override {
|
||||
return this->y;
|
||||
}
|
||||
|
||||
inline void SetY(const s32 y) {
|
||||
this->y = y;
|
||||
}
|
||||
|
||||
inline s32 GetWidth() override {
|
||||
return this->w;
|
||||
}
|
||||
|
||||
inline void SetWidth(const s32 w) {
|
||||
this->w = w;
|
||||
}
|
||||
|
||||
inline s32 GetHeight() override {
|
||||
return this->h;
|
||||
}
|
||||
|
||||
inline void SetHeight(const s32 h) {
|
||||
this->h = h;
|
||||
}
|
||||
|
||||
inline std::string GetImage() {
|
||||
return this->img;
|
||||
}
|
||||
|
||||
void SetImage(const std::string &img);
|
||||
|
||||
inline bool IsImageValid() {
|
||||
return this->img_tex != nullptr;
|
||||
}
|
||||
|
||||
inline void SetOnClick(OnClickCallback cb) {
|
||||
this->cb = cb;
|
||||
}
|
||||
|
||||
void OnRender(pu::ui::render::Renderer::Ref &drawer, const s32 x, const s32 y) override;
|
||||
void OnInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos) override;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,23 +1,21 @@
|
|||
|
||||
#pragma once
|
||||
#include <pu/Plutonium>
|
||||
#include <ul_Include.hpp>
|
||||
#include <pu/Plutonium>
|
||||
|
||||
namespace ui {
|
||||
|
||||
class IMenuLayout : public pu::ui::Layout {
|
||||
|
||||
private:
|
||||
std::atomic_bool home_pressed;
|
||||
|
||||
public:
|
||||
IMenuLayout();
|
||||
|
||||
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 OnInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos);
|
||||
void DoOnHomeButtonPress();
|
||||
virtual void OnMenuInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos) = 0;
|
||||
virtual bool OnHomeButtonPress() = 0;
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -1,27 +1,24 @@
|
|||
|
||||
#pragma once
|
||||
#include <ul_Include.hpp>
|
||||
#include <ui/ui_IMenuLayout.hpp>
|
||||
#include <cfg/cfg_Config.hpp>
|
||||
|
||||
namespace ui {
|
||||
|
||||
class LanguagesMenuLayout : public IMenuLayout {
|
||||
|
||||
private:
|
||||
pu::ui::elm::TextBlock::Ref infoText;
|
||||
pu::ui::elm::Menu::Ref langsMenu;
|
||||
pu::ui::elm::TextBlock::Ref info_text;
|
||||
pu::ui::elm::Menu::Ref langs_menu;
|
||||
|
||||
void lang_DefaultKey(const u32 idx);
|
||||
|
||||
public:
|
||||
LanguagesMenuLayout();
|
||||
PU_SMART_CTOR(LanguagesMenuLayout)
|
||||
|
||||
void OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) override;
|
||||
void OnMenuInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos) override;
|
||||
bool OnHomeButtonPress() override;
|
||||
|
||||
void Reload();
|
||||
void lang_Click(u32 idx);
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
namespace ui {
|
||||
|
||||
// TODO: user-defined literal? ---> "ok"_lang
|
||||
std::string GetLanguageString(const std::string &name);
|
||||
|
||||
enum class MenuType {
|
||||
|
@ -17,35 +18,37 @@ namespace ui {
|
|||
Main,
|
||||
Settings,
|
||||
Theme,
|
||||
Languages,
|
||||
Languages
|
||||
};
|
||||
|
||||
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;
|
||||
dmi::MenuStartMode start_mode;
|
||||
StartupLayout::Ref startup_lyt;
|
||||
MenuLayout::Ref menu_lyt;
|
||||
ThemeMenuLayout::Ref theme_menu_lyt;
|
||||
SettingsMenuLayout::Ref settings_menu_lyt;
|
||||
LanguagesMenuLayout::Ref languages_menu_lyt;
|
||||
pu::ui::extras::Toast::Ref notif_toast;
|
||||
dmi::DaemonStatus daemon_status;
|
||||
MenuType loaded_menu;
|
||||
JSON uijson;
|
||||
JSON bgmjson;
|
||||
JSON ui_json;
|
||||
JSON bgm_json;
|
||||
bool bgm_loop;
|
||||
u32 bgm_fade_in_ms;
|
||||
u32 bgm_fade_out_ms;
|
||||
pu::audio::Music bgm;
|
||||
pu::ui::Color text_clr;
|
||||
pu::ui::Color menu_focus_clr;
|
||||
pu::ui::Color menu_bg_clr;
|
||||
|
||||
public:
|
||||
using Application::Application;
|
||||
|
||||
~MenuApplication() {
|
||||
pu::audio::Delete(this->bgm);
|
||||
pu::audio::DestroyMusic(this->bgm);
|
||||
}
|
||||
|
||||
PU_SMART_CTOR(MenuApplication)
|
||||
|
@ -56,69 +59,155 @@ namespace ui {
|
|||
|
||||
void OnLoad() override;
|
||||
|
||||
void SetInformation(dmi::MenuStartMode mode, dmi::DaemonStatus status, JSON ui_json);
|
||||
void LoadMenu();
|
||||
void LoadStartupMenu();
|
||||
void LoadThemeMenu();
|
||||
void LoadSettingsMenu();
|
||||
void LoadSettingsLanguagesMenu();
|
||||
inline void SetInformation(const dmi::MenuStartMode start_mode, const dmi::DaemonStatus daemon_status, const JSON ui_json) {
|
||||
this->start_mode = start_mode;
|
||||
this->daemon_status = daemon_status;
|
||||
this->ui_json = ui_json;
|
||||
}
|
||||
|
||||
bool IsSuspended();
|
||||
bool IsTitleSuspended();
|
||||
bool IsHomebrewSuspended();
|
||||
bool EqualsSuspendedHomebrewPath(const std::string &path);
|
||||
u64 GetSuspendedApplicationId();
|
||||
void NotifyEndSuspended();
|
||||
bool LaunchFailed();
|
||||
void ShowNotification(const std::string &text, u64 timeout = 1500);
|
||||
inline void LoadMenu() {
|
||||
this->menu_lyt->SetUser(this->daemon_status.selected_user);
|
||||
this->LoadLayout(this->menu_lyt);
|
||||
this->loaded_menu = MenuType::Main;
|
||||
}
|
||||
|
||||
inline void LoadStartupMenu() {
|
||||
this->StopPlayBGM();
|
||||
this->startup_lyt->ReloadMenu();
|
||||
this->LoadLayout(this->startup_lyt);
|
||||
this->loaded_menu = MenuType::Startup;
|
||||
}
|
||||
|
||||
inline void LoadThemeMenu() {
|
||||
this->theme_menu_lyt->Reload();
|
||||
this->LoadLayout(this->theme_menu_lyt);
|
||||
this->loaded_menu = MenuType::Theme;
|
||||
}
|
||||
|
||||
inline void LoadSettingsMenu() {
|
||||
this->settings_menu_lyt->Reload(true);
|
||||
this->LoadLayout(this->settings_menu_lyt);
|
||||
this->loaded_menu = MenuType::Settings;
|
||||
}
|
||||
|
||||
inline void LoadSettingsLanguagesMenu() {
|
||||
this->languages_menu_lyt->Reload();
|
||||
this->LoadLayout(this->languages_menu_lyt);
|
||||
this->loaded_menu = MenuType::Languages;
|
||||
}
|
||||
|
||||
inline bool IsTitleSuspended() {
|
||||
return this->daemon_status.app_id != 0;
|
||||
}
|
||||
|
||||
inline bool IsHomebrewSuspended() {
|
||||
return strlen(this->daemon_status.params.nro_path) > 0;
|
||||
}
|
||||
|
||||
inline bool IsSuspended() {
|
||||
return this->IsTitleSuspended() || this->IsHomebrewSuspended();
|
||||
}
|
||||
|
||||
inline bool EqualsSuspendedHomebrewPath(const std::string &path) {
|
||||
return this->daemon_status.params.nro_path == path;
|
||||
}
|
||||
|
||||
inline u64 GetSuspendedApplicationId() {
|
||||
return this->daemon_status.app_id;
|
||||
}
|
||||
|
||||
inline void NotifyEndSuspended() {
|
||||
// Blanking the whole status would also blank the selected user, thus we only blank the params
|
||||
this->daemon_status.params = {};
|
||||
this->daemon_status.app_id = 0;
|
||||
}
|
||||
|
||||
inline bool LaunchFailed() {
|
||||
return this->start_mode == dmi::MenuStartMode::MenuLaunchFailure;
|
||||
}
|
||||
|
||||
void ShowNotification(const std::string &text, const u64 timeout = 1500);
|
||||
|
||||
template<typename T>
|
||||
inline T GetUIConfigValue(const std::string &name, T def) {
|
||||
return this->uijson.value<T>(name, def);
|
||||
inline T GetUIConfigValue(const std::string &name, const T def) {
|
||||
return this->ui_json.value<T>(name, def);
|
||||
}
|
||||
|
||||
template<typename Elem>
|
||||
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)) {
|
||||
auto jelem = jmenu[name];
|
||||
bool set_coords = false;
|
||||
if(also_visible) {
|
||||
bool visible = jelem.value("visible", true);
|
||||
Ref->SetVisible(visible);
|
||||
inline void ApplyConfigForElement(const std::string &menu, const std::string &name, std::shared_ptr<Elem> &elem, const bool apply_visible = true) {
|
||||
if(this->ui_json.count(menu)) {
|
||||
const auto menu_json = this->ui_json[menu];
|
||||
if(menu_json.count(name)) {
|
||||
const auto elem_json = menu_json[name];
|
||||
|
||||
auto set_coords = false;
|
||||
if(apply_visible) {
|
||||
const auto visible = elem_json.value("visible", true);
|
||||
elem->SetVisible(visible);
|
||||
set_coords = visible;
|
||||
}
|
||||
else {
|
||||
set_coords = true;
|
||||
}
|
||||
|
||||
if(set_coords) {
|
||||
if(jelem.count("x")) {
|
||||
s32 x = jelem["x"];
|
||||
Ref->SetX(x);
|
||||
if(elem_json.count("x")) {
|
||||
const s32 x = elem_json.value("x", 0);
|
||||
elem->SetX(x);
|
||||
}
|
||||
if(jelem.count("y")) {
|
||||
s32 y = jelem["y"];
|
||||
Ref->SetY(y);
|
||||
if(elem_json.count("y")) {
|
||||
const s32 y = elem_json.value("y", 0);
|
||||
elem->SetY(y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline pu::ui::Color GetTextColor() {
|
||||
return this->text_clr;
|
||||
}
|
||||
|
||||
inline pu::ui::Color GetMenuFocusColor() {
|
||||
return this->menu_focus_clr;
|
||||
}
|
||||
|
||||
inline pu::ui::Color GetMenuBackgroundColor() {
|
||||
return this->menu_bg_clr;
|
||||
}
|
||||
|
||||
void StartPlayBGM();
|
||||
void StopPlayBGM();
|
||||
|
||||
StartupLayout::Ref &GetStartupLayout();
|
||||
MenuLayout::Ref &GetMenuLayout();
|
||||
ThemeMenuLayout::Ref &GetThemeMenuLayout();
|
||||
SettingsMenuLayout::Ref &GetSettingsMenuLayout();
|
||||
LanguagesMenuLayout::Ref &GetLanguagesMenuLayout();
|
||||
inline StartupLayout::Ref &GetStartupLayout() {
|
||||
return this->startup_lyt;
|
||||
}
|
||||
|
||||
void SetSelectedUser(AccountUid user_id);
|
||||
AccountUid GetSelectedUser();
|
||||
MenuType GetCurrentLoadedMenu();
|
||||
|
||||
inline MenuLayout::Ref &GetMenuLayout() {
|
||||
return this->menu_lyt;
|
||||
}
|
||||
|
||||
inline ThemeMenuLayout::Ref &GetThemeMenuLayout() {
|
||||
return this->theme_menu_lyt;
|
||||
}
|
||||
|
||||
inline SettingsMenuLayout::Ref &GetSettingsMenuLayout() {
|
||||
return this->settings_menu_lyt;
|
||||
}
|
||||
|
||||
inline LanguagesMenuLayout::Ref &GetLanguagesMenuLayout() {
|
||||
return this->languages_menu_lyt;
|
||||
}
|
||||
|
||||
void SetSelectedUser(const AccountUid user_id);
|
||||
|
||||
inline AccountUid GetSelectedUser() {
|
||||
return this->daemon_status.selected_user;
|
||||
}
|
||||
|
||||
inline MenuType GetCurrentLoadedMenu() {
|
||||
return this->loaded_menu;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
#include <ul_Include.hpp>
|
||||
#include <ui/ui_IMenuLayout.hpp>
|
||||
#include <ui/ui_SideMenu.hpp>
|
||||
#include <ui/ui_RawData.hpp>
|
||||
#include <ui/ui_RawRgbaImage.hpp>
|
||||
#include <ui/ui_ClickableImage.hpp>
|
||||
#include <ui/ui_QuickMenu.hpp>
|
||||
#include <ui/ui_Actions.hpp>
|
||||
|
@ -12,83 +12,91 @@
|
|||
namespace ui {
|
||||
|
||||
class MenuLayout : public IMenuLayout {
|
||||
public:
|
||||
// 16:9 ratio
|
||||
static constexpr u32 SuspendedImageWidthIncrement = (u32)30.0f;
|
||||
static constexpr u32 SuspendedImageHeightIncrement = (u32)(SuspendedImageWidthIncrement / (16.0f / 9.0f));
|
||||
|
||||
static constexpr u8 SuspendedScreenAlphaIncrement = 10;
|
||||
|
||||
private:
|
||||
void *susptr;
|
||||
bool last_hasconn;
|
||||
u32 last_batterylvl;
|
||||
bool last_charge;
|
||||
pu::ui::elm::Image::Ref topMenuImage;
|
||||
pu::ui::elm::Image::Ref connIcon;
|
||||
ClickableImage::Ref users;
|
||||
ClickableImage::Ref controller;
|
||||
ClickableImage::Ref logo;
|
||||
pu::ui::elm::TextBlock::Ref timeText;
|
||||
pu::ui::elm::TextBlock::Ref batteryText;
|
||||
pu::ui::elm::Image::Ref batteryIcon;
|
||||
ClickableImage::Ref settings;
|
||||
ClickableImage::Ref themes;
|
||||
pu::ui::elm::TextBlock::Ref fwText;
|
||||
SideMenu::Ref itemsMenu;
|
||||
RawData::Ref bgSuspendedRaw;
|
||||
pu::ui::elm::TextBlock::Ref itemName;
|
||||
pu::ui::elm::TextBlock::Ref itemAuthor;
|
||||
pu::ui::elm::TextBlock::Ref itemVersion;
|
||||
pu::ui::elm::Image::Ref bannerImage;
|
||||
pu::ui::elm::Image::Ref guideButtons;
|
||||
ClickableImage::Ref menuToggle;
|
||||
QuickMenu::Ref quickMenu;
|
||||
std::string curfolder;
|
||||
std::chrono::steady_clock::time_point tp;
|
||||
bool warnshown;
|
||||
bool last_has_connection;
|
||||
u32 last_battery_lvl;
|
||||
bool last_is_charging;
|
||||
pu::ui::elm::Image::Ref top_menu_img;
|
||||
pu::ui::elm::Image::Ref connection_icon;
|
||||
ClickableImage::Ref users_img;
|
||||
ClickableImage::Ref controller_img;
|
||||
ClickableImage::Ref logo_img;
|
||||
pu::ui::elm::TextBlock::Ref time_text;
|
||||
pu::ui::elm::TextBlock::Ref battery_text;
|
||||
pu::ui::elm::Image::Ref battery_icon;
|
||||
ClickableImage::Ref settings_img;
|
||||
ClickableImage::Ref themes_img;
|
||||
pu::ui::elm::TextBlock::Ref fw_text;
|
||||
SideMenu::Ref items_menu;
|
||||
RawRgbaImage::Ref suspended_screen_img;
|
||||
pu::ui::elm::TextBlock::Ref selected_item_name_text;
|
||||
pu::ui::elm::TextBlock::Ref selected_item_author_text;
|
||||
pu::ui::elm::TextBlock::Ref selected_item_version_text;
|
||||
pu::ui::elm::Image::Ref banner_img;
|
||||
pu::ui::elm::Image::Ref guide_buttons_img;
|
||||
ClickableImage::Ref menu_totggle_img;
|
||||
QuickMenu::Ref quick_menu;
|
||||
std::string cur_folder;
|
||||
std::chrono::steady_clock::time_point startup_tp;
|
||||
bool launch_fail_warn_shown;
|
||||
bool homebrew_mode;
|
||||
bool select_on;
|
||||
bool select_dir;
|
||||
u8 minalpha;
|
||||
u8 min_alpha;
|
||||
u32 mode;
|
||||
s32 rawalpha;
|
||||
pu::audio::Sfx sfxTitleLaunch;
|
||||
pu::audio::Sfx sfxMenuToggle;
|
||||
s32 suspended_screen_alpha;
|
||||
pu::audio::Sfx title_launch_sfx;
|
||||
pu::audio::Sfx menu_toggle_sfx;
|
||||
|
||||
void DoMoveFolder(const std::string &name);
|
||||
|
||||
void menu_Click(const u64 keys_down, const u32 idx);
|
||||
void menu_OnSelected(const u32 idx);
|
||||
void menuToggle_Click();
|
||||
|
||||
inline void ApplySuspendedRatio(const bool increase) {
|
||||
auto susp_w = this->suspended_screen_img->GetWidth();
|
||||
auto susp_h = this->suspended_screen_img->GetHeight();
|
||||
|
||||
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;
|
||||
susp_w += (s32)SuspendedImageWidthIncrement;
|
||||
susp_h += (s32)SuspendedImageHeightIncrement;
|
||||
}
|
||||
else {
|
||||
susp_w -= 16;
|
||||
susp_h -= 9;
|
||||
susp_w -= (s32)SuspendedImageWidthIncrement;
|
||||
susp_h -= (s32)SuspendedImageHeightIncrement;
|
||||
}
|
||||
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);
|
||||
|
||||
const auto susp_x = (pu::ui::render::ScreenWidth - susp_w) / 2;
|
||||
const auto susp_y = (pu::ui::render::ScreenHeight - susp_h) / 2;
|
||||
this->suspended_screen_img->SetX(susp_x);
|
||||
this->suspended_screen_img->SetY(susp_y);
|
||||
this->suspended_screen_img->SetWidth(susp_w);
|
||||
this->suspended_screen_img->SetHeight(susp_h);
|
||||
}
|
||||
|
||||
public:
|
||||
MenuLayout(void *raw, u8 min_alpha);
|
||||
MenuLayout(const u8 *captured_screen_buf, const u8 min_alpha);
|
||||
~MenuLayout();
|
||||
PU_SMART_CTOR(MenuLayout)
|
||||
|
||||
void OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) override;
|
||||
void OnMenuInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos) override;
|
||||
bool 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 MoveFolder(const std::string &name, const bool fade);
|
||||
void SetUser(const AccountUid user);
|
||||
void HandleCloseSuspended();
|
||||
void HandleHomebrewLaunch(cfg::TitleRecord &rec);
|
||||
void HandleHomebrewLaunch(const cfg::TitleRecord &rec);
|
||||
void HandleMultiselectMoveToFolder(const std::string &folder);
|
||||
void StopMultiselect();
|
||||
void DoTerminateApplication();
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -5,59 +5,70 @@
|
|||
|
||||
namespace ui {
|
||||
|
||||
enum class QuickMenuDirection {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
UpLeft,
|
||||
UpRight,
|
||||
DownLeft,
|
||||
DownRight,
|
||||
None
|
||||
};
|
||||
|
||||
struct QuickMenuSubItem {
|
||||
std::function<void()> on_select;
|
||||
pu::sdl2::Texture nicon;
|
||||
};
|
||||
|
||||
void QuickMenuOnHomeButtonDetection();
|
||||
|
||||
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;
|
||||
static constexpr s32 MainItemX = (pu::ui::render::ScreenWidth - MainItemSize) / 2;
|
||||
static constexpr s32 MainItemY = (pu::ui::render::ScreenHeight - MainItemSize) / 2;
|
||||
|
||||
static constexpr u8 BackgroundAlphaMax = 0xDC;
|
||||
static constexpr u8 BackgroundAlphaIncrement = 20;
|
||||
|
||||
static constexpr u32 MenuMargin = 200;
|
||||
static constexpr u32 MenuX = MenuMargin;
|
||||
static constexpr u32 MenuY = 115;
|
||||
static constexpr u32 MenuWidth = pu::ui::render::ScreenWidth - 2 * MenuMargin;
|
||||
static constexpr u32 MenuItemHeight = 60;
|
||||
static constexpr u32 MenuItemsToShow = 8;
|
||||
|
||||
static inline constexpr pu::ui::Color MakeBackgroundColor(const u8 alpha) {
|
||||
return { 50, 50, 50, alpha };
|
||||
}
|
||||
|
||||
private:
|
||||
bool on;
|
||||
s32 bgalpha;
|
||||
s32 bg_alpha;
|
||||
pu::ui::elm::Menu::Ref options_menu;
|
||||
|
||||
static void OnHomeButtonDetection();
|
||||
|
||||
public:
|
||||
QuickMenu(const std::string &main_icon);
|
||||
PU_SMART_CTOR(QuickMenu)
|
||||
|
||||
static inline void RegisterHomeButtonDetection() {
|
||||
am::RegisterOnMessageDetect(&QuickMenuOnHomeButtonDetection, dmi::MenuMessage::HomeRequest);
|
||||
inline constexpr s32 GetX() override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 GetX();
|
||||
s32 GetY();
|
||||
s32 GetWidth();
|
||||
s32 GetHeight();
|
||||
inline constexpr s32 GetY() override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline constexpr s32 GetWidth() override {
|
||||
return pu::ui::render::ScreenWidth;
|
||||
}
|
||||
|
||||
inline constexpr s32 GetHeight() override {
|
||||
return pu::ui::render::ScreenHeight;
|
||||
}
|
||||
|
||||
void Toggle(); // Off if on, on if off (just change to the opposite state)
|
||||
bool IsOn();
|
||||
inline void Toggle() {
|
||||
this->on = !this->on;
|
||||
}
|
||||
|
||||
void OnRender(pu::ui::render::Renderer::Ref &Drawer, s32 X, s32 Y);
|
||||
void OnInput(u64 Down, u64 Up, u64 Held, pu::ui::Touch Pos);
|
||||
inline constexpr bool IsOn() {
|
||||
return this->on && (this->bg_alpha > 0);
|
||||
}
|
||||
|
||||
void OnRender(pu::ui::render::Renderer::Ref &drawer, const s32 x, const s32 y) override;
|
||||
void OnInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos) override;
|
||||
|
||||
static inline void RegisterHomeButtonDetection() {
|
||||
am::RegisterOnMessageDetect(&OnHomeButtonDetection, dmi::MenuMessage::HomeRequest);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
|
||||
#pragma once
|
||||
#include <ul_Include.hpp>
|
||||
#include <pu/Plutonium>
|
||||
|
||||
namespace ui {
|
||||
|
||||
class RawData : public pu::ui::elm::Element {
|
||||
|
||||
protected:
|
||||
s32 x;
|
||||
s32 y;
|
||||
s32 w;
|
||||
s32 h;
|
||||
pu::sdl2::Texture ntex;
|
||||
u8 falpha;
|
||||
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);
|
||||
|
||||
};
|
||||
|
||||
}
|
62
uMenu/include/ui/ui_RawRgbaImage.hpp
Normal file
62
uMenu/include/ui/ui_RawRgbaImage.hpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
|
||||
#pragma once
|
||||
#include <ul_Include.hpp>
|
||||
#include <pu/Plutonium>
|
||||
|
||||
namespace ui {
|
||||
|
||||
class RawRgbaImage : public pu::ui::elm::Element {
|
||||
private:
|
||||
s32 x;
|
||||
s32 y;
|
||||
s32 w;
|
||||
s32 h;
|
||||
pu::sdl2::Texture img_tex;
|
||||
u8 alpha;
|
||||
|
||||
public:
|
||||
RawRgbaImage(const s32 x, const s32 y, const u8 *rgba_data, const s32 w, const s32 h, const u32 pix_num);
|
||||
PU_SMART_CTOR(RawRgbaImage)
|
||||
~RawRgbaImage();
|
||||
|
||||
inline s32 GetX() override {
|
||||
return this->x;
|
||||
}
|
||||
|
||||
inline void SetX(const s32 x) {
|
||||
this->x = x;
|
||||
}
|
||||
|
||||
inline s32 GetY() override {
|
||||
return this->y;
|
||||
}
|
||||
|
||||
inline void SetY(const s32 y) {
|
||||
this->y = y;
|
||||
}
|
||||
|
||||
inline s32 GetWidth() override {
|
||||
return this->w;
|
||||
}
|
||||
|
||||
inline void SetWidth(const s32 w) {
|
||||
this->w = w;
|
||||
}
|
||||
|
||||
inline s32 GetHeight() override {
|
||||
return this->h;
|
||||
}
|
||||
|
||||
inline void SetHeight(const s32 h) {
|
||||
this->h = h;
|
||||
}
|
||||
|
||||
inline void SetAlpha(const u8 alpha) {
|
||||
this->alpha = alpha;
|
||||
}
|
||||
|
||||
void OnRender(pu::ui::render::Renderer::Ref &drawer, const s32 x, const s32 y) override;
|
||||
void OnInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos) override {}
|
||||
};
|
||||
|
||||
}
|
|
@ -7,22 +7,21 @@
|
|||
namespace ui {
|
||||
|
||||
class SettingsMenuLayout : public IMenuLayout {
|
||||
|
||||
private:
|
||||
pu::ui::elm::TextBlock::Ref infoText;
|
||||
pu::ui::elm::Menu::Ref settingsMenu;
|
||||
pu::ui::elm::TextBlock::Ref info_text;
|
||||
pu::ui::elm::Menu::Ref settings_menu;
|
||||
|
||||
void PushSettingItem(const std::string &name, const std::string &value_display, const int id);
|
||||
void setting_DefaultKey(const u32 id);
|
||||
|
||||
public:
|
||||
SettingsMenuLayout();
|
||||
PU_SMART_CTOR(SettingsMenuLayout)
|
||||
|
||||
void OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) override;
|
||||
void OnMenuInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos) override;
|
||||
bool OnHomeButtonPress() override;
|
||||
|
||||
void Reload();
|
||||
void PushSettingItem(const std::string &name, const std::string &value_display, int id);
|
||||
void setting_Click(u32 id);
|
||||
|
||||
void Reload(const bool reset_idx);
|
||||
};
|
||||
|
||||
}
|
|
@ -1,13 +1,12 @@
|
|||
|
||||
#pragma once
|
||||
#include <pu/Plutonium>
|
||||
#include <map>
|
||||
|
||||
namespace ui {
|
||||
|
||||
class SideMenu : public pu::ui::elm::Element {
|
||||
|
||||
public:
|
||||
static constexpr u32 BaseX = 98;
|
||||
static constexpr u32 ItemSize = 256;
|
||||
static constexpr u32 Margin = 20;
|
||||
static constexpr u32 ItemCount = 4;
|
||||
|
@ -15,77 +14,156 @@ namespace ui {
|
|||
static constexpr u32 FocusMargin = 5;
|
||||
static constexpr u32 ExtraIconSize = ItemSize + (Margin * 2);
|
||||
|
||||
static constexpr u8 MoveAlphaIncrement = 30;
|
||||
|
||||
static constexpr u64 ScrollBaseWaitTimeMs = 50;
|
||||
static constexpr u64 ScrollMoveWaitTimeMs = 200;
|
||||
|
||||
using OnSelectCallback = std::function<void(const u64, const u32)>;
|
||||
using OnSelectionChangedCallback = std::function<void(const u32)>;
|
||||
|
||||
private:
|
||||
s32 y;
|
||||
u32 selitm;
|
||||
u32 preselitm;
|
||||
int suspitm;
|
||||
u32 baseiconidx;
|
||||
u8 movalpha;
|
||||
u32 textx;
|
||||
u32 texty;
|
||||
u32 selected_item_idx;
|
||||
u32 prev_selected_item_idx;
|
||||
s32 suspended_item_idx;
|
||||
u32 base_icon_idx;
|
||||
u8 move_alpha;
|
||||
u32 text_x;
|
||||
u32 text_y;
|
||||
bool enabled;
|
||||
pu::ui::Color suspclr;
|
||||
pu::ui::Color textclr;
|
||||
std::vector<std::string> icons;
|
||||
std::vector<std::string> icons_texts;
|
||||
std::vector<bool> icons_mselected;
|
||||
std::function<void(u64, u32)> onselect;
|
||||
std::function<void(u32)> onselch;
|
||||
std::vector<pu::sdl2::Texture> ricons;
|
||||
std::vector<pu::sdl2::Texture> ricons_texts;
|
||||
pu::sdl2::Texture cursoricon;
|
||||
pu::sdl2::Texture suspicon;
|
||||
pu::sdl2::Texture leftbicon;
|
||||
pu::sdl2::Texture rightbicon;
|
||||
pu::sdl2::Texture mselicon;
|
||||
pu::String textfont;
|
||||
std::chrono::steady_clock::time_point scrolltp;
|
||||
bool scrollmoveflag;
|
||||
std::chrono::steady_clock::time_point scrollmovetp;
|
||||
u32 scrollflag;
|
||||
u32 scrolltpvalue;
|
||||
u32 scrollcount;
|
||||
pu::ui::Color text_clr;
|
||||
std::vector<std::string> items_icon_paths;
|
||||
std::vector<std::string> items_icon_texts;
|
||||
std::vector<bool> items_multiselected;
|
||||
OnSelectCallback on_select_cb;
|
||||
OnSelectionChangedCallback on_selection_changed_cb;
|
||||
std::vector<pu::sdl2::Texture> rendered_icons;
|
||||
std::vector<pu::sdl2::Texture> rendered_texts;
|
||||
pu::sdl2::Texture cursor_icon;
|
||||
pu::sdl2::Texture suspended_icon;
|
||||
pu::sdl2::Texture left_border_icon;
|
||||
pu::sdl2::Texture right_border_icon;
|
||||
pu::sdl2::Texture multiselect_icon;
|
||||
std::string text_font;
|
||||
std::chrono::steady_clock::time_point scroll_tp;
|
||||
bool scroll_move_flag;
|
||||
std::chrono::steady_clock::time_point scroll_move_tp;
|
||||
u32 scroll_flag;
|
||||
u32 scroll_tp_value;
|
||||
u32 scroll_count;
|
||||
|
||||
inline void DoOnItemSelected(const u64 keys) {
|
||||
if(this->on_select_cb) {
|
||||
(this->on_select_cb)(keys, this->selected_item_idx);
|
||||
}
|
||||
}
|
||||
|
||||
inline void DoOnSelectionChanged() {
|
||||
if(this->on_selection_changed_cb) {
|
||||
(this->on_selection_changed_cb)(this->selected_item_idx);
|
||||
}
|
||||
}
|
||||
|
||||
inline void ClearBorderIcons() {
|
||||
pu::ui::render::DeleteTexture(this->left_border_icon);
|
||||
pu::ui::render::DeleteTexture(this->right_border_icon);
|
||||
}
|
||||
|
||||
inline void ClearRenderedItems() {
|
||||
for(auto &icon_tex: this->rendered_icons) {
|
||||
pu::ui::render::DeleteTexture(icon_tex);
|
||||
}
|
||||
this->rendered_icons.clear();
|
||||
|
||||
for(auto &text_tex: this->rendered_texts) {
|
||||
pu::ui::render::DeleteTexture(text_tex);
|
||||
}
|
||||
this->rendered_texts.clear();
|
||||
|
||||
this->ClearBorderIcons();
|
||||
}
|
||||
|
||||
bool IsLeftFirst();
|
||||
bool IsRightLast();
|
||||
void ReloadIcons(u32 dir);
|
||||
void MoveReloadIcons(const bool moving_right);
|
||||
|
||||
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);
|
||||
SideMenu(const pu::ui::Color suspended_clr, const std::string &cursor_path, const std::string &suspended_img_path, const std::string &multiselect_img_path, const s32 txt_x, const s32 txt_y, const std::string &font_name, const pu::ui::Color txt_clr, const 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);
|
||||
inline constexpr s32 GetX() override {
|
||||
return BaseX;
|
||||
}
|
||||
|
||||
inline s32 GetY() override {
|
||||
return this->y;
|
||||
}
|
||||
|
||||
// Note: stubbed for ApplyConfigForElement to work with this element
|
||||
inline void SetX(const s32 x) {}
|
||||
|
||||
inline void SetY(const s32 y) {
|
||||
this->y = y;
|
||||
}
|
||||
|
||||
inline constexpr s32 GetWidth() override {
|
||||
return pu::ui::render::ScreenWidth;
|
||||
}
|
||||
|
||||
inline constexpr s32 GetHeight() override {
|
||||
return ItemSize + FocusSize + FocusMargin;
|
||||
}
|
||||
|
||||
void OnRender(pu::ui::render::Renderer::Ref &drawer, const s32 x, const s32 y) override;
|
||||
void OnInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos) override;
|
||||
|
||||
inline void SetOnItemSelected(OnSelectCallback cb) {
|
||||
this->on_select_cb = cb;
|
||||
}
|
||||
|
||||
inline void SetOnSelectionChanged(OnSelectionChangedCallback cb) {
|
||||
this->on_selection_changed_cb = cb;
|
||||
}
|
||||
|
||||
void ClearItems();
|
||||
void AddItem(const std::string &icon, const std::string &txt = "");
|
||||
void SetSuspendedItem(u32 idx);
|
||||
void UnsetSuspendedItem();
|
||||
void SetSelectedItem(u32 idx);
|
||||
|
||||
inline void SetSuspendedItem(const u32 idx) {
|
||||
if(idx < this->items_icon_paths.size()) {
|
||||
this->suspended_item_idx = idx;
|
||||
}
|
||||
}
|
||||
|
||||
inline void ResetSuspendedItem() {
|
||||
this->suspended_item_idx = -1;
|
||||
}
|
||||
|
||||
inline void Rewind() {
|
||||
this->ClearRenderedItems();
|
||||
|
||||
this->selected_item_idx = 0;
|
||||
this->scroll_count = 0;
|
||||
this->base_icon_idx = 0;
|
||||
}
|
||||
|
||||
void HandleMoveLeft();
|
||||
void HandleMoveRight();
|
||||
int GetSuspendedItem();
|
||||
u32 GetSelectedItem();
|
||||
u32 GetBaseItemIndex();
|
||||
void SetBaseItemIndex(u32 idx);
|
||||
void SetBasePositions(u32 selected_idx, u32 base_idx);
|
||||
void ClearBorderIcons();
|
||||
|
||||
inline u32 GetSelectedItem() {
|
||||
return this->selected_item_idx;
|
||||
}
|
||||
|
||||
void UpdateBorderIcons();
|
||||
void ResetMultiselections();
|
||||
void SetItemMultiselected(u32 idx, bool selected);
|
||||
bool IsItemMultiselected(u32 idx);
|
||||
void SetItemMultiselected(const u32 idx, const bool selected);
|
||||
bool IsItemMultiselected(const u32 idx);
|
||||
bool IsAnyMultiselected();
|
||||
void SetEnabled(bool enabled);
|
||||
|
||||
|
||||
inline void SetEnabled(const bool enabled) {
|
||||
this->enabled = enabled;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,30 +1,26 @@
|
|||
|
||||
#pragma once
|
||||
#include <ul_Include.hpp>
|
||||
#include <dmi/dmi_DaemonMenuInteraction.hpp>
|
||||
#include <db/db_Save.hpp>
|
||||
#include <ui/ui_IMenuLayout.hpp>
|
||||
|
||||
namespace ui {
|
||||
|
||||
class StartupLayout : public IMenuLayout {
|
||||
|
||||
private:
|
||||
bool loadmenu;
|
||||
pu::ui::elm::TextBlock::Ref infoText;
|
||||
pu::ui::elm::Menu::Ref usersMenu;
|
||||
bool load_menu;
|
||||
pu::ui::elm::TextBlock::Ref info_text;
|
||||
pu::ui::elm::Menu::Ref users_menu;
|
||||
|
||||
void user_DefaultKey(const AccountUid uid);
|
||||
void create_DefaultKey();
|
||||
|
||||
public:
|
||||
StartupLayout();
|
||||
PU_SMART_CTOR(StartupLayout)
|
||||
|
||||
void OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) override;
|
||||
void OnMenuInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos) override;
|
||||
bool OnHomeButtonPress() override;
|
||||
|
||||
void user_Click(AccountUid uid);
|
||||
void create_Click();
|
||||
void ReloadMenu();
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -1,33 +1,31 @@
|
|||
|
||||
#pragma once
|
||||
#include <ul_Include.hpp>
|
||||
#include <ui/ui_IMenuLayout.hpp>
|
||||
#include <cfg/cfg_Config.hpp>
|
||||
|
||||
namespace ui {
|
||||
|
||||
class ThemeMenuLayout : public IMenuLayout {
|
||||
|
||||
private:
|
||||
pu::ui::elm::Menu::Ref themesMenu;
|
||||
pu::ui::elm::TextBlock::Ref curThemeText;
|
||||
pu::ui::elm::TextBlock::Ref curThemeName;
|
||||
pu::ui::elm::TextBlock::Ref curThemeAuthor;
|
||||
pu::ui::elm::TextBlock::Ref curThemeVersion;
|
||||
pu::ui::elm::Image::Ref curThemeIcon;
|
||||
pu::ui::elm::Image::Ref curThemeBanner;
|
||||
std::vector<cfg::Theme> loadedThemes;
|
||||
pu::ui::elm::Menu::Ref themes_menu;
|
||||
pu::ui::elm::TextBlock::Ref cur_theme_text;
|
||||
pu::ui::elm::TextBlock::Ref cur_theme_name_text;
|
||||
pu::ui::elm::TextBlock::Ref cur_theme_author_text;
|
||||
pu::ui::elm::TextBlock::Ref cur_theme_version_text;
|
||||
pu::ui::elm::Image::Ref cur_theme_icon;
|
||||
pu::ui::elm::Image::Ref cur_theme_banner;
|
||||
std::vector<cfg::Theme> loaded_themes;
|
||||
|
||||
void theme_DefaultKey();
|
||||
|
||||
public:
|
||||
ThemeMenuLayout();
|
||||
PU_SMART_CTOR(ThemeMenuLayout)
|
||||
|
||||
void OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) override;
|
||||
void OnMenuInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos) override;
|
||||
bool OnHomeButtonPress() override;
|
||||
|
||||
void Reload();
|
||||
void theme_Click();
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
namespace ui {
|
||||
|
||||
// Note: this guard ensures that certain code won't run again while we're already inside its invokation (which would happen when calling render loops)
|
||||
|
||||
class TransitionGuard {
|
||||
private:
|
||||
bool on_transition;
|
||||
|
@ -11,7 +13,7 @@ namespace ui {
|
|||
public:
|
||||
TransitionGuard() : on_transition(false) {}
|
||||
|
||||
bool Run(std::function<void()> fn) {
|
||||
inline bool Run(std::function<void()> fn) {
|
||||
if(this->on_transition) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
"app_take_over_done": "This title was selected to be used for homebrew launching.",
|
||||
"app_unexpected_error": "A title failed to start, crashed or unexpectedly terminated.\n(Could it be corrupted? If it is a gamecard title, do you have it inserted?)",
|
||||
"ulaunch_about": "About uLaunch",
|
||||
"ulaunch_desc": "uLaunch is a free, open source, extended and homebrew-oriented HOME menu replacement.\nNote that some original HOME menu functionalities aren't implemented yet.\n\nIf you're looking for new uLaunch themes, check r/uLaunchThemes subreddit.\nIf you would like to contribute, check uLaunch's GitHub repository",
|
||||
"ulaunch_desc": "uLaunch is an extended, homebrew-oriented, FOSS HOME menu replacement.\nNote that some original HOME menu functionalities aren't implemented yet.\n\nIf you're looking for new uLaunch themes, check the r/uLaunchThemes subreddit.\nIf you would like to contribute, check uLaunch's GitHub repository",
|
||||
"control_minus": "Swap the main menu",
|
||||
"suspended_app": "Suspended title",
|
||||
"suspended_close": "Would you like to close this title? All unsaved data will be lost.",
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
#include <pu/Plutonium>
|
||||
#include <ctime>
|
||||
#include <chrono>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <db/db_Save.hpp>
|
||||
#include <fs/fs_Stdio.hpp>
|
||||
#include <cfg/cfg_Config.hpp>
|
||||
|
@ -13,7 +8,7 @@
|
|||
#include <util/util_Convert.hpp>
|
||||
#include <am/am_LibraryApplet.hpp>
|
||||
#include <am/am_DaemonMessages.hpp>
|
||||
#include <am/am_LibAppletWrap.hpp>
|
||||
#include <am/am_LibnxLibappletWrap.hpp>
|
||||
#include <am/am_LibraryAppletUtils.hpp>
|
||||
|
||||
extern "C" {
|
||||
|
@ -29,7 +24,6 @@ extern "C" {
|
|||
|
||||
ui::MenuApplication::Ref g_MenuApplication;
|
||||
ui::TransitionGuard g_TransitionGuard;
|
||||
u8 *g_ScreenCaptureBuffer;
|
||||
|
||||
cfg::TitleList g_EntryList;
|
||||
std::vector<cfg::TitleRecord> g_HomebrewRecordList;
|
||||
|
@ -44,15 +38,15 @@ char g_FwVersion[0x18] = {};
|
|||
namespace {
|
||||
|
||||
void Initialize() {
|
||||
UL_ASSERT(accountInitialize(AccountServiceType_System));
|
||||
UL_ASSERT(nsInitialize());
|
||||
UL_ASSERT(net::Initialize());
|
||||
UL_ASSERT(psmInitialize());
|
||||
UL_ASSERT(setsysInitialize());
|
||||
UL_ASSERT(setInitialize());
|
||||
UL_RC_ASSERT(accountInitialize(AccountServiceType_System));
|
||||
UL_RC_ASSERT(nsInitialize());
|
||||
UL_RC_ASSERT(net::Initialize());
|
||||
UL_RC_ASSERT(psmInitialize());
|
||||
UL_RC_ASSERT(setsysInitialize());
|
||||
UL_RC_ASSERT(setInitialize());
|
||||
|
||||
// Initialize uDaemon message handling
|
||||
UL_ASSERT(am::InitializeDaemonMessageHandler());
|
||||
UL_RC_ASSERT(am::InitializeDaemonMessageHandler());
|
||||
|
||||
// Load menu config and theme
|
||||
g_Config = cfg::LoadConfig();
|
||||
|
@ -77,25 +71,24 @@ namespace {
|
|||
|
||||
int main() {
|
||||
auto start_mode = dmi::MenuStartMode::Invalid;
|
||||
UL_ASSERT(am::ReadStartMode(start_mode));
|
||||
UL_RC_ASSERT(am::ReadStartMode(start_mode));
|
||||
UL_ASSERT_TRUE(start_mode != dmi::MenuStartMode::Invalid);
|
||||
|
||||
// Information sent as an extra storage to uMenu
|
||||
dmi::DaemonStatus status = {};
|
||||
UL_ASSERT(am::ReadDataFromStorage(&status, sizeof(status)));
|
||||
UL_RC_ASSERT(am::ReadDataFromStorage(&status, sizeof(status)));
|
||||
|
||||
memcpy(g_FwVersion, status.fw_version, sizeof(g_FwVersion));
|
||||
|
||||
// Check if our RomFs data exists...
|
||||
if(!fs::ExistsFile(UL_MENU_ROMFS_BIN)) {
|
||||
UL_ASSERT(menu::ResultRomfsFileNotFound);
|
||||
UL_RC_ASSERT(menu::ResultRomfsFileNotFound);
|
||||
}
|
||||
|
||||
// Try to mount it
|
||||
UL_ASSERT(romfsMountFromFsdev(UL_MENU_ROMFS_BIN, 0, "romfs"));
|
||||
UL_RC_ASSERT(romfsMountFromFsdev(UL_MENU_ROMFS_BIN, 0, "romfs"));
|
||||
|
||||
// After initializing RomFs, start initializing the rest of stuff here
|
||||
g_ScreenCaptureBuffer = new u8[RawRGBAScreenBufferSize]();
|
||||
Initialize();
|
||||
|
||||
// Cache title and homebrew icons
|
||||
|
@ -105,34 +98,38 @@ int main() {
|
|||
|
||||
// Get system language and load translations (default one if not present)
|
||||
u64 lang_code = 0;
|
||||
UL_ASSERT(setGetLanguageCode(&lang_code));
|
||||
std::string sys_lang = reinterpret_cast<char*>(&lang_code);
|
||||
auto lang_path = cfg::GetLanguageJSONPath(sys_lang);
|
||||
UL_ASSERT(util::LoadJSONFromFile(g_DefaultLanguage, CFG_LANG_DEFAULT));
|
||||
UL_RC_ASSERT(setGetLanguageCode(&lang_code));
|
||||
const auto lang_path = cfg::GetLanguageJSONPath(reinterpret_cast<char*>(&lang_code));
|
||||
UL_RC_ASSERT(util::LoadJSONFromFile(g_DefaultLanguage, CFG_LANG_DEFAULT));
|
||||
g_MainLanguage = g_DefaultLanguage;
|
||||
if(fs::ExistsFile(lang_path)) {
|
||||
auto lang_json = JSON::object();
|
||||
UL_ASSERT(util::LoadJSONFromFile(lang_json, lang_path));
|
||||
UL_RC_ASSERT(util::LoadJSONFromFile(lang_json, lang_path));
|
||||
g_MainLanguage = lang_json;
|
||||
}
|
||||
|
||||
// Get the text sizes to initialize default fonts
|
||||
auto uijson = JSON::object();
|
||||
UL_ASSERT(util::LoadJSONFromFile(uijson, cfg::GetAssetByTheme(g_Theme, "ui/UI.json")));
|
||||
const auto menu_folder_txt_sz = uijson.value<u32>("menu_folder_text_size", 25);
|
||||
|
||||
auto ui_json = JSON::object();
|
||||
UL_RC_ASSERT(util::LoadJSONFromFile(ui_json, cfg::GetAssetByTheme(g_Theme, "ui/UI.json")));
|
||||
const auto menu_folder_text_size = ui_json.value<u32>("menu_folder_text_size", 25);
|
||||
const auto default_font_path = cfg::GetAssetByTheme(g_Theme, "ui/Font.ttf");
|
||||
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(default_font_path).WithDefaultFontSize(menu_folder_txt_sz));
|
||||
|
||||
auto renderer_opts = pu::ui::render::RendererInitOptions(SDL_INIT_EVERYTHING, pu::ui::render::RendererHardwareFlags);
|
||||
renderer_opts.UseTTF(default_font_path);
|
||||
renderer_opts.UseImage(pu::ui::render::IMGAllFlags);
|
||||
renderer_opts.UseAudio(pu::ui::render::MixerAllFlags);
|
||||
renderer_opts.SetExtraDefaultFontSize(menu_folder_text_size);
|
||||
auto renderer = pu::ui::render::Renderer::New(renderer_opts);
|
||||
g_MenuApplication = ui::MenuApplication::New(renderer);
|
||||
|
||||
g_MenuApplication->SetInformation(start_mode, status, uijson);
|
||||
g_MenuApplication->SetInformation(start_mode, status, ui_json);
|
||||
g_MenuApplication->Prepare();
|
||||
|
||||
// Register handlers for HOME button press detection
|
||||
am::RegisterLibAppletHomeButtonDetection();
|
||||
am::RegisterLibnxLibappletHomeButtonDetection();
|
||||
ui::MenuApplication::RegisterHomeButtonDetection();
|
||||
ui::QuickMenu::RegisterHomeButtonDetection();
|
||||
|
||||
|
||||
if(start_mode == dmi::MenuStartMode::MenuApplicationSuspended) {
|
||||
g_MenuApplication->Show();
|
||||
}
|
||||
|
@ -143,7 +140,6 @@ int main() {
|
|||
// Exit RomFs manually, since we also initialized it manually
|
||||
romfsExit();
|
||||
|
||||
delete[] g_ScreenCaptureBuffer;
|
||||
Exit();
|
||||
return 0;
|
||||
}
|
|
@ -20,8 +20,8 @@ namespace {
|
|||
return ResultSuccess;
|
||||
}
|
||||
|
||||
R_TRY(smGetService(&g_DaemonPrivateService, PrivateServiceName));
|
||||
R_TRY(daemonPrivateInitialize(&g_DaemonPrivateService));
|
||||
UL_RC_TRY(smGetService(&g_DaemonPrivateService, PrivateServiceName));
|
||||
UL_RC_TRY(daemonPrivateInitialize(&g_DaemonPrivateService));
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
@ -30,9 +30,9 @@ namespace {
|
|||
serviceClose(&g_DaemonPrivateService);
|
||||
}
|
||||
|
||||
dmi::MenuMessage daemonGetMessage() {
|
||||
dmi::MenuMessage daemonPrivateServiceGetMessage() {
|
||||
auto msg = dmi::MenuMessage::Invalid;
|
||||
UL_ASSERT(daemonPrivateGetMessage(&g_DaemonPrivateService, &msg));
|
||||
UL_RC_ASSERT(daemonPrivateGetMessage(&g_DaemonPrivateService, &msg));
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,6 @@ namespace am {
|
|||
|
||||
namespace {
|
||||
|
||||
|
||||
bool g_Initialized = false;
|
||||
std::atomic_bool g_ReceiveThreadShouldStop = false;
|
||||
Thread g_ReceiverThread;
|
||||
|
@ -55,14 +54,16 @@ namespace am {
|
|||
break;
|
||||
}
|
||||
|
||||
const auto last_msg = daemonGetMessage();
|
||||
mutexLock(&g_CallbackTableLock);
|
||||
for(auto &[cb, msg] : g_MessageCallbackTable) {
|
||||
if(msg == last_msg) {
|
||||
cb();
|
||||
{
|
||||
const auto last_msg = daemonPrivateServiceGetMessage();
|
||||
ScopedLock lk(g_CallbackTableLock);
|
||||
|
||||
for(const auto &[cb, msg] : g_MessageCallbackTable) {
|
||||
if(msg == last_msg) {
|
||||
cb();
|
||||
}
|
||||
}
|
||||
}
|
||||
mutexUnlock(&g_CallbackTableLock);
|
||||
|
||||
svcSleepThread(10'000'000ul);
|
||||
}
|
||||
|
@ -74,11 +75,11 @@ namespace am {
|
|||
if(g_Initialized) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
R_TRY(daemonInitializePrivateService());
|
||||
UL_RC_TRY(daemonInitializePrivateService());
|
||||
|
||||
g_ReceiveThreadShouldStop = false;
|
||||
R_TRY(threadCreate(&g_ReceiverThread, &DaemonMessageReceiverThread, nullptr, nullptr, 0x1000, 49, -2));
|
||||
R_TRY(threadStart(&g_ReceiverThread));
|
||||
UL_RC_TRY(threadCreate(&g_ReceiverThread, &DaemonMessageReceiverThread, nullptr, nullptr, 0x1000, 49, -2));
|
||||
UL_RC_TRY(threadStart(&g_ReceiverThread));
|
||||
|
||||
g_Initialized = true;
|
||||
return ResultSuccess;
|
||||
|
@ -97,10 +98,10 @@ namespace am {
|
|||
g_Initialized = false;
|
||||
}
|
||||
|
||||
void RegisterOnMessageDetect(OnMessageCallback callback, dmi::MenuMessage desired_msg) {
|
||||
mutexLock(&g_CallbackTableLock);
|
||||
g_MessageCallbackTable.push_back(std::make_pair(callback, desired_msg));
|
||||
mutexUnlock(&g_CallbackTableLock);
|
||||
void RegisterOnMessageDetect(OnMessageCallback callback, const dmi::MenuMessage desired_msg) {
|
||||
ScopedLock lk(g_CallbackTableLock);
|
||||
|
||||
g_MessageCallbackTable.push_back({ callback, desired_msg });
|
||||
}
|
||||
|
||||
}
|
|
@ -1,26 +1,23 @@
|
|||
#include <dmi/dmi_DaemonMenuInteraction.hpp>
|
||||
#include <am/am_LibAppletWrap.hpp>
|
||||
#include <am/am_LibnxLibappletWrap.hpp>
|
||||
#include <am/am_DaemonMessages.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
Mutex g_LibAppletWrapLock = {};
|
||||
bool g_IsAppletRunning = false;
|
||||
bool g_DetectionHomePressed = false;
|
||||
std::atomic_bool g_IsAppletRunning = false;
|
||||
std::atomic_bool g_DetectionHomePressed = false;
|
||||
|
||||
}
|
||||
|
||||
namespace am {
|
||||
|
||||
void OnHomeButtonDetection() {
|
||||
mutexLock(&g_LibAppletWrapLock);
|
||||
if(g_IsAppletRunning) {
|
||||
g_DetectionHomePressed = true;
|
||||
}
|
||||
mutexUnlock(&g_LibAppletWrapLock);
|
||||
}
|
||||
|
||||
void RegisterLibAppletHomeButtonDetection() {
|
||||
void RegisterLibnxLibappletHomeButtonDetection() {
|
||||
RegisterOnMessageDetect(&OnHomeButtonDetection, dmi::MenuMessage::HomeRequest);
|
||||
}
|
||||
|
||||
|
@ -28,14 +25,12 @@ namespace am {
|
|||
|
||||
extern "C" {
|
||||
|
||||
// Wrap libappletStart and libappletLaunch to use our custom waiting system
|
||||
// Wrap libappletStart and libappletLaunch to use our custom waiting system (mostly so that they respond to HOME menu presses)
|
||||
|
||||
Result __wrap_libappletStart(AppletHolder *h) {
|
||||
R_TRY(appletHolderStart(h));
|
||||
UL_RC_TRY(appletHolderStart(h));
|
||||
|
||||
mutexLock(&g_LibAppletWrapLock);
|
||||
g_IsAppletRunning = true;
|
||||
mutexUnlock(&g_LibAppletWrapLock);
|
||||
|
||||
while(true) {
|
||||
if(appletHolderCheckFinished(h)) {
|
||||
|
@ -45,10 +40,8 @@ extern "C" {
|
|||
break;
|
||||
}
|
||||
|
||||
mutexLock(&g_LibAppletWrapLock);
|
||||
auto home_pressed = g_DetectionHomePressed;
|
||||
const bool home_pressed = g_DetectionHomePressed;
|
||||
g_DetectionHomePressed = false;
|
||||
mutexUnlock(&g_LibAppletWrapLock);
|
||||
|
||||
if(home_pressed) {
|
||||
appletHolderRequestExitOrTerminate(h, 15'000'000'000ul);
|
||||
|
@ -60,32 +53,31 @@ extern "C" {
|
|||
appletHolderJoin(h);
|
||||
|
||||
auto rc = ResultSuccess;
|
||||
|
||||
auto reason = appletHolderGetExitReason(h);
|
||||
const auto reason = appletHolderGetExitReason(h);
|
||||
if(reason == LibAppletExitReason_Canceled || reason == LibAppletExitReason_Abnormal || reason == LibAppletExitReason_Unexpected) {
|
||||
rc = MAKERESULT(Module_Libnx, LibnxError_LibAppletBadExit);
|
||||
}
|
||||
|
||||
mutexLock(&g_LibAppletWrapLock);
|
||||
g_IsAppletRunning = false;
|
||||
mutexUnlock(&g_LibAppletWrapLock);
|
||||
|
||||
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) {
|
||||
// Note: libappletLaunch is exactly the same but we redefine it so that it calls our libappletStart (libnx is already compiled, the libappletStart wrap wouldn't affect libappletLaunch otherwise)
|
||||
|
||||
Result __wrap_libappletLaunch(AppletId id, LibAppletArgs *common_args, 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_RC_TRY(appletCreateLibraryApplet(&holder, id, LibAppletMode_AllForeground));
|
||||
UL_ON_SCOPE_EXIT({
|
||||
appletHolderClose(&holder);
|
||||
});
|
||||
R_TRY(libappletArgsPush(commonargs, &holder));
|
||||
UL_RC_TRY(libappletArgsPush(common_args, &holder));
|
||||
if(arg && arg_size) {
|
||||
R_TRY(libappletPushInData(&holder, arg, arg_size));
|
||||
UL_RC_TRY(libappletPushInData(&holder, arg, arg_size));
|
||||
}
|
||||
R_TRY(__wrap_libappletStart(&holder));
|
||||
UL_RC_TRY(libappletStart(&holder));
|
||||
if(reply && reply_size) {
|
||||
R_TRY(libappletPopOutData(&holder, reply, reply_size, out_reply_size));
|
||||
UL_RC_TRY(libappletPopOutData(&holder, reply, reply_size, out_reply_size));
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
|
@ -2,19 +2,18 @@
|
|||
|
||||
namespace am {
|
||||
|
||||
Result ReadDataFromStorage(void *data, size_t data_size) {
|
||||
Result ReadDataFromStorage(void *out_data, const size_t data_size) {
|
||||
AppletStorage st;
|
||||
R_TRY(appletPopInData(&st));
|
||||
UL_ON_SCOPE_EXIT({
|
||||
appletStorageClose(&st);
|
||||
});
|
||||
R_TRY(appletStorageRead(&st, 0, data, data_size));
|
||||
UL_RC_TRY(appletPopInData(&st));
|
||||
UL_ON_SCOPE_EXIT({ appletStorageClose(&st); });
|
||||
|
||||
UL_RC_TRY(appletStorageRead(&st, 0, out_data, data_size));
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ReadStartMode(dmi::MenuStartMode &out_start_mode) {
|
||||
LibAppletArgs args = {};
|
||||
R_TRY(ReadDataFromStorage(&args, sizeof(args)));
|
||||
UL_RC_TRY(ReadDataFromStorage(&args, sizeof(args)));
|
||||
out_start_mode = static_cast<dmi::MenuStartMode>(args.LaVersion);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
|
|
@ -39,27 +39,28 @@ namespace ui::actions {
|
|||
}
|
||||
|
||||
void ShowUserMenu() {
|
||||
auto uid = g_MenuApplication->GetSelectedUser();
|
||||
const auto uid = g_MenuApplication->GetSelectedUser();
|
||||
std::string name;
|
||||
os::GetAccountName(name, uid);
|
||||
auto sopt = g_MenuApplication->CreateShowDialog(GetLanguageString("user_settings"), GetLanguageString("user_selected") + ": " + name + "\n" + GetLanguageString("user_option"), { GetLanguageString("user_view_page"), GetLanguageString("user_logoff"), GetLanguageString("cancel") }, true, os::GetIconCacheImagePath(uid));
|
||||
if(sopt == 0) {
|
||||
os::GetAccountName(uid, name);
|
||||
const auto option = g_MenuApplication->CreateShowDialog(GetLanguageString("user_settings"), GetLanguageString("user_selected") + ": " + name + "\n" + GetLanguageString("user_option"), { GetLanguageString("user_view_page"), GetLanguageString("user_logoff"), GetLanguageString("cancel") }, true, os::GetIconCacheImagePath(uid));
|
||||
if(option == 0) {
|
||||
friendsLaShowMyProfileForHomeMenu(uid);
|
||||
}
|
||||
else if(sopt == 1) {
|
||||
u32 logoff = 0;
|
||||
else if(option == 1) {
|
||||
auto log_off = false;
|
||||
if(g_MenuApplication->IsSuspended()) {
|
||||
auto sopt = g_MenuApplication->CreateShowDialog(GetLanguageString("suspended_app"), GetLanguageString("user_logoff_app_suspended"), { GetLanguageString("yes"), GetLanguageString("cancel") }, true);
|
||||
if(sopt == 0) {
|
||||
logoff = 2;
|
||||
const auto option_2 = g_MenuApplication->CreateShowDialog(GetLanguageString("suspended_app"), GetLanguageString("user_logoff_app_suspended"), { GetLanguageString("yes"), GetLanguageString("cancel") }, true);
|
||||
if(option_2 == 0) {
|
||||
log_off = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
logoff = 1;
|
||||
log_off = true;
|
||||
}
|
||||
if(logoff > 0) {
|
||||
|
||||
if(log_off) {
|
||||
auto &menu_lyt = g_MenuApplication->GetMenuLayout();
|
||||
if(logoff == 2) {
|
||||
if(g_MenuApplication->IsSuspended()) {
|
||||
menu_lyt->DoTerminateApplication();
|
||||
}
|
||||
|
||||
|
@ -95,8 +96,8 @@ namespace ui::actions {
|
|||
char url[500] = {0};
|
||||
swkbdShow(&swkbd, url, 500);
|
||||
|
||||
UL_ASSERT(dmi::menu::SendCommand(dmi::DaemonMessage::OpenWebPage, [&](dmi::menu::MenuScopedStorageWriter &writer) {
|
||||
R_TRY(writer.PushData(url, sizeof(url)));
|
||||
UL_RC_ASSERT(dmi::menu::SendCommand(dmi::DaemonMessage::OpenWebPage, [&](dmi::menu::MenuScopedStorageWriter &writer) {
|
||||
UL_RC_TRY(writer.PushData(url, sizeof(url)));
|
||||
return ResultSuccess;
|
||||
},
|
||||
[&](dmi::menu::MenuScopedStorageReader &reader) {
|
||||
|
@ -122,7 +123,7 @@ namespace ui::actions {
|
|||
}
|
||||
|
||||
void ShowAlbumApplet() {
|
||||
UL_ASSERT(dmi::menu::SendCommand(dmi::DaemonMessage::OpenAlbum, [&](dmi::menu::MenuScopedStorageWriter &writer) {
|
||||
UL_RC_ASSERT(dmi::menu::SendCommand(dmi::DaemonMessage::OpenAlbum, [&](dmi::menu::MenuScopedStorageWriter &writer) {
|
||||
// ...
|
||||
return ResultSuccess;
|
||||
},
|
||||
|
|
|
@ -6,80 +6,40 @@
|
|||
|
||||
namespace ui {
|
||||
|
||||
s32 ClickableImage::GetX() {
|
||||
return this->x;
|
||||
}
|
||||
|
||||
void ClickableImage::SetX(s32 x) {
|
||||
this->x = x;
|
||||
}
|
||||
|
||||
s32 ClickableImage::GetY() {
|
||||
return this->y;
|
||||
}
|
||||
|
||||
void ClickableImage::SetY(s32 y) {
|
||||
this->y = y;
|
||||
}
|
||||
|
||||
s32 ClickableImage::GetWidth() {
|
||||
return this->w;
|
||||
}
|
||||
|
||||
void ClickableImage::SetWidth(s32 w) {
|
||||
this->w = w;
|
||||
}
|
||||
|
||||
s32 ClickableImage::GetHeight() {
|
||||
return this->h;
|
||||
}
|
||||
|
||||
void ClickableImage::SetHeight(s32 h) {
|
||||
this->h = h;
|
||||
}
|
||||
|
||||
std::string ClickableImage::GetImage() {
|
||||
return this->img;
|
||||
ClickableImage::~ClickableImage() {
|
||||
pu::ui::render::DeleteTexture(this->img_tex);
|
||||
}
|
||||
|
||||
void ClickableImage::SetImage(const std::string &img) {
|
||||
this->Dispose();
|
||||
pu::ui::render::DeleteTexture(this->img_tex);
|
||||
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);
|
||||
this->img_tex = pu::ui::render::LoadImage(img);
|
||||
this->w = pu::ui::render::GetTextureWidth(this->img_tex);
|
||||
this->h = pu::ui::render::GetTextureHeight(this->img_tex);
|
||||
}
|
||||
}
|
||||
|
||||
bool ClickableImage::IsImageValid() {
|
||||
return (ntex != nullptr) && !this->img.empty();
|
||||
void ClickableImage::OnRender(pu::ui::render::Renderer::Ref &drawer, const s32 x, const s32 y) {
|
||||
drawer->RenderTexture(this->img_tex, x, y, pu::ui::render::TextureRenderOptions::WithCustomDimensions(this->w, this->h));
|
||||
}
|
||||
|
||||
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::OnInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) {
|
||||
void ClickableImage::OnInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos) {
|
||||
if(this->touched) {
|
||||
auto tpnow = std::chrono::steady_clock::now();
|
||||
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(tpnow - this->touchtp).count();
|
||||
if(diff >= 200) {
|
||||
const auto tp_now = std::chrono::steady_clock::now();
|
||||
const u64 diff = std::chrono::duration_cast<std::chrono::milliseconds>(tp_now - this->touch_tp).count();
|
||||
if(diff >= TouchActionTimeMilliseconds) {
|
||||
this->touched = false;
|
||||
(this->cb)();
|
||||
SDL_SetTextureColorMod(this->ntex, 255, 255, 255);
|
||||
if(this->cb) {
|
||||
(this->cb)();
|
||||
}
|
||||
SDL_SetTextureColorMod(this->img_tex, 0xFF, 0xFF, 0xFF);
|
||||
}
|
||||
}
|
||||
else if(!touch_pos.IsEmpty()) {
|
||||
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);
|
||||
}
|
||||
else if(touch_pos.HitsRegion(this->GetProcessedX(), this->GetProcessedY(), this->w, this->h)) {
|
||||
this->touch_tp = std::chrono::steady_clock::now();
|
||||
this->touched = true;
|
||||
SDL_SetTextureColorMod(this->img_tex, RedColorMod, GreenColorMod, BlueColorMod);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace ui {
|
|||
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 touch_pos) {
|
||||
void IMenuLayout::OnInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos) {
|
||||
if(this->home_pressed) {
|
||||
if(this->OnHomeButtonPress()) {
|
||||
// Input consumed
|
||||
|
@ -21,7 +21,7 @@ namespace ui {
|
|||
}
|
||||
*/
|
||||
|
||||
this->OnMenuInput(down, up, held, touch_pos);
|
||||
this->OnMenuInput(keys_down, keys_up, keys_held, touch_pos);
|
||||
}
|
||||
|
||||
void IMenuLayout::DoOnHomeButtonPress() {
|
||||
|
|
|
@ -18,24 +18,19 @@ namespace ui {
|
|||
LanguagesMenuLayout::LanguagesMenuLayout() {
|
||||
this->SetBackgroundImage(cfg::GetAssetByTheme(g_Theme, "ui/Background.png"));
|
||||
|
||||
pu::ui::Color textclr = pu::ui::Color::FromHex(g_MenuApplication->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
|
||||
pu::ui::Color menufocusclr = pu::ui::Color::FromHex(g_MenuApplication->GetUIConfigValue<std::string>("menu_focus_color", "#5ebcffff"));
|
||||
pu::ui::Color menubgclr = pu::ui::Color::FromHex(g_MenuApplication->GetUIConfigValue<std::string>("menu_bg_color", "#0094ffff"));
|
||||
this->info_text = pu::ui::elm::TextBlock::New(0, 100, GetLanguageString("lang_info_text"));
|
||||
this->info_text->SetColor(g_MenuApplication->GetTextColor());
|
||||
this->info_text->SetHorizontalAlign(pu::ui::elm::HorizontalAlign::Center);
|
||||
g_MenuApplication->ApplyConfigForElement("languages_menu", "info_text", this->info_text);
|
||||
this->Add(this->info_text);
|
||||
|
||||
this->infoText = pu::ui::elm::TextBlock::New(0, 100, GetLanguageString("lang_info_text"));
|
||||
this->infoText->SetColor(textclr);
|
||||
this->infoText->SetHorizontalAlign(pu::ui::elm::HorizontalAlign::Center);
|
||||
g_MenuApplication->ApplyConfigForElement("languages_menu", "info_text", this->infoText);
|
||||
this->Add(this->infoText);
|
||||
|
||||
this->langsMenu = pu::ui::elm::Menu::New(200, 160, 880, menubgclr, 100, 4);
|
||||
this->langsMenu->SetOnFocusColor(menufocusclr);
|
||||
g_MenuApplication->ApplyConfigForElement("languages_menu", "languages_menu_item", this->langsMenu);
|
||||
this->Add(this->langsMenu);
|
||||
this->langs_menu = pu::ui::elm::Menu::New(200, 160, 880, g_MenuApplication->GetMenuBackgroundColor(), g_MenuApplication->GetMenuFocusColor(), 100, 4);
|
||||
g_MenuApplication->ApplyConfigForElement("languages_menu", "languages_menu_item", this->langs_menu);
|
||||
this->Add(this->langs_menu);
|
||||
}
|
||||
|
||||
void LanguagesMenuLayout::OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) {
|
||||
if(down & HidNpadButton_B) {
|
||||
void LanguagesMenuLayout::OnMenuInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos) {
|
||||
if(keys_down & HidNpadButton_B) {
|
||||
g_TransitionGuard.Run([]() {
|
||||
g_MenuApplication->FadeOut();
|
||||
g_MenuApplication->LoadSettingsMenu();
|
||||
|
@ -53,45 +48,39 @@ namespace ui {
|
|||
}
|
||||
|
||||
void LanguagesMenuLayout::Reload() {
|
||||
this->langsMenu->ClearItems();
|
||||
this->langsMenu->SetSelectedIndex(0);
|
||||
this->langs_menu->ClearItems();
|
||||
|
||||
auto textclr = pu::ui::Color::FromHex(g_MenuApplication->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
|
||||
u64 lcode = 0;
|
||||
auto ilang = SetLanguage_ENUS;
|
||||
setGetLanguageCode(&lcode);
|
||||
setMakeLanguage(lcode, &ilang);
|
||||
|
||||
const auto sys_lang = os::GetSystemLanguage();
|
||||
for(u32 i = 0; i < os::LanguageNameCount; i++) {
|
||||
std::string name = os::LanguageNameList[i];
|
||||
if(static_cast<u32>(ilang) == i) {
|
||||
if(static_cast<u32>(sys_lang) == i) {
|
||||
name += " " + GetLanguageString("lang_selected");
|
||||
}
|
||||
auto litm = pu::ui::elm::MenuItem::New(name);
|
||||
litm->SetColor(textclr);
|
||||
litm->AddOnClick(std::bind(&LanguagesMenuLayout::lang_Click, this, i));
|
||||
this->langsMenu->AddItem(litm);
|
||||
|
||||
auto lang_item = pu::ui::elm::MenuItem::New(name);
|
||||
lang_item->SetColor(g_MenuApplication->GetTextColor());
|
||||
lang_item->AddOnKey(std::bind(&LanguagesMenuLayout::lang_DefaultKey, this, i));
|
||||
this->langs_menu->AddItem(lang_item);
|
||||
}
|
||||
|
||||
this->langs_menu->SetSelectedIndex(0);
|
||||
}
|
||||
|
||||
void LanguagesMenuLayout::lang_Click(u32 idx) {
|
||||
u64 lcode = 0;
|
||||
SetLanguage ilang = SetLanguage_ENUS;
|
||||
setGetLanguageCode(&lcode);
|
||||
setMakeLanguage(lcode, &ilang);
|
||||
|
||||
if(static_cast<u32>(ilang) == idx) {
|
||||
void LanguagesMenuLayout::lang_DefaultKey(const u32 idx) {
|
||||
// TODO: cache system language...
|
||||
const auto sys_lang = os::GetSystemLanguage();
|
||||
if(static_cast<u32>(sys_lang) == idx) {
|
||||
g_MenuApplication->ShowNotification(GetLanguageString("lang_active_this"));
|
||||
}
|
||||
else {
|
||||
auto sopt = g_MenuApplication->CreateShowDialog(GetLanguageString("lang_set"), GetLanguageString("lang_set_conf"), { GetLanguageString("yes"), GetLanguageString("no") }, true);
|
||||
if(sopt == 0) {
|
||||
u64 codes[16] = {0};
|
||||
const auto option = g_MenuApplication->CreateShowDialog(GetLanguageString("lang_set"), GetLanguageString("lang_set_conf"), { GetLanguageString("yes"), GetLanguageString("no") }, true);
|
||||
if(option == 0) {
|
||||
u64 lang_codes[os::LanguageNameCount] = {};
|
||||
s32 tmp;
|
||||
setGetAvailableLanguageCodes(&tmp, codes, 16);
|
||||
auto code = codes[this->langsMenu->GetSelectedIndex()];
|
||||
setGetAvailableLanguageCodes(&tmp, lang_codes, os::LanguageNameCount);
|
||||
const auto lang_code = lang_codes[this->langs_menu->GetSelectedIndex()];
|
||||
|
||||
auto rc = setsysSetLanguageCode(code);
|
||||
const auto rc = setsysSetLanguageCode(lang_code);
|
||||
g_MenuApplication->CreateShowDialog(GetLanguageString("lang_set"), R_SUCCEEDED(rc) ? GetLanguageString("lang_set_ok") : GetLanguageString("lang_set_error") + ": " + util::FormatResult(rc), { GetLanguageString("ok") }, true);
|
||||
if(R_SUCCEEDED(rc)) {
|
||||
g_TransitionGuard.Run([]() {
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
extern ui::MenuApplication::Ref g_MenuApplication;
|
||||
extern ui::TransitionGuard g_TransitionGuard;
|
||||
extern u8 *g_ScreenCaptureBuffer;
|
||||
|
||||
extern cfg::Theme g_Theme;
|
||||
|
||||
|
@ -21,31 +20,37 @@ namespace ui {
|
|||
}
|
||||
|
||||
void MenuApplication::OnLoad() {
|
||||
u8 *screen_capture_buf = nullptr;
|
||||
if(this->IsSuspended()) {
|
||||
screen_capture_buf = new u8[PlainRgbaScreenBufferSize]();
|
||||
bool flag;
|
||||
appletGetLastApplicationCaptureImageEx(g_ScreenCaptureBuffer, RawRGBAScreenBufferSize, &flag);
|
||||
appletGetLastApplicationCaptureImageEx(screen_capture_buf, PlainRgbaScreenBufferSize, &flag);
|
||||
}
|
||||
|
||||
auto jbgm = JSON::object();
|
||||
util::LoadJSONFromFile(jbgm, cfg::GetAssetByTheme(g_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);
|
||||
this->bgm_json = JSON::object();
|
||||
util::LoadJSONFromFile(this->bgm_json, cfg::GetAssetByTheme(g_Theme, "sound/BGM.json"));
|
||||
this->bgm_loop = this->bgm_json.value("loop", true);
|
||||
this->bgm_fade_in_ms = this->bgm_json.value("fade_in_ms", 1500);
|
||||
this->bgm_fade_out_ms = this->bgm_json.value("fade_out_ms", 500);
|
||||
|
||||
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);
|
||||
const auto toast_text_clr = pu::ui::Color::FromHex(GetUIConfigValue<std::string>("toast_text_color", "#e1e1e1ff"));
|
||||
const auto toast_base_clr = pu::ui::Color::FromHex(GetUIConfigValue<std::string>("toast_base_color", "#282828ff"));
|
||||
this->notif_toast = pu::ui::extras::Toast::New("...", pu::ui::GetDefaultFont(pu::ui::DefaultFontSize::Medium), toast_text_clr, toast_base_clr);
|
||||
|
||||
this->bgm = pu::audio::Open(cfg::GetAssetByTheme(g_Theme, "sound/BGM.mp3"));
|
||||
this->bgm = pu::audio::OpenMusic(cfg::GetAssetByTheme(g_Theme, "sound/BGM.mp3"));
|
||||
|
||||
this->startupLayout = StartupLayout::New();
|
||||
this->menuLayout = MenuLayout::New(g_ScreenCaptureBuffer, this->uijson.value("suspended_final_alpha", 80));
|
||||
this->themeMenuLayout = ThemeMenuLayout::New();
|
||||
this->settingsMenuLayout = SettingsMenuLayout::New();
|
||||
this->languagesMenuLayout = LanguagesMenuLayout::New();
|
||||
this->text_clr = pu::ui::Color::FromHex(this->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
|
||||
this->menu_focus_clr = pu::ui::Color::FromHex(this->GetUIConfigValue<std::string>("menu_focus_color", "#5ebcffff"));
|
||||
this->menu_bg_clr = pu::ui::Color::FromHex(this->GetUIConfigValue<std::string>("menu_bg_color", "#0094ffff"));
|
||||
|
||||
switch(this->stmode) {
|
||||
const u8 suspended_final_alpha = this->ui_json.value("suspended_final_alpha", 80);
|
||||
this->startup_lyt = StartupLayout::New();
|
||||
this->menu_lyt = MenuLayout::New(screen_capture_buf, suspended_final_alpha);
|
||||
this->theme_menu_lyt = ThemeMenuLayout::New();
|
||||
this->settings_menu_lyt = SettingsMenuLayout::New();
|
||||
this->languages_menu_lyt = LanguagesMenuLayout::New();
|
||||
|
||||
switch(this->start_mode) {
|
||||
case dmi::MenuStartMode::StartupScreen: {
|
||||
this->LoadStartupMenu();
|
||||
break;
|
||||
|
@ -58,124 +63,37 @@ namespace ui {
|
|||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
this->menuLayout->SetUser(this->status.selected_user);
|
||||
this->LoadLayout(this->menuLayout);
|
||||
this->loaded_menu = MenuType::Main;
|
||||
}
|
||||
|
||||
void MenuApplication::LoadStartupMenu() {
|
||||
this->StopPlayBGM();
|
||||
this->startupLayout->ReloadMenu();
|
||||
this->LoadLayout(this->startupLayout);
|
||||
this->loaded_menu = MenuType::Startup;
|
||||
}
|
||||
|
||||
void MenuApplication::LoadThemeMenu() {
|
||||
this->themeMenuLayout->Reload();
|
||||
this->LoadLayout(this->themeMenuLayout);
|
||||
this->loaded_menu = MenuType::Theme;
|
||||
}
|
||||
|
||||
void MenuApplication::LoadSettingsMenu() {
|
||||
this->settingsMenuLayout->Reload();
|
||||
this->LoadLayout(this->settingsMenuLayout);
|
||||
this->loaded_menu = MenuType::Settings;
|
||||
}
|
||||
|
||||
void MenuApplication::LoadSettingsLanguagesMenu() {
|
||||
this->languagesMenuLayout->Reload();
|
||||
this->LoadLayout(this->languagesMenuLayout);
|
||||
this->loaded_menu = MenuType::Languages;
|
||||
}
|
||||
|
||||
bool MenuApplication::IsSuspended() {
|
||||
return this->IsTitleSuspended() || this->IsHomebrewSuspended();
|
||||
}
|
||||
|
||||
bool MenuApplication::IsTitleSuspended() {
|
||||
return this->status.app_id > 0;
|
||||
}
|
||||
|
||||
bool MenuApplication::IsHomebrewSuspended() {
|
||||
return strlen(this->status.params.nro_path);
|
||||
}
|
||||
|
||||
bool MenuApplication::EqualsSuspendedHomebrewPath(const std::string &path) {
|
||||
return this->status.params.nro_path == path;
|
||||
}
|
||||
|
||||
u64 MenuApplication::GetSuspendedApplicationId() {
|
||||
return this->status.app_id;
|
||||
}
|
||||
|
||||
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 == dmi::MenuStartMode::MenuLaunchFailure;
|
||||
}
|
||||
|
||||
void MenuApplication::ShowNotification(const std::string &text, u64 timeout) {
|
||||
void MenuApplication::ShowNotification(const std::string &text, const u64 timeout) {
|
||||
this->EndOverlay();
|
||||
this->notifToast->SetText(text);
|
||||
this->StartOverlayWithTimeout(this->notifToast, timeout);
|
||||
this->notif_toast->SetText(text);
|
||||
this->StartOverlayWithTimeout(this->notif_toast, timeout);
|
||||
}
|
||||
|
||||
void MenuApplication::StartPlayBGM() {
|
||||
if(this->bgm != nullptr) {
|
||||
int loops = this->bgm_loop ? -1 : 1;
|
||||
const 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);
|
||||
pu::audio::PlayMusicWithFadeIn(this->bgm, loops, this->bgm_fade_in_ms);
|
||||
}
|
||||
else {
|
||||
pu::audio::Play(this->bgm, loops);
|
||||
pu::audio::PlayMusic(this->bgm, loops);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MenuApplication::StopPlayBGM() {
|
||||
if(this->bgm_fade_out_ms > 0) {
|
||||
pu::audio::FadeOut(this->bgm_fade_out_ms);
|
||||
pu::audio::FadeOutMusic(this->bgm_fade_out_ms);
|
||||
}
|
||||
else {
|
||||
pu::audio::Stop();
|
||||
pu::audio::StopMusic();
|
||||
}
|
||||
}
|
||||
|
||||
StartupLayout::Ref &MenuApplication::GetStartupLayout() {
|
||||
return this->startupLayout;
|
||||
}
|
||||
|
||||
MenuLayout::Ref &MenuApplication::GetMenuLayout() {
|
||||
return this->menuLayout;
|
||||
}
|
||||
|
||||
ThemeMenuLayout::Ref &MenuApplication::GetThemeMenuLayout() {
|
||||
return this->themeMenuLayout;
|
||||
}
|
||||
|
||||
SettingsMenuLayout::Ref &MenuApplication::GetSettingsMenuLayout() {
|
||||
return this->settingsMenuLayout;
|
||||
}
|
||||
|
||||
LanguagesMenuLayout::Ref &MenuApplication::GetLanguagesMenuLayout() {
|
||||
return this->languagesMenuLayout;
|
||||
}
|
||||
|
||||
void MenuApplication::SetSelectedUser(AccountUid user_id) {
|
||||
this->status.selected_user = user_id;
|
||||
void MenuApplication::SetSelectedUser(const AccountUid user_id) {
|
||||
this->daemon_status.selected_user = user_id;
|
||||
|
||||
UL_ASSERT(dmi::menu::SendCommand(dmi::DaemonMessage::SetSelectedUser, [&](dmi::menu::MenuScopedStorageWriter &writer) {
|
||||
UL_RC_ASSERT(dmi::menu::SendCommand(dmi::DaemonMessage::SetSelectedUser, [&](dmi::menu::MenuScopedStorageWriter &writer) {
|
||||
writer.Push(user_id);
|
||||
return ResultSuccess;
|
||||
},
|
||||
|
@ -185,12 +103,4 @@ namespace ui {
|
|||
}));
|
||||
}
|
||||
|
||||
AccountUid MenuApplication::GetSelectedUser() {
|
||||
return this->status.selected_user;
|
||||
}
|
||||
|
||||
MenuType MenuApplication::GetCurrentLoadedMenu() {
|
||||
return this->loaded_menu;
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -14,134 +14,107 @@ namespace ui {
|
|||
|
||||
}
|
||||
|
||||
void QuickMenu::OnHomeButtonDetection() {
|
||||
g_HomePressed = true;
|
||||
}
|
||||
|
||||
QuickMenu::QuickMenu(const std::string &main_icon) {
|
||||
this->on = false;
|
||||
this->bgalpha = 0;
|
||||
this->bg_alpha = 0;
|
||||
|
||||
auto textclr = pu::ui::Color::FromHex(g_MenuApplication->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
|
||||
auto menufocusclr = pu::ui::Color::FromHex(g_MenuApplication->GetUIConfigValue<std::string>("menu_focus_color", "#5ebcffff"));
|
||||
auto menubgclr = pu::ui::Color::FromHex(g_MenuApplication->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);
|
||||
this->options_menu = pu::ui::elm::Menu::New(MenuX, MenuY, MenuWidth, g_MenuApplication->GetMenuBackgroundColor(), g_MenuApplication->GetMenuFocusColor(), MenuItemHeight, MenuItemsToShow);
|
||||
g_MenuApplication->ApplyConfigForElement("quick_menu", "quick_menu_item", this->options_menu);
|
||||
|
||||
auto opt_item = pu::ui::elm::MenuItem::New("Help & information");
|
||||
opt_item->SetIcon(cfg::GetAssetByTheme(g_Theme, "ui/HelpIcon.png"));
|
||||
opt_item->AddOnClick(&actions::ShowHelpDialog);
|
||||
opt_item->SetColor(textclr);
|
||||
this->options_menu->AddItem(opt_item);
|
||||
auto help_item = pu::ui::elm::MenuItem::New("Help & information");
|
||||
help_item->SetIcon(cfg::GetAssetByTheme(g_Theme, "ui/HelpIcon.png"));
|
||||
help_item->AddOnKey(&actions::ShowHelpDialog);
|
||||
help_item->SetColor(g_MenuApplication->GetTextColor());
|
||||
this->options_menu->AddItem(help_item);
|
||||
|
||||
opt_item = pu::ui::elm::MenuItem::New("Power options");
|
||||
opt_item->SetIcon(cfg::GetAssetByTheme(g_Theme, "ui/PowerIcon.png"));
|
||||
opt_item->AddOnClick(&actions::ShowPowerDialog);
|
||||
opt_item->SetColor(textclr);
|
||||
this->options_menu->AddItem(opt_item);
|
||||
auto power_item = pu::ui::elm::MenuItem::New("Power options");
|
||||
power_item->SetIcon(cfg::GetAssetByTheme(g_Theme, "ui/PowerIcon.png"));
|
||||
power_item->AddOnKey(&actions::ShowPowerDialog);
|
||||
power_item->SetColor(g_MenuApplication->GetTextColor());
|
||||
this->options_menu->AddItem(power_item);
|
||||
|
||||
opt_item = pu::ui::elm::MenuItem::New("Controller options");
|
||||
opt_item->SetIcon(cfg::GetAssetByTheme(g_Theme, "ui/ControllerIcon.png"));
|
||||
opt_item->AddOnClick(&actions::ShowControllerSupport);
|
||||
opt_item->SetColor(textclr);
|
||||
this->options_menu->AddItem(opt_item);
|
||||
auto controller_item = pu::ui::elm::MenuItem::New("Controller options");
|
||||
controller_item->SetIcon(cfg::GetAssetByTheme(g_Theme, "ui/ControllerIcon.png"));
|
||||
controller_item->AddOnKey(&actions::ShowControllerSupport);
|
||||
controller_item->SetColor(g_MenuApplication->GetTextColor());
|
||||
this->options_menu->AddItem(controller_item);
|
||||
|
||||
opt_item = pu::ui::elm::MenuItem::New("Open album");
|
||||
opt_item->SetIcon(cfg::GetAssetByTheme(g_Theme, "ui/AlbumIcon.png"));
|
||||
opt_item->AddOnClick(&actions::ShowAlbumApplet);
|
||||
opt_item->SetColor(textclr);
|
||||
this->options_menu->AddItem(opt_item);
|
||||
auto album_item = pu::ui::elm::MenuItem::New("Open album");
|
||||
album_item->SetIcon(cfg::GetAssetByTheme(g_Theme, "ui/AlbumIcon.png"));
|
||||
album_item->AddOnKey(&actions::ShowAlbumApplet);
|
||||
album_item->SetColor(g_MenuApplication->GetTextColor());
|
||||
this->options_menu->AddItem(album_item);
|
||||
|
||||
opt_item = pu::ui::elm::MenuItem::New("Open web-page");
|
||||
opt_item->SetIcon(cfg::GetAssetByTheme(g_Theme, "ui/WebIcon.png"));
|
||||
opt_item->AddOnClick(&actions::ShowWebPage);
|
||||
opt_item->SetColor(textclr);
|
||||
this->options_menu->AddItem(opt_item);
|
||||
auto web_item = pu::ui::elm::MenuItem::New("Open web-page");
|
||||
web_item->SetIcon(cfg::GetAssetByTheme(g_Theme, "ui/WebIcon.png"));
|
||||
web_item->AddOnKey(&actions::ShowWebPage);
|
||||
web_item->SetColor(g_MenuApplication->GetTextColor());
|
||||
this->options_menu->AddItem(web_item);
|
||||
|
||||
opt_item = pu::ui::elm::MenuItem::New("User menu");
|
||||
opt_item->SetIcon(cfg::GetAssetByTheme(g_Theme, "ui/UserIcon.png"));
|
||||
opt_item->AddOnClick(&actions::ShowUserMenu);
|
||||
opt_item->SetColor(textclr);
|
||||
this->options_menu->AddItem(opt_item);
|
||||
auto user_item = pu::ui::elm::MenuItem::New("User menu");
|
||||
user_item->SetIcon(cfg::GetAssetByTheme(g_Theme, "ui/UserIcon.png"));
|
||||
user_item->AddOnKey(&actions::ShowUserMenu);
|
||||
user_item->SetColor(g_MenuApplication->GetTextColor());
|
||||
this->options_menu->AddItem(user_item);
|
||||
|
||||
opt_item = pu::ui::elm::MenuItem::New("Themes menu");
|
||||
opt_item->SetIcon(cfg::GetAssetByTheme(g_Theme, "ui/ThemesIcon.png"));
|
||||
opt_item->AddOnClick(&actions::ShowThemesMenu);
|
||||
opt_item->SetColor(textclr);
|
||||
this->options_menu->AddItem(opt_item);
|
||||
auto themes_item = pu::ui::elm::MenuItem::New("Themes menu");
|
||||
themes_item->SetIcon(cfg::GetAssetByTheme(g_Theme, "ui/ThemesIcon.png"));
|
||||
themes_item->AddOnKey(&actions::ShowThemesMenu);
|
||||
themes_item->SetColor(g_MenuApplication->GetTextColor());
|
||||
this->options_menu->AddItem(themes_item);
|
||||
|
||||
opt_item = pu::ui::elm::MenuItem::New("Settings menu");
|
||||
opt_item->SetIcon(cfg::GetAssetByTheme(g_Theme, "ui/SettingsIcon.png"));
|
||||
opt_item->AddOnClick(&actions::ShowSettingsMenu);
|
||||
opt_item->SetColor(textclr);
|
||||
this->options_menu->AddItem(opt_item);
|
||||
auto settings_item = pu::ui::elm::MenuItem::New("Settings menu");
|
||||
settings_item->SetIcon(cfg::GetAssetByTheme(g_Theme, "ui/SettingsIcon.png"));
|
||||
settings_item->AddOnKey(&actions::ShowSettingsMenu);
|
||||
settings_item->SetColor(g_MenuApplication->GetTextColor());
|
||||
this->options_menu->AddItem(settings_item);
|
||||
}
|
||||
|
||||
s32 QuickMenu::GetX() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 QuickMenu::GetY() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 QuickMenu::GetWidth() {
|
||||
return 1280;
|
||||
}
|
||||
|
||||
s32 QuickMenu::GetHeight() {
|
||||
return 720;
|
||||
}
|
||||
|
||||
void QuickMenu::Toggle() {
|
||||
this->on = !this->on;
|
||||
}
|
||||
|
||||
bool QuickMenu::IsOn() {
|
||||
return this->on && (this->bgalpha > 0);
|
||||
}
|
||||
|
||||
void QuickMenu::OnRender(pu::ui::render::Renderer::Ref &drawer, s32 x, s32 y) {
|
||||
void QuickMenu::OnRender(pu::ui::render::Renderer::Ref &drawer, const s32 x, const s32 y) {
|
||||
if(!this->on) {
|
||||
if(this->bgalpha > 0) {
|
||||
this->bgalpha -= 20;
|
||||
if(this->bgalpha < 0) {
|
||||
this->bgalpha = 0;
|
||||
if(this->bg_alpha > 0) {
|
||||
this->bg_alpha -= BackgroundAlphaIncrement;
|
||||
if(this->bg_alpha < 0) {
|
||||
this->bg_alpha = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(this->bgalpha < 220) {
|
||||
this->bgalpha += 20;
|
||||
if(this->bgalpha > 220) {
|
||||
this->bgalpha = 220;
|
||||
if(this->bg_alpha < BackgroundAlphaMax) {
|
||||
this->bg_alpha += BackgroundAlphaIncrement;
|
||||
if(this->bg_alpha > BackgroundAlphaMax) {
|
||||
this->bg_alpha = BackgroundAlphaMax;
|
||||
}
|
||||
}
|
||||
}
|
||||
this->options_menu->SetVisible(this->bgalpha != 0);
|
||||
this->options_menu->SetVisible(this->bg_alpha != 0);
|
||||
|
||||
auto bgalphau8 = static_cast<u8>(this->bgalpha);
|
||||
drawer->RenderRectangleFill(MakeBackgroundColor(static_cast<u8>(this->bg_alpha)), this->GetX(), this->GetY(), this->GetWidth(), this->GetHeight());
|
||||
|
||||
drawer->RenderRectangleFill({ 50, 50, 50, bgalphau8 }, 0, 0, 1280, 720);
|
||||
|
||||
if(this->bgalpha > 0) {
|
||||
if(this->bgalpha < 220) {
|
||||
drawer->SetBaseRenderAlpha(bgalphau8);
|
||||
if(this->bg_alpha > 0) {
|
||||
if(this->bg_alpha < BackgroundAlphaMax) {
|
||||
drawer->SetBaseRenderAlpha(static_cast<u8>(this->bg_alpha));
|
||||
}
|
||||
this->options_menu->OnRender(drawer, this->options_menu->GetProcessedX(), this->options_menu->GetProcessedY());
|
||||
if(this->bgalpha < 220) {
|
||||
drawer->UnsetBaseRenderAlpha();
|
||||
if(this->bg_alpha < BackgroundAlphaMax) {
|
||||
drawer->ResetBaseRenderAlpha();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QuickMenu::OnInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) {
|
||||
void QuickMenu::OnInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos) {
|
||||
if(this->on) {
|
||||
this->options_menu->OnInput(down, up, held, touch_pos);
|
||||
this->options_menu->OnInput(keys_down, keys_up, keys_held, touch_pos);
|
||||
}
|
||||
|
||||
if((down & HidNpadButton_L) || (down & HidNpadButton_R) || (down & HidNpadButton_ZL) || (down & HidNpadButton_ZR)) {
|
||||
if((keys_down & HidNpadButton_L) || (keys_down & HidNpadButton_R) || (keys_down & HidNpadButton_ZL) || (keys_down & HidNpadButton_ZR)) {
|
||||
this->Toggle();
|
||||
}
|
||||
else if((down & HidNpadButton_B) || (down & HidNpadButton_A)) {
|
||||
else if((keys_down & HidNpadButton_B) || (keys_down & HidNpadButton_A)) {
|
||||
// B only valid for toggling off
|
||||
// A = something selected in the menu
|
||||
if(this->on) {
|
||||
|
@ -159,8 +132,4 @@ namespace ui {
|
|||
}
|
||||
}
|
||||
|
||||
void QuickMenuOnHomeButtonDetection() {
|
||||
g_HomePressed = true;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
#include <ui/ui_RawData.hpp>
|
||||
|
||||
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) {
|
||||
pu::ui::render::DeleteTexture(this->ntex);
|
||||
this->ntex = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
s32 RawData::GetX() {
|
||||
return this->x;
|
||||
}
|
||||
|
||||
void RawData::SetX(s32 x) {
|
||||
this->x = x;
|
||||
}
|
||||
|
||||
s32 RawData::GetY() {
|
||||
return this->y;
|
||||
}
|
||||
|
||||
void RawData::SetY(s32 y) {
|
||||
this->y = y;
|
||||
}
|
||||
|
||||
s32 RawData::GetWidth() {
|
||||
return this->w;
|
||||
}
|
||||
|
||||
void RawData::SetWidth(s32 w) {
|
||||
this->w = w;
|
||||
}
|
||||
|
||||
s32 RawData::GetHeight() {
|
||||
return this->h;
|
||||
}
|
||||
|
||||
void RawData::SetHeight(s32 h) {
|
||||
this->h = h;
|
||||
}
|
||||
|
||||
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::OnInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) {}
|
||||
|
||||
}
|
24
uMenu/source/ui/ui_RawRgbaImage.cpp
Normal file
24
uMenu/source/ui/ui_RawRgbaImage.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include <ui/ui_RawRgbaImage.hpp>
|
||||
|
||||
namespace ui {
|
||||
|
||||
RawRgbaImage::RawRgbaImage(const s32 x, const s32 y, const u8 *rgba_data, const s32 w, const s32 h, const u32 pix_num) : x(x), y(y), w(w), h(h), alpha(0xFF) {
|
||||
if(rgba_data != nullptr) {
|
||||
this->img_tex = SDL_CreateTexture(pu::ui::render::GetMainRenderer(), SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, this->w, this->h);
|
||||
if(this->img_tex != nullptr) {
|
||||
SDL_UpdateTexture(this->img_tex, nullptr, rgba_data, w * pix_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RawRgbaImage::~RawRgbaImage() {
|
||||
pu::ui::render::DeleteTexture(this->img_tex);
|
||||
}
|
||||
|
||||
void RawRgbaImage::OnRender(pu::ui::render::Renderer::Ref &drawer, const s32 x, const s32 y) {
|
||||
if(this->img_tex != nullptr) {
|
||||
drawer->RenderTexture(this->img_tex, x, y, pu::ui::render::TextureRenderOptions::WithCustomAlphaAndDimensions(this->alpha, this->w, this->h));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -37,24 +37,19 @@ namespace ui {
|
|||
SettingsMenuLayout::SettingsMenuLayout() {
|
||||
this->SetBackgroundImage(cfg::GetAssetByTheme(g_Theme, "ui/Background.png"));
|
||||
|
||||
pu::ui::Color textclr = pu::ui::Color::FromHex(g_MenuApplication->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
|
||||
pu::ui::Color menufocusclr = pu::ui::Color::FromHex(g_MenuApplication->GetUIConfigValue<std::string>("menu_focus_color", "#5ebcffff"));
|
||||
pu::ui::Color menubgclr = pu::ui::Color::FromHex(g_MenuApplication->GetUIConfigValue<std::string>("menu_bg_color", "#0094ffff"));
|
||||
this->info_text = pu::ui::elm::TextBlock::New(0, 25, GetLanguageString("set_info_text"));
|
||||
this->info_text->SetColor(g_MenuApplication->GetTextColor());
|
||||
this->info_text->SetHorizontalAlign(pu::ui::elm::HorizontalAlign::Center);
|
||||
g_MenuApplication->ApplyConfigForElement("settings_menu", "info_text", this->info_text);
|
||||
this->Add(this->info_text);
|
||||
|
||||
this->infoText = pu::ui::elm::TextBlock::New(0, 100, GetLanguageString("set_info_text"));
|
||||
this->infoText->SetColor(textclr);
|
||||
this->infoText->SetHorizontalAlign(pu::ui::elm::HorizontalAlign::Center);
|
||||
g_MenuApplication->ApplyConfigForElement("settings_menu", "info_text", this->infoText);
|
||||
this->Add(this->infoText);
|
||||
|
||||
this->settingsMenu = pu::ui::elm::Menu::New(50, 160, 1180, menubgclr, 100, 4);
|
||||
this->settingsMenu->SetOnFocusColor(menufocusclr);
|
||||
g_MenuApplication->ApplyConfigForElement("settings_menu", "settings_menu_item", this->settingsMenu);
|
||||
this->Add(this->settingsMenu);
|
||||
this->settings_menu = pu::ui::elm::Menu::New(50, 80, 1180, g_MenuApplication->GetMenuBackgroundColor(), g_MenuApplication->GetMenuFocusColor(), 100, 6);
|
||||
g_MenuApplication->ApplyConfigForElement("settings_menu", "settings_menu_item", this->settings_menu);
|
||||
this->Add(this->settings_menu);
|
||||
}
|
||||
|
||||
void SettingsMenuLayout::OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch pos) {
|
||||
if(down & HidNpadButton_B) {
|
||||
void SettingsMenuLayout::OnMenuInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos) {
|
||||
if(keys_down & HidNpadButton_B) {
|
||||
g_TransitionGuard.Run([]() {
|
||||
g_MenuApplication->FadeOut();
|
||||
g_MenuApplication->LoadMenu();
|
||||
|
@ -71,20 +66,22 @@ namespace ui {
|
|||
});
|
||||
}
|
||||
|
||||
void SettingsMenuLayout::Reload() {
|
||||
void SettingsMenuLayout::Reload(const bool reset_idx) {
|
||||
// TODO: more settings
|
||||
this->settingsMenu->ClearItems();
|
||||
this->settingsMenu->SetSelectedIndex(0);
|
||||
this->settings_menu->ClearItems();
|
||||
|
||||
SetSysDeviceNickName console_name = {};
|
||||
setsysGetDeviceNickname(&console_name);
|
||||
this->PushSettingItem(GetLanguageString("set_console_nickname"), EncodeForSettings<std::string>(console_name.nickname), 0);
|
||||
|
||||
TimeLocationName loc = {};
|
||||
timeGetDeviceLocationName(&loc);
|
||||
this->PushSettingItem(GetLanguageString("set_console_timezone"), EncodeForSettings<std::string>(loc.name), -1);
|
||||
|
||||
bool viewer_usb_enabled;
|
||||
UL_ASSERT_TRUE(g_Config.GetEntry(cfg::ConfigEntryId::ViewerUsbEnabled, viewer_usb_enabled));
|
||||
this->PushSettingItem(GetLanguageString("set_viewer_enabled"), EncodeForSettings(viewer_usb_enabled), 1);
|
||||
|
||||
auto connected_wifi_name = GetLanguageString("set_wifi_none");
|
||||
if(net::HasConnection()) {
|
||||
net::NetworkProfileData data = {};
|
||||
|
@ -99,48 +96,64 @@ namespace ui {
|
|||
setMakeLanguage(lang_code, &lang_val);
|
||||
const std::string lang_str = os::LanguageNameList[static_cast<u32>(lang_val)];
|
||||
this->PushSettingItem(GetLanguageString("set_console_lang"), EncodeForSettings(lang_str), 3);
|
||||
|
||||
auto console_info_upload = false;
|
||||
setsysGetConsoleInformationUploadFlag(&console_info_upload);
|
||||
this->PushSettingItem(GetLanguageString("set_console_info_upload"), EncodeForSettings(console_info_upload), 4);
|
||||
|
||||
auto auto_titles_dl = false;
|
||||
setsysGetAutomaticApplicationDownloadFlag(&auto_titles_dl);
|
||||
this->PushSettingItem(GetLanguageString("set_auto_titles_dl"), EncodeForSettings(auto_titles_dl), 5);
|
||||
|
||||
auto auto_update = false;
|
||||
setsysGetAutoUpdateEnableFlag(&auto_update);
|
||||
this->PushSettingItem(GetLanguageString("set_auto_update"), EncodeForSettings(auto_update), 6);
|
||||
|
||||
auto wireless_lan = false;
|
||||
setsysGetWirelessLanEnableFlag(&wireless_lan);
|
||||
this->PushSettingItem(GetLanguageString("set_wireless_lan"), EncodeForSettings(wireless_lan), 7);
|
||||
|
||||
auto bluetooth = false;
|
||||
setsysGetBluetoothEnableFlag(&bluetooth);
|
||||
this->PushSettingItem(GetLanguageString("set_bluetooth"), EncodeForSettings(bluetooth), 8);
|
||||
|
||||
auto usb_30 = false;
|
||||
setsysGetUsb30EnableFlag(&usb_30);
|
||||
this->PushSettingItem(GetLanguageString("set_usb_30"), EncodeForSettings(usb_30), 9);
|
||||
|
||||
auto nfc = false;
|
||||
setsysGetNfcEnableFlag(&nfc);
|
||||
this->PushSettingItem(GetLanguageString("set_nfc"), EncodeForSettings(nfc), 10);
|
||||
|
||||
SetSysSerialNumber serial = {};
|
||||
setsysGetSerialNumber(&serial);
|
||||
this->PushSettingItem(GetLanguageString("set_serial_no"), EncodeForSettings<std::string>(serial.number), -1);
|
||||
|
||||
u64 mac = 0;
|
||||
net::GetMACAddress(&mac);
|
||||
const auto mac_addr_str = net::FormatMACAddress(mac);
|
||||
this->PushSettingItem(GetLanguageString("set_mac_addr"), EncodeForSettings(mac_addr_str), -1);
|
||||
|
||||
const auto ip_str = net::GetConsoleIPAddress();
|
||||
this->PushSettingItem("Console IP address", EncodeForSettings(ip_str), -1);
|
||||
|
||||
if(reset_idx) {
|
||||
this->settings_menu->SetSelectedIndex(0);
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsMenuLayout::PushSettingItem(const std::string &name, const std::string &value_display, int id) {
|
||||
auto textclr = pu::ui::Color::FromHex(g_MenuApplication->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_Theme, "ui/Setting" + std::string((id < 0) ? "No" : "") + "Editable.png"));
|
||||
itm->SetColor(textclr);
|
||||
this->settingsMenu->AddItem(itm);
|
||||
const auto is_editable = id >= 0;
|
||||
auto setting_item = pu::ui::elm::MenuItem::New(name + ": " + value_display);
|
||||
if(is_editable) {
|
||||
setting_item->AddOnKey(std::bind(&SettingsMenuLayout::setting_DefaultKey, this, id));
|
||||
}
|
||||
setting_item->SetIcon(cfg::GetAssetByTheme(g_Theme, "ui/Setting" + std::string(is_editable ? "" : "No") + "Editable.png"));
|
||||
setting_item->SetColor(g_MenuApplication->GetTextColor());
|
||||
this->settings_menu->AddItem(setting_item);
|
||||
}
|
||||
|
||||
void SettingsMenuLayout::setting_Click(u32 id) {
|
||||
void SettingsMenuLayout::setting_DefaultKey(const u32 id) {
|
||||
bool reload_need = false;
|
||||
switch(id) {
|
||||
case 0: {
|
||||
|
@ -256,9 +269,10 @@ namespace ui {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(reload_need) {
|
||||
cfg::SaveConfig(g_Config);
|
||||
this->Reload();
|
||||
this->Reload(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,446 +1,347 @@
|
|||
#include <ui/ui_SideMenu.hpp>
|
||||
|
||||
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) : 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);
|
||||
namespace ui {
|
||||
|
||||
bool SideMenu::IsLeftFirst() {
|
||||
auto base_x = GetProcessedX();
|
||||
constexpr auto first_item_x = BaseX;
|
||||
for(u32 i = 0; i < this->rendered_icons.size(); i++) {
|
||||
if((base_x == first_item_x) && (this->selected_item_idx == (this->base_icon_idx + i))) {
|
||||
return true;
|
||||
}
|
||||
base_x += ItemSize + Margin;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SideMenu::IsRightLast() {
|
||||
if(this->selected_item_idx == (this->items_icon_paths.size() - 1)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto base_x = GetProcessedX();
|
||||
constexpr auto last_item_x = BaseX + (Margin + ItemSize) * (ItemCount - 1);
|
||||
for(u32 i = 0; i < this->rendered_icons.size(); i++) {
|
||||
if((base_x == last_item_x) && (this->selected_item_idx == (this->base_icon_idx + i))) {
|
||||
return true;
|
||||
}
|
||||
base_x += ItemSize + Margin;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SideMenu::MoveReloadIcons(const bool moving_right) {
|
||||
if(moving_right) {
|
||||
auto icon_tex = pu::ui::render::LoadImage(this->items_icon_paths.at(this->selected_item_idx));
|
||||
this->rendered_icons.push_back(icon_tex);
|
||||
|
||||
auto text_tex = pu::ui::render::RenderText(this->text_font, this->items_icon_texts.at(this->selected_item_idx), this->text_clr);
|
||||
this->rendered_texts.push_back(text_tex);
|
||||
|
||||
if(this->rendered_icons.size() > ItemCount) {
|
||||
pu::ui::render::DeleteTexture(this->rendered_icons.front());
|
||||
this->rendered_icons.erase(this->rendered_icons.begin());
|
||||
pu::ui::render::DeleteTexture(this->rendered_texts.front());
|
||||
this->rendered_texts.erase(this->rendered_texts.begin());
|
||||
this->base_icon_idx++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto icon_tex = pu::ui::render::LoadImage(this->items_icon_paths.at(this->selected_item_idx));
|
||||
this->rendered_icons.insert(this->rendered_icons.begin(), icon_tex);
|
||||
const auto text = this->items_icon_texts.at(this->selected_item_idx);
|
||||
pu::sdl2::Texture text_tex = nullptr;
|
||||
if(!text.empty()) {
|
||||
text_tex = pu::ui::render::RenderText(this->text_font, text, this->text_clr);
|
||||
}
|
||||
this->rendered_texts.insert(this->rendered_texts.begin(), text_tex);
|
||||
|
||||
this->base_icon_idx--;
|
||||
if(this->rendered_icons.size() > ItemCount) {
|
||||
pu::ui::render::DeleteTexture(this->rendered_icons.back());
|
||||
this->rendered_icons.pop_back();
|
||||
pu::ui::render::DeleteTexture(this->rendered_texts.back());
|
||||
this->rendered_texts.pop_back();
|
||||
}
|
||||
}
|
||||
this->UpdateBorderIcons();
|
||||
}
|
||||
|
||||
SideMenu::SideMenu(const pu::ui::Color suspended_clr, const std::string &cursor_path, const std::string &suspended_img_path, const std::string &multiselect_img_path, const s32 txt_x, const s32 txt_y, const std::string &font_name, const pu::ui::Color txt_clr, const s32 y) : selected_item_idx(0), suspended_item_idx(-1), base_icon_idx(0), move_alpha(0), text_x(txt_x), text_y(txt_y), enabled(true), text_clr(txt_clr), on_select_cb(), on_selection_changed_cb(), left_border_icon(nullptr), right_border_icon(nullptr), text_font(font_name), scroll_flag(0), scroll_tp_value(50), scroll_count(0) {
|
||||
this->cursor_icon = pu::ui::render::LoadImage(cursor_path);
|
||||
this->suspended_icon = pu::ui::render::LoadImage(suspended_img_path);
|
||||
this->multiselect_icon = pu::ui::render::LoadImage(multiselect_img_path);
|
||||
this->SetY(y);
|
||||
}
|
||||
|
||||
SideMenu::~SideMenu() {
|
||||
if(this->cursoricon != nullptr) {
|
||||
pu::ui::render::DeleteTexture(this->cursoricon);
|
||||
this->cursoricon = nullptr;
|
||||
}
|
||||
if(this->suspicon != nullptr) {
|
||||
pu::ui::render::DeleteTexture(this->suspicon);
|
||||
this->suspicon = nullptr;
|
||||
}
|
||||
pu::ui::render::DeleteTexture(this->cursor_icon);
|
||||
pu::ui::render::DeleteTexture(this->suspended_icon);
|
||||
this->ClearItems();
|
||||
}
|
||||
|
||||
s32 SideMenu::GetX() {
|
||||
return 98;
|
||||
}
|
||||
|
||||
s32 SideMenu::GetY() {
|
||||
return this->y;
|
||||
}
|
||||
|
||||
void SideMenu::SetX(s32 x) {}
|
||||
|
||||
void SideMenu::SetY(s32 y) {
|
||||
this->y = y;
|
||||
}
|
||||
|
||||
s32 SideMenu::GetWidth() {
|
||||
return 1280;
|
||||
}
|
||||
s32 SideMenu::GetHeight() {
|
||||
return ItemSize + FocusSize + FocusMargin;
|
||||
}
|
||||
|
||||
void SideMenu::OnRender(pu::ui::render::Renderer::Ref &drawer, s32 x, s32 y) {
|
||||
if(this->icons.empty()) {
|
||||
void SideMenu::OnRender(pu::ui::render::Renderer::Ref &drawer, const s32 x, const s32 y) {
|
||||
if(this->items_icon_paths.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(this->rendered_icons.empty()) {
|
||||
for(u32 i = 0; i < std::min(static_cast<size_t>(ItemCount), this->items_icon_paths.size() - this->base_icon_idx); i++) {
|
||||
auto icon_tex = pu::ui::render::LoadImage(this->items_icon_paths.at(this->base_icon_idx + i));
|
||||
const auto text = this->items_icon_texts.at(this->base_icon_idx + i);
|
||||
this->rendered_icons.push_back(icon_tex);
|
||||
|
||||
pu::sdl2::Texture text_tex = nullptr;
|
||||
if(!text.empty()) {
|
||||
ntext = pu::ui::render::RenderText(this->textfont, text, this->textclr);
|
||||
text_tex = pu::ui::render::RenderText(this->text_font, text, this->text_clr);
|
||||
}
|
||||
this->ricons_texts.push_back(ntext);
|
||||
this->rendered_texts.push_back(text_tex);
|
||||
}
|
||||
this->UpdateBorderIcons();
|
||||
(this->onselch)(this->selitm);
|
||||
this->DoOnSelectionChanged();
|
||||
}
|
||||
|
||||
u32 basex = x;
|
||||
|
||||
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);
|
||||
auto base_x = x;
|
||||
for(u32 i = 0; i < this->rendered_icons.size(); i++) {
|
||||
auto icon_tex = this->rendered_icons.at(i);
|
||||
drawer->RenderTexture(icon_tex, base_x, y, pu::ui::render::TextureRenderOptions::WithCustomDimensions(ItemSize, ItemSize));
|
||||
|
||||
auto text_tex = this->rendered_texts.at(i);
|
||||
if(text_tex != nullptr) {
|
||||
drawer->RenderTexture(text_tex, base_x + this->text_x, y + this->text_y);
|
||||
}
|
||||
if(this->IsItemMultiselected(this->baseiconidx + i)) {
|
||||
drawer->RenderTexture(this->mselicon, basex - Margin, y - Margin, { -1, ExtraIconSize, ExtraIconSize, -1 });
|
||||
if(this->IsItemMultiselected(this->base_icon_idx + i)) {
|
||||
drawer->RenderTexture(this->multiselect_icon, base_x - Margin, y - Margin, pu::ui::render::TextureRenderOptions::WithCustomDimensions(ExtraIconSize, ExtraIconSize));
|
||||
}
|
||||
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->suspended_item_idx >= 0) {
|
||||
if((this->base_icon_idx + i) == static_cast<u32>(this->suspended_item_idx)) {
|
||||
if(this->suspended_icon != nullptr) {
|
||||
drawer->RenderTexture(this->suspended_icon, base_x - Margin, y - Margin, pu::ui::render::TextureRenderOptions::WithCustomDimensions(ExtraIconSize, ExtraIconSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
if(this->cursoricon != nullptr) {
|
||||
if((this->baseiconidx + i) == selitm) {
|
||||
drawer->RenderTexture(this->cursoricon, basex - Margin, y - Margin, { 255 - movalpha, ExtraIconSize, ExtraIconSize, -1 });
|
||||
if(this->cursor_icon != nullptr) {
|
||||
if((this->base_icon_idx + i) == this->selected_item_idx) {
|
||||
drawer->RenderTexture(this->cursor_icon, base_x - Margin, y - Margin, pu::ui::render::TextureRenderOptions::WithCustomAlphaAndDimensions(0xFF - this->move_alpha, ExtraIconSize, ExtraIconSize));
|
||||
}
|
||||
else if((this->baseiconidx + i) == preselitm) {
|
||||
drawer->RenderTexture(this->cursoricon, basex - Margin, y - Margin, { movalpha, ExtraIconSize, ExtraIconSize, -1 });
|
||||
else if((this->base_icon_idx + i) == this->prev_selected_item_idx) {
|
||||
drawer->RenderTexture(this->cursor_icon, base_x - Margin, y - Margin, pu::ui::render::TextureRenderOptions::WithCustomAlphaAndDimensions(this->move_alpha, ExtraIconSize, ExtraIconSize));
|
||||
}
|
||||
}
|
||||
basex += ItemSize + Margin;
|
||||
base_x += ItemSize + Margin;
|
||||
}
|
||||
|
||||
if(leftbicon != nullptr) {
|
||||
drawer->RenderTexture(leftbicon, x - ItemSize - Margin, y, { -1, ItemSize, ItemSize, -1 });
|
||||
if(this->left_border_icon != nullptr) {
|
||||
drawer->RenderTexture(this->left_border_icon, x - ItemSize - Margin, y, pu::ui::render::TextureRenderOptions::WithCustomDimensions(ItemSize, ItemSize));
|
||||
}
|
||||
if(rightbicon != nullptr) {
|
||||
drawer->RenderTexture(rightbicon, x + ((ItemSize + Margin) * 4), y, { -1, ItemSize, ItemSize, -1 });
|
||||
if(this->right_border_icon != nullptr) {
|
||||
drawer->RenderTexture(this->right_border_icon, x + ((ItemSize + Margin) * ItemCount), y, pu::ui::render::TextureRenderOptions::WithCustomDimensions(ItemSize, ItemSize));
|
||||
}
|
||||
|
||||
if(movalpha > 0) {
|
||||
s32 tmpalpha = movalpha - 30;
|
||||
if(tmpalpha < 0) {
|
||||
tmpalpha = 0;
|
||||
if(move_alpha > 0) {
|
||||
s32 tmp_alpha = move_alpha - MoveAlphaIncrement;
|
||||
if(tmp_alpha < 0) {
|
||||
tmp_alpha = 0;
|
||||
}
|
||||
movalpha = static_cast<u8>(tmpalpha);
|
||||
move_alpha = static_cast<u8>(tmp_alpha);
|
||||
}
|
||||
}
|
||||
|
||||
void SideMenu::OnInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) {
|
||||
if(this->ricons.empty()) {
|
||||
void SideMenu::OnInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos) {
|
||||
if(this->rendered_icons.empty()) {
|
||||
return;
|
||||
}
|
||||
if(!this->enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(down & HidNpadButton_AnyLeft) {
|
||||
if(keys_down & HidNpadButton_AnyLeft) {
|
||||
HandleMoveLeft();
|
||||
}
|
||||
else if(down & HidNpadButton_AnyRight) {
|
||||
else if(keys_down & HidNpadButton_AnyRight) {
|
||||
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++) {
|
||||
auto base_x = this->GetProcessedX();
|
||||
const auto y = this->GetProcessedY();
|
||||
if(this->cursor_icon != nullptr) {
|
||||
for(u32 i = 0; i < this->rendered_icons.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)(HidNpadButton_A, this->selitm);
|
||||
if(touch_pos.HitsRegion(base_x, y, item_size, item_size)) {
|
||||
if((this->base_icon_idx + i) == this->selected_item_idx) {
|
||||
this->DoOnItemSelected(HidNpadButton_A);
|
||||
}
|
||||
else {
|
||||
preselitm = selitm;
|
||||
selitm = this->baseiconidx + i;
|
||||
movalpha = 0xFF;
|
||||
(this->onselch)(this->selitm);
|
||||
this->prev_selected_item_idx = this->selected_item_idx;
|
||||
this->selected_item_idx = this->base_icon_idx + i;
|
||||
this->move_alpha = 0xFF;
|
||||
this->DoOnSelectionChanged();
|
||||
}
|
||||
break;
|
||||
}
|
||||
basex += ItemSize + Margin;
|
||||
base_x += ItemSize + Margin;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
(this->onselect)(down, this->selitm);
|
||||
this->DoOnItemSelected(keys_down);
|
||||
}
|
||||
|
||||
if(held & HidNpadButton_AnyLeft) {
|
||||
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) {
|
||||
auto diff2 = std::chrono::duration_cast<std::chrono::milliseconds>(curtp - this->scrollmovetp).count();
|
||||
if(diff2 >= this->scrolltpvalue) {
|
||||
if(this->scrollcount >= 5) {
|
||||
this->scrollcount = 0;
|
||||
this->scrolltpvalue /= 2;
|
||||
if(keys_held & HidNpadButton_AnyLeft) {
|
||||
if(this->scroll_flag == 1) {
|
||||
const auto cur_tp = std::chrono::steady_clock::now();
|
||||
const u64 diff = std::chrono::duration_cast<std::chrono::milliseconds>(cur_tp - this->scroll_tp).count();
|
||||
if(diff >= ScrollMoveWaitTimeMs) {
|
||||
if(this->scroll_move_flag) {
|
||||
const u64 move_diff = std::chrono::duration_cast<std::chrono::milliseconds>(cur_tp - this->scroll_move_tp).count();
|
||||
if(move_diff >= this->scroll_tp_value) {
|
||||
if(this->scroll_count > ItemCount) {
|
||||
this->scroll_count = 0;
|
||||
this->scroll_tp_value /= 2;
|
||||
}
|
||||
this->scrollmoveflag = false;
|
||||
this->scroll_move_flag = false;
|
||||
this->HandleMoveLeft();
|
||||
this->scrollcount++;
|
||||
this->scroll_count++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->scrollmovetp = std::chrono::steady_clock::now();
|
||||
this->scrollmoveflag = true;
|
||||
this->scroll_move_tp = std::chrono::steady_clock::now();
|
||||
this->scroll_move_flag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->scrollflag = 1;
|
||||
this->scrolltp = std::chrono::steady_clock::now();
|
||||
this->scroll_flag = 1;
|
||||
this->scroll_tp = std::chrono::steady_clock::now();
|
||||
}
|
||||
}
|
||||
else if(held & HidNpadButton_AnyRight) {
|
||||
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) {
|
||||
auto diff2 = std::chrono::duration_cast<std::chrono::milliseconds>(curtp - this->scrollmovetp).count();
|
||||
if(diff2 >= this->scrolltpvalue) {
|
||||
if(this->scrollcount >= 5) {
|
||||
this->scrollcount = 0;
|
||||
this->scrolltpvalue /= 2;
|
||||
else if(keys_held & HidNpadButton_AnyRight) {
|
||||
if(this->scroll_flag == 2) {
|
||||
const auto cur_tp = std::chrono::steady_clock::now();
|
||||
const u64 diff = std::chrono::duration_cast<std::chrono::milliseconds>(cur_tp - this->scroll_tp).count();
|
||||
if(diff >= ScrollMoveWaitTimeMs) {
|
||||
if(this->scroll_move_flag) {
|
||||
const u64 move_diff = std::chrono::duration_cast<std::chrono::milliseconds>(cur_tp - this->scroll_move_tp).count();
|
||||
if(move_diff >= this->scroll_tp_value) {
|
||||
if(this->scroll_count > ItemCount) {
|
||||
this->scroll_count = 0;
|
||||
this->scroll_tp_value /= 2;
|
||||
}
|
||||
this->scrollmoveflag = false;
|
||||
this->scroll_move_flag = false;
|
||||
this->HandleMoveRight();
|
||||
this->scrollcount++;
|
||||
this->scroll_count++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->scrollmovetp = std::chrono::steady_clock::now();
|
||||
this->scrollmoveflag = true;
|
||||
this->scroll_move_tp = std::chrono::steady_clock::now();
|
||||
this->scroll_move_flag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->scrollflag = 2;
|
||||
this->scrolltp = std::chrono::steady_clock::now();
|
||||
this->scroll_flag = 2;
|
||||
this->scroll_tp = std::chrono::steady_clock::now();
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->scrollflag = 0;
|
||||
this->scrollcount = 0;
|
||||
this->scrolltpvalue = 50;
|
||||
this->scroll_flag = 0;
|
||||
this->scroll_count = 0;
|
||||
this->scroll_tp_value = ScrollBaseWaitTimeMs;
|
||||
}
|
||||
}
|
||||
|
||||
void SideMenu::SetOnItemSelected(std::function<void(u64, u32)> fn) {
|
||||
this->onselect = fn;
|
||||
}
|
||||
|
||||
void SideMenu::SetOnSelectionChanged(std::function<void(u32)> fn) {
|
||||
this->onselch = fn;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
this->ricons.clear();
|
||||
for(auto rtext: this->ricons_texts) {
|
||||
if(rtext != nullptr) {
|
||||
pu::ui::render::DeleteTexture(rtext);
|
||||
}
|
||||
}
|
||||
this->ricons_texts.clear();
|
||||
this->selitm = 0;
|
||||
this->baseiconidx = 0;
|
||||
this->suspitm = -1;
|
||||
this->ClearBorderIcons();
|
||||
this->ClearRenderedItems();
|
||||
|
||||
this->items_icon_paths.clear();
|
||||
this->items_icon_texts.clear();
|
||||
this->items_multiselected.clear();
|
||||
|
||||
this->selected_item_idx = 0;
|
||||
this->base_icon_idx = 0;
|
||||
this->suspended_item_idx = -1;
|
||||
}
|
||||
|
||||
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) {
|
||||
this->suspitm = Index;
|
||||
}
|
||||
|
||||
void SideMenu::UnsetSuspendedItem() {
|
||||
this->suspitm = -1;
|
||||
}
|
||||
|
||||
void SideMenu::SetSelectedItem(u32 idx) {
|
||||
if(idx < this->icons.size()) {
|
||||
this->selitm = idx;
|
||||
}
|
||||
this->items_icon_paths.push_back(icon);
|
||||
this->items_icon_texts.push_back(txt);
|
||||
this->items_multiselected.push_back(false);
|
||||
}
|
||||
|
||||
void SideMenu::HandleMoveLeft() {
|
||||
if(selitm > 0) {
|
||||
bool ilf = IsLeftFirst();
|
||||
preselitm = selitm;
|
||||
selitm--;
|
||||
if(ilf) {
|
||||
ReloadIcons(1);
|
||||
if(this->selected_item_idx > 0) {
|
||||
const auto is_left_first = IsLeftFirst();
|
||||
this->prev_selected_item_idx = this->selected_item_idx;
|
||||
this->selected_item_idx--;
|
||||
if(is_left_first) {
|
||||
MoveReloadIcons(false);
|
||||
}
|
||||
else movalpha = 0xFF;
|
||||
(this->onselch)(this->selitm);
|
||||
else {
|
||||
this->move_alpha = 0xFF;
|
||||
}
|
||||
|
||||
this->DoOnSelectionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void SideMenu::HandleMoveRight() {
|
||||
if((selitm + 1) < this->icons.size()) {
|
||||
bool irl = IsRightLast();
|
||||
preselitm = selitm;
|
||||
selitm++;
|
||||
if(irl) {
|
||||
ReloadIcons(2);
|
||||
if((selected_item_idx + 1) < this->items_icon_paths.size()) {
|
||||
const auto is_right_last = IsRightLast();
|
||||
prev_selected_item_idx = selected_item_idx;
|
||||
selected_item_idx++;
|
||||
if(is_right_last) {
|
||||
MoveReloadIcons(true);
|
||||
}
|
||||
else movalpha = 0xFF;
|
||||
(this->onselch)(this->selitm);
|
||||
}
|
||||
}
|
||||
|
||||
int SideMenu::GetSuspendedItem() {
|
||||
return this->suspitm;
|
||||
}
|
||||
|
||||
u32 SideMenu::GetSelectedItem() {
|
||||
return this->selitm;
|
||||
}
|
||||
|
||||
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;
|
||||
else {
|
||||
this->move_alpha = 0xFF;
|
||||
}
|
||||
basex += ItemSize + Margin;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SideMenu::IsRightLast()
|
||||
{
|
||||
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) {
|
||||
switch(dir)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
this->ricons_texts.insert(this->ricons_texts.begin(), ntext);
|
||||
this->baseiconidx--;
|
||||
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);
|
||||
}
|
||||
this->ricons_texts.pop_back();
|
||||
}
|
||||
break;
|
||||
}
|
||||
// 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) {
|
||||
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);
|
||||
}
|
||||
this->ricons_texts.erase(this->ricons_texts.begin());
|
||||
this->baseiconidx++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
this->UpdateBorderIcons();
|
||||
}
|
||||
|
||||
u32 SideMenu::GetBaseItemIndex() {
|
||||
return this->baseiconidx;
|
||||
}
|
||||
|
||||
void SideMenu::SetBaseItemIndex(u32 index) {
|
||||
this->baseiconidx = index;
|
||||
}
|
||||
|
||||
void SideMenu::SetBasePositions(u32 selected_idx, u32 base_idx) {
|
||||
if(selected_idx < this->icons.size()) {
|
||||
this->SetSelectedItem(selected_idx);
|
||||
this->SetBaseItemIndex(base_idx);
|
||||
this->DoOnSelectionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
this->rightbicon = nullptr;
|
||||
}
|
||||
|
||||
void SideMenu::UpdateBorderIcons()
|
||||
{
|
||||
void SideMenu::UpdateBorderIcons() {
|
||||
this->ClearBorderIcons();
|
||||
if(this->baseiconidx > 0) {
|
||||
this->leftbicon = pu::ui::render::LoadImage(this->icons[this->baseiconidx - 1]);
|
||||
|
||||
if(this->base_icon_idx > 0) {
|
||||
this->left_border_icon = pu::ui::render::LoadImage(this->items_icon_paths.at(this->base_icon_idx - 1));
|
||||
}
|
||||
if((this->baseiconidx + 4) < this->icons.size()) {
|
||||
this->rightbicon = pu::ui::render::LoadImage(this->icons[this->baseiconidx + 4]);
|
||||
if((this->base_icon_idx + ItemCount) < this->items_icon_paths.size()) {
|
||||
this->right_border_icon = pu::ui::render::LoadImage(this->items_icon_paths.at(this->base_icon_idx + ItemCount));
|
||||
}
|
||||
}
|
||||
|
||||
void SideMenu::ResetMultiselections()
|
||||
{
|
||||
this->icons_mselected.clear();
|
||||
for(u32 i = 0; i < this->icons.size(); i++) {
|
||||
this->icons_mselected.push_back(false);
|
||||
void SideMenu::ResetMultiselections() {
|
||||
this->items_multiselected.clear();
|
||||
for(u32 i = 0; i < this->items_icon_paths.size(); i++) {
|
||||
this->items_multiselected.push_back(false);
|
||||
}
|
||||
}
|
||||
|
||||
void SideMenu::SetItemMultiselected(u32 index, bool selected) {
|
||||
if(index < this->icons_mselected.size()) {
|
||||
this->icons_mselected[index] = selected;
|
||||
void SideMenu::SetItemMultiselected(const u32 idx, const bool selected) {
|
||||
if(idx < this->items_multiselected.size()) {
|
||||
this->items_multiselected.at(idx) = selected;
|
||||
}
|
||||
}
|
||||
|
||||
bool SideMenu::IsItemMultiselected(u32 index)
|
||||
{
|
||||
if(index < this->icons_mselected.size()) {
|
||||
return this->icons_mselected[index];
|
||||
bool SideMenu::IsItemMultiselected(const u32 idx) {
|
||||
if(idx < this->items_multiselected.size()) {
|
||||
return this->items_multiselected.at(idx);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SideMenu::IsAnyMultiselected()
|
||||
{
|
||||
bool any = false;
|
||||
for(auto msel: this->icons_mselected) {
|
||||
if(msel) {
|
||||
any = true;
|
||||
break;
|
||||
bool SideMenu::IsAnyMultiselected() {
|
||||
for(const auto &multiselected: this->items_multiselected) {
|
||||
if(multiselected) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return any;
|
||||
}
|
||||
|
||||
void SideMenu::SetEnabled(bool enabled) {
|
||||
this->enabled = enabled;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -11,28 +11,38 @@ extern cfg::Config g_Config;
|
|||
|
||||
namespace ui {
|
||||
|
||||
StartupLayout::StartupLayout() {
|
||||
this->SetBackgroundImage(cfg::GetAssetByTheme(g_Theme, "ui/Background.png"));
|
||||
this->loadmenu = false;
|
||||
|
||||
auto textclr = pu::ui::Color::FromHex(g_MenuApplication->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
|
||||
auto menufocusclr = pu::ui::Color::FromHex(g_MenuApplication->GetUIConfigValue<std::string>("menu_focus_color", "#5ebcffff"));
|
||||
auto menubgclr = pu::ui::Color::FromHex(g_MenuApplication->GetUIConfigValue<std::string>("menu_bg_color", "#0094ffff"));
|
||||
|
||||
this->infoText = pu::ui::elm::TextBlock::New(35, 650, GetLanguageString("startup_welcome_info"));
|
||||
this->infoText->SetColor(textclr);
|
||||
g_MenuApplication->ApplyConfigForElement("startup_menu", "info_text", this->infoText);
|
||||
this->Add(this->infoText);
|
||||
|
||||
this->usersMenu = pu::ui::elm::Menu::New(200, 60, 880, menubgclr, 100, 5);
|
||||
this->usersMenu->SetOnFocusColor(menufocusclr);
|
||||
g_MenuApplication->ApplyConfigForElement("startup_menu", "users_menu_item", this->usersMenu);
|
||||
this->Add(this->usersMenu);
|
||||
void StartupLayout::user_DefaultKey(const AccountUid uid) {
|
||||
this->load_menu = true;
|
||||
g_MenuApplication->SetSelectedUser(uid);
|
||||
}
|
||||
|
||||
void StartupLayout::OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) {
|
||||
if(this->loadmenu) {
|
||||
this->loadmenu = false;
|
||||
void StartupLayout::create_DefaultKey() {
|
||||
if(R_SUCCEEDED(pselShowUserCreator())) {
|
||||
g_TransitionGuard.Run([&]() {
|
||||
g_MenuApplication->FadeOut();
|
||||
this->ReloadMenu();
|
||||
g_MenuApplication->FadeIn();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
StartupLayout::StartupLayout() {
|
||||
this->SetBackgroundImage(cfg::GetAssetByTheme(g_Theme, "ui/Background.png"));
|
||||
this->load_menu = false;
|
||||
|
||||
this->info_text = pu::ui::elm::TextBlock::New(35, 650, GetLanguageString("startup_welcome_info"));
|
||||
this->info_text->SetColor(g_MenuApplication->GetTextColor());
|
||||
g_MenuApplication->ApplyConfigForElement("startup_menu", "info_text", this->info_text);
|
||||
this->Add(this->info_text);
|
||||
|
||||
this->users_menu = pu::ui::elm::Menu::New(200, 60, 880, g_MenuApplication->GetMenuBackgroundColor(), g_MenuApplication->GetMenuFocusColor(), 100, 5);
|
||||
g_MenuApplication->ApplyConfigForElement("startup_menu", "users_menu_item", this->users_menu);
|
||||
this->Add(this->users_menu);
|
||||
}
|
||||
|
||||
void StartupLayout::OnMenuInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos) {
|
||||
if(this->load_menu) {
|
||||
this->load_menu = false;
|
||||
g_MenuApplication->StartPlayBGM();
|
||||
g_TransitionGuard.Run([]() {
|
||||
g_MenuApplication->FadeOut();
|
||||
|
@ -47,49 +57,30 @@ namespace ui {
|
|||
return true;
|
||||
}
|
||||
|
||||
void StartupLayout::user_Click(AccountUid uid) {
|
||||
this->loadmenu = true;
|
||||
g_MenuApplication->SetSelectedUser(uid);
|
||||
}
|
||||
|
||||
void StartupLayout::create_Click() {
|
||||
auto rc = pselShowUserCreator();
|
||||
if(R_SUCCEEDED(rc)) {
|
||||
g_TransitionGuard.Run([&]() {
|
||||
g_MenuApplication->FadeOut();
|
||||
this->ReloadMenu();
|
||||
g_MenuApplication->FadeIn();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void StartupLayout::ReloadMenu() {
|
||||
this->usersMenu->ClearItems();
|
||||
this->usersMenu->SetSelectedIndex(0);
|
||||
this->users_menu->ClearItems();
|
||||
|
||||
auto textclr = pu::ui::Color::FromHex(g_MenuApplication->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
|
||||
std::vector<AccountUid> users;
|
||||
auto rc = os::QuerySystemAccounts(users, true);
|
||||
if(R_SUCCEEDED(rc)) {
|
||||
for(auto &user: users) {
|
||||
if(R_SUCCEEDED(os::QuerySystemAccounts(true, users))) {
|
||||
for(const 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);
|
||||
|
||||
this->usersMenu->AddItem(uitm);
|
||||
if(R_SUCCEEDED(os::GetAccountName(user, name))) {
|
||||
const auto path = os::GetIconCacheImagePath(user);
|
||||
auto user_item = pu::ui::elm::MenuItem::New(name);
|
||||
user_item->SetIcon(path);
|
||||
user_item->AddOnKey(std::bind(&StartupLayout::user_DefaultKey, this, user));
|
||||
user_item->SetColor(g_MenuApplication->GetTextColor());
|
||||
this->users_menu->AddItem(user_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto citm = pu::ui::elm::MenuItem::New(GetLanguageString("startup_new_user"));
|
||||
citm->SetColor(textclr);
|
||||
citm->AddOnClick(std::bind(&StartupLayout::create_Click, this));
|
||||
this->usersMenu->AddItem(citm);
|
||||
auto create_user_item = pu::ui::elm::MenuItem::New(GetLanguageString("startup_new_user"));
|
||||
create_user_item->SetColor(g_MenuApplication->GetTextColor());
|
||||
create_user_item->AddOnKey(std::bind(&StartupLayout::create_DefaultKey, this));
|
||||
this->users_menu->AddItem(create_user_item);
|
||||
|
||||
this->users_menu->SetSelectedIndex(0);
|
||||
}
|
||||
|
||||
}
|
|
@ -14,49 +14,47 @@ namespace ui {
|
|||
ThemeMenuLayout::ThemeMenuLayout() {
|
||||
this->SetBackgroundImage(cfg::GetAssetByTheme(g_Theme, "ui/Background.png"));
|
||||
|
||||
auto textclr = pu::ui::Color::FromHex(g_MenuApplication->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
|
||||
auto menufocusclr = pu::ui::Color::FromHex(g_MenuApplication->GetUIConfigValue<std::string>("menu_focus_color", "#5ebcffff"));
|
||||
auto menubgclr = pu::ui::Color::FromHex(g_MenuApplication->GetUIConfigValue<std::string>("menu_bg_color", "#0094ffff"));
|
||||
this->cur_theme_banner = pu::ui::elm::Image::New(0, 585, cfg::GetAssetByTheme(g_Theme, "ui/BannerTheme.png"));
|
||||
g_MenuApplication->ApplyConfigForElement("themes_menu", "banner_image", this->cur_theme_banner);
|
||||
this->Add(this->cur_theme_banner);
|
||||
|
||||
this->curThemeBanner = pu::ui::elm::Image::New(0, 585, cfg::GetAssetByTheme(g_Theme, "ui/BannerTheme.png"));
|
||||
g_MenuApplication->ApplyConfigForElement("themes_menu", "banner_image", this->curThemeBanner);
|
||||
this->Add(this->curThemeBanner);
|
||||
this->themes_menu = pu::ui::elm::Menu::New(200, 60, 880, g_MenuApplication->GetMenuBackgroundColor(), g_MenuApplication->GetMenuFocusColor(), 100, 5);
|
||||
g_MenuApplication->ApplyConfigForElement("themes_menu", "themes_menu_item", this->themes_menu);
|
||||
this->Add(this->themes_menu);
|
||||
|
||||
this->themesMenu = pu::ui::elm::Menu::New(200, 60, 880, menubgclr, 100, 5);
|
||||
this->themesMenu->SetOnFocusColor(menufocusclr);
|
||||
g_MenuApplication->ApplyConfigForElement("themes_menu", "themes_menu_item", this->themesMenu);
|
||||
this->Add(this->themesMenu);
|
||||
this->cur_theme_text = pu::ui::elm::TextBlock::New(20, 540, GetLanguageString("theme_current") + ":");
|
||||
this->cur_theme_text->SetFont(pu::ui::GetDefaultFont(pu::ui::DefaultFontSize::Large));
|
||||
this->cur_theme_text->SetColor(g_MenuApplication->GetTextColor());
|
||||
g_MenuApplication->ApplyConfigForElement("themes_menu", "current_theme_text", this->cur_theme_text);
|
||||
this->Add(this->cur_theme_text);
|
||||
|
||||
this->curThemeText = pu::ui::elm::TextBlock::New(20, 540, GetLanguageString("theme_current") + ":");
|
||||
this->curThemeText->SetFont("DefaultFont@30");
|
||||
this->curThemeText->SetColor(textclr);
|
||||
g_MenuApplication->ApplyConfigForElement("themes_menu", "current_theme_text", this->curThemeText);
|
||||
this->Add(this->curThemeText);
|
||||
|
||||
this->curThemeName = pu::ui::elm::TextBlock::New(40, 610, "");
|
||||
this->curThemeName->SetFont("DefaultFont@30");
|
||||
this->curThemeName->SetColor(textclr);
|
||||
g_MenuApplication->ApplyConfigForElement("themes_menu", "current_theme_name_text", this->curThemeName);
|
||||
this->Add(this->curThemeName);
|
||||
this->curThemeAuthor = pu::ui::elm::TextBlock::New(45, 650, "");
|
||||
this->curThemeAuthor->SetFont("DefaultFont@20");
|
||||
this->curThemeAuthor->SetColor(textclr);
|
||||
g_MenuApplication->ApplyConfigForElement("themes_menu", "current_theme_author_text", this->curThemeAuthor);
|
||||
this->Add(this->curThemeAuthor);
|
||||
this->curThemeVersion = pu::ui::elm::TextBlock::New(45, 675, "");
|
||||
this->curThemeVersion->SetFont("DefaultFont@30");
|
||||
this->curThemeVersion->SetColor(textclr);
|
||||
g_MenuApplication->ApplyConfigForElement("themes_menu", "current_theme_version_text", this->curThemeVersion);
|
||||
this->Add(this->curThemeVersion);
|
||||
this->curThemeIcon = pu::ui::elm::Image::New(1000, 605, "");
|
||||
this->curThemeIcon->SetWidth(100);
|
||||
this->curThemeIcon->SetHeight(100);
|
||||
g_MenuApplication->ApplyConfigForElement("themes_menu", "current_theme_icon", this->curThemeIcon);
|
||||
this->Add(this->curThemeIcon);
|
||||
this->cur_theme_name_text = pu::ui::elm::TextBlock::New(40, 610, "");
|
||||
this->cur_theme_name_text->SetFont(pu::ui::GetDefaultFont(pu::ui::DefaultFontSize::Large));
|
||||
this->cur_theme_name_text->SetColor(g_MenuApplication->GetTextColor());
|
||||
g_MenuApplication->ApplyConfigForElement("themes_menu", "current_theme_name_text", this->cur_theme_name_text);
|
||||
this->Add(this->cur_theme_name_text);
|
||||
|
||||
this->cur_theme_author_text = pu::ui::elm::TextBlock::New(45, 650, "");
|
||||
this->cur_theme_author_text->SetFont(pu::ui::GetDefaultFont(pu::ui::DefaultFontSize::Medium));
|
||||
this->cur_theme_author_text->SetColor(g_MenuApplication->GetTextColor());
|
||||
g_MenuApplication->ApplyConfigForElement("themes_menu", "current_theme_author_text", this->cur_theme_author_text);
|
||||
this->Add(this->cur_theme_author_text);
|
||||
|
||||
this->cur_theme_version_text = pu::ui::elm::TextBlock::New(45, 675, "");
|
||||
this->cur_theme_version_text->SetFont(pu::ui::GetDefaultFont(pu::ui::DefaultFontSize::Large));
|
||||
this->cur_theme_version_text->SetColor(g_MenuApplication->GetTextColor());
|
||||
g_MenuApplication->ApplyConfigForElement("themes_menu", "current_theme_version_text", this->cur_theme_version_text);
|
||||
this->Add(this->cur_theme_version_text);
|
||||
|
||||
this->cur_theme_icon = pu::ui::elm::Image::New(1000, 605, "");
|
||||
this->cur_theme_icon->SetWidth(100);
|
||||
this->cur_theme_icon->SetHeight(100);
|
||||
g_MenuApplication->ApplyConfigForElement("themes_menu", "current_theme_icon", this->cur_theme_icon);
|
||||
this->Add(this->cur_theme_icon);
|
||||
}
|
||||
|
||||
void ThemeMenuLayout::OnMenuInput(u64 down, u64 up, u64 held, pu::ui::Touch touch_pos) {
|
||||
if(down & HidNpadButton_B) {
|
||||
void ThemeMenuLayout::OnMenuInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos) {
|
||||
if(keys_down & HidNpadButton_B) {
|
||||
g_TransitionGuard.Run([]() {
|
||||
g_MenuApplication->FadeOut();
|
||||
g_MenuApplication->LoadMenu();
|
||||
|
@ -74,60 +72,59 @@ namespace ui {
|
|||
}
|
||||
|
||||
void ThemeMenuLayout::Reload() {
|
||||
auto textclr = pu::ui::Color::FromHex(g_MenuApplication->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
|
||||
if(cfg::ThemeIsDefault(g_Theme)) {
|
||||
this->curThemeText->SetText(GetLanguageString("theme_no_custom"));
|
||||
this->curThemeName->SetVisible(false);
|
||||
this->curThemeAuthor->SetVisible(false);
|
||||
this->curThemeVersion->SetVisible(false);
|
||||
this->curThemeBanner->SetVisible(false);
|
||||
this->curThemeIcon->SetVisible(false);
|
||||
if(g_Theme.IsDefault()) {
|
||||
this->cur_theme_text->SetText(GetLanguageString("theme_no_custom"));
|
||||
this->cur_theme_name_text->SetVisible(false);
|
||||
this->cur_theme_author_text->SetVisible(false);
|
||||
this->cur_theme_version_text->SetVisible(false);
|
||||
this->cur_theme_banner->SetVisible(false);
|
||||
this->cur_theme_icon->SetVisible(false);
|
||||
}
|
||||
else {
|
||||
this->curThemeText->SetText(GetLanguageString("theme_current") + ":");
|
||||
this->curThemeName->SetVisible(true);
|
||||
this->curThemeName->SetText(g_Theme.manifest.name);
|
||||
this->curThemeAuthor->SetVisible(true);
|
||||
this->curThemeAuthor->SetText(g_Theme.manifest.author);
|
||||
this->curThemeVersion->SetVisible(true);
|
||||
this->curThemeVersion->SetText("v" + g_Theme.manifest.release);
|
||||
this->curThemeBanner->SetVisible(true);
|
||||
this->curThemeIcon->SetVisible(true);
|
||||
this->curThemeIcon->SetImage(g_Theme.path + "/theme/Icon.png");
|
||||
this->curThemeIcon->SetWidth(100);
|
||||
this->curThemeIcon->SetHeight(100);
|
||||
this->cur_theme_text->SetText(GetLanguageString("theme_current") + ":");
|
||||
this->cur_theme_name_text->SetVisible(true);
|
||||
this->cur_theme_name_text->SetText(g_Theme.manifest.name);
|
||||
this->cur_theme_author_text->SetVisible(true);
|
||||
this->cur_theme_author_text->SetText(g_Theme.manifest.author);
|
||||
this->cur_theme_version_text->SetVisible(true);
|
||||
this->cur_theme_version_text->SetText("v" + g_Theme.manifest.release);
|
||||
this->cur_theme_banner->SetVisible(true);
|
||||
this->cur_theme_icon->SetVisible(true);
|
||||
this->cur_theme_icon->SetImage(g_Theme.path + "/theme/Icon.png");
|
||||
this->cur_theme_icon->SetWidth(100);
|
||||
this->cur_theme_icon->SetHeight(100);
|
||||
}
|
||||
this->themesMenu->ClearItems();
|
||||
this->themesMenu->SetSelectedIndex(0);
|
||||
|
||||
this->loadedThemes.clear();
|
||||
this->loadedThemes = cfg::LoadThemes();
|
||||
|
||||
auto ditm = pu::ui::elm::MenuItem::New(GetLanguageString("theme_reset"));
|
||||
ditm->AddOnClick(std::bind(&ThemeMenuLayout::theme_Click, this));
|
||||
ditm->SetColor(textclr);
|
||||
ditm->SetIcon("romfs:/Logo.png");
|
||||
this->themesMenu->AddItem(ditm);
|
||||
this->themes_menu->ClearItems();
|
||||
this->loaded_themes.clear();
|
||||
|
||||
for(auto <heme: this->loadedThemes) {
|
||||
auto itm = pu::ui::elm::MenuItem::New(ltheme.manifest.name + " (v" + ltheme.manifest.release + ", " + GetLanguageString("theme_by") + " " + ltheme.manifest.author + ")");
|
||||
itm->AddOnClick(std::bind(&ThemeMenuLayout::theme_Click, this));
|
||||
itm->SetColor(textclr);
|
||||
auto iconpath = ltheme.path + "/theme/Icon.png";
|
||||
itm->SetIcon(iconpath);
|
||||
this->themesMenu->AddItem(itm);
|
||||
this->loaded_themes = cfg::LoadThemes();
|
||||
|
||||
auto theme_reset_item = pu::ui::elm::MenuItem::New(GetLanguageString("theme_reset"));
|
||||
theme_reset_item->AddOnKey(std::bind(&ThemeMenuLayout::theme_DefaultKey, this));
|
||||
theme_reset_item->SetColor(g_MenuApplication->GetTextColor());
|
||||
theme_reset_item->SetIcon("romfs:/Logo.png");
|
||||
this->themes_menu->AddItem(theme_reset_item);
|
||||
|
||||
for(const auto &theme: this->loaded_themes) {
|
||||
auto theme_item = pu::ui::elm::MenuItem::New(theme.manifest.name + " (v" + theme.manifest.release + ", " + GetLanguageString("theme_by") + " " + theme.manifest.author + ")");
|
||||
theme_item->AddOnKey(std::bind(&ThemeMenuLayout::theme_DefaultKey, this));
|
||||
theme_item->SetColor(g_MenuApplication->GetTextColor());
|
||||
theme_item->SetIcon(theme.path + "/theme/Icon.png");
|
||||
this->themes_menu->AddItem(theme_item);
|
||||
}
|
||||
|
||||
this->themes_menu->SetSelectedIndex(0);
|
||||
}
|
||||
|
||||
void ThemeMenuLayout::theme_Click() {
|
||||
auto idx = this->themesMenu->GetSelectedIndex();
|
||||
void ThemeMenuLayout::theme_DefaultKey() {
|
||||
const auto idx = this->themes_menu->GetSelectedIndex();
|
||||
if(idx == 0) {
|
||||
if(cfg::ThemeIsDefault(g_Theme)) {
|
||||
if(g_Theme.IsDefault()) {
|
||||
g_MenuApplication->ShowNotification(GetLanguageString("theme_no_custom"));
|
||||
}
|
||||
else {
|
||||
auto sopt = g_MenuApplication->CreateShowDialog(GetLanguageString("theme_reset"), GetLanguageString("theme_reset_conf"), { GetLanguageString("yes"), GetLanguageString("cancel") }, true);
|
||||
if(sopt == 0) {
|
||||
const auto option = g_MenuApplication->CreateShowDialog(GetLanguageString("theme_reset"), GetLanguageString("theme_reset_conf"), { GetLanguageString("yes"), GetLanguageString("cancel") }, true);
|
||||
if(option == 0) {
|
||||
UL_ASSERT_TRUE(g_Config.SetEntry(cfg::ConfigEntryId::ActiveThemeName, std::string()));
|
||||
cfg::SaveConfig(g_Config);
|
||||
|
||||
|
@ -135,7 +132,7 @@ namespace ui {
|
|||
g_MenuApplication->CloseWithFadeOut();
|
||||
g_MenuApplication->ShowNotification(GetLanguageString("theme_changed"));
|
||||
|
||||
UL_ASSERT(dmi::menu::SendCommand(dmi::DaemonMessage::RestartMenu, [&](dmi::menu::MenuScopedStorageWriter &writer) {
|
||||
UL_RC_ASSERT(dmi::menu::SendCommand(dmi::DaemonMessage::RestartMenu, [&](dmi::menu::MenuScopedStorageWriter &writer) {
|
||||
// ...
|
||||
return ResultSuccess;
|
||||
},
|
||||
|
@ -147,23 +144,22 @@ namespace ui {
|
|||
}
|
||||
}
|
||||
else {
|
||||
idx--;
|
||||
auto seltheme = this->loadedThemes[idx];
|
||||
auto iconpath = seltheme.path + "/theme/Icon.png";
|
||||
if(seltheme.base_name == g_Theme.base_name) {
|
||||
const auto selected_theme = this->loaded_themes.at(idx - 1);
|
||||
const auto theme_icon_path = selected_theme.path + "/theme/Icon.png";
|
||||
if(selected_theme.base_name == g_Theme.base_name) {
|
||||
g_MenuApplication->ShowNotification(GetLanguageString("theme_active_this"));
|
||||
}
|
||||
else {
|
||||
auto sopt = g_MenuApplication->CreateShowDialog(GetLanguageString("theme_set"), GetLanguageString("theme_set_conf"), { GetLanguageString("yes"), GetLanguageString("cancel") }, true, iconpath);
|
||||
if(sopt == 0) {
|
||||
UL_ASSERT_TRUE(g_Config.SetEntry(cfg::ConfigEntryId::ActiveThemeName, seltheme.base_name));
|
||||
const auto option = g_MenuApplication->CreateShowDialog(GetLanguageString("theme_set"), GetLanguageString("theme_set_conf"), { GetLanguageString("yes"), GetLanguageString("cancel") }, true, theme_icon_path);
|
||||
if(option == 0) {
|
||||
UL_ASSERT_TRUE(g_Config.SetEntry(cfg::ConfigEntryId::ActiveThemeName, selected_theme.base_name));
|
||||
cfg::SaveConfig(g_Config);
|
||||
|
||||
g_MenuApplication->StopPlayBGM();
|
||||
g_MenuApplication->CloseWithFadeOut();
|
||||
g_MenuApplication->ShowNotification(GetLanguageString("theme_changed"));
|
||||
|
||||
UL_ASSERT(dmi::menu::SendCommand(dmi::DaemonMessage::RestartMenu, [&](dmi::menu::MenuScopedStorageWriter &writer) {
|
||||
UL_RC_ASSERT(dmi::menu::SendCommand(dmi::DaemonMessage::RestartMenu, [&](dmi::menu::MenuScopedStorageWriter &writer) {
|
||||
// ...
|
||||
return ResultSuccess;
|
||||
},
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace uViewer
|
|||
private Thread USBThread = null;
|
||||
|
||||
private ToolboxForm Toolbox = null;
|
||||
private static MemoryStream BaseStream = new MemoryStream((int)RawRGBAScreenBufferSize);
|
||||
private static MemoryStream BaseStream = new MemoryStream((int)PlainRgbaScreenBufferSize);
|
||||
private static USBMode Mode = USBMode.Invalid;
|
||||
|
||||
public delegate void ApplyTypeImplDelegate(PictureBox Box, byte[] Data);
|
||||
|
@ -56,12 +56,12 @@ namespace uViewer
|
|||
private static void ApplyJPEG(PictureBox Box, byte[] JPEG)
|
||||
{
|
||||
BaseStream.Position = 0;
|
||||
BaseStream.Write(JPEG, 4, (int)RawRGBAScreenBufferSize);
|
||||
BaseStream.Write(JPEG, 4, (int)PlainRgbaScreenBufferSize);
|
||||
Box.Image = Image.FromStream(BaseStream);
|
||||
}
|
||||
|
||||
public const long RawRGBAScreenBufferSize = 1280 * 720 * 4;
|
||||
public const long USBPacketSize = RawRGBAScreenBufferSize + 4;
|
||||
public const long PlainRgbaScreenBufferSize = 1280 * 720 * 4;
|
||||
public const long USBPacketSize = PlainRgbaScreenBufferSize + 4;
|
||||
|
||||
public byte[][] CaptureBlocks = new byte[][]
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue