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_);
|
return std::get_if<T>(&variant_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class Visitor>
|
||||||
|
auto Visit(Visitor&& v) const {
|
||||||
|
return std::visit(v, variant_);
|
||||||
|
}
|
||||||
|
|
||||||
// private:
|
// private:
|
||||||
PakFileData::Variant variant_;
|
PakFileData::Variant variant_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,34 +14,35 @@
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "europa/structs/Pak.hpp"
|
#include "europa/structs/Pak.hpp"
|
||||||
|
|
||||||
namespace europa::io {
|
namespace europa::io {
|
||||||
|
|
||||||
/**
|
/// A efficient writer for Europa package (.pak) files.
|
||||||
* Writer for package files.
|
|
||||||
*/
|
|
||||||
struct PakWriter {
|
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>;
|
using FlattenedType = std::pair<std::string, PakFile>;
|
||||||
|
|
||||||
//void Init(structs::PakHeader::Version version);
|
/// Initalize for the given package version.
|
||||||
|
|
||||||
//const HeaderType& GetHeader() const { return pakHeader; }
|
|
||||||
|
|
||||||
void SetVersion(structs::PakVersion version);
|
void SetVersion(structs::PakVersion version);
|
||||||
|
|
||||||
/**
|
/// Write archive to the given output stream.
|
||||||
* Write the resulting 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)
|
||||||
void Write(std::ostream& os, std::vector<FlattenedType>&& vec, PakProgressReportSink& sink);
|
/// [sectorAlignment] controls sector alignment
|
||||||
|
void Write(std::ostream& os, std::vector<FlattenedType>&& vec, PakProgressReportSink& sink, SectorAlignment sectorAlignment = SectorAlignment::DoNotAlign);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
template <class T>
|
||||||
|
void WriteImpl(std::ostream& os, std::vector<FlattenedType>&& vec, PakProgressReportSink& sink, SectorAlignment sectorAlignment);
|
||||||
|
|
||||||
template<class T>
|
structs::PakVersion version {};
|
||||||
void WriteImpl(std::ostream& os, std::vector<FlattenedType>&& vec, PakProgressReportSink& sink, bool sectorAligned = false);
|
|
||||||
|
|
||||||
structs::PakVersion version{};
|
|
||||||
//HeaderType pakHeader {};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace europa::io
|
} // namespace europa::io
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "europa/structs/Pak.hpp"
|
#include "europa/structs/Pak.hpp"
|
||||||
|
#include "europa/util/Overloaded.hpp"
|
||||||
#include "StreamUtils.h"
|
#include "StreamUtils.h"
|
||||||
|
|
||||||
namespace europa::io {
|
namespace europa::io {
|
||||||
|
@ -34,16 +35,18 @@ namespace europa::io {
|
||||||
return (-value) & alignment - 1;
|
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) {
|
switch(version) {
|
||||||
case structs::PakVersion::Ver3:
|
case structs::PakVersion::Ver3:
|
||||||
WriteImpl<structs::PakHeader_V3>(os, std::move(vec), sink);
|
WriteImpl<structs::PakHeader_V3>(os, std::move(vec), sink, sectorAlignment);
|
||||||
break;
|
break;
|
||||||
case structs::PakVersion::Ver4:
|
case structs::PakVersion::Ver4:
|
||||||
WriteImpl<structs::PakHeader_V4>(os, std::move(vec), sink);
|
WriteImpl<structs::PakHeader_V4>(os, std::move(vec), sink, sectorAlignment);
|
||||||
break;
|
break;
|
||||||
case structs::PakVersion::Ver5:
|
case structs::PakVersion::Ver5:
|
||||||
WriteImpl<structs::PakHeader_V5>(os, std::move(vec), sink);
|
WriteImpl<structs::PakHeader_V5>(os, std::move(vec), sink, sectorAlignment);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw std::invalid_argument("Invalid version");
|
throw std::invalid_argument("Invalid version");
|
||||||
|
@ -51,7 +54,7 @@ namespace europa::io {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class THeader>
|
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);
|
std::vector<FlattenedType> sortedFiles = std::move(vec);
|
||||||
|
|
||||||
THeader pakHeader {};
|
THeader pakHeader {};
|
||||||
|
@ -71,7 +74,7 @@ namespace europa::io {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Align first file to sector boundary.
|
// Align first file to sector boundary.
|
||||||
if(sectorAligned)
|
if(sectorAlignment == SectorAlignment::Align)
|
||||||
os.seekp(
|
os.seekp(
|
||||||
AlignBy(os.tellp(), kCDSectorSize),
|
AlignBy(os.tellp(), kCDSectorSize),
|
||||||
std::istream::beg);
|
std::istream::beg);
|
||||||
|
@ -88,21 +91,30 @@ namespace europa::io {
|
||||||
|
|
||||||
auto& fileData = file.GetData();
|
auto& fileData = file.GetData();
|
||||||
|
|
||||||
// FIXME: use a visitor or something. For now I'm lazy and this should work
|
// Visit the file data sum type and do the right operation
|
||||||
if(auto* path = fileData.template GetIf<std::filesystem::path>(); path) {
|
// For filesystem paths, we open the file and then tee it into the package file
|
||||||
auto fs = std::ifstream((*path).string(), std::ifstream::binary);
|
// 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)
|
if(!fs)
|
||||||
throw std::runtime_error("couldnt open input file? HOW");
|
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);
|
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.
|
// Align to sector boundary.
|
||||||
if(sectorAligned)
|
if(sectorAlignment == SectorAlignment::Align)
|
||||||
os.seekp(
|
os.seekp(
|
||||||
AlignBy(os.tellp(), kCDSectorSize),
|
AlignBy(os.tellp(), kCDSectorSize),
|
||||||
std::istream::beg);
|
std::istream::beg);
|
||||||
|
|
Loading…
Reference in a new issue