filebrowser/picker: backport changes in totalsms (optimise zip peek, remove unused vars and code, optimise folder count, fix missed extension parse).

This commit is contained in:
ITotalJustice
2025-08-03 03:26:24 +01:00
parent a3780bdcea
commit 4300c9ee1b
8 changed files with 250 additions and 156 deletions

View File

@@ -537,6 +537,9 @@ enum class SphairaResult : Result {
ZipOpenNewFileInZip, ZipOpenNewFileInZip,
ZipWriteInFileInZip, ZipWriteInFileInZip,
MmzBadLocalHeaderSig,
MmzBadLocalHeaderRead,
FileBrowserFailedUpload, FileBrowserFailedUpload,
FileBrowserDirNotDaybreak, FileBrowserDirNotDaybreak,
@@ -685,6 +688,8 @@ enum : Result {
MAKE_SPHAIRA_RESULT_ENUM(ZipOpen2_64), MAKE_SPHAIRA_RESULT_ENUM(ZipOpen2_64),
MAKE_SPHAIRA_RESULT_ENUM(ZipOpenNewFileInZip), MAKE_SPHAIRA_RESULT_ENUM(ZipOpenNewFileInZip),
MAKE_SPHAIRA_RESULT_ENUM(ZipWriteInFileInZip), MAKE_SPHAIRA_RESULT_ENUM(ZipWriteInFileInZip),
MAKE_SPHAIRA_RESULT_ENUM(MmzBadLocalHeaderSig),
MAKE_SPHAIRA_RESULT_ENUM(MmzBadLocalHeaderRead),
MAKE_SPHAIRA_RESULT_ENUM(FileBrowserFailedUpload), MAKE_SPHAIRA_RESULT_ENUM(FileBrowserFailedUpload),
MAKE_SPHAIRA_RESULT_ENUM(FileBrowserDirNotDaybreak), MAKE_SPHAIRA_RESULT_ENUM(FileBrowserDirNotDaybreak),
MAKE_SPHAIRA_RESULT_ENUM(AppstoreFailedZipDownload), MAKE_SPHAIRA_RESULT_ENUM(AppstoreFailedZipDownload),

View File

@@ -4,6 +4,7 @@
#include <vector> #include <vector>
#include <span> #include <span>
#include <switch.h> #include <switch.h>
#include "fs.hpp"
namespace sphaira::mz { namespace sphaira::mz {
@@ -20,5 +21,12 @@ struct MzSpan {
void FileFuncMem(MzMem* mem, zlib_filefunc64_def* funcs); void FileFuncMem(MzMem* mem, zlib_filefunc64_def* funcs);
void FileFuncSpan(MzSpan* span, zlib_filefunc64_def* funcs); void FileFuncSpan(MzSpan* span, zlib_filefunc64_def* funcs);
void FileFuncStdio(zlib_filefunc64_def* funcs); void FileFuncStdio(zlib_filefunc64_def* funcs);
void FileFuncNative(zlib_filefunc64_def* funcs);
// minizip takes 18ms to open a zip and 4ms to parse the first file entry.
// this results in a dropped frame.
// this version simply reads the local header + file name in 2 reads,
// which takes 1-2ms.
Result PeekFirstFileName(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& name);
} // namespace sphaira::mz } // namespace sphaira::mz

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include "ui/menus/filebrowser.hpp"
#include "ui/menus/menu_base.hpp" #include "ui/menus/menu_base.hpp"
#include "ui/scrolling_text.hpp" #include "ui/scrolling_text.hpp"
#include "ui/list.hpp" #include "ui/list.hpp"
@@ -17,13 +18,6 @@ enum FsEntryFlag {
FsEntryFlag_Assoc = 1 << 1, FsEntryFlag_Assoc = 1 << 1,
}; };
enum class FsType {
Sd,
ImageNand,
ImageSd,
Stdio,
};
enum SortType { enum SortType {
SortType_Size, SortType_Size,
SortType_Alphabetical, SortType_Alphabetical,
@@ -34,77 +28,10 @@ enum OrderType {
OrderType_Ascending, OrderType_Ascending,
}; };
struct FsEntry { using FsType = filebrowser::FsType;
fs::FsPath name{}; using FsEntry = filebrowser::FsEntry;
fs::FsPath root{}; using FileEntry = filebrowser::FileEntry;
FsType type{}; using LastFile = filebrowser::LastFile;
u32 flags{FsEntryFlag_None};
auto IsReadOnly() const -> bool {
return flags & FsEntryFlag_ReadOnly;
}
auto IsAssoc() const -> bool {
return flags & FsEntryFlag_Assoc;
}
auto IsSame(const FsEntry& e) const {
return root == e.root && type == e.type;
}
};
// roughly 1kib in size per entry
struct FileEntry : FsDirectoryEntry {
std::string extension{}; // if any
std::string internal_name{}; // if any
std::string internal_extension{}; // if any
s64 file_count{-1}; // number of files in a folder, non-recursive
s64 dir_count{-1}; // number folders in a folder, non-recursive
FsTimeStampRaw time_stamp{};
bool checked_extension{}; // did we already search for an ext?
bool checked_internal_extension{}; // did we already search for an ext?
auto IsFile() const -> bool {
return type == FsDirEntryType_File;
}
auto IsDir() const -> bool {
return !IsFile();
}
auto IsHidden() const -> bool {
return name[0] == '.';
}
auto GetName() const -> std::string {
return name;
}
auto GetExtension() const -> std::string {
return extension;
}
auto GetInternalName() const -> std::string {
if (!internal_name.empty()) {
return internal_name;
}
return GetName();
}
auto GetInternalExtension() const -> std::string {
if (!internal_extension.empty()) {
return internal_extension;
}
return GetExtension();
}
};
struct LastFile {
fs::FsPath name{};
s64 index{};
float offset{};
s64 entries_count{};
};
using Callback = std::function<bool(const fs::FsPath& path)>; using Callback = std::function<bool(const fs::FsPath& path)>;

View File

@@ -7,7 +7,6 @@
#include "fs.hpp" #include "fs.hpp"
#include "option.hpp" #include "option.hpp"
#include "hasher.hpp" #include "hasher.hpp"
// #include <optional>
#include <span> #include <span>
namespace sphaira::ui::menu::filebrowser { namespace sphaira::ui::menu::filebrowser {
@@ -97,6 +96,11 @@ struct FileEntry : FsDirectoryEntry {
} }
auto GetExtension() const -> std::string { auto GetExtension() const -> std::string {
if (!checked_extension) {
if (auto ext = std::strrchr(name, '.')) {
return ext+1;
}
}
return extension; return extension;
} }
@@ -396,14 +400,6 @@ private:
std::vector<FileAssocEntry> m_assoc_entries{}; std::vector<FileAssocEntry> m_assoc_entries{};
SelectedStash m_selected{}; SelectedStash m_selected{};
// this keeps track of the highlighted file before opening a folder
// if the user presses B to go back to the previous dir
// this vector is popped, then, that entry is checked if it still exists
// if it does, the index becomes that file.
std::vector<LastFile> m_previous_highlighted_file{};
s64 m_index{};
s64 m_selected_count{};
option::OptionLong m_sort{INI_SECTION, "sort", SortType::SortType_Alphabetical}; option::OptionLong m_sort{INI_SECTION, "sort", SortType::SortType_Alphabetical};
option::OptionLong m_order{INI_SECTION, "order", OrderType::OrderType_Descending}; option::OptionLong m_order{INI_SECTION, "order", OrderType::OrderType_Descending};
option::OptionBool m_show_hidden{INI_SECTION, "show_hidden", false}; option::OptionBool m_show_hidden{INI_SECTION, "show_hidden", false};
@@ -412,7 +408,6 @@ private:
option::OptionBool m_ignore_read_only{INI_SECTION, "ignore_read_only", false}; option::OptionBool m_ignore_read_only{INI_SECTION, "ignore_read_only", false};
bool m_loaded_assoc_entries{}; bool m_loaded_assoc_entries{};
bool m_is_update_folder{};
bool m_split_screen{}; bool m_split_screen{};
}; };

View File

@@ -7,6 +7,68 @@
namespace sphaira::mz { namespace sphaira::mz {
namespace { namespace {
// mmz is part of ftpsrv code.
#define LOCAL_HEADER_SIG 0x4034B50
#define FILE_HEADER_SIG 0x2014B50
#define END_RECORD_SIG 0x6054B50
// 30 bytes (0x1E)
#pragma pack(push,1)
typedef struct mmz_LocalHeader {
uint32_t sig;
uint16_t version;
uint16_t flags;
uint16_t compression;
uint16_t modtime;
uint16_t moddate;
uint32_t crc32;
uint32_t compressed_size;
uint32_t uncompressed_size;
uint16_t filename_len;
uint16_t extrafield_len;
} mmz_LocalHeader;
#pragma pack(pop)
// 46 bytes (0x2E)
#pragma pack(push,1)
typedef struct mmz_FileHeader {
uint32_t sig;
uint16_t version;
uint16_t version_needed;
uint16_t flags;
uint16_t compression;
uint16_t modtime;
uint16_t moddate;
uint32_t crc32;
uint32_t compressed_size;
uint32_t uncompressed_size;
uint16_t filename_len;
uint16_t extrafield_len;
uint16_t filecomment_len;
uint16_t disk_start; // wat
uint16_t internal_attr; // wat
uint32_t external_attr; // wat
uint32_t local_hdr_off;
} mmz_FileHeader;
#pragma pack(pop)
#pragma pack(push,1)
typedef struct mmz_EndRecord {
uint32_t sig;
uint16_t disk_number;
uint16_t disk_wcd;
uint16_t disk_entries;
uint16_t total_entries;
uint32_t central_directory_size;
uint32_t file_hdr_off;
uint16_t comment_len;
} mmz_EndRecord;
#pragma pack(pop)
static_assert(sizeof(mmz_LocalHeader) == 0x1E);
static_assert(sizeof(mmz_FileHeader) == 0x2E);
static_assert(sizeof(mmz_EndRecord) == 0x16);
voidpf minizip_open_file_func_mem(voidpf opaque, const void* filename, int mode) { voidpf minizip_open_file_func_mem(voidpf opaque, const void* filename, int mode) {
return opaque; return opaque;
} }
@@ -191,6 +253,99 @@ constexpr zlib_filefunc64_def zlib_filefunc_stdio = {
.zerror_file = minizip_error_file_func_stdio, .zerror_file = minizip_error_file_func_stdio,
}; };
struct Internal {
FsFile file;
s64 offset;
s64 size;
Result rc;
};
static void* zopen64_file(void* opaque, const void* filename, int mode)
{
struct Internal* fs = (struct Internal*)calloc(1, sizeof(*fs));
if (R_FAILED(fs->rc = fsFsOpenFile(fsdevGetDeviceFileSystem("sdmc:"), (const char*)filename, FsOpenMode_Read, &fs->file))) {
free(fs);
return NULL;
}
if (R_FAILED(fs->rc = fsFileGetSize(&fs->file, &fs->size))) {
free(fs);
return NULL;
}
return fs;
}
static uLong zread_file(void* opaque, void* stream, void* buf, unsigned long size)
{
struct Internal* fs = (struct Internal*)stream;
u64 bytes_read;
if (R_FAILED(fs->rc = fsFileRead(&fs->file, fs->offset, buf, size, 0, &bytes_read))) {
return 0;
}
fs->offset += bytes_read;
return bytes_read;
}
static ZPOS64_T ztell64_file(void* opaque, void* stream)
{
struct Internal* fs = (struct Internal*)stream;
return fs->offset;
}
static long zseek64_file(void* opaque, void* stream, ZPOS64_T offset, int origin)
{
struct Internal* fs = (struct Internal*)stream;
switch (origin) {
case SEEK_SET: {
fs->offset = offset;
} break;
case SEEK_CUR: {
fs->offset += offset;
} break;
case SEEK_END: {
fs->offset = fs->size + offset;
} break;
}
return 0;
}
static int zclose_file(void* opaque, void* stream)
{
if (stream) {
struct Internal* fs = (struct Internal*)stream;
fsFileClose(&fs->file);
memset(fs, 0, sizeof(*fs));
free(fs);
}
return 0;
}
static int zerror_file(void* opaque, void* stream)
{
struct Internal* fs = (struct Internal*)stream;
if (R_FAILED(fs->rc)) {
return -1;
}
return 0;
}
static const zlib_filefunc64_def zlib_filefunc_native = {
.zopen64_file = zopen64_file,
.zread_file = zread_file,
.ztell64_file = ztell64_file,
.zseek64_file = zseek64_file,
.zclose_file = zclose_file,
.zerror_file = zerror_file,
};
} // namespace } // namespace
void FileFuncMem(MzMem* mem, zlib_filefunc64_def* funcs) { void FileFuncMem(MzMem* mem, zlib_filefunc64_def* funcs) {
@@ -207,4 +362,26 @@ void FileFuncStdio(zlib_filefunc64_def* funcs) {
*funcs = zlib_filefunc_stdio; *funcs = zlib_filefunc_stdio;
} }
void FileFuncNative(zlib_filefunc64_def* funcs) {
*funcs = zlib_filefunc_native;
}
Result PeekFirstFileName(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& name) {
fs::File file;
R_TRY(fs->OpenFile(path, FsOpenMode_Read, &file));
mmz_LocalHeader local_hdr;
u64 bytes_read;
R_TRY(file.Read(0, &local_hdr, sizeof(local_hdr), 0, &bytes_read));
R_UNLESS(bytes_read == sizeof(local_hdr), Result_MmzBadLocalHeaderRead);
R_UNLESS(local_hdr.sig == LOCAL_HEADER_SIG, Result_MmzBadLocalHeaderSig);
const auto name_len = std::min<u64>(local_hdr.filename_len, sizeof(name) - 1);
R_TRY(file.Read(bytes_read, name, name_len, 0, &bytes_read));
name[name_len] = '\0';
R_SUCCEED();
}
} // namespace sphaira::mz } // namespace sphaira::mz

View File

@@ -71,6 +71,8 @@ auto GetCodeMessage(Result rc) -> const char* {
case Result_ZipOpen2_64: return "SphairaError_ZipOpen2_64"; case Result_ZipOpen2_64: return "SphairaError_ZipOpen2_64";
case Result_ZipOpenNewFileInZip: return "SphairaError_ZipOpenNewFileInZip"; case Result_ZipOpenNewFileInZip: return "SphairaError_ZipOpenNewFileInZip";
case Result_ZipWriteInFileInZip: return "SphairaError_ZipWriteInFileInZip"; case Result_ZipWriteInFileInZip: return "SphairaError_ZipWriteInFileInZip";
case Result_MmzBadLocalHeaderSig: return "SphairaError_MmzBadLocalHeaderSig";
case Result_MmzBadLocalHeaderRead: return "SphairaError_MmzBadLocalHeaderRead";
case Result_FileBrowserFailedUpload: return "SphairaError_FileBrowserFailedUpload"; case Result_FileBrowserFailedUpload: return "SphairaError_FileBrowserFailedUpload";
case Result_FileBrowserDirNotDaybreak: return "SphairaError_FileBrowserDirNotDaybreak"; case Result_FileBrowserDirNotDaybreak: return "SphairaError_FileBrowserDirNotDaybreak";
case Result_AppstoreFailedZipDownload: return "SphairaError_AppstoreFailedZipDownload"; case Result_AppstoreFailedZipDownload: return "SphairaError_AppstoreFailedZipDownload";

View File

@@ -14,7 +14,6 @@
#include "minizip_helper.hpp" #include "minizip_helper.hpp"
#include <minIni.h> #include <minIni.h>
#include <minizip/unzip.h>
#include <cstring> #include <cstring>
#include <cassert> #include <cassert>
#include <string> #include <string>
@@ -82,21 +81,14 @@ void Menu::SetIndex(s64 index) {
if (IsSd() && !m_entries_current.empty() && !GetEntry().checked_internal_extension && IsSamePath(GetEntry().extension, "zip")) { if (IsSd() && !m_entries_current.empty() && !GetEntry().checked_internal_extension && IsSamePath(GetEntry().extension, "zip")) {
GetEntry().checked_internal_extension = true; GetEntry().checked_internal_extension = true;
if (auto zfile = unzOpen64(GetNewPathCurrent())) { TimeStamp ts;
ON_SCOPE_EXIT(unzClose(zfile)); fs::FsPath filename_inzip{};
if (R_SUCCEEDED(mz::PeekFirstFileName(GetFs(), GetNewPathCurrent(), filename_inzip))) {
// only check first entry (i think RA does the same) if (auto ext = std::strrchr(filename_inzip, '.')) {
fs::FsPath filename_inzip{}; GetEntry().internal_name = filename_inzip.toString();
unz_file_info64 file_info{}; GetEntry().internal_extension = ext+1;
if (UNZ_OK == unzOpenCurrentFile(zfile)) {
ON_SCOPE_EXIT(unzCloseCurrentFile(zfile));
if (UNZ_OK == unzGetCurrentFileInfo64(zfile, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0)) {
if (auto ext = std::strrchr(filename_inzip, '.')) {
GetEntry().internal_name = filename_inzip.toString();
GetEntry().internal_extension = ext+1;
}
}
} }
log_write("\tzip, time taken: %.2fs %zums\n", ts.GetSecondsD(), ts.GetMs());
} }
} }
@@ -450,24 +442,12 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
} }
constexpr float text_xoffset{15.f}; constexpr float text_xoffset{15.f};
bool got_dir_count = false;
m_list->Draw(vg, theme, m_entries_current.size(), [this, text_col](auto* vg, auto* theme, auto v, auto i) { m_list->Draw(vg, theme, m_entries_current.size(), [this, text_col, &got_dir_count](auto* vg, auto* theme, auto v, auto i) {
const auto& [x, y, w, h] = v; const auto& [x, y, w, h] = v;
auto& e = GetEntry(i); auto& e = GetEntry(i);
if (e.IsDir()) {
// NOTE: make this native only if hdd dir scan is too slow.
// if (m_fs->IsNative() && e.file_count == -1 && e.dir_count == -1) {
if (e.file_count == -1 && e.dir_count == -1) {
m_fs->DirGetEntryCount(GetNewPath(e), &e.file_count, &e.dir_count);
}
} else if (!e.checked_extension) {
e.checked_extension = true;
if (auto ext = std::strrchr(e.name, '.')) {
e.extension = ext+1;
}
}
auto text_id = ThemeEntryID_TEXT; auto text_id = ThemeEntryID_TEXT;
const auto selected = m_index == i; const auto selected = m_index == i;
if (selected) { if (selected) {
@@ -506,8 +486,19 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
// NOTE: make this native only if i disable dir scan from above. // NOTE: make this native only if i disable dir scan from above.
if (e.IsDir()) { if (e.IsDir()) {
gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f) - 3, 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_BOTTOM, theme->GetColour(text_id), "%zd files"_i18n.c_str(), e.file_count); // NOTE: this takes longer than 16ms when opening a new folder due to it
gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f) + 3, 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP, theme->GetColour(text_id), "%zd dirs"_i18n.c_str(), e.dir_count); // checking all 9 folders at once.
if (!got_dir_count && e.file_count == -1 && e.dir_count == -1) {
got_dir_count = true;
m_fs->DirGetEntryCount(GetNewPath(e), &e.file_count, &e.dir_count);
}
if (e.file_count != -1) {
gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f) - 3, 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_BOTTOM, theme->GetColour(text_id), "%zd files"_i18n.c_str(), e.file_count);
}
if (e.dir_count != -1) {
gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f) + 3, 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP, theme->GetColour(text_id), "%zd dirs"_i18n.c_str(), e.dir_count);
}
} else if (e.IsFile()) { } else if (e.IsFile()) {
if (!e.time_stamp.is_valid) { if (!e.time_stamp.is_valid) {
const auto path = GetNewPath(e); const auto path = GetNewPath(e);

View File

@@ -566,24 +566,12 @@ void FsView::Draw(NVGcontext* vg, Theme* theme) {
} }
constexpr float text_xoffset{15.f}; constexpr float text_xoffset{15.f};
bool got_dir_count = false;
m_list->Draw(vg, theme, m_entries_current.size(), [this, text_col](auto* vg, auto* theme, auto v, auto i) { m_list->Draw(vg, theme, m_entries_current.size(), [this, text_col, &got_dir_count](auto* vg, auto* theme, auto v, auto i) {
const auto& [x, y, w, h] = v; const auto& [x, y, w, h] = v;
auto& e = GetEntry(i); auto& e = GetEntry(i);
if (e.IsDir()) {
// NOTE: make this native only if hdd dir scan is too slow.
// if (m_fs->IsNative() && e.file_count == -1 && e.dir_count == -1) {
if (e.file_count == -1 && e.dir_count == -1) {
m_fs->DirGetEntryCount(GetNewPath(e), &e.file_count, &e.dir_count);
}
} else if (!e.checked_extension) {
e.checked_extension = true;
if (auto ext = std::strrchr(e.name, '.')) {
e.extension = ext+1;
}
}
auto text_id = ThemeEntryID_TEXT; auto text_id = ThemeEntryID_TEXT;
const auto selected = m_index == i; const auto selected = m_index == i;
if (selected) { if (selected) {
@@ -626,8 +614,19 @@ void FsView::Draw(NVGcontext* vg, Theme* theme) {
// NOTE: make this native only if i disable dir scan from above. // NOTE: make this native only if i disable dir scan from above.
if (e.IsDir()) { if (e.IsDir()) {
gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f) - 3, 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_BOTTOM, theme->GetColour(text_id), "%zd files"_i18n.c_str(), e.file_count); // NOTE: this takes longer than 16ms when opening a new folder due to it
gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f) + 3, 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP, theme->GetColour(text_id), "%zd dirs"_i18n.c_str(), e.dir_count); // checking all 9 folders at once.
if (!got_dir_count && e.file_count == -1 && e.dir_count == -1) {
got_dir_count = true;
m_fs->DirGetEntryCount(GetNewPath(e), &e.file_count, &e.dir_count);
}
if (e.file_count != -1) {
gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f) - 3, 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_BOTTOM, theme->GetColour(text_id), "%zd files"_i18n.c_str(), e.file_count);
}
if (e.dir_count != -1) {
gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f) + 3, 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP, theme->GetColour(text_id), "%zd dirs"_i18n.c_str(), e.dir_count);
}
} else if (e.IsFile()) { } else if (e.IsFile()) {
if (!e.time_stamp.is_valid) { if (!e.time_stamp.is_valid) {
const auto path = GetNewPath(e); const auto path = GetNewPath(e);
@@ -703,24 +702,17 @@ void FsView::SetIndex(s64 index) {
m_list->SetYoff(); m_list->SetYoff();
} }
if (IsSd() && !m_entries_current.empty() && !GetEntry().checked_internal_extension && IsSamePath(GetEntry().extension, "zip")) { if (IsSd() && !m_entries_current.empty() && !GetEntry().checked_internal_extension && IsSamePath(GetEntry().GetExtension(), "zip")) {
GetEntry().checked_internal_extension = true; GetEntry().checked_internal_extension = true;
if (auto zfile = unzOpen64(GetNewPathCurrent())) { TimeStamp ts;
ON_SCOPE_EXIT(unzClose(zfile)); fs::FsPath filename_inzip{};
if (R_SUCCEEDED(mz::PeekFirstFileName(GetFs(), GetNewPathCurrent(), filename_inzip))) {
// only check first entry (i think RA does the same) if (auto ext = std::strrchr(filename_inzip, '.')) {
fs::FsPath filename_inzip{}; GetEntry().internal_name = filename_inzip.toString();
unz_file_info64 file_info{}; GetEntry().internal_extension = ext+1;
if (UNZ_OK == unzOpenCurrentFile(zfile)) {
ON_SCOPE_EXIT(unzCloseCurrentFile(zfile));
if (UNZ_OK == unzGetCurrentFileInfo64(zfile, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0)) {
if (auto ext = std::strrchr(filename_inzip, '.')) {
GetEntry().internal_name = filename_inzip.toString();
GetEntry().internal_extension = ext+1;
}
}
} }
log_write("\tzip, time taken: %.2fs %zums\n", ts.GetSecondsD(), ts.GetMs());
} }
} }
@@ -737,7 +729,7 @@ void FsView::InstallForwarder() {
const auto assoc_list = m_menu->FindFileAssocFor(); const auto assoc_list = m_menu->FindFileAssocFor();
if (assoc_list.empty()) { if (assoc_list.empty()) {
log_write("failed to find assoc for: %s ext: %s\n", GetEntry().name, GetEntry().extension.c_str()); log_write("failed to find assoc for: %s ext: %s\n", GetEntry().name, GetEntry().GetExtension().c_str());
return; return;
} }
@@ -1081,9 +1073,6 @@ auto FsView::Scan(const fs::FsPath& new_path, bool is_walk_up) -> Result {
i++; i++;
} }
m_entries.shrink_to_fit();
m_entries_index.shrink_to_fit();
m_entries_index_hidden.shrink_to_fit();
Sort(); Sort();
// quick check to see if this is an update folder // quick check to see if this is an update folder
@@ -2000,8 +1989,8 @@ auto Menu::FindFileAssocFor() -> std::vector<FileAssocEntry> {
// only support roms in correctly named folders, sorry! // only support roms in correctly named folders, sorry!
const auto db_indexs = GetRomDatabaseFromPath(view->m_path); const auto db_indexs = GetRomDatabaseFromPath(view->m_path);
const auto& entry = view->GetEntry(); const auto& entry = view->GetEntry();
const auto extension = entry.extension; const auto extension = entry.GetExtension();
const auto internal_extension = entry.internal_extension.empty() ? entry.extension : entry.internal_extension; const auto internal_extension = entry.GetInternalExtension();
if (extension.empty() && internal_extension.empty()) { if (extension.empty() && internal_extension.empty()) {
// log_write("failed to get extension for db: %s path: %s\n", database_entry.c_str(), m_path); // log_write("failed to get extension for db: %s path: %s\n", database_entry.c_str(), m_path);
return {}; return {};