libeuropa/io: clean up PakWriter a bit more
also use visitor pattern instead of get_if chain
This commit is contained in:
parent
7cbff3d81f
commit
175a5ff40a
3 changed files with 52 additions and 34 deletions
|
@ -73,6 +73,11 @@ namespace europa::io {
|
|||
return std::get_if<T>(&variant_);
|
||||
}
|
||||
|
||||
template <class Visitor>
|
||||
auto Visit(Visitor&& v) const {
|
||||
return std::visit(v, variant_);
|
||||
}
|
||||
|
||||
// private:
|
||||
PakFileData::Variant variant_;
|
||||
};
|
||||
|
|
|
@ -14,34 +14,35 @@
|
|||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#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<std::string, PakFile>;
|
||||
|
||||
//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<FlattenedType>&& 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<FlattenedType>&& vec, PakProgressReportSink& sink, SectorAlignment sectorAlignment = SectorAlignment::DoNotAlign);
|
||||
|
||||
private:
|
||||
template <class T>
|
||||
void WriteImpl(std::ostream& os, std::vector<FlattenedType>&& vec, PakProgressReportSink& sink, SectorAlignment sectorAlignment);
|
||||
|
||||
template<class T>
|
||||
void WriteImpl(std::ostream& os, std::vector<FlattenedType>&& vec, PakProgressReportSink& sink, bool sectorAligned = false);
|
||||
|
||||
structs::PakVersion version{};
|
||||
//HeaderType pakHeader {};
|
||||
structs::PakVersion version {};
|
||||
};
|
||||
|
||||
} // namespace europa::io
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <stdexcept>
|
||||
|
||||
#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<FlattenedType>&& vec, PakProgressReportSink& sink) {
|
||||
void PakWriter::Write(std::ostream& os, std::vector<FlattenedType>&& 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<structs::PakHeader_V3>(os, std::move(vec), sink);
|
||||
WriteImpl<structs::PakHeader_V3>(os, std::move(vec), sink, sectorAlignment);
|
||||
break;
|
||||
case structs::PakVersion::Ver4:
|
||||
WriteImpl<structs::PakHeader_V4>(os, std::move(vec), sink);
|
||||
WriteImpl<structs::PakHeader_V4>(os, std::move(vec), sink, sectorAlignment);
|
||||
break;
|
||||
case structs::PakVersion::Ver5:
|
||||
WriteImpl<structs::PakHeader_V5>(os, std::move(vec), sink);
|
||||
WriteImpl<structs::PakHeader_V5>(os, std::move(vec), sink, sectorAlignment);
|
||||
break;
|
||||
default:
|
||||
throw std::invalid_argument("Invalid version");
|
||||
|
@ -51,7 +54,7 @@ namespace europa::io {
|
|||
}
|
||||
|
||||
template <class THeader>
|
||||
void PakWriter::WriteImpl(std::ostream& os, std::vector<FlattenedType>&& vec, PakProgressReportSink& sink, bool sectorAligned) {
|
||||
void PakWriter::WriteImpl(std::ostream& os, std::vector<FlattenedType>&& vec, PakProgressReportSink& sink, SectorAlignment sectorAlignment) {
|
||||
std::vector<FlattenedType> 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<std::filesystem::path>(); path) {
|
||||
auto fs = std::ifstream((*path).string(), std::ifstream::binary);
|
||||
// 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.)
|
||||
|
||||
// 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 from the file stream efficiently
|
||||
// Tee data into the package file stream
|
||||
impl::TeeInOut(fs, os);
|
||||
} else if(auto* buffer = fileData.template GetIf<std::vector<std::uint8_t>>(); buffer) {
|
||||
// will eventually use this so we dont have to round trip to file IO probably
|
||||
os.write(reinterpret_cast<const char*>((*buffer).data()), file.GetSize());
|
||||
},
|
||||
|
||||
[&](const std::vector<std::uint8_t>& buffer) {
|
||||
os.write(reinterpret_cast<const char*>(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);
|
||||
|
|
Loading…
Reference in a new issue