diff --git a/include/europa/io/PakFile.hpp b/include/europa/io/PakFile.hpp index c6e5169..016fe91 100644 --- a/include/europa/io/PakFile.hpp +++ b/include/europa/io/PakFile.hpp @@ -73,6 +73,11 @@ namespace europa::io { return std::get_if(&variant_); } + template + auto Visit(Visitor&& v) const { + return std::visit(v, variant_); + } + // private: PakFileData::Variant variant_; }; diff --git a/include/europa/io/PakWriter.hpp b/include/europa/io/PakWriter.hpp index c9bd814..e4f7631 100644 --- a/include/europa/io/PakWriter.hpp +++ b/include/europa/io/PakWriter.hpp @@ -14,34 +14,35 @@ #include #include #include + #include "europa/structs/Pak.hpp" namespace europa::io { - /** - * Writer for package files. - */ + /// A efficient writer for Europa package (.pak) files. struct PakWriter { + /// Vocabulary type for making sector alignment stuff a bit easier to see. + enum class SectorAlignment { + DoNotAlign, /// Do not align to a sector boundary + Align /// Align to a sector boundary + }; + using FlattenedType = std::pair; - //void Init(structs::PakHeader::Version version); - - //const HeaderType& GetHeader() const { return pakHeader; } - + /// Initalize for the given package version. void SetVersion(structs::PakVersion version); - /** - * Write the resulting archive to the given output stream. - */ - void Write(std::ostream& os, std::vector&& vec, PakProgressReportSink& sink); + /// Write archive to the given output stream. + /// [vec] is all files which should be packaged + /// [sink] is a implementation of PakProgressReportsSink which should get events (TODO: Make this optional) + /// [sectorAlignment] controls sector alignment + void Write(std::ostream& os, std::vector&& vec, PakProgressReportSink& sink, SectorAlignment sectorAlignment = SectorAlignment::DoNotAlign); private: + template + void WriteImpl(std::ostream& os, std::vector&& vec, PakProgressReportSink& sink, SectorAlignment sectorAlignment); - template - void WriteImpl(std::ostream& os, std::vector&& vec, PakProgressReportSink& sink, bool sectorAligned = false); - - structs::PakVersion version{}; - //HeaderType pakHeader {}; + structs::PakVersion version {}; }; } // namespace europa::io diff --git a/src/libeuropa/io/PakWriter.cpp b/src/libeuropa/io/PakWriter.cpp index 28e5682..46fe8c9 100644 --- a/src/libeuropa/io/PakWriter.cpp +++ b/src/libeuropa/io/PakWriter.cpp @@ -15,6 +15,7 @@ #include #include "europa/structs/Pak.hpp" +#include "europa/util/Overloaded.hpp" #include "StreamUtils.h" namespace europa::io { @@ -34,16 +35,18 @@ namespace europa::io { return (-value) & alignment - 1; } - void PakWriter::Write(std::ostream& os, std::vector&& vec, PakProgressReportSink& sink) { + void PakWriter::Write(std::ostream& os, std::vector&& vec, PakProgressReportSink& sink, SectorAlignment sectorAlignment) { + // Depending on the version, do a mix of runtime/compile-time dispatch to the right + // package format version we have been told to write. switch(version) { case structs::PakVersion::Ver3: - WriteImpl(os, std::move(vec), sink); + WriteImpl(os, std::move(vec), sink, sectorAlignment); break; case structs::PakVersion::Ver4: - WriteImpl(os, std::move(vec), sink); + WriteImpl(os, std::move(vec), sink, sectorAlignment); break; case structs::PakVersion::Ver5: - WriteImpl(os, std::move(vec), sink); + WriteImpl(os, std::move(vec), sink, sectorAlignment); break; default: throw std::invalid_argument("Invalid version"); @@ -51,7 +54,7 @@ namespace europa::io { } template - void PakWriter::WriteImpl(std::ostream& os, std::vector&& vec, PakProgressReportSink& sink, bool sectorAligned) { + void PakWriter::WriteImpl(std::ostream& os, std::vector&& vec, PakProgressReportSink& sink, SectorAlignment sectorAlignment) { std::vector sortedFiles = std::move(vec); THeader pakHeader {}; @@ -71,7 +74,7 @@ namespace europa::io { } // Align first file to sector boundary. - if(sectorAligned) + if(sectorAlignment == SectorAlignment::Align) os.seekp( AlignBy(os.tellp(), kCDSectorSize), std::istream::beg); @@ -88,21 +91,30 @@ namespace europa::io { auto& fileData = file.GetData(); - // FIXME: use a visitor or something. For now I'm lazy and this should work - if(auto* path = fileData.template GetIf(); path) { - auto fs = std::ifstream((*path).string(), std::ifstream::binary); - if(!fs) - throw std::runtime_error("couldnt open input file? HOW"); + // Visit the file data sum type and do the right operation + // For filesystem paths, we open the file and then tee it into the package file + // effiently saving a lot of memory usage when packing (trading off some IO overhead, + // but hey.) - // tee data from the file stream efficiently - impl::TeeInOut(fs, os); - } else if(auto* buffer = fileData.template GetIf>(); buffer) { - // will eventually use this so we dont have to round trip to file IO probably - os.write(reinterpret_cast((*buffer).data()), file.GetSize()); - } + // clang-format off + fileData.Visit(overloaded { + [&](const std::filesystem::path& path) { + auto fs = std::ifstream(path.string(), std::ifstream::binary); + if(!fs) + throw std::runtime_error("couldnt open input file? HOW"); + + // Tee data into the package file stream + impl::TeeInOut(fs, os); + }, + + [&](const std::vector& buffer) { + os.write(reinterpret_cast(buffer.data()), file.GetSize()); + } + }); + // clang-format on // Align to sector boundary. - if(sectorAligned) + if(sectorAlignment == SectorAlignment::Align) os.seekp( AlignBy(os.tellp(), kCDSectorSize), std::istream::beg);