filebrowser: add forwarder creator.
This commit is contained in:
@@ -140,7 +140,7 @@ public:
|
||||
m_callback = cb;
|
||||
}
|
||||
|
||||
auto GetValue() const {
|
||||
auto GetValue() const -> const std::string& {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
@@ -155,14 +155,12 @@ private:
|
||||
|
||||
class SidebarEntryTextInput final : public SidebarEntryTextBase {
|
||||
public:
|
||||
explicit SidebarEntryTextInput(const std::string& title, const std::string& value, const std::string& guide = {}, const std::string& info = "");
|
||||
|
||||
void SetGuide(const std::string& guide) {
|
||||
m_guide = guide;
|
||||
}
|
||||
explicit SidebarEntryTextInput(const std::string& title, const std::string& value, const std::string& guide = {}, s64 len_min = -1, s64 len_max = FS_MAX_PATH, const std::string& info = "");
|
||||
|
||||
private:
|
||||
std::string m_guide;
|
||||
const std::string m_guide;
|
||||
const s64 m_len_min;
|
||||
const s64 m_len_max;
|
||||
};
|
||||
|
||||
class SidebarEntryFilePicker final : public SidebarEntryTextBase {
|
||||
@@ -178,7 +176,7 @@ private:
|
||||
std::vector<std::string> m_filter{};
|
||||
};
|
||||
|
||||
class Sidebar final : public Widget {
|
||||
class Sidebar : public Widget {
|
||||
public:
|
||||
enum class Side { LEFT, RIGHT };
|
||||
using Items = std::vector<std::unique_ptr<SidebarEntryBase>>;
|
||||
@@ -197,8 +195,8 @@ public:
|
||||
auto Add(std::unique_ptr<SidebarEntryBase>&& entry) -> SidebarEntryBase*;
|
||||
|
||||
template<DerivedFromSidebarBase T, typename... Args>
|
||||
auto Add(Args&&... args) -> SidebarEntryBase* {
|
||||
return Add(std::make_unique<T>(std::forward<Args>(args)...));
|
||||
auto Add(Args&&... args) -> T* {
|
||||
return (T*)Add(std::make_unique<T>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -843,7 +843,7 @@ auto install_forwader_internal(ui::ProgressBox* pbox, OwoConfig& config, NcmStor
|
||||
pbox->SetImageDataConst(config.icon);
|
||||
|
||||
R_UNLESS(!config.nro_path.empty(), Result_OwoBadArgs);
|
||||
// R_UNLESS(!config.icon.empty(), OwoError_BadArgs);
|
||||
R_UNLESS(!config.icon.empty(), Result_OwoBadArgs);
|
||||
|
||||
R_TRY(splCryptoInitialize());
|
||||
ON_SCOPE_EXIT(splCryptoExit());
|
||||
|
||||
@@ -38,12 +38,33 @@
|
||||
#include <span>
|
||||
#include <utility>
|
||||
#include <ranges>
|
||||
// #include <stack>
|
||||
#include <expected>
|
||||
|
||||
namespace sphaira::ui::menu::filebrowser {
|
||||
namespace {
|
||||
|
||||
using RomDatabaseIndexs = std::vector<size_t>;
|
||||
|
||||
struct ForwarderForm final : public Sidebar {
|
||||
explicit ForwarderForm(const FileAssocEntry& assoc, const RomDatabaseIndexs& db_indexs, const FileEntry& entry, const fs::FsPath& arg_path);
|
||||
|
||||
private:
|
||||
auto LoadNroMeta() -> Result;
|
||||
|
||||
private:
|
||||
const FileAssocEntry m_assoc;
|
||||
const RomDatabaseIndexs m_db_indexs;
|
||||
const fs::FsPath m_arg_path;
|
||||
|
||||
NroEntry m_nro{};
|
||||
NacpStruct m_nacp{};
|
||||
|
||||
SidebarEntryTextInput* m_name{};
|
||||
SidebarEntryTextInput* m_author{};
|
||||
SidebarEntryTextInput* m_version{};
|
||||
SidebarEntryFilePicker* m_icon{};
|
||||
};
|
||||
|
||||
constinit UEvent g_change_uevent;
|
||||
|
||||
constexpr FsEntry FS_ENTRY_DEFAULT{
|
||||
@@ -176,7 +197,6 @@ auto IsExtension(std::string_view ext1, std::string_view ext2) -> bool {
|
||||
// tries to find database path using folder name
|
||||
// names are taken from retropie
|
||||
// retroarch database names can also be used
|
||||
using RomDatabaseIndexs = std::vector<size_t>;
|
||||
auto GetRomDatabaseFromPath(std::string_view path) -> RomDatabaseIndexs {
|
||||
if (path.length() <= 1) {
|
||||
return {};
|
||||
@@ -216,7 +236,7 @@ auto GetRomDatabaseFromPath(std::string_view path) -> RomDatabaseIndexs {
|
||||
}
|
||||
|
||||
//
|
||||
auto GetRomIcon(fs::Fs* fs, ProgressBox* pbox, std::string filename, const RomDatabaseIndexs& db_indexs, const NroEntry& nro) {
|
||||
auto GetRomIcon(std::string filename, const RomDatabaseIndexs& db_indexs, const NroEntry& nro) {
|
||||
// if no db entries, use nro icon
|
||||
if (db_indexs.empty()) {
|
||||
log_write("using nro image\n");
|
||||
@@ -234,8 +254,6 @@ auto GetRomIcon(fs::Fs* fs, ProgressBox* pbox, std::string filename, const RomDa
|
||||
}
|
||||
}
|
||||
|
||||
#define RA_BOXART_URL "https://thumbnails.libretro.com/"
|
||||
#define GH_BOXART_URL "https://raw.githubusercontent.com/libretro-thumbnails/"
|
||||
#define RA_BOXART_NAME "/Named_Boxarts/"
|
||||
#define RA_THUMBNAIL_PATH "/retroarch/thumbnails/"
|
||||
#define RA_BOXART_EXT ".png"
|
||||
@@ -249,42 +267,15 @@ auto GetRomIcon(fs::Fs* fs, ProgressBox* pbox, std::string filename, const RomDa
|
||||
}
|
||||
}
|
||||
|
||||
std::string filename_gh;
|
||||
filename_gh.reserve(filename.size());
|
||||
for (auto c : filename) {
|
||||
if (c == ' ') {
|
||||
filename_gh += "%20";
|
||||
} else {
|
||||
filename_gh.push_back(c);
|
||||
}
|
||||
}
|
||||
|
||||
const std::string thumbnail_path = system_name + RA_BOXART_NAME + filename + RA_BOXART_EXT;
|
||||
const std::string ra_thumbnail_path = RA_THUMBNAIL_PATH + thumbnail_path;
|
||||
const std::string ra_thumbnail_url = RA_BOXART_URL + thumbnail_path;
|
||||
const std::string gh_thumbnail_url = GH_BOXART_URL + system_name_gh + RA_BOXART_NAME + filename_gh + RA_BOXART_EXT;
|
||||
|
||||
log_write("starting image convert on: %s\n", ra_thumbnail_path.c_str());
|
||||
|
||||
// try and find icon locally
|
||||
if (!pbox->ShouldExit()) {
|
||||
pbox->NewTransfer("Trying to load "_i18n + ra_thumbnail_path);
|
||||
std::vector<u8> image_file;
|
||||
if (R_SUCCEEDED(fs->read_entire_file(ra_thumbnail_path.c_str(), image_file))) {
|
||||
return image_file;
|
||||
}
|
||||
}
|
||||
|
||||
// try and download icon
|
||||
if (!pbox->ShouldExit()) {
|
||||
pbox->NewTransfer("Downloading "_i18n + gh_thumbnail_url);
|
||||
const auto result = curl::Api().ToMemory(
|
||||
curl::Url{gh_thumbnail_url},
|
||||
curl::OnProgress{pbox->OnDownloadProgressCallback()}
|
||||
);
|
||||
|
||||
if (result.success && !result.data.empty()) {
|
||||
return result.data;
|
||||
}
|
||||
std::vector<u8> image_file;
|
||||
if (R_SUCCEEDED(fs::FsNativeSd().read_entire_file(ra_thumbnail_path, image_file))) {
|
||||
return image_file;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,6 +284,116 @@ auto GetRomIcon(fs::Fs* fs, ProgressBox* pbox, std::string filename, const RomDa
|
||||
return nro_get_icon(nro.path, nro.icon_size, nro.icon_offset);
|
||||
}
|
||||
|
||||
ForwarderForm::ForwarderForm(const FileAssocEntry& assoc, const RomDatabaseIndexs& db_indexs, const FileEntry& entry, const fs::FsPath& arg_path)
|
||||
: Sidebar{"Forwarder Creation", Side::RIGHT}
|
||||
, m_assoc{assoc}
|
||||
, m_db_indexs{db_indexs}
|
||||
, m_arg_path{arg_path} {
|
||||
log_write("parsing nro\n");
|
||||
if (R_FAILED(LoadNroMeta())) {
|
||||
App::Notify("Failed to parse nro"_i18n);
|
||||
SetPop();
|
||||
return;
|
||||
}
|
||||
|
||||
log_write("got nro data\n");
|
||||
auto file_name = m_assoc.use_base_name ? entry.GetName() : entry.GetInternalName();
|
||||
|
||||
if (auto pos = file_name.find_last_of('.'); pos != std::string::npos) {
|
||||
log_write("got filename\n");
|
||||
file_name = file_name.substr(0, pos);
|
||||
log_write("got filename2: %s\n\n", file_name.c_str());
|
||||
}
|
||||
|
||||
const auto name = m_nro.nacp.lang.name + std::string{" | "} + file_name;
|
||||
const auto author = m_nacp.lang[0].author;
|
||||
const auto version = m_nacp.display_version;
|
||||
const auto icon = m_assoc.path;
|
||||
|
||||
m_name = this->Add<SidebarEntryTextInput>(
|
||||
"Name", name, "", -1, sizeof(NacpLanguageEntry::name) - 1,
|
||||
"Set the name of the application"_i18n
|
||||
);
|
||||
|
||||
m_author = this->Add<SidebarEntryTextInput>(
|
||||
"Author", author, "", -1, sizeof(NacpLanguageEntry::author) - 1,
|
||||
"Set the author of the application"_i18n
|
||||
);
|
||||
|
||||
m_version = this->Add<SidebarEntryTextInput>(
|
||||
"Version", version, "", -1, sizeof(NacpStruct::display_version) - 1,
|
||||
"Set the display version of the application"_i18n
|
||||
);
|
||||
|
||||
const std::vector<std::string> filters{".nro", ".png", ".jpg"};
|
||||
m_icon = this->Add<SidebarEntryFilePicker>(
|
||||
"Icon", icon, filters,
|
||||
"Set the path to the icon for the forwarder"_i18n
|
||||
);
|
||||
|
||||
auto callback = this->Add<SidebarEntryCallback>("Create", [this, file_name](){
|
||||
OwoConfig config{};
|
||||
config.nro_path = m_assoc.path.toString();
|
||||
config.args = nro_add_arg_file(m_arg_path);
|
||||
config.nacp = m_nacp;
|
||||
|
||||
// patch the name.
|
||||
config.name = m_name->GetValue();
|
||||
|
||||
// patch the author.
|
||||
config.author = m_author->GetValue();
|
||||
|
||||
// patch the display version.
|
||||
std::snprintf(config.nacp.display_version, sizeof(config.nacp.display_version), "%s", m_version->GetValue().c_str());
|
||||
|
||||
// load icon fron nro or image.
|
||||
if (m_icon->GetValue().ends_with(".nro")) {
|
||||
// if path was left as the default, try and load the icon from rom db.
|
||||
if (config.nro_path == m_icon->GetValue()) {
|
||||
config.icon = GetRomIcon(file_name, m_db_indexs, m_nro);
|
||||
} else {
|
||||
config.icon = nro_get_icon(m_icon->GetValue());
|
||||
}
|
||||
} else {
|
||||
// try and read icon file into memory, bail if this fails.
|
||||
const auto rc = fs::FsStdio().read_entire_file(m_icon->GetValue(), config.icon);
|
||||
if (R_FAILED(rc)) {
|
||||
App::PushErrorBox(rc, "Failed to load icon");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if this is a rom, load intro logo.
|
||||
if (!m_db_indexs.empty()) {
|
||||
fs::FsNativeSd().read_entire_file("/config/sphaira/logo/rom/NintendoLogo.png", config.logo);
|
||||
fs::FsNativeSd().read_entire_file("/config/sphaira/logo/rom/StartupMovie.gif", config.gif);
|
||||
}
|
||||
|
||||
// try and install.
|
||||
if (R_FAILED(App::Install(config))) {
|
||||
App::Notify("Failed to install forwarder"_i18n);
|
||||
} else {
|
||||
SetPop();
|
||||
}
|
||||
}, "Create the forwarder."_i18n);
|
||||
|
||||
// ensure that all fields are valid.
|
||||
callback->Depends([this](){
|
||||
return
|
||||
!m_name->GetValue().empty() &&
|
||||
!m_author->GetValue().empty() &&
|
||||
!m_version->GetValue().empty() &&
|
||||
!m_icon->GetValue().empty();
|
||||
}, "All fields must be non-empty!"_i18n);
|
||||
}
|
||||
|
||||
auto ForwarderForm::LoadNroMeta() -> Result {
|
||||
// try and load nro meta data.
|
||||
R_TRY(nro_parse(m_assoc.path, m_nro));
|
||||
R_TRY(nro_get_nacp(m_assoc.path, m_nacp));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void SignalChange() {
|
||||
@@ -650,51 +751,7 @@ void FsView::InstallForwarder() {
|
||||
title, items, [this, assoc_list](auto op_index){
|
||||
if (op_index) {
|
||||
const auto assoc = assoc_list[*op_index];
|
||||
log_write("pushing it\n");
|
||||
App::Push<ProgressBox>(0, "Installing Forwarder"_i18n, GetEntry().name, [assoc, this](auto pbox) -> Result {
|
||||
log_write("inside callback\n");
|
||||
|
||||
NroEntry nro{};
|
||||
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();
|
||||
|
||||
if (auto pos = file_name.find_last_of('.'); pos != std::string::npos) {
|
||||
log_write("got filename\n");
|
||||
file_name = file_name.substr(0, pos);
|
||||
log_write("got filename2: %s\n\n", file_name.c_str());
|
||||
}
|
||||
|
||||
const auto db_indexs = GetRomDatabaseFromPath(m_path);
|
||||
|
||||
OwoConfig config{};
|
||||
config.nro_path = assoc.path.toString();
|
||||
config.args = nro_add_arg_file(GetNewPathCurrent());
|
||||
config.name = nro.nacp.lang.name + std::string{" | "} + file_name;
|
||||
// config.name = file_name;
|
||||
config.nacp = nacp;
|
||||
config.icon = GetRomIcon(m_fs.get(), pbox, file_name, db_indexs, nro);
|
||||
pbox->SetImageDataConst(config.icon);
|
||||
|
||||
if (!db_indexs.empty()) {
|
||||
fs::FsNativeSd().read_entire_file("/config/sphaira/logo/rom/NintendoLogo.png", config.logo);
|
||||
fs::FsNativeSd().read_entire_file("/config/sphaira/logo/rom/StartupMovie.gif", config.gif);
|
||||
}
|
||||
|
||||
return App::Install(pbox, config);
|
||||
}, [this](Result rc){
|
||||
App::PushErrorBox(rc, "Failed to install forwarder"_i18n);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
App::PlaySoundEffect(SoundEffect_Install);
|
||||
App::Notify("Installed!"_i18n);
|
||||
}
|
||||
});
|
||||
App::Push<ForwarderForm>(assoc, GetRomDatabaseFromPath(m_path), GetEntry(), GetNewPathCurrent());
|
||||
} else {
|
||||
log_write("pressed B to skip launch...\n");
|
||||
}
|
||||
|
||||
@@ -252,19 +252,17 @@ SidebarEntryTextBase::SidebarEntryTextBase(const std::string& title, const std::
|
||||
void SidebarEntryTextBase::Draw(NVGcontext* vg, Theme* theme, const Vec4& root_pos, bool left) {
|
||||
SidebarEntryBase::Draw(vg, theme, root_pos, left);
|
||||
SidebarEntryBase::DrawEntry(vg, theme, m_title, m_value, true);
|
||||
|
||||
// const auto colour_id = IsEnabled() ? ThemeEntryID_TEXT : ThemeEntryID_TEXT_INFO;
|
||||
// const auto max_w = m_pos.w - 15.f * 2;
|
||||
|
||||
// m_scolling_title.Draw(vg, HasFocus(), m_pos.x + 15.f, m_pos.y + (m_pos.h / 2.f), max_w, 20.f, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE, theme->GetColour(colour_id), m_title);
|
||||
}
|
||||
|
||||
SidebarEntryTextInput::SidebarEntryTextInput(const std::string& title, const std::string& value, const std::string& guide, const std::string& info)
|
||||
: SidebarEntryTextBase{title, value, {}, info}, m_guide{guide} {
|
||||
SidebarEntryTextInput::SidebarEntryTextInput(const std::string& title, const std::string& value, const std::string& guide, s64 len_min, s64 len_max, const std::string& info)
|
||||
: SidebarEntryTextBase{title, value, {}, info}
|
||||
, m_guide{guide}
|
||||
, m_len_min{len_min}
|
||||
, m_len_max{len_max} {
|
||||
|
||||
SetCallback([this](){
|
||||
std::string out;
|
||||
if (R_SUCCEEDED(swkbd::ShowText(out, m_guide.c_str(), GetValue().c_str()))) {
|
||||
if (R_SUCCEEDED(swkbd::ShowText(out, m_guide.c_str(), GetValue().c_str(), m_len_min, m_len_max))) {
|
||||
SetValue(out);
|
||||
}
|
||||
});
|
||||
@@ -347,30 +345,6 @@ auto Sidebar::Update(Controller* controller, TouchInfo* touch) -> void {
|
||||
}
|
||||
|
||||
auto Sidebar::Draw(NVGcontext* vg, Theme* theme) -> void {
|
||||
// Vec4 info_box{};
|
||||
// info_box.y = m_top_bar.y;
|
||||
// info_box.w = 300;
|
||||
// info_box.h = 250;
|
||||
|
||||
// if (m_side == Side::LEFT) {
|
||||
// info_box.x = m_pos.x + m_pos.w + 10;
|
||||
// } else {
|
||||
// info_box.x = m_pos.x - info_box.w - 10;
|
||||
// }
|
||||
|
||||
// const float info_pad = 30;
|
||||
// const float info_font_size = 18;
|
||||
// const char* msg = "Skips verifying the nca header signature";
|
||||
// float bounds[4];
|
||||
// nvgFontSize(vg, info_font_size);
|
||||
// nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
|
||||
// nvgTextLineHeight(vg, 1.7);
|
||||
// nvgTextBoxBounds(vg, info_box.x + info_pad, info_box.y + info_pad, info_box.w - info_pad * 2, msg, nullptr, bounds);
|
||||
// info_box.h = info_pad * 2 + bounds[3] - bounds[1];
|
||||
|
||||
// gfx::drawRect(vg, info_box, theme->GetColour(ThemeEntryID_SIDEBAR), 5);
|
||||
// gfx::drawTextBox(vg, bounds[0], bounds[1], info_font_size, info_box.w - info_pad * 2, theme->GetColour(ThemeEntryID_TEXT), msg);
|
||||
|
||||
gfx::drawRect(vg, m_pos, theme->GetColour(ThemeEntryID_SIDEBAR));
|
||||
gfx::drawText(vg, m_title_pos, m_title_size, theme->GetColour(ThemeEntryID_TEXT), m_title.c_str());
|
||||
if (!m_sub.empty()) {
|
||||
|
||||
Reference in New Issue
Block a user