Redesign config and default theme, many more changes and fixes

This commit is contained in:
XorTroll 2021-07-11 03:40:01 +02:00
parent f0641556a3
commit 30c6ff8ff3
40 changed files with 637 additions and 384 deletions

View file

@ -1,21 +0,0 @@
# Changelog
## uLaunch
- Updated with last libnx and Atmosphere.
- Now, instead of always overriding certain applets (if you had uLaunch on your SD), uLaunch makes use of ECS to launch its processes over a certain applet, so that the applet can be used normally when the process isn't launched.
- The internal comunication system between uLaunch's processes has changed internally and made more fast and efficient.
## USB support
- USB support is back (it was temporarily removed in 0.2.1 due to weird technical issues)
- USB now supports an alternative system, which is available under certain circumstances (having patches enabling it being on >10.0.0)
## uMenu
- When launching homebrew as applications, uMenu won't make use of the internal flog system application it used to use (which might have been risky for potential bans). Instead, making use of ECS (mentioned above), after a donor title has been selected by pressing up, homebrew can easily be launched over that application like normal Atmosphere does. Note that you won't be able to launch homebrew over an application unless you select that application as the donor application.
- HOME menu pressing is properly supported on the different menus. For instance, pressing the HOME button inside the settings menu will make it return to the main menu.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

View file

