diff --git a/README.md b/README.md index 7d1ca4c..a9767c8 100644 --- a/README.md +++ b/README.md @@ -74,10 +74,26 @@ gfs::MountID mountId = dataMount; std::filesystem::path filename = "archive_file.pbin"; std::vector files{ 98475845, 111, 222, 666 }; bool wasCreated = fs.CreateArchive(mountId, filename, files); + +/* Import files */ +struct MyImporter : gfs::FileImporter +{ + bool Import(gfs::Filesystem& fs, const std::filesystem::path& importFilename, gfs::MountID outputMount, const std::filesystem::path& outputDir) override + { + ... + } + + bool Reimport(gfs::Filesystem& fs, const gfs::Filesystem::File& file) override + { + ... + } +}; +fs.SetImporter({ ".txt", ".ext" }, std::make_shared()); +bool wasImported = fs.Import("path/to/external/file.txt", mountId, "mount/rel/output/dir/"); +bool wasReimported = fs.Reimport(fileId); + ``` ## Planned Features -- Add data compression threshold eg. only compress data greater than ~0.5MB -- File importing - Call Filesystem::ImportFile(filename) which gets a FileImporter assigned to file ext and reads, processes and writes a new file - - Files that were imported track the source filename - allows for reimport, which would potentially allow for asset/resource hot-reloading \ No newline at end of file +- Add data compression threshold eg. only compress data greater than ~0.5MB \ No newline at end of file diff --git a/include/gfs/file_importer.hpp b/include/gfs/file_importer.hpp index e48f6e0..64fae48 100644 --- a/include/gfs/file_importer.hpp +++ b/include/gfs/file_importer.hpp @@ -6,12 +6,13 @@ namespace gfs { - class FileImporter - { - public: - virtual ~FileImporter() = default; + class FileImporter + { + public: + virtual ~FileImporter() = default; - virtual bool Import(const std::filesystem::path& importFilename, const std::filesystem::path& outputDir) = 0; - virtual bool Reimport(const Filesystem::File& file) = 0; - }; -} \ No newline at end of file + virtual bool Import(Filesystem& fs, const std::filesystem::path& importFilename, MountID outputMount, const std::filesystem::path& outputDir) = 0; + virtual bool Reimport(Filesystem& fs, const Filesystem::File& file) = 0; + }; + +} // namespace gfs \ No newline at end of file diff --git a/include/gfs/filesystem.hpp b/include/gfs/filesystem.hpp index ce25e4b..1b8a9b0 100644 --- a/include/gfs/filesystem.hpp +++ b/include/gfs/filesystem.hpp @@ -97,6 +97,7 @@ namespace gfs FileID FileId; // The Unique Identifier of the file. MountID MountId; // The Id of the mount this file is in. std::filesystem::path MountRelPath; // Path to this file relative to the mount it is in. + std::filesystem::path SourceFilename; // The source file this file was imported from. std::vector FileDependencies; // Files this file references. uint32_t UncompressedSize; uint32_t CompressedSize; @@ -132,7 +133,8 @@ namespace gfs FileID fileId, const std::vector& fileDependencies, const BinaryStreamable& dataObject, - bool compress); + bool compress, + const std::filesystem::path& sourceFilename = ""); /** * @brief @@ -166,6 +168,10 @@ namespace gfs */ auto GetImporter(const std::string& fileExt) -> std::shared_ptr; + bool Import(const std::filesystem::path& filename, MountID outputMount, const std::filesystem::path& outputDir); + + bool Reimport(FileID fileId); + ////////////////////////////////////////////////////////////////////////// // Utility ////////////////////////////////////////////////////////////////////////// diff --git a/src/gfs/filesystem.cpp b/src/gfs/filesystem.cpp index 5dce9bd..af784d2 100644 --- a/src/gfs/filesystem.cpp +++ b/src/gfs/filesystem.cpp @@ -1,9 +1,12 @@ #include "gfs/filesystem.hpp" #include "gfs/binary_streams.hpp" +#include "gfs/file_importer.hpp" -#include +#include +#include #include +#include #include #include #include @@ -87,7 +90,8 @@ namespace gfs FileID fileId, const std::vector& fileDependencies, const BinaryStreamable& dataObject, - bool compress) + bool compress, + const std::filesystem::path& sourceFilename) { auto* mount = GetMount(mountId); if (!mount) @@ -121,6 +125,7 @@ namespace gfs file.FileId = fileId; file.MountId = mountId; file.MountRelPath = filename; + file.SourceFilename = sourceFilename; file.FileDependencies = fileDependencies; file.UncompressedSize = uint32_t(uncompressedDataBuffer.GetSize()); file.CompressedSize = uint32_t(compressedDataBuffer.GetSize()); @@ -285,6 +290,36 @@ namespace gfs return it->second; } + bool Filesystem::Import(const std::filesystem::path& filename, MountID outputMount, const std::filesystem::path& outputDir) + { + if (filename.empty() || !std::filesystem::exists(filename) || !std::filesystem::is_regular_file(filename)) + return false; + + const auto fileExt = filename.extension(); + auto importer = GetImporter(fileExt); + if (!importer) + return false; + + return importer->Import(*this, filename, outputMount, outputDir); + } + + bool Filesystem::Reimport(FileID fileId) + { + auto* file = GetFile(fileId); + if (!file) + return false; + + if (file->SourceFilename.empty() || !std::filesystem::exists(file->SourceFilename) || !std::filesystem::is_regular_file(file->SourceFilename)) + return false; + + const auto fileExt = file->SourceFilename.extension(); + auto importer = GetImporter(fileExt); + if (!importer) + return false; + + return importer->Reimport(*this, *file); + } + bool Filesystem::IsPathInMount(const std::filesystem::path& path, MountID mountId) { auto* mount = GetMount(mountId); @@ -409,6 +444,12 @@ namespace gfs auto pathStr = file.MountRelPath.string(); stream.write(reinterpret_cast(pathStr.data()), strLen); + // Source filename + strLen = file.SourceFilename.string().size(); + stream.write(reinterpret_cast(&strLen), sizeof(strLen)); + pathStr = file.SourceFilename.string(); + stream.write(reinterpret_cast(pathStr.data()), strLen); + // File dependencies const auto count = uint16_t(file.FileDependencies.size()); stream.write(reinterpret_cast(&count), sizeof(count)); @@ -432,6 +473,13 @@ namespace gfs stream.read(reinterpret_cast(pathStr.data()), strLen); file.MountRelPath = pathStr; + // Source filename + strLen = 0; + stream.read(reinterpret_cast(&strLen), sizeof(strLen)); + pathStr = std::string(strLen, 0); + stream.read(reinterpret_cast(pathStr.data()), strLen); + file.SourceFilename = pathStr; + // File dependencies uint16_t count = 0; stream.read(reinterpret_cast(&count), sizeof(count)); diff --git a/testbed/testbed.cpp b/testbed/testbed.cpp index 4232f0a..be17f7f 100644 --- a/testbed/testbed.cpp +++ b/testbed/testbed.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,29 @@ struct TextResource : gfs::BinaryStreamable void Write(gfs::WriteOnlyByteBuffer& buffer) const override { buffer.Write(Text); } }; +struct TextFileImporter : gfs::FileImporter +{ + bool Import(gfs::Filesystem& fs, const std::filesystem::path& importFilename, gfs::MountID outputMount, const std::filesystem::path& outputDir) override + { + auto text = ReadTextFile(importFilename); + if (text.empty()) + return false; + + TextResource resource{}; + resource.Text = text; + + auto outputFilename = outputDir / importFilename.filename(); + outputFilename.replace_extension(".rbin"); + + const auto fileId = std::hash{}(importFilename); + fs.WriteFile(outputMount, outputFilename, fileId, {}, resource, resource.Text.size() >= 524288, importFilename); + + return true; + } + + bool Reimport(gfs::Filesystem& fs, const gfs::Filesystem::File& file) override { return false; } +}; + int main() { std::cout << "GFS Testbed\n"; @@ -147,6 +171,18 @@ int main() } } + { + // Importing + fs.SetImporter({ ".txt" }, std::make_shared()); + if (!fs.Import("external_files/txt_file.txt", mountA, "")) + assert(false); + + TextResource importedFile{}; + if (!fs.ReadFile(5319311783236469214, importedFile)) + assert(false); + assert(importedFile.Text == shortText.Text); + } + std::cout << "Files" << std::endl; fs.ForEachFile([](const gfs::Filesystem::File& file) { std::cout << "- " << file.FileId << " - " << file.MountRelPath << std::endl; });