r5sdk/r5dev/rtech/pak/pakstream.cpp

153 lines
6.1 KiB
C++
Raw Normal View History

//=============================================================================//
//
// Purpose: pak stream data loading and unloading
//
//=============================================================================//
#include "tier0/commandline.h"
#include "rtech/async/asyncio.h"
#include "rtech/ipakfile.h"
#include "pakstream.h"
#include "pakparse.h"
//-----------------------------------------------------------------------------
// determines whether or not to emulate the streaming install, this basically
// tells the system to not load streaming sets at all. note that this only
// applies to optional streaming sets, mandatory ones must be available!
//-----------------------------------------------------------------------------
static bool Pak_ShouldEmulateStreamingInstall()
{
static bool initialized = false;
static bool shouldEmulate = false;
// don't run the command line check every query
if (initialized)
return shouldEmulate;
const char* value = nullptr;
if (CommandLine()->CheckParm("-emulate_streaming_install", &value))
{
if (value && atoi(value))
shouldEmulate = true;
}
initialized = true;
return shouldEmulate;
}
//-----------------------------------------------------------------------------
// returns whether the optional streaming sets are finished downloading
//-----------------------------------------------------------------------------
static bool Pak_OptionalStreamingDataDownloaded()
{
return (!Pak_ShouldEmulateStreamingInstall() && Pak_StreamingDownloadFinished());
}
//-----------------------------------------------------------------------------
// opens all associated streaming assets for this pak
//-----------------------------------------------------------------------------
void Pak_OpenAssociatedStreamingFiles(PakLoadedInfo_t* const loadedInfo, PakLoadedInfo_t::StreamingInfo_t& streamInfo,
const uint16_t fileNamesBufSize, const EPakStreamSet set)
{
assert(set < STREAMING_SET_COUNT);
const PakMemoryData_t& memoryData = loadedInfo->pakFile->memoryData;
uint16_t numStreamFiles = 0;
// load all streaming sets
for (uint64_t lenRead = 0; lenRead < fileNamesBufSize && numStreamFiles < PAK_MAX_STREAMING_FILE_HANDLES_PER_SET;)
{
// read streaming path and advance buffer
const char* const streamingFilePath = &memoryData.streamingFilePaths[set][lenRead];
// check if we processed all strings, the buffer is aligned to 4 bytes,
// so its possible we reach padding before the end of the buffer
if (!*streamingFilePath)
break;
// must advance over null character as well for the next read
lenRead += strnlen(streamingFilePath, fileNamesBufSize - lenRead) + 1;
const int fileNumber = FS_OpenAsyncFile(streamingFilePath, loadedInfo->logLevel, nullptr);
// make sure we successfully loaded mandatory streaming files, as we
// would otherwise error in the game itself
if (set == STREAMING_SET_MANDATORY && fileNumber == -1)
Error(eDLL_T::RTECH, EXIT_FAILURE, "Error opening streaming file '%s'\n", streamingFilePath);
streamInfo.streamFileNumber[numStreamFiles++] = fileNumber;
}
streamInfo.streamFileCount = numStreamFiles;
}
//-----------------------------------------------------------------------------
// allocates the pak string to be used for embedded streaming data
//-----------------------------------------------------------------------------
void Pak_EnableEmbeddedStreamingData(PakLoadedInfo_t* const loadedInfo, PakLoadedInfo_t::StreamingInfo_t& streamInfo)
{
const char* const baseName = V_UnqualifiedFileName(loadedInfo->fileName);
const size_t baseNameLen = strlen(baseName);
// if the path isn't specified, we have to prepend one
const bool hasPath = (baseName != loadedInfo->fileName);
const size_t basePathLen = hasPath ? 0 : strlen(PAK_BASE_PATH);
const size_t totalBufLen = basePathLen + baseNameLen + 1;
char* const embeddedName = reinterpret_cast<char* const>(loadedInfo->allocator->Alloc(totalBufLen, sizeof(char)));
assert(embeddedName);
// copy the base path if none was found in the file name
if (!hasPath)
memcpy(embeddedName, PAK_BASE_PATH, basePathLen);
memcpy(embeddedName + basePathLen, baseName, baseNameLen);
// at this point we shouldn't have read loose streaming data, we only
// should be looking for embedded if there are no external ones !!!
assert(streamInfo.streamFileCount == 0);
streamInfo.streamFileCount = 1;
}
//-----------------------------------------------------------------------------
// parse and open all streaming files
//-----------------------------------------------------------------------------
void Pak_LoadStreamingData(PakLoadedInfo_t* const loadedInfo)
{
const PakFileHeader_t& pakHeader = loadedInfo->pakFile->GetHeader();
for (int i = 0; i < STREAMING_SET_COUNT; i++)
{
PakLoadedInfo_t::StreamingInfo_t& streamInfo = loadedInfo->streamInfo[i];
streamInfo.Reset();
const bool optional = (i == STREAMING_SET_OPTIONAL);
// NOTE: mandatory streaming data must be available at this point!
const bool disableStreaming = optional ? !Pak_OptionalStreamingDataDownloaded() : false;
streamInfo.streamingDisabled = disableStreaming;
// don't attempt to open the streaming file if it isn't downloaded yet
if (disableStreaming)
continue;
const uint16_t filesBufLen = pakHeader.streamingFilesBufSize[i];
const uint64_t embeddedStreamingDataSize = pakHeader.embeddedStreamingDataSize[i];
if (filesBufLen > 0)
{
// embedded streaming data won't be loaded if the pak is linked to
// external streaming files; mistake while building the pak?
assert(!embeddedStreamingDataSize);
Pak_OpenAssociatedStreamingFiles(loadedInfo, streamInfo, filesBufLen, EPakStreamSet(i));
}
else if (embeddedStreamingDataSize > 0)
{
Pak_EnableEmbeddedStreamingData(loadedInfo, streamInfo);
}
}
}