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:
parent
81dad9965c
commit
4452b87780
|
@ -36,13 +36,11 @@ namespace europa::structs {
|
||||||
|
|
||||||
u32 tocOffset;
|
u32 tocOffset;
|
||||||
|
|
||||||
// Dunno what this is
|
u32 tocSize;
|
||||||
u32 unk;
|
|
||||||
|
|
||||||
u32 fileCount;
|
u32 fileCount;
|
||||||
|
|
||||||
// Could be Windows FILETIME?
|
u32 creationUnixTime;
|
||||||
u32 unk2;
|
|
||||||
|
|
||||||
u32 reservedPad;
|
u32 reservedPad;
|
||||||
|
|
||||||
|
@ -97,10 +95,7 @@ namespace europa::structs {
|
||||||
struct [[gnu::packed]] PakTocEntry {
|
struct [[gnu::packed]] PakTocEntry {
|
||||||
u32 offset;
|
u32 offset;
|
||||||
u32 size;
|
u32 size;
|
||||||
|
u32 creationUnixTime;
|
||||||
// Seems to be the same as the header's
|
|
||||||
// unk2?
|
|
||||||
u32 unk3;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(PakHeader) == 0x29, "PakHeader wrong size!!");
|
static_assert(sizeof(PakHeader) == 0x29, "PakHeader wrong size!!");
|
||||||
|
|
|
@ -24,14 +24,13 @@ namespace europa::io {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PakWriter::Write(std::ostream& os) {
|
void PakWriter::Write(std::ostream& os) {
|
||||||
// Set up the header a bit more...
|
|
||||||
pakHeader.fileCount = archiveFiles.size();
|
pakHeader.fileCount = archiveFiles.size();
|
||||||
|
|
||||||
// Leave space for the header
|
// Leave space for the header
|
||||||
os.seekp(sizeof(structs::PakHeader), std::ostream::beg);
|
os.seekp(sizeof(structs::PakHeader), std::ostream::beg);
|
||||||
|
|
||||||
// Seek forwards for version 2 PAKs, as the only
|
// 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) {
|
if(pakHeader.version == structs::PakVersion::Ver2) {
|
||||||
os.seekp(6, std::ostream::cur);
|
os.seekp(6, std::ostream::cur);
|
||||||
}
|
}
|
||||||
|
@ -57,6 +56,9 @@ namespace europa::io {
|
||||||
impl::WriteStreamType(os, file.GetTOCEntry());
|
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);
|
os.seekp(0, std::ostream::beg);
|
||||||
impl::WriteStreamType(os, pakHeader);
|
impl::WriteStreamType(os, pakHeader);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
add_executable(europa_pack_extractor europa_pack_extractor.cpp)
|
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
|
indicators::indicators
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -9,10 +10,24 @@ set_target_properties(europa_pack_extractor PROPERTIES
|
||||||
CXX_STANDARD_REQUIRED ON
|
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)
|
add_executable(texdump texdump.cpp)
|
||||||
|
|
||||||
target_link_libraries(texdump PUBLIC libeuropa)
|
target_link_libraries(texdump PUBLIC
|
||||||
|
libeuropa
|
||||||
|
)
|
||||||
|
|
||||||
set_target_properties(texdump PROPERTIES
|
set_target_properties(texdump PROPERTIES
|
||||||
CXX_STANDARD 20
|
CXX_STANDARD 20
|
||||||
|
@ -21,7 +36,9 @@ set_target_properties(texdump PROPERTIES
|
||||||
|
|
||||||
add_executable(paktest paktest.cpp)
|
add_executable(paktest paktest.cpp)
|
||||||
|
|
||||||
target_link_libraries(paktest PUBLIC libeuropa)
|
target_link_libraries(paktest PUBLIC
|
||||||
|
libeuropa
|
||||||
|
)
|
||||||
|
|
||||||
set_target_properties(paktest PROPERTIES
|
set_target_properties(paktest PROPERTIES
|
||||||
CXX_STANDARD 20
|
CXX_STANDARD 20
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -14,29 +14,51 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace europa;
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
std::ifstream ifs(argv[1], std::ifstream::binary);
|
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.
|
// 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),
|
// 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,
|
// but the writer will contain the first copy,
|
||||||
// until it's cleared.
|
// until it's cleared.
|
||||||
{
|
{
|
||||||
europa::io::PakReader reader(ifs);
|
io::PakReader reader(ifs);
|
||||||
reader.ReadData();
|
reader.ReadData();
|
||||||
|
|
||||||
|
if(reader.Invalid()) {
|
||||||
|
std::cout << "Invalid pak file, exiting\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
for(auto& [filename, file] : reader.GetFiles()) {
|
for(auto& [filename, file] : reader.GetFiles()) {
|
||||||
|
std::cout << "Reading \"" << filename << "\" into memory\n";
|
||||||
|
reader.ReadFile(filename);
|
||||||
writer.GetFiles()[filename] = file;
|
writer.GetFiles()[filename] = file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Write(ofs);
|
writer.Write(ofs);
|
||||||
|
|
||||||
std::cout << "Wrote regurgitated archive to new.pak!\n";
|
std::cout << "Wrote regurgitated archive to \"" << argv[2] << "\"!\n";
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
Loading…
Reference in New Issue