add save backup/restore, fix filebrowser touch screen, optimise all zip/unzip file code by using bigger stdio buffers.

This commit is contained in:
ITotalJustice
2025-06-05 00:17:55 +01:00
parent b2e25abf08
commit f2462cff81
17 changed files with 1743 additions and 85 deletions

View File

@@ -40,6 +40,7 @@ add_executable(sphaira
source/ui/menus/irs_menu.cpp
source/ui/menus/main_menu.cpp
source/ui/menus/menu_base.cpp
source/ui/menus/save_menu.cpp
source/ui/menus/themezer.cpp
source/ui/menus/ghdl.cpp
source/ui/menus/usb_menu.cpp
@@ -79,6 +80,7 @@ add_executable(sphaira
source/i18n.cpp
source/ftpsrv_helper.cpp
source/threaded_file_transfer.cpp
source/minizip_helper.cpp
source/usb/base.cpp
source/usb/usbds.cpp
@@ -198,8 +200,8 @@ FetchContent_Declare(libusbhsfs
)
FetchContent_Declare(libnxtc
GIT_REPOSITORY https://github.com/DarkMatterCore/libnxtc.git
GIT_TAG v0.0.2
GIT_REPOSITORY https://github.com/ITotalJustice/libnxtc.git
GIT_TAG 0d369b8
)
FetchContent_Declare(nvjpg

View File

@@ -527,27 +527,37 @@ struct FsNativeSd final : FsNative {
#endif
struct FsNativeBis final : FsNative {
FsNativeBis(FsBisPartitionId id, const FsPath& string, bool ignore_read_only = true) : FsNative{ignore_read_only} {
FsNativeBis(FsBisPartitionId id, const FsPath& string) {
m_open_result = fsOpenBisFileSystem(&m_fs, id, string);
}
};
struct FsNativeImage final : FsNative {
FsNativeImage(FsImageDirectoryId id, bool ignore_read_only = true) : FsNative{ignore_read_only} {
FsNativeImage(FsImageDirectoryId id) {
m_open_result = fsOpenImageDirectoryFileSystem(&m_fs, id);
}
};
struct FsNativeContentStorage final : FsNative {
FsNativeContentStorage(FsContentStorageId id, bool ignore_read_only = true) : FsNative{ignore_read_only} {
FsNativeContentStorage(FsContentStorageId id) {
m_open_result = fsOpenContentStorageFileSystem(&m_fs, id);
}
};
struct FsNativeGameCard final : FsNative {
FsNativeGameCard(const FsGameCardHandle* handle, FsGameCardPartition partition, bool ignore_read_only = true) : FsNative{ignore_read_only} {
FsNativeGameCard(const FsGameCardHandle* handle, FsGameCardPartition partition) {
m_open_result = fsOpenGameCardFileSystem(&m_fs, handle, partition);
}
};
struct FsNativeSave final : FsNative {
FsNativeSave(FsSaveDataSpaceId save_data_space_id, const FsSaveDataAttribute *attr, bool read_only) {
if (read_only) {
m_open_result = fsOpenReadOnlySaveDataFileSystem(&m_fs, save_data_space_id, attr);
} else {
m_open_result = fsOpenSaveDataFileSystem(&m_fs, save_data_space_id, attr);
}
}
};
} // namespace fs

View File

@@ -0,0 +1,24 @@
#pragma once
#include <minizip/ioapi.h>
#include <vector>
#include <span>
#include <switch.h>
namespace sphaira::mz {
struct MzMem {
std::vector<u8> buf;
size_t offset;
};
struct MzSpan {
std::span<const u8> buf;
size_t offset;
};
void FileFuncMem(MzMem* mem, zlib_filefunc64_def* funcs);
void FileFuncSpan(MzSpan* span, zlib_filefunc64_def* funcs);
void FileFuncStdio(zlib_filefunc64_def* funcs);
} // namespace sphaira::mz

View File

@@ -43,7 +43,7 @@ Result TransferPull(ui::ProgressBox* pbox, s64 size, ReadCallback rfunc, StartCa
Result TransferUnzip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsPath& path, s64 size, u32 crc32 = 0);
// same as above but for zipping files.
Result TransferZip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsPath& path);
Result TransferZip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsPath& path, u32* crc32 = nullptr);
// passes the name inside the zip an final output path.
using UnzipAllFilter = std::function<bool(const fs::FsPath& name, fs::FsPath& path)>;

View File

@@ -181,6 +181,9 @@ struct FsView final : Widget {
void SetSide(ViewSide side);
static auto get_collection(fs::Fs* fs, const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollection& out, bool inc_file, bool inc_dir, bool inc_size) -> Result;
static auto get_collections(fs::Fs* fs, const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollections& out, bool inc_size = false) -> Result;
private:
void SetIndex(s64 index);
void InstallForwarder();
@@ -249,9 +252,6 @@ private:
void OnRenameCallback();
auto CheckIfUpdateFolder() -> Result;
static auto get_collection(fs::Fs* fs, const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollection& out, bool inc_file, bool inc_dir, bool inc_size) -> Result;
static auto get_collections(fs::Fs* fs, const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollections& out, bool inc_size = false) -> Result;
auto get_collection(const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollection& out, bool inc_file, bool inc_dir, bool inc_size) -> Result;
auto get_collections(const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollections& out, bool inc_size = false) -> Result;

View File

@@ -6,6 +6,7 @@
#include "option.hpp"
#include <memory>
#include <vector>
#include <span>
namespace sphaira::ui::menu::game {

View File

@@ -0,0 +1,156 @@
#pragma once
#include "ui/menus/grid_menu_base.hpp"
#include "ui/list.hpp"
#include "fs.hpp"
#include "option.hpp"
#include <memory>
#include <vector>
#include <span>
namespace sphaira::ui::menu::save {
enum class NacpLoadStatus {
// not yet attempted to be loaded.
None,
// started loading.
Progress,
// loaded, ready to parse.
Loaded,
// failed to load, do not attempt to load again!
Error,
};
struct Entry final : FsSaveDataInfo {
NacpLanguageEntry lang{};
int image{};
bool selected{};
std::shared_ptr<NsApplicationControlData> control{};
u64 jpeg_size{};
NacpLoadStatus status{NacpLoadStatus::None};
auto GetName() const -> const char* {
return lang.name;
}
auto GetAuthor() const -> const char* {
return lang.author;
}
};
struct AccountEntry {
AccountUid uid;
AccountProfile profile;
AccountProfileBase base;
};
struct ThreadResultData {
u64 id{};
std::shared_ptr<NsApplicationControlData> control{};
u64 jpeg_size{};
NacpLanguageEntry lang{};
NacpLoadStatus status{NacpLoadStatus::None};
};
struct ThreadData {
ThreadData();
auto IsRunning() const -> bool;
void Run();
void Close();
void Push(u64 id);
void Push(std::span<const Entry> entries);
void Pop(std::vector<ThreadResultData>& out);
private:
UEvent m_uevent{};
Mutex m_mutex_id{};
Mutex m_mutex_result{};
// app_ids pushed to the queue, signal uevent when pushed.
std::vector<u64> m_ids{};
// control data pushed to the queue.
std::vector<ThreadResultData> m_result{};
std::atomic_bool m_running{};
};
enum SortType {
SortType_Updated,
};
enum OrderType {
OrderType_Descending,
OrderType_Ascending,
};
using LayoutType = grid::LayoutType;
struct Menu final : grid::Menu {
Menu(u32 flags);
~Menu();
auto GetShortTitle() const -> const char* override { return "Saves"; };
void Update(Controller* controller, TouchInfo* touch) override;
void Draw(NVGcontext* vg, Theme* theme) override;
void OnFocusGained() override;
private:
void SetIndex(s64 index);
void ScanHomebrew();
void Sort();
void SortAndFindLastFile(bool scan);
void FreeEntries();
void OnLayoutChange();
auto GetSelectedEntries() const {
std::vector<Entry> out;
for (auto& e : m_entries) {
if (e.selected) {
out.emplace_back(e);
}
}
if (!m_entries.empty() && out.empty()) {
out.emplace_back(m_entries[m_index]);
}
return out;
}
void ClearSelection() {
for (auto& e : m_entries) {
e.selected = false;
}
m_selected_count = 0;
}
void BackupSaves(std::vector<std::reference_wrapper<Entry>>& entries);
void RestoreSave();
private:
static constexpr inline const char* INI_SECTION = "saves";
std::vector<Entry> m_entries{};
s64 m_index{}; // where i am in the array
s64 m_selected_count{};
std::unique_ptr<List> m_list{};
bool m_is_reversed{};
bool m_dirty{};
std::vector<AccountEntry> m_accounts{};
s64 m_account_index{};
ThreadData m_thread_data{};
Thread m_thread{};
option::OptionLong m_sort{INI_SECTION, "sort", SortType::SortType_Updated};
option::OptionLong m_order{INI_SECTION, "order", OrderType::OrderType_Descending};
option::OptionLong m_layout{INI_SECTION, "layout", LayoutType::LayoutType_Grid};
option::OptionBool m_auto_backup_on_restore{INI_SECTION, "auto_backup_on_restore", true};
option::OptionBool m_compress_save_backup{INI_SECTION, "compress_save_backup", false};
};
} // namespace sphaira::ui::menu::save

View File

@@ -25,6 +25,7 @@ struct ProgressBox final : Widget {
auto Update(Controller* controller, TouchInfo* touch) -> void override;
auto Draw(NVGcontext* vg, Theme* theme) -> void override;
auto SetActionName(const std::string& action) -> ProgressBox&;
auto SetTitle(const std::string& title) -> ProgressBox&;
auto NewTransfer(const std::string& transfer) -> ProgressBox&;
auto UpdateTransfer(s64 offset, s64 size) -> ProgressBox&;

View File

@@ -0,0 +1,210 @@
#include "minizip_helper.hpp"
#include <minizip/unzip.h>
#include <minizip/zip.h>
#include <cstring>
#include <cstdio>
namespace sphaira::mz {
namespace {
voidpf minizip_open_file_func_mem(voidpf opaque, const void* filename, int mode) {
return opaque;
}
ZPOS64_T minizip_tell_file_func_mem(voidpf opaque, voidpf stream) {
auto mem = static_cast<const MzMem*>(opaque);
return mem->offset;
}
long minizip_seek_file_func_mem(voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) {
auto mem = static_cast<MzMem*>(opaque);
size_t new_offset = 0;
switch (origin) {
case ZLIB_FILEFUNC_SEEK_SET: new_offset = offset; break;
case ZLIB_FILEFUNC_SEEK_CUR: new_offset = mem->offset + offset; break;
case ZLIB_FILEFUNC_SEEK_END: new_offset = (mem->buf.size() - 1) + offset; break;
default: return -1;
}
if (new_offset > mem->buf.size()) {
return -1;
}
mem->offset = new_offset;
return 0;
}
uLong minizip_read_file_func_mem(voidpf opaque, voidpf stream, void* buf, uLong size) {
auto mem = static_cast<MzMem*>(opaque);
size = std::min(size, mem->buf.size() - mem->offset);
std::memcpy(buf, mem->buf.data() + mem->offset, size);
mem->offset += size;
return size;
}
uLong minizip_write_file_func_mem(voidpf opaque, voidpf stream, const void* buf, uLong size) {
auto mem = static_cast<MzMem*>(opaque);
// give it more memory
if (mem->buf.capacity() < mem->offset + size) {
mem->buf.reserve(mem->buf.capacity() + 1024*1024*64);
}
if (mem->buf.size() < mem->offset + size) {
mem->buf.resize(mem->offset + size);
}
std::memcpy(mem->buf.data() + mem->offset, buf, size);
mem->offset += size;
return size;
}
int minizip_close_file_func_mem(voidpf opaque, voidpf stream) {
return 0;
}
constexpr zlib_filefunc64_def zlib_filefunc_mem = {
.zopen64_file = minizip_open_file_func_mem,
.zread_file = minizip_read_file_func_mem,
.zwrite_file = minizip_write_file_func_mem,
.ztell64_file = minizip_tell_file_func_mem,
.zseek64_file = minizip_seek_file_func_mem,
.zclose_file = minizip_close_file_func_mem,
};
voidpf minizip_open_file_func_span(voidpf opaque, const void* filename, int mode) {
return opaque;
}
ZPOS64_T minizip_tell_file_func_span(voidpf opaque, voidpf stream) {
auto mem = static_cast<const MzSpan*>(opaque);
return mem->offset;
}
long minizip_seek_file_func_span(voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) {
auto mem = static_cast<MzSpan*>(opaque);
size_t new_offset = 0;
switch (origin) {
case ZLIB_FILEFUNC_SEEK_SET: new_offset = offset; break;
case ZLIB_FILEFUNC_SEEK_CUR: new_offset = mem->offset + offset; break;
case ZLIB_FILEFUNC_SEEK_END: new_offset = (mem->buf.size() - 1) + offset; break;
default: return -1;
}
if (new_offset > mem->buf.size()) {
return -1;
}
mem->offset = new_offset;
return 0;
}
uLong minizip_read_file_func_span(voidpf opaque, voidpf stream, void* buf, uLong size) {
auto mem = static_cast<MzSpan*>(opaque);
size = std::min(size, mem->buf.size() - mem->offset);
std::memcpy(buf, mem->buf.data() + mem->offset, size);
mem->offset += size;
return size;
}
int minizip_close_file_func_span(voidpf opaque, voidpf stream) {
return 0;
}
constexpr zlib_filefunc64_def zlib_filefunc_span = {
.zopen64_file = minizip_open_file_func_span,
.zread_file = minizip_read_file_func_span,
.ztell64_file = minizip_tell_file_func_span,
.zseek64_file = minizip_seek_file_func_span,
.zclose_file = minizip_close_file_func_span,
};
voidpf minizip_open_file_func_stdio(voidpf opaque, const void* filename, int mode) {
const char* mode_fopen = NULL;
if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) {
mode_fopen = "rb";
} else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) {
mode_fopen = "r+b";
} else if (mode & ZLIB_FILEFUNC_MODE_CREATE) {
mode_fopen = "wb";
} else {
return NULL;
}
auto f = std::fopen((const char*)filename, mode_fopen);
if (f) {
std::setvbuf(f, nullptr, _IOFBF, 1024 * 512);
}
return f;
}
ZPOS64_T minizip_tell_file_func_stdio(voidpf opaque, voidpf stream) {
auto file = static_cast<std::FILE*>(stream);
return std::ftell(file);
}
long minizip_seek_file_func_stdio(voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) {
auto file = static_cast<std::FILE*>(stream);
return std::fseek(file, offset, origin);
}
uLong minizip_read_file_func_stdio(voidpf opaque, voidpf stream, void* buf, uLong size) {
auto file = static_cast<std::FILE*>(stream);
return std::fread(buf, 1, size, file);
}
uLong minizip_write_file_func_stdio(voidpf opaque, voidpf stream, const void* buf, uLong size) {
auto file = static_cast<std::FILE*>(stream);
return std::fwrite(buf, 1, size, file);
}
int minizip_close_file_func_stdio(voidpf opaque, voidpf stream) {
auto file = static_cast<std::FILE*>(stream);
if (file) {
return std::fclose(file);
}
return 0;
}
int minizip_error_file_func_stdio(voidpf opaque, voidpf stream) {
auto file = static_cast<std::FILE*>(stream);
if (file) {
return std::ferror(file);
}
return 0;
}
constexpr zlib_filefunc64_def zlib_filefunc_stdio = {
.zopen64_file = minizip_open_file_func_stdio,
.zread_file = minizip_read_file_func_stdio,
.zwrite_file = minizip_write_file_func_stdio,
.ztell64_file = minizip_tell_file_func_stdio,
.zseek64_file = minizip_seek_file_func_stdio,
.zclose_file = minizip_close_file_func_stdio,
.zerror_file = minizip_error_file_func_stdio,
};
} // namespace
void FileFuncMem(MzMem* mem, zlib_filefunc64_def* funcs) {
*funcs = zlib_filefunc_mem;
funcs->opaque = mem;
}
void FileFuncSpan(MzSpan* span, zlib_filefunc64_def* funcs) {
*funcs = zlib_filefunc_span;
funcs->opaque = span;
}
void FileFuncStdio(zlib_filefunc64_def* funcs) {
*funcs = zlib_filefunc_stdio;
}
} // namespace sphaira::mz

View File

@@ -2,6 +2,7 @@
#include "log.hpp"
#include "defines.hpp"
#include "app.hpp"
#include "minizip_helper.hpp"
#include <vector>
#include <algorithm>
@@ -456,7 +457,7 @@ Result TransferUnzip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::F
[&](void* data, s64 off, s64 size, u64* bytes_read) -> Result {
const auto result = unzReadCurrentFile(zfile, data, size);
if (result <= 0) {
// log_write("failed to read zip file: %s\n", inzip.c_str());
log_write("failed to read zip file: %s %d\n", path.s, result);
R_THROW(0x1);
}
@@ -474,21 +475,29 @@ Result TransferUnzip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::F
));
// validate crc32 (if set in the info).
R_UNLESS(!crc32 || crc32 == crc32_out, 0x1);
R_UNLESS(!crc32 || crc32 == crc32_out, 0x8);
R_SUCCEED();
}
Result TransferZip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsPath& path) {
Result TransferZip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsPath& path, u32* crc32) {
fs::File f;
R_TRY(fs->OpenFile(path, FsOpenMode_Read, &f));
s64 file_size;
R_TRY(f.GetSize(&file_size));
if (crc32) {
*crc32 = 0;
}
return thread::TransferInternal(pbox, file_size,
[&](void* data, s64 off, s64 size, u64* bytes_read) -> Result {
return f.Read(off, data, size, FsReadOption_None, bytes_read);
const auto rc = f.Read(off, data, size, FsReadOption_None, bytes_read);
if (R_SUCCEEDED(rc) && crc32) {
*crc32 = crc32CalculateWithSeed(*crc32, data, *bytes_read);
}
return rc;
},
[&](const void* data, s64 off, s64 size) -> Result {
if (ZIP_OK != zipWriteInFileInZip(zfile, data, size)) {
@@ -559,7 +568,10 @@ Result TransferUnzipAll(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs
}
Result TransferUnzipAll(ui::ProgressBox* pbox, const fs::FsPath& zip_out, fs::Fs* fs, const fs::FsPath& base_path, UnzipAllFilter filter) {
auto zfile = unzOpen64(zip_out);
zlib_filefunc64_def file_func;
mz::FileFuncStdio(&file_func);
auto zfile = unzOpen2_64(zip_out, &file_func);
R_UNLESS(zfile, 0x1);
ON_SCOPE_EXIT(unzClose(zfile));

View File

@@ -17,6 +17,7 @@
#include "threaded_file_transfer.hpp"
#include "nro.hpp"
#include "web.hpp"
#include "minizip_helper.hpp"
#include <minIni.h>
#include <string>
@@ -77,62 +78,6 @@ constexpr const char* ORDER_STR[] = {
"Asc",
};
struct MzMem {
const void* buf;
size_t size;
size_t offset;
};
ZPOS64_T minizip_tell_file_func(voidpf opaque, voidpf stream) {
auto mem = static_cast<const MzMem*>(opaque);
return mem->offset;
}
long minizip_seek_file_func(voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) {
auto mem = static_cast<MzMem*>(opaque);
size_t new_offset = 0;
switch (origin) {
case ZLIB_FILEFUNC_SEEK_SET: new_offset = offset; break;
case ZLIB_FILEFUNC_SEEK_CUR: new_offset = mem->offset + offset; break;
case ZLIB_FILEFUNC_SEEK_END: new_offset = (mem->size - 1) + offset; break;
default: return -1;
}
if (new_offset > mem->size) {
return -1;
}
mem->offset = new_offset;
return 0;
}
voidpf minizip_open_file_func(voidpf opaque, const void* filename, int mode) {
return opaque;
}
uLong minizip_read_file_func(voidpf opaque, voidpf stream, void* buf, uLong size) {
auto mem = static_cast<MzMem*>(opaque);
size = std::min(size, mem->size - mem->offset);
std::memcpy(buf, (const u8*)mem->buf + mem->offset, size);
mem->offset += size;
return size;
}
int minizip_close_file_func(voidpf opaque, voidpf stream) {
return 0;
}
constexpr zlib_filefunc64_def zlib_filefunc = {
.zopen64_file = minizip_open_file_func,
.zread_file = minizip_read_file_func,
.ztell64_file = minizip_tell_file_func,
.zseek64_file = minizip_seek_file_func,
.zclose_file = minizip_close_file_func,
};
auto BuildIconUrl(const Entry& e) -> std::string {
char out[0x100];
std::snprintf(out, sizeof(out), "%s/packages/%s/icon.png", URL_BASE, e.name.c_str());
@@ -474,20 +419,17 @@ auto InstallApp(ProgressBox* pbox, const Entry& entry) -> Result {
}
}
struct MzMem mem{};
mem.buf = api_result.data.data();
mem.size = api_result.data.size();
auto file_func = zlib_filefunc;
file_func.opaque = &mem;
zlib_filefunc64_def* file_func_ptr{};
mz::MzSpan mz_span{api_result.data};
zlib_filefunc64_def file_func;
if (!file_download) {
file_func_ptr = &file_func;
mz::FileFuncSpan(&mz_span, &file_func);
} else {
mz::FileFuncStdio(&file_func);
}
// 3. extract the zip
if (!pbox->ShouldExit()) {
auto zfile = unzOpen2_64(zip_out, file_func_ptr);
auto zfile = unzOpen2_64(zip_out, &file_func);
R_UNLESS(zfile, 0x1);
ON_SCOPE_EXIT(unzClose(zfile));

View File

@@ -21,6 +21,7 @@
#include "hasher.hpp"
#include "location.hpp"
#include "threaded_file_transfer.hpp"
#include "minizip_helper.hpp"
#include "yati/yati.hpp"
#include "yati/source/file.hpp"
@@ -435,7 +436,6 @@ FsView::~FsView() {
}
void FsView::Update(Controller* controller, TouchInfo* touch) {
Widget::Update(controller, touch);
m_list->OnUpdate(controller, touch, m_index, m_entries_current.size(), [this](bool touch, auto i) {
if (touch && m_index == i) {
FireAction(Button::A);
@@ -790,7 +790,10 @@ void FsView::ZipFiles(fs::FsPath zip_out) {
zip_info.tmz_date.tm_mon = tm->tm_mon;
zip_info.tmz_date.tm_year = tm->tm_year;
auto zfile = zipOpen(zip_out, APPEND_STATUS_CREATE);
zlib_filefunc64_def file_func;
mz::FileFuncStdio(&file_func);
auto zfile = zipOpen2_64(zip_out, APPEND_STATUS_CREATE, nullptr, &file_func);
R_UNLESS(zfile, 0x1);
ON_SCOPE_EXIT(zipClose(zfile, "sphaira v" APP_VERSION_HASH));
@@ -1854,6 +1857,13 @@ Menu::~Menu() {
}
void Menu::Update(Controller* controller, TouchInfo* touch) {
// workaround the buttons not being display properly.
// basically, inherit all actions from the view, draw them,
// then restore state after.
const auto actions_copy = GetActions();
ON_SCOPE_EXIT(m_actions = actions_copy);
m_actions.insert_range(view->GetActions());
MenuBase::Update(controller, touch);
view->Update(controller, touch);
}

View File

@@ -998,6 +998,11 @@ void Menu::Update(Controller* controller, TouchInfo* touch) {
void Menu::Draw(NVGcontext* vg, Theme* theme) {
MenuBase::Draw(vg, theme);
if (m_entries.empty()) {
gfx::drawTextArgs(vg, GetX() + GetW() / 2.f, GetY() + GetH() / 2.f, 36.f, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE, theme->GetColour(ThemeEntryID_TEXT_INFO), "Empty..."_i18n.c_str());
return;
}
// max images per frame, in order to not hit io / gpu too hard.
const int image_load_max = 2;
int image_load_count = 0;
@@ -1096,7 +1101,7 @@ void Menu::ScanHomebrew() {
u64 unk_x11;// = e.unk_x11;
memcpy(&unk_x0a, e.unk_x0a, sizeof(e.unk_x0a));
memcpy(&unk_x11, e.unk_x11, sizeof(e.unk_x11));
log_write("ID: %016lx got type: %u unk_x09: %u unk_x0a: %zu unk_x10: %u unk_x11: %zu\n", e.application_id, e.type,
log_write("ID: %016lx got type: %u unk_x09: %u unk_x0a: %zu unk_x10: %u unk_x11: %zu\n", e.app_id, e.type,
unk_x09,
unk_x0a,
unk_x10,

View File

@@ -529,7 +529,7 @@ Result Menu::GcMount() {
// the fs, same as mounting storage.
for (u32 i = 0; i < REMOUNT_ATTEMPT_MAX; i++) {
R_TRY(fsDeviceOperatorGetGameCardHandle(std::addressof(m_dev_op), std::addressof(m_handle)));
m_fs = std::make_unique<fs::FsNativeGameCard>(std::addressof(m_handle), FsGameCardPartition_Secure, false);
m_fs = std::make_unique<fs::FsNativeGameCard>(std::addressof(m_handle), FsGameCardPartition_Secure);
if (R_SUCCEEDED(m_fs->GetFsOpenResult())) {
break;
}

View File

@@ -15,6 +15,7 @@
#include "ui/menus/ftp_menu.hpp"
#include "ui/menus/gc_menu.hpp"
#include "ui/menus/game_menu.hpp"
#include "ui/menus/save_menu.hpp"
#include "ui/menus/appstore.hpp"
#include "app.hpp"
@@ -49,6 +50,7 @@ const MiscMenuEntry MISC_MENU_ENTRIES[] = {
{ .name = "Appstore", .title = "Appstore", .func = MiscMenuFuncGenerator<ui::menu::appstore::Menu>, .flag = MiscMenuFlag_Shortcut },
{ .name = "Games", .title = "Games", .func = MiscMenuFuncGenerator<ui::menu::game::Menu>, .flag = MiscMenuFlag_Shortcut },
{ .name = "FileBrowser", .title = "FileBrowser", .func = MiscMenuFuncGenerator<ui::menu::filebrowser::Menu>, .flag = MiscMenuFlag_Shortcut },
{ .name = "Saves", .title = "Saves", .func = MiscMenuFuncGenerator<ui::menu::save::Menu>, .flag = MiscMenuFlag_Shortcut },
{ .name = "Themezer", .title = "Themezer", .func = MiscMenuFuncGenerator<ui::menu::themezer::Menu>, .flag = MiscMenuFlag_Shortcut },
{ .name = "GitHub", .title = "GitHub", .func = MiscMenuFuncGenerator<ui::menu::gh::Menu>, .flag = MiscMenuFlag_Shortcut },
{ .name = "FTP", .title = "FTP Install", .func = MiscMenuFuncGenerator<ui::menu::ftp::Menu>, .flag = MiscMenuFlag_Install },

File diff suppressed because it is too large Load Diff

View File

@@ -88,6 +88,7 @@ auto ProgressBox::Draw(NVGcontext* vg, Theme* theme) -> void {
m_last_offset = m_offset;
}
const auto action = m_action;
const auto title = m_title;
const auto transfer = m_transfer;
const auto size = m_size;
@@ -166,7 +167,7 @@ auto ProgressBox::Draw(NVGcontext* vg, Theme* theme) -> void {
gfx::drawTextArgs(vg, center_x, prog_bar.y + prog_bar.h + 30, 18, NVG_ALIGN_CENTER | NVG_ALIGN_TOP, theme->GetColour(ThemeEntryID_TEXT), "%s (%s)", time_str, speed_str);
}
gfx::drawTextArgs(vg, center_x, m_pos.y + 40, 24, NVG_ALIGN_CENTER | NVG_ALIGN_TOP, theme->GetColour(ThemeEntryID_TEXT), m_action.c_str());
gfx::drawTextArgs(vg, center_x, m_pos.y + 40, 24, NVG_ALIGN_CENTER | NVG_ALIGN_TOP, theme->GetColour(ThemeEntryID_TEXT), action.c_str());
const auto draw_text = [&](ScrollingText& scroll, const std::string& txt, float y, float size, float pad, ThemeEntryID id){
float bounds[4];
@@ -187,6 +188,14 @@ auto ProgressBox::Draw(NVGcontext* vg, Theme* theme) -> void {
nvgRestore(vg);
}
auto ProgressBox::SetActionName(const std::string& action) -> ProgressBox& {
mutexLock(&m_mutex);
m_action = action;
mutexUnlock(&m_mutex);
Yield();
return *this;
}
auto ProgressBox::SetTitle(const std::string& title) -> ProgressBox& {
mutexLock(&m_mutex);
m_title = title;