add save backup/restore, fix filebrowser touch screen, optimise all zip/unzip file code by using bigger stdio buffers.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
24
sphaira/include/minizip_helper.hpp
Normal file
24
sphaira/include/minizip_helper.hpp
Normal 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
|
||||
@@ -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)>;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "option.hpp"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <span>
|
||||
|
||||
namespace sphaira::ui::menu::game {
|
||||
|
||||
|
||||
156
sphaira/include/ui/menus/save_menu.hpp
Normal file
156
sphaira/include/ui/menus/save_menu.hpp
Normal 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
|
||||
@@ -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&;
|
||||
|
||||
210
sphaira/source/minizip_helper.cpp
Normal file
210
sphaira/source/minizip_helper.cpp
Normal 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
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 },
|
||||
|
||||
1274
sphaira/source/ui/menus/save_menu.cpp
Normal file
1274
sphaira/source/ui/menus/save_menu.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user