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
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/modeco80/EuropaTools.git
|
||||
$ git clone https://git.crustywindo.ws/GameModding/EuropaTools.git
|
||||
$ cd EuropaTools
|
||||
$ cmake -Bbuild -DCMAKE_BUILD_TYPE=Release
|
||||
$ cmake --build build -j $(nproc)
|
||||
|
|
|
@ -18,6 +18,10 @@ namespace europa::io {
|
|||
struct PakReader;
|
||||
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 {
|
||||
using DataType = std::vector<std::uint8_t>;
|
||||
|
||||
|
@ -57,6 +61,21 @@ namespace europa::io {
|
|||
|
||||
void SetData(DataType&& 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 {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// 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
|
||||
//
|
||||
|
@ -16,6 +16,9 @@
|
|||
|
||||
namespace europa::io {
|
||||
|
||||
/// The size of a CD-ROM (ISO 9660) secor.
|
||||
constexpr auto kCDSectorSize = 0x800;
|
||||
|
||||
void PakWriter::SetVersion(structs::PakVersion version) {
|
||||
// for now.
|
||||
this->version = version;
|
||||
|
@ -52,7 +55,7 @@ namespace europa::io {
|
|||
// Sort the flattened array by file size, the biggest first.
|
||||
// Doesn't seem to help (neither does name length)
|
||||
std::ranges::sort(sortedFiles, std::greater{}, [](const FlattenedType& elem) {
|
||||
return elem.second.GetSize();
|
||||
return elem.second.GetCreationUnixTime();
|
||||
});
|
||||
|
||||
// Leave space for the header
|
||||
|
@ -64,10 +67,12 @@ namespace europa::io {
|
|||
os.seekp(6, std::ostream::cur);
|
||||
}
|
||||
|
||||
//os.seekp(
|
||||
// AlignBy(os.tellp(), 2048),
|
||||
// std::istream::beg
|
||||
//);
|
||||
// Align first file to sector boundary.
|
||||
if(sectorAligned)
|
||||
os.seekp(
|
||||
AlignBy(os.tellp(), kCDSectorSize),
|
||||
std::istream::beg
|
||||
);
|
||||
|
||||
// Write file data
|
||||
for(auto& [filename, file] : sortedFiles) {
|
||||
|
@ -76,20 +81,21 @@ namespace europa::io {
|
|||
filename
|
||||
});
|
||||
|
||||
|
||||
// Update the offset to where we currently are, since we will be writing the file there
|
||||
file.Visit([&](auto& tocEntry) {
|
||||
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.seekp(
|
||||
// AlignBy(os.tellp(), 2048),
|
||||
// std::istream::beg
|
||||
//);
|
||||
|
||||
// Flush on file writing
|
||||
os.flush();
|
||||
// Align to sector boundary.
|
||||
if(sectorAligned)
|
||||
os.seekp(
|
||||
AlignBy(os.tellp(), kCDSectorSize),
|
||||
std::istream::beg
|
||||
);
|
||||
|
||||
sink.OnEvent({
|
||||
PakProgressReportSink::FileEvent::Type::FileEndWrite,
|
||||
|
@ -107,7 +113,7 @@ namespace europa::io {
|
|||
for(auto& [filename, file] : sortedFiles) {
|
||||
file.FillTOCEntry();
|
||||
|
||||
// Write the pstring
|
||||
// Write the filename Pascal string.
|
||||
os.put(static_cast<char>(filename.length() + 1));
|
||||
for(const auto c : filename)
|
||||
os.put(c);
|
||||
|
@ -116,7 +122,6 @@ namespace europa::io {
|
|||
file.Visit([&](auto& tocEntry) {
|
||||
impl::WriteStreamType(os, tocEntry);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -127,6 +132,7 @@ namespace europa::io {
|
|||
// Fill out the rest of the header.
|
||||
pakHeader.fileCount = sortedFiles.size();
|
||||
pakHeader.tocSize = static_cast<std::uint32_t>(os.tellp()) - (pakHeader.tocOffset - 1);
|
||||
pakHeader.creationUnixTime = 132890732;
|
||||
|
||||
|
||||
sink.OnEvent({
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
// 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 <vector>
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
|
||||
#include <chrono>
|
||||
#include <europa/io/PakWriter.hpp>
|
||||
#include <fstream>
|
||||
#include <indicators/cursor_control.hpp>
|
||||
|
@ -17,7 +18,6 @@
|
|||
namespace eupak::tasks {
|
||||
|
||||
struct CreateArchiveReportSink : public europa::io::PakProgressReportSink {
|
||||
|
||||
CreateArchiveReportSink(int fileCount = 0)
|
||||
: europa::io::PakProgressReportSink() {
|
||||
indicators::show_console_cursor(false);
|
||||
|
@ -32,17 +32,17 @@ namespace eupak::tasks {
|
|||
using enum PakEvent::Type;
|
||||
switch(event.type) {
|
||||
case WritingHeader:
|
||||
progress.set_option(indicators::option::PostfixText {"Writing header"});
|
||||
progress.set_option(indicators::option::PostfixText { "Writing header" });
|
||||
progress.print_progress();
|
||||
break;
|
||||
|
||||
case FillInHeader:
|
||||
progress.set_option(indicators::option::PostfixText {"Filling in header"});
|
||||
progress.set_option(indicators::option::PostfixText { "Filling in header" });
|
||||
progress.print_progress();
|
||||
break;
|
||||
|
||||
case WritingToc:
|
||||
progress.set_option(indicators::option::PostfixText {"Writing TOC"});
|
||||
progress.set_option(indicators::option::PostfixText { "Writing TOC" });
|
||||
progress.print_progress();
|
||||
break;
|
||||
}
|
||||
|
@ -52,12 +52,12 @@ namespace eupak::tasks {
|
|||
using enum FileEvent::Type;
|
||||
switch(event.type) {
|
||||
case FileBeginWrite:
|
||||
progress.set_option(indicators::option::PostfixText {"Writing " + event.filename});
|
||||
progress.set_option(indicators::option::PostfixText { "Writing " + event.filename });
|
||||
progress.print_progress();
|
||||
break;
|
||||
|
||||
case FileEndWrite:
|
||||
progress.set_option(indicators::option::PostfixText {"Written " + event.filename});
|
||||
progress.set_option(indicators::option::PostfixText { "Written " + event.filename });
|
||||
progress.tick();
|
||||
break;
|
||||
}
|
||||
|
@ -123,8 +123,7 @@ namespace eupak::tasks {
|
|||
if(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);
|
||||
|
||||
|
@ -142,11 +141,18 @@ namespace eupak::tasks {
|
|||
|
||||
ifs.read(reinterpret_cast<char*>(&pakData[0]), pakData.size());
|
||||
|
||||
file.SetData(std::move(pakData));
|
||||
|
||||
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)));
|
||||
progress.tick();
|
||||
|
|
|
@ -13,11 +13,22 @@
|
|||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
// 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.
|
||||
/// 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.
|
||||
#define INTERACTIVE
|
||||
|
||||
template<typename... Args>
|
||||
/// 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>
|
||||
inline std::string StringPrintf(std::string_view format, Args&&... args) {
|
||||
char buffer[1024];
|
||||
auto len = std::snprintf(&buffer[0], sizeof(buffer) - 1, format.data(), static_cast<Args&&>(args)...);
|
||||
|
@ -36,13 +47,13 @@ std::uint32_t swsfScramble(const std::string& code) {
|
|||
for(auto& c : copy)
|
||||
c = tolower(c);
|
||||
|
||||
#ifdef INTERACTIVE
|
||||
#ifdef INTERACTIVE
|
||||
std::printf("working with string \"%s\"\n", copy.c_str());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Now actually do the scramble. The algorithm is pretty simple.
|
||||
for(auto* p = copy.data(); *p; ++p) {
|
||||
#ifdef INTERACTIVE
|
||||
#ifdef INTERACTIVE
|
||||
std::uint32_t clone = *p;
|
||||
std::printf("load character '%c' -> 0x%08x (%d)\n", *p, clone, clone);
|
||||
|
||||
|
@ -50,14 +61,18 @@ std::uint32_t swsfScramble(const std::string& code) {
|
|||
std::printf("add (0x%08x (%d) * 5) (%d) -> 0x%08x (%d)\n", tally, tally, tally * 5, clone, clone);
|
||||
|
||||
tally = clone;
|
||||
#else
|
||||
#else
|
||||
tally = *p + (tally * 5);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
return tally;
|
||||
}
|
||||
|
||||
#else
|
||||
#error FIXME: Import fast/optimized version
|
||||
#endif
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if(argc < 2) {
|
||||
std::printf("usage: %s [code-string]\n", argv[0]);
|
||||
|
|
Loading…
Reference in New Issue