diff --git a/README.md b/README.md index 3d89283..01dcda5 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/include/europa/io/PakFile.hpp b/include/europa/io/PakFile.hpp index 464f479..96b9d60 100644 --- a/include/europa/io/PakFile.hpp +++ b/include/europa/io/PakFile.hpp @@ -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; @@ -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 { diff --git a/src/libeuropa/io/PakWriter.cpp b/src/libeuropa/io/PakWriter.cpp index 3ec796c..a596ff2 100644 --- a/src/libeuropa/io/PakWriter.cpp +++ b/src/libeuropa/io/PakWriter.cpp @@ -1,7 +1,7 @@ // // EuropaTools // -// (C) 2021-2022 modeco80 +// (C) 2021-2025 modeco80 // // 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(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(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(os.tellp()) - (pakHeader.tocOffset - 1); + pakHeader.creationUnixTime = 132890732; sink.OnEvent({ diff --git a/src/libeuropa/io/YatfReader.cpp b/src/libeuropa/io/YatfReader.cpp index 4943a35..a81f3ba 100644 --- a/src/libeuropa/io/YatfReader.cpp +++ b/src/libeuropa/io/YatfReader.cpp @@ -1,11 +1,15 @@ // // EuropaTools // -// (C) 2021-2022 modeco80 +// (C) 2021-2025 modeco80 // // 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 #include diff --git a/src/tools/eupak/tasks/CreateTask.cpp b/src/tools/eupak/tasks/CreateTask.cpp index 381e2a8..f631940 100644 --- a/src/tools/eupak/tasks/CreateTask.cpp +++ b/src/tools/eupak/tasks/CreateTask.cpp @@ -6,6 +6,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later // +#include #include #include #include @@ -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(&pakData[0]), pakData.size()); - file.SetData(std::move(pakData)); - file.InitAs(args.pakVersion); - //file.GetTOCEntry().creationUnixTime = static_cast(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(lastModified); + auto seconds = std::chrono::time_point_cast(lastModified); + tocEntry.creationUnixTime = static_cast(seconds.time_since_epoch().count()); + }); files.emplace_back(std::make_pair(relativePathName, std::move(file))); progress.tick(); diff --git a/src/tools/jsfscramble.cpp b/src/tools/jsfscramble.cpp index 9a776e3..20ece1f 100755 --- a/src/tools/jsfscramble.cpp +++ b/src/tools/jsfscramble.cpp @@ -13,11 +13,22 @@ #include #include -// 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 +/// 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 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)...); @@ -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]);