libeuropa/io: Fix PakWriter/PakFile zero-size-bug
This bug was introduced during refactoring (to allow pmdl export support); it causes the writer to not write any file data. Additionally the writer now sorts by creation time. I'm not sure if greater or less are the correct order.
This commit is contained in:
parent
2c0237933c
commit
b74ca42d72
|
@ -8,7 +8,7 @@ As per usual for lily, written in C++20.
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git clone https://github.com/modeco80/EuropaTools.git
|
$ git clone https://git.crustywindo.ws/GameModding/EuropaTools.git
|
||||||
$ cd EuropaTools
|
$ cd EuropaTools
|
||||||
$ cmake -Bbuild -DCMAKE_BUILD_TYPE=Release
|
$ cmake -Bbuild -DCMAKE_BUILD_TYPE=Release
|
||||||
$ cmake --build build -j $(nproc)
|
$ cmake --build build -j $(nproc)
|
||||||
|
|
|
@ -18,6 +18,10 @@ namespace europa::io {
|
||||||
struct PakReader;
|
struct PakReader;
|
||||||
struct PakWriter;
|
struct PakWriter;
|
||||||
|
|
||||||
|
/// Repressents a package file.
|
||||||
|
/// FIXME: Maybe make this not hold a buffer at some point,
|
||||||
|
/// or a sumtype which can contain either buffer OR path to os file
|
||||||
|
/// (which we can then efficiently tee into)
|
||||||
struct PakFile {
|
struct PakFile {
|
||||||
using DataType = std::vector<std::uint8_t>;
|
using DataType = std::vector<std::uint8_t>;
|
||||||
|
|
||||||
|
@ -57,6 +61,21 @@ namespace europa::io {
|
||||||
|
|
||||||
void SetData(DataType&& data) {
|
void SetData(DataType&& data) {
|
||||||
this->data = std::move(data);
|
this->data = std::move(data);
|
||||||
|
|
||||||
|
// Update the TOC size.
|
||||||
|
std::visit([&](auto& entry) {
|
||||||
|
entry.size = this->data.size();
|
||||||
|
}, toc);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t GetCreationUnixTime() const {
|
||||||
|
std::uint32_t time{};
|
||||||
|
|
||||||
|
std::visit([&](auto& entry) {
|
||||||
|
time = entry.creationUnixTime;
|
||||||
|
}, toc);
|
||||||
|
|
||||||
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint32_t GetOffset() const {
|
std::uint32_t GetOffset() const {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//
|
//
|
||||||
// EuropaTools
|
// EuropaTools
|
||||||
//
|
//
|
||||||
// (C) 2021-2022 modeco80 <lily.modeco80@protonmail.ch>
|
// (C) 2021-2025 modeco80 <lily.modeco80@protonmail.ch>
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
//
|
//
|
||||||
|
@ -16,6 +16,9 @@
|
||||||
|
|
||||||
namespace europa::io {
|
namespace europa::io {
|
||||||
|
|
||||||
|
/// The size of a CD-ROM (ISO 9660) secor.
|
||||||
|
constexpr auto kCDSectorSize = 0x800;
|
||||||
|
|
||||||
void PakWriter::SetVersion(structs::PakVersion version) {
|
void PakWriter::SetVersion(structs::PakVersion version) {
|
||||||
// for now.
|
// for now.
|
||||||
this->version = version;
|
this->version = version;
|
||||||
|
@ -52,7 +55,7 @@ namespace europa::io {
|
||||||
// Sort the flattened array by file size, the biggest first.
|
// Sort the flattened array by file size, the biggest first.
|
||||||
// Doesn't seem to help (neither does name length)
|
// Doesn't seem to help (neither does name length)
|
||||||
std::ranges::sort(sortedFiles, std::greater{}, [](const FlattenedType& elem) {
|
std::ranges::sort(sortedFiles, std::greater{}, [](const FlattenedType& elem) {
|
||||||
return elem.second.GetSize();
|
return elem.second.GetCreationUnixTime();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Leave space for the header
|
// Leave space for the header
|
||||||
|
@ -64,10 +67,12 @@ namespace europa::io {
|
||||||
os.seekp(6, std::ostream::cur);
|
os.seekp(6, std::ostream::cur);
|
||||||
}
|
}
|
||||||
|
|
||||||
//os.seekp(
|
// Align first file to sector boundary.
|
||||||
// AlignBy(os.tellp(), 2048),
|
if(sectorAligned)
|
||||||
// std::istream::beg
|
os.seekp(
|
||||||
//);
|
AlignBy(os.tellp(), kCDSectorSize),
|
||||||
|
std::istream::beg
|
||||||
|
);
|
||||||
|
|
||||||
// Write file data
|
// Write file data
|
||||||
for(auto& [filename, file] : sortedFiles) {
|
for(auto& [filename, file] : sortedFiles) {
|
||||||
|
@ -76,20 +81,21 @@ namespace europa::io {
|
||||||
filename
|
filename
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Update the offset to where we currently are, since we will be writing the file there
|
||||||
file.Visit([&](auto& tocEntry) {
|
file.Visit([&](auto& tocEntry) {
|
||||||
tocEntry.offset = os.tellp();
|
tocEntry.offset = os.tellp();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// FIXME: Should we rely on GetSize() when writing? Honestly, it seems like a bit of a
|
||||||
|
// mistake that caused a pretty glaring bug.
|
||||||
os.write(reinterpret_cast<const char*>(file.GetData().data()), file.GetSize());
|
os.write(reinterpret_cast<const char*>(file.GetData().data()), file.GetSize());
|
||||||
|
|
||||||
//os.seekp(
|
// Align to sector boundary.
|
||||||
// AlignBy(os.tellp(), 2048),
|
if(sectorAligned)
|
||||||
// std::istream::beg
|
os.seekp(
|
||||||
//);
|
AlignBy(os.tellp(), kCDSectorSize),
|
||||||
|
std::istream::beg
|
||||||
// Flush on file writing
|
);
|
||||||
os.flush();
|
|
||||||
|
|
||||||
sink.OnEvent({
|
sink.OnEvent({
|
||||||
PakProgressReportSink::FileEvent::Type::FileEndWrite,
|
PakProgressReportSink::FileEvent::Type::FileEndWrite,
|
||||||
|
@ -107,7 +113,7 @@ namespace europa::io {
|
||||||
for(auto& [filename, file] : sortedFiles) {
|
for(auto& [filename, file] : sortedFiles) {
|
||||||
file.FillTOCEntry();
|
file.FillTOCEntry();
|
||||||
|
|
||||||
// Write the pstring
|
// Write the filename Pascal string.
|
||||||
os.put(static_cast<char>(filename.length() + 1));
|
os.put(static_cast<char>(filename.length() + 1));
|
||||||
for(const auto c : filename)
|
for(const auto c : filename)
|
||||||
os.put(c);
|
os.put(c);
|
||||||
|
@ -116,7 +122,6 @@ namespace europa::io {
|
||||||
file.Visit([&](auto& tocEntry) {
|
file.Visit([&](auto& tocEntry) {
|
||||||
impl::WriteStreamType(os, tocEntry);
|
impl::WriteStreamType(os, tocEntry);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -127,6 +132,7 @@ namespace europa::io {
|
||||||
// Fill out the rest of the header.
|
// Fill out the rest of the header.
|
||||||
pakHeader.fileCount = sortedFiles.size();
|
pakHeader.fileCount = sortedFiles.size();
|
||||||
pakHeader.tocSize = static_cast<std::uint32_t>(os.tellp()) - (pakHeader.tocOffset - 1);
|
pakHeader.tocSize = static_cast<std::uint32_t>(os.tellp()) - (pakHeader.tocOffset - 1);
|
||||||
|
pakHeader.creationUnixTime = 132890732;
|
||||||
|
|
||||||
|
|
||||||
sink.OnEvent({
|
sink.OnEvent({
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
//
|
//
|
||||||
// EuropaTools
|
// EuropaTools
|
||||||
//
|
//
|
||||||
// (C) 2021-2022 modeco80 <lily.modeco80@protonmail.ch>
|
// (C) 2021-2025 modeco80 <lily.modeco80@protonmail.ch>
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// FIXME: Drop libpixel dependency. Instead
|
||||||
|
// we should just use stbiw directly and provide our own
|
||||||
|
// simpler/faster utilities for image buffers.
|
||||||
|
|
||||||
#include <europa/io/YatfReader.hpp>
|
#include <europa/io/YatfReader.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <europa/io/PakWriter.hpp>
|
#include <europa/io/PakWriter.hpp>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <indicators/cursor_control.hpp>
|
#include <indicators/cursor_control.hpp>
|
||||||
|
@ -17,7 +18,6 @@
|
||||||
namespace eupak::tasks {
|
namespace eupak::tasks {
|
||||||
|
|
||||||
struct CreateArchiveReportSink : public europa::io::PakProgressReportSink {
|
struct CreateArchiveReportSink : public europa::io::PakProgressReportSink {
|
||||||
|
|
||||||
CreateArchiveReportSink(int fileCount = 0)
|
CreateArchiveReportSink(int fileCount = 0)
|
||||||
: europa::io::PakProgressReportSink() {
|
: europa::io::PakProgressReportSink() {
|
||||||
indicators::show_console_cursor(false);
|
indicators::show_console_cursor(false);
|
||||||
|
@ -123,7 +123,6 @@ namespace eupak::tasks {
|
||||||
if(c == '/')
|
if(c == '/')
|
||||||
c = '\\';
|
c = '\\';
|
||||||
|
|
||||||
|
|
||||||
progress.set_option(indicators::option::PostfixText { relativePathName + " (" + std::to_string(currFile + 1) + '/' + std::to_string(fileCount) + ")" });
|
progress.set_option(indicators::option::PostfixText { relativePathName + " (" + std::to_string(currFile + 1) + '/' + std::to_string(fileCount) + ")" });
|
||||||
|
|
||||||
std::ifstream ifs(ent.path(), std::ifstream::binary);
|
std::ifstream ifs(ent.path(), std::ifstream::binary);
|
||||||
|
@ -142,11 +141,18 @@ namespace eupak::tasks {
|
||||||
|
|
||||||
ifs.read(reinterpret_cast<char*>(&pakData[0]), pakData.size());
|
ifs.read(reinterpret_cast<char*>(&pakData[0]), pakData.size());
|
||||||
|
|
||||||
file.SetData(std::move(pakData));
|
|
||||||
|
|
||||||
file.InitAs(args.pakVersion);
|
file.InitAs(args.pakVersion);
|
||||||
|
|
||||||
//file.GetTOCEntry().creationUnixTime = static_cast<std::uint32_t>(lastModified.time_since_epoch().count());
|
// Add data
|
||||||
|
file.SetData(std::move(pakData));
|
||||||
|
|
||||||
|
// Setup other stuff like modtime
|
||||||
|
file.Visit([&](auto& tocEntry) {
|
||||||
|
// Need to figure out why this is broken and fucked up
|
||||||
|
//auto casted = std::chrono::clock_cast<std::chrono::system_clock>(lastModified);
|
||||||
|
auto seconds = std::chrono::time_point_cast<std::chrono::seconds>(lastModified);
|
||||||
|
tocEntry.creationUnixTime = static_cast<std::uint32_t>(seconds.time_since_epoch().count());
|
||||||
|
});
|
||||||
|
|
||||||
files.emplace_back(std::make_pair(relativePathName, std::move(file)));
|
files.emplace_back(std::make_pair(relativePathName, std::move(file)));
|
||||||
progress.tick();
|
progress.tick();
|
||||||
|
|
|
@ -13,10 +13,21 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
// Define this to enable an "interactive" mode where the scramble routine will
|
/// Define this to enable an "interactive" mode where the scramble routine will
|
||||||
// dump out whatever it does to standard out. Undefine this to make it not do so.
|
/// dump out whatever it does to standard out. Undefine this to make it not do so.
|
||||||
#define INTERACTIVE
|
#define INTERACTIVE
|
||||||
|
|
||||||
|
/// Define this to use a slower, more academic implementation of the algorithm.
|
||||||
|
// #define SLOW
|
||||||
|
|
||||||
|
#ifdef INTERACTIVE
|
||||||
|
// We need to use the
|
||||||
|
#ifndef SLOW
|
||||||
|
#define SLOW
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SLOW
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
inline std::string StringPrintf(std::string_view format, Args&&... args) {
|
inline std::string StringPrintf(std::string_view format, Args&&... args) {
|
||||||
char buffer[1024];
|
char buffer[1024];
|
||||||
|
@ -58,6 +69,10 @@ std::uint32_t swsfScramble(const std::string& code) {
|
||||||
return tally;
|
return tally;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error FIXME: Import fast/optimized version
|
||||||
|
#endif
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
if(argc < 2) {
|
if(argc < 2) {
|
||||||
std::printf("usage: %s [code-string]\n", argv[0]);
|
std::printf("usage: %s [code-string]\n", argv[0]);
|
||||||
|
|
Loading…
Reference in New Issue