diff --git a/CMakeLists.txt b/CMakeLists.txt index f128860..7c68a61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.10) project(GodotPckTool) set(GODOT_PCK_TOOL_VERSION_MAJOR 1) -set(GODOT_PCK_TOOL_VERSION_MINOR 3) +set(GODOT_PCK_TOOL_VERSION_MINOR 4) set(GODOT_PCK_TOOL_VERSION_STR "${GODOT_PCK_TOOL_VERSION_MAJOR}.${GODOT_PCK_TOOL_VERSION_MINOR}") diff --git a/README.md b/README.md index 2dc7135..b7c4163 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,38 @@ Long form: godotpcktool --pack Thrive.pck --action add --remove-prefix extracted --file extracted ``` +### Filters + +Filters can be used to only act on a subset of files in a pck file, or +from the filesystem. + +#### Min size + +Specify the minimum size under which files are excluded: + +```sh +godotpcktool --min-size-filter 1000 +``` + +This will exclude files with size 999 bytes and below. + +### Max size + +Specify the maximum size above which files are excluded: + +```sh +godotpcktool --max-size-filter 1000 +``` + +NOTE: if you use max size to compliment min size extraction, you +should subtract one from the size, otherwise you'll operate on the +same files twice. + +However if you want to work on exactly some size files you can specify the same size twice: +```sh +godotpcktool --min-size-filter 1 --max-size-filter 1 +``` + ### General info In the long form multiple files may be included like this: diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a2c36f2..ce053a7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,7 @@ add_library(pck pck/PckFile.h pck/PckFile.cpp PckTool.h PckTool.cpp + FileFilter.h FileFilter.cpp Include.h Define.h ) diff --git a/src/FileFilter.cpp b/src/FileFilter.cpp new file mode 100644 index 0000000..4d7b75e --- /dev/null +++ b/src/FileFilter.cpp @@ -0,0 +1,17 @@ +// ------------------------------------ // +#include "FileFilter.h" + +using namespace pcktool; +// ------------------------------------ // +bool FileFilter::Include(const PckFile::ContainedFile& file) +{ + if(file.Size < MinSizeLimit) + return false; + + if(file.Size > MaxSizeLimit) + return false; + + // Wasn't excluded + return true; +} +// ------------------------------------ // diff --git a/src/FileFilter.h b/src/FileFilter.h new file mode 100644 index 0000000..0569348 --- /dev/null +++ b/src/FileFilter.h @@ -0,0 +1,33 @@ +#pragma once + +#include "Define.h" + +#include "pck/PckFile.h" + +namespace pcktool { + +//! \brief An include / exclude filter for files +class FileFilter { +public: + //! \returns true if filter doesn't exclude a file + bool Include(const PckFile::ContainedFile& file); + + void SetSizeMinLimit(uint64_t size) + { + MinSizeLimit = size; + } + + void SetSizeMaxLimit(uint64_t size) + { + MaxSizeLimit = size; + } + +private: + //! File is excluded if it is under this size + uint64_t MinSizeLimit = 0; + + //! File is excluded if it is over this size + uint64_t MaxSizeLimit = std::numeric_limits::max(); +}; + +} // namespace pcktool diff --git a/src/PckTool.cpp b/src/PckTool.cpp index 0f73eb4..a66014d 100644 --- a/src/PckTool.cpp +++ b/src/PckTool.cpp @@ -68,13 +68,13 @@ int PckTool::Run() return 0; } else if(Opts.Action == "add" || Opts.Action == "a") { - std::unique_ptr pck; - if(Files.empty()) { std::cout << "ERROR: no files specified\n"; return 1; } + std::unique_ptr pck; + if(TargetExists()) { std::cout << "Target pck exists, loading it before adding new files\n"; @@ -88,6 +88,8 @@ int PckTool::Run() } else { pck = std::make_unique(Opts.Pack); + SetIncludeFilter(*pck); + pck->SetGodotVersion(Opts.GodotMajor, Opts.GodotMinor, Opts.GodotPatch); } @@ -140,6 +142,8 @@ std::unique_ptr PckTool::LoadPck() auto pck = std::make_unique(Opts.Pack); + SetIncludeFilter(*pck); + if(!pck->Load()) { std::cout << "ERROR: couldn't load pck file: " << pck->GetPath() << "\n"; return nullptr; @@ -148,6 +152,11 @@ std::unique_ptr PckTool::LoadPck() return pck; } // ------------------------------------ // +void PckTool::SetIncludeFilter(PckFile& pck) +{ + pck.SetIncludeFilter(std::bind(&FileFilter::Include, Opts.Filter, std::placeholders::_1)); +} +// ------------------------------------ // bool PckTool::BuildFileList() { // Copy standard files diff --git a/src/PckTool.h b/src/PckTool.h index ad0a344..6de800b 100644 --- a/src/PckTool.h +++ b/src/PckTool.h @@ -2,6 +2,8 @@ #include "Define.h" +#include "FileFilter.h" + #include #include @@ -35,6 +37,8 @@ class PckTool { int GodotPatch; json FileCommands; + + FileFilter Filter; }; public: @@ -53,6 +57,8 @@ class PckTool { std::unique_ptr LoadPck(); + void SetIncludeFilter(PckFile& pck); + private: Options Opts; diff --git a/src/main.cpp b/src/main.cpp index a9c68a9..e3c59a8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,5 @@ #include "Define.h" +#include "FileFilter.h" #include "PckTool.h" #include @@ -50,6 +51,10 @@ int main(int argc, char* argv[]) cxxopts::value()) ("set-godot-version", "Set the godot version to use when creating a new pck", cxxopts::value()->default_value("3.0.0")) + ("min-size-filter", "Set minimum size for files to include in operation", + cxxopts::value()) + ("max-size-filter", "Set maximum size for files to include in operation", + cxxopts::value()) ("v,version", "Print version and quit") ("h,help", "Print help and quit") ; @@ -85,6 +90,7 @@ int main(int argc, char* argv[]) std::string removePrefix; int godotMajor, godotMinor, godotPatch; nlohmann::json fileCommands; + pcktool::FileFilter filter; if(result.count("file")) { files = result["file"].as(); @@ -102,6 +108,18 @@ int main(int argc, char* argv[]) pack = result["pack"].as(); } + if(result.count("pack")) { + pack = result["pack"].as(); + } + + if(result.count("min-size-filter")) { + filter.SetSizeMinLimit(result["min-size-filter"].as()); + } + + if(result.count("max-size-filter")) { + filter.SetSizeMaxLimit(result["max-size-filter"].as()); + } + action = result["action"].as(); try { @@ -144,7 +162,7 @@ int main(int argc, char* argv[]) } auto tool = pcktool::PckTool({pack, action, files, output, removePrefix, godotMajor, - godotMinor, godotPatch, fileCommands}); + godotMinor, godotPatch, fileCommands, filter}); return tool.Run(); } diff --git a/src/pck/PckFile.cpp b/src/pck/PckFile.cpp index 485459d..1208888 100644 --- a/src/pck/PckFile.cpp +++ b/src/pck/PckFile.cpp @@ -77,6 +77,9 @@ bool PckFile::Load() return ReadContainedFileContents(offset, size); }; + if(IncludeFilter && !IncludeFilter(entry)) + continue; + Contents[entry.Path] = std::move(entry); }; @@ -114,6 +117,7 @@ bool PckFile::Save() Write32(0); } + // Things are filtered before adding to Contents, so we don't do any filtering here // File count Write32(Contents.size()); @@ -148,6 +152,8 @@ bool PckFile::Save() // Align int alignment = 0; + // TODO: add command line flag to enable alignment + if(alignment >= 4) { while(File->tellg() % alignment != 0) { uint8_t null = 0; @@ -200,6 +206,9 @@ bool PckFile::Save() // ------------------------------------ // void PckFile::AddFile(ContainedFile&& file) { + if(IncludeFilter && !IncludeFilter(file)) + return; + Contents[file.Path] = std::move(file); } // ------------------------------------ // @@ -232,8 +241,6 @@ void PckFile::AddSingleFile(const std::string& filesystemPath, std::string pckPa if(pckPath.empty()) throw std::runtime_error("path inside pck is empty to add file to"); - std::cout << "Adding " << filesystemPath << " as " << pckPath << "\n"; - ContainedFile file; const auto size = std::filesystem::file_size(filesystemPath); @@ -261,6 +268,12 @@ void PckFile::AddSingleFile(const std::string& filesystemPath, std::string pckPa return data; }; + // TODO: might be nice to add an option to print out rejected files + if(IncludeFilter && !IncludeFilter(file)) + return; + + std::cout << "Adding " << filesystemPath << " as " << pckPath << "\n"; + Contents[pckPath] = file; } diff --git a/src/pck/PckFile.h b/src/pck/PckFile.h index 451e208..588bdf2 100644 --- a/src/pck/PckFile.h +++ b/src/pck/PckFile.h @@ -70,6 +70,14 @@ class PckFile { PatchGodotVersion = patch; } + //! \brief Sets a filter for entries to be added to this object + //! + //! This must be set before loading the data. The Save method doesn't apply the filter. + void SetIncludeFilter(std::function callback) + { + IncludeFilter = callback; + } + inline const auto& GetPath() { return Path; @@ -96,6 +104,9 @@ class PckFile { size_t PadPathsToMultipleWithNULLS = 4; std::map Contents; + + //! Used in a bunch of operations to check if a file entry should be included or ignored + std::function IncludeFilter; }; } // namespace pcktool