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)
|
Before Width: | Height: | Size: 569 B After Width: | Height: | Size: 569 B |
|
Before Width: | Height: | Size: 703 B After Width: | Height: | Size: 703 B |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 783 B After Width: | Height: | Size: 783 B |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
@@ -78,11 +78,13 @@ function(nx_create_npdm target config)
|
||||
dkp_set_target_file(${outtarget} "${NX_NPDM_OUTPUT}")
|
||||
endfunction()
|
||||
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/exefs)
|
||||
|
||||
nx_create_nso(hbl
|
||||
OUTPUT main
|
||||
OUTPUT exefs/main
|
||||
)
|
||||
|
||||
nx_create_npdm(hbl
|
||||
OUTPUT main.npdm
|
||||
OUTPUT exefs/main.npdm
|
||||
CONFIG ${CMAKE_CURRENT_SOURCE_DIR}/hbl.json
|
||||
)
|
||||
|
||||
@@ -147,8 +147,6 @@ target_compile_options(sphaira PRIVATE
|
||||
-Wimplicit-fallthrough=5
|
||||
-Wsuggest-final-types
|
||||
-Wuninitialized
|
||||
-fimplicit-constexpr
|
||||
-Wmissing-requires
|
||||
)
|
||||
|
||||
include(FetchContent)
|
||||
@@ -314,9 +312,9 @@ if (NOT USE_NEW_ZSTD)
|
||||
endif()
|
||||
|
||||
set_target_properties(sphaira PROPERTIES
|
||||
C_STANDARD 11
|
||||
C_STANDARD 23
|
||||
C_EXTENSIONS ON
|
||||
CXX_STANDARD 23
|
||||
CXX_STANDARD 26
|
||||
CXX_EXTENSIONS ON
|
||||
)
|
||||
|
||||
@@ -357,12 +355,13 @@ file(COPY ${CMAKE_SOURCE_DIR}/assets/romfs DESTINATION ${CMAKE_CURRENT_BINARY_DI
|
||||
# create assets target
|
||||
dkp_add_asset_target(sphaira_romfs ${CMAKE_CURRENT_BINARY_DIR}/romfs)
|
||||
|
||||
# add hbl exefs to romfs, used for forwarders
|
||||
dkp_install_assets(sphaira_romfs
|
||||
DESTINATION exefs
|
||||
TARGETS
|
||||
hbl_nso
|
||||
hbl_npdm
|
||||
# wait until hbl is built first as we need the exefs to embed
|
||||
add_dependencies(sphaira hbl_nso hbl_npdm)
|
||||
|
||||
# set the embed path for assets and hbl
|
||||
target_compile_options(sphaira PRIVATE
|
||||
--embed-dir=${CMAKE_SOURCE_DIR}/assets/embed
|
||||
--embed-dir=${CMAKE_BINARY_DIR}/hbl
|
||||
)
|
||||
|
||||
# add nanovg shaders to romfs
|
||||
|
||||
@@ -69,6 +69,7 @@ public:
|
||||
static auto GetThemeIndex() -> s64;
|
||||
|
||||
static auto GetDefaultImage() -> int;
|
||||
static auto GetDefaultImageData() -> std::span<const u8>;
|
||||
|
||||
// returns argv[0]
|
||||
static auto GetExePath() -> fs::FsPath;
|
||||
@@ -196,6 +197,7 @@ public:
|
||||
static constexpr inline auto CONFIG_PATH = "/config/sphaira/config.ini";
|
||||
static constexpr inline auto PLAYLOG_PATH = "/config/sphaira/playlog.ini";
|
||||
static constexpr inline auto INI_SECTION = "config";
|
||||
static constexpr inline auto DEFAULT_THEME_PATH = "romfs:/themes/abyss_theme.ini";
|
||||
|
||||
fs::FsPath m_app_path;
|
||||
u64 m_start_timestamp{};
|
||||
@@ -229,17 +231,18 @@ public:
|
||||
option::OptionBool m_hdd_enabled{INI_SECTION, "hdd_enabled", false};
|
||||
option::OptionBool m_log_enabled{INI_SECTION, "log_enabled", 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_12hour_time{INI_SECTION, "12hour_time", false};
|
||||
option::OptionLong m_language{INI_SECTION, "language", 0}; // auto
|
||||
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
|
||||
option::OptionBool m_install_sysmmc{INI_SECTION, "install_sysmmc", false};
|
||||
option::OptionBool m_install_emummc{INI_SECTION, "install_emummc", false};
|
||||
option::OptionBool m_install_sd{INI_SECTION, "install_sd", 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_skip_if_already_installed{INI_SECTION, "skip_if_already_installed", true};
|
||||
option::OptionBool m_ticket_only{INI_SECTION, "ticket_only", false};
|
||||
|
||||
@@ -11,13 +11,17 @@ namespace sphaira {
|
||||
|
||||
struct Hbini {
|
||||
u64 timestamp{}; // timestamp of last launch
|
||||
u32 launch_count{}; //
|
||||
};
|
||||
|
||||
struct MiniNacp {
|
||||
NacpLanguageEntry lang;
|
||||
char display_version[0x10];
|
||||
};
|
||||
|
||||
struct NroEntry {
|
||||
fs::FsPath path{};
|
||||
s64 size{};
|
||||
NacpStruct nacp{};
|
||||
MiniNacp nacp{};
|
||||
|
||||
u64 icon_size{};
|
||||
u64 icon_offset{};
|
||||
@@ -31,11 +35,11 @@ struct NroEntry {
|
||||
std::optional<bool> has_star{std::nullopt};
|
||||
|
||||
auto GetName() const -> const char* {
|
||||
return nacp.lang[0].name;
|
||||
return nacp.lang.name;
|
||||
}
|
||||
|
||||
auto GetAuthor() const -> const char* {
|
||||
return nacp.lang[0].author;
|
||||
return nacp.lang.author;
|
||||
}
|
||||
|
||||
auto GetDisplayVersion() const -> const char* {
|
||||
|
||||
@@ -17,6 +17,11 @@ struct OptionBase {
|
||||
auto GetOr(const char* name) -> T;
|
||||
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:
|
||||
auto GetInternal(const char* name) -> T;
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "ui/progress_box.hpp"
|
||||
// #include <optional>
|
||||
|
||||
namespace sphaira {
|
||||
|
||||
@@ -15,12 +14,9 @@ struct OwoConfig {
|
||||
std::string author{};
|
||||
NacpStruct nacp;
|
||||
std::vector<u8> icon;
|
||||
std::vector<u8> main;
|
||||
std::vector<u8> npdm;
|
||||
std::vector<u8> logo;
|
||||
std::vector<u8> gif;
|
||||
|
||||
// std::optional<u64> tid;
|
||||
std::vector<u8> program_nca{};
|
||||
};
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ struct Menu final : grid::Menu {
|
||||
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);
|
||||
|
||||
private:
|
||||
|
||||
@@ -70,10 +70,6 @@ enum : Result {
|
||||
struct Config {
|
||||
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.
|
||||
bool allow_downgrade{};
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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,10 +197,6 @@ auto EntryLoadImageFile(fs::Fs& fs, const fs::FsPath& path, LazyImage& image) ->
|
||||
}
|
||||
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;
|
||||
auto buf = stbi_load_from_memory(image_buf.data(), image_buf.size(), &image.w, &image.h, &channels_in_file, 4);
|
||||
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));
|
||||
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) {
|
||||
@@ -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) {
|
||||
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());
|
||||
|
||||
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 'U':
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
log_write("bad command: %c\n", new_entry.command);
|
||||
log_write("bad command: %c\n", it->command);
|
||||
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
|
||||
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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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());
|
||||
|
||||