15std::unique_ptr<PackFile>
PCK::create(
const std::string& path, uint32_t version, uint32_t godotMajorVersion, uint32_t godotMinorVersion, uint32_t godotPatchVersion) {
16 if (version != 1 && version != 2) {
21 FileStream stream{path, FileStream::OPT_TRUNCATE | FileStream::OPT_CREATE_IF_NONEXISTENT};
26 .write(godotMajorVersion)
27 .write(godotMinorVersion)
28 .write(godotPatchVersion);
37 .write(std::array<int32_t, 16>{})
43std::unique_ptr<PackFile>
PCK::open(
const std::string& path,
const EntryCallback& callback) {
44 if (!std::filesystem::exists(path)) {
49 auto* pck =
new PCK{path};
50 auto packFile = std::unique_ptr<PackFile>(pck);
52 FileStream reader{pck->fullFilePath};
55 if (
auto signature = reader.read<uint32_t>(); signature !=
PCK_SIGNATURE) {
57 reader.seek_in(
sizeof(uint32_t), std::ios::end);
58 if (
auto endSignature = reader.read<uint32_t>(); endSignature !=
PCK_SIGNATURE) {
62 reader.seek_in(-
static_cast<int64_t
>(
sizeof(uint32_t) +
sizeof(uint64_t)), std::ios::cur);
63 auto distanceIntoFile = reader.read<uint64_t>();
65 reader.seek_in(-
static_cast<int64_t
>(distanceIntoFile +
sizeof(uint64_t)), std::ios::cur);
66 if (
auto startSignature = reader.read<uint32_t>(); startSignature !=
PCK_SIGNATURE) {
70 pck->startOffset = reader.tell_in() -
sizeof(uint32_t);
73 reader.read(pck->header.packVersion);
74 reader.read(pck->header.godotVersionMajor);
75 reader.read(pck->header.godotVersionMinor);
76 reader.read(pck->header.godotVersionPatch);
79 std::size_t extraEntryContentsOffset = 0;
80 if (pck->header.packVersion > 1) {
82 extraEntryContentsOffset = reader.read<uint64_t>();
90 extraEntryContentsOffset += pck->startOffset;
91 pck->header.flags =
static_cast<FlagsDirV2>(pck->header.flags & ~FLAG_DIR_RELATIVE_FILE_DATA);
95 reader.skip_in<int32_t>(16);
98 auto fileCount = reader.read<uint32_t>();
99 for (uint32_t i = 0; i < fileCount; i++) {
102 auto entryPath = pck->cleanEntryPath(reader.read_string(reader.read<uint32_t>()));
107 entry.
offset = reader.read<uint64_t>() + extraEntryContentsOffset;
108 entry.
length = reader.read<uint64_t>();
111 if (pck->header.packVersion > 1) {
112 entry.
flags = reader.read<uint32_t>();
118 pck->entries.emplace(entryPath, entry);
121 callback(entryPath, entry);
126 pck->dataOffset = reader.tell_in();
131std::optional<std::vector<std::byte>>
PCK::readEntry(
const std::string& path_)
const {
151 stream.seek_in_u(entry->
offset);
152 return stream.read_bytes(entry->
length);
156 entry.
length = buffer.size();
159 entry.
extraData = {md5.begin(), md5.end()};
168 std::string outputPath = outputDir +
'/' + this->
getFilename();
171 std::vector<std::pair<std::string, Entry*>> entriesToBake;
173 entriesToBake.emplace_back(path, &entry);
177 std::vector<std::byte> fileData;
178 for (
auto& [path, entry] : entriesToBake) {
179 if (
auto binData = this->
readEntry(path)) {
180 entry->
offset = fileData.size();
182 fileData.insert(fileData.end(), binData->begin(), binData->end());
184 for (
int i = 0; i < padding; i++) {
185 fileData.push_back(
static_cast<std::byte
>(0));
194 std::vector<std::byte> exeData;
206 FileStream stream{outputPath, FileStream::OPT_TRUNCATE | FileStream::OPT_CREATE_IF_NONEXISTENT};
209 if (!exeData.empty()) {
210 stream.write(exeData);
224 stream.write<uint64_t>(0);
228 stream.write(std::array<int32_t, 16>{});
231 stream.write(
static_cast<uint32_t
>(entriesToBake.size()));
235 for (
const auto& path : std::views::keys(entriesToBake)) {
240 entryPath.length() + padding +
241 (
sizeof(std::size_t) * 2) +
242 (
sizeof(std::byte) * 16);
250 for (
const auto& [path, entry] : entriesToBake) {
253 stream.write(
static_cast<uint32_t
>(entryPath.length() + padding));
254 stream.write(entryPath,
false, entryPath.length() + padding);
257 stream.write(entry->
offset);
258 stream.write(entry->
length);
262 stream.write(entry->
flags);
266 callback(path, *entry);
271 stream.write(fileData);
291PCK::operator std::string()
const {
292 auto out = PackFile::operator std::string() +
293 " | Version v" + std::to_string(this->header.packVersion) +
294 " | Godot Version v" + std::to_string(this->header.godotVersionMajor) +
'.' + std::to_string(this->header.godotVersionMinor) +
'.' + std::to_string(this->header.godotVersionPatch);
295 if (this->startOffset > 0) {
296 out +=
" | Embedded";
298 if (this->header.flags & FLAG_DIR_ENCRYPTED) {
299 out +=
" | Encrypted";
309 if (version == 1 || version == 2) {
constexpr int PCK_DIRECTORY_STRING_PADDING
constexpr int PCK_FILE_DATA_PADDING
This class represents the metadata that a file has inside a PackFile.
bool unbaked
Used to check if entry is saved to disk.
uint32_t flags
Format-specific flags (PCK: File flags, VPK: Internal parser state, ZIP: Compression method / strengt...
uint64_t offset
Offset, format-specific meaning - 0 if unused, or if the offset genuinely is 0.
uint64_t length
Length in bytes (in formats with compression, this is the uncompressed length)
std::vector< std::byte > extraData
Format-specific (PCK: MD5 hash, VPK: Preloaded data)
void setGodotVersion(uint32_t major, uint32_t minor=0, uint32_t patch=0)
void addEntryInternal(Entry &entry, const std::string &path, std::vector< std::byte > &buffer, EntryOptions options) override
std::tuple< uint32_t, uint32_t, uint32_t > getGodotVersion() const
static std::unique_ptr< PackFile > open(const std::string &path, const EntryCallback &callback=nullptr)
Open a PCK file (potentially embedded in an executable)
std::optional< std::vector< std::byte > > readEntry(const std::string &path_) const override
Try to read the entry's data to a bytebuffer.
uint32_t getVersion() const
Returns 1 for v1, 2 for v2.
Attribute getSupportedEntryAttributes() const override
Returns a list of supported entry attributes Mostly for GUI programs that show entries and their meta...
bool bake(const std::string &outputDir_, BakeOptions options, const EntryCallback &callback) override
If output folder is an empty string, it will overwrite the original.
static std::unique_ptr< PackFile > create(const std::string &path, uint32_t version=2, uint32_t godotMajorVersion=0, uint32_t godotMinorVersion=0, uint32_t godotPatchVersion=0)
Create a new PCK file.
void setVersion(uint32_t version)
Change the version of the PCK. Valid values are 1 and 2.
@ FLAG_DIR_RELATIVE_FILE_DATA
EntryCallbackBase< void > EntryCallback
void mergeUnbakedEntries()
std::optional< Entry > findEntry(const std::string &path_, bool includeUnbaked=true) const
Try to find an entry given the file path.
void runForAllEntriesInternal(const std::function< void(const std::string &, Entry &)> &operation, bool includeUnbaked=true)
std::string getFilename() const
/home/user/pak01_dir.vpk -> pak01_dir.vpk
std::string getBakeOutputDir(const std::string &outputDir) const
void setFullFilePath(const std::string &outputDir)
std::string cleanEntryPath(const std::string &path) const
static Entry createNewEntry()
static std::optional< std::vector< std::byte > > readUnbakedEntry(const Entry &entry)
std::array< std::byte, 16 > computeMD5(std::span< const std::byte > buffer)
constexpr uint16_t paddingForAlignment(uint16_t alignment, uint64_t n)
constexpr auto PCK_SIGNATURE
constexpr std::string_view PCK_PATH_PREFIX