@ -1,6 +1,6 @@
<img width="200" src="LibraryAppletDaemon/RomFs/LogoLarge.png">
<img width="200" src="uMenu/RomFs/LogoLarge.png">
> Custom, open-source replacement/reimplementation for Nintendo Switch's HOME menu (qlaunch), extending it with amazing, homebrew-oriented functionality!
> uLaunch is a custom, open-source replacement/reimplementation for the Nintendo Switch's HOME menu (qlaunch), extending it with amazing, homebrew-oriented features!
<img src="Screenshots/s1.png" alt="drawing" width="400"/> <img src="Screenshots/s2.png" alt="drawing" width="400"/>
@ -8,11 +8,9 @@
<img src="Screenshots/s5.png" alt="drawing" width="400"/> <img src="Screenshots/s6.png" alt="drawing" width="400"/>
uLaunch is a very ambitious project, consisting on two custom library applets, a custom system application and a custom system applet, in order to replace the console's **HOME menu** with a custom, homebrew-oriented one.
uLaunch is a project which aims to replace the console's **HOME menu** with a custom, homebrew-oriented one.
- This isn't any kind of HOME menu extension, injection, patch, etc. uLaunch is a **complete** reimplementation, 100% open-source, which also takes over eShop and Parental control applets and flog system title (all of them are pretty much useless with this reimpl) for its extended functionality.
- The project is licensed as **GPLv2**.
- This isn't some kind of HOME menu extension, injection, patch, etc. uLaunch is a **complete** reimplementation, 100% open-source, which also takes over eShop and Parental control applets (all of them pretty much useless with this reimpl) for its extended functionality.
- For those who are interested in how the UI was done, this project is, like [Goldleaf](https://github.com/XorTroll/Goldleaf), a good example of how powerful [Plutonium libraries](https://github.com/XorTroll/Plutonium) can be in order to make beautiful UIs.
@ -20,15 +18,15 @@ uLaunch is a very ambitious project, consisting on two custom library applets, a
### Want to create **custom forwarders** (eg. RetroArch ones)? check **uViewer** tool in [latest releases](https://github.com/XorTroll/uLaunch/releases/latest)!
### Want to find **themes** for uLaunch? Check [r/uLaunchThemes subreddit](https://www.reddit.com/r/uLaunchThemes/)!
### Want to find **themes** for uLaunch? Check the [r/uLaunchThemes subreddit](https://www.reddit.com/r/uLaunchThemes/)!
### For more detailed **information** about the whole project (*themeing* too), check its [wiki](https://github.com/XorTroll/uLaunch/wiki)!
### For more detailed **information** about the whole project (and *themeing* too), check its [wiki](https://github.com/XorTroll/uLaunch/wiki)!
## Features
**List of HOME menu features uLaunch has**:
- Proper launching and foreground management: launch, suspend and close titles and applets
- Proper launching and foreground management: launch, suspend and close applications and applets
- Proper general channel handling (some of it might be not implemented): sleep, shutdown, reboot, HOME menu press detection...
@ -52,7 +50,7 @@ uLaunch is a very ambitious project, consisting on two custom library applets, a
- Launching as applets (no need of **Album**!)
- Launching as **applications** (no need of **any titles** to do so!)
- Launching as **applications** (using a **donor title**!)
- Custom basic homebrew menu
@ -82,17 +80,25 @@ uLaunch is a very ambitious project, consisting on two custom library applets, a
## Disclaimer
### Homebrew-as-application 'flog' takeover
### Ban risks
uLaunch allows you to launch homebrew as an application, taking advantage of the system's '**flog**' built-in application title, which was stubbed but not removed, thus it's content can be overriden via LayeredFS and launched.
While no bans have been reported related to using uLaunch, replacing the retail HOME menu's functionality is never a completely safe idea, so always use it at your own risk.
Since launching this title should be impossible, it might involve ban risk. uLaunch has this option **disabled by default**, so enable and use it **use it at your own risk**. Always make youre you're safe from bans (by using tools like 90DNS) before using uLaunch to avoid any possible risks.
## TODO list
- Improve Daemons's IPC with two services, one only for Daemon and the other one for any process to interact with uLaunch:
- Easy way to detect whether uLaunch is present
- Get whether a title/homebrew is opened
- (check suggestions and bugs in issues for user-submitted TODOs)
## Compiling
You will need devkitPro, devkitA64, libnx and all SDL2 libraries for switch development.
Clone (**recursively!**) this repo (uses Atmosphere-libs and Plutonium submodules) and hit `make` in root. It should build everything and generate a `titles` folder ready for use inside `SdOut`.
Clone (**recursively!**) this repo (since it uses Atmosphere-libs and Plutonium submodules) and just enter `make` in the command line. It should build everything and generate a `SdOut` folder whose contents sould directly be copied to the root of a console SD card.
In order to only build a certain subproject, you can run `make` plus the subproject's name (`make daemon`, `make hbtarget` or `make menu`).
@ -100,7 +106,7 @@ In order to only build a certain subproject, you can run `make` plus the subproj
- SciresM for [Atmosphere-libs](https://github.com/Atmosphere-NX/Atmosphere-libs).
- Switchbrew team for libnx and [nx-hbloader](https://github.com/switchbrew/nx-hbloader), the base of *QHbTarget processes (they're just simple wrappers of hbloader in the end)
- Switchbrew team for [libnx](https://github.com/switchbrew/libnx) and [nx-hbloader](https://github.com/switchbrew/nx-hbloader), the base of the uHbTarget processes (they're essentially wrappers of nx-hbloader). This project also makes use of hx-hbmenu's icon.
- C4Phoenix for the amazing design of this project's logo.
@ -108,4 +114,4 @@ In order to only build a certain subproject, you can run `make` plus the subproj
- Several scene developers for their help with small issues or features.
- Everyone from Discord or other places whose suggestions made this project a little bit better :)
- Everyone from Discord or other places whose suggestions made this project a little bit better ;)

View file

@ -1,9 +0,0 @@
# uLaunch's TODO list
- Improve Daemons's IPC with two services, one only for Daemon and the other one for any process to interact with uLaunch:
- Easy way to detect whether uLaunch is present
- Get whether a title/homebrew is opened
- (check suggestions and bugs in issues for user-submitted TODOs)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

View file

@ -135,7 +135,9 @@ namespace {
}
inline Result LaunchMenu(dmi::MenuStartMode st_mode, dmi::DaemonStatus status) {
return ecs::RegisterLaunchAsApplet(g_Config.menu_program_id, static_cast<u32>(st_mode), "/ulaunch/bin/uMenu", &status, sizeof(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));
}
void HandleHomeButton() {
@ -445,8 +447,10 @@ namespace {
}
if(strlen(g_HbTargetLaunchFlag.nro_path)) {
if(!am::LibraryAppletIsActive()) {
auto params = hb::HbTargetParams::Create(g_HbTargetLaunchFlag.nro_path, g_HbTargetLaunchFlag.nro_argv, true);
UL_ASSERT(ecs::RegisterLaunchAsApplet(g_Config.homebrew_applet_program_id, 0, "/ulaunch/bin/uHbTarget/applet", &params, sizeof(params)));
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", &params, sizeof(params)));
sth_done = true;
g_HbTargetLaunchFlag.nro_path[0] = '\0';
@ -454,7 +458,9 @@ namespace {
}
if(!am::LibraryAppletIsActive()) {
auto cur_id = am::LibraryAppletGetId();
if((cur_id == AppletId_LibraryAppletWeb) || (cur_id == AppletId_LibraryAppletPhotoViewer) || (cur_id == g_Config.homebrew_applet_program_id)) {
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));
@ -462,9 +468,9 @@ namespace {
}
}
const auto applet_active_old = g_AppletActive;
const auto prev_applet_active = g_AppletActive;
g_AppletActive = am::LibraryAppletIsActive();
if(!sth_done && !applet_active_old) {
if(!sth_done && !prev_applet_active) {
// If nothing was done, but nothing is active... An application or applet might have crashed, terminated, failed to launch...
// No matter what is it, we reopen Menu in launch-error mode.
if(!am::ApplicationIsActive() && !am::LibraryAppletIsActive()) {
@ -520,10 +526,14 @@ namespace {
fs::CreateDirectory(UL_BASE_SD_DIR "/nro");
fs::CreateDirectory(UL_BASE_SD_DIR "/lang");
g_Config = cfg::EnsureConfig();
am::LibraryAppletSetMenuAppletId(am::LibraryAppletGetAppletIdForProgramId(g_Config.menu_program_id));
g_Config = cfg::LoadConfig();
u64 menu_program_id;
UL_ASSERT_TRUE(g_Config.GetEntry(cfg::ConfigEntryId::MenuTakeoverProgramId, menu_program_id));
am::LibraryAppletSetMenuAppletId(am::LibraryAppletGetAppletIdForProgramId(menu_program_id));
if(g_Config.viewer_usb_enabled) {
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());
@ -535,7 +545,9 @@ namespace {
}
void Exit() {
if(g_Config.viewer_usb_enabled) {
bool viewer_usb_enabled;
UL_ASSERT_TRUE(g_Config.GetEntry(cfg::ConfigEntryId::ViewerUsbEnabled, viewer_usb_enabled));
if(viewer_usb_enabled) {
usbCommsExit();
if(g_UsbViewerMode == UsbMode::JPEG) {
capsscExit();

View file

@ -63,32 +63,236 @@ namespace cfg {
std::string icon_path;
};
// Take over eShop by default
static constexpr u64 DefaultMenuProgramId = 0x010000000000100B;
enum class ConfigEntryId : u8 {
MenuTakeoverProgramId,
HomebrewAppletTakeoverProgramId,
HomebrewApplicationTakeoverApplicationId,
ViewerUsbEnabled,
ActiveThemeName
};
// Take over parental controls applet by default
static constexpr u64 DefaultHomebrewAppletProgramId = 0x0100000000001001;
enum class ConfigEntryType : u8 {
Bool,
U64,
String
};
struct ConfigEntryHeader {
ConfigEntryId id;
ConfigEntryType type;
u8 size;
u8 pad;
};
struct ConfigEntry {
ConfigEntryHeader header;
bool bool_value;
u64 u64_value;
std::string str_value;
template<typename T>
bool Get(T &out_t) const {
switch(this->header.type) {
case ConfigEntryType::Bool: {
if constexpr(std::is_same_v<T, bool>) {
out_t = this->bool_value;
return true;
}
else {
return false;
}
}
case ConfigEntryType::U64: {
if constexpr(std::is_same_v<T, u64>) {
out_t = this->u64_value;
return true;
}
else {
return false;
}
}
case ConfigEntryType::String: {
if constexpr(std::is_same_v<T, std::string>) {
out_t = this->str_value;
return true;
}
else {
return false;
}
}
}
return false;
}
template<typename T>
bool Set(const T &t) {
switch(this->header.type) {
case ConfigEntryType::Bool: {
if constexpr(std::is_same_v<T, bool>) {
this->bool_value = t;
return true;
}
else {
return false;
}
}
case ConfigEntryType::U64: {
if constexpr(std::is_same_v<T, u64>) {
this->u64_value = t;
return true;
}
else {
return false;
}
}
case ConfigEntryType::String: {
if constexpr(std::is_same_v<T, std::string>) {
this->str_value = t;
this->header.size = this->str_value.length();
return true;
}
else {
return false;
}
}
}
return false;
}
};
struct ConfigHeader {
u32 magic;
u32 entry_count;
static constexpr u32 Magic = 0x47464355; // "UCFG"
};
struct Config {
std::string theme_name;
bool system_title_override_enabled;
bool viewer_usb_enabled;
u64 menu_program_id;
u64 homebrew_applet_program_id;
u64 homebrew_title_application_id;
JSON main_lang;
JSON default_lang;
Config() : system_title_override_enabled(false), viewer_usb_enabled(false), menu_program_id(DefaultMenuProgramId), homebrew_applet_program_id(DefaultHomebrewAppletProgramId), homebrew_title_application_id(0) {}
std::vector<ConfigEntry> entries;
template<typename T>
bool SetEntry(const ConfigEntryId id, const T &t) {
for(auto &entry : this->entries) {
if(entry.header.id == id) {
return entry.Set(t);
}
}
// Create new entry
ConfigEntry new_entry = {
.header = {
.id = id
}
};
switch(id) {
case ConfigEntryId::MenuTakeoverProgramId:
case ConfigEntryId::HomebrewAppletTakeoverProgramId:
case ConfigEntryId::HomebrewApplicationTakeoverApplicationId: {
if constexpr(std::is_same_v<T, u64>) {
new_entry.header.type = ConfigEntryType::U64;
new_entry.header.size = sizeof(t);
new_entry.u64_value = t;
break;
}
else {
return false;
}
}
case ConfigEntryId::ViewerUsbEnabled: {
if constexpr(std::is_same_v<T, bool>) {
new_entry.header.type = ConfigEntryType::Bool;
new_entry.header.size = sizeof(t);
new_entry.bool_value = t;
break;
}
else {
return false;
}
}
case ConfigEntryId::ActiveThemeName: {
if constexpr(std::is_same_v<T, std::string>) {
new_entry.header.type = ConfigEntryType::String;
new_entry.header.size = t.length();
new_entry.str_value = t;
break;
}
else {
return false;
}
}
}
this->entries.push_back(std::move(new_entry));
return true;
}
template<typename T>
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: {
if constexpr(std::is_same_v<T, u64>) {
// Take over eShop by default
out_t = 0x010000000000100B;
return true;
}
else {
return false;
}
}
case ConfigEntryId::HomebrewAppletTakeoverProgramId: {
if constexpr(std::is_same_v<T, u64>) {
// Take over parental control applet by default
out_t = 0x0100000000001001;
return true;
}
else {
return false;
}
}
case ConfigEntryId::HomebrewApplicationTakeoverApplicationId: {
if constexpr(std::is_same_v<T, u64>) {
// No donor title by default
out_t = 0;
return true;
}
else {
return false;
}
}
case ConfigEntryId::ViewerUsbEnabled: {
if constexpr(std::is_same_v<T, bool>) {
// Disabled by default, it might interfer with other homebrews
out_t = false;
return true;
}
else {
return false;
}
}
case ConfigEntryId::ActiveThemeName: {
if constexpr(std::is_same_v<T, std::string>) {
// Empty by default
out_t = "";
return true;
}
else {
return false;
}
}
}
return false;
}
};
static constexpr u32 CurrentThemeFormatVersion = 1;
#define CFG_THEME_DEFAULT "romfs:/default"
#define CFG_LANG_DEFAULT "romfs:/LangDefault.json"
#define CFG_CONFIG_JSON UL_BASE_SD_DIR "/config.json"
#define CFG_CONFIG_FILE UL_BASE_SD_DIR "/config.cfg"
TitleList LoadTitleList();
std::vector<TitleRecord> QueryAllHomebrew(const std::string &base = "sdmc:/switch");
@ -112,15 +316,6 @@ namespace cfg {
Config CreateNewAndLoadConfig();
Config LoadConfig();
inline Config EnsureConfig() {
if(fs::ExistsFile(CFG_CONFIG_JSON)) {
return LoadConfig();
}
else {
return CreateNewAndLoadConfig();
}
}
void SaveConfig(const Config &cfg);

View file

@ -45,7 +45,7 @@ namespace fs {
remove(path.c_str());
}
inline bool WriteFile(const std::string &path, void *data, size_t size, bool overwrite) {
inline bool WriteFile(const std::string &path, const void *data, size_t size, bool overwrite) {
auto f = fopen(path.c_str(), overwrite ? "wb" : "ab+");
if(f) {
fwrite(data, 1, size, f);

View file

@ -22,6 +22,8 @@ namespace os {
} \
})
constexpr u32 MaxTitleCount = 64000;
std::vector<cfg::TitleRecord> QueryInstalledTitles();
}

View file

@ -29,7 +29,7 @@ using JSON = nlohmann::json;
#define UL_THEMES_PATH UL_BASE_SD_DIR "/themes"
#ifndef UL_VERSION
#error uLaunch's release version isn't defined.
#error uLaunch's build version isn't defined
#endif
#include <ul_Scope.hpp>
@ -47,20 +47,19 @@ static constexpr size_t RawRGBAScreenBufferSize = 1280 * 720 * 4;
#endif
#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_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());
#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())
static constexpr Result ResultSuccess = 0;
static constexpr Mutex EmptyMutex = (Mutex)0;
constexpr Result ResultSuccess = 0;
constexpr Mutex EmptyMutex = {};
#include <ul_Results.hpp>
#define UL_ASSERTION_LOG_FILE UL_BASE_SD_DIR "/err.log"
inline __attribute__((noreturn)) void OnAssertionFailed(const char *log_buf, size_t log_buf_len, const Result rc) {
inline void NORETURN OnAssertionFailed(const char *log_buf, size_t log_buf_len, const Result rc) {
auto log_f = fopen(UL_ASSERTION_LOG_FILE, "wb"); \
if(log_f) {
fwrite(log_buf, 1, log_buf_len, log_f);
@ -70,15 +69,23 @@ inline __attribute__((noreturn)) void OnAssertionFailed(const char *log_buf, siz
fatalThrow(rc);
}
// TODO: Is this a good size?
#define UL_ASSERT_LOG_LEN 0x400
#define UL_ASSERT(expr) ({ \
const auto _tmp_rc = (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); \
} \
})
#define UL_ASSERT_TRUE(expr) ({ \
const auto _tmp_expr = (expr); \
if(!_tmp_expr) { \
char logbuf[UL_ASSERT_LOG_LEN] = {}; \
sprintf(logbuf, "%s asserted 0x%X...", #expr, _tmp_rc); \
OnAssertionFailed(logbuf, UL_ASSERT_LOG_LEN, _tmp_rc); \
const auto log_buf_len = sprintf(logbuf, "%s failed...", #expr); \
OnAssertionFailed(logbuf, log_buf_len, 0xDEAD); \
} \
})

View file

@ -6,70 +6,77 @@
namespace cfg {
static void CacheHomebrew(const std::string &nro_path) {
auto nroimg = 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(NroHeader), 1, f) == 1) {
fseek(f, hdr.size, SEEK_SET);
NroAssetHeader ahdr = {};
if(fread(&ahdr, sizeof(NroAssetHeader), 1, f) == 1) {
if(ahdr.magic == NROASSETHEADER_MAGIC) {
if((ahdr.icon.offset > 0) && (ahdr.icon.size > 0)) {
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;
namespace {
void CacheHomebrew(const std::string &nro_path) {
auto nroimg = 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;
}
}
}
}
fclose(f);
}
fclose(f);
}
}
static void CacheInstalledTitles() {
UL_OS_FOR_EACH_APP_RECORD(rec, {
NsApplicationControlData control = {};
rc = nsGetApplicationControlData(NsApplicationControlSource_Storage, rec.application_id, &control, sizeof(control), nullptr);
if(R_SUCCEEDED(rc)) {
void CacheInstalledTitles() {
UL_OS_FOR_EACH_APP_RECORD(rec, {
auto fname = cfg::GetTitleCacheIconPath(rec.application_id);
fs::WriteFile(fname, control.icon, sizeof(control.icon), true);
}
});
}
static void CacheAllHomebrew(const std::string &hb_base_path) {
UL_FS_FOR(hb_base_path, name, path, {
if(dt->d_type & DT_DIR) {
CacheAllHomebrew(path);
}
else if(util::StringEndsWith(name, ".nro")) {
CacheHomebrew(path);
}
});
}
static 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;
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);
}
}
lent = nullptr;
});
}
void CacheAllHomebrew(const std::string &hb_base_path) {
UL_FS_FOR(hb_base_path, name, path, {
if(dt->d_type & DT_DIR) {
CacheAllHomebrew(path);
}
else if(util::StringEndsWith(name, ".nro")) {
CacheHomebrew(path);
}
});
}
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;
}
}
if(lent != nullptr) {
strs.name = lent->name;
strs.author = lent->author;
strs.version = nacp->display_version;
}
}
if(lent != nullptr) {
strs.name = lent->name;
strs.author = lent->author;
strs.version = nacp->display_version;
}
}
std::vector<TitleRecord> QueryAllHomebrew(const std::string &base) {
@ -210,55 +217,94 @@ namespace cfg {
}
Config CreateNewAndLoadConfig() {
// Default constructor sets everything
Config cfg = {};
SaveConfig(cfg);
return cfg;
const Config empty_cfg = {};
SaveConfig(empty_cfg);
return empty_cfg;
}
Config LoadConfig() {
// Default constructor sets everything
Config cfg = {};
JSON cfgjson;
auto rc = util::LoadJSONFromFile(cfgjson, CFG_CONFIG_JSON);
if(R_SUCCEEDED(rc)) {
cfg.theme_name = cfgjson.value("theme_name", "");
cfg.system_title_override_enabled = cfgjson.value("system_title_override_enabled", false);
cfg.viewer_usb_enabled = cfgjson.value("viewer_usb_enabled", false);
auto menu_id_str = cfgjson.value("menu_program_id", "");
if(!menu_id_str.empty()) {
cfg.menu_program_id = util::Get64FromString(menu_id_str);
const auto cfg_file_size = fs::GetFileSize(CFG_CONFIG_FILE);
auto cfg_file_buf = new u8[cfg_file_size]();
if(fs::ReadFile(CFG_CONFIG_FILE, cfg_file_buf, cfg_file_size)) {
size_t cur_offset = 0;
const auto cfg_header = *reinterpret_cast<ConfigHeader*>(cfg_file_buf);
if(cfg_header.magic == ConfigHeader::Magic) {
cur_offset += sizeof(ConfigHeader);
if(cur_offset <= cfg_file_size) {
cfg.entries.reserve(cfg_header.entry_count);
for(u32 i = 0; i < cfg_header.entry_count; i++) {
ConfigEntry ent = {};
ent.header = *reinterpret_cast<ConfigEntryHeader*>(cfg_file_buf + cur_offset);
cur_offset += sizeof(ConfigEntryHeader);
if(cur_offset > cfg_file_size) {
break;
}
switch(ent.header.type) {
case ConfigEntryType::Bool: {
if(ent.header.size != sizeof(bool)) {
return CreateNewAndLoadConfig();
}
ent.bool_value = *reinterpret_cast<bool*>(cfg_file_buf + cur_offset);
break;
}
case ConfigEntryType::U64: {
if(ent.header.size != sizeof(u64)) {
return CreateNewAndLoadConfig();
}
ent.u64_value = *reinterpret_cast<u64*>(cfg_file_buf + cur_offset);
break;
}
case ConfigEntryType::String: {
if(ent.header.size == 0) {
ent.str_value = "";
}
else {
ent.str_value = std::string(reinterpret_cast<char*>(cfg_file_buf + cur_offset), ent.header.size);
}
break;
}
}
cur_offset += ent.header.size;
if(cur_offset > cfg_file_size) {
break;
}
cfg.entries.push_back(std::move(ent));
}
if(cur_offset <= cfg_file_size) {
return cfg;
}
}
}
auto hb_applet_id_str = cfgjson.value("homebrew_applet_program_id", "");
if(!hb_applet_id_str.empty()) {
cfg.homebrew_applet_program_id = util::Get64FromString(hb_applet_id_str);
}
auto hb_title_id_str = cfgjson.value("homebrew_title_application_id", "");
if(!hb_title_id_str.empty()) {
cfg.homebrew_title_application_id = util::Get64FromString(hb_title_id_str);
}
// Doing this saves any fields not set previously
SaveConfig(cfg);
return cfg;
}
fs::DeleteFile(CFG_CONFIG_JSON);
std::string fail = "Bad load 4";
OnAssertionFailed(fail.c_str(), fail.length(), 0xbabe);
return CreateNewAndLoadConfig();
}
void SaveConfig(const Config &cfg) {
fs::DeleteFile(CFG_CONFIG_JSON);
auto json = JSON::object();
json["theme_name"] = cfg.theme_name;
json["viewer_usb_enabled"] = cfg.viewer_usb_enabled;
json["system_title_override_enabled"] = cfg.system_title_override_enabled;
json["menu_program_id"] = util::FormatApplicationId(cfg.menu_program_id);
json["homebrew_applet_program_id"] = util::FormatApplicationId(cfg.homebrew_applet_program_id);
json["homebrew_title_application_id"] = util::FormatApplicationId(cfg.homebrew_title_application_id);
std::ofstream ofs(CFG_CONFIG_JSON);
ofs << std::setw(4) << json;
ofs.close();
const ConfigHeader cfg_header = {
.magic = ConfigHeader::Magic,
.entry_count = static_cast<u32>(cfg.entries.size())
};
fs::WriteFile(CFG_CONFIG_FILE, &cfg_header, sizeof(cfg_header), true);
for(const auto &entry : cfg.entries) {
fs::WriteFile(CFG_CONFIG_FILE, &entry.header, sizeof(entry.header), false);
switch(entry.header.type) {
case ConfigEntryType::Bool: {
fs::WriteFile(CFG_CONFIG_FILE, &entry.bool_value, sizeof(entry.bool_value), false);
break;
}
case ConfigEntryType::U64: {
fs::WriteFile(CFG_CONFIG_FILE, &entry.u64_value, sizeof(entry.u64_value), false);
break;
}
case ConfigEntryType::String: {
fs::WriteFile(CFG_CONFIG_FILE, entry.str_value.c_str(), entry.str_value.length(), false);
break;
}
}
}
}
void SaveRecord(TitleRecord &record) {

View file

@ -4,15 +4,21 @@ namespace os {
std::vector<cfg::TitleRecord> QueryInstalledTitles() {
std::vector<cfg::TitleRecord> titles;
UL_OS_FOR_EACH_APP_RECORD(record, {
cfg::TitleRecord rec = {};
rec.app_id = record.application_id;
if(rec.app_id == 0) {
auto records_buf = new NsApplicationRecord[MaxTitleCount]();
s32 record_count;
UL_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;
}
rec.title_type = static_cast<u32>(cfg::TitleType::Installed);
titles.push_back(rec);
});
const cfg::TitleRecord rec = {
.title_type = static_cast<u32>(cfg::TitleType::Installed),
.app_id = record.application_id,
};
titles.push_back(std::move(rec));
}
delete[] records_buf;
return titles;
}

View file

@ -9,6 +9,8 @@
namespace ui {
std::string GetLanguageString(const std::string &name);
enum class MenuType {
Startup,
Main,

View file

@ -35,6 +35,7 @@ namespace ui {
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;

View file

@ -3,7 +3,6 @@
"no": "No",
"ok": "Ok",
"cancel": "Cancel",
"menu_quick_info": "Tip: to open the quick menu, hold a stick (L or R-stick) and release it after selecting an option by moving it (while being held)",
"menu_multiselect": "Multiselect",
"menu_multiselect_cancel": "Multiselect was cancelled.",
"menu_rename_folder": "Rename folder",
@ -102,7 +101,6 @@
"set_viewer_info": "You must enable this if you want to use the PC screen viewer (uViewer). It is not necessary otherwise.",
"set_flog_info": "This must be enabled to be able to launch homebrew directly as applications. Note that this might involve BAN RISK.",
"startup_welcome_info": "Welcome! Please select an account to use.",
"startup_control_info": "Hold the L / R-stick or Z / R / ZL / ZR in order to open menus easily from main menu.",
"startup_login_error": "Invalid password. Please try again.",
"startup_password": "(password)",
"startup_new_user": "Create new user",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 858 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 951 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

@ -1,7 +1,7 @@
{
"suspended_final_alpha": 80,
"menu_focus_color": "#5ebcffff",
"menu_bg_color": "#0094ffff",
"menu_focus_color": "#864ea0ff",
"menu_bg_color": "#57007fff",
"menu_folder_text_x": 30,
"menu_folder_text_y": 200,
"menu_folder_text_size": 25

View file

@ -3,7 +3,6 @@
"no": "いいえ",
"ok": "Ok",
"cancel": "キャンセル",
"menu_quick_info": "ヒントクイックメニューを開くには、スティックLスティックまたはRスティックを押したまま、オプションを選択した後、押しながら動かして離します",
"menu_multiselect": "複数選択",
"menu_multiselect_cancel": "選択を解除しました。",
"menu_rename_folder": "フォルダ-の名前を変更する",
@ -97,7 +96,6 @@
"set_viewer_info": "画面をQForegroundViewerPcTOOLにストリーミングするには、このオプションを有効にする必要があります。\n使用しない場合は無効のままにしてください。",
"set_flog_info": "uLaunchは自作ソフトをアプリとして起動することができるようになります。\nこのオプションはBANのリスクがあるため、デフォルトでは無効化されています。",
"startup_welcome_info": "ようこそ!使用するアカウントを選択してください。",
"startup_control_info": "メインメニューからメニューを簡単に開くには、LまたはRスティックを押したままにします。",
"startup_login_error": "パスワードが違います。もう一度入力してください。",
"startup_password": "(パスワード)",
"startup_new_user": "新しいアカウントを作成",

View file

@ -28,11 +28,16 @@ extern "C" {
#define UL_MENU_ROMFS_BIN UL_BASE_SD_DIR "/bin/uMenu/romfs.bin"
ui::MenuApplication::Ref g_MenuApplication;
u8 *g_ScreenCaptureBuffer;
cfg::TitleList g_EntryList;
std::vector<cfg::TitleRecord> g_HomebrewRecordList;
cfg::Config g_Config;
cfg::Theme g_Theme;
u8 *g_ScreenCaptureBuffer;
JSON g_DefaultLanguage;
JSON g_MainLanguage;
namespace {
@ -48,8 +53,10 @@ namespace {
UL_ASSERT(am::InitializeDaemonMessageHandler());
// Load menu config and theme
g_Config = cfg::EnsureConfig();
g_Theme = cfg::LoadTheme(g_Config.theme_name);
g_Config = cfg::LoadConfig();
std::string theme_name;
UL_ASSERT_TRUE(g_Config.GetEntry(cfg::ConfigEntryId::ActiveThemeName, theme_name));
g_Theme = cfg::LoadTheme(theme_name);
}
void Exit() {
@ -67,74 +74,72 @@ namespace {
// uMenu procedure: read sent storages, initialize RomFs (in a special way), load config and other stuff, finally create the renderer and start the UI
int main() {
auto smode = dmi::MenuStartMode::Invalid;
UL_ASSERT(am::ReadStartMode(smode));
auto start_mode = dmi::MenuStartMode::Invalid;
UL_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)));
// TODO: better way to handle an invalid mode? maybe directly call UL_ASSERT?
if(smode != dmi::MenuStartMode::Invalid) {
// Check if our RomFs data exists...
if(!fs::ExistsFile(UL_MENU_ROMFS_BIN)) {
UL_ASSERT(RES_VALUE(Menu, RomfsBinNotFound));
}
// Try to mount it
UL_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
cfg::CacheEverything();
g_EntryList = cfg::LoadTitleList();
// Get system language and load translations (default one if not present)
u64 lcode = 0;
setGetLanguageCode(&lcode);
std::string syslang = reinterpret_cast<char*>(&lcode);
auto lpath = cfg::GetLanguageJSONPath(syslang);
UL_ASSERT(util::LoadJSONFromFile(g_Config.default_lang, CFG_LANG_DEFAULT));
g_Config.main_lang = g_Config.default_lang;
if(fs::ExistsFile(lpath)) {
auto ljson = JSON::object();
UL_ASSERT(util::LoadJSONFromFile(ljson, lpath));
g_Config.main_lang = ljson;
}
// Get the text sizes to initialize default fonts
auto uijson = JSON::object();
UL_ASSERT(util::LoadJSONFromFile(uijson, cfg::GetAssetByTheme(g_Theme, "ui/UI.json")));
auto menu_folder_txt_sz = uijson.value<u32>("menu_folder_text_size", 25);
auto renderer = pu::ui::render::Renderer::New(pu::ui::render::RendererInitOptions(SDL_INIT_EVERYTHING, pu::ui::render::RendererHardwareFlags).WithIMG(pu::ui::render::IMGAllFlags).WithMixer(pu::ui::render::MixerAllFlags).WithTTF().WithDefaultFontSize(menu_folder_txt_sz));
g_MenuApplication = ui::MenuApplication::New(renderer);
g_MenuApplication->SetInformation(smode, status, uijson);
g_MenuApplication->Prepare();
// Register handlers for HOME button press detection
am::RegisterLibAppletHomeButtonDetection();
ui::MenuApplication::RegisterHomeButtonDetection();
ui::QuickMenu::RegisterHomeButtonDetection();
if(smode == dmi::MenuStartMode::MenuApplicationSuspended) {
g_MenuApplication->Show();
}
else {
g_MenuApplication->ShowWithFadeIn();
}
// Exit RomFs manually, since we also initialized it manually
romfsExit();
delete[] g_ScreenCaptureBuffer;
Exit();
// Check if our RomFs data exists...
if(!fs::ExistsFile(UL_MENU_ROMFS_BIN)) {
UL_ASSERT(RES_VALUE(Menu, RomfsBinNotFound));
}
// Try to mount it
UL_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
cfg::CacheEverything();
g_EntryList = cfg::LoadTitleList();
// 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));
g_MainLanguage = g_DefaultLanguage;
if(fs::ExistsFile(lang_path)) {
auto lang_json = JSON::object();
UL_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);
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));
g_MenuApplication = ui::MenuApplication::New(renderer);
g_MenuApplication->SetInformation(start_mode, status, uijson);
g_MenuApplication->Prepare();
// Register handlers for HOME button press detection
am::RegisterLibAppletHomeButtonDetection();
ui::MenuApplication::RegisterHomeButtonDetection();
ui::QuickMenu::RegisterHomeButtonDetection();
if(start_mode == dmi::MenuStartMode::MenuApplicationSuspended) {
g_MenuApplication->Show();
}
else {
g_MenuApplication->ShowWithFadeIn();
}
// Exit RomFs manually, since we also initialized it manually
romfsExit();
delete[] g_ScreenCaptureBuffer;
Exit();
return 0;
}

View file

@ -13,12 +13,13 @@
#include <net/net_Service.hpp>
extern ui::MenuApplication::Ref g_MenuApplication;
extern cfg::Config g_Config;
namespace ui::actions {
void ShowAboutDialog() {
g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "ulaunch_about"), "uLaunch v" + std::string(UL_VERSION) + "\n\n" + cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "ulaunch_desc") + ":\nhttps://github.com/XorTroll/uLaunch", { cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "ok") }, true, "romfs:/LogoLarge.png");
g_MenuApplication->CreateShowDialog(GetLanguageString("ulaunch_about"), "uLaunch v" + std::string(UL_VERSION) + "\n\n" + GetLanguageString("ulaunch_desc") + ":\nhttps://github.com/XorTroll/uLaunch", { GetLanguageString("ok") }, true, "romfs:/LogoLarge.png");
}
void ShowSettingsMenu() {
@ -37,14 +38,14 @@ namespace ui::actions {
auto uid = g_MenuApplication->GetSelectedUser();
std::string name;
os::GetAccountName(name, uid);
auto sopt = g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "user_settings"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "user_selected") + ": " + name + "\n" + cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "user_option"), { cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "user_view_page"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "user_logoff"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "cancel") }, true, os::GetIconCacheImagePath(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) {
friendsLaShowMyProfileForHomeMenu(uid);
}
else if(sopt == 1) {
u32 logoff = 0;
if(g_MenuApplication->IsSuspended()) {
auto sopt = g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "suspended_app"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "user_logoff_app_suspended"), { cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "yes"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "cancel") }, true);
auto sopt = g_MenuApplication->CreateShowDialog(GetLanguageString("suspended_app"), GetLanguageString("user_logoff_app_suspended"), { GetLanguageString("yes"), GetLanguageString("cancel") }, true);
if(sopt == 0) {
logoff = 2;
}
@ -82,7 +83,7 @@ namespace ui::actions {
swkbdClose(&swkbd);
});
swkbdConfigSetGuideText(&swkbd, cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "swkbd_webpage_guide").c_str());
swkbdConfigSetGuideText(&swkbd, GetLanguageString("swkbd_webpage_guide").c_str());
char url[500] = {0};
swkbdShow(&swkbd, url, 500);
@ -102,15 +103,15 @@ namespace ui::actions {
void ShowHelpDialog() {
std::string msg;
msg += " - " + cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "help_launch") + "\n";
msg += " - " + cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "help_close") + "\n";
msg += " - " + cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "help_quick") + "\n";
msg += " - " + cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "help_multiselect") + "\n";
msg += " - " + cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "help_back") + "\n";
msg += " - " + cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "help_minus") + "\n";
msg += " - " + cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "help_plus") + "\n";
msg += " - " + GetLanguageString("help_launch") + "\n";
msg += " - " + GetLanguageString("help_close") + "\n";
msg += " - " + GetLanguageString("help_quick") + "\n";
msg += " - " + GetLanguageString("help_multiselect") + "\n";
msg += " - " + GetLanguageString("help_back") + "\n";
msg += " - " + GetLanguageString("help_minus") + "\n";
msg += " - " + GetLanguageString("help_plus") + "\n";
g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "help_title"), msg, { cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "ok") }, true);
g_MenuApplication->CreateShowDialog(GetLanguageString("help_title"), msg, { GetLanguageString("ok") }, true);
}
void ShowAlbumApplet() {
@ -130,7 +131,7 @@ namespace ui::actions {
void ShowPowerDialog() {
auto msg = os::GeneralChannelMessage::Invalid;
auto sopt = g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "power_dialog"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "power_dialog_info"), { cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "power_sleep"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "power_power_off"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "power_reboot"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "cancel") }, true);
auto sopt = g_MenuApplication->CreateShowDialog(GetLanguageString("power_dialog"), GetLanguageString("power_dialog_info"), { GetLanguageString("power_sleep"), GetLanguageString("power_power_off"), GetLanguageString("power_reboot"), GetLanguageString("cancel") }, true);
if(sopt == 0) {
msg = os::GeneralChannelMessage::Sleep;
}

