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

@@ -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;