mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
283 lines
9.1 KiB
C++
283 lines
9.1 KiB
C++
//=============================================================================//
|
|
//
|
|
// Purpose: Standalone VPK tool
|
|
//
|
|
//=============================================================================//
|
|
#include "core/logdef.h"
|
|
#include "core/logger.h"
|
|
#include "tier0/fasttimer.h"
|
|
#include "tier0/cpu.h"
|
|
#include "tier1/cmd.h"
|
|
#include "tier1/fmtstr.h"
|
|
#include "tier1/keyvalues.h"
|
|
#include "windows/console.h"
|
|
#include "vpklib/packedstore.h"
|
|
|
|
#include "vstdlib/keyvaluessystem.h"
|
|
#include "filesystem/filesystem_std.h"
|
|
|
|
#define PACK_COMMAND "pack"
|
|
#define UNPACK_COMMAND "unpack"
|
|
|
|
#define PACK_LOG_DIR "manifest/pack_logs/"
|
|
#define UNPACK_LOG_DIR "manifest/unpack_logs/"
|
|
|
|
#define FRONTEND_ENABLE_FILE "enable.txt"
|
|
|
|
static CKeyValuesSystem s_KeyValuesSystem;
|
|
static CFileSystem_Stdio g_FullFileSystem;
|
|
static bool s_bUseAnsiColors = true;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: keyvalues singleton accessor
|
|
//-----------------------------------------------------------------------------
|
|
IKeyValuesSystem* KeyValuesSystem()
|
|
{
|
|
return &s_KeyValuesSystem;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: filesystem singleton accessor
|
|
//-----------------------------------------------------------------------------
|
|
CFileSystem_Stdio* FileSystem()
|
|
{
|
|
return &g_FullFileSystem;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: init
|
|
//-----------------------------------------------------------------------------
|
|
static void ReVPK_Init()
|
|
{
|
|
CheckCPUforSSE2();
|
|
|
|
g_CoreMsgVCallback = EngineLoggerSink;
|
|
lzham_enable_fail_exceptions(true);
|
|
|
|
Console_Init(s_bUseAnsiColors);
|
|
SpdLog_Init(s_bUseAnsiColors);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: shutdown
|
|
//-----------------------------------------------------------------------------
|
|
static void ReVPK_Shutdown()
|
|
{
|
|
// Must be done to flush all buffers.
|
|
SpdLog_Shutdown();
|
|
Console_Shutdown();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: logs tool's usage
|
|
//-----------------------------------------------------------------------------
|
|
static void ReVPK_Usage()
|
|
{
|
|
CFmtStr1024 usage;
|
|
|
|
usage.Format(
|
|
"ReVPK instructions and options:\n"
|
|
"For packing; run 'revpk %s' with the following parameters:\n"
|
|
"\t<%s>\t- locale prefix for the directory tree file\n"
|
|
"\t<%s>\t- context scope for the VPK files [\"server\", \"client\"]\n"
|
|
"\t<%s>\t- level name for the VPK files\n"
|
|
"\t<%s>\t- ( optional ) path to the workspace containing the control file\n"
|
|
"\t<%s>\t- ( optional ) path in which the VPK files will be built\n"
|
|
"\t<%s>\t- ( optional ) max LZHAM helper threads [\"%d\", \"%d\"] \"%d\" ( default ) for max practical\n"
|
|
"\t<%s>\t- ( optional ) the level of compression [\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"]\n\n"
|
|
|
|
"For unpacking; run 'revpk %s' with the following parameters:\n"
|
|
"\t<%s>\t- name of the target directory tree file\n"
|
|
"\t<%s>\t- ( optional ) path to directory containing the target directory tree file\n"
|
|
"\t<%s>\t- ( optional ) whether to parse the directory tree file name from the data block file name\n",
|
|
|
|
PACK_COMMAND, // Pack parameters:
|
|
"locale", "context", "levelName", "workspacePath", "buildPath",
|
|
|
|
"numThreads", // Num helper threads.
|
|
-1, LZHAM_MAX_HELPER_THREADS, -1,
|
|
|
|
"compressLevel", // Compress level.
|
|
"fastest", "faster", "default", "better", "uber",
|
|
|
|
UNPACK_COMMAND,// Unpack parameters:
|
|
"fileName", "inputDir", "sanitize"
|
|
);
|
|
|
|
Warning(eDLL_T::FS, "%s", usage.Get());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: writes the VPK front-end enable file
|
|
//-----------------------------------------------------------------------------
|
|
static void ReVPK_WriteEnableFile(const char* containingPath)
|
|
{
|
|
CFmtStr1024 textFileName;
|
|
textFileName.Format("%s%s", containingPath, FRONTEND_ENABLE_FILE);
|
|
|
|
if (FileSystem()->FileExists(textFileName.String(), "PLATFORM"))
|
|
{
|
|
// Already exists.
|
|
return;
|
|
}
|
|
|
|
FileHandle_t enableTxt = FileSystem()->Open(textFileName.String(), "wb", "PLATFORM");
|
|
|
|
if (enableTxt)
|
|
{
|
|
const char* textData = "1 \r\n";
|
|
FileSystem()->Write(textData, strlen(textData), enableTxt);
|
|
FileSystem()->Close(enableTxt);
|
|
}
|
|
else
|
|
{
|
|
Error(eDLL_T::FS, NO_ERROR, "Failed to write front-end enable file \"%s\"; insufficient rights?\n",
|
|
textFileName.String());
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: packs VPK files into 'SHIP' VPK directory
|
|
//-----------------------------------------------------------------------------
|
|
static void ReVPK_Pack(const CCommand& args)
|
|
{
|
|
const int argCount = args.ArgC();
|
|
|
|
if (argCount < 5)
|
|
{
|
|
ReVPK_Usage();
|
|
return;
|
|
}
|
|
|
|
CFmtStr1024 workspacePath = argCount > 5 ? args.Arg(5) : "ship/";
|
|
CFmtStr1024 buildPath = argCount > 6 ? args.Arg(6) : "vpk/";
|
|
|
|
// Make sure there's always a trailing slash.
|
|
V_AppendSlash(workspacePath.Access(), workspacePath.GetMaxLength(), '/');
|
|
V_AppendSlash(buildPath.Access(), buildPath.GetMaxLength(), '/');
|
|
|
|
if (!FileSystem()->IsDirectory(workspacePath, "PLATFORM"))
|
|
{
|
|
Error(eDLL_T::FS, NO_ERROR, "Workspace path \"%s\" doesn't exist!\n", workspacePath.String());
|
|
return;
|
|
}
|
|
|
|
const char* localeName = args.Arg(2);
|
|
const char* contextName = args.Arg(3);
|
|
const char* levelName = args.Arg(4);
|
|
|
|
// For clients, we need an enable file which the engine uses to determine
|
|
// whether or not to mount the front-end VPK file.
|
|
if (V_strcmp(contextName, DIR_TARGET[EPackedStoreTargets::STORE_TARGET_CLIENT]) == NULL)
|
|
{
|
|
ReVPK_WriteEnableFile(buildPath);
|
|
}
|
|
|
|
// Write the pack log to a file.
|
|
CFmtStr1024 textFileName("%s%s%s%s_%s.log", buildPath.String(), PACK_LOG_DIR, localeName, contextName, levelName);
|
|
SpdLog_InstallSupplementalLogger("supplemental_logger_mt", textFileName.String());
|
|
|
|
VPKPair_t pair(localeName, contextName, levelName, NULL);
|
|
Msg(eDLL_T::FS, "*** Starting VPK build command for: '%s'\n", pair.m_DirName.Get());
|
|
|
|
CFastTimer timer;
|
|
timer.Start();
|
|
|
|
CPackedStoreBuilder builder;
|
|
|
|
builder.InitLzEncoder(
|
|
argCount > 7 ? (std::min)(atoi(args.Arg(7)), LZHAM_MAX_HELPER_THREADS) : -1, // Num threads.
|
|
argCount > 8 ? args.Arg(8) : "default"); // Compress level.
|
|
|
|
builder.PackStore(pair, workspacePath.String(), buildPath.String());
|
|
|
|
timer.End();
|
|
Msg(eDLL_T::FS, "*** Time elapsed: '%lf' seconds\n", timer.GetDuration().GetSeconds());
|
|
Msg(eDLL_T::FS, "\n");
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: unpacks VPK files into workspace directory
|
|
//-----------------------------------------------------------------------------
|
|
static void ReVPK_Unpack(const CCommand& args)
|
|
{
|
|
const int argCount = args.ArgC();
|
|
|
|
if (argCount < 3)
|
|
{
|
|
ReVPK_Usage();
|
|
return;
|
|
}
|
|
|
|
const char* fileName = args.Arg(2);
|
|
const char* outPath = argCount > 3 ? args.Arg(3) : "ship/";
|
|
const bool sanitize = argCount > 4 ? atoi(args.Arg(4)) != NULL : false;
|
|
|
|
VPKDir_t vpk(fileName, sanitize);
|
|
|
|
// Make sure the VPK directory tree was actually parsed correctly.
|
|
if (vpk.Failed())
|
|
{
|
|
Error(eDLL_T::FS, NO_ERROR, "Failed to parse directory tree file \"%s\"!\n", fileName);
|
|
return;
|
|
}
|
|
|
|
CUtlString baseName = PackedStore_GetDirBaseName(vpk.m_DirFilePath);
|
|
|
|
// Write the unpack log to a file.
|
|
CFmtStr1024 textFileName("%s%s%s.log", outPath, UNPACK_LOG_DIR, baseName.String());
|
|
SpdLog_InstallSupplementalLogger("supplemental_logger_mt", textFileName.String());
|
|
|
|
const char* actualDirFile = vpk.m_DirFilePath.String();
|
|
Msg(eDLL_T::FS, "*** Starting VPK extraction command for: '%s'\n", actualDirFile);
|
|
|
|
CFastTimer timer;
|
|
timer.Start();
|
|
|
|
CPackedStoreBuilder builder;
|
|
|
|
builder.InitLzDecoder();
|
|
builder.UnpackStore(vpk, argCount > 3 ? args.Arg(3) : "ship/");
|
|
|
|
timer.End();
|
|
Msg(eDLL_T::FS, "*** Time elapsed: '%lf' seconds\n", timer.GetDuration().GetSeconds());
|
|
Msg(eDLL_T::FS, "\n");
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
int main(int argc, char* argv[])
|
|
{
|
|
ReVPK_Init();
|
|
|
|
CCommand args;
|
|
CUtlString str;
|
|
|
|
for (int i = 0; i < argc; i++)
|
|
{
|
|
str.Append(argv[i]);
|
|
str.Append(' ');
|
|
}
|
|
|
|
args.Tokenize(str.Get(), cmd_source_t::kCommandSrcCode);
|
|
|
|
if (!args.ArgC()) {
|
|
ReVPK_Usage();
|
|
}
|
|
else
|
|
{
|
|
if (V_strcmp(args.Arg(1), PACK_COMMAND) == NULL) {
|
|
ReVPK_Pack(args);
|
|
}
|
|
else if (V_strcmp(args.Arg(1), UNPACK_COMMAND) == NULL) {
|
|
ReVPK_Unpack(args);
|
|
}
|
|
else {
|
|
ReVPK_Usage();
|
|
}
|
|
}
|
|
|
|
ReVPK_Shutdown();
|
|
}
|