View file

@ -21,7 +21,7 @@ namespace ui {
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->infoText = pu::ui::elm::TextBlock::New(0, 100, cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "lang_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);
@ -61,7 +61,7 @@ namespace ui {
for(auto &lang: os::GetLanguageNameList()) {
auto name = lang;
if(static_cast<u32>(ilang) == idx) {
name += " " + cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "lang_selected");
name += " " + GetLanguageString("lang_selected");
}
auto litm = pu::ui::elm::MenuItem::New(name);
litm->SetColor(textclr);
@ -78,10 +78,10 @@ namespace ui {
setMakeLanguage(lcode, &ilang);
if(static_cast<u32>(ilang) == idx) {
g_MenuApplication->ShowNotification(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "lang_active_this"));
g_MenuApplication->ShowNotification(GetLanguageString("lang_active_this"));
}
else {
auto sopt = g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "lang_set"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "lang_set_conf"), { cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "yes"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "no") }, true);
auto sopt = g_MenuApplication->CreateShowDialog(GetLanguageString("lang_set"), GetLanguageString("lang_set_conf"), { GetLanguageString("yes"), GetLanguageString("no") }, true);
if(sopt == 0) {
u64 codes[16] = {0};
s32 tmp;
@ -89,7 +89,7 @@ namespace ui {
auto code = codes[this->langsMenu->GetSelectedIndex()];
auto rc = setsysSetLanguageCode(code);
g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "lang_set"), R_SUCCEEDED(rc) ? cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "lang_set_ok") : cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "lang_set_error") + ": " + util::FormatResult(rc), { cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "ok") }, true);
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_MenuApplication->FadeOut();

View file

@ -1,12 +1,20 @@
#include <ui/ui_MenuApplication.hpp>
#include <util/util_Misc.hpp>
extern u8 *g_ScreenCaptureBuffer;
extern cfg::Theme g_Theme;
extern ui::MenuApplication::Ref g_MenuApplication;
extern u8 *g_ScreenCaptureBuffer;
extern cfg::Theme g_Theme;
extern JSON g_DefaultLanguage;
extern JSON g_MainLanguage;
namespace ui {
std::string GetLanguageString(const std::string &name) {
return cfg::GetLanguageString(g_MainLanguage, g_DefaultLanguage, name);
}
void UiOnHomeButtonDetection() {
switch(g_MenuApplication->GetCurrentLoadedMenu()) {
case MenuType::Startup: {

View file

@ -59,13 +59,14 @@ namespace ui {
this->Add(this->controller);
auto curtime = os::GetCurrentTime();
this->timeText = pu::ui::elm::TextBlock::New(515, 68, curtime);
this->timeText = pu::ui::elm::TextBlock::New(515, 65, curtime);
this->timeText->SetColor(textclr);
g_MenuApplication->ApplyConfigForElement("main_menu", "time_text", this->timeText);
this->Add(this->timeText);
auto lvl = os::GetBatteryLevel();
auto lvlstr = std::to_string(lvl) + "%";
this->batteryText = pu::ui::elm::TextBlock::New(700, 55, lvlstr);
this->batteryText->SetFont("DefaultFont@20");
this->batteryText->SetColor(textclr);
g_MenuApplication->ApplyConfigForElement("main_menu", "battery_text", this->batteryText);
this->Add(this->batteryText);
@ -82,11 +83,15 @@ namespace ui {
g_MenuApplication->ApplyConfigForElement("main_menu", "themes_icon", this->themes);
this->Add(this->themes);
this->fwText = pu::ui::elm::TextBlock::New(1140, 68, os::GetFirmwareVersion());
this->fwText = pu::ui::elm::TextBlock::New(1120, 65, os::GetFirmwareVersion());
this->fwText->SetColor(textclr);
g_MenuApplication->ApplyConfigForElement("main_menu", "firmware_text", this->fwText);
this->Add(this->fwText);
this->guideButtons = pu::ui::elm::Image::New(540, 120, cfg::GetAssetByTheme(g_Theme, "ui/GuideButtons.png"));
g_MenuApplication->ApplyConfigForElement("main_menu", "guide_buttons", this->guideButtons);
this->Add(this->guideButtons);
this->menuToggle = ClickableImage::New(520, 200, cfg::GetAssetByTheme(g_Theme, "ui/ToggleClick.png"));
this->menuToggle->SetOnClick(std::bind(&MenuLayout::menuToggle_Click, this));
g_MenuApplication->ApplyConfigForElement("main_menu", "menu_toggle_button", this->menuToggle);
@ -171,7 +176,7 @@ namespace ui {
auto ctp = std::chrono::steady_clock::now();
if(std::chrono::duration_cast<std::chrono::milliseconds>(ctp - this->tp).count() >= 500) {
if(g_MenuApplication->LaunchFailed() && !this->warnshown) {
g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "app_launch"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "app_unexpected_error"), { cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "ok") }, true);
g_MenuApplication->CreateShowDialog(GetLanguageString("app_launch"), GetLanguageString("app_unexpected_error"), { GetLanguageString("ok") }, true);
this->warnshown = true;
}
}
@ -259,7 +264,7 @@ namespace ui {
if((!this->homebrew_mode) && this->curfolder.empty()) {
if(index < g_EntryList.folders.size()) {
auto &folder = g_EntryList.folders[index];
auto sopt = g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "menu_multiselect"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "menu_move_existing_folder_conf"), { cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "yes"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "no"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "cancel") }, true);
auto sopt = g_MenuApplication->CreateShowDialog(GetLanguageString("menu_multiselect"), GetLanguageString("menu_move_existing_folder_conf"), { GetLanguageString("yes"), GetLanguageString("no"), GetLanguageString("cancel") }, true);
if(sopt == 0) {
this->HandleMultiselectMoveToFolder(folder.name);
}
@ -271,12 +276,12 @@ namespace ui {
}
else if(down & HidNpadButton_B) {
this->select_dir = false;
g_MenuApplication->ShowNotification(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "menu_move_select_folder_cancel"));
g_MenuApplication->ShowNotification(GetLanguageString("menu_move_select_folder_cancel"));
}
}
else {
if(down & HidNpadButton_B) {
g_MenuApplication->ShowNotification(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "menu_multiselect_cancel"));
g_MenuApplication->ShowNotification(GetLanguageString("menu_multiselect_cancel"));
this->StopMultiselect();
}
else if(down & HidNpadButton_Y) {
@ -298,7 +303,7 @@ namespace ui {
}
else if(down & HidNpadButton_A) {
if(this->homebrew_mode) {
auto sopt = g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "menu_multiselect"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "hb_mode_entries_add"), { cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "yes"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "no"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "cancel") }, true);
auto sopt = g_MenuApplication->CreateShowDialog(GetLanguageString("menu_multiselect"), GetLanguageString("hb_mode_entries_add"), { GetLanguageString("yes"), GetLanguageString("no"), GetLanguageString("cancel") }, true);
if(sopt == 0) {
// Get the idx of the last g_HomebrewRecordList element.
s32 hbidx = 0;
@ -325,7 +330,7 @@ namespace ui {
}
}
if(any) {
g_MenuApplication->ShowNotification(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "hb_mode_entries_added"));
g_MenuApplication->ShowNotification(GetLanguageString("hb_mode_entries_added"));
}
this->StopMultiselect();
}
@ -334,12 +339,12 @@ namespace ui {
}
}
else if(this->curfolder.empty()) {
auto sopt = g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "menu_multiselect"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "menu_move_to_folder"), { cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "menu_move_new_folder"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "menu_move_existing_folder"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "no"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "cancel") }, true);
auto sopt = g_MenuApplication->CreateShowDialog(GetLanguageString("menu_multiselect"), GetLanguageString("menu_move_to_folder"), { GetLanguageString("menu_move_new_folder"), GetLanguageString("menu_move_existing_folder"), GetLanguageString("no"), GetLanguageString("cancel") }, true);
if(sopt == 0) {
SwkbdConfig swkbd;
swkbdCreate(&swkbd, 0);
swkbdConfigSetGuideText(&swkbd, cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "swkbd_new_folder_guide").c_str());
char dir[500] = {0};
swkbdConfigSetGuideText(&swkbd, GetLanguageString("swkbd_new_folder_guide").c_str());
char dir[500] = {};
auto rc = swkbdShow(&swkbd, dir, 500);
swkbdClose(&swkbd);
if(R_SUCCEEDED(rc)) {
@ -348,14 +353,14 @@ namespace ui {
}
else if(sopt == 1) {
this->select_dir = true;
g_MenuApplication->ShowNotification(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "menu_move_select_folder"));
g_MenuApplication->ShowNotification(GetLanguageString("menu_move_select_folder"));
}
else if(sopt == 2) {
this->StopMultiselect();
}
}
else {
auto sopt = g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "menu_multiselect"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "menu_move_from_folder"), { cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "yes"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "no"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "cancel") }, true);
auto sopt = g_MenuApplication->CreateShowDialog(GetLanguageString("menu_multiselect"), GetLanguageString("menu_move_from_folder"), { GetLanguageString("yes"), GetLanguageString("no"), GetLanguageString("cancel") }, true);
if(sopt == 0) {
u32 rmvd = 0;
auto &folder = cfg::FindFolderByName(g_EntryList, this->curfolder);
@ -451,11 +456,11 @@ namespace ui {
this->MoveFolder(foldr.name, true);
}
else if(down & HidNpadButton_Y) {
auto sopt = g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "menu_rename_folder"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "menu_rename_folder_conf"), { cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "yes"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "no") }, true);
auto sopt = g_MenuApplication->CreateShowDialog(GetLanguageString("menu_rename_folder"), GetLanguageString("menu_rename_folder_conf"), { GetLanguageString("yes"), GetLanguageString("no") }, true);
if(sopt == 0) {
SwkbdConfig swkbd;
swkbdCreate(&swkbd, 0);
swkbdConfigSetGuideText(&swkbd, cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "swkbd_rename_folder_guide").c_str());
swkbdConfigSetGuideText(&swkbd, GetLanguageString("swkbd_rename_folder_guide").c_str());
char dir[500] = {0};
auto rc = swkbdShow(&swkbd, dir, 500);
swkbdClose(&swkbd);
@ -522,7 +527,7 @@ namespace ui {
return;
}
else {
g_MenuApplication->ShowNotification(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "app_launch_error") + ": " + util::FormatResult(rc));
g_MenuApplication->ShowNotification(GetLanguageString("app_launch_error") + ": " + util::FormatResult(rc));
}
}
}
@ -543,7 +548,7 @@ namespace ui {
}
else if(down & HidNpadButton_Y) {
if(type == cfg::TitleType::Homebrew) {
auto sopt = g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "entry_options"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "entry_action"), { cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "entry_move"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "entry_remove"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "cancel") }, true);
auto sopt = g_MenuApplication->CreateShowDialog(GetLanguageString("entry_options"), GetLanguageString("entry_action"), { GetLanguageString("entry_move"), GetLanguageString("entry_remove"), GetLanguageString("cancel") }, true);
if(sopt == 0) {
if(!this->select_on) {
this->select_on = true;
@ -551,11 +556,11 @@ namespace ui {
this->itemsMenu->SetItemMultiselected(this->itemsMenu->GetSelectedItem(), true);
}
else if(sopt == 1) {
auto sopt2 = g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "entry_remove"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "entry_remove_conf"), { cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "yes"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "no") }, true);
auto sopt2 = g_MenuApplication->CreateShowDialog(GetLanguageString("entry_remove"), GetLanguageString("entry_remove_conf"), { GetLanguageString("yes"), GetLanguageString("no") }, true);
if(sopt2 == 0) {
cfg::RemoveRecord(title);
folder.titles.erase(folder.titles.begin() + titleidx);
g_MenuApplication->ShowNotification(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "entry_remove_ok"));
g_MenuApplication->ShowNotification(GetLanguageString("entry_remove_ok"));
this->MoveFolder(this->curfolder, true);
}
}
@ -567,11 +572,11 @@ namespace ui {
}
else if(down & HidNpadButton_AnyUp) {
if(type == cfg::TitleType::Installed) {
auto sopt = g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "app_launch"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "app_take_over_select") + "\n" + cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "app_take_over_selected"), { "Yes", "Cancel" }, true);
auto sopt = g_MenuApplication->CreateShowDialog(GetLanguageString("app_launch"), GetLanguageString("app_take_over_select") + "\n" + GetLanguageString("app_take_over_selected"), { "Yes", "Cancel" }, true);
if(sopt == 0) {
g_Config.homebrew_title_application_id = title.app_id;
UL_ASSERT_TRUE(g_Config.SetEntry(cfg::ConfigEntryId::HomebrewApplicationTakeoverApplicationId, title.app_id));
cfg::SaveConfig(g_Config);
g_MenuApplication->ShowNotification(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "app_take_over_done"));
g_MenuApplication->ShowNotification(GetLanguageString("app_take_over_done"));
}
}
}
@ -590,7 +595,7 @@ namespace ui {
this->itemAuthor->SetVisible(false);
this->itemVersion->SetVisible(false);
this->bannerImage->SetImage(cfg::GetAssetByTheme(g_Theme, "ui/BannerHomebrew.png"));
this->itemName->SetText(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "hbmenu_launch"));
this->itemName->SetText(GetLanguageString("hbmenu_launch"));
}
else {
realidx--;
@ -598,14 +603,14 @@ namespace ui {
auto info = cfg::GetRecordInformation(hb);
if(info.strings.name.empty()) {
this->itemName->SetText(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "unknown"));
this->itemName->SetText(GetLanguageString("unknown"));
}
else {
this->itemName->SetText(info.strings.name);
}
if(info.strings.author.empty()) {
this->itemAuthor->SetText(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "unknown"));
this->itemAuthor->SetText(GetLanguageString("unknown"));
}
else {
this->itemAuthor->SetText(info.strings.author);
@ -632,7 +637,7 @@ namespace ui {
auto foldr = g_EntryList.folders[realidx];
this->bannerImage->SetImage(cfg::GetAssetByTheme(g_Theme, "ui/BannerFolder.png"));
auto sz = foldr.titles.size();
this->itemAuthor->SetText(std::to_string(sz) + " " + ((sz == 1) ? cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "folder_entry_single") : cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "folder_entry_mult")));
this->itemAuthor->SetText(std::to_string(sz) + " " + ((sz == 1) ? GetLanguageString("folder_entry_single") : GetLanguageString("folder_entry_mult")));
this->itemVersion->SetVisible(false);
this->itemName->SetText(foldr.name);
titleidx = -1;
@ -643,14 +648,14 @@ namespace ui {
auto info = cfg::GetRecordInformation(title);
if(info.strings.name.empty()) {
this->itemName->SetText(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "unknown"));
this->itemName->SetText(GetLanguageString("unknown"));
}
else {
this->itemName->SetText(info.strings.name);
}
if(info.strings.author.empty()) {
this->itemAuthor->SetText(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "unknown"));
this->itemAuthor->SetText(GetLanguageString("unknown"));
}
else {
this->itemAuthor->SetText(info.strings.author);
@ -703,7 +708,7 @@ namespace ui {
else {
if(name.empty()) {
// Remove empty folders
STL_REMOVE_IF(g_EntryList.folders, fldr, (fldr.titles.empty()))
STL_REMOVE_IF(g_EntryList.folders, fldr, (fldr.titles.empty()));
for(auto folder: g_EntryList.folders) {
this->itemsMenu->AddItem(cfg::GetAssetByTheme(g_Theme, "ui/Folder.png"), folder.name);
}
@ -764,14 +769,14 @@ namespace ui {
pu::audio::Play(this->sfxMenuToggle);
this->homebrew_mode = !this->homebrew_mode;
if(this->select_on) {
g_MenuApplication->ShowNotification(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "menu_multiselect_cancel"));
g_MenuApplication->ShowNotification(GetLanguageString("menu_multiselect_cancel"));
this->StopMultiselect();
}
this->MoveFolder("", true);
}
void MenuLayout::HandleCloseSuspended() {
auto sopt = g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "suspended_app"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "suspended_close"), { cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "yes"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "no") }, true);
auto sopt = g_MenuApplication->CreateShowDialog(GetLanguageString("suspended_app"), GetLanguageString("suspended_close"), { GetLanguageString("yes"), GetLanguageString("no") }, true);
if(sopt == 0) {
this->DoTerminateApplication();
}
@ -779,18 +784,15 @@ namespace ui {
void MenuLayout::HandleHomebrewLaunch(cfg::TitleRecord &rec) {
u32 launchmode = 0;
if(g_Config.system_title_override_enabled) {
auto sopt = g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "hb_launch"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "hb_launch_conf"), { cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "hb_applet"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "hb_app"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "cancel") }, true);
if(sopt == 0) {
launchmode = 1;
}
else if(sopt == 1) {
launchmode = 2;
}
}
else {
u64 title_takeover_id;
UL_ASSERT_TRUE(g_Config.GetEntry(cfg::ConfigEntryId::HomebrewApplicationTakeoverApplicationId, title_takeover_id));
auto sopt = g_MenuApplication->CreateShowDialog(GetLanguageString("hb_launch"), GetLanguageString("hb_launch_conf"), { GetLanguageString("hb_applet"), GetLanguageString("hb_app"), GetLanguageString("cancel") }, true);
if(sopt == 0) {
launchmode = 1;
}
else if(sopt == 1) {
launchmode = 2;
}
if(launchmode == 1) {
pu::audio::Play(this->sfxTitleLaunch);
hb::HbTargetParams ipt = {};
@ -815,8 +817,7 @@ namespace ui {
return;
}
else if(launchmode == 2) {
if(g_Config.homebrew_title_application_id != 0)
{
if(title_takeover_id != 0) {
bool launch = true;
if(g_MenuApplication->IsSuspended()) {
launch = false;
@ -837,7 +838,7 @@ namespace ui {
strncpy(ipt.nro_argv, rec.nro_target.nro_argv, sizeof(ipt.nro_argv) - 1);
}
auto rc = dmi::menu::SendCommand(dmi::DaemonMessage::LaunchHomebrewApplication, [&](dmi::menu::MenuScopedStorageWriter &writer) {
writer.Push(g_Config.homebrew_title_application_id);
writer.Push(title_takeover_id);
writer.Push(ipt);
return ResultSuccess;
},
@ -852,12 +853,12 @@ namespace ui {
return;
}
else {
g_MenuApplication->ShowNotification(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "app_launch_error") + ": " + util::FormatResult(rc));
g_MenuApplication->ShowNotification(GetLanguageString("app_launch_error") + ": " + util::FormatResult(rc));
}
}
}
else {
g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "app_launch"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "app_no_take_over_title") + "\n" + cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "app_take_over_title_select"), { cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "ok") }, true);
g_MenuApplication->CreateShowDialog(GetLanguageString("app_launch"), GetLanguageString("app_no_take_over_title") + "\n" + GetLanguageString("app_take_over_title_select"), { GetLanguageString("ok") }, true);
}
}
}

View file

@ -15,7 +15,7 @@ namespace ui {
template<typename T>
inline std::string EncodeForSettings(T t) {
return cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_unknown_value");
return GetLanguageString("set_unknown_value");
}
template<>
@ -30,7 +30,7 @@ namespace ui {
template<>
inline std::string EncodeForSettings<bool>(bool t) {
return t ? cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_true_value") : cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_false_value");
return t ? GetLanguageString("set_true_value") : GetLanguageString("set_false_value");
}
SettingsMenuLayout::SettingsMenuLayout() {
@ -40,7 +40,7 @@ namespace ui {
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->infoText = pu::ui::elm::TextBlock::New(0, 100, cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_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);
@ -72,53 +72,54 @@ namespace ui {
SetSysDeviceNickName console_name = {};
setsysGetDeviceNickname(&console_name);
this->PushSettingItem(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_console_nickname"), EncodeForSettings<std::string>(console_name.nickname), 0);
this->PushSettingItem(GetLanguageString("set_console_nickname"), EncodeForSettings<std::string>(console_name.nickname), 0);
TimeLocationName loc = {};
timeGetDeviceLocationName(&loc);
this->PushSettingItem(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_console_timezone"), EncodeForSettings<std::string>(loc.name), -1);
this->PushSettingItem(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_viewer_enabled"), EncodeForSettings(g_Config.viewer_usb_enabled), 1);
this->PushSettingItem(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_flog_enabled"), EncodeForSettings(g_Config.system_title_override_enabled), 2);
auto connectednet = cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_wifi_none");
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 connectednet = GetLanguageString("set_wifi_none");
if(net::HasConnection()) {
net::NetworkProfileData data = {};
net::GetCurrentNetworkProfile(&data);
connectednet = data.wifi_name;
}
this->PushSettingItem(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_wifi_name"), EncodeForSettings(connectednet), 3);
this->PushSettingItem(GetLanguageString("set_wifi_name"), EncodeForSettings(connectednet), 2);
u64 lcode = 0;
auto ilang = SetLanguage_ENUS;
setGetLanguageCode(&lcode);
setMakeLanguage(lcode, &ilang);
this->PushSettingItem(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_console_lang"), EncodeForSettings(os::GetLanguageName(ilang)), 4);
this->PushSettingItem(GetLanguageString("set_console_lang"), EncodeForSettings(os::GetLanguageName(ilang)), 3);
bool console_info_upload = false;
setsysGetConsoleInformationUploadFlag(&console_info_upload);
this->PushSettingItem(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_console_info_upload"), EncodeForSettings(console_info_upload), 5);
this->PushSettingItem(GetLanguageString("set_console_info_upload"), EncodeForSettings(console_info_upload), 4);
bool auto_titles_dl = false;
setsysGetAutomaticApplicationDownloadFlag(&auto_titles_dl);
this->PushSettingItem(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_auto_titles_dl"), EncodeForSettings(auto_titles_dl), 6);
this->PushSettingItem(GetLanguageString("set_auto_titles_dl"), EncodeForSettings(auto_titles_dl), 5);
bool auto_update = false;
setsysGetAutoUpdateEnableFlag(&auto_update);
this->PushSettingItem(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_auto_update"), EncodeForSettings(auto_update), 7);
this->PushSettingItem(GetLanguageString("set_auto_update"), EncodeForSettings(auto_update), 6);
bool wireless_lan = false;
setsysGetWirelessLanEnableFlag(&wireless_lan);
this->PushSettingItem(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_wireless_lan"), EncodeForSettings(wireless_lan), 8);
this->PushSettingItem(GetLanguageString("set_wireless_lan"), EncodeForSettings(wireless_lan), 7);
bool bluetooth = false;
setsysGetBluetoothEnableFlag(&bluetooth);
this->PushSettingItem(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_bluetooth"), EncodeForSettings(bluetooth), 9);
this->PushSettingItem(GetLanguageString("set_bluetooth"), EncodeForSettings(bluetooth), 8);
bool usb_30 = false;
setsysGetUsb30EnableFlag(&usb_30);
this->PushSettingItem(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_usb_30"), EncodeForSettings(usb_30), 10);
this->PushSettingItem(GetLanguageString("set_usb_30"), EncodeForSettings(usb_30), 9);
bool nfc = false;
setsysGetNfcEnableFlag(&nfc);
this->PushSettingItem(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_nfc"), EncodeForSettings(nfc), 11);
this->PushSettingItem(GetLanguageString("set_nfc"), EncodeForSettings(nfc), 10);
SetSysSerialNumber serial = {};
setsysGetSerialNumber(&serial);
this->PushSettingItem(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_serial_no"), EncodeForSettings<std::string>(serial.number), -1);
this->PushSettingItem(GetLanguageString("set_serial_no"), EncodeForSettings<std::string>(serial.number), -1);
u64 mac = 0;
net::GetMACAddress(&mac);
auto strmac = net::FormatMACAddress(mac);
this->PushSettingItem(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_mac_addr"), EncodeForSettings(strmac), -1);
this->PushSettingItem(GetLanguageString("set_mac_addr"), EncodeForSettings(strmac), -1);
auto ipstr = net::GetConsoleIPAddress();
// TODO: strings
this->PushSettingItem("Console IP address", EncodeForSettings(ipstr), -1);
@ -139,7 +140,7 @@ namespace ui {
case 0: {
SwkbdConfig swkbd;
swkbdCreate(&swkbd, 0);
swkbdConfigSetGuideText(&swkbd, cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "swkbd_console_nick_guide").c_str());
swkbdConfigSetGuideText(&swkbd, GetLanguageString("swkbd_console_nick_guide").c_str());
SetSysDeviceNickName console_name = {};
setsysGetDeviceNickname(&console_name);
swkbdConfigSetInitialText(&swkbd, console_name.nickname);
@ -154,23 +155,18 @@ namespace ui {
break;
}
case 1: {
auto sopt = g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_viewer_enabled"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_viewer_info") + "\n" + (g_Config.viewer_usb_enabled ? cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_disable_conf") : cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_enable_conf")), { cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "yes"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "cancel") }, true);
bool viewer_usb_enabled;
UL_ASSERT_TRUE(g_Config.GetEntry(cfg::ConfigEntryId::ViewerUsbEnabled, viewer_usb_enabled));
auto sopt = g_MenuApplication->CreateShowDialog(GetLanguageString("set_viewer_enabled"), GetLanguageString("set_viewer_info") + "\n" + (viewer_usb_enabled ? GetLanguageString("set_disable_conf") : GetLanguageString("set_enable_conf")), { GetLanguageString("yes"), GetLanguageString("cancel") }, true);
if(sopt == 0) {
g_Config.viewer_usb_enabled = !g_Config.viewer_usb_enabled;
viewer_usb_enabled = !viewer_usb_enabled;
UL_ASSERT_TRUE(g_Config.SetEntry(cfg::ConfigEntryId::ViewerUsbEnabled, viewer_usb_enabled));
reload_need = true;
g_MenuApplication->ShowNotification(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_changed_reboot"));
g_MenuApplication->ShowNotification(GetLanguageString("set_changed_reboot"));
}
break;
}
case 2: {
auto sopt = g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_flog_enabled"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_flog_info") + "\n" + (g_Config.viewer_usb_enabled ? cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_disable_conf") : cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "set_enable_conf")), { cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "yes"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "cancel") }, true);
if(sopt == 0) {
g_Config.system_title_override_enabled = !g_Config.system_title_override_enabled;
reload_need = true;
}
break;
}
case 3: {
u8 in[28] = {0};
// 0 = normal, 1 = qlaunch, 2 = starter...?
*reinterpret_cast<u32*>(in) = 1;
@ -188,14 +184,14 @@ namespace ui {
}
break;
}
case 4: {
case 3: {
g_MenuApplication->FadeOut();
g_MenuApplication->LoadSettingsLanguagesMenu();
g_MenuApplication->FadeIn();
break;
}
case 5: {
case 4: {
auto console_info_upload = false;
setsysGetConsoleInformationUploadFlag(&console_info_upload);
setsysSetConsoleInformationUploadFlag(!console_info_upload);
@ -203,7 +199,7 @@ namespace ui {
reload_need = true;
break;
}
case 6: {
case 5: {
auto auto_titles_dl = false;
setsysGetAutomaticApplicationDownloadFlag(&auto_titles_dl);
setsysSetAutomaticApplicationDownloadFlag(!auto_titles_dl);
@ -211,7 +207,7 @@ namespace ui {
reload_need = true;
break;
}
case 7: {
case 6: {
auto auto_update = false;
setsysGetAutoUpdateEnableFlag(&auto_update);
setsysSetAutoUpdateEnableFlag(!auto_update);
@ -219,7 +215,7 @@ namespace ui {
reload_need = true;
break;
}
case 8: {
case 7: {
auto wireless_lan = false;
setsysGetWirelessLanEnableFlag(&wireless_lan);
setsysSetWirelessLanEnableFlag(!wireless_lan);
@ -227,7 +223,7 @@ namespace ui {
reload_need = true;
break;
}
case 9: {
case 8: {
auto bluetooth = false;
setsysGetBluetoothEnableFlag(&bluetooth);
setsysSetBluetoothEnableFlag(!bluetooth);
@ -235,7 +231,7 @@ namespace ui {
reload_need = true;
break;
}
case 10: {
case 9: {
auto usb_30 = false;
setsysGetUsb30EnableFlag(&usb_30);
setsysSetUsb30EnableFlag(!usb_30);
@ -243,7 +239,7 @@ namespace ui {
reload_need = true;
break;
}
case 11: {
case 10: {
auto nfc = false;
setsysGetNfcEnableFlag(&nfc);
setsysSetNfcEnableFlag(!nfc);

View file

@ -18,7 +18,7 @@ namespace ui {
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, 635, cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "startup_welcome_info") + "\n" + cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "startup_control_info"));
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);
@ -36,7 +36,6 @@ namespace ui {
g_MenuApplication->FadeOut();
g_MenuApplication->LoadMenu();
g_MenuApplication->FadeIn();
g_MenuApplication->ShowNotification(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "menu_quick_info"), 3000); // Show for 3s
}
}
@ -81,7 +80,7 @@ namespace ui {
}
}
auto citm = pu::ui::elm::MenuItem::New(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "startup_new_user"));
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);

View file

@ -26,7 +26,7 @@ namespace ui {
g_MenuApplication->ApplyConfigForElement("themes_menu", "themes_menu_item", this->themesMenu);
this->Add(this->themesMenu);
this->curThemeText = pu::ui::elm::TextBlock::New(20, 540, cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "theme_current") + ":");
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);
@ -71,7 +71,7 @@ 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(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "theme_no_custom"));
this->curThemeText->SetText(GetLanguageString("theme_no_custom"));
this->curThemeName->SetVisible(false);
this->curThemeAuthor->SetVisible(false);
this->curThemeVersion->SetVisible(false);
@ -79,7 +79,7 @@ namespace ui {
this->curThemeIcon->SetVisible(false);
}
else {
this->curThemeText->SetText(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "theme_current") + ":");
this->curThemeText->SetText(GetLanguageString("theme_current") + ":");
this->curThemeName->SetVisible(true);
this->curThemeName->SetText(g_Theme.manifest.name);
this->curThemeAuthor->SetVisible(true);
@ -98,14 +98,14 @@ namespace ui {
this->loadedThemes.clear();
this->loadedThemes = cfg::LoadThemes();
auto ditm = pu::ui::elm::MenuItem::New(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "theme_reset"));
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);
for(auto &ltheme: this->loadedThemes) {
auto itm = pu::ui::elm::MenuItem::New(ltheme.manifest.name + " (v" + ltheme.manifest.release + ", " + cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "theme_by") + " " + ltheme.manifest.author + ")");
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";
@ -118,17 +118,17 @@ namespace ui {
auto idx = this->themesMenu->GetSelectedIndex();
if(idx == 0) {
if(cfg::ThemeIsDefault(g_Theme)) {
g_MenuApplication->ShowNotification(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "theme_no_custom"));
g_MenuApplication->ShowNotification(GetLanguageString("theme_no_custom"));
}
else {
auto sopt = g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "theme_reset"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "theme_reset_conf"), { cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "yes"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "cancel") }, true);
auto sopt = g_MenuApplication->CreateShowDialog(GetLanguageString("theme_reset"), GetLanguageString("theme_reset_conf"), { GetLanguageString("yes"), GetLanguageString("cancel") }, true);
if(sopt == 0) {
g_Config.theme_name = "";
UL_ASSERT_TRUE(g_Config.SetEntry(cfg::ConfigEntryId::ActiveThemeName, std::string()));
cfg::SaveConfig(g_Config);
g_MenuApplication->StopPlayBGM();
g_MenuApplication->CloseWithFadeOut();
g_MenuApplication->ShowNotification(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "theme_changed"));
g_MenuApplication->ShowNotification(GetLanguageString("theme_changed"));
UL_ASSERT(dmi::menu::SendCommand(dmi::DaemonMessage::RestartMenu, [&](dmi::menu::MenuScopedStorageWriter &writer) {
// ...
@ -146,17 +146,17 @@ namespace ui {
auto seltheme = this->loadedThemes[idx];
auto iconpath = seltheme.path + "/theme/Icon.png";
if(seltheme.base_name == g_Theme.base_name) {
g_MenuApplication->ShowNotification(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "theme_active_this"));
g_MenuApplication->ShowNotification(GetLanguageString("theme_active_this"));
}
else {
auto sopt = g_MenuApplication->CreateShowDialog(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "theme_set"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "theme_set_conf"), { cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "yes"), cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "cancel") }, true, iconpath);
auto sopt = g_MenuApplication->CreateShowDialog(GetLanguageString("theme_set"), GetLanguageString("theme_set_conf"), { GetLanguageString("yes"), GetLanguageString("cancel") }, true, iconpath);
if(sopt == 0) {
g_Config.theme_name = seltheme.base_name;
UL_ASSERT_TRUE(g_Config.SetEntry(cfg::ConfigEntryId::ActiveThemeName, seltheme.base_name));
cfg::SaveConfig(g_Config);
g_MenuApplication->StopPlayBGM();
g_MenuApplication->CloseWithFadeOut();
g_MenuApplication->ShowNotification(cfg::GetLanguageString(g_Config.main_lang, g_Config.default_lang, "theme_changed"));
g_MenuApplication->ShowNotification(GetLanguageString("theme_changed"));
UL_ASSERT(dmi::menu::SendCommand(dmi::DaemonMessage::RestartMenu, [&](dmi::menu::MenuScopedStorageWriter &writer) {
// ...