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;
|
||||
|
||||
// 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!!");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <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;
|
||||
}
|
Loading…
Reference in New Issue