From 8b72f08dcc23f5065d55752da2d329cd0feea5a6 Mon Sep 17 00:00:00 2001 From: J-D-K Date: Mon, 8 Sep 2025 12:51:15 -0400 Subject: [PATCH] Update ZIP logic to include and detect directory entries. Fixes 289. --- Makefile | 2 +- include/fs/MiniUnzip.hpp | 3 +++ include/fs/MiniZip.hpp | 4 ++++ source/JKSV.cpp | 2 +- source/fs/MiniUnzip.cpp | 7 ++++++ source/fs/MiniZip.cpp | 9 ++++++++ source/fs/zip.cpp | 47 ++++++++++++++++++++-------------------- 7 files changed, 48 insertions(+), 26 deletions(-) diff --git a/Makefile b/Makefile index 785b013..b2b4b55 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ INCLUDES := include ./Libraries/FsLib/Switch/FsLib/include ./Libraries/SDLLib/SD EXEFS_SRC := exefs_src APP_TITLE := JKSV APP_AUTHOR := JK -APP_VERSION := 08.25.2025 +APP_VERSION := 09.08.2025 ROMFS := romfs ICON := icon.jpg diff --git a/include/fs/MiniUnzip.hpp b/include/fs/MiniUnzip.hpp index e2d4c74..c9041fc 100644 --- a/include/fs/MiniUnzip.hpp +++ b/include/fs/MiniUnzip.hpp @@ -41,6 +41,9 @@ namespace fs /// @brief Reads from the currently open file to the buffer passed. ssize_t read(void *buffer, size_t bufferSize); + /// @brief Returns if the entry is just a directory. + bool is_directory() const noexcept; + /// @brief Returns the name of the current file. const char *get_filename() const noexcept; diff --git a/include/fs/MiniZip.hpp b/include/fs/MiniZip.hpp index 480c30b..bb5129d 100644 --- a/include/fs/MiniZip.hpp +++ b/include/fs/MiniZip.hpp @@ -28,6 +28,10 @@ namespace fs /// @brief Manual call for closing the zipFile. void close(); + /// @brief Creates a new dummy directory entry in the ZIP. + /// @param Path Path of the directory. Will be appended with a trailing slash if needed. + void add_directory(std::string_view path); + /// @brief Opens a new file with filename as the path. bool open_new_file(std::string_view filename, bool trimPath = false, size_t trimPlaces = 0); diff --git a/source/JKSV.cpp b/source/JKSV.cpp index a270582..c9a7be3 100644 --- a/source/JKSV.cpp +++ b/source/JKSV.cpp @@ -32,7 +32,7 @@ namespace /// @brief Build month. constexpr uint8_t BUILD_MON = 9; /// @brief Build day. - constexpr uint8_t BUILD_DAY = 6; + constexpr uint8_t BUILD_DAY = 8; /// @brief Year. constexpr uint16_t BUILD_YEAR = 2025; diff --git a/source/fs/MiniUnzip.cpp b/source/fs/MiniUnzip.cpp index bdb6f16..fb47add 100644 --- a/source/fs/MiniUnzip.cpp +++ b/source/fs/MiniUnzip.cpp @@ -59,6 +59,13 @@ bool fs::MiniUnzip::reset() ssize_t fs::MiniUnzip::read(void *buffer, size_t bufferSize) { return unzReadCurrentFile(m_unz, buffer, bufferSize); } +bool fs::MiniUnzip::is_directory() const noexcept +{ + const size_t length = std::char_traits::length(m_filename); + + return m_filename[length - 1] == '/'; +} + const char *fs::MiniUnzip::get_filename() const noexcept { return m_filename; } uint64_t fs::MiniUnzip::get_compressed_size() const noexcept { return m_fileInfo.compressed_size; } diff --git a/source/fs/MiniZip.cpp b/source/fs/MiniZip.cpp index 6d85bc2..9e4f8b8 100644 --- a/source/fs/MiniZip.cpp +++ b/source/fs/MiniZip.cpp @@ -37,6 +37,15 @@ void fs::MiniZip::close() m_isOpen = false; } +void fs::MiniZip::add_directory(std::string_view filename) +{ + std::string zipPath{filename}; + if (zipPath.back() != '/') { zipPath.append("/"); } + + MiniZip::open_new_file(zipPath); + MiniZip::close_current_file(); +} + bool fs::MiniZip::open_new_file(std::string_view filename, bool trimPath, size_t trimPlaces) { const size_t pathBegin = filename.find_first_of('/'); diff --git a/source/fs/zip.cpp b/source/fs/zip.cpp index a25391c..130ce35 100644 --- a/source/fs/zip.cpp +++ b/source/fs/zip.cpp @@ -23,9 +23,6 @@ namespace /// @brief Buffer size used for decompressing files from ZIP. constexpr size_t SIZE_UNZIP_BUFFER = 0x600000; - - /// @brief This is the file written to ZIP backups to preserve empty directories. - constexpr std::string_view NAME_SAVE_ME = ".save_me"; } // namespace // Shared struct for Zip/File IO @@ -103,19 +100,14 @@ void fs::copy_directory_to_zip(const fslib::Path &source, fs::MiniZip &dest, sys fslib::Directory sourceDir{source}; if (error::fslib(sourceDir.is_open())) { return; } - // This is needed to preserve empty directories. - if (sourceDir.get_count() <= 0) - { - const fslib::Path saveMe{source / NAME_SAVE_ME}; - dest.open_new_file(saveMe.get_path()); - dest.close_current_file(); - return; - } - for (const fslib::DirectoryEntry &entry : sourceDir) { const fslib::Path fullSource{source / entry}; - if (entry.is_directory()) { fs::copy_directory_to_zip(fullSource, dest, task); } + if (entry.is_directory()) + { + dest.add_directory(fullSource.string()); + fs::copy_directory_to_zip(fullSource, dest, task); + } else { fslib::File sourceFile{fullSource, FsOpenMode_Read}; @@ -181,20 +173,27 @@ void fs::copy_zip_to_directory(fs::MiniUnzip &unzip, const fslib::Path &dest, in do { if (unzip.get_filename() == fs::NAME_SAVE_META) { continue; } + else if (unzip.is_directory()) + { + // Just do this and continue. + const fslib::Path dirPath{dest / unzip.get_filename()}; + error::fslib(fslib::create_directories_recursively(dirPath)); + continue; + } fslib::Path fullDest{dest / unzip.get_filename()}; const size_t lastDir = fullDest.find_last_of('/'); if (lastDir == fullDest.NOT_FOUND) { continue; } - const fslib::Path dirPath{fullDest.sub_path(lastDir)}; - const bool isValid = dirPath.is_valid(); - const bool exists = isValid && fslib::directory_exists(dirPath); - const bool createError = isValid && !exists && error::fslib(fslib::create_directories_recursively(dirPath)); - bool commitError = !createError && error::fslib(fslib::commit_data_to_file_system(dirPath.get_device_name())); - if (isValid && !exists && (createError || commitError)) { continue; } - - // This is here so the directory still gets created if needed. - if (fullDest.get_filename() == NAME_SAVE_ME) { continue; } + if (lastDir > 0) + { + const fslib::Path dirPath{fullDest.sub_path(lastDir)}; + const bool isValid = dirPath.is_valid(); + const bool exists = isValid && fslib::directory_exists(dirPath); + const bool createError = isValid && !exists && error::fslib(fslib::create_directories_recursively(dirPath)); + bool commitError = !createError && error::fslib(fslib::commit_data_to_file_system(dirPath.get_device_name())); + if (isValid && !exists && (createError || commitError)) { continue; } + } const int64_t fileSize = unzip.get_uncompressed_size(); fslib::File destFile{fullDest, FsOpenMode_Create | FsOpenMode_Write, fileSize}; @@ -237,7 +236,7 @@ void fs::copy_zip_to_directory(fs::MiniUnzip &unzip, const fslib::Path &dest, in if (commitNeeded) { destFile.close(); - commitError = error::fslib(fslib::commit_data_to_file_system(dest.get_device_name())); + const bool commitError = error::fslib(fslib::commit_data_to_file_system(dest.get_device_name())); if (commitError) { ui::PopMessageManager::push_message(popTicks, popCommitFailed); } // To do: How to recover? destFile.open(fullDest, FsOpenMode_Write); @@ -254,7 +253,7 @@ void fs::copy_zip_to_directory(fs::MiniUnzip &unzip, const fslib::Path &dest, in readThread.join(); destFile.close(); - commitError = needCommits && error::fslib(fslib::commit_data_to_file_system(dest.get_device_name())); + const bool commitError = needCommits && error::fslib(fslib::commit_data_to_file_system(dest.get_device_name())); if (commitError) { ui::PopMessageManager::push_message(popTicks, popCommitFailed); } } while (unzip.next_file()); }