libeuropa/io: Split pak into new subdirectory

Namespaces exist. Use them, perhaps.
This commit is contained in:
Lily Tsuru 2025-01-15 21:50:34 -05:00
parent 087657d5c1
commit 84c537c873
15 changed files with 102 additions and 99 deletions

View file

@ -18,13 +18,13 @@
#include <variant>
#include <vector>
namespace europa::io {
namespace europa::io::pak {
struct PakReader;
struct PakWriter;
struct Reader;
struct Writer;
/// sumtype
struct PakFileData {
/// sumtype for package file data
struct FileData {
// clang-format off
using Variant = std::variant<
// File data
@ -35,14 +35,14 @@ namespace europa::io {
>;
// clang-format on
static PakFileData InitAsBuffer(std::vector<std::uint8_t>&& buffer) {
return PakFileData {
static FileData InitAsBuffer(std::vector<std::uint8_t>&& buffer) {
return FileData {
.variant_ = Variant(std::move(buffer))
};
}
static PakFileData InitAsPath(const std::filesystem::path& path) {
return PakFileData {
static FileData InitAsPath(const std::filesystem::path& path) {
return FileData {
.variant_ = Variant(path)
};
}
@ -80,13 +80,13 @@ namespace europa::io {
}
// private:
PakFileData::Variant variant_;
FileData::Variant variant_;
};
/// Repressents a package file. Can either hold a memory buffer of contents
/// or a filesystem path (for creating packages).
struct PakFile {
using DataType = PakFileData;
struct File {
using DataType = FileData;
template <class T>
void InitWithExistingTocEntry(const T& value) {
@ -194,13 +194,15 @@ namespace europa::io {
}
private:
friend PakReader;
friend PakWriter;
// FIXME: Are these `friend`s required? I don't think so,
// we use public APIs now.
friend Reader;
friend Writer;
std::optional<PakFileData> fileData;
std::optional<FileData> fileData;
structs::PakTocEntryVariant toc;
};
} // namespace europa::io
} // namespace europa::io::pak
#endif // EUROPA_IO_PAKFILE_H

View file

@ -9,22 +9,21 @@
#ifndef EUROPA_IO_PAKREADER_H
#define EUROPA_IO_PAKREADER_H
#include <europa/io/PakFile.hpp>
#include <europa/io/pak/File.hpp>
#include <europa/structs/Pak.hpp>
#include <iosfwd>
#include <string>
#include <map>
namespace europa::io {
namespace europa::io::pak {
/// Reader for Europa package files (.pak).
struct PakReader {
using FlatType = std::pair<std::string, PakFile>;
struct Reader {
using FlatType = std::pair<std::string, File>;
using MapType = std::vector<FlatType>;
/// Constructor. Takes in a input stream to read pak data from.
/// This stream should only be used by the PakReader, nothing else.
explicit PakReader(std::istream& is);
explicit Reader(std::istream& is);
/// Reads the header and the file TOC.
/// This function should be called first.
@ -61,6 +60,6 @@ namespace europa::io {
MapType files;
};
} // namespace europa::io
} // namespace europa::io::pak
#endif // EUROPA_IO_PAKREADER_H

View file

@ -9,29 +9,29 @@
#ifndef EUROPA_IO_PAKWRITER_H
#define EUROPA_IO_PAKWRITER_H
#include <europa/io/PakFile.hpp>
#include <europa/io/PakProgressReportSink.hpp>
#include <europa/io/pak/File.hpp>
#include <europa/io/pak/WriterProgressReportSink.hpp>
#include <iosfwd>
#include <string>
#include <utility>
#include "europa/structs/Pak.hpp"
namespace europa::io {
namespace europa::io::pak {
/// A efficient writer for Europa package (.pak) files.
struct PakWriter {
struct Writer {
/// 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, File>;
constexpr PakWriter() = default;
constexpr Writer() = default;
PakWriter(structs::PakVersion version) {
Writer(structs::PakVersion version) {
SetVersion(version);
}
@ -42,11 +42,11 @@ namespace europa::io {
/// [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. It is ignored unless the package's version is [structs::PakVersion::Ver5].
void Write(std::ostream& os, std::vector<FlattenedType>&& vec, PakProgressReportSink& sink, SectorAlignment sectorAlignment = SectorAlignment::DoNotAlign);
void Write(std::ostream& os, std::vector<FlattenedType>&& vec, WriterProgressReportSink& sink, SectorAlignment sectorAlignment = SectorAlignment::DoNotAlign);
private:
template <class T>
void WriteImpl(std::ostream& os, std::vector<FlattenedType>&& vec, PakProgressReportSink& sink, SectorAlignment sectorAlignment);
void WriteImpl(std::ostream& os, std::vector<FlattenedType>&& vec, WriterProgressReportSink& sink, SectorAlignment sectorAlignment);
structs::PakVersion version {};
};

View file

@ -11,10 +11,10 @@
#include <string>
namespace europa::io {
namespace europa::io::pak {
/// Interface for [PakWriter] to output detailed progress information.
struct PakProgressReportSink {
struct WriterProgressReportSink {
struct PakEvent {
enum class EventCode {
FillInHeader, /// Filling in header.
@ -35,7 +35,7 @@ namespace europa::io {
const std::string& targetFileName;
};
virtual ~PakProgressReportSink() = default;
virtual ~WriterProgressReportSink() = default;
virtual void OnEvent(const PakEvent& event) = 0;
virtual void OnEvent(const FileEvent& event) = 0;

View file

@ -11,8 +11,8 @@ add_library(europa
io/StreamUtils.cpp
# Pak IO
io/PakReader.cpp
io/PakWriter.cpp
io/pak/Reader.cpp
io/pak/Writer.cpp
# Yatf IO
io/YatfReader.cpp

View file

@ -9,21 +9,21 @@
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <europa/io/PakReader.hpp>
#include <europa/io/pak/File.hpp>
#include <europa/io/pak/Reader.hpp>
#include <europa/structs/Pak.hpp>
#include <stdexcept>
#include "europa/io/PakFile.hpp"
#include "StreamUtils.h"
#include "../StreamUtils.h"
namespace europa::io {
namespace europa::io::pak {
PakReader::PakReader(std::istream& is)
Reader::Reader(std::istream& is)
: stream(is) {
}
template <class T>
void PakReader::ReadData_Impl() {
void Reader::ReadData_Impl() {
auto header_type = impl::ReadStreamType<T>(stream);
if(!header_type.Valid()) {
@ -39,7 +39,7 @@ namespace europa::io {
//
// Read this in first.
auto filename = impl::ReadPString(stream);
auto file = PakFile {};
auto file = File {};
if constexpr(std::is_same_v<T, structs::PakHeader_V5>) {
// Version 5 supports sector aligned packages which have an additional field in them
// so we need to handle it here
@ -59,7 +59,7 @@ namespace europa::io {
header = header_type;
}
void PakReader::ReadHeaderAndTOC() {
void Reader::ReadHeaderAndTOC() {
auto commonHeader = impl::ReadStreamType<structs::PakHeader_Common>(stream);
stream.seekg(0, std::istream::beg);
@ -78,13 +78,13 @@ namespace europa::io {
}
}
void PakReader::ReadFiles() {
void Reader::ReadFiles() {
for(auto& [filename, file] : files)
ReadFile(filename);
}
void PakReader::ReadFile(const std::string& file) {
auto it = std::find_if(files.begin(), files.end(), [&file](PakReader::FlatType& fl) { return fl.first == file; });
void Reader::ReadFile(const std::string& file) {
auto it = std::find_if(files.begin(), files.end(), [&file](Reader::FlatType& fl) { return fl.first == file; });
if(it == files.end())
return;
@ -104,16 +104,16 @@ namespace europa::io {
if(!stream)
throw std::runtime_error("Stream went bad while trying to read file");
auto data = PakFileData::InitAsBuffer(std::move(buffer));
auto data = FileData::InitAsBuffer(std::move(buffer));
fileObject.SetData(std::move(data));
}
PakReader::MapType& PakReader::GetFiles() {
Reader::MapType& Reader::GetFiles() {
return files;
}
const PakReader::MapType& PakReader::GetFiles() const {
const Reader::MapType& Reader::GetFiles() const {
return files;
}
} // namespace europa::io
} // namespace europa::io::pak

View file

@ -8,7 +8,8 @@
#include <algorithm>
#include <chrono>
#include <europa/io/PakWriter.hpp>
#include <europa/io/pak/Writer.hpp>
#include <europa/structs/Pak.hpp>
#include <europa/util/AlignHelpers.hpp>
#include <europa/util/Overloaded.hpp>
#include <europa/util/TupleElement.hpp>
@ -18,17 +19,16 @@
#include <iostream>
#include <stdexcept>
#include "europa/structs/Pak.hpp"
#include "StreamUtils.h"
#include "../StreamUtils.h"
namespace europa::io {
namespace europa::io::pak {
void PakWriter::SetVersion(structs::PakVersion version) {
void Writer::SetVersion(structs::PakVersion version) {
// for now.
this->version = version;
}
void PakWriter::Write(std::ostream& os, std::vector<FlattenedType>&& vec, PakProgressReportSink& sink, SectorAlignment sectorAlignment) {
void Writer::Write(std::ostream& os, std::vector<FlattenedType>&& vec, WriterProgressReportSink& 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) {
@ -47,7 +47,7 @@ namespace europa::io {
}
template <class THeader>
void PakWriter::WriteImpl(std::ostream& os, std::vector<FlattenedType>&& vec, PakProgressReportSink& sink, SectorAlignment sectorAlignment) {
void Writer::WriteImpl(std::ostream& os, std::vector<FlattenedType>&& vec, WriterProgressReportSink& sink, SectorAlignment sectorAlignment) {
std::vector<FlattenedType> sortedFiles = std::move(vec);
THeader pakHeader {};
@ -82,7 +82,7 @@ namespace europa::io {
// Write all the file data
for(auto& [filename, file] : sortedFiles) {
sink.OnEvent({ PakProgressReportSink::FileEvent::EventCode::FileWriteBegin,
sink.OnEvent({ WriterProgressReportSink::FileEvent::EventCode::FileWriteBegin,
filename });
// Update the offset to where we currently are, since we will be writing the file there
@ -129,13 +129,13 @@ namespace europa::io {
os.seekp(util::AlignBy(static_cast<std::size_t>(os.tellp()), util::kCDSectorSize), std::istream::beg);
}
sink.OnEvent({ PakProgressReportSink::FileEvent::EventCode::FileWriteEnd,
sink.OnEvent({ WriterProgressReportSink::FileEvent::EventCode::FileWriteEnd,
filename });
}
pakHeader.tocOffset = os.tellp();
sink.OnEvent({ PakProgressReportSink::PakEvent::EventCode::WritingToc });
sink.OnEvent({ WriterProgressReportSink::PakEvent::EventCode::WritingToc });
#if 0
// Sort for toc stuff? idk
@ -154,7 +154,7 @@ namespace europa::io {
});
}
sink.OnEvent({ PakProgressReportSink::PakEvent::EventCode::FillInHeader });
sink.OnEvent({ WriterProgressReportSink::PakEvent::EventCode::FillInHeader });
// Fill out the rest of the header.
pakHeader.fileCount = sortedFiles.size();
@ -164,11 +164,11 @@ namespace europa::io {
auto now = std::chrono::system_clock::now();
pakHeader.creationUnixTime = static_cast<std::uint32_t>(std::chrono::time_point_cast<std::chrono::seconds>(now).time_since_epoch().count());
sink.OnEvent({ PakProgressReportSink::PakEvent::EventCode::WritingHeader });
sink.OnEvent({ WriterProgressReportSink::PakEvent::EventCode::WritingHeader });
// As the last step, write it.
os.seekp(0, std::ostream::beg);
impl::WriteStreamType(os, pakHeader);
}
} // namespace europa::io
} // namespace europa::io::pak

View file

@ -8,7 +8,9 @@
#include <chrono>
#include <EupakConfig.hpp>
#include <europa/io/PakWriter.hpp>
#include <europa/io/pak/File.hpp>
#include <europa/io/pak/Writer.hpp>
#include <europa/io/pak/WriterProgressReportSink.hpp>
#include <fstream>
#include <indicators/cursor_control.hpp>
#include <indicators/progress_bar.hpp>
@ -17,15 +19,14 @@
#include <Utils.hpp>
#include "argparse/argparse.hpp"
#include "europa/io/PakFile.hpp"
#include "europa/structs/Pak.hpp"
#include "tasks/Task.hpp"
namespace eupak::tasks {
struct CreateArchiveReportSink : public europa::io::PakProgressReportSink {
struct CreateArchiveReportSink : public eio::pak::WriterProgressReportSink {
CreateArchiveReportSink(int fileCount = 0)
: europa::io::PakProgressReportSink() {
: eio::pak::WriterProgressReportSink() {
indicators::show_console_cursor(false);
progress.set_option(indicators::option::MaxProgress { fileCount });
}
@ -143,8 +144,8 @@ namespace eupak::tasks {
auto& args = currentArgs;
args.verbose = parser.get<bool>("--verbose");
args.inputDirectory = eupak::fs::path(parser.get("--directory"));
args.outputFile = eupak::fs::path(parser.get("output"));
args.inputDirectory = fs::path(parser.get("--directory"));
args.outputFile = fs::path(parser.get("output"));
if(parser.is_used("--archive-version")) {
const auto& versionStr = parser.get("--archive-version");
@ -156,12 +157,12 @@ namespace eupak::tasks {
return 1;
}
} else {
args.pakVersion = europa::structs::PakVersion::Ver4;
args.pakVersion = estructs::PakVersion::Ver4;
}
args.sectorAligned = parser.get<bool>("--sector-aligned");
if(args.sectorAligned && args.pakVersion != eupak::estructs::PakVersion::Ver5) {
if(args.sectorAligned && args.pakVersion != estructs::PakVersion::Ver5) {
std::cout << "Error: --sector-aligned is only valid for creating a package with \"-V jedistarfighter\".\n"
<< parser;
return 1;
@ -212,7 +213,7 @@ namespace eupak::tasks {
// TODO: use time to write in the header
// also: is there any point to verbosity? could add archive written size ig
std::vector<europa::io::PakWriter::FlattenedType> files;
std::vector<eio::pak::Writer::FlattenedType> files;
files.reserve(fileCount);
for(auto& ent : fs::recursive_directory_iterator(args.inputDirectory)) {
@ -229,8 +230,8 @@ namespace eupak::tasks {
progress.set_option(indicators::option::PostfixText { relativePathName + " (" + std::to_string(currFile + 1) + '/' + std::to_string(fileCount) + ")" });
eio::PakFile file;
eio::PakFile::DataType pakData = eio::PakFileData::InitAsPath(ent.path());
eio::pak::File file;
eio::pak::FileData pakData = eio::pak::FileData::InitAsPath(ent.path());
file.InitAs(args.pakVersion, args.sectorAligned);
@ -260,11 +261,11 @@ namespace eupak::tasks {
}
CreateArchiveReportSink reportSink(fileCount);
eio::PakWriter writer(args.pakVersion);
eio::pak::Writer writer(args.pakVersion);
using enum eio::PakWriter::SectorAlignment;
using enum eio::pak::Writer::SectorAlignment;
eio::PakWriter::SectorAlignment alignment = DoNotAlign;
eio::pak::Writer::SectorAlignment alignment = DoNotAlign;
if(args.sectorAligned)
alignment = Align;

View file

@ -9,12 +9,11 @@
#ifndef EUROPA_EUPAK_TASKS_CREATETASK_HPP
#define EUROPA_EUPAK_TASKS_CREATETASK_HPP
#include <argparse/argparse.hpp>
#include <CommonDefs.hpp>
#include <europa/structs/Pak.hpp>
#include <tasks/Task.hpp>
#include "argparse/argparse.hpp"
namespace eupak::tasks {
struct CreateTask : ITask {

View file

@ -7,7 +7,7 @@
//
#include <EupakConfig.hpp>
#include <europa/io/PakReader.hpp>
#include <europa/io/pak/Reader.hpp>
#include <fstream>
#include <indicators/cursor_control.hpp>
#include <indicators/progress_bar.hpp>
@ -82,7 +82,7 @@ namespace eupak::tasks {
return 1;
}
eio::PakReader reader(ifs);
eio::pak::Reader reader(ifs);
reader.ReadHeaderAndTOC();

View file

@ -10,8 +10,7 @@
#define EUROPA_EUPAK_TASKS_EXTRACTTASK_HPP
#include <CommonDefs.hpp>
#include "tasks/Task.hpp"
#include <tasks/Task.hpp>
namespace eupak::tasks {

View file

@ -6,17 +6,15 @@
// SPDX-License-Identifier: MIT
//
#include <argparse/argparse.hpp>
#include <EupakConfig.hpp>
#include <europa/io/PakReader.hpp>
#include <europa/io/pak/Reader.hpp>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <tasks/InfoTask.hpp>
#include <Utils.hpp>
#include "argparse/argparse.hpp"
#include "europa/structs/Pak.hpp"
namespace eupak::tasks {
constexpr static auto DATE_FORMAT = "%m/%d/%Y %r";
@ -78,7 +76,7 @@ namespace eupak::tasks {
return 1;
}
eio::PakReader reader(ifs);
eio::pak::Reader reader(ifs);
reader.ReadHeaderAndTOC();
@ -120,7 +118,6 @@ namespace eupak::tasks {
}
});
} else {
file.VisitTocEntry([&](auto& tocEntry) {
std::printf("%16s %10s %8s", FormatUnixTimestamp(tocEntry.creationUnixTime, DATE_FORMAT).c_str(), FormatUnit(tocEntry.size).c_str(), filename.c_str());

View file

@ -10,8 +10,7 @@
#define EUROPA_EUPAK_TASKS_INFOTASK_HPP
#include <CommonDefs.hpp>
#include "tasks/Task.hpp"
#include <tasks/Task.hpp>
namespace eupak::tasks {

View file

@ -1,3 +1,11 @@
//
// EuropaTools
//
// (C) 2021-2025 modeco80 <lily.modeco80@protonmail.ch>
//
// SPDX-License-Identifier: MIT
//
#include "Task.hpp"
#include <stdexcept>

View file

@ -7,12 +7,11 @@
//
#pragma once
#include <argparse/argparse.hpp>
#include <CommonDefs.hpp>
#include <type_traits>
#include <unordered_map>
#include "argparse/argparse.hpp"
namespace eupak::tasks {
/// Base-class for all eupak tasks.