option to hide homebrew.
This commit is contained in:
@@ -190,7 +190,7 @@ FetchContent_Declare(yyjson
|
||||
|
||||
FetchContent_Declare(minIni
|
||||
GIT_REPOSITORY https://github.com/ITotalJustice/minIni-nx.git
|
||||
GIT_TAG 11cac8b
|
||||
GIT_TAG 6e952b6
|
||||
)
|
||||
|
||||
FetchContent_Declare(zstd
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace sphaira {
|
||||
|
||||
struct Hbini {
|
||||
u64 timestamp{}; // timestamp of last launch
|
||||
bool hidden{};
|
||||
};
|
||||
|
||||
struct MiniNacp {
|
||||
@@ -61,7 +62,7 @@ auto nro_parse(const fs::FsPath& path, NroEntry& entry) -> Result;
|
||||
* nro found.
|
||||
* this does nothing if nested=false.
|
||||
*/
|
||||
auto nro_scan(const fs::FsPath& path, std::vector<NroEntry>& nros, bool hide_spahira, bool nested = true, bool scan_all_dir = true) -> Result;
|
||||
auto nro_scan(const fs::FsPath& path, std::vector<NroEntry>& nros, bool nested = true, bool scan_all_dir = true) -> Result;
|
||||
|
||||
auto nro_get_icon(const fs::FsPath& path, u64 size, u64 offset) -> std::vector<u8>;
|
||||
auto nro_get_icon(const fs::FsPath& path) -> std::vector<u8>;
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
|
||||
namespace sphaira::ui::menu::homebrew {
|
||||
|
||||
enum Filter {
|
||||
Filter_All,
|
||||
Filter_HideHidden,
|
||||
Filter_MAX,
|
||||
};
|
||||
|
||||
enum SortType {
|
||||
SortType_Updated,
|
||||
SortType_Alphabetical,
|
||||
@@ -43,6 +49,14 @@ struct Menu final : grid::Menu {
|
||||
static Result InstallHomebrew(const fs::FsPath& path, const std::vector<u8>& icon);
|
||||
static Result InstallHomebrewFromPath(const fs::FsPath& path);
|
||||
|
||||
auto GetEntry(s64 i) -> NroEntry& {
|
||||
return m_entries[m_entries_current[i]];
|
||||
}
|
||||
|
||||
auto GetEntry() -> NroEntry& {
|
||||
return GetEntry(m_index);
|
||||
}
|
||||
|
||||
private:
|
||||
void SetIndex(s64 index);
|
||||
void InstallHomebrew();
|
||||
@@ -51,6 +65,7 @@ private:
|
||||
void SortAndFindLastFile(bool scan = false);
|
||||
void FreeEntries();
|
||||
void OnLayoutChange();
|
||||
void DisplayOptions();
|
||||
|
||||
auto IsStarEnabled() -> bool {
|
||||
return m_sort.Get() >= SortType_UpdatedStar;
|
||||
@@ -60,6 +75,9 @@ private:
|
||||
static constexpr inline const char* INI_SECTION = "homebrew";
|
||||
|
||||
std::vector<NroEntry> m_entries{};
|
||||
std::vector<u32> m_entries_index[Filter_MAX]{};
|
||||
std::span<u32> m_entries_current{};
|
||||
|
||||
s64 m_index{}; // where i am in the array
|
||||
std::unique_ptr<List> m_list{};
|
||||
bool m_dirty{};
|
||||
@@ -67,7 +85,7 @@ private:
|
||||
option::OptionLong m_sort{INI_SECTION, "sort", SortType::SortType_AlphabeticalStar};
|
||||
option::OptionLong m_order{INI_SECTION, "order", OrderType::OrderType_Descending};
|
||||
option::OptionLong m_layout{INI_SECTION, "layout", LayoutType::LayoutType_GridDetail};
|
||||
option::OptionBool m_hide_sphaira{INI_SECTION, "hide_sphaira", false};
|
||||
option::OptionBool m_show_hidden{INI_SECTION, "show_hidden", false};
|
||||
};
|
||||
|
||||
} // namespace sphaira::ui::menu::homebrew
|
||||
|
||||
@@ -79,7 +79,7 @@ auto nro_parse_internal(fs::Fs* fs, const fs::FsPath& path, NroEntry& entry) ->
|
||||
// this function is recursive by 1 level deep
|
||||
// if the nro is in switch/folder/folder2/app.nro it will NOT be found
|
||||
// switch/folder/app.nro for example will work fine.
|
||||
auto nro_scan_internal(fs::Fs* fs, const fs::FsPath& path, std::vector<NroEntry>& nros, bool hide_sphaira, bool nested, bool scan_all_dir, bool root) -> Result {
|
||||
auto nro_scan_internal(fs::Fs* fs, const fs::FsPath& path, std::vector<NroEntry>& nros, bool nested, bool scan_all_dir, bool root) -> Result {
|
||||
// we don't need to scan for folders if we are not root
|
||||
u32 dir_open_type = FsDirOpenMode_ReadFiles | FsDirOpenMode_NoFileSize;
|
||||
if (root) {
|
||||
@@ -99,11 +99,6 @@ auto nro_scan_internal(fs::Fs* fs, const fs::FsPath& path, std::vector<NroEntry>
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip self
|
||||
if (hide_sphaira && !strncmp(e.name, "sphaira", strlen("sphaira"))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e.type == FsDirEntryType_Dir) {
|
||||
// assert(!root && "dir should only be scanned on non-root!");
|
||||
fs::FsPath fullpath;
|
||||
@@ -117,7 +112,7 @@ auto nro_scan_internal(fs::Fs* fs, const fs::FsPath& path, std::vector<NroEntry>
|
||||
} else {
|
||||
// slow path...
|
||||
std::snprintf(fullpath, sizeof(fullpath), "%s/%s", path.s, e.name);
|
||||
nro_scan_internal(fs, fullpath, nros, hide_sphaira, nested, scan_all_dir, false);
|
||||
nro_scan_internal(fs, fullpath, nros, nested, scan_all_dir, false);
|
||||
}
|
||||
} else if (e.type == FsDirEntryType_File && std::string_view{e.name}.ends_with(".nro")) {
|
||||
fs::FsPath fullpath;
|
||||
@@ -139,9 +134,9 @@ auto nro_scan_internal(fs::Fs* fs, const fs::FsPath& path, std::vector<NroEntry>
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
auto nro_scan_internal(const fs::FsPath& path, std::vector<NroEntry>& nros, bool hide_sphaira, bool nested, bool scan_all_dir, bool root) -> Result {
|
||||
auto nro_scan_internal(const fs::FsPath& path, std::vector<NroEntry>& nros, bool nested, bool scan_all_dir, bool root) -> Result {
|
||||
fs::FsNativeSd fs;
|
||||
return nro_scan_internal(&fs, path, nros, hide_sphaira, nested, scan_all_dir, root);
|
||||
return nro_scan_internal(&fs, path, nros, nested, scan_all_dir, root);
|
||||
}
|
||||
|
||||
auto nro_get_icon_internal(fs::File* f, u64 size, u64 offset) -> std::vector<u8> {
|
||||
@@ -198,8 +193,8 @@ auto nro_parse(const fs::FsPath& path, NroEntry& entry) -> Result {
|
||||
return nro_parse_internal(&fs, path, entry);
|
||||
}
|
||||
|
||||
auto nro_scan(const fs::FsPath& path, std::vector<NroEntry>& nros, bool hide_sphaira, bool nested, bool scan_all_dir) -> Result {
|
||||
return nro_scan_internal(path, nros, hide_sphaira, nested, scan_all_dir, true);
|
||||
auto nro_scan(const fs::FsPath& path, std::vector<NroEntry>& nros, bool nested, bool scan_all_dir) -> Result {
|
||||
return nro_scan_internal(path, nros, nested, scan_all_dir, true);
|
||||
}
|
||||
|
||||
auto nro_get_icon(const fs::FsPath& path, u64 size, u64 offset) -> std::vector<u8> {
|
||||
|
||||
@@ -8,28 +8,6 @@
|
||||
#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 {
|
||||
@@ -90,9 +68,9 @@ auto OptionBase<T>::LoadFrom(const char* name, const char* value) -> bool {
|
||||
if (m_name == name) {
|
||||
if (m_file) {
|
||||
if constexpr(std::is_same_v<T, bool>) {
|
||||
m_value = getbool(value, m_default_value);
|
||||
m_value = ini_parse_getbool(value, m_default_value);
|
||||
} else if constexpr(std::is_same_v<T, long>) {
|
||||
m_value = getl(value, m_default_value);
|
||||
m_value = ini_parse_getl(value, m_default_value);
|
||||
} else if constexpr(std::is_same_v<T, std::string>) {
|
||||
m_value = value;
|
||||
}
|
||||
|
||||
@@ -53,93 +53,10 @@ Menu::Menu() : grid::Menu{"Homebrew"_i18n, MenuFlag_Tab} {
|
||||
|
||||
this->SetActions(
|
||||
std::make_pair(Button::A, Action{"Launch"_i18n, [this](){
|
||||
nro_launch(m_entries[m_index].path);
|
||||
nro_launch(GetEntry().path);
|
||||
}}),
|
||||
std::make_pair(Button::X, Action{"Options"_i18n, [this](){
|
||||
auto options = std::make_unique<Sidebar>("Homebrew Options"_i18n, Sidebar::Side::RIGHT);
|
||||
ON_SCOPE_EXIT(App::Push(std::move(options)));
|
||||
|
||||
if (m_entries.size()) {
|
||||
options->Add<SidebarEntryCallback>("Sort By"_i18n, [this](){
|
||||
auto options = std::make_unique<Sidebar>("Sort Options"_i18n, Sidebar::Side::RIGHT);
|
||||
ON_SCOPE_EXIT(App::Push(std::move(options)));
|
||||
|
||||
SidebarEntryArray::Items sort_items;
|
||||
sort_items.push_back("Updated"_i18n);
|
||||
sort_items.push_back("Alphabetical"_i18n);
|
||||
sort_items.push_back("Size"_i18n);
|
||||
sort_items.push_back("Updated (Star)"_i18n);
|
||||
sort_items.push_back("Alphabetical (Star)"_i18n);
|
||||
sort_items.push_back("Size (Star)"_i18n);
|
||||
|
||||
SidebarEntryArray::Items order_items;
|
||||
order_items.push_back("Descending"_i18n);
|
||||
order_items.push_back("Ascending"_i18n);
|
||||
|
||||
SidebarEntryArray::Items layout_items;
|
||||
layout_items.push_back("List"_i18n);
|
||||
layout_items.push_back("Icon"_i18n);
|
||||
layout_items.push_back("Grid"_i18n);
|
||||
|
||||
options->Add<SidebarEntryArray>("Sort"_i18n, sort_items, [this, sort_items](s64& index_out){
|
||||
m_sort.Set(index_out);
|
||||
SortAndFindLastFile();
|
||||
}, m_sort.Get());
|
||||
|
||||
options->Add<SidebarEntryArray>("Order"_i18n, order_items, [this, order_items](s64& index_out){
|
||||
m_order.Set(index_out);
|
||||
SortAndFindLastFile();
|
||||
}, m_order.Get());
|
||||
|
||||
options->Add<SidebarEntryArray>("Layout"_i18n, layout_items, [this](s64& index_out){
|
||||
m_layout.Set(index_out);
|
||||
OnLayoutChange();
|
||||
}, m_layout.Get());
|
||||
|
||||
options->Add<SidebarEntryBool>("Hide Sphaira"_i18n, m_hide_sphaira.Get(), [this](bool& enable){
|
||||
m_hide_sphaira.Set(enable);
|
||||
});
|
||||
});
|
||||
|
||||
#if 0
|
||||
options->Add<SidebarEntryCallback>("Info"_i18n, [this](){
|
||||
|
||||
});
|
||||
#endif
|
||||
|
||||
options->Add<SidebarEntryCallback>("Delete"_i18n, [this](){
|
||||
const auto buf = "Are you sure you want to delete "_i18n + m_entries[m_index].path.toString() + "?";
|
||||
App::Push<OptionBox>(
|
||||
buf,
|
||||
"Back"_i18n, "Delete"_i18n, 1, [this](auto op_index){
|
||||
if (op_index && *op_index) {
|
||||
if (R_SUCCEEDED(fs::FsNativeSd().DeleteFile(m_entries[m_index].path))) {
|
||||
FreeEntry(App::GetVg(), m_entries[m_index]);
|
||||
m_entries.erase(m_entries.begin() + m_index);
|
||||
SetIndex(m_index ? m_index - 1 : 0);
|
||||
}
|
||||
}
|
||||
}, m_entries[m_index].image
|
||||
);
|
||||
}, true);
|
||||
|
||||
auto forwarder_entry = options->Add<SidebarEntryCallback>("Install Forwarder"_i18n, [this](){
|
||||
if (App::GetInstallPrompt()) {
|
||||
App::Push<OptionBox>(
|
||||
"WARNING: Installing forwarders will lead to a ban!"_i18n,
|
||||
"Back"_i18n, "Install"_i18n, 0, [this](auto op_index){
|
||||
if (op_index && *op_index) {
|
||||
InstallHomebrew();
|
||||
}
|
||||
}, m_entries[m_index].image
|
||||
);
|
||||
} else {
|
||||
InstallHomebrew();
|
||||
}
|
||||
}, true);
|
||||
|
||||
forwarder_entry->Depends(App::GetInstallEnable, i18n::get(App::INSTALL_DEPENDS_STR));
|
||||
}
|
||||
DisplayOptions();
|
||||
}})
|
||||
);
|
||||
|
||||
@@ -179,8 +96,9 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
const int image_load_max = 2;
|
||||
int image_load_count = 0;
|
||||
|
||||
m_list->Draw(vg, theme, m_entries.size(), [this, &image_load_count](auto* vg, auto* theme, auto v, auto pos) {
|
||||
auto& e = m_entries[pos];
|
||||
m_list->Draw(vg, theme, m_entries_current.size(), [this, &image_load_count](auto* vg, auto* theme, auto v, auto pos) {
|
||||
const auto index = m_entries_current[pos];
|
||||
auto& e = m_entries[index];
|
||||
|
||||
// lazy load image
|
||||
if (image_load_count < image_load_max) {
|
||||
@@ -240,17 +158,17 @@ void Menu::SetIndex(s64 index) {
|
||||
}
|
||||
|
||||
if (IsStarEnabled()) {
|
||||
const auto star_path = GenerateStarPath(m_entries[m_index].path);
|
||||
const auto star_path = GenerateStarPath(GetEntry().path);
|
||||
if (fs::FsNativeSd().FileExists(star_path)) {
|
||||
SetAction(Button::R3, Action{"Unstar"_i18n, [this](){
|
||||
fs::FsNativeSd().DeleteFile(GenerateStarPath(m_entries[m_index].path));
|
||||
App::Notify("Unstarred "_i18n + m_entries[m_index].GetName());
|
||||
fs::FsNativeSd().DeleteFile(GenerateStarPath(GetEntry().path));
|
||||
App::Notify("Unstarred "_i18n + GetEntry().GetName());
|
||||
SortAndFindLastFile();
|
||||
}});
|
||||
} else {
|
||||
SetAction(Button::R3, Action{"Star"_i18n, [this](){
|
||||
fs::FsNativeSd().CreateFile(GenerateStarPath(m_entries[m_index].path));
|
||||
App::Notify("Starred "_i18n + m_entries[m_index].GetName());
|
||||
fs::FsNativeSd().CreateFile(GenerateStarPath(GetEntry().path));
|
||||
App::Notify("Starred "_i18n + GetEntry().GetName());
|
||||
SortAndFindLastFile();
|
||||
}});
|
||||
}
|
||||
@@ -263,19 +181,19 @@ void Menu::SetIndex(s64 index) {
|
||||
// todo: fix GetFileTimeStampRaw being different to timeGetCurrentTime
|
||||
// log_write("name: %s hbini.ts: %lu file.ts: %lu smaller: %s\n", e.GetName(), e.hbini.timestamp, e.timestamp.modified, e.hbini.timestamp < e.timestamp.modified ? "true" : "false");
|
||||
|
||||
SetTitleSubHeading(m_entries[m_index].path);
|
||||
this->SetSubHeading(std::to_string(m_index + 1) + " / " + std::to_string(m_entries.size()));
|
||||
SetTitleSubHeading(GetEntry().path);
|
||||
this->SetSubHeading(std::to_string(m_index + 1) + " / " + std::to_string(m_entries_current.size()));
|
||||
}
|
||||
|
||||
void Menu::InstallHomebrew() {
|
||||
const auto& nro = m_entries[m_index];
|
||||
const auto& nro = GetEntry();
|
||||
InstallHomebrew(nro.path, nro_get_icon(nro.path, nro.icon_size, nro.icon_offset));
|
||||
}
|
||||
|
||||
void Menu::ScanHomebrew() {
|
||||
TimeStamp ts;
|
||||
FreeEntries();
|
||||
nro_scan("/switch", m_entries, m_hide_sphaira.Get());
|
||||
nro_scan("/switch", m_entries);
|
||||
log_write("nros found: %zu time_taken: %.2f\n", m_entries.size(), ts.GetSecondsD());
|
||||
|
||||
struct IniUser {
|
||||
@@ -301,7 +219,9 @@ void Menu::ScanHomebrew() {
|
||||
|
||||
if (user->ini) {
|
||||
if (!strcmp(Key, "timestamp")) {
|
||||
user->ini->timestamp = atoi(Value);
|
||||
user->ini->timestamp = ini_parse_getl(Value, 0);
|
||||
} else if (!strcmp(Key, "hidden")) {
|
||||
user->ini->hidden = ini_parse_getbool(Value, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,6 +229,21 @@ void Menu::ScanHomebrew() {
|
||||
return 1;
|
||||
}, &ini_user, App::PLAYLOG_PATH);
|
||||
|
||||
// pre-allocate the max size.
|
||||
for (auto& index : m_entries_index) {
|
||||
index.reserve(m_entries.size());
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < m_entries.size(); i++) {
|
||||
auto& e = m_entries[i];
|
||||
|
||||
m_entries_index[Filter_All].emplace_back(i);
|
||||
|
||||
if (!e.hbini.hidden) {
|
||||
m_entries_index[Filter_HideHidden].emplace_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
this->Sort();
|
||||
SetIndex(0);
|
||||
m_dirty = false;
|
||||
@@ -327,7 +262,10 @@ void Menu::Sort() {
|
||||
const auto sort = m_sort.Get();
|
||||
const auto order = m_order.Get();
|
||||
|
||||
const auto sorter = [this, sort, order](const NroEntry& lhs, const NroEntry& rhs) -> bool {
|
||||
const auto sorter = [this, sort, order](u32 _lhs, u32 _rhs) -> bool {
|
||||
const auto& lhs = m_entries[_lhs];
|
||||
const auto& rhs = m_entries[_rhs];
|
||||
|
||||
const auto name_cmp = [order](const NroEntry& lhs, const NroEntry& rhs) -> bool {
|
||||
auto r = strcasecmp(lhs.GetName(), rhs.GetName());
|
||||
if (!r) {
|
||||
@@ -403,11 +341,18 @@ void Menu::Sort() {
|
||||
std::unreachable();
|
||||
};
|
||||
|
||||
std::sort(m_entries.begin(), m_entries.end(), sorter);
|
||||
if (m_show_hidden.Get()) {
|
||||
m_entries_current = m_entries_index[Filter_All];
|
||||
} else {
|
||||
m_entries_current = m_entries_index[Filter_HideHidden];
|
||||
}
|
||||
|
||||
std::sort(m_entries_current.begin(), m_entries_current.end(), sorter);
|
||||
}
|
||||
|
||||
void Menu::SortAndFindLastFile(bool scan) {
|
||||
const auto path = m_entries[m_index].path;
|
||||
const auto path = GetEntry().path;
|
||||
|
||||
if (scan) {
|
||||
ScanHomebrew();
|
||||
} else {
|
||||
@@ -416,8 +361,8 @@ void Menu::SortAndFindLastFile(bool scan) {
|
||||
SetIndex(0);
|
||||
|
||||
s64 index = -1;
|
||||
for (u64 i = 0; i < m_entries.size(); i++) {
|
||||
if (path == m_entries[i].path) {
|
||||
for (u64 i = 0; i < m_entries_current.size(); i++) {
|
||||
if (path == GetEntry(i).path) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
@@ -444,6 +389,9 @@ void Menu::FreeEntries() {
|
||||
}
|
||||
|
||||
m_entries.clear();
|
||||
for (auto& e : m_entries_index) {
|
||||
e.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::OnLayoutChange() {
|
||||
@@ -463,4 +411,104 @@ Result Menu::InstallHomebrewFromPath(const fs::FsPath& path) {
|
||||
return InstallHomebrew(path, nro_get_icon(path));
|
||||
}
|
||||
|
||||
void Menu::DisplayOptions() {
|
||||
auto options = std::make_unique<Sidebar>("Homebrew Options"_i18n, Sidebar::Side::RIGHT);
|
||||
ON_SCOPE_EXIT(App::Push(std::move(options)));
|
||||
|
||||
options->Add<SidebarEntryCallback>("Sort By"_i18n, [this](){
|
||||
auto options = std::make_unique<Sidebar>("Sort Options"_i18n, Sidebar::Side::RIGHT);
|
||||
ON_SCOPE_EXIT(App::Push(std::move(options)));
|
||||
|
||||
SidebarEntryArray::Items sort_items;
|
||||
sort_items.push_back("Updated"_i18n);
|
||||
sort_items.push_back("Alphabetical"_i18n);
|
||||
sort_items.push_back("Size"_i18n);
|
||||
sort_items.push_back("Updated (Star)"_i18n);
|
||||
sort_items.push_back("Alphabetical (Star)"_i18n);
|
||||
sort_items.push_back("Size (Star)"_i18n);
|
||||
|
||||
SidebarEntryArray::Items order_items;
|
||||
order_items.push_back("Descending"_i18n);
|
||||
order_items.push_back("Ascending"_i18n);
|
||||
|
||||
SidebarEntryArray::Items layout_items;
|
||||
layout_items.push_back("List"_i18n);
|
||||
layout_items.push_back("Icon"_i18n);
|
||||
layout_items.push_back("Grid"_i18n);
|
||||
|
||||
options->Add<SidebarEntryArray>("Sort"_i18n, sort_items, [this, sort_items](s64& index_out){
|
||||
m_sort.Set(index_out);
|
||||
SortAndFindLastFile();
|
||||
}, m_sort.Get());
|
||||
|
||||
options->Add<SidebarEntryArray>("Order"_i18n, order_items, [this, order_items](s64& index_out){
|
||||
m_order.Set(index_out);
|
||||
SortAndFindLastFile();
|
||||
}, m_order.Get(), "Display entries in Ascending or Descending order."_i18n);
|
||||
|
||||
options->Add<SidebarEntryArray>("Layout"_i18n, layout_items, [this](s64& index_out){
|
||||
m_layout.Set(index_out);
|
||||
OnLayoutChange();
|
||||
}, m_layout.Get(), "Change the layout to List, Icon and Grid."_i18n);
|
||||
|
||||
options->Add<SidebarEntryBool>("Show hidden"_i18n, m_show_hidden.Get(), [this](bool& enable){
|
||||
m_show_hidden.Set(enable);
|
||||
SortAndFindLastFile();
|
||||
}, "Shows all hidden homebrew."_i18n);
|
||||
});
|
||||
|
||||
if (!m_entries_current.empty()) {
|
||||
#if 0
|
||||
options->Add<SidebarEntryCallback>("Info"_i18n, [this](){
|
||||
|
||||
});
|
||||
#endif
|
||||
|
||||
options->Add<SidebarEntryBool>("Hide"_i18n, GetEntry().hbini.hidden, [this](bool& v_out){
|
||||
ini_putl(GetEntry().path, "hidden", v_out, App::PLAYLOG_PATH);
|
||||
ScanHomebrew();
|
||||
App::PopToMenu();
|
||||
}, "Hides the selected homebrew.\n\n"
|
||||
"To Unhide homebrew, enable \"Show hidden\" in the sort options."_i18n);
|
||||
|
||||
options->Add<SidebarEntryCallback>("Delete"_i18n, [this](){
|
||||
const auto buf = "Are you sure you want to delete "_i18n + GetEntry().path.toString() + "?";
|
||||
App::Push<OptionBox>(
|
||||
buf,
|
||||
"Back"_i18n, "Delete"_i18n, 1, [this](auto op_index){
|
||||
if (op_index && *op_index) {
|
||||
if (R_SUCCEEDED(fs::FsNativeSd().DeleteFile(GetEntry().path))) {
|
||||
// todo: remove from list using real index here.
|
||||
FreeEntry(App::GetVg(), GetEntry());
|
||||
ScanHomebrew();
|
||||
// m_entries.erase(m_entries.begin() + m_index);
|
||||
// SetIndex(m_index ? m_index - 1 : 0);
|
||||
App::PopToMenu();
|
||||
}
|
||||
}
|
||||
}, GetEntry().image
|
||||
);
|
||||
}, "Perminately delete the selected homebrew.\n\n"
|
||||
"Files and folders created by the homebrew will still remain. "
|
||||
"Use the FileBrowser to delete them."_i18n);
|
||||
|
||||
auto forwarder_entry = options->Add<SidebarEntryCallback>("Install Forwarder"_i18n, [this](){
|
||||
if (App::GetInstallPrompt()) {
|
||||
App::Push<OptionBox>(
|
||||
"WARNING: Installing forwarders will lead to a ban!"_i18n,
|
||||
"Back"_i18n, "Install"_i18n, 0, [this](auto op_index){
|
||||
if (op_index && *op_index) {
|
||||
InstallHomebrew();
|
||||
}
|
||||
}, GetEntry().image
|
||||
);
|
||||
} else {
|
||||
InstallHomebrew();
|
||||
}
|
||||
}, true);
|
||||
|
||||
forwarder_entry->Depends(App::GetInstallEnable, i18n::get(App::INSTALL_DEPENDS_STR));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sphaira::ui::menu::homebrew
|
||||
|
||||
Reference in New Issue
Block a user