huge optimisations (see below). Build with c++26 and c23.

- build with c++26 and c23, fixes warnings due to this change.
- use #embed over romfs where applicable.
- load all configs upfront in the app menu, massively improves boot time
- enable boost mode during load/scan time in all (slow loading) menus, huge load time improvement.
- enable boost mode when exiting the app, to speed up closing all the menus and saving the config.
- reduce the size of the nro nacp when loading to just the title + author + display version.
- add option to enable boost mode for all progress bar menus, huge perf improvement again.
- remove unused launch_count var from the playlog file.
- display full path when dumping.
- optimise appstore unzip code by iterating through the zip rather than finding a specific file, reduces retroarch extract time from 52s to 26s.

overall, this commit has reduced boot time from 0.4s to 0.3s and massively increased load times of other menus.
(it also reduced the binary size by 4kb, so yay)
This commit is contained in:
ITotalJustice
2025-05-25 20:57:03 +01:00
parent 5ce23f29fa
commit f7c5ccfa87
26 changed files with 351 additions and 181 deletions

View File

Before

Width:  |  Height:  |  Size: 569 B

After

Width:  |  Height:  |  Size: 569 B

View File

Before

Width:  |  Height:  |  Size: 703 B

After

Width:  |  Height:  |  Size: 703 B

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 783 B

After

Width:  |  Height:  |  Size: 783 B

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -78,11 +78,13 @@ function(nx_create_npdm target config)
dkp_set_target_file(${outtarget} "${NX_NPDM_OUTPUT}") dkp_set_target_file(${outtarget} "${NX_NPDM_OUTPUT}")
endfunction() endfunction()
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/exefs)
nx_create_nso(hbl nx_create_nso(hbl
OUTPUT main OUTPUT exefs/main
) )
nx_create_npdm(hbl nx_create_npdm(hbl
OUTPUT main.npdm OUTPUT exefs/main.npdm
CONFIG ${CMAKE_CURRENT_SOURCE_DIR}/hbl.json CONFIG ${CMAKE_CURRENT_SOURCE_DIR}/hbl.json
) )

View File

