14 if (!std::filesystem::exists(path)) {
19 auto* gma =
new GMA{path};
20 auto packFile = std::unique_ptr<PackFile>(gma);
22 FileStream reader{gma->fullFilePath};
25 reader.read(gma->header.signature);
30 reader.read(gma->header.version);
31 reader.read(gma->header.steamID);
32 reader.read(gma->header.timestamp);
33 reader.read(gma->header.requiredContent);
34 reader.read(gma->header.addonName);
35 reader.read(gma->header.addonDescription);
36 reader.read(gma->header.addonAuthor);
37 reader.read(gma->header.addonVersion);
39 std::vector<std::pair<std::string, Entry>>
entries;
40 while (reader.read<uint32_t>() > 0) {
43 auto entryPath = gma->cleanEntryPath(reader.read_string());
45 entry.
length = reader.read<uint64_t>();
46 reader.read(entry.
crc32);
48 entries.emplace_back(entryPath, entry);
52 std::size_t offset = reader.tell_in();
53 for (
auto& [entryPath, entry] :
entries) {
54 entry.offset = offset;
55 offset += entry.length;
57 gma->entries.emplace(entryPath, entry);
60 callback(entryPath, entry);
77 if (data.size() <= 4) {
81 const auto checksum = *(
reinterpret_cast<uint32_t*
>(data.data() + data.size()) - 1);
89std::optional<std::vector<std::byte>>
GMA::readEntry(
const std::string& path_)
const {
104 stream.seek_in_u(entry->offset);
105 return stream.read_bytes(entry->length);
109 entry.
length = buffer.size();
119 std::string outputPath = outputDir +
'/' + this->
getFilename();
122 std::vector<std::pair<std::string, Entry*>> entriesToBake;
124 entriesToBake.emplace_back(path, &entry);
128 std::vector<std::byte> fileData;
129 for (
auto& [path, entry] : entriesToBake) {
130 if (
auto binData = this->
readEntry(path)) {
131 fileData.insert(fileData.end(), binData->begin(), binData->end());
138 FileStream stream{outputPath, FileStream::OPT_TRUNCATE | FileStream::OPT_CREATE_IF_NONEXISTENT};
142 stream.write(this->
header.signature);
143 stream.write(this->
header.version);
144 stream.write(this->
header.steamID);
145 stream.write(this->
header.timestamp);
146 stream.write(this->
header.requiredContent);
147 stream.write(this->
header.addonName);
148 stream.write(this->
header.addonDescription);
149 stream.write(this->
header.addonAuthor);
150 stream.write(this->
header.addonVersion);
153 for (uint32_t i = 1; i <= entriesToBake.size(); i++) {
155 const auto& [path, entry] = entriesToBake[i - 1];
157 stream.write(entry->
length);
161 callback(path, *entry);
164 stream.write<uint32_t>(0);
167 std::size_t offset = stream.tell_out();
168 for (
auto& [path, entry] : entriesToBake) {
174 stream.write(fileData);
180 auto fileSize = std::filesystem::file_size(outputPath);
181 FileStream stream{outputPath};
186 FileStream stream{outputPath, FileStream::OPT_APPEND};
201GMA::operator std::string()
const {
202 return PackFile::operator std::string() + std::format(
" | Version v{} | Addon Name: \"{}\"", this->
header.version, this->header.addonName);
This class represents the metadata that a file has inside a PackFile.
uint64_t offset
Offset, format-specific meaning - 0 if unused, or if the offset genuinely is 0.
uint32_t crc32
CRC32 checksum - 0 if unused.
uint64_t length
Length in bytes (in formats with compression, this is the uncompressed length).
void addEntryInternal(Entry &entry, const std::string &path, std::vector< std::byte > &buffer, EntryOptions options) override
bool verifyPackFileChecksum() const override
Verify the checksum of the entire file, returns true on success Will return true if there is no check...
Attribute getSupportedEntryAttributes() const override
Returns a list of supported entry attributes Mostly for GUI programs that show entries and their meta...
static std::unique_ptr< PackFile > open(const std::string &path, const EntryCallback &callback=nullptr)
Open a GMA file.
std::vector< std::string > verifyEntryChecksums() const override
Verify the checksums of each file, if a file fails the check its path will be added to the vector If ...
bool hasPackFileChecksum() const override
Returns true if the entire file has a checksum.
std::optional< std::vector< std::byte > > readEntry(const std::string &path_) const override
Try to read the entry's data to a bytebuffer.
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.
std::vector< std::string > verifyEntryChecksumsUsingCRC32() const
void runForAllEntriesInternal(const std::function< void(const std::string &, Entry &)> &operation, bool includeUnbaked=true)
bool bake()
If output folder is an empty string, it will overwrite the original.
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)
uint32_t computeCRC32(std::span< const std::byte > buffer)
std::vector< std::byte > readFileBuffer(const std::filesystem::path &filepath, std::size_t startOffset=0)
constexpr auto GMA_SIGNATURE
bool gma_writeCRCs
GMA - Write CRCs for files and the overall GMA file when baking.