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

@@ -25,6 +25,7 @@
#include <pulsar.h>
#include <haze.h>
#include <algorithm>
#include <ranges>
#include <cassert>
#include <cstring>
#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://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() {
App::Push(std::make_shared<ui::ProgressBox>(0, "Downloading "_i18n, "default_music.bfstm", [](auto pbox){
const auto result = curl::Api().ToFile(
@@ -444,10 +449,7 @@ void App::Loop() {
// update timestamp
ini_putl(nro_path.c_str(), "timestamp", timestamp, App::PLAYLOG_PATH);
// update launch_count
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);
log_write("updating timestamp for: %s %lu\n", nro_path.c_str(), timestamp);
// force disable pop-back to main menu.
__nx_applet_exit_mode = 0;
@@ -516,11 +518,11 @@ auto App::Push(std::shared_ptr<ui::Widget> widget) -> void {
}
auto App::PopToMenu() -> void {
for (auto it = g_app->m_widgets.rbegin(); it != g_app->m_widgets.rend(); it++) {
const auto& p = *it;
for (auto& p : std::ranges::views::reverse(g_app->m_widgets)) {
if (p->IsMenu()) {
break;
}
p->SetPop();
}
}
@@ -595,6 +597,10 @@ auto App::GetDefaultImage() -> int {
return g_app->m_default_image;
}
auto App::GetDefaultImageData() -> std::span<const u8> {
return DEFAULT_IMAGE_DATA;
}
auto App::GetExePath() -> fs::FsPath {
return g_app->m_app_path;
}
@@ -868,18 +874,7 @@ void App::SetTextScrollSpeed(long index) {
}
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.main = main_data;
config.npdm = npdm_data;
config.logo = logo_data;
config.gif = gif_data;
if (!config.icon.empty()) {
config.icon = GetNroIcon(config.icon);
}
@@ -898,18 +893,7 @@ auto App::Install(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.main = main_data;
config.npdm = npdm_data;
config.logo = logo_data;
config.gif = gif_data;
if (!config.icon.empty()) {
config.icon = GetNroIcon(config.icon);
}
@@ -1268,6 +1252,9 @@ void App::ScanThemeEntries() {
App::App(const char* argv0) {
TimeStamp ts;
appletSetCpuBoostMode(ApmCpuBoostMode_FastLoad);
ON_SCOPE_EXIT(appletSetCpuBoostMode(ApmCpuBoostMode_Normal));
g_app = this;
m_start_timestamp = armGetSystemTick();
if (!std::strncmp(argv0, "sdmc:/", 6)) {
@@ -1283,10 +1270,55 @@ App::App(const char* argv0) {
}
fs::FsNativeSd fs;
fs.CreateDirectoryRecursively("/config/sphaira/assoc");
fs.CreateDirectoryRecursively("/config/sphaira/themes");
fs.CreateDirectoryRecursively("/config/sphaira/github");
fs.CreateDirectoryRecursively("/config/sphaira/i18n");
fs.CreateDirectoryRecursively("/config/sphaira");
fs.CreateDirectory("/config/sphaira/assoc");
fs.CreateDirectory("/config/sphaira/themes");
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()) {
log_file_init();
@@ -1409,17 +1441,14 @@ App::App(const char* argv0) {
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.
fs::FsPath theme_path = m_theme_path.Get();
ThemeMeta theme_meta;
if (R_SUCCEEDED(romfsInit())) {
ON_SCOPE_EXIT(romfsExit());
if (!LoadThemeMeta(theme_path, theme_meta)) {
log_write("failed to load meta using default\n");
theme_path = default_theme_path;
theme_path = DEFAULT_THEME_PATH;
LoadThemeMeta(theme_path, theme_meta);
}
}
@@ -1456,20 +1485,12 @@ App::App(const char* argv0) {
}
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
if (R_SUCCEEDED(romfsInit())) {
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());
}
}
m_default_image = nvgCreateImageMem(vg, 0, DEFAULT_IMAGE_DATA, std::size(DEFAULT_IMAGE_DATA));
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) {
@@ -1588,6 +1609,10 @@ void App::DisplayAdvancedOptions(bool left_side) {
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){
App::SetTextScrollSpeed(index_out);
}, App::GetTextScrollSpeed()));
@@ -1637,10 +1662,6 @@ void App::DisplayInstallOptions(bool left_side) {
App::SetInstallSdEnable(index_out);
}, (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){
App::GetApp()->m_allow_downgrade.Set(enable);
}));
@@ -1724,6 +1745,9 @@ void App::DisplayDumpOptions(bool left_side) {
}
App::~App() {
appletSetCpuBoostMode(ApmCpuBoostMode_FastLoad);
ON_SCOPE_EXIT(appletSetCpuBoostMode(ApmCpuBoostMode_Normal));
log_write("starting to exit\n");
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) {
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);
pbox->SetImage(source->GetIcon(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->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(path);
R_TRY(fs->RenameFile(temp_path, path));
fs->DeleteFile(base_path);
R_TRY(fs->RenameFile(temp_path, base_path));
}
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);
// 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(&entry.nacp, 0, sizeof(entry.nacp));
std::memset(&nacp, 0, sizeof(nacp));
// get the name without the .nro
const auto file_name = std::strrchr(path, '/') + 1;
const auto file_name_len = std::strlen(file_name);
for (auto& lang : entry.nacp.lang) {
std::strncpy(lang.name, file_name, file_name_len - 4);
std::strcpy(lang.author, "Unknown");
}
std::strcpy(entry.nacp.display_version, "Unknown");
std::strncpy(nacp.lang.name, file_name, file_name_len - 4);
std::strcpy(nacp.lang.author, "Unknown");
std::strcpy(nacp.display_version, "Unknown");
entry.is_nacp_valid = false;
} 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;
}

View File

@@ -3,7 +3,33 @@
#include "option.hpp"
#include "app.hpp"
#include <cctype>
#include <cstring>
#include <cstdlib>
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>
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<long>;
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_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
struct BufHelper {
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 {
pbox->SetTitle(config.name);
pbox->SetImageDataConst(config.icon);
R_UNLESS(!config.nro_path.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
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);
FileEntries exefs;
add_file_entry(exefs, "main", config.main);
add_file_entry(exefs, "main.npdm", config.npdm);
add_file_entry(exefs, "main", HBL_MAIN_DATA);
add_file_entry(exefs, "main.npdm", HBL_NPDM_DATA);
FileEntries romfs;
add_file_entry(romfs, "/nextArgv", config.args.data(), config.args.length());

View File

@@ -22,6 +22,7 @@
#include <yyjson.h>
#include <stb_image.h>
#include <minizip/unzip.h>
#include <algorithm>
#include <ranges>
#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_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[] = {
"All",
"Games",
@@ -172,7 +189,7 @@ auto LoadAndParseManifest(const Entry& e) -> ManifestEntries {
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
if (image.image) {
// log_write("warning, tried to load image: %s when already loaded\n", path);
@@ -180,17 +197,29 @@ auto EntryLoadImageFile(fs::Fs& fs, const fs::FsPath& path, LazyImage& image) ->
}
auto vg = App::GetVg();
int channels_in_file;
auto buf = stbi_load_from_memory(image_buf.data(), image_buf.size(), &image.w, &image.h, &channels_in_file, 4);
if (buf) {
ON_SCOPE_EXIT(stbi_image_free(buf));
std::memcpy(image.first_pixel, buf, sizeof(image.first_pixel));
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 {
int channels_in_file;
auto buf = stbi_load_from_memory(image_buf.data(), image_buf.size(), &image.w, &image.h, &channels_in_file, 4);
if (buf) {
ON_SCOPE_EXIT(stbi_image_free(buf));
std::memcpy(image.first_pixel, buf, sizeof(image.first_pixel));
image.image = nvgCreateImageRGBA(vg, image.w, image.h, 0, buf);
}
EntryLoadImageData(image_buf, 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
const auto dir = BuildPackageCachePath(entry);
pbox->NewTransfer("Removing "_i18n + dir);
pbox->NewTransfer("Removing "_i18n + dir.toString());
if (R_FAILED(fs.DeleteDirectoryRecursively(dir))) {
log_write("failed to delete folder: %s\n", dir.s);
} else {
@@ -329,7 +358,7 @@ auto UninstallApp(ProgressBox* pbox, const Entry& entry) -> Result {
// 4. move everything from placeholder to normal location
auto InstallApp(ProgressBox* pbox, const Entry& entry) -> Result {
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;
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 {
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);
}
const auto unzip_to_file = [&](const unz_file_info64& info, const fs::FsPath& inzip, fs::FsPath output) -> Result {
if (output[0] != '/') {
output = fs::AppendPath("/", output);
}
@@ -444,7 +454,6 @@ auto InstallApp(ProgressBox* pbox, const Entry& entry) -> Result {
R_TRY(fsFileSetSize(&f, info.uncompressed_size));
std::vector<char> buf(chunk_size);
u64 offset{};
while (offset < info.uncompressed_size) {
R_TRY(pbox->ShouldExitResult());
@@ -464,37 +473,106 @@ auto InstallApp(ProgressBox* pbox, const Entry& entry) -> Result {
R_SUCCEED();
};
// unzip manifest and info
R_TRY(unzip_to("info.json", BuildInfoCachePath(entry)));
R_TRY(unzip_to("manifest.install", BuildManifestCachePath(entry)));
const auto unzip_to = [&](const fs::FsPath& inzip, const fs::FsPath& output) -> Result {
pbox->NewTransfer(inzip);
for (auto& new_entry : new_manifest) {
R_TRY(pbox->ShouldExitResult());
switch (new_entry.command) {
case 'E': // both are the same?
case 'U':
break;
case 'G': { // checks if file exists, if not, extract
if (fs.FileExists(fs::AppendPath("/", new_entry.path))) {
continue;
}
} break;
default:
log_write("bad command: %c\n", new_entry.command);
continue;
if (UNZ_END_OF_LIST_OF_FILE == unzLocateFile(zfile, inzip, 0)) {
log_write("failed to find %s\n", inzip.s);
R_THROW(0x1);
}
R_TRY(unzip_to(new_entry.path, new_entry.path));
}
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());
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 'U':
break;
case 'G': { // checks if file exists, if not, extract
if (fs.FileExists(fs::AppendPath("/", it->path))) {
continue;
}
} break;
default:
log_write("bad command: %c\n", it->command);
continue;
}
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
for (auto& old_entry : old_manifest) {
bool found = false;
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;
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());
if (!m_default_image.image) {
if (R_SUCCEEDED(romfsInit())) {
ON_SCOPE_EXIT(romfsExit());
EntryLoadImageFile("romfs:/default.png", m_default_image);
EntryLoadImageFile("romfs:/UPDATE.png", m_update);
EntryLoadImageFile("romfs:/GET.png", m_get);
EntryLoadImageFile("romfs:/LOCAL.png", m_local);
EntryLoadImageFile("romfs:/INSTALLED.png", m_installed);
}
EntryLoadImageData(App::GetDefaultImageData(), m_default_image);
EntryLoadImageData(UPDATE_IMAGE_DATA, m_update);
EntryLoadImageData(GET_IMAGE_DATA, m_get);
EntryLoadImageData(LOCAL_IMAGE_DATA, m_local);
EntryLoadImageData(INSTALLED_IMAGE_DATA, m_installed);
}
if (m_entries.empty()) {
@@ -1074,6 +1149,9 @@ void Menu::SetIndex(s64 index) {
}
void Menu::ScanHomebrew() {
appletSetCpuBoostMode(ApmCpuBoostMode_FastLoad);
ON_SCOPE_EXIT(appletSetCpuBoostMode(ApmCpuBoostMode_Normal));
from_json(REPO_PATH, m_entries);
fs::FsNativeSd fs;

View File

@@ -941,6 +941,9 @@ void FsView::InstallForwarder() {
log_write("parsing nro\n");
R_TRY(nro_parse(assoc.path, nro));
NacpStruct nacp;
R_TRY(nro_get_nacp(assoc.path, nacp));
log_write("got nro data\n");
auto file_name = assoc.use_base_name ? GetEntry().GetName() : GetEntry().GetInternalName();
@@ -955,9 +958,9 @@ void FsView::InstallForwarder() {
OwoConfig config{};
config.nro_path = assoc.path.toString();
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.nacp = nro.nacp;
config.nacp = nacp;
config.icon = GetRomIcon(m_fs.get(), pbox, file_name, db_indexs, nro);
pbox->SetImageDataConst(config.icon);
@@ -1017,7 +1020,7 @@ void FsView::UnzipFiles(fs::FsPath dir_path) {
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 (UNZ_OK != unzGoToNextFile(zfile)) {
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 {
appletSetCpuBoostMode(ApmCpuBoostMode_FastLoad);
ON_SCOPE_EXIT(appletSetCpuBoostMode(ApmCpuBoostMode_Normal));
log_write("new scan path: %s\n", new_path.s);
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());
@@ -1781,7 +1787,7 @@ static Result DeleteAllCollections(ProgressBox* pbox, fs::Fs* fs, const Selected
const auto full_path = FsView::GetNewPath(c.path, 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) {
log_write("deleting dir: %s\n", full_path.s);
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);
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) {
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();
TimeStamp ts;
appletSetCpuBoostMode(ApmCpuBoostMode_FastLoad);
ON_SCOPE_EXIT(appletSetCpuBoostMode(ApmCpuBoostMode_Normal));
FreeEntries();
m_entries.reserve(ENTRY_CHUNK_COUNT);

View File

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

View File

@@ -233,7 +233,7 @@ void Menu::SetIndex(s64 index) {
void Menu::InstallHomebrew() {
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() {
@@ -266,8 +266,6 @@ void Menu::ScanHomebrew() {
if (user->ini) {
if (!strcmp(Key, "timestamp")) {
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());
}
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{};
config.nro_path = path.toString();
config.nacp = nacp;
R_TRY(nro_get_nacp(path, config.nacp));
config.icon = icon;
return App::Install(config);
}
Result Menu::InstallHomebrewFromPath(const fs::FsPath& path) {
NacpStruct nacp;
R_TRY(nro_get_nacp(path, nacp))
const auto icon = nro_get_icon(path);
return InstallHomebrew(path, nacp, icon);
return InstallHomebrew(path, nro_get_icon(path));
}
} // namespace sphaira::ui::menu::homebrew

View File

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

View File

@@ -19,6 +19,10 @@ void threadFunc(void* arg) {
} // namespace
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](){
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) {
@@ -61,6 +65,8 @@ ProgressBox::~ProgressBox() {
FreeImage();
m_done(m_thread_data.result);
appletSetCpuBoostMode(ApmCpuBoostMode_Normal);
}
auto ProgressBox::Update(Controller* controller, TouchInfo* touch) -> void {

View File

@@ -767,16 +767,11 @@ Yati::~Yati() {
ncmContentStorageClose(std::addressof(ncm_cs[i]));
}
if (config.boost_mode) {
appletSetCpuBoostMode(ApmCpuBoostMode_Normal);
}
App::SetAutoSleepDisabled(false);
}
Result Yati::Setup(const ConfigOverride& override) {
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.skip_if_already_installed = App::GetApp()->m_skip_if_already_installed.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());
storage_id = config.sd_card_install ? NcmStorageId_SdCard : NcmStorageId_BuiltInUser;
if (config.boost_mode) {
appletSetCpuBoostMode(ApmCpuBoostMode_FastLoad);
}
R_TRY(source->GetOpenResult());
R_TRY(splCryptoInitialize());
R_TRY(nsInitialize());