From 4452b877806526d8a89ce84009f2b05526418b9b Mon Sep 17 00:00:00 2001 From: modeco80 Date: Thu, 15 Sep 2022 04:11:53 -0500 Subject: [PATCH] Fix io::PakWriter, add pakcreate Also improves paktest. Pakcreate is a new utility for creating completely new archives from a directory. Usage: pakcreate [input path] [output .PAK/pmdl filename] [--jedi] Options: --jedi : Make Jedi Starfighter archive --- include/europa/structs/Pak.h | 11 ++--- src/libeuropa/io/PakWriter.cpp | 6 ++- src/tools/CMakeLists.txt | 23 ++++++++-- src/tools/pakcreate.cpp | 80 ++++++++++++++++++++++++++++++++++ src/tools/paktest.cpp | 32 +++++++++++--- 5 files changed, 134 insertions(+), 18 deletions(-) create mode 100644 src/tools/pakcreate.cpp diff --git a/include/europa/structs/Pak.h b/include/europa/structs/Pak.h index c431263..5ea7f0b 100644 --- a/include/europa/structs/Pak.h +++ b/include/europa/structs/Pak.h @@ -36,13 +36,11 @@ namespace europa::structs { u32 tocOffset; - // Dunno what this is - u32 unk; + u32 tocSize; u32 fileCount; - // Could be Windows FILETIME? - u32 unk2; + u32 creationUnixTime; u32 reservedPad; @@ -97,10 +95,7 @@ namespace europa::structs { struct [[gnu::packed]] PakTocEntry { u32 offset; u32 size; - - // Seems to be the same as the header's - // unk2? - u32 unk3; + u32 creationUnixTime; }; static_assert(sizeof(PakHeader) == 0x29, "PakHeader wrong size!!"); diff --git a/src/libeuropa/io/PakWriter.cpp b/src/libeuropa/io/PakWriter.cpp index fa609b6..79d96e4 100644 --- a/src/libeuropa/io/PakWriter.cpp +++ b/src/libeuropa/io/PakWriter.cpp @@ -24,14 +24,13 @@ namespace europa::io { } void PakWriter::Write(std::ostream& os) { - // Set up the header a bit more... pakHeader.fileCount = archiveFiles.size(); // Leave space for the header os.seekp(sizeof(structs::PakHeader), std::ostream::beg); // Seek forwards for version 2 PAKs, as the only - // difference seems to be + // difference seems to be this additional bump if(pakHeader.version == structs::PakVersion::Ver2) { os.seekp(6, std::ostream::cur); } @@ -57,6 +56,9 @@ namespace europa::io { impl::WriteStreamType(os, file.GetTOCEntry()); } + // Fill out the TOC size. + pakHeader.tocSize = static_cast(os.tellp()) - (pakHeader.tocOffset - 1); + os.seekp(0, std::ostream::beg); impl::WriteStreamType(os, pakHeader); } diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index f81b4ba..116a9d8 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -1,6 +1,7 @@ add_executable(europa_pack_extractor europa_pack_extractor.cpp) -target_link_libraries(europa_pack_extractor PUBLIC libeuropa +target_link_libraries(europa_pack_extractor PUBLIC + libeuropa indicators::indicators ) @@ -9,10 +10,24 @@ set_target_properties(europa_pack_extractor PROPERTIES CXX_STANDARD_REQUIRED ON ) +add_executable(pakcreate pakcreate.cpp) + +target_link_libraries(pakcreate PUBLIC + libeuropa + indicators::indicators + ) + +set_target_properties(pakcreate PROPERTIES + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED ON + ) + add_executable(texdump texdump.cpp) -target_link_libraries(texdump PUBLIC libeuropa) +target_link_libraries(texdump PUBLIC + libeuropa + ) set_target_properties(texdump PROPERTIES CXX_STANDARD 20 @@ -21,7 +36,9 @@ set_target_properties(texdump PROPERTIES add_executable(paktest paktest.cpp) -target_link_libraries(paktest PUBLIC libeuropa) +target_link_libraries(paktest PUBLIC + libeuropa + ) set_target_properties(paktest PROPERTIES CXX_STANDARD 20 diff --git a/src/tools/pakcreate.cpp b/src/tools/pakcreate.cpp new file mode 100644 index 0000000..39f3d2b --- /dev/null +++ b/src/tools/pakcreate.cpp @@ -0,0 +1,80 @@ +// +// EuropaTools +// +// (C) 2021-2022 modeco80 +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + +// A test utility to regurgitate a pak. + +#include +#include + +#include +#include +#include + +namespace fs = std::filesystem; + +using namespace europa; + +int main(int argc, char** argv) { + std::ofstream ofs(argv[2], std::ofstream::binary); + + if(!ofs) { + std::cout << "Couldn't open output PAK file\n"; + return 1; + } + + io::PakWriter writer; + + if(argv[3] != nullptr) { + if(!strcmp(argv[3], "--jedi")) { + std::cout << "Writing Jedi Starfighter archive\n"; + writer.Init(structs::PakVersion::Ver2); + } + } else { + std::cout << "Writing Starfighter archive\n"; + writer.Init(structs::PakVersion::Starfighter); + } + + for(auto& ent : fs::recursive_directory_iterator(argv[1])) { + if(ent.is_directory()) + continue; + + auto relativePathName = fs::relative(ent.path(), argv[1]).string(); + + // Convert to Windows path separator always (that's what the game wants, after all) + for(auto& c : relativePathName) + if(c == '/') + c = '\\'; + + std::ifstream ifs(ent.path(), std::ifstream::binary); + + if(!ifs) { + std::cout << "ERROR: Couldn't open file for archive path \"" << relativePathName << "\"\n"; + return 1; + } + + io::PakFile file; + io::PakFile::DataType pakData; + + ifs.seekg(0, std::ifstream::end); + pakData.resize(ifs.tellg()); + ifs.seekg(0, std::ifstream::beg); + + ifs.read(reinterpret_cast(&pakData[0]), pakData.size()); + + file.SetData(std::move(pakData)); + file.FillTOCEntry(); + + std::cout << "Added \"" << relativePathName << "\"\n"; + writer.GetFiles()[relativePathName] = std::move(file); + } + + writer.Write(ofs); + + std::cout << "Wrote archive to \"" << argv[2] << "\"!\n"; + return 0; +} \ No newline at end of file diff --git a/src/tools/paktest.cpp b/src/tools/paktest.cpp index 9809972..d4bb355 100644 --- a/src/tools/paktest.cpp +++ b/src/tools/paktest.cpp @@ -14,29 +14,51 @@ #include #include +using namespace europa; + int main(int argc, char** argv) { std::ifstream ifs(argv[1], std::ifstream::binary); - std::ofstream ofs("new_archive.pak", std::ofstream::binary); + std::ofstream ofs(argv[2], std::ofstream::binary); - europa::io::PakWriter writer; + if(!ifs) { + std::cout << "Couldn't open input PAK file\n"; + return 1; + } - writer.Init(europa::structs::PakVersion::Ver2); + io::PakWriter writer; + + if(argv[3] != nullptr) { + if(!strcmp(argv[3], "--jedi")) { + std::cout << "Writing Jedi Starfighter archive\n"; + writer.Init(structs::PakVersion::Ver2); + } + } else { + std::cout << "Writing Starfighter archive\n"; + writer.Init(structs::PakVersion::Starfighter); + } // Read pak data and vomit it into the writer. // This will temporarily consume 2x the memory (so about 240mb for the biggest paks I've seen), // but the writer will contain the first copy, // until it's cleared. { - europa::io::PakReader reader(ifs); + io::PakReader reader(ifs); reader.ReadData(); + if(reader.Invalid()) { + std::cout << "Invalid pak file, exiting\n"; + return 1; + } + for(auto& [filename, file] : reader.GetFiles()) { + std::cout << "Reading \"" << filename << "\" into memory\n"; + reader.ReadFile(filename); writer.GetFiles()[filename] = file; } } writer.Write(ofs); - std::cout << "Wrote regurgitated archive to new.pak!\n"; + std::cout << "Wrote regurgitated archive to \"" << argv[2] << "\"!\n"; return 0; } \ No newline at end of file