16 FileStream stream{path, FileStream::OPT_TRUNCATE | FileStream::OPT_CREATE_IF_NONEXISTENT};
26 if (!std::filesystem::exists(path)) {
32 const auto stem = std::filesystem::path{path}.stem().string();
38 auto packFile = std::unique_ptr<PackFile>(vpkVTMB);
40 for (
int i = 0; i <= 9; i++) {
44 for (
int j = 0; j <= 99; j++) {
46 if (!std::filesystem::exists(numberedPath)) {
49 vpkVTMB->openNumbered(i * 100 + j, numberedPath, callback);
53 if (vpkVTMB->knownArchives.empty()) {
54 uint32_t archiveIndex;
56 vpkVTMB->openNumbered(archiveIndex, path, callback);
59 vpkVTMB->currentArchive++;
64 FileStream reader{path};
65 reader.seek_in(
sizeof(uint32_t) * 2 +
sizeof(uint8_t), std::ios::end);
67 const auto fileCount = reader.read<uint32_t>();
68 const auto dirOffset = reader.read<uint32_t>();
71 const auto version = reader.read<uint8_t>();
82 reader.seek_in(dirOffset);
83 for (uint32_t i = 0; i < fileCount; i++) {
87 auto entryPath = this->
cleanEntryPath(reader.read_string(reader.read<uint32_t>()));
89 entry.
offset = reader.read<uint32_t>();
90 entry.
length = reader.read<uint32_t>();
92 this->
entries.emplace(entryPath, entry);
95 callback(entryPath, entry);
102 const auto entry = this->
findEntry(path);
106 if (entry->unbaked) {
115 stream.seek_in_u(entry->offset);
116 return stream.read_bytes(entry->length);
121 entry.
length = buffer.size();
139 if (!std::filesystem::create_directory(tempDir, ec)) {
153 std::unordered_map<uint16_t, std::vector<std::pair<std::string, Entry*>>> entriesToBake;
158 entriesToBake[entry.
archiveIndex].emplace_back(path, &entry);
161 for (
auto& [archiveIndex, entriesToBakeInArchive] : entriesToBake) {
166 for (
auto& [path, entry] : entriesToBakeInArchive) {
167 if (
auto binData = this->
readEntry(path)) {
168 entry->
offset = stream.tell_out();
169 stream.write(*binData);
177 const auto dirOffset = stream.tell_out();
178 for (
const auto& [path, entry] : entriesToBakeInArchive) {
179 stream.write<uint32_t>(path.length());
180 stream.write(path,
false);
181 stream.write<uint32_t>(entry->
offset);
182 stream.write<uint32_t>(entry->
length);
185 callback(path, *entry);
190 stream.write<uint32_t>(entriesToBakeInArchive.size());
191 stream.write<uint32_t>(dirOffset);
192 stream.write<uint8_t>(0);
195 if (entriesToBake.contains(this->currentArchive)) {
201 std::filesystem::remove_all(tempDir, ec);
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 archiveIndex
Which external archive this entry is in.
uint64_t length
Length in bytes (in formats with compression, this is the uncompressed length).
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)
bool bake()
If output folder is an empty string, it will overwrite the original.
std::string getBakeOutputDir(const std::string &outputDir) const
std::string getTruncatedFilepath() const
/home/user/pak01_dir.vpk -> /home/user/pak01
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)
static std::unique_ptr< PackFile > open(const std::string &path, const EntryCallback &callback=nullptr)
Open Vampire: The Masquerade - Bloodlines VPK files.
static std::unique_ptr< PackFile > create(const std::string &path)
Create Vampire: The Masquerade - Bloodlines VPK files.
Attribute getSupportedEntryAttributes() const override
Returns a list of supported entry attributes Mostly for GUI programs that show entries and their meta...
std::vector< uint32_t > knownArchives
std::optional< std::vector< std::byte > > readEntry(const std::string &path_) const override
Try to read the entry's data to a bytebuffer.
void addEntryInternal(Entry &entry, const std::string &path, std::vector< std::byte > &buffer, EntryOptions options) override
std::string getTruncatedFilestem() const override
/home/user/pak01_dir.vpk -> pak01
void openNumbered(uint32_t archiveIndex, const std::string &path, const EntryCallback &callback)
bool isNumber(char c)
If a char is a numerical character (0-9).
std::string padNumber(int64_t number, int width)
std::from_chars_result toInt(std::string_view number, std::integral auto &out, int base=10)
std::string generateUUIDv4()
constexpr std::string_view VPK_VTMB_EXTENSION