2022-10-12 23:39:26 +02:00
|
|
|
|
//=============================================================================//
|
|
|
|
|
//
|
|
|
|
|
// Purpose: Valve Pak utility class.
|
|
|
|
|
//
|
|
|
|
|
//=============================================================================//
|
|
|
|
|
// packedstore.cpp
|
|
|
|
|
//
|
2022-11-16 00:54:51 +01:00
|
|
|
|
// Note: VPK's are created in pairs of a directory file and pack file(s).
|
|
|
|
|
// - <locale><target>_<level>.bsp.pak000_dir.vpk --> directory file.
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// - <target>_<level>.bsp.pak000_<patch>.vpk ------> pack file.
|
2022-11-16 00:54:51 +01:00
|
|
|
|
//
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// The directory file contains the entire directory tree of the VPK. The
|
|
|
|
|
// filesystem essentially mounts this as additional paths to search through.
|
|
|
|
|
//
|
|
|
|
|
// Each asset is an entry in the VPK directory (see 'VPKEntryBlock_t'), an asset
|
|
|
|
|
// contains at least 1 chunk (see 'VPKChunkDescriptor_t'). If an asset is larger
|
|
|
|
|
// than 'ENTRY_MAX_LEN', the asset will be carved into chunks of 'ENTRY_MAX_LEN'
|
|
|
|
|
// or smaller, as this is the size of the decompress buffer in the engine.
|
|
|
|
|
//
|
|
|
|
|
// The VPK can be patched; the descriptor of this file would be adjusted as such
|
|
|
|
|
// that it would read the data from a different pack file containing the patched
|
|
|
|
|
// data. The only files that need to be shipped after a patch is the patched VPK
|
|
|
|
|
// directory file, and the additional pack file containing the patch. Untouched
|
|
|
|
|
// data is still getting read from the old pack file.
|
2022-10-12 23:39:26 +02:00
|
|
|
|
//
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
2023-06-26 22:34:24 +02:00
|
|
|
|
|
2022-06-02 01:59:03 +02:00
|
|
|
|
#include "tier1/cvar.h"
|
2023-05-29 21:41:17 +02:00
|
|
|
|
#include "tier2/fileutils.h"
|
2022-06-02 01:59:03 +02:00
|
|
|
|
#include "mathlib/adler32.h"
|
|
|
|
|
#include "mathlib/crc32.h"
|
2022-06-04 13:55:24 +02:00
|
|
|
|
#include "mathlib/sha1.h"
|
2022-11-23 12:18:33 +01:00
|
|
|
|
#include "filesystem/filesystem.h"
|
|
|
|
|
#include "vpc/keyvalues.h"
|
2022-06-02 01:59:03 +02:00
|
|
|
|
#include "vpklib/packedstore.h"
|
2022-03-04 12:22:17 +01:00
|
|
|
|
|
2023-07-03 02:27:09 +02:00
|
|
|
|
static const std::regex s_DirFileRegex{ R"((?:.*\/)?([^_]*_)(.*)(.bsp.pak000_dir).*)" };
|
|
|
|
|
static const std::regex s_BlockFileRegex{ R"(pak000_([0-9]{3}))" };
|
|
|
|
|
|
2022-06-02 01:59:03 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: initialize parameters for compression algorithm
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CPackedStore::InitLzCompParams(void)
|
|
|
|
|
{
|
|
|
|
|
/*| PARAMETERS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||*/
|
2022-06-05 12:28:49 +02:00
|
|
|
|
m_lzCompParams.m_dict_size_log2 = VPK_DICT_SIZE;
|
2022-09-11 00:16:31 +02:00
|
|
|
|
m_lzCompParams.m_level = GetCompressionLevel();
|
|
|
|
|
m_lzCompParams.m_compress_flags = lzham_compress_flags::LZHAM_COMP_FLAG_DETERMINISTIC_PARSING;
|
2022-11-17 20:37:12 +01:00
|
|
|
|
m_lzCompParams.m_max_helper_threads = fs_packedstore_max_helper_threads->GetInt();
|
2022-06-02 01:59:03 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: initialize parameters for decompression algorithm
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CPackedStore::InitLzDecompParams(void)
|
|
|
|
|
{
|
|
|
|
|
/*| PARAMETERS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||*/
|
2022-06-05 12:28:49 +02:00
|
|
|
|
m_lzDecompParams.m_dict_size_log2 = VPK_DICT_SIZE;
|
2022-12-04 01:33:10 +01:00
|
|
|
|
m_lzDecompParams.m_decompress_flags = lzham_decompress_flags::LZHAM_DECOMP_FLAG_OUTPUT_UNBUFFERED;
|
2022-06-02 01:59:03 +02:00
|
|
|
|
m_lzDecompParams.m_struct_size = sizeof(lzham_decompress_params);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-11 00:16:31 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: gets the LZHAM compression level
|
|
|
|
|
// output : lzham_compress_level
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
lzham_compress_level CPackedStore::GetCompressionLevel(void) const
|
|
|
|
|
{
|
|
|
|
|
const char* pszLevel = fs_packedstore_compression_level->GetString();
|
|
|
|
|
|
|
|
|
|
if(strcmp(pszLevel, "fastest") == NULL)
|
|
|
|
|
return lzham_compress_level::LZHAM_COMP_LEVEL_FASTEST;
|
|
|
|
|
else if (strcmp(pszLevel, "faster") == NULL)
|
|
|
|
|
return lzham_compress_level::LZHAM_COMP_LEVEL_FASTER;
|
|
|
|
|
else if (strcmp(pszLevel, "default") == NULL)
|
|
|
|
|
return lzham_compress_level::LZHAM_COMP_LEVEL_DEFAULT;
|
|
|
|
|
else if (strcmp(pszLevel, "better") == NULL)
|
|
|
|
|
return lzham_compress_level::LZHAM_COMP_LEVEL_BETTER;
|
|
|
|
|
else if (strcmp(pszLevel, "uber") == NULL)
|
|
|
|
|
return lzham_compress_level::LZHAM_COMP_LEVEL_UBER;
|
|
|
|
|
else
|
|
|
|
|
return lzham_compress_level::LZHAM_COMP_LEVEL_DEFAULT;
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-02 01:59:03 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: obtains and returns the entry block to the vector
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// Input : &entryBlocks -
|
|
|
|
|
// hDirectoryFile -
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
// output : vector<VPKEntryBlock_t>
|
2022-06-02 01:59:03 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-05-29 21:41:17 +02:00
|
|
|
|
void CPackedStore::GetEntryBlocks(CUtlVector<VPKEntryBlock_t>& entryBlocks, FileHandle_t hDirectoryFile) const
|
2022-06-02 01:59:03 +02:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
CUtlString fileName, filePath, fileExtension;
|
2022-06-02 01:59:03 +02:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
while (!(fileExtension = FileSystem()->ReadString(hDirectoryFile)).IsEmpty())
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
while (!(filePath = FileSystem()->ReadString(hDirectoryFile)).IsEmpty())
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
while (!(fileName = FileSystem()->ReadString(hDirectoryFile)).IsEmpty())
|
2022-09-10 00:07:42 +02:00
|
|
|
|
{
|
2023-07-04 21:39:31 +02:00
|
|
|
|
filePath.AppendSlash();
|
2023-05-29 21:41:17 +02:00
|
|
|
|
const CUtlString svFilePath = FormatEntryPath(filePath, fileName, fileExtension);
|
2023-07-04 21:39:31 +02:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
entryBlocks.AddToTail(VPKEntryBlock_t(hDirectoryFile, svFilePath.Get()));
|
2022-09-10 00:07:42 +02:00
|
|
|
|
}
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2022-11-23 17:09:48 +01:00
|
|
|
|
// Purpose: scans the input directory and returns the values to the vector if path exists in manifest
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// Input : &entryValues -
|
|
|
|
|
// *workspacePath -
|
|
|
|
|
// *dirFileName -
|
|
|
|
|
// Output : true on success, false otherwise
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-05-29 21:41:17 +02:00
|
|
|
|
bool CPackedStore::GetEntryValues(CUtlVector<VPKKeyValues_t>& entryValues,
|
|
|
|
|
const CUtlString& workspacePath, const CUtlString& dirFileName) const
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
KeyValues* pManifestKV = GetManifest(workspacePath, GetLevelName(dirFileName));
|
2022-11-23 12:18:33 +01:00
|
|
|
|
|
|
|
|
|
if (!pManifestKV)
|
|
|
|
|
{
|
2023-02-04 15:59:45 +01:00
|
|
|
|
Warning(eDLL_T::FS, "Invalid VPK build manifest KV; unable to parse entry list\n");
|
2023-05-29 21:41:17 +02:00
|
|
|
|
return false;
|
2022-11-23 12:18:33 +01:00
|
|
|
|
}
|
2022-06-06 23:08:53 +02:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
CUtlVector<CUtlString> ignoreList;
|
2022-11-23 12:18:33 +01:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
if (!GetIgnoreList(ignoreList, workspacePath))
|
2022-06-02 01:59:03 +02:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
Warning(eDLL_T::FS, "No ignore file provided; continuing build without...\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (KeyValues* pSubKey = pManifestKV->GetFirstSubKey(); pSubKey != nullptr; pSubKey = pSubKey->GetNextKey())
|
|
|
|
|
{
|
|
|
|
|
const char* pszFileName = pSubKey->GetName();
|
|
|
|
|
|
|
|
|
|
if (!VALID_CHARSTAR(pszFileName))
|
2022-06-02 01:59:03 +02:00
|
|
|
|
{
|
2023-01-26 03:00:53 +01:00
|
|
|
|
continue;
|
2022-06-02 01:59:03 +02:00
|
|
|
|
}
|
2022-11-23 12:18:33 +01:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
CUtlString fileName;
|
2023-07-05 23:04:07 +02:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
fileName.Format("%s%s", workspacePath.Get(), pszFileName);
|
2023-07-05 23:04:07 +02:00
|
|
|
|
fileName.FixSlashes('/');
|
2023-05-29 21:41:17 +02:00
|
|
|
|
|
|
|
|
|
if (ShouldPrune(fileName, ignoreList))
|
|
|
|
|
{
|
|
|
|
|
// Do not add to the build list.
|
|
|
|
|
continue;
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
}
|
2023-05-29 21:41:17 +02:00
|
|
|
|
|
|
|
|
|
entryValues.AddToTail(VPKKeyValues_t(
|
2023-07-08 02:18:18 +02:00
|
|
|
|
Move(fileName),
|
2023-05-29 21:41:17 +02:00
|
|
|
|
int16_t(pSubKey->GetInt("preloadSize", NULL)),
|
|
|
|
|
pSubKey->GetInt("loadFlags", static_cast<uint32_t>(EPackedLoadFlags::LOAD_VISIBLE) | static_cast<uint32_t>(EPackedLoadFlags::LOAD_CACHE)),
|
|
|
|
|
int16_t(pSubKey->GetInt("textureFlags", static_cast<uint16_t>(EPackedTextureFlags::TEXTURE_DEFAULT))),
|
|
|
|
|
pSubKey->GetBool("useCompression", true),
|
|
|
|
|
pSubKey->GetBool("deDuplicate", true))
|
|
|
|
|
);
|
2022-06-02 01:59:03 +02:00
|
|
|
|
}
|
2023-05-29 21:41:17 +02:00
|
|
|
|
|
|
|
|
|
pManifestKV->DeleteThis();
|
|
|
|
|
return true;
|
2022-06-02 01:59:03 +02:00
|
|
|
|
}
|
|
|
|
|
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2022-10-11 15:37:34 +02:00
|
|
|
|
// Purpose: gets the parts of the directory file name
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// Input : &dirFileName -
|
|
|
|
|
// nCaptureGroup - (1 = locale + target, 2 = level)
|
2022-10-11 15:37:34 +02:00
|
|
|
|
// Output : part of directory file name as string
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-05-29 21:41:17 +02:00
|
|
|
|
CUtlString CPackedStore::GetNameParts(const CUtlString& dirFileName, int nCaptureGroup) const
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
std::cmatch regexMatches;
|
2023-07-03 02:27:09 +02:00
|
|
|
|
std::regex_search(dirFileName.Get(), regexMatches, s_DirFileRegex);
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
return regexMatches[nCaptureGroup].str().c_str();
|
2022-06-05 17:28:39 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2022-11-16 00:54:51 +01:00
|
|
|
|
// Purpose: gets the level name from the directory file name
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// Input : &dirFileName -
|
2022-11-16 00:54:51 +01:00
|
|
|
|
// Output : level name as string (e.g. "mp_rr_box")
|
2022-06-05 17:28:39 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-05-29 21:41:17 +02:00
|
|
|
|
CUtlString CPackedStore::GetLevelName(const CUtlString& dirFileName) const
|
2022-06-05 17:28:39 +02:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
std::cmatch regexMatches;
|
2023-07-03 02:27:09 +02:00
|
|
|
|
std::regex_search(dirFileName.Get(), regexMatches, s_DirFileRegex);
|
2022-06-05 17:28:39 +02:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
CUtlString result;
|
|
|
|
|
result.Format("%s%s", regexMatches[1].str().c_str(), regexMatches[2].str().c_str());
|
|
|
|
|
|
|
|
|
|
return result;
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2022-11-23 12:18:33 +01:00
|
|
|
|
// Purpose: gets the manifest file associated with the VPK name (must be freed after wards)
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// Input : &workspacePath -
|
|
|
|
|
// &manifestFile -
|
2022-11-23 17:09:48 +01:00
|
|
|
|
// Output : KeyValues (build manifest pointer)
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-05-29 21:41:17 +02:00
|
|
|
|
KeyValues* CPackedStore::GetManifest(const CUtlString& workspacePath, const CUtlString& manifestFile) const
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
CUtlString outPath;
|
|
|
|
|
outPath.Format("%s%s%s.txt", workspacePath.Get(), "manifest/", manifestFile.Get());
|
2022-11-23 12:18:33 +01:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
KeyValues* pManifestKV = FileSystem()->LoadKeyValues(IFileSystem::TYPE_COMMON, outPath.Get(), "PLATFORM");
|
2022-11-23 12:18:33 +01:00
|
|
|
|
return pManifestKV;
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-06 23:08:53 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: gets the contents from the global ignore list (.vpkignore)
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// Input : &ignoreList -
|
|
|
|
|
// &workspacePath -
|
2022-11-16 00:54:51 +01:00
|
|
|
|
// Output : a string vector of ignored directories/files and extensions
|
2022-06-06 23:08:53 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-05-29 21:41:17 +02:00
|
|
|
|
bool CPackedStore::GetIgnoreList(CUtlVector<CUtlString>& ignoreList, const CUtlString& workspacePath) const
|
2022-06-06 23:08:53 +02:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
CUtlString toIgnore;
|
|
|
|
|
toIgnore.Format("%s%s", workspacePath.Get(), VPK_IGNORE_FILE);
|
|
|
|
|
|
|
|
|
|
FileHandle_t hIgnoreFile = FileSystem()->Open(toIgnore.Get(), "rt", "PLATFORM");
|
2022-11-23 12:18:33 +01:00
|
|
|
|
if (!hIgnoreFile)
|
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
return false;
|
2022-11-23 12:18:33 +01:00
|
|
|
|
}
|
2022-06-06 23:08:53 +02:00
|
|
|
|
|
2022-11-23 12:18:33 +01:00
|
|
|
|
char szIgnore[MAX_PATH];
|
|
|
|
|
|
|
|
|
|
while (FileSystem()->ReadLine(szIgnore, sizeof(szIgnore) - 1, hIgnoreFile))
|
2022-06-06 23:08:53 +02:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
if (!strstr(szIgnore, "//")) // Skip comments.
|
2022-06-06 23:08:53 +02:00
|
|
|
|
{
|
2022-11-23 12:18:33 +01:00
|
|
|
|
if (char* pEOL = strchr(szIgnore, '\n'))
|
2022-06-06 23:08:53 +02:00
|
|
|
|
{
|
2022-11-23 12:18:33 +01:00
|
|
|
|
// Null newline character.
|
|
|
|
|
*pEOL = '\0';
|
|
|
|
|
if (pEOL - szIgnore > 0)
|
2022-06-06 23:08:53 +02:00
|
|
|
|
{
|
2022-11-23 12:18:33 +01:00
|
|
|
|
// Null carriage return.
|
|
|
|
|
if (*(pEOL - 1) == '\r')
|
|
|
|
|
{
|
|
|
|
|
*(pEOL - 1) = '\0';
|
|
|
|
|
}
|
2022-06-06 23:08:53 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-23 12:18:33 +01:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
ignoreList.AddToTail(szIgnore);
|
2022-06-06 23:08:53 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-23 12:18:33 +01:00
|
|
|
|
|
|
|
|
|
FileSystem()->Close(hIgnoreFile);
|
2023-05-29 21:41:17 +02:00
|
|
|
|
return true;
|
2022-06-06 23:08:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-02 01:59:03 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2022-06-06 14:54:22 +02:00
|
|
|
|
// Purpose: formats the file entry path
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// Input : &filePath -
|
|
|
|
|
// &fileName -
|
|
|
|
|
// &fileExt -
|
2022-10-11 15:37:34 +02:00
|
|
|
|
// Output : formatted entry path
|
2022-06-02 01:59:03 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-05-29 21:41:17 +02:00
|
|
|
|
CUtlString CPackedStore::FormatEntryPath(const CUtlString& filePath,
|
|
|
|
|
const CUtlString& fileName, const CUtlString& fileExt) const
|
2022-06-02 01:59:03 +02:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
CUtlString result;
|
|
|
|
|
|
|
|
|
|
const char* pszFilePath = filePath.Get();
|
2023-07-04 22:08:44 +02:00
|
|
|
|
const bool isRoot = pszFilePath[0] == ' ';
|
2023-05-29 21:41:17 +02:00
|
|
|
|
|
|
|
|
|
result.Format("%s%s.%s", isRoot ? "" : pszFilePath,
|
|
|
|
|
fileName.Get(), fileExt.Get());
|
|
|
|
|
|
2023-07-05 23:04:07 +02:00
|
|
|
|
result.FixSlashes('/');
|
2023-05-29 21:41:17 +02:00
|
|
|
|
return result;
|
2022-06-02 01:59:03 +02:00
|
|
|
|
}
|
|
|
|
|
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: builds the VPK manifest file
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// Input : &entryBlocks -
|
|
|
|
|
// &workspacePath -
|
|
|
|
|
// &manifestName -
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-05-29 21:41:17 +02:00
|
|
|
|
void CPackedStore::BuildManifest(const CUtlVector<VPKEntryBlock_t>& entryBlocks, const CUtlString& workspacePath, const CUtlString& manifestName) const
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
{
|
2022-11-23 12:18:33 +01:00
|
|
|
|
KeyValues kv("BuildManifest");
|
|
|
|
|
KeyValues* pManifestKV = kv.FindKey("BuildManifest", true);
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
|
2023-07-04 22:08:44 +02:00
|
|
|
|
FOR_EACH_VEC(entryBlocks, i)
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
{
|
2023-07-04 22:08:44 +02:00
|
|
|
|
const VPKEntryBlock_t& entry = entryBlocks[i];
|
|
|
|
|
const VPKChunkDescriptor_t& descriptor = entry.m_Fragments[0];
|
|
|
|
|
|
2023-07-05 23:04:07 +02:00
|
|
|
|
// Copy, because we need to change the '/' slashes into
|
|
|
|
|
// '\\'. KeyValues has the '/' character reserved for
|
|
|
|
|
// delimiting subfields.
|
|
|
|
|
CUtlString entryPath = entry.m_EntryPath;
|
|
|
|
|
entryPath.FixSlashes('\\');
|
|
|
|
|
|
|
|
|
|
KeyValues* pEntryKV = pManifestKV->FindKey(entryPath.Get(), true);
|
2022-11-23 12:18:33 +01:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
pEntryKV->SetInt("preloadSize", entry.m_iPreloadSize);
|
2023-07-04 22:08:44 +02:00
|
|
|
|
pEntryKV->SetInt("loadFlags", descriptor.m_nLoadFlags);
|
|
|
|
|
pEntryKV->SetInt("textureFlags", descriptor.m_nTextureFlags);
|
|
|
|
|
pEntryKV->SetBool("useCompression", descriptor.m_nCompressedSize != descriptor.m_nUncompressedSize);
|
2023-04-03 20:08:36 +02:00
|
|
|
|
pEntryKV->SetBool("deDuplicate", true);
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
CUtlString outPath;
|
|
|
|
|
outPath.Format("%s%s%s.txt", workspacePath.Get(), "manifest/", manifestName.Get());
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
CUtlBuffer outBuf(int64_t(0), 0, CUtlBuffer::TEXT_BUFFER);
|
|
|
|
|
kv.RecursiveSaveToFile(outBuf, 0);
|
2022-11-23 12:18:33 +01:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
FileSystem()->CreateDirHierarchy(outPath.DirName().Get(), "PLATFORM");
|
|
|
|
|
FileSystem()->WriteFile(outPath.Get(), "PLATFORM", outBuf);
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-02 01:59:03 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: validates extraction result with precomputed CRC32 hash
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// Input : &assetPath -
|
|
|
|
|
// : nFileCRC -
|
2022-06-02 01:59:03 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-05-29 21:41:17 +02:00
|
|
|
|
void CPackedStore::ValidateCRC32PostDecomp(const CUtlString& assetPath, const uint32_t nFileCRC)
|
2022-06-02 01:59:03 +02:00
|
|
|
|
{
|
2023-07-08 02:18:18 +02:00
|
|
|
|
const char* pAssetPath = assetPath.Get();
|
|
|
|
|
|
|
|
|
|
FileHandle_t hAsset = FileSystem()->Open(pAssetPath, "rb", "PLATFORM");
|
2022-11-23 12:18:33 +01:00
|
|
|
|
if (!hAsset)
|
|
|
|
|
{
|
2023-07-08 02:18:18 +02:00
|
|
|
|
Error(eDLL_T::FS, NO_ERROR, "%s - Unable to open '%s' (insufficient rights?)\n", __FUNCTION__, pAssetPath);
|
2022-11-23 12:18:33 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
2023-05-29 21:41:17 +02:00
|
|
|
|
|
2023-08-09 14:43:54 +02:00
|
|
|
|
const ssize_t nLen = FileSystem()->Size(hAsset);
|
2023-06-26 22:34:24 +02:00
|
|
|
|
std::unique_ptr<uint8_t[]> pBuf(new uint8_t[nLen]);
|
2022-11-23 12:18:33 +01:00
|
|
|
|
|
2023-06-26 22:34:24 +02:00
|
|
|
|
FileSystem()->Read(pBuf.get(), nLen, hAsset);
|
2022-11-23 12:18:33 +01:00
|
|
|
|
FileSystem()->Close(hAsset);
|
|
|
|
|
|
2023-06-26 22:34:24 +02:00
|
|
|
|
uint32_t nCrc32 = crc32::update(NULL, pBuf.get(), nLen);
|
2022-12-04 01:33:10 +01:00
|
|
|
|
if (nCrc32 != nFileCRC)
|
2022-06-02 01:59:03 +02:00
|
|
|
|
{
|
2022-12-04 01:33:10 +01:00
|
|
|
|
Warning(eDLL_T::FS, "Computed checksum '0x%lX' doesn't match expected checksum '0x%lX'. File may be corrupt!\n", nCrc32, nFileCRC);
|
2022-06-02 01:59:03 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-03 22:46:10 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: attempts to deduplicate a chunk of data by comparing it to existing chunks
|
|
|
|
|
// Input : *pEntryBuffer -
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// &descriptor -
|
|
|
|
|
// chunkIndex -
|
2023-04-03 22:46:10 +02:00
|
|
|
|
// Output : true if the chunk was deduplicated, false otherwise
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
bool CPackedStore::Deduplicate(const uint8_t* pEntryBuffer, VPKChunkDescriptor_t& descriptor, const size_t chunkIndex)
|
|
|
|
|
{
|
|
|
|
|
string entryHash(reinterpret_cast<const char*>(pEntryBuffer), descriptor.m_nUncompressedSize);
|
|
|
|
|
entryHash = sha1(entryHash);
|
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
auto p = m_ChunkHashMap.insert({ entryHash.c_str(), descriptor });
|
2023-04-03 22:46:10 +02:00
|
|
|
|
if (!p.second) // Map to existing chunk to avoid having copies of the same data.
|
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
DevMsg(eDLL_T::FS, "Mapping chunk '%zu' ('%s') to existing chunk at '0x%llx'\n",
|
2023-04-03 22:46:10 +02:00
|
|
|
|
chunkIndex, entryHash.c_str(), p.first->second.m_nPackFileOffset);
|
|
|
|
|
descriptor = p.first->second;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: determines whether the file should be pruned from the build list
|
|
|
|
|
// Input : &filePath -
|
|
|
|
|
// &ignoreList -
|
|
|
|
|
// Output : true if it should be pruned, false otherwise
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
bool CPackedStore::ShouldPrune(const CUtlString& filePath, CUtlVector<CUtlString>& ignoreList) const
|
|
|
|
|
{
|
2023-07-08 02:18:18 +02:00
|
|
|
|
const char* pFilePath = filePath.Get();
|
|
|
|
|
|
|
|
|
|
if (!V_IsValidPath(pFilePath))
|
2023-05-29 21:41:17 +02:00
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-26 22:34:24 +02:00
|
|
|
|
FOR_EACH_VEC(ignoreList, j)
|
2023-05-29 21:41:17 +02:00
|
|
|
|
{
|
2023-07-04 22:08:44 +02:00
|
|
|
|
const CUtlString& ignoreEntry = ignoreList[j];
|
2023-05-29 21:41:17 +02:00
|
|
|
|
|
2023-07-08 02:18:18 +02:00
|
|
|
|
if (ignoreEntry.IsEqual_CaseInsensitive(pFilePath))
|
2023-05-29 21:41:17 +02:00
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-08 02:18:18 +02:00
|
|
|
|
FileHandle_t fileHandle = FileSystem()->Open(pFilePath, "rb", "PLATFORM");
|
2023-05-29 21:41:17 +02:00
|
|
|
|
|
|
|
|
|
if (fileHandle)
|
|
|
|
|
{
|
2023-08-09 14:43:54 +02:00
|
|
|
|
const ssize_t nSize = FileSystem()->Size(fileHandle);
|
2023-05-29 21:41:17 +02:00
|
|
|
|
|
|
|
|
|
if (!nSize)
|
|
|
|
|
{
|
2023-07-08 02:18:18 +02:00
|
|
|
|
Warning(eDLL_T::FS, "File '%s' listed in build manifest appears empty or truncated\n", pFilePath);
|
2023-05-29 21:41:17 +02:00
|
|
|
|
FileSystem()->Close(fileHandle);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FileSystem()->Close(fileHandle);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-07-08 02:18:18 +02:00
|
|
|
|
Warning(eDLL_T::FS, "File '%s' listed in build manifest couldn't be opened\n", pFilePath);
|
2023-05-29 21:41:17 +02:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2022-11-16 00:54:51 +01:00
|
|
|
|
// Purpose: packs all files from workspace path into VPK file
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// Input : &vpkPair -
|
|
|
|
|
// *workspaceName -
|
|
|
|
|
// *buildPath -
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-05-29 21:41:17 +02:00
|
|
|
|
void CPackedStore::PackWorkspace(const VPKPair_t& vpkPair, const char* workspaceName, const char* buildPath)
|
2022-06-02 02:00:35 +02:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
CUtlString workspacePath(workspaceName);
|
|
|
|
|
workspacePath.AppendSlash();
|
2023-07-05 23:04:07 +02:00
|
|
|
|
workspacePath.FixSlashes('/');
|
2023-05-29 21:41:17 +02:00
|
|
|
|
|
|
|
|
|
CUtlString packFilePath;
|
|
|
|
|
CUtlString dirFilePath;
|
2023-07-08 02:18:18 +02:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
packFilePath.Format("%s%s", buildPath, vpkPair.m_PackName.Get());
|
|
|
|
|
dirFilePath.Format("%s%s", buildPath, vpkPair.m_DirName.Get());
|
2022-06-02 02:00:35 +02:00
|
|
|
|
|
2023-07-08 02:18:18 +02:00
|
|
|
|
FileSystem()->CreateDirHierarchy(packFilePath.DirName().Get(), "GAME");
|
2023-05-29 21:41:17 +02:00
|
|
|
|
FileHandle_t hPackFile = FileSystem()->Open(packFilePath.Get(), "wb", "GAME");
|
2022-11-23 12:18:33 +01:00
|
|
|
|
if (!hPackFile)
|
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
Error(eDLL_T::FS, NO_ERROR, "%s - Unable to write to '%s' (read-only?)\n", __FUNCTION__, packFilePath.Get());
|
2022-11-23 12:18:33 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
|
2023-06-26 22:34:24 +02:00
|
|
|
|
std::unique_ptr<uint8_t[]> pEntryBuffer(new uint8_t[ENTRY_MAX_LEN]);
|
|
|
|
|
|
2023-04-03 20:02:56 +02:00
|
|
|
|
if (!pEntryBuffer)
|
|
|
|
|
{
|
|
|
|
|
Error(eDLL_T::FS, NO_ERROR, "%s - Unable to allocate memory for entry buffer!\n", __FUNCTION__);
|
2023-04-03 22:46:10 +02:00
|
|
|
|
FileSystem()->Close(hPackFile);
|
2023-04-03 20:02:56 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
CUtlVector<VPKKeyValues_t> entryValues;
|
|
|
|
|
CUtlVector<VPKEntryBlock_t> entryBlocks;
|
2022-06-06 23:08:53 +02:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
if (!GetEntryValues(entryValues, workspacePath, vpkPair.m_DirName))
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
FileSystem()->Close(hPackFile);
|
|
|
|
|
return;
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
}
|
2022-06-02 02:00:35 +02:00
|
|
|
|
|
2023-08-09 14:43:54 +02:00
|
|
|
|
size_t nSharedTotal = NULL;
|
|
|
|
|
size_t nSharedCount = NULL;
|
2022-06-05 17:28:39 +02:00
|
|
|
|
|
2023-06-26 22:34:24 +02:00
|
|
|
|
FOR_EACH_VEC(entryValues, i)
|
2022-06-02 02:00:35 +02:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
const VPKKeyValues_t& entryValue = entryValues[i];
|
2023-07-08 02:18:18 +02:00
|
|
|
|
const char* pEntryPath = entryValue.m_EntryPath.Get();
|
|
|
|
|
|
|
|
|
|
FileHandle_t hAsset = FileSystem()->Open(pEntryPath, "rb", "PLATFORM");
|
2023-07-04 22:08:44 +02:00
|
|
|
|
|
2022-11-23 12:18:33 +01:00
|
|
|
|
if (!hAsset)
|
2022-06-02 02:00:35 +02:00
|
|
|
|
{
|
2023-07-08 02:18:18 +02:00
|
|
|
|
Error(eDLL_T::FS, NO_ERROR, "%s - Unable to open '%s' (insufficient rights?)\n", __FUNCTION__, pEntryPath);
|
2022-11-23 12:18:33 +01:00
|
|
|
|
continue;
|
|
|
|
|
}
|
2022-10-02 19:24:00 +02:00
|
|
|
|
|
2023-07-08 02:18:18 +02:00
|
|
|
|
const char* szDestPath = (pEntryPath + workspacePath.Length());
|
2023-05-29 21:41:17 +02:00
|
|
|
|
if (PATHSEPARATOR(szDestPath[0]))
|
|
|
|
|
{
|
|
|
|
|
szDestPath++;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-09 14:43:54 +02:00
|
|
|
|
const ssize_t nLen = FileSystem()->Size(hAsset);
|
2023-06-26 22:34:24 +02:00
|
|
|
|
std::unique_ptr<uint8_t[]> pBuf(new uint8_t[nLen]);
|
2022-06-02 02:00:35 +02:00
|
|
|
|
|
2023-06-26 22:34:24 +02:00
|
|
|
|
FileSystem()->Read(pBuf.get(), nLen, hAsset);
|
2022-11-23 12:18:33 +01:00
|
|
|
|
FileSystem()->Seek(hAsset, 0, FileSystemSeek_t::FILESYSTEM_SEEK_HEAD);
|
2022-06-02 02:00:35 +02:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
DevMsg(eDLL_T::FS, "Packing entry '%i' ('%s')\n", i, szDestPath);
|
2023-07-05 23:04:07 +02:00
|
|
|
|
int index = entryBlocks.AddToTail(VPKEntryBlock_t(
|
2023-06-26 22:34:24 +02:00
|
|
|
|
pBuf.get(),
|
2023-05-29 21:41:17 +02:00
|
|
|
|
nLen,
|
|
|
|
|
FileSystem()->Tell(hPackFile),
|
|
|
|
|
entryValue.m_iPreloadSize,
|
|
|
|
|
0,
|
|
|
|
|
entryValue.m_nLoadFlags,
|
|
|
|
|
entryValue.m_nTextureFlags,
|
|
|
|
|
CUtlString(szDestPath)));
|
|
|
|
|
|
2023-07-05 23:04:07 +02:00
|
|
|
|
VPKEntryBlock_t& entryBlock = entryBlocks[index];
|
2023-06-26 22:34:24 +02:00
|
|
|
|
|
|
|
|
|
FOR_EACH_VEC(entryBlock.m_Fragments, j)
|
2022-11-23 12:18:33 +01:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
VPKChunkDescriptor_t& descriptor = entryBlock.m_Fragments[j];
|
2022-06-02 02:00:35 +02:00
|
|
|
|
|
2023-08-09 14:43:54 +02:00
|
|
|
|
FileSystem()->Read(pEntryBuffer.get(), descriptor.m_nCompressedSize, hAsset);
|
2023-05-29 21:41:17 +02:00
|
|
|
|
descriptor.m_nPackFileOffset = FileSystem()->Tell(hPackFile);
|
2022-10-02 19:24:00 +02:00
|
|
|
|
|
2023-06-26 22:34:24 +02:00
|
|
|
|
if (entryValue.m_bDeduplicate && Deduplicate(pEntryBuffer.get(), descriptor, j))
|
2022-11-23 12:18:33 +01:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
nSharedTotal += descriptor.m_nCompressedSize;
|
2023-04-03 22:46:10 +02:00
|
|
|
|
nSharedCount++;
|
2022-10-02 12:17:03 +02:00
|
|
|
|
|
2023-04-03 22:46:10 +02:00
|
|
|
|
// Data was deduplicated.
|
|
|
|
|
continue;
|
2022-11-23 12:18:33 +01:00
|
|
|
|
}
|
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
if (entryValue.m_bUseCompression)
|
2022-11-23 12:18:33 +01:00
|
|
|
|
{
|
2023-06-26 22:34:24 +02:00
|
|
|
|
lzham_compress_status_t lzCompStatus = lzham_compress_memory(&m_lzCompParams, pEntryBuffer.get(), &descriptor.m_nCompressedSize, pEntryBuffer.get(),
|
2023-05-29 21:41:17 +02:00
|
|
|
|
descriptor.m_nUncompressedSize, nullptr);
|
2022-11-23 12:18:33 +01:00
|
|
|
|
|
2022-12-04 14:17:14 +01:00
|
|
|
|
if (lzCompStatus != lzham_compress_status_t::LZHAM_COMP_STATUS_SUCCESS)
|
2022-10-02 19:24:00 +02:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
Warning(eDLL_T::FS, "Status '%d' for chunk '%i' within entry '%i' in block '%hu' (chunk packed without compression)\n",
|
2023-07-05 23:04:07 +02:00
|
|
|
|
lzCompStatus, j, i, entryBlock.m_iPackFileIndex);
|
2022-11-23 12:18:33 +01:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
descriptor.m_nCompressedSize = descriptor.m_nUncompressedSize;
|
2022-06-02 02:00:35 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-23 12:18:33 +01:00
|
|
|
|
else // Write data uncompressed.
|
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
descriptor.m_nCompressedSize = descriptor.m_nUncompressedSize;
|
2022-11-23 12:18:33 +01:00
|
|
|
|
}
|
|
|
|
|
|
2023-08-09 14:43:54 +02:00
|
|
|
|
FileSystem()->Write(pEntryBuffer.get(), descriptor.m_nCompressedSize, hPackFile);
|
2022-06-02 02:00:35 +02:00
|
|
|
|
}
|
2022-11-23 12:18:33 +01:00
|
|
|
|
|
|
|
|
|
FileSystem()->Close(hAsset);
|
2022-06-02 02:00:35 +02:00
|
|
|
|
}
|
2022-11-23 12:18:33 +01:00
|
|
|
|
|
2023-08-09 14:43:54 +02:00
|
|
|
|
DevMsg(eDLL_T::FS, "*** Build block totaling '%zd' bytes with '%zu' shared bytes among '%zu' chunks\n", FileSystem()->Tell(hPackFile), nSharedTotal, nSharedCount);
|
2022-11-23 12:18:33 +01:00
|
|
|
|
FileSystem()->Close(hPackFile);
|
2022-10-02 12:17:03 +02:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
m_ChunkHashMap.clear();
|
2022-06-05 17:28:39 +02:00
|
|
|
|
|
2022-11-23 17:09:48 +01:00
|
|
|
|
VPKDir_t vDirectory;
|
2023-05-29 21:41:17 +02:00
|
|
|
|
vDirectory.BuildDirectoryFile(dirFilePath, entryBlocks);
|
2022-06-02 02:00:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2022-11-16 00:54:51 +01:00
|
|
|
|
// Purpose: rebuilds manifest and extracts all files from specified VPK file
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// Input : &vpkDirectory -
|
|
|
|
|
// &workspaceName -
|
2022-06-02 02:00:35 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-05-29 21:41:17 +02:00
|
|
|
|
void CPackedStore::UnpackWorkspace(const VPKDir_t& vpkDir, const char* workspaceName)
|
2022-06-02 02:00:35 +02:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
CUtlString workspacePath(workspaceName);
|
2023-07-05 23:04:07 +02:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
workspacePath.AppendSlash();
|
2023-07-05 23:04:07 +02:00
|
|
|
|
workspacePath.FixSlashes('/');
|
2023-05-29 21:41:17 +02:00
|
|
|
|
|
|
|
|
|
if (vpkDir.m_Header.m_nHeaderMarker != VPK_HEADER_MARKER ||
|
|
|
|
|
vpkDir.m_Header.m_nMajorVersion != VPK_MAJOR_VERSION ||
|
|
|
|
|
vpkDir.m_Header.m_nMinorVersion != VPK_MINOR_VERSION)
|
2022-06-05 17:28:39 +02:00
|
|
|
|
{
|
2022-09-14 01:14:51 +02:00
|
|
|
|
Error(eDLL_T::FS, NO_ERROR, "Unsupported VPK directory file (invalid header criteria)\n");
|
2022-06-05 17:28:39 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2022-11-23 12:18:33 +01:00
|
|
|
|
|
2023-06-26 22:34:24 +02:00
|
|
|
|
std::unique_ptr<uint8_t[]> pDestBuffer(new uint8_t[ENTRY_MAX_LEN]);
|
|
|
|
|
std::unique_ptr<uint8_t[]> pSourceBuffer(new uint8_t[ENTRY_MAX_LEN]);
|
2023-05-29 21:41:17 +02:00
|
|
|
|
|
2023-04-03 20:02:56 +02:00
|
|
|
|
if (!pDestBuffer || !pSourceBuffer)
|
|
|
|
|
{
|
|
|
|
|
Error(eDLL_T::FS, NO_ERROR, "%s - Unable to allocate memory for entry buffer!\n", __FUNCTION__);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
BuildManifest(vpkDir.m_EntryBlocks, workspacePath, GetLevelName(vpkDir.m_DirFilePath));
|
|
|
|
|
const CUtlString basePath = vpkDir.m_DirFilePath.StripFilename(false);
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
|
2023-07-06 00:49:17 +02:00
|
|
|
|
for (uint16_t packFileIndex : vpkDir.m_PakFileIndices)
|
2022-06-02 02:00:35 +02:00
|
|
|
|
{
|
2023-07-06 00:49:17 +02:00
|
|
|
|
const CUtlString packFile = basePath + vpkDir.GetPackFileNameForIndex(packFileIndex);
|
2023-07-08 02:18:18 +02:00
|
|
|
|
const char* pPackFile = packFile.Get();
|
2022-11-23 12:18:33 +01:00
|
|
|
|
|
|
|
|
|
// Read from each pack file.
|
2023-07-08 02:18:18 +02:00
|
|
|
|
FileHandle_t hPackFile = FileSystem()->Open(pPackFile, "rb", "GAME");
|
2022-11-23 12:18:33 +01:00
|
|
|
|
if (!hPackFile)
|
|
|
|
|
{
|
2023-07-08 02:18:18 +02:00
|
|
|
|
Error(eDLL_T::FS, NO_ERROR, "%s - Unable to open '%s' (insufficient rights?)\n", __FUNCTION__, pPackFile);
|
2022-11-23 12:18:33 +01:00
|
|
|
|
continue;
|
|
|
|
|
}
|
2022-06-02 02:00:35 +02:00
|
|
|
|
|
2023-06-26 22:34:24 +02:00
|
|
|
|
FOR_EACH_VEC(vpkDir.m_EntryBlocks, j)
|
2022-06-02 02:00:35 +02:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
const VPKEntryBlock_t& entryBlock = vpkDir.m_EntryBlocks[j];
|
2023-07-04 22:08:44 +02:00
|
|
|
|
|
2023-07-06 00:49:17 +02:00
|
|
|
|
if (entryBlock.m_iPackFileIndex != packFileIndex)
|
2022-06-04 03:12:46 +02:00
|
|
|
|
{
|
2023-04-03 22:46:10 +02:00
|
|
|
|
// Chunk doesn't belongs to this block.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-08 02:18:18 +02:00
|
|
|
|
const char* pEntryPath = entryBlock.m_EntryPath.Get();
|
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
CUtlString filePath;
|
2023-07-08 02:18:18 +02:00
|
|
|
|
filePath.Format("%s%s", workspacePath.Get(), pEntryPath);
|
2023-05-29 21:41:17 +02:00
|
|
|
|
|
|
|
|
|
FileSystem()->CreateDirHierarchy(filePath.DirName().Get(), "PLATFORM");
|
|
|
|
|
FileHandle_t hAsset = FileSystem()->Open(filePath.Get(), "wb", "PLATFORM");
|
2023-04-03 22:46:10 +02:00
|
|
|
|
|
|
|
|
|
if (!hAsset)
|
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
Error(eDLL_T::FS, NO_ERROR, "%s - Unable to write to '%s' (read-only?)\n", __FUNCTION__, filePath.Get());
|
2022-10-02 19:24:00 +02:00
|
|
|
|
continue;
|
2022-06-04 03:12:46 +02:00
|
|
|
|
}
|
2023-04-03 22:46:10 +02:00
|
|
|
|
|
2023-07-15 16:25:35 +02:00
|
|
|
|
DevMsg(eDLL_T::FS, "Unpacking entry '%i' from block '%hu' ('%s')\n",
|
|
|
|
|
j, packFileIndex, pEntryPath);
|
2023-06-26 22:34:24 +02:00
|
|
|
|
|
|
|
|
|
FOR_EACH_VEC(entryBlock.m_Fragments, k)
|
2022-06-02 02:00:35 +02:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
const VPKChunkDescriptor_t& fragment = entryBlock.m_Fragments[k];
|
2022-06-02 02:00:35 +02:00
|
|
|
|
|
2023-08-09 14:43:54 +02:00
|
|
|
|
FileSystem()->Seek(hPackFile, fragment.m_nPackFileOffset, FileSystemSeek_t::FILESYSTEM_SEEK_HEAD);
|
|
|
|
|
FileSystem()->Read(pSourceBuffer.get(), fragment.m_nCompressedSize, hPackFile);
|
2023-04-03 22:46:10 +02:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
if (fragment.m_nCompressedSize == fragment.m_nUncompressedSize) // Data is not compressed.
|
2022-06-02 02:00:35 +02:00
|
|
|
|
{
|
2023-08-09 14:43:54 +02:00
|
|
|
|
FileSystem()->Write(pSourceBuffer.get(), fragment.m_nUncompressedSize, hAsset);
|
2023-05-29 15:56:59 +02:00
|
|
|
|
continue;
|
2022-06-02 02:00:35 +02:00
|
|
|
|
}
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
|
2023-04-03 22:46:10 +02:00
|
|
|
|
size_t nDstLen = ENTRY_MAX_LEN;
|
2023-05-29 21:41:17 +02:00
|
|
|
|
assert(fragment.m_nCompressedSize <= nDstLen);
|
2022-06-02 02:00:35 +02:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
if (fragment.m_nCompressedSize > nDstLen)
|
2023-04-03 22:46:10 +02:00
|
|
|
|
break; // Corrupt or invalid chunk descriptor.
|
2022-06-04 03:12:46 +02:00
|
|
|
|
|
2023-06-26 22:34:24 +02:00
|
|
|
|
lzham_decompress_status_t lzDecompStatus = lzham_decompress_memory(&m_lzDecompParams, pDestBuffer.get(),
|
|
|
|
|
&nDstLen, pSourceBuffer.get(), fragment.m_nCompressedSize, nullptr);
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
|
2023-04-03 22:46:10 +02:00
|
|
|
|
if (lzDecompStatus != lzham_decompress_status_t::LZHAM_DECOMP_STATUS_SUCCESS)
|
|
|
|
|
{
|
2023-07-06 00:49:17 +02:00
|
|
|
|
Error(eDLL_T::FS, NO_ERROR, "Status '%d' for chunk '%i' within entry '%i' in block '%hu' (chunk not decompressed)\n",
|
|
|
|
|
lzDecompStatus, k, j, packFileIndex);
|
2023-04-03 22:46:10 +02:00
|
|
|
|
}
|
|
|
|
|
else // If successfully decompressed, write to file.
|
2022-06-02 02:00:35 +02:00
|
|
|
|
{
|
2023-08-09 14:43:54 +02:00
|
|
|
|
FileSystem()->Write(pDestBuffer.get(), nDstLen, hAsset);
|
2022-06-02 02:00:35 +02:00
|
|
|
|
}
|
2022-10-02 19:24:00 +02:00
|
|
|
|
}
|
2023-04-03 22:46:10 +02:00
|
|
|
|
|
|
|
|
|
FileSystem()->Close(hAsset);
|
2023-05-29 21:41:17 +02:00
|
|
|
|
ValidateCRC32PostDecomp(filePath, entryBlock.m_nFileCRC);
|
2022-06-02 02:00:35 +02:00
|
|
|
|
}
|
2022-11-23 12:18:33 +01:00
|
|
|
|
FileSystem()->Close(hPackFile);
|
2022-06-02 02:00:35 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-23 12:18:33 +01:00
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: 'VPKKeyValues_t' memory constructor
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// Input : &entryPath -
|
|
|
|
|
// iPreloadSize -
|
|
|
|
|
// nLoadFlags -
|
|
|
|
|
// nTextureFlags -
|
2022-11-23 12:18:33 +01:00
|
|
|
|
// bUseCompression -
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// bDeduplicate -
|
2022-11-23 12:18:33 +01:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-05-29 21:41:17 +02:00
|
|
|
|
VPKKeyValues_t::VPKKeyValues_t(const CUtlString& entryPath, uint16_t iPreloadSize,
|
|
|
|
|
uint32_t nLoadFlags, uint16_t nTextureFlags, bool bUseCompression, bool bDeduplicate)
|
2022-11-23 12:18:33 +01:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
m_EntryPath = entryPath;
|
2022-11-23 12:18:33 +01:00
|
|
|
|
m_iPreloadSize = iPreloadSize;
|
|
|
|
|
m_nLoadFlags = nLoadFlags;
|
|
|
|
|
m_nTextureFlags = nTextureFlags;
|
|
|
|
|
m_bUseCompression = bUseCompression;
|
2023-04-03 20:08:36 +02:00
|
|
|
|
m_bDeduplicate = bDeduplicate;
|
2022-11-23 12:18:33 +01:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-02 02:00:35 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
// Purpose: 'VPKEntryBlock_t' file constructor
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// Input : hDirFile -
|
|
|
|
|
// *pEntryPath -
|
2022-06-02 02:00:35 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-05-29 21:41:17 +02:00
|
|
|
|
VPKEntryBlock_t::VPKEntryBlock_t(FileHandle_t hDirFile, const char* pEntryPath)
|
2022-06-02 02:00:35 +02:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
m_EntryPath = pEntryPath; // Set the entry path.
|
|
|
|
|
|
2023-07-05 23:04:07 +02:00
|
|
|
|
m_EntryPath.FixSlashes('/'); // Fix slashes and remove space character representing VPK root.
|
|
|
|
|
m_EntryPath = m_EntryPath.Replace(" /", "");
|
2022-06-02 02:00:35 +02:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
FileSystem()->Read(&m_nFileCRC, sizeof(uint32_t), hDirFile); //
|
|
|
|
|
FileSystem()->Read(&m_iPreloadSize, sizeof(uint16_t), hDirFile); //
|
|
|
|
|
FileSystem()->Read(&m_iPackFileIndex, sizeof(uint16_t), hDirFile); //
|
2022-11-23 12:18:33 +01:00
|
|
|
|
|
|
|
|
|
uint16_t nMarker = 0;
|
2022-11-16 00:54:51 +01:00
|
|
|
|
do // Loop through all chunks in the entry and add to list.
|
2022-06-02 02:00:35 +02:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
VPKChunkDescriptor_t entry(hDirFile);
|
|
|
|
|
m_Fragments.AddToTail(entry);
|
2022-11-23 12:18:33 +01:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
FileSystem()->Read(&nMarker, sizeof(nMarker), hDirFile);
|
2022-11-23 12:18:33 +01:00
|
|
|
|
|
|
|
|
|
} while (nMarker != static_cast<uint16_t>(PACKFILEINDEX_END));
|
2022-06-02 02:00:35 +02:00
|
|
|
|
}
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: 'VPKEntryBlock_t' memory constructor
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// Input : *pData -
|
|
|
|
|
// nLen -
|
|
|
|
|
// nOffset -
|
|
|
|
|
// iPreloadSize -
|
2022-11-16 00:54:51 +01:00
|
|
|
|
// iPackFileIndex -
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// nLoadFlags -
|
|
|
|
|
// nTextureFlags -
|
|
|
|
|
// &pEntryPath -
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-07-08 02:18:18 +02:00
|
|
|
|
VPKEntryBlock_t::VPKEntryBlock_t(const uint8_t* pData, size_t nLen, int64_t nOffset, uint16_t iPreloadSize,
|
2023-05-29 21:41:17 +02:00
|
|
|
|
uint16_t iPackFileIndex, uint32_t nLoadFlags, uint16_t nTextureFlags, const char* pEntryPath)
|
2022-06-02 02:00:35 +02:00
|
|
|
|
{
|
2022-11-23 12:18:33 +01:00
|
|
|
|
m_nFileCRC = crc32::update(NULL, pData, nLen);
|
2022-11-16 00:54:51 +01:00
|
|
|
|
m_iPreloadSize = iPreloadSize;
|
|
|
|
|
m_iPackFileIndex = iPackFileIndex;
|
2023-05-29 21:41:17 +02:00
|
|
|
|
m_EntryPath = pEntryPath;
|
2022-06-02 02:00:35 +02:00
|
|
|
|
|
2023-07-05 23:04:07 +02:00
|
|
|
|
m_EntryPath.FixSlashes('/');
|
|
|
|
|
|
2022-11-23 12:18:33 +01:00
|
|
|
|
size_t nFragmentCount = (nLen + ENTRY_MAX_LEN - 1) / ENTRY_MAX_LEN;
|
|
|
|
|
size_t nFileSize = nLen;
|
2022-06-02 02:00:35 +02:00
|
|
|
|
int64_t nCurrentOffset = nOffset;
|
2022-06-05 17:28:39 +02:00
|
|
|
|
|
2022-11-16 00:54:51 +01:00
|
|
|
|
for (size_t i = 0; i < nFragmentCount; i++) // Fragment data into 1 MiB chunks.
|
2022-06-02 02:00:35 +02:00
|
|
|
|
{
|
2022-11-16 00:54:51 +01:00
|
|
|
|
size_t nSize = std::min<uint64_t>(ENTRY_MAX_LEN, nFileSize);
|
|
|
|
|
nFileSize -= nSize;
|
2023-05-29 21:41:17 +02:00
|
|
|
|
m_Fragments.AddToTail(VPKChunkDescriptor_t(nLoadFlags, nTextureFlags, nCurrentOffset, nSize, nSize));
|
2022-06-02 02:00:35 +02:00
|
|
|
|
nCurrentOffset += nSize;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2022-06-06 14:54:22 +02:00
|
|
|
|
// Purpose: 'VPKChunkDescriptor_t' file constructor
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// Input : hDirFile -
|
2022-06-05 17:28:39 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-05-29 21:41:17 +02:00
|
|
|
|
VPKChunkDescriptor_t::VPKChunkDescriptor_t(FileHandle_t hDirFile)
|
2022-06-05 17:28:39 +02:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
FileSystem()->Read(&m_nLoadFlags, sizeof(uint32_t), hDirFile); //
|
|
|
|
|
FileSystem()->Read(&m_nTextureFlags, sizeof(uint16_t), hDirFile); //
|
|
|
|
|
FileSystem()->Read(&m_nPackFileOffset, sizeof(uint64_t), hDirFile); //
|
|
|
|
|
FileSystem()->Read(&m_nCompressedSize, sizeof(uint64_t), hDirFile); //
|
|
|
|
|
FileSystem()->Read(&m_nUncompressedSize, sizeof(uint64_t), hDirFile); //
|
2022-06-05 17:28:39 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2022-06-06 14:54:22 +02:00
|
|
|
|
// Purpose: 'VPKChunkDescriptor_t' memory constructor
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// Input : nLoadFlags -
|
|
|
|
|
// nTextureFlags -
|
|
|
|
|
// nArchiveOffset -
|
|
|
|
|
// nCompressedSize -
|
2022-06-06 23:08:53 +02:00
|
|
|
|
// nUncompressedSize -
|
Proper VPK repacking
Initial proper implementation pending cleanup.
The new system builds a manifest file when a VPK is unpacked. The manifest files contains data such as the entry flags and texture flags. It also contains a field determining whether the file should be compressed or not.
When a user repacks a pack, the system attempts to load this manifest file and does a lookup to the object to retrieve the flags (most of these flags are unknown, but they are used by the engine and are necessary for stuff like cubemaps and texture files to work correctly. Cubemaps won't work with proper flags, and textures (decals, particle system components, etc..) will look washed out without them.
I think some also determine whether a file within the VPK should be cached or not, so simply marking everything as 0x101 will probably end up in more CPU time and higher filesystem cache usage (depot/ is only 0x1, I don't think anything there is getting cached ever without the 0x100 flag).
User could also repack a VPK while excluding anything that is not in the manifest file. So you could unpack all VPK's into a single directory (each VPK has its own manifest file tied to its level name), and rebuild all the VPK's with only the files that where originally in them.
fs_pack_vpk command usage: <locale> <context> <level_name> <manifest_only>
locale determines the pak language (default english), context determines whether is a server/client vpk, level_name determines the BSP name of the pak, manifest_only determines whether the pack system should only include files within the manifest (leaving this arg out will build all files into the vpk).
The VPK workspace path is determined with ConVar 'fs_packedstore_workspace'.
2022-06-04 01:08:23 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-07-08 02:18:18 +02:00
|
|
|
|
VPKChunkDescriptor_t::VPKChunkDescriptor_t(uint32_t nLoadFlags, uint16_t nTextureFlags,
|
2022-11-23 17:09:48 +01:00
|
|
|
|
uint64_t nPackFileOffset, uint64_t nCompressedSize, uint64_t nUncompressedSize)
|
2022-06-02 02:00:35 +02:00
|
|
|
|
{
|
2022-06-06 14:54:22 +02:00
|
|
|
|
m_nLoadFlags = nLoadFlags;
|
2022-06-02 02:00:35 +02:00
|
|
|
|
m_nTextureFlags = nTextureFlags;
|
2022-11-23 17:09:48 +01:00
|
|
|
|
m_nPackFileOffset = nPackFileOffset;
|
2022-06-02 02:00:35 +02:00
|
|
|
|
|
|
|
|
|
m_nCompressedSize = nCompressedSize;
|
|
|
|
|
m_nUncompressedSize = nUncompressedSize;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-23 17:09:48 +01:00
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: builds a valid file name for the VPK
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// Input : *pLocale -
|
|
|
|
|
// *pTarget -
|
|
|
|
|
// *pLevel -
|
|
|
|
|
// nPatch -
|
2022-11-23 17:09:48 +01:00
|
|
|
|
// Output : a vpk file pair (block and directory file names)
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-05-29 21:41:17 +02:00
|
|
|
|
VPKPair_t::VPKPair_t(const char* pLocale, const char* pTarget, const char* pLevel, int nPatch)
|
2022-11-23 17:09:48 +01:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
bool bFoundLocale = false;
|
2023-04-04 00:41:53 +02:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
for (size_t i = 0; i < SDK_ARRAYSIZE(DIR_LOCALE); i++)
|
2022-11-23 17:09:48 +01:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
if (V_strcmp(pLocale, DIR_LOCALE[i]) == NULL)
|
|
|
|
|
{
|
|
|
|
|
bFoundLocale = true;
|
|
|
|
|
}
|
2022-11-23 17:09:48 +01:00
|
|
|
|
}
|
2023-05-29 21:41:17 +02:00
|
|
|
|
|
|
|
|
|
if (!bFoundLocale)
|
2022-11-23 17:09:48 +01:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
Warning(eDLL_T::FS, "Locale '%s' not supported; using default '%s'\n", pLocale, DIR_LOCALE[0]);
|
|
|
|
|
pLocale = DIR_LOCALE[0];
|
2022-11-23 17:09:48 +01:00
|
|
|
|
}
|
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
bool bFoundTarget = false;
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < SDK_ARRAYSIZE(DIR_TARGET); i++)
|
|
|
|
|
{
|
|
|
|
|
if (V_strcmp(pTarget, DIR_TARGET[i]) == NULL)
|
|
|
|
|
{
|
|
|
|
|
bFoundTarget = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!bFoundTarget)
|
|
|
|
|
{
|
|
|
|
|
Warning(eDLL_T::FS, "Target '%s' not supported; using default '%s'\n", pTarget, DIR_TARGET[0]);
|
|
|
|
|
pTarget = DIR_TARGET[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_PackName.Format("%s_%s.bsp.pak000_%03d.vpk", pTarget, pLevel, nPatch);
|
|
|
|
|
m_DirName.Format("%s%s_%s.bsp.pak000_dir.vpk", pLocale, pTarget, pLevel);
|
2022-11-23 17:09:48 +01:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-02 02:00:35 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: 'VPKDir_t' file constructor
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// Input : &dirFilePath -
|
2022-06-02 02:00:35 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-05-29 21:41:17 +02:00
|
|
|
|
VPKDir_t::VPKDir_t(const CUtlString& dirFilePath)
|
2022-11-23 17:09:48 +01:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
Init(dirFilePath);
|
2022-11-23 17:09:48 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: 'VPKDir_t' file constructor with sanitation
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// Input : &dirFilePath -
|
2022-11-23 17:09:48 +01:00
|
|
|
|
// bSanitizeName - retrieve the directory file name from block name
|
|
|
|
|
// Output : VPKDir_t
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-05-29 21:41:17 +02:00
|
|
|
|
VPKDir_t::VPKDir_t(const CUtlString& dirFilePath, bool bSanitizeName)
|
2022-11-23 17:09:48 +01:00
|
|
|
|
{
|
|
|
|
|
if (!bSanitizeName)
|
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
Init(dirFilePath);
|
2022-11-23 17:09:48 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
std::cmatch regexMatches;
|
2023-07-03 02:27:09 +02:00
|
|
|
|
std::regex_search(dirFilePath.String(), regexMatches, s_BlockFileRegex);
|
2022-11-23 17:09:48 +01:00
|
|
|
|
|
2023-04-05 20:09:23 +02:00
|
|
|
|
if (regexMatches.empty())
|
2022-11-23 17:09:48 +01:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
Init(dirFilePath);
|
2022-11-23 17:09:48 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
CUtlString sanitizedName = dirFilePath;
|
|
|
|
|
sanitizedName = sanitizedName.Replace(regexMatches[0].str().c_str(), "pak000_dir");
|
2022-11-23 17:09:48 +01:00
|
|
|
|
|
|
|
|
|
bool bHasLocale = false;
|
2023-05-29 21:41:17 +02:00
|
|
|
|
|
2023-06-01 00:08:22 +02:00
|
|
|
|
for (size_t i = 0; i < SDK_ARRAYSIZE(DIR_LOCALE); i++)
|
2022-11-23 17:09:48 +01:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
if (sanitizedName.Find(DIR_LOCALE[i]) != -1)
|
2022-11-23 17:09:48 +01:00
|
|
|
|
{
|
|
|
|
|
bHasLocale = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!bHasLocale) // Only sanitize if no locale was provided.
|
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
CUtlString packDirPrefix;
|
|
|
|
|
packDirPrefix.Append(DIR_LOCALE[0]);
|
2022-11-23 17:09:48 +01:00
|
|
|
|
|
2023-06-01 00:08:22 +02:00
|
|
|
|
for (size_t i = 0; i < SDK_ARRAYSIZE(DIR_TARGET); i++)
|
2022-11-23 17:09:48 +01:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
const char* targetName = DIR_TARGET[i];
|
|
|
|
|
|
|
|
|
|
if (sanitizedName.Find(targetName) != -1)
|
2022-11-23 17:09:48 +01:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
packDirPrefix.Append(targetName);
|
|
|
|
|
packDirPrefix = packDirPrefix.Replace(targetName, packDirPrefix);
|
2022-11-23 17:09:48 +01:00
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
Init(sanitizedName);
|
2022-11-23 17:09:48 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: 'VPKDir_t' file constructor
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// Input : &dirFilePath -
|
2022-11-23 17:09:48 +01:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-05-29 21:41:17 +02:00
|
|
|
|
void VPKDir_t::Init(const CUtlString& dirFilePath)
|
2022-06-02 02:00:35 +02:00
|
|
|
|
{
|
2022-11-23 12:18:33 +01:00
|
|
|
|
// Create stream to read from each pack file.
|
2023-05-29 21:41:17 +02:00
|
|
|
|
FileHandle_t hDirFile = FileSystem()->Open(dirFilePath.Get(), "rb", "GAME");
|
|
|
|
|
if (!hDirFile)
|
2022-11-23 12:18:33 +01:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
Error(eDLL_T::FS, NO_ERROR, "%s - Unable to open '%s' (insufficient rights?)\n", __FUNCTION__, dirFilePath.Get());
|
2022-11-23 12:18:33 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
2022-06-02 02:00:35 +02:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
FileSystem()->Read(&m_Header.m_nHeaderMarker, sizeof(uint32_t), hDirFile);
|
|
|
|
|
FileSystem()->Read(&m_Header.m_nMajorVersion, sizeof(uint16_t), hDirFile); //
|
|
|
|
|
FileSystem()->Read(&m_Header.m_nMinorVersion, sizeof(uint16_t), hDirFile); //
|
|
|
|
|
FileSystem()->Read(&m_Header.m_nDirectorySize, sizeof(uint32_t), hDirFile); //
|
|
|
|
|
FileSystem()->Read(&m_Header.m_nSignatureSize, sizeof(uint32_t), hDirFile); //
|
|
|
|
|
|
|
|
|
|
g_pPackedStore->GetEntryBlocks(m_EntryBlocks, hDirFile);
|
|
|
|
|
m_DirFilePath = dirFilePath; // Set path to vpk directory file.
|
2022-06-02 02:00:35 +02:00
|
|
|
|
|
2023-07-06 00:49:17 +02:00
|
|
|
|
// Obtain every referenced pack file from the directory tree.
|
2023-07-04 22:08:44 +02:00
|
|
|
|
FOR_EACH_VEC(m_EntryBlocks, i)
|
2022-06-02 02:00:35 +02:00
|
|
|
|
{
|
2023-07-04 22:08:44 +02:00
|
|
|
|
const VPKEntryBlock_t& entryBlock = m_EntryBlocks[i];
|
2023-07-06 00:49:17 +02:00
|
|
|
|
m_PakFileIndices.insert(entryBlock.m_iPackFileIndex);
|
2022-11-23 17:09:48 +01:00
|
|
|
|
}
|
2023-04-03 22:48:29 +02:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
FileSystem()->Close(hDirFile);
|
2022-11-23 17:09:48 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-07-06 00:49:17 +02:00
|
|
|
|
// Purpose: formats pack file path for specified patch
|
|
|
|
|
// Input : iPackFileIndex - (patch)
|
2022-11-23 17:09:48 +01:00
|
|
|
|
// output : string
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-07-06 00:49:17 +02:00
|
|
|
|
CUtlString VPKDir_t::GetPackFileNameForIndex(uint16_t iPackFileIndex) const
|
2022-11-23 17:09:48 +01:00
|
|
|
|
{
|
2023-07-06 00:49:17 +02:00
|
|
|
|
CUtlString packChunkName = StripLocalePrefix(m_DirFilePath);
|
2023-05-29 21:41:17 +02:00
|
|
|
|
CUtlString packChunkIndex;
|
2022-11-23 17:09:48 +01:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
packChunkIndex.Format("pak000_%03d", iPackFileIndex);
|
|
|
|
|
packChunkName = packChunkName.Replace("pak000_dir", packChunkIndex.Get());
|
|
|
|
|
|
|
|
|
|
return packChunkName;
|
2022-11-23 17:09:48 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: strips locale prefix from file path
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// Input : &directoryPath -
|
2022-11-23 17:09:48 +01:00
|
|
|
|
// Output : directory filename without locale prefix
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-05-29 21:41:17 +02:00
|
|
|
|
CUtlString VPKDir_t::StripLocalePrefix(const CUtlString& directoryPath) const
|
2022-11-23 17:09:48 +01:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
CUtlString fileName = directoryPath.UnqualifiedFilename();
|
2022-11-23 17:09:48 +01:00
|
|
|
|
|
2023-06-01 00:08:22 +02:00
|
|
|
|
for (size_t i = 0; i < SDK_ARRAYSIZE(DIR_LOCALE); i++)
|
2022-11-23 17:09:48 +01:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
fileName = fileName.Replace(DIR_LOCALE[i], "");
|
2022-06-02 02:00:35 +02:00
|
|
|
|
}
|
2023-05-29 21:41:17 +02:00
|
|
|
|
|
|
|
|
|
return fileName;
|
2022-06-02 02:00:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2022-11-23 12:27:57 +01:00
|
|
|
|
// Purpose: writes the vpk directory header
|
2022-11-23 17:09:48 +01:00
|
|
|
|
// Input : hDirectoryFile -
|
2022-06-02 02:00:35 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2022-11-23 17:09:48 +01:00
|
|
|
|
void VPKDir_t::WriteHeader(FileHandle_t hDirectoryFile) const
|
2022-06-02 02:00:35 +02:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
FileSystem()->Write(&m_Header.m_nHeaderMarker, sizeof(uint32_t), hDirectoryFile);
|
|
|
|
|
FileSystem()->Write(&m_Header.m_nMajorVersion, sizeof(uint16_t), hDirectoryFile);
|
|
|
|
|
FileSystem()->Write(&m_Header.m_nMinorVersion, sizeof(uint16_t), hDirectoryFile);
|
|
|
|
|
FileSystem()->Write(&m_Header.m_nDirectorySize, sizeof(uint32_t), hDirectoryFile);
|
|
|
|
|
FileSystem()->Write(&m_Header.m_nSignatureSize, sizeof(uint32_t), hDirectoryFile);
|
2022-11-23 12:27:57 +01:00
|
|
|
|
}
|
2022-06-02 02:00:35 +02:00
|
|
|
|
|
2022-11-23 12:27:57 +01:00
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: writes the directory tree size
|
2022-11-23 17:09:48 +01:00
|
|
|
|
// Input : hDirectoryFile -
|
2022-11-23 12:27:57 +01:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2022-11-23 17:09:48 +01:00
|
|
|
|
void VPKDir_t::WriteTreeSize(FileHandle_t hDirectoryFile) const
|
2022-11-23 12:27:57 +01:00
|
|
|
|
{
|
2023-05-29 21:41:17 +02:00
|
|
|
|
FileSystem()->Seek(hDirectoryFile, offsetof(VPKDir_t, m_Header.m_nDirectorySize), FileSystemSeek_t::FILESYSTEM_SEEK_HEAD);
|
|
|
|
|
FileSystem()->Write(&m_Header.m_nDirectorySize, sizeof(uint32_t), hDirectoryFile);
|
2022-11-23 17:09:48 +01:00
|
|
|
|
FileSystem()->Write(&PACKFILEINDEX_SEP, sizeof(uint32_t), hDirectoryFile);
|
2022-11-23 12:27:57 +01:00
|
|
|
|
}
|
2022-06-02 02:00:35 +02:00
|
|
|
|
|
2022-11-23 12:27:57 +01:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-07-05 23:04:07 +02:00
|
|
|
|
// Purpose: builds the vpk directory tree
|
|
|
|
|
// Input : &entryBlocks -
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void VPKDir_t::CTreeBuilder::BuildTree(const CUtlVector<VPKEntryBlock_t>& entryBlocks)
|
|
|
|
|
{
|
|
|
|
|
FOR_EACH_VEC(entryBlocks, i)
|
|
|
|
|
{
|
|
|
|
|
const VPKEntryBlock_t& entryBlock = entryBlocks[i];
|
|
|
|
|
|
|
|
|
|
CUtlString fileExt = entryBlock.m_EntryPath.GetExtension();
|
|
|
|
|
CUtlString filePath = entryBlock.m_EntryPath.DirName();
|
|
|
|
|
|
|
|
|
|
if (!filePath.IsEmpty() && filePath[0] == '.')
|
|
|
|
|
{
|
|
|
|
|
// Has to be padded with a space character if empty [root].
|
|
|
|
|
filePath = " ";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**********************************************************************
|
|
|
|
|
* The code below creates a directory tree structure as follows:
|
|
|
|
|
*
|
|
|
|
|
* Extension0
|
|
|
|
|
* |
|
2023-07-10 01:38:29 +02:00
|
|
|
|
* |___ Path0
|
2023-07-05 23:04:07 +02:00
|
|
|
|
* | |
|
2023-07-10 01:38:29 +02:00
|
|
|
|
* | |___ File0
|
|
|
|
|
* | |___ File1
|
|
|
|
|
* | |___ File2
|
2023-07-05 23:04:07 +02:00
|
|
|
|
* |
|
2023-07-10 01:38:29 +02:00
|
|
|
|
* |___ Path1
|
2023-07-05 23:04:07 +02:00
|
|
|
|
* |
|
2023-07-10 01:38:29 +02:00
|
|
|
|
* |___ File0
|
|
|
|
|
* |___ File1
|
|
|
|
|
* |___ File2
|
2023-07-05 23:04:07 +02:00
|
|
|
|
* ...
|
|
|
|
|
*
|
|
|
|
|
* A tree scope cannot contain duplicate elements,
|
|
|
|
|
* which ultimately means that:
|
|
|
|
|
*
|
|
|
|
|
* - An extension is only written once to the tree.
|
|
|
|
|
* - A file path is only written once per extension tree.
|
|
|
|
|
* - A file name is only written once per file path tree.
|
|
|
|
|
**********************************************************************/
|
|
|
|
|
const char* pFileExt = fileExt.Get();
|
|
|
|
|
auto extIt = m_FileTree.find(pFileExt);
|
|
|
|
|
|
|
|
|
|
if (extIt == m_FileTree.end())
|
|
|
|
|
{
|
|
|
|
|
extIt = m_FileTree.insert({ pFileExt, PathContainer_t() }).first;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PathContainer_t& pathTree = extIt->second;
|
|
|
|
|
|
|
|
|
|
const char* pFilePath = filePath.Get();
|
|
|
|
|
auto pathIt = pathTree.find(pFilePath);
|
|
|
|
|
|
|
|
|
|
if (pathIt == pathTree.end())
|
|
|
|
|
{
|
|
|
|
|
pathIt = pathTree.insert({ pFilePath, std::list<VPKEntryBlock_t>() }).first;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pathIt->second.push_back(entryBlock);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: writes the vpk directory tree
|
2022-11-23 17:09:48 +01:00
|
|
|
|
// Input : hDirectoryFile -
|
2022-11-23 12:27:57 +01:00
|
|
|
|
// Output : number of descriptors written
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-07-15 16:25:35 +02:00
|
|
|
|
int VPKDir_t::CTreeBuilder::WriteTree(FileHandle_t hDirectoryFile) const
|
2022-11-23 12:27:57 +01:00
|
|
|
|
{
|
2023-07-15 16:25:35 +02:00
|
|
|
|
int nDescriptors = NULL;
|
2022-06-02 02:00:35 +02:00
|
|
|
|
|
2023-07-05 23:04:07 +02:00
|
|
|
|
for (auto& iKeyValue : m_FileTree)
|
2022-06-02 02:00:35 +02:00
|
|
|
|
{
|
2023-08-09 14:43:54 +02:00
|
|
|
|
FileSystem()->Write(iKeyValue.first.c_str(), iKeyValue.first.length() + 1, hDirectoryFile);
|
2022-06-02 02:00:35 +02:00
|
|
|
|
for (auto& jKeyValue : iKeyValue.second)
|
|
|
|
|
{
|
2023-08-09 14:43:54 +02:00
|
|
|
|
FileSystem()->Write(jKeyValue.first.c_str(), jKeyValue.first.length() + 1, hDirectoryFile);
|
2022-06-05 17:28:39 +02:00
|
|
|
|
for (auto& vEntry : jKeyValue.second)
|
2022-06-02 02:00:35 +02:00
|
|
|
|
{
|
2023-08-09 14:43:54 +02:00
|
|
|
|
const CUtlString entryPath = vEntry.m_EntryPath.UnqualifiedFilename().StripExtension();
|
|
|
|
|
FileSystem()->Write(entryPath.Get(), entryPath.Length() + 1, hDirectoryFile);
|
2022-11-23 12:18:33 +01:00
|
|
|
|
|
2022-11-23 17:09:48 +01:00
|
|
|
|
FileSystem()->Write(&vEntry.m_nFileCRC, sizeof(uint32_t), hDirectoryFile);
|
|
|
|
|
FileSystem()->Write(&vEntry.m_iPreloadSize, sizeof(uint16_t), hDirectoryFile);
|
|
|
|
|
FileSystem()->Write(&vEntry.m_iPackFileIndex, sizeof(uint16_t), hDirectoryFile);
|
2022-06-02 02:00:35 +02:00
|
|
|
|
|
2023-06-26 22:34:24 +02:00
|
|
|
|
FOR_EACH_VEC(vEntry.m_Fragments, i)
|
2022-10-09 12:08:54 +02:00
|
|
|
|
{
|
|
|
|
|
/*Write chunk descriptor*/
|
2023-07-04 22:08:44 +02:00
|
|
|
|
const VPKChunkDescriptor_t& descriptor = vEntry.m_Fragments[i];
|
2022-06-02 02:00:35 +02:00
|
|
|
|
|
2023-07-04 22:08:44 +02:00
|
|
|
|
FileSystem()->Write(&descriptor.m_nLoadFlags, sizeof(uint32_t), hDirectoryFile);
|
|
|
|
|
FileSystem()->Write(&descriptor.m_nTextureFlags, sizeof(uint16_t), hDirectoryFile);
|
|
|
|
|
FileSystem()->Write(&descriptor.m_nPackFileOffset, sizeof(uint64_t), hDirectoryFile);
|
|
|
|
|
FileSystem()->Write(&descriptor.m_nCompressedSize, sizeof(uint64_t), hDirectoryFile);
|
|
|
|
|
FileSystem()->Write(&descriptor.m_nUncompressedSize, sizeof(uint64_t), hDirectoryFile);
|
2022-10-09 12:08:54 +02:00
|
|
|
|
|
2023-06-26 22:34:24 +02:00
|
|
|
|
if (i != (vEntry.m_Fragments.Count() - 1))
|
2022-10-09 12:08:54 +02:00
|
|
|
|
{
|
2022-11-23 17:09:48 +01:00
|
|
|
|
FileSystem()->Write(&PACKFILEINDEX_SEP, sizeof(uint16_t), hDirectoryFile);
|
2022-10-09 12:08:54 +02:00
|
|
|
|
}
|
2022-11-16 00:54:51 +01:00
|
|
|
|
else // Mark end of entry.
|
2022-10-09 12:08:54 +02:00
|
|
|
|
{
|
2022-11-23 17:09:48 +01:00
|
|
|
|
FileSystem()->Write(&PACKFILEINDEX_END, sizeof(uint16_t), hDirectoryFile);
|
2022-06-02 02:00:35 +02:00
|
|
|
|
}
|
2022-10-09 12:08:54 +02:00
|
|
|
|
nDescriptors++;
|
2022-06-02 02:00:35 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-23 17:09:48 +01:00
|
|
|
|
FileSystem()->Write(&PACKFILEINDEX_SEP, sizeof(uint8_t), hDirectoryFile);
|
2022-11-23 12:27:57 +01:00
|
|
|
|
}
|
2022-11-23 17:09:48 +01:00
|
|
|
|
FileSystem()->Write(&PACKFILEINDEX_SEP, sizeof(uint8_t), hDirectoryFile);
|
2022-11-23 12:27:57 +01:00
|
|
|
|
}
|
2022-11-23 17:09:48 +01:00
|
|
|
|
FileSystem()->Write(&PACKFILEINDEX_SEP, sizeof(uint8_t), hDirectoryFile);
|
2022-11-23 12:27:57 +01:00
|
|
|
|
|
|
|
|
|
return nDescriptors;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: builds the vpk directory file
|
2022-11-23 17:09:48 +01:00
|
|
|
|
// Input : &svDirectoryPath -
|
2023-05-29 21:41:17 +02:00
|
|
|
|
// &vEntryBlocks -
|
2022-11-23 12:27:57 +01:00
|
|
|
|
//-----------------------------------------------------------------------------
|
2023-07-04 22:08:44 +02:00
|
|
|
|
void VPKDir_t::BuildDirectoryFile(const CUtlString& directoryPath, const CUtlVector<VPKEntryBlock_t>& entryBlocks)
|
2022-11-23 12:27:57 +01:00
|
|
|
|
{
|
2023-07-08 02:18:18 +02:00
|
|
|
|
const char* pDirectoryFile = directoryPath.Get();
|
|
|
|
|
|
|
|
|
|
FileHandle_t hDirectoryFile = FileSystem()->Open(pDirectoryFile, "wb", "GAME");
|
2022-11-23 17:09:48 +01:00
|
|
|
|
if (!hDirectoryFile)
|
2022-11-23 12:27:57 +01:00
|
|
|
|
{
|
2023-07-08 02:18:18 +02:00
|
|
|
|
Error(eDLL_T::FS, NO_ERROR, "%s - Unable to write to '%s' (read-only?)\n", __FUNCTION__, pDirectoryFile);
|
2022-11-23 12:27:57 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
2022-06-02 02:00:35 +02:00
|
|
|
|
|
2023-07-05 23:04:07 +02:00
|
|
|
|
CTreeBuilder treeBuilder;
|
|
|
|
|
treeBuilder.BuildTree(entryBlocks);
|
2022-11-23 12:27:57 +01:00
|
|
|
|
|
2022-11-23 17:09:48 +01:00
|
|
|
|
WriteHeader(hDirectoryFile);
|
2023-07-15 16:25:35 +02:00
|
|
|
|
int nDescriptors = treeBuilder.WriteTree(hDirectoryFile);
|
2022-06-05 17:28:39 +02:00
|
|
|
|
|
2023-05-29 21:41:17 +02:00
|
|
|
|
m_Header.m_nDirectorySize = static_cast<uint32_t>(FileSystem()->Tell(hDirectoryFile) - sizeof(VPKDirHeader_t));
|
2022-11-23 17:09:48 +01:00
|
|
|
|
WriteTreeSize(hDirectoryFile);
|
2022-11-23 12:27:57 +01:00
|
|
|
|
|
2022-11-23 17:09:48 +01:00
|
|
|
|
FileSystem()->Close(hDirectoryFile);
|
2023-08-09 14:43:54 +02:00
|
|
|
|
DevMsg(eDLL_T::FS, "*** Build directory totaling '%zu' bytes with '%i' entries and '%i' descriptors\n",
|
2023-07-04 22:08:44 +02:00
|
|
|
|
size_t(sizeof(VPKDirHeader_t) + m_Header.m_nDirectorySize), entryBlocks.Count(), nDescriptors);
|
2022-06-02 02:00:35 +02:00
|
|
|
|
}
|
2022-11-16 00:54:51 +01:00
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Singleton
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2022-06-02 01:59:03 +02:00
|
|
|
|
CPackedStore* g_pPackedStore = new CPackedStore();
|