@@ -147,8 +147,6 @@ target_compile_options(sphaira PRIVATE
-Wimplicit-fallthrough=5 -Wimplicit-fallthrough=5
-Wsuggest-final-types -Wsuggest-final-types
-Wuninitialized -Wuninitialized
-fimplicit-constexpr
-Wmissing-requires
) )
include(FetchContent) include(FetchContent)
@@ -314,9 +312,9 @@ if (NOT USE_NEW_ZSTD)
endif() endif()
set_target_properties(sphaira PROPERTIES set_target_properties(sphaira PROPERTIES
C_STANDARD 11 C_STANDARD 23
C_EXTENSIONS ON C_EXTENSIONS ON
CXX_STANDARD 23 CXX_STANDARD 26
CXX_EXTENSIONS ON CXX_EXTENSIONS ON
) )
@@ -357,12 +355,13 @@ file(COPY ${CMAKE_SOURCE_DIR}/assets/romfs DESTINATION ${CMAKE_CURRENT_BINARY_DI
# create assets target # create assets target
dkp_add_asset_target(sphaira_romfs ${CMAKE_CURRENT_BINARY_DIR}/romfs) dkp_add_asset_target(sphaira_romfs ${CMAKE_CURRENT_BINARY_DIR}/romfs)
# add hbl exefs to romfs, used for forwarders # wait until hbl is built first as we need the exefs to embed
dkp_install_assets(sphaira_romfs add_dependencies(sphaira hbl_nso hbl_npdm)
DESTINATION exefs
TARGETS # set the embed path for assets and hbl
hbl_nso target_compile_options(sphaira PRIVATE
hbl_npdm --embed-dir=${CMAKE_SOURCE_DIR}/assets/embed
--embed-dir=${CMAKE_BINARY_DIR}/hbl
) )
# add nanovg shaders to romfs # add nanovg shaders to romfs

View File

@@ -69,6 +69,7 @@ public:
static auto GetThemeIndex() -> s64; static auto GetThemeIndex() -> s64;
static auto GetDefaultImage() -> int; static auto GetDefaultImage() -> int;
static auto GetDefaultImageData() -> std::span<const u8>;
// returns argv[0] // returns argv[0]
static auto GetExePath() -> fs::FsPath; static auto GetExePath() -> fs::FsPath;
@@ -196,6 +197,7 @@ public:
static constexpr inline auto CONFIG_PATH = "/config/sphaira/config.ini"; static constexpr inline auto CONFIG_PATH = "/config/sphaira/config.ini";
static constexpr inline auto PLAYLOG_PATH = "/config/sphaira/playlog.ini"; static constexpr inline auto PLAYLOG_PATH = "/config/sphaira/playlog.ini";
static constexpr inline auto INI_SECTION = "config"; static constexpr inline auto INI_SECTION = "config";
static constexpr inline auto DEFAULT_THEME_PATH = "romfs:/themes/abyss_theme.ini";
fs::FsPath m_app_path; fs::FsPath m_app_path;
u64 m_start_timestamp{}; u64 m_start_timestamp{};
@@ -229,17 +231,18 @@ public:
option::OptionBool m_hdd_enabled{INI_SECTION, "hdd_enabled", false}; option::OptionBool m_hdd_enabled{INI_SECTION, "hdd_enabled", false};
option::OptionBool m_log_enabled{INI_SECTION, "log_enabled", false}; option::OptionBool m_log_enabled{INI_SECTION, "log_enabled", false};
option::OptionBool m_replace_hbmenu{INI_SECTION, "replace_hbmenu", false}; option::OptionBool m_replace_hbmenu{INI_SECTION, "replace_hbmenu", false};
option::OptionString m_theme_path{INI_SECTION, "theme", DEFAULT_THEME_PATH};
option::OptionBool m_theme_music{INI_SECTION, "theme_music", true}; option::OptionBool m_theme_music{INI_SECTION, "theme_music", true};
option::OptionBool m_12hour_time{INI_SECTION, "12hour_time", false}; option::OptionBool m_12hour_time{INI_SECTION, "12hour_time", false};
option::OptionLong m_language{INI_SECTION, "language", 0}; // auto option::OptionLong m_language{INI_SECTION, "language", 0}; // auto
option::OptionString m_right_side_menu{INI_SECTION, "right_side_menu", "Appstore"}; option::OptionString m_right_side_menu{INI_SECTION, "right_side_menu", "Appstore"};
option::OptionBool m_progress_boost_mode{INI_SECTION, "progress_boost_mode", false};
// install options // install options
option::OptionBool m_install_sysmmc{INI_SECTION, "install_sysmmc", false}; option::OptionBool m_install_sysmmc{INI_SECTION, "install_sysmmc", false};
option::OptionBool m_install_emummc{INI_SECTION, "install_emummc", false}; option::OptionBool m_install_emummc{INI_SECTION, "install_emummc", false};
option::OptionBool m_install_sd{INI_SECTION, "install_sd", true}; option::OptionBool m_install_sd{INI_SECTION, "install_sd", true};
option::OptionLong m_install_prompt{INI_SECTION, "install_prompt", true}; option::OptionLong m_install_prompt{INI_SECTION, "install_prompt", true};
option::OptionLong m_boost_mode{INI_SECTION, "boost_mode", false};
option::OptionBool m_allow_downgrade{INI_SECTION, "allow_downgrade", false}; option::OptionBool m_allow_downgrade{INI_SECTION, "allow_downgrade", false};
option::OptionBool m_skip_if_already_installed{INI_SECTION, "skip_if_already_installed", true}; option::OptionBool m_skip_if_already_installed{INI_SECTION, "skip_if_already_installed", true};
option::OptionBool m_ticket_only{INI_SECTION, "ticket_only", false}; option::OptionBool m_ticket_only{INI_SECTION, "ticket_only", false};

View File

@@ -11,13 +11,17 @@ namespace sphaira {
struct Hbini { struct Hbini {
u64 timestamp{}; // timestamp of last launch u64 timestamp{}; // timestamp of last launch
u32 launch_count{}; // };
struct MiniNacp {
NacpLanguageEntry lang;
char display_version[0x10];
}; };
struct NroEntry { struct NroEntry {
fs::FsPath path{}; fs::FsPath path{};
s64 size{}; s64 size{};
NacpStruct nacp{}; MiniNacp nacp{};
u64 icon_size{}; u64 icon_size{};
u64 icon_offset{}; u64 icon_offset{};
@@ -31,11 +35,11 @@ struct NroEntry {
std::optional<bool> has_star{std::nullopt}; std::optional<bool> has_star{std::nullopt};
auto GetName() const -> const char* { auto GetName() const -> const char* {
return nacp.lang[0].name; return nacp.lang.name;
} }
auto GetAuthor() const -> const char* { auto GetAuthor() const -> const char* {
return nacp.lang[0].author; return nacp.lang.author;
} }
auto GetDisplayVersion() const -> const char* { auto GetDisplayVersion() const -> const char* {

View File

@@ -17,6 +17,11 @@ struct OptionBase {
auto GetOr(const char* name) -> T; auto GetOr(const char* name) -> T;
void Set(T value); void Set(T value);
// returns true if loaded.
auto LoadFrom(const char* section, const char* name, const char* value) -> bool;
// same as above, but only checks the name.
auto LoadFrom(const char* name, const char* value) -> bool;
private: private:
auto GetInternal(const char* name) -> T; auto GetInternal(const char* name) -> T;

View File

@@ -4,7 +4,6 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "ui/progress_box.hpp" #include "ui/progress_box.hpp"
// #include <optional>
namespace sphaira { namespace sphaira {
@@ -15,12 +14,9 @@ struct OwoConfig {
std::string author{}; std::string author{};
NacpStruct nacp; NacpStruct nacp;
std::vector<u8> icon; std::vector<u8> icon;
std::vector<u8> main;
std::vector<u8> npdm;
std::vector<u8> logo; std::vector<u8> logo;
std::vector<u8> gif; std::vector<u8> gif;
// std::optional<u64> tid;
std::vector<u8> program_nca{}; std::vector<u8> program_nca{};
}; };

View File

@@ -37,7 +37,7 @@ struct Menu final : grid::Menu {
return m_entries; return m_entries;
} }
static Result InstallHomebrew(const fs::FsPath& path, const NacpStruct& nacp, const std::vector<u8>& icon); static Result InstallHomebrew(const fs::FsPath& path, const std::vector<u8>& icon);
static Result InstallHomebrewFromPath(const fs::FsPath& path); static Result InstallHomebrewFromPath(const fs::FsPath& path);
private: private:

View File

@@ -70,10 +70,6 @@ enum : Result {
struct Config { struct Config {
bool sd_card_install{}; bool sd_card_install{};
// sets the performance mode to FastLoad which boosts the CPU clock
// and lowers the GPU clock.
bool boost_mode{};
// enables downgrading patch / data patch (dlc) version. // enables downgrading patch / data patch (dlc) version.
bool allow_downgrade{}; bool allow_downgrade{};

View File

@@ -25,6 +25,7 @@
#include <pulsar.h> #include <pulsar.h>
#include <haze.h> #include <haze.h>
#include <algorithm> #include <algorithm>
#include <ranges>
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
#include <ctime> #include <ctime>
@@ -43,6 +44,10 @@ constexpr fs::FsPath DEFAULT_MUSIC_PATH = "/config/sphaira/themes/default_music.
constexpr const char* DEFAULT_MUSIC_URL = "https://files.catbox.moe/1ovji1.bfstm"; constexpr const char* DEFAULT_MUSIC_URL = "https://files.catbox.moe/1ovji1.bfstm";
// constexpr const char* DEFAULT_MUSIC_URL = "https://raw.githubusercontent.com/ITotalJustice/sphaira/refs/heads/master/assets/default_music.bfstm"; // constexpr const char* DEFAULT_MUSIC_URL = "https://raw.githubusercontent.com/ITotalJustice/sphaira/refs/heads/master/assets/default_music.bfstm";
constexpr const u8 DEFAULT_IMAGE_DATA[]{
#embed <icons/default.png>
};
void download_default_music() { void download_default_music() {
App::Push(std::make_shared<ui::ProgressBox>(0, "Downloading "_i18n, "default_music.bfstm", [](auto pbox){ App::Push(std::make_shared<ui::ProgressBox>(0, "Downloading "_i18n, "default_music.bfstm", [](auto pbox){
const auto result = curl::Api().ToFile( const auto result = curl::Api().ToFile(
@@ -444,10 +449,7 @@ void App::Loop() {
// update timestamp // update timestamp
ini_putl(nro_path.c_str(), "timestamp", timestamp, App::PLAYLOG_PATH); ini_putl(nro_path.c_str(), "timestamp", timestamp, App::PLAYLOG_PATH);
// update launch_count log_write("updating timestamp for: %s %lu\n", nro_path.c_str(), timestamp);
const long old_launch_count = ini_getl(nro_path.c_str(), "launch_count", 0, App::PLAYLOG_PATH);
ini_putl(nro_path.c_str(), "launch_count", old_launch_count + 1, App::PLAYLOG_PATH);
log_write("updating timestamp and launch count for: %s %lu %ld\n", nro_path.c_str(), timestamp, old_launch_count + 1);
// force disable pop-back to main menu. // force disable pop-back to main menu.
__nx_applet_exit_mode = 0; __nx_applet_exit_mode = 0;
@@ -516,11 +518,11 @@ auto App::Push(std::shared_ptr<ui::Widget> widget) -> void {
} }
auto App::PopToMenu() -> void { auto App::PopToMenu() -> void {
for (auto it = g_app->m_widgets.rbegin(); it != g_app->m_widgets.rend(); it++) { for (auto& p : std::ranges::views::reverse(g_app->m_widgets)) {
const auto& p = *it;
if (p->IsMenu()) { if (p->IsMenu()) {
break; break;
} }
p->SetPop(); p->SetPop();
} }
} }
@@ -595,6 +597,10 @@ auto App::GetDefaultImage() -> int {
return g_app->m_default_image; return g_app->m_default_image;
} }
auto App::GetDefaultImageData() -> std::span<const u8> {
return DEFAULT_IMAGE_DATA;
}
auto App::GetExePath() -> fs::FsPath { auto App::GetExePath() -> fs::FsPath {
return g_app->m_app_path; return g_app->m_app_path;
} }
@@ -868,18 +874,7 @@ void App::SetTextScrollSpeed(long index) {
} }
auto App::Install(OwoConfig& config) -> Result { auto App::Install(OwoConfig& config) -> Result {
R_TRY(romfsInit());
ON_SCOPE_EXIT(romfsExit());
std::vector<u8> main_data, npdm_data, logo_data, gif_data;
R_TRY(fs::read_entire_file("romfs:/exefs/main", main_data));
R_TRY(fs::read_entire_file("romfs:/exefs/main.npdm", npdm_data));
config.nro_path = nro_add_arg_file(config.nro_path); config.nro_path = nro_add_arg_file(config.nro_path);
config.main = main_data;
config.npdm = npdm_data;
config.logo = logo_data;
config.gif = gif_data;
if (!config.icon.empty()) { if (!config.icon.empty()) {
config.icon = GetNroIcon(config.icon); config.icon = GetNroIcon(config.icon);
} }
@@ -898,18 +893,7 @@ auto App::Install(OwoConfig& config) -> Result {
} }
auto App::Install(ui::ProgressBox* pbox, OwoConfig& config) -> Result { auto App::Install(ui::ProgressBox* pbox, OwoConfig& config) -> Result {
R_TRY(romfsInit());
ON_SCOPE_EXIT(romfsExit());
std::vector<u8> main_data, npdm_data, logo_data, gif_data;
R_TRY(fs::read_entire_file("romfs:/exefs/main", main_data));
R_TRY(fs::read_entire_file("romfs:/exefs/main.npdm", npdm_data));
config.nro_path = nro_add_arg_file(config.nro_path); config.nro_path = nro_add_arg_file(config.nro_path);
config.main = main_data;
config.npdm = npdm_data;
config.logo = logo_data;
config.gif = gif_data;
if (!config.icon.empty()) { if (!config.icon.empty()) {
config.icon = GetNroIcon(config.icon); config.icon = GetNroIcon(config.icon);
} }
@@ -1268,6 +1252,9 @@ void App::ScanThemeEntries() {
App::App(const char* argv0) { App::App(const char* argv0) {
TimeStamp ts; TimeStamp ts;
appletSetCpuBoostMode(ApmCpuBoostMode_FastLoad);
ON_SCOPE_EXIT(appletSetCpuBoostMode(ApmCpuBoostMode_Normal));
g_app = this; g_app = this;
m_start_timestamp = armGetSystemTick(); m_start_timestamp = armGetSystemTick();
if (!std::strncmp(argv0, "sdmc:/", 6)) { if (!std::strncmp(argv0, "sdmc:/", 6)) {
@@ -1283,10 +1270,55 @@ App::App(const char* argv0) {
} }
fs::FsNativeSd fs; fs::FsNativeSd fs;
fs.CreateDirectoryRecursively("/config/sphaira/assoc"); fs.CreateDirectoryRecursively("/config/sphaira");
fs.CreateDirectoryRecursively("/config/sphaira/themes"); fs.CreateDirectory("/config/sphaira/assoc");
fs.CreateDirectoryRecursively("/config/sphaira/github"); fs.CreateDirectory("/config/sphaira/themes");
fs.CreateDirectoryRecursively("/config/sphaira/i18n"); fs.CreateDirectory("/config/sphaira/github");
fs.CreateDirectory("/config/sphaira/i18n");
auto cb = [](const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, void *UserData) -> int {
auto app = static_cast<App*>(UserData);
if (!std::strcmp(Section, INI_SECTION)) {
if (app->m_nxlink_enabled.LoadFrom(Key, Value)) {}
else if (app->m_mtp_enabled.LoadFrom(Key, Value)) {}
else if (app->m_ftp_enabled.LoadFrom(Key, Value)) {}
else if (app->m_hdd_enabled.LoadFrom(Key, Value)) {}
else if (app->m_log_enabled.LoadFrom(Key, Value)) {}
else if (app->m_replace_hbmenu.LoadFrom(Key, Value)) {}
else if (app->m_theme_path.LoadFrom(Key, Value)) {}
else if (app->m_theme_music.LoadFrom(Key, Value)) {}
else if (app->m_12hour_time.LoadFrom(Key, Value)) {}
else if (app->m_language.LoadFrom(Key, Value)) {}
else if (app->m_right_side_menu.LoadFrom(Key, Value)) {}
else if (app->m_install_sysmmc.LoadFrom(Key, Value)) {}
else if (app->m_install_emummc.LoadFrom(Key, Value)) {}
else if (app->m_install_sd.LoadFrom(Key, Value)) {}
else if (app->m_install_prompt.LoadFrom(Key, Value)) {}
else if (app->m_progress_boost_mode.LoadFrom(Key, Value)) {}
else if (app->m_allow_downgrade.LoadFrom(Key, Value)) {}
else if (app->m_skip_if_already_installed.LoadFrom(Key, Value)) {}
else if (app->m_ticket_only.LoadFrom(Key, Value)) {}
else if (app->m_skip_base.LoadFrom(Key, Value)) {}
else if (app->m_skip_patch.LoadFrom(Key, Value)) {}
else if (app->m_skip_addon.LoadFrom(Key, Value)) {}
else if (app->m_skip_data_patch.LoadFrom(Key, Value)) {}
else if (app->m_skip_ticket.LoadFrom(Key, Value)) {}
else if (app->m_skip_nca_hash_verify.LoadFrom(Key, Value)) {}
else if (app->m_skip_rsa_header_fixed_key_verify.LoadFrom(Key, Value)) {}
else if (app->m_skip_rsa_npdm_fixed_key_verify.LoadFrom(Key, Value)) {}
else if (app->m_ignore_distribution_bit.LoadFrom(Key, Value)) {}
else if (app->m_convert_to_standard_crypto.LoadFrom(Key, Value)) {}
else if (app->m_lower_master_key.LoadFrom(Key, Value)) {}
else if (app->m_lower_system_version.LoadFrom(Key, Value)) {}
}
return 1;
};
// load all configs ahead of time, as this is actually faster than
// loading each config one by one as it avoids re-opening the file multiple times.
ini_browse(cb, this, CONFIG_PATH);
if (App::GetLogEnable()) { if (App::GetLogEnable()) {
log_file_init(); log_file_init();
@@ -1409,17 +1441,14 @@ App::App(const char* argv0) {
ScanThemeEntries(); ScanThemeEntries();
fs::FsPath theme_path{};
constexpr fs::FsPath default_theme_path{"romfs:/themes/abyss_theme.ini"};
ini_gets("config", "theme", default_theme_path, theme_path, sizeof(theme_path), CONFIG_PATH);
// try and load previous theme, default to previous version otherwise. // try and load previous theme, default to previous version otherwise.
fs::FsPath theme_path = m_theme_path.Get();
ThemeMeta theme_meta; ThemeMeta theme_meta;
if (R_SUCCEEDED(romfsInit())) { if (R_SUCCEEDED(romfsInit())) {
ON_SCOPE_EXIT(romfsExit()); ON_SCOPE_EXIT(romfsExit());
if (!LoadThemeMeta(theme_path, theme_meta)) { if (!LoadThemeMeta(theme_path, theme_meta)) {
log_write("failed to load meta using default\n"); log_write("failed to load meta using default\n");
theme_path = default_theme_path; theme_path = DEFAULT_THEME_PATH;
LoadThemeMeta(theme_path, theme_meta); LoadThemeMeta(theme_path, theme_meta);
} }
} }
@@ -1456,20 +1485,12 @@ App::App(const char* argv0) {
} }
ini_putl(GetExePath(), "timestamp", m_start_timestamp, App::PLAYLOG_PATH); ini_putl(GetExePath(), "timestamp", m_start_timestamp, App::PLAYLOG_PATH);
const long old_launch_count = ini_getl(GetExePath(), "launch_count", 0, App::PLAYLOG_PATH);
ini_putl(GetExePath(), "launch_count", old_launch_count + 1, App::PLAYLOG_PATH);
// load default image // load default image
if (R_SUCCEEDED(romfsInit())) { m_default_image = nvgCreateImageMem(vg, 0, DEFAULT_IMAGE_DATA, std::size(DEFAULT_IMAGE_DATA));
ON_SCOPE_EXIT(romfsExit());
const auto image = ImageLoadFromFile("romfs:/default.png");
if (!image.data.empty()) {
m_default_image = nvgCreateImageRGBA(vg, image.w, image.h, 0, image.data.data());
}
}
App::Push(std::make_shared<ui::menu::main::MainMenu>()); App::Push(std::make_shared<ui::menu::main::MainMenu>());
log_write("finished app constructor, time taken: %.2fs %zums\n", ts.GetSecondsD(), ts.GetMs()); log_write("\n\tfinished app constructor, time taken: %.2fs %zums\n\n", ts.GetSecondsD(), ts.GetMs());
} }
void App::PlaySoundEffect(SoundEffect effect) { void App::PlaySoundEffect(SoundEffect effect) {
@@ -1588,6 +1609,10 @@ void App::DisplayAdvancedOptions(bool left_side) {
App::SetReplaceHbmenuEnable(enable); App::SetReplaceHbmenuEnable(enable);
})); }));
options->Add(std::make_shared<ui::SidebarEntryBool>("Boost CPU during transfer"_i18n, App::GetApp()->m_progress_boost_mode.Get(), [](bool& enable){
App::GetApp()->m_progress_boost_mode.Set(enable);
}));
options->Add(std::make_shared<ui::SidebarEntryArray>("Text scroll speed"_i18n, text_scroll_speed_items, [](s64& index_out){ options->Add(std::make_shared<ui::SidebarEntryArray>("Text scroll speed"_i18n, text_scroll_speed_items, [](s64& index_out){
App::SetTextScrollSpeed(index_out); App::SetTextScrollSpeed(index_out);
}, App::GetTextScrollSpeed())); }, App::GetTextScrollSpeed()));
@@ -1637,10 +1662,6 @@ void App::DisplayInstallOptions(bool left_side) {
App::SetInstallSdEnable(index_out); App::SetInstallSdEnable(index_out);
}, (s64)App::GetInstallSdEnable())); }, (s64)App::GetInstallSdEnable()));
options->Add(std::make_shared<ui::SidebarEntryBool>("Boost CPU clock"_i18n, App::GetApp()->m_boost_mode.Get(), [](bool& enable){
App::GetApp()->m_boost_mode.Set(enable);
}));
options->Add(std::make_shared<ui::SidebarEntryBool>("Allow downgrade"_i18n, App::GetApp()->m_allow_downgrade.Get(), [](bool& enable){ options->Add(std::make_shared<ui::SidebarEntryBool>("Allow downgrade"_i18n, App::GetApp()->m_allow_downgrade.Get(), [](bool& enable){
App::GetApp()->m_allow_downgrade.Set(enable); App::GetApp()->m_allow_downgrade.Set(enable);
})); }));
@@ -1724,6 +1745,9 @@ void App::DisplayDumpOptions(bool left_side) {
} }
App::~App() { App::~App() {
appletSetCpuBoostMode(ApmCpuBoostMode_FastLoad);
ON_SCOPE_EXIT(appletSetCpuBoostMode(ApmCpuBoostMode_Normal));
log_write("starting to exit\n"); log_write("starting to exit\n");
i18n::exit(); i18n::exit();

View File

@@ -106,13 +106,14 @@ private:
Result DumpToFile(ui::ProgressBox* pbox, fs::Fs* fs, const fs::FsPath& root, BaseSource* source, std::span<const fs::FsPath> paths) { Result DumpToFile(ui::ProgressBox* pbox, fs::Fs* fs, const fs::FsPath& root, BaseSource* source, std::span<const fs::FsPath> paths) {
constexpr s64 BIG_FILE_SIZE = 1024ULL*1024ULL*1024ULL*4ULL; constexpr s64 BIG_FILE_SIZE = 1024ULL*1024ULL*1024ULL*4ULL;
for (auto path : paths) { for (const auto& path : paths) {
const auto base_path = fs::AppendPath(root, path);
const auto file_size = source->GetSize(path); const auto file_size = source->GetSize(path);
pbox->SetImage(source->GetIcon(path)); pbox->SetImage(source->GetIcon(path));
pbox->SetTitle(source->GetName(path)); pbox->SetTitle(source->GetName(path));
pbox->NewTransfer(path); pbox->NewTransfer(base_path);
const auto temp_path = fs::AppendPath(root, path + ".temp"); const auto temp_path = base_path + ".temp";
fs->CreateDirectoryRecursivelyWithPath(temp_path); fs->CreateDirectoryRecursivelyWithPath(temp_path);
fs->DeleteFile(temp_path); fs->DeleteFile(temp_path);
@@ -135,9 +136,8 @@ Result DumpToFile(ui::ProgressBox* pbox, fs::Fs* fs, const fs::FsPath& root, Bas
)); ));
} }
path = fs::AppendPath(root, path); fs->DeleteFile(base_path);
fs->DeleteFile(path); R_TRY(fs->RenameFile(temp_path, base_path));
R_TRY(fs->RenameFile(temp_path, path));
} }
R_SUCCEED(); R_SUCCEED();

View File

@@ -56,21 +56,21 @@ auto nro_parse_internal(fs::FsNative& fs, const fs::FsPath& path, NroEntry& entr
// R_UNLESS(asset.magic == NROASSETHEADER_MAGIC, NroError_BadMagic); // R_UNLESS(asset.magic == NROASSETHEADER_MAGIC, NroError_BadMagic);
// some .nro (vgedit) have bad nacp, fake the nacp // some .nro (vgedit) have bad nacp, fake the nacp
if (asset.magic != NROASSETHEADER_MAGIC || asset.nacp.offset == 0 || asset.nacp.size != sizeof(entry.nacp)) { auto& nacp = entry.nacp;
if (asset.magic != NROASSETHEADER_MAGIC || asset.nacp.offset == 0 || asset.nacp.size != sizeof(NacpStruct)) {
std::memset(&asset, 0, sizeof(asset)); std::memset(&asset, 0, sizeof(asset));
std::memset(&entry.nacp, 0, sizeof(entry.nacp)); std::memset(&nacp, 0, sizeof(nacp));
// get the name without the .nro // get the name without the .nro
const auto file_name = std::strrchr(path, '/') + 1; const auto file_name = std::strrchr(path, '/') + 1;
const auto file_name_len = std::strlen(file_name); const auto file_name_len = std::strlen(file_name);
for (auto& lang : entry.nacp.lang) { std::strncpy(nacp.lang.name, file_name, file_name_len - 4);
std::strncpy(lang.name, file_name, file_name_len - 4); std::strcpy(nacp.lang.author, "Unknown");
std::strcpy(lang.author, "Unknown"); std::strcpy(nacp.display_version, "Unknown");
}
std::strcpy(entry.nacp.display_version, "Unknown");
entry.is_nacp_valid = false; entry.is_nacp_valid = false;
} else { } else {
R_TRY(fsFileRead(&f, data.header.size + asset.nacp.offset, &entry.nacp, sizeof(entry.nacp), FsReadOption_None, &bytes_read)); R_TRY(fsFileRead(&f, data.header.size + asset.nacp.offset, &nacp.lang, sizeof(nacp.lang), FsReadOption_None, &bytes_read));
R_TRY(fsFileRead(&f, data.header.size + asset.nacp.offset + offsetof(NacpStruct, display_version), nacp.display_version, sizeof(nacp.display_version), FsReadOption_None, &bytes_read));
entry.is_nacp_valid = true; entry.is_nacp_valid = true;
} }

View File

@@ -3,7 +3,33 @@
#include "option.hpp" #include "option.hpp"
#include "app.hpp" #include "app.hpp"
#include <cctype>
#include <cstring>
#include <cstdlib>
namespace sphaira::option { namespace sphaira::option {
namespace {
// these are taken from minini in order to parse a value already loaded in memory.
long getl(const char* LocalBuffer, long def) {
const auto len = strlen(LocalBuffer);
return (len == 0) ? def
: ((len >= 2 && toupper((int)LocalBuffer[1]) == 'X') ? strtol(LocalBuffer, NULL, 16)
: strtol(LocalBuffer, NULL, 10));
}
bool getbool(const char* LocalBuffer, bool def) {
const auto c = toupper(LocalBuffer[0]);
if (c == 'Y' || c == '1' || c == 'T')
return true;
else if (c == 'N' || c == '0' || c == 'F')
return false;
else
return def;
}
} // namespace
template<typename T> template<typename T>
auto OptionBase<T>::GetInternal(const char* name) -> T { auto OptionBase<T>::GetInternal(const char* name) -> T {
@@ -47,6 +73,28 @@ void OptionBase<T>::Set(T value) {
} }
} }
template<typename T>
auto OptionBase<T>::LoadFrom(const char* section, const char* name, const char* value) -> bool {
return m_section == section && LoadFrom(name, value);
}
template<typename T>
auto OptionBase<T>::LoadFrom(const char* name, const char* value) -> bool {
if (m_name == name) {
if constexpr(std::is_same_v<T, bool>) {
m_value = getbool(value, m_default_value);
} else if constexpr(std::is_same_v<T, long>) {
m_value = getl(value, m_default_value);
} else if constexpr(std::is_same_v<T, std::string>) {
m_value = value;
}
return true;
}
return false;
}
template struct OptionBase<bool>; template struct OptionBase<bool>;
template struct OptionBase<long>; template struct OptionBase<long>;
template struct OptionBase<std::string>; template struct OptionBase<std::string>;

View File

@@ -34,6 +34,14 @@ constexpr u32 PFS0_PADDING_SIZE = 0x200;
constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF; constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF;
constexpr u32 ROMFS_FILEPARTITION_OFS = 0x200; constexpr u32 ROMFS_FILEPARTITION_OFS = 0x200;
constexpr const u8 HBL_MAIN_DATA[]{
#embed <exefs/main>
};
constexpr const u8 HBL_NPDM_DATA[]{
#embed <exefs/main.npdm>
};
// stdio-like wrapper for std::vector // stdio-like wrapper for std::vector
struct BufHelper { struct BufHelper {
BufHelper() = default; BufHelper() = default;
@@ -831,6 +839,9 @@ auto create_meta_nca(u64 tid, const keys::Keys& keys, NcmStorageId storage_id, c
} }
auto install_forwader_internal(ui::ProgressBox* pbox, OwoConfig& config, NcmStorageId storage_id) -> Result { auto install_forwader_internal(ui::ProgressBox* pbox, OwoConfig& config, NcmStorageId storage_id) -> Result {
pbox->SetTitle(config.name);
pbox->SetImageDataConst(config.icon);
R_UNLESS(!config.nro_path.empty(), OwoError_BadArgs); R_UNLESS(!config.nro_path.empty(), OwoError_BadArgs);
// R_UNLESS(!config.icon.empty(), OwoError_BadArgs); // R_UNLESS(!config.icon.empty(), OwoError_BadArgs);
@@ -864,13 +875,10 @@ auto install_forwader_internal(ui::ProgressBox* pbox, OwoConfig& config, NcmStor
// create program // create program
if (config.program_nca.empty()) { if (config.program_nca.empty()) {
R_UNLESS(!config.main.empty(), OwoError_BadArgs);
R_UNLESS(!config.npdm.empty(), OwoError_BadArgs);
pbox->NewTransfer("Creating Program"_i18n).UpdateTransfer(0, 8); pbox->NewTransfer("Creating Program"_i18n).UpdateTransfer(0, 8);
FileEntries exefs; FileEntries exefs;
add_file_entry(exefs, "main", config.main); add_file_entry(exefs, "main", HBL_MAIN_DATA);
add_file_entry(exefs, "main.npdm", config.npdm); add_file_entry(exefs, "main.npdm", HBL_NPDM_DATA);
FileEntries romfs; FileEntries romfs;
add_file_entry(romfs, "/nextArgv", config.args.data(), config.args.length()); add_file_entry(romfs, "/nextArgv", config.args.data(), config.args.length());

View File

@@ -22,6 +22,7 @@
#include <yyjson.h> #include <yyjson.h>
#include <stb_image.h> #include <stb_image.h>
#include <minizip/unzip.h> #include <minizip/unzip.h>
#include <algorithm>
#include <ranges> #include <ranges>
#include <utility> #include <utility>
@@ -35,6 +36,22 @@ constexpr auto URL_JSON = "https://switch.cdn.fortheusers.org/repo.json";
constexpr auto URL_POST_FEEDBACK = "http://switchbru.com/appstore/feedback"; constexpr auto URL_POST_FEEDBACK = "http://switchbru.com/appstore/feedback";
constexpr auto URL_GET_FEEDACK = "http://switchbru.com/appstore/feedback"; constexpr auto URL_GET_FEEDACK = "http://switchbru.com/appstore/feedback";
constexpr const u8 UPDATE_IMAGE_DATA[]{
#embed <icons/UPDATE.png>
};
constexpr const u8 GET_IMAGE_DATA[]{
#embed <icons/GET.png>
};
constexpr const u8 LOCAL_IMAGE_DATA[]{
#embed <icons/LOCAL.png>
};
constexpr const u8 INSTALLED_IMAGE_DATA[]{
#embed <icons/INSTALLED.png>
};
constexpr const char* FILTER_STR[] = { constexpr const char* FILTER_STR[] = {
"All", "All",
"Games", "Games",
@@ -172,7 +189,7 @@ auto LoadAndParseManifest(const Entry& e) -> ManifestEntries {
return ParseManifest(std::span{(const char*)data.data(), data.size()}); return ParseManifest(std::span{(const char*)data.data(), data.size()});
} }
auto EntryLoadImageFile(fs::Fs& fs, const fs::FsPath& path, LazyImage& image) -> bool { auto EntryLoadImageData(std::span<const u8> image_buf, LazyImage& image) -> bool {
// already have the image // already have the image
if (image.image) { if (image.image) {
// log_write("warning, tried to load image: %s when already loaded\n", path); // log_write("warning, tried to load image: %s when already loaded\n", path);
@@ -180,10 +197,6 @@ auto EntryLoadImageFile(fs::Fs& fs, const fs::FsPath& path, LazyImage& image) ->
} }
auto vg = App::GetVg(); auto vg = App::GetVg();
std::vector<u8> image_buf;
if (R_FAILED(fs.read_entire_file(path, image_buf))) {
log_write("failed to load image from file: %s\n", path.s);
} else {
int channels_in_file; int channels_in_file;
auto buf = stbi_load_from_memory(image_buf.data(), image_buf.size(), &image.w, &image.h, &channels_in_file, 4); auto buf = stbi_load_from_memory(image_buf.data(), image_buf.size(), &image.w, &image.h, &channels_in_file, 4);
if (buf) { if (buf) {
@@ -191,6 +204,22 @@ auto EntryLoadImageFile(fs::Fs& fs, const fs::FsPath& path, LazyImage& image) ->
std::memcpy(image.first_pixel, buf, sizeof(image.first_pixel)); std::memcpy(image.first_pixel, buf, sizeof(image.first_pixel));
image.image = nvgCreateImageRGBA(vg, image.w, image.h, 0, buf); image.image = nvgCreateImageRGBA(vg, image.w, image.h, 0, buf);
} }
return image.image;
}
auto EntryLoadImageFile(fs::Fs& fs, const fs::FsPath& path, LazyImage& image) -> bool {
// already have the image
if (image.image) {
// log_write("warning, tried to load image: %s when already loaded\n", path);
return true;
}
std::vector<u8> image_buf;
if (R_FAILED(fs.read_entire_file(path, image_buf))) {
log_write("failed to load image from file: %s\n", path.s);
} else {
EntryLoadImageData(image_buf, image);
} }
if (!image.image) { if (!image.image) {
@@ -311,7 +340,7 @@ auto UninstallApp(ProgressBox* pbox, const Entry& entry) -> Result {
// remove directory, this will also delete manifest and info // remove directory, this will also delete manifest and info
const auto dir = BuildPackageCachePath(entry); const auto dir = BuildPackageCachePath(entry);
pbox->NewTransfer("Removing "_i18n + dir); pbox->NewTransfer("Removing "_i18n + dir.toString());
if (R_FAILED(fs.DeleteDirectoryRecursively(dir))) { if (R_FAILED(fs.DeleteDirectoryRecursively(dir))) {
log_write("failed to delete folder: %s\n", dir.s); log_write("failed to delete folder: %s\n", dir.s);
} else { } else {
@@ -329,7 +358,7 @@ auto UninstallApp(ProgressBox* pbox, const Entry& entry) -> Result {
// 4. move everything from placeholder to normal location // 4. move everything from placeholder to normal location
auto InstallApp(ProgressBox* pbox, const Entry& entry) -> Result { auto InstallApp(ProgressBox* pbox, const Entry& entry) -> Result {
static const fs::FsPath zip_out{"/switch/sphaira/cache/appstore/temp.zip"}; static const fs::FsPath zip_out{"/switch/sphaira/cache/appstore/temp.zip"};
constexpr auto chunk_size = 1024 * 512; // 512KiB std::vector<u8> buf(1024 * 512); // 512KiB
fs::FsNativeSd fs; fs::FsNativeSd fs;
R_TRY(fs.GetFsOpenResult()); R_TRY(fs.GetFsOpenResult());
@@ -405,26 +434,7 @@ auto InstallApp(ProgressBox* pbox, const Entry& entry) -> Result {
} }
} }
const auto unzip_to = [pbox, &fs, zfile](const fs::FsPath& inzip, fs::FsPath output) -> Result { const auto unzip_to_file = [&](const unz_file_info64& info, const fs::FsPath& inzip, fs::FsPath output) -> Result {
pbox->NewTransfer(inzip);
if (UNZ_END_OF_LIST_OF_FILE == unzLocateFile(zfile, inzip, 0)) {
log_write("failed to find %s\n", inzip.s);
R_THROW(0x1);
}
if (UNZ_OK != unzOpenCurrentFile(zfile)) {
log_write("failed to open current file\n");
R_THROW(0x1);
}
ON_SCOPE_EXIT(unzCloseCurrentFile(zfile));
unz_file_info64 info;
if (UNZ_OK != unzGetCurrentFileInfo64(zfile, &info, 0, 0, 0, 0, 0, 0)) {
log_write("failed to get current info\n");
R_THROW(0x1);
}
if (output[0] != '/') { if (output[0] != '/') {
output = fs::AppendPath("/", output); output = fs::AppendPath("/", output);
} }
@@ -444,7 +454,6 @@ auto InstallApp(ProgressBox* pbox, const Entry& entry) -> Result {
R_TRY(fsFileSetSize(&f, info.uncompressed_size)); R_TRY(fsFileSetSize(&f, info.uncompressed_size));
std::vector<char> buf(chunk_size);
u64 offset{}; u64 offset{};
while (offset < info.uncompressed_size) { while (offset < info.uncompressed_size) {
R_TRY(pbox->ShouldExitResult()); R_TRY(pbox->ShouldExitResult());
@@ -464,37 +473,106 @@ auto InstallApp(ProgressBox* pbox, const Entry& entry) -> Result {
R_SUCCEED(); R_SUCCEED();
}; };
// unzip manifest and info const auto unzip_to = [&](const fs::FsPath& inzip, const fs::FsPath& output) -> Result {
R_TRY(unzip_to("info.json", BuildInfoCachePath(entry))); pbox->NewTransfer(inzip);
R_TRY(unzip_to("manifest.install", BuildManifestCachePath(entry)));
for (auto& new_entry : new_manifest) { if (UNZ_END_OF_LIST_OF_FILE == unzLocateFile(zfile, inzip, 0)) {
log_write("failed to find %s\n", inzip.s);
R_THROW(0x1);
}
if (UNZ_OK != unzOpenCurrentFile(zfile)) {
log_write("failed to open current file\n");
R_THROW(0x1);
}
ON_SCOPE_EXIT(unzCloseCurrentFile(zfile));
unz_file_info64 info;
if (UNZ_OK != unzGetCurrentFileInfo64(zfile, &info, 0, 0, 0, 0, 0, 0)) {
log_write("failed to get current info\n");
R_THROW(0x1);
}
return unzip_to_file(info, inzip, output);
};
const auto unzip_all = [&](std::span<const ManifestEntry> entries) -> Result {
unz_global_info64 ginfo;
if (UNZ_OK != unzGetGlobalInfo64(zfile, &ginfo)) {
R_THROW(0x1);
}
if (UNZ_OK != unzGoToFirstFile(zfile)) {
R_THROW(0x1);
}
for (s64 i = 0; i < ginfo.number_entry; i++) {
R_TRY(pbox->ShouldExitResult()); R_TRY(pbox->ShouldExitResult());
switch (new_entry.command) { if (i > 0) {
if (UNZ_OK != unzGoToNextFile(zfile)) {
log_write("failed to unzGoToNextFile\n");
R_THROW(0x1);
}
}
if (UNZ_OK != unzOpenCurrentFile(zfile)) {
log_write("failed to open current file\n");
R_THROW(0x1);
}
ON_SCOPE_EXIT(unzCloseCurrentFile(zfile));
unz_file_info64 info;
char name[512];
if (UNZ_OK != unzGetCurrentFileInfo64(zfile, &info, name, sizeof(name), 0, 0, 0, 0)) {
log_write("failed to get current info\n");
R_THROW(0x1);
}
const auto it = std::ranges::find_if(entries, [&name](auto& e){
return !strcasecmp(name, e.path);
});
if (it == entries.end()) [[unlikely]] {
continue;
}
pbox->NewTransfer(it->path);
switch (it->command) {
case 'E': // both are the same? case 'E': // both are the same?
case 'U': case 'U':
break; break;
case 'G': { // checks if file exists, if not, extract case 'G': { // checks if file exists, if not, extract
if (fs.FileExists(fs::AppendPath("/", new_entry.path))) { if (fs.FileExists(fs::AppendPath("/", it->path))) {
continue; continue;
} }
} break; } break;
default: default:
log_write("bad command: %c\n", new_entry.command); log_write("bad command: %c\n", it->command);
continue; continue;
} }
R_TRY(unzip_to(new_entry.path, new_entry.path)); R_TRY(unzip_to_file(info, it->path, it->path));
} }
R_SUCCEED();
};
// unzip manifest, info and all entries.
TimeStamp ts;
R_TRY(unzip_to("info.json", BuildInfoCachePath(entry)));
R_TRY(unzip_to("manifest.install", BuildManifestCachePath(entry)));
R_TRY(unzip_all(new_manifest));
log_write("\n\t[APPSTORE] finished extract new, time taken: %.2fs %zums\n\n", ts.GetSecondsD(), ts.GetMs());
// finally finally, remove files no longer in the manifest // finally finally, remove files no longer in the manifest
for (auto& old_entry : old_manifest) { for (auto& old_entry : old_manifest) {
bool found = false; bool found = false;
for (auto& new_entry : new_manifest) { for (auto& new_entry : new_manifest) {
if (!std::strcmp(old_entry.path, new_entry.path)) { if (!strcasecmp(old_entry.path, new_entry.path)) {
found = true; found = true;
break; break;
} }
@@ -1022,14 +1100,11 @@ void Menu::OnFocusGained() {
// log_write("saying we got focus base: size: %zu count: %zu\n", repo_json.size(), m_entries.size()); // log_write("saying we got focus base: size: %zu count: %zu\n", repo_json.size(), m_entries.size());
if (!m_default_image.image) { if (!m_default_image.image) {
if (R_SUCCEEDED(romfsInit())) { EntryLoadImageData(App::GetDefaultImageData(), m_default_image);
ON_SCOPE_EXIT(romfsExit()); EntryLoadImageData(UPDATE_IMAGE_DATA, m_update);
EntryLoadImageFile("romfs:/default.png", m_default_image); EntryLoadImageData(GET_IMAGE_DATA, m_get);
EntryLoadImageFile("romfs:/UPDATE.png", m_update); EntryLoadImageData(LOCAL_IMAGE_DATA, m_local);
EntryLoadImageFile("romfs:/GET.png", m_get); EntryLoadImageData(INSTALLED_IMAGE_DATA, m_installed);
EntryLoadImageFile("romfs:/LOCAL.png", m_local);
EntryLoadImageFile("romfs:/INSTALLED.png", m_installed);
}
} }
if (m_entries.empty()) { if (m_entries.empty()) {
@@ -1074,6 +1149,9 @@ void Menu::SetIndex(s64 index) {
} }
void Menu::ScanHomebrew() { void Menu::ScanHomebrew() {
appletSetCpuBoostMode(ApmCpuBoostMode_FastLoad);
ON_SCOPE_EXIT(appletSetCpuBoostMode(ApmCpuBoostMode_Normal));
from_json(REPO_PATH, m_entries); from_json(REPO_PATH, m_entries);
fs::FsNativeSd fs; fs::FsNativeSd fs;

View File

@@ -941,6 +941,9 @@ void FsView::InstallForwarder() {
log_write("parsing nro\n"); log_write("parsing nro\n");
R_TRY(nro_parse(assoc.path, nro)); R_TRY(nro_parse(assoc.path, nro));
NacpStruct nacp;
R_TRY(nro_get_nacp(assoc.path, nacp));
log_write("got nro data\n"); log_write("got nro data\n");
auto file_name = assoc.use_base_name ? GetEntry().GetName() : GetEntry().GetInternalName(); auto file_name = assoc.use_base_name ? GetEntry().GetName() : GetEntry().GetInternalName();
@@ -955,9 +958,9 @@ void FsView::InstallForwarder() {
OwoConfig config{}; OwoConfig config{};
config.nro_path = assoc.path.toString(); config.nro_path = assoc.path.toString();
config.args = nro_add_arg_file(GetNewPathCurrent()); config.args = nro_add_arg_file(GetNewPathCurrent());
config.name = nro.nacp.lang[0].name + std::string{" | "} + file_name; config.name = nro.nacp.lang.name + std::string{" | "} + file_name;
// config.name = file_name; // config.name = file_name;
config.nacp = nro.nacp; config.nacp = nacp;
config.icon = GetRomIcon(m_fs.get(), pbox, file_name, db_indexs, nro); config.icon = GetRomIcon(m_fs.get(), pbox, file_name, db_indexs, nro);
pbox->SetImageDataConst(config.icon); pbox->SetImageDataConst(config.icon);
@@ -1017,7 +1020,7 @@ void FsView::UnzipFiles(fs::FsPath dir_path) {
R_THROW(0x1); R_THROW(0x1);
} }
for (int i = 0; i < pglobal_info.number_entry; i++) { for (s64 i = 0; i < pglobal_info.number_entry; i++) {
if (i > 0) { if (i > 0) {
if (UNZ_OK != unzGoToNextFile(zfile)) { if (UNZ_OK != unzGoToNextFile(zfile)) {
log_write("failed to unzGoToNextFile\n"); log_write("failed to unzGoToNextFile\n");
@@ -1325,6 +1328,9 @@ void FsView::UploadFiles() {
} }
auto FsView::Scan(const fs::FsPath& new_path, bool is_walk_up) -> Result { auto FsView::Scan(const fs::FsPath& new_path, bool is_walk_up) -> Result {
appletSetCpuBoostMode(ApmCpuBoostMode_FastLoad);
ON_SCOPE_EXIT(appletSetCpuBoostMode(ApmCpuBoostMode_Normal));
log_write("new scan path: %s\n", new_path.s); log_write("new scan path: %s\n", new_path.s);
if (!is_walk_up && !m_path.empty() && !m_entries_current.empty()) { if (!is_walk_up && !m_path.empty() && !m_entries_current.empty()) {
const LastFile f(GetEntry().name, m_index, m_list->GetYoff(), m_entries_current.size()); const LastFile f(GetEntry().name, m_index, m_list->GetYoff(), m_entries_current.size());
@@ -1781,7 +1787,7 @@ static Result DeleteAllCollections(ProgressBox* pbox, fs::Fs* fs, const Selected
const auto full_path = FsView::GetNewPath(c.path, p.name); const auto full_path = FsView::GetNewPath(c.path, p.name);
pbox->SetTitle(p.name); pbox->SetTitle(p.name);
pbox->NewTransfer("Deleting "_i18n + full_path); pbox->NewTransfer("Deleting "_i18n + full_path.toString());
if ((mode & FsDirOpenMode_ReadDirs) && p.type == FsDirEntryType_Dir) { if ((mode & FsDirOpenMode_ReadDirs) && p.type == FsDirEntryType_Dir) {
log_write("deleting dir: %s\n", full_path.s); log_write("deleting dir: %s\n", full_path.s);
R_TRY(fs->DeleteDirectory(full_path)); R_TRY(fs->DeleteDirectory(full_path));
@@ -1804,7 +1810,7 @@ static Result DeleteAllCollections(ProgressBox* pbox, fs::Fs* fs, const Selected
const auto full_path = FsView::GetNewPath(selected.m_path, p.name); const auto full_path = FsView::GetNewPath(selected.m_path, p.name);
pbox->SetTitle(p.name); pbox->SetTitle(p.name);
pbox->NewTransfer("Deleting "_i18n + full_path); pbox->NewTransfer("Deleting "_i18n + full_path.toString());
if ((mode & FsDirOpenMode_ReadDirs) && p.type == FsDirEntryType_Dir) { if ((mode & FsDirOpenMode_ReadDirs) && p.type == FsDirEntryType_Dir) {
log_write("deleting dir: %s\n", full_path.s); log_write("deleting dir: %s\n", full_path.s);

View File

@@ -981,6 +981,9 @@ void Menu::ScanHomebrew() {
const auto hide_forwarders = m_hide_forwarders.Get(); const auto hide_forwarders = m_hide_forwarders.Get();
TimeStamp ts; TimeStamp ts;
appletSetCpuBoostMode(ApmCpuBoostMode_FastLoad);
ON_SCOPE_EXIT(appletSetCpuBoostMode(ApmCpuBoostMode_Normal));
FreeEntries(); FreeEntries();
m_entries.reserve(ENTRY_CHUNK_COUNT); m_entries.reserve(ENTRY_CHUNK_COUNT);

View File

@@ -953,6 +953,9 @@ void Menu::OnChangeIndex(s64 new_index) {
} }
Result Menu::DumpGames(u32 flags) { Result Menu::DumpGames(u32 flags) {
appletSetCpuBoostMode(ApmCpuBoostMode_FastLoad);
ON_SCOPE_EXIT(appletSetCpuBoostMode(ApmCpuBoostMode_Normal));
R_TRY(GcMountStorage()); R_TRY(GcMountStorage());
u32 location_flags = dump::DumpLocationFlag_All; u32 location_flags = dump::DumpLocationFlag_All;

View File

@@ -233,7 +233,7 @@ void Menu::SetIndex(s64 index) {
void Menu::InstallHomebrew() { void Menu::InstallHomebrew() {
const auto& nro = m_entries[m_index]; const auto& nro = m_entries[m_index];
InstallHomebrew(nro.path, nro.nacp, nro_get_icon(nro.path, nro.icon_size, nro.icon_offset)); InstallHomebrew(nro.path, nro_get_icon(nro.path, nro.icon_size, nro.icon_offset));
} }
void Menu::ScanHomebrew() { void Menu::ScanHomebrew() {
@@ -266,8 +266,6 @@ void Menu::ScanHomebrew() {
if (user->ini) { if (user->ini) {
if (!strcmp(Key, "timestamp")) { if (!strcmp(Key, "timestamp")) {
user->ini->timestamp = atoi(Value); user->ini->timestamp = atoi(Value);
} else if (!strcmp(Key, "launch_count")) {
user->ini->launch_count = atoi(Value);
} }
} }
@@ -412,19 +410,16 @@ void Menu::OnLayoutChange() {
grid::Menu::OnLayoutChange(m_list, m_layout.Get()); grid::Menu::OnLayoutChange(m_list, m_layout.Get());
} }
Result Menu::InstallHomebrew(const fs::FsPath& path, const NacpStruct& nacp, const std::vector<u8>& icon) { Result Menu::InstallHomebrew(const fs::FsPath& path, const std::vector<u8>& icon) {
OwoConfig config{}; OwoConfig config{};
config.nro_path = path.toString(); config.nro_path = path.toString();
config.nacp = nacp; R_TRY(nro_get_nacp(path, config.nacp));
config.icon = icon; config.icon = icon;
return App::Install(config); return App::Install(config);
} }
Result Menu::InstallHomebrewFromPath(const fs::FsPath& path) { Result Menu::InstallHomebrewFromPath(const fs::FsPath& path) {
NacpStruct nacp; return InstallHomebrew(path, nro_get_icon(path));
R_TRY(nro_get_nacp(path, nacp))
const auto icon = nro_get_icon(path);
return InstallHomebrew(path, nacp, icon);
} }
} // namespace sphaira::ui::menu::homebrew } // namespace sphaira::ui::menu::homebrew

View File

@@ -647,6 +647,9 @@ void Menu::PackListDownload() {
curl::Flags{curl::Flag_Cache}, curl::Flags{curl::Flag_Cache},
curl::StopToken{this->GetToken()}, curl::StopToken{this->GetToken()},
curl::OnComplete{[this, page_index](auto& result){ curl::OnComplete{[this, page_index](auto& result){
appletSetCpuBoostMode(ApmCpuBoostMode_FastLoad);
ON_SCOPE_EXIT(appletSetCpuBoostMode(ApmCpuBoostMode_Normal));
log_write("got themezer data\n"); log_write("got themezer data\n");
if (!result.success) { if (!result.success) {
auto& page = m_pages[page_index-1]; auto& page = m_pages[page_index-1];

View File

@@ -19,6 +19,10 @@ void threadFunc(void* arg) {
} // namespace } // namespace
ProgressBox::ProgressBox(int image, const std::string& action, const std::string& title, ProgressBoxCallback callback, ProgressBoxDoneCallback done, int cpuid, int prio, int stack_size) { ProgressBox::ProgressBox(int image, const std::string& action, const std::string& title, ProgressBoxCallback callback, ProgressBoxDoneCallback done, int cpuid, int prio, int stack_size) {
if (App::GetApp()->m_progress_boost_mode.Get()) {
appletSetCpuBoostMode(ApmCpuBoostMode_FastLoad);
}
SetAction(Button::B, Action{"Back"_i18n, [this](){ SetAction(Button::B, Action{"Back"_i18n, [this](){
App::Push(std::make_shared<OptionBox>("Are you sure you wish to cancel?"_i18n, "No"_i18n, "Yes"_i18n, 1, [this](auto op_index){ App::Push(std::make_shared<OptionBox>("Are you sure you wish to cancel?"_i18n, "No"_i18n, "Yes"_i18n, 1, [this](auto op_index){
if (op_index && *op_index) { if (op_index && *op_index) {
@@ -61,6 +65,8 @@ ProgressBox::~ProgressBox() {
FreeImage(); FreeImage();
m_done(m_thread_data.result); m_done(m_thread_data.result);
appletSetCpuBoostMode(ApmCpuBoostMode_Normal);
} }
auto ProgressBox::Update(Controller* controller, TouchInfo* touch) -> void { auto ProgressBox::Update(Controller* controller, TouchInfo* touch) -> void {

View File

@@ -767,16 +767,11 @@ Yati::~Yati() {
ncmContentStorageClose(std::addressof(ncm_cs[i])); ncmContentStorageClose(std::addressof(ncm_cs[i]));
} }
if (config.boost_mode) {
appletSetCpuBoostMode(ApmCpuBoostMode_Normal);
}
App::SetAutoSleepDisabled(false); App::SetAutoSleepDisabled(false);
} }
Result Yati::Setup(const ConfigOverride& override) { Result Yati::Setup(const ConfigOverride& override) {
config.sd_card_install = override.sd_card_install.value_or(App::GetApp()->m_install_sd.Get()); config.sd_card_install = override.sd_card_install.value_or(App::GetApp()->m_install_sd.Get());
config.boost_mode = App::GetApp()->m_boost_mode.Get();
config.allow_downgrade = App::GetApp()->m_allow_downgrade.Get(); config.allow_downgrade = App::GetApp()->m_allow_downgrade.Get();
config.skip_if_already_installed = App::GetApp()->m_skip_if_already_installed.Get(); config.skip_if_already_installed = App::GetApp()->m_skip_if_already_installed.Get();
config.ticket_only = App::GetApp()->m_ticket_only.Get(); config.ticket_only = App::GetApp()->m_ticket_only.Get();
@@ -794,10 +789,6 @@ Result Yati::Setup(const ConfigOverride& override) {
config.lower_system_version = override.lower_system_version.value_or(App::GetApp()->m_lower_system_version.Get()); config.lower_system_version = override.lower_system_version.value_or(App::GetApp()->m_lower_system_version.Get());
storage_id = config.sd_card_install ? NcmStorageId_SdCard : NcmStorageId_BuiltInUser; storage_id = config.sd_card_install ? NcmStorageId_SdCard : NcmStorageId_BuiltInUser;
if (config.boost_mode) {
appletSetCpuBoostMode(ApmCpuBoostMode_FastLoad);
}
R_TRY(source->GetOpenResult()); R_TRY(source->GetOpenResult());
R_TRY(splCryptoInitialize()); R_TRY(splCryptoInitialize());
R_TRY(nsInitialize()); R_TRY(nsInitialize());