add stars in homebrew menu (hbmenu feature)

fixes #22
This commit is contained in:
ITotalJustice
2024-12-21 16:30:32 +00:00
parent dd6371997c
commit 55c952a51f
4 changed files with 117 additions and 18 deletions

View File

@@ -42,5 +42,12 @@ function(dkp_fatal_if_not_found var package)
endif()
endfunction(dkp_fatal_if_not_found var package)
# disable exceptions and rtti in order to shrink final binary size.
add_compile_options(
"$<$<COMPILE_LANGUAGE:C>:-fno-exceptions>"
"$<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions>"
"$<$<COMPILE_LANGUAGE:CXX>:-fno-rtti>"
)
add_subdirectory(hbl)
add_subdirectory(sphaira)

View File

@@ -4,6 +4,7 @@
#include <vector>
#include <string>
#include <span>
#include <optional>
#include "fs.hpp"
namespace sphaira {
@@ -28,6 +29,7 @@ struct NroEntry {
int image{}; // nvg image
int x,y,w,h{}; // image
bool is_nacp_valid{};
std::optional<bool> has_star{std::nullopt};
auto GetName() const -> const char* {
return nacp.lang[0].name;

View File

@@ -9,8 +9,11 @@ namespace sphaira::ui::menu::homebrew {
enum SortType {
SortType_Updated,
SortType_Size,
SortType_Alphabetical,
SortType_Size,
SortType_UpdatedStar,
SortType_AlphabeticalStar,
SortType_SizeStar,
};
enum OrderType {
@@ -36,6 +39,10 @@ struct Menu final : MenuBase {
return m_entries;
}
auto IsStarEnabled() -> bool {
return m_sort.Get() >= SortType_UpdatedStar;
}
static Result InstallHomebrew(const fs::FsPath& path, const NacpStruct& nacp, const std::vector<u8>& icon);
static Result InstallHomebrewFromPath(const fs::FsPath& path);
@@ -46,9 +53,9 @@ private:
std::size_t m_start{};
std::size_t m_index{}; // where i am in the array
option::OptionLong m_sort{INI_SECTION, "sort", SortType::SortType_Updated};
option::OptionLong m_sort{INI_SECTION, "sort", SortType::SortType_AlphabeticalStar};
option::OptionLong m_order{INI_SECTION, "order", OrderType::OrderType_Decending};
option::OptionBool m_hide_sphaira{INI_SECTION, "hide_sphaira", false};}
;
option::OptionBool m_hide_sphaira{INI_SECTION, "hide_sphaira", false};
};
} // namespace sphaira::ui::menu::homebrew

View File

@@ -17,6 +17,12 @@
namespace sphaira::ui::menu::homebrew {
namespace {
auto GenerateStarPath(const fs::FsPath& nro_path) -> fs::FsPath {
fs::FsPath out{};
const auto dilem = std::strrchr(nro_path.s, '/');
std::snprintf(out, sizeof(out), "%.*s.%s.star", dilem - nro_path.s + 1, nro_path.s, dilem + 1);
return out;
}
} // namespace
@@ -74,8 +80,11 @@ Menu::Menu() : MenuBase{"Homebrew"_i18n} {
SidebarEntryArray::Items sort_items;
sort_items.push_back("Updated"_i18n);
sort_items.push_back("Size"_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("Decending"_i18n);
@@ -90,6 +99,10 @@ Menu::Menu() : MenuBase{"Homebrew"_i18n} {
m_order.Set(index_out);
SortAndFindLastFile();
}, m_order.Get()));
options->Add(std::make_shared<SidebarEntryBool>("Hide Sphaira"_i18n, m_hide_sphaira.Get(), [this](bool& enable){
m_hide_sphaira.Set(enable);
}, "Enabled"_i18n, "Disabled"_i18n));
}));
#if 0
@@ -113,10 +126,6 @@ Menu::Menu() : MenuBase{"Homebrew"_i18n} {
));
}, true));
options->Add(std::make_shared<SidebarEntryBool>("Hide Sphaira"_i18n, m_hide_sphaira.Get(), [this](bool& enable){
m_hide_sphaira.Set(enable);
}, "Enabled"_i18n, "Disabled"_i18n));
options->Add(std::make_shared<SidebarEntryCallback>("Install Forwarder"_i18n, [this](){
App::Push(std::make_shared<OptionBox>(
"WARNING: Installing forwarders will lead to a ban!"_i18n,
@@ -151,6 +160,7 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
const u64 max_entry_display = 9;
const u64 nro_total = m_entries.size();
const u64 cursor_pos = m_index;
fs::FsNativeSd fs;
// only draw scrollbar if needed
if (nro_total > max_entry_display) {
@@ -187,18 +197,18 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
nvgSave(vg);
nvgScissor(vg, x, y, w - 30.f, h); // clip
{
bool has_star = false;
if (IsStarEnabled()) {
if (!e.has_star.has_value()) {
e.has_star = fs.FileExists(GenerateStarPath(e.path));
}
has_star = e.has_star.value();
}
const float font_size = 18;
const float diff = 32;
#if 1
gfx::drawTextArgs(vg, x + 148, y + 45, font_size, NVG_ALIGN_LEFT, theme->elements[text_id].colour, "%s", e.GetName());
gfx::drawTextArgs(vg, x + 148, y + 45, font_size, NVG_ALIGN_LEFT, theme->elements[text_id].colour, "%s%s", has_star ? "\u2605 " : "", e.GetName());
gfx::drawTextArgs(vg, x + 148, y + 80, font_size, NVG_ALIGN_LEFT, theme->elements[text_id].colour, e.GetAuthor());
gfx::drawTextArgs(vg, x + 148, y + 115, font_size, NVG_ALIGN_LEFT, theme->elements[text_id].colour, e.GetDisplayVersion());
#else
gfx::drawTextArgs(vg, x + 148, y + 35, font_size, NVG_ALIGN_LEFT, theme->elements[text_id].colour, "%s", e.GetName());
gfx::drawTextArgs(vg, x + 148, y + 35 + (diff * 1), font_size, NVG_ALIGN_LEFT, theme->elements[text_id].colour, e.GetAuthor());
gfx::drawTextArgs(vg, x + 148, y + 35 + (diff * 2), font_size, NVG_ALIGN_LEFT, theme->elements[text_id].colour, e.GetDisplayVersion());
// gfx::drawTextArgs(vg, x + 148, y + 110, font_size, NVG_ALIGN_LEFT, theme->elements[text_id].colour, "PlayCount: %u", e.hbini.launch_count);
#endif
}
nvgRestore(vg);
}
@@ -219,6 +229,26 @@ void Menu::SetIndex(std::size_t index) {
}
const auto& e = m_entries[m_index];
if (IsStarEnabled()) {
const auto star_path = GenerateStarPath(m_entries[m_index].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());
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());
SortAndFindLastFile();
}});
}
} else {
RemoveAction(Button::R3);
}
// TimeCalendarTime caltime;
// timeToCalendarTimeWithMyRule()
// todo: fix GetFileTimeStampRaw being different to timeGetCurrentTime
@@ -276,12 +306,31 @@ void Menu::ScanHomebrew() {
}
void Menu::Sort() {
if (IsStarEnabled()) {
fs::FsNativeSd fs;
fs::FsPath star_path;
for (auto& p : m_entries) {
p.has_star = fs.FileExists(GenerateStarPath(p.path));
if (p.has_star) {
log_write("found star: %s\n", p.path.s);
} else {
log_write("no star: %s\n", p.path.s);
}
}
}
// returns true if lhs should be before rhs
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 {
switch (sort) {
case SortType_UpdatedStar:
if (lhs.has_star.value() && !rhs.has_star.value()) {
return true;
} else if (!lhs.has_star.value() && rhs.has_star.value()) {
return false;
}
case SortType_Updated: {
auto lhs_timestamp = lhs.hbini.timestamp;
auto rhs_timestamp = rhs.hbini.timestamp;
@@ -300,6 +349,13 @@ void Menu::Sort() {
return lhs_timestamp < rhs_timestamp;
}
} break;
case SortType_SizeStar:
if (lhs.has_star.value() && !rhs.has_star.value()) {
return true;
} else if (!lhs.has_star.value() && rhs.has_star.value()) {
return false;
}
case SortType_Size: {
if (lhs.size == rhs.size) {
return strcasecmp(lhs.GetName(), rhs.GetName()) < 0;
@@ -309,6 +365,13 @@ void Menu::Sort() {
return lhs.size < rhs.size;
}
} break;
case SortType_AlphabeticalStar:
if (lhs.has_star.value() && !rhs.has_star.value()) {
return true;
} else if (!lhs.has_star.value() && rhs.has_star.value()) {
return false;
}
case SortType_Alphabetical: {
if (order == OrderType_Decending) {
return strcasecmp(lhs.GetName(), rhs.GetName()) < 0;
@@ -325,7 +388,27 @@ void Menu::Sort() {
}
void Menu::SortAndFindLastFile() {
const auto path = m_entries[m_index].path;
Sort();
SetIndex(0);
s64 index = -1;
for (u64 i = 0; i < m_entries.size(); i++) {
if (path == m_entries[i].path) {
index = i;
break;
}
}
if (index >= 0) {
// guesstimate where the position is
if (index >= 9) {
m_start = (index - 9) / 3 * 3 + 3;
} else {
m_start = 0;
}
SetIndex(index);
}
}
Result Menu::InstallHomebrew(const fs::FsPath& path, const NacpStruct& nacp, const std::vector<u8>& icon) {