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
This commit is contained in:
Lily Tsuru 2022-09-15 04:11:53 -05:00
parent 81dad9965c
commit 4452b87780
5 changed files with 134 additions and 18 deletions

View File

@ -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!!");

View File

@ -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<std::uint32_t>(os.tellp()) - (pakHeader.tocOffset - 1);
os.seekp(0, std::ostream::beg);
impl::WriteStreamType(os, pakHeader);
}

View File

@ -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

80
src/tools/pakcreate.cpp Normal file
View File

@ -0,0 +1,80 @@
//
// EuropaTools
//
// (C) 2021-2022 modeco80 <lily.modeco80@protonmail.ch>
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// A test utility to regurgitate a pak.
#include <europa/io/PakReader.h>
#include <europa/io/PakWriter.h>
#include <fstream>
#include <iostream>
#include <filesystem>
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<char*>(&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;
}

View File

@ -14,29 +14,51 @@
#include <fstream>
#include <iostream>
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;
}