mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
373 lines
8.7 KiB
C++
373 lines
8.7 KiB
C++
//=============================================================================//
|
|
//
|
|
// Purpose: FileSystem class utilizing standard libraries.
|
|
// ----------------------------------------------------------------------------
|
|
// NOTE: use this for standalone/tools projects
|
|
//=============================================================================//
|
|
#include "filesystem_std.h"
|
|
|
|
// These are used for the 'stat()' and 'access()' in CBaseFileSystem::IsDirectory().
|
|
#include <io.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
ssize_t CBaseFileSystem::Read(void* pOutput, ssize_t size, FileHandle_t file)
|
|
{
|
|
return fread(pOutput, sizeof(uint8_t), size, (FILE*)file);
|
|
}
|
|
|
|
ssize_t CBaseFileSystem::Write(void const* pInput, ssize_t size, FileHandle_t file)
|
|
{
|
|
return fwrite(pInput, sizeof(uint8_t), size, (FILE*)file);
|
|
}
|
|
|
|
FileHandle_t CBaseFileSystem::Open(const char* pFileName, const char* pOptions, const char* pPathID, int64_t unknown)
|
|
{
|
|
NOTE_UNUSED(unknown);
|
|
|
|
char fullPath[1024];
|
|
snprintf(fullPath, sizeof(fullPath), "%s", pFileName);
|
|
|
|
V_FixSlashes(fullPath);
|
|
|
|
return (FileHandle_t)fopen(fullPath, pOptions);
|
|
}
|
|
|
|
void CBaseFileSystem::Close(FileHandle_t file)
|
|
{
|
|
fclose((FILE*)file);
|
|
}
|
|
|
|
void CBaseFileSystem::Seek(FileHandle_t file, ssize_t pos, FileSystemSeek_t seekType)
|
|
{
|
|
fseek((FILE*)file, (long)pos, seekType);
|
|
}
|
|
|
|
ptrdiff_t CBaseFileSystem::Tell(FileHandle_t file)
|
|
{
|
|
return ftell((FILE*)file);
|
|
}
|
|
|
|
ssize_t CBaseFileSystem::FSize(const char* pFileName, const char* pPathID)
|
|
{
|
|
char fullPath[1024];
|
|
snprintf(fullPath, sizeof(fullPath), "%s", pFileName);
|
|
|
|
V_FixSlashes(fullPath);
|
|
|
|
FILE* fp = fopen(fullPath, "rb");
|
|
if (!fp)
|
|
return 0;
|
|
|
|
fseek(fp, 0, SEEK_END);
|
|
ptrdiff_t size = Tell(fp);
|
|
|
|
fclose(fp);
|
|
return size;
|
|
}
|
|
|
|
ssize_t CBaseFileSystem::Size(FileHandle_t file)
|
|
{
|
|
fseek((FILE*)file, 0, SEEK_END);
|
|
ptrdiff_t size = Tell((FILE*)file);
|
|
|
|
fseek((FILE*)file, 0, SEEK_SET);
|
|
return size;
|
|
}
|
|
|
|
void CBaseFileSystem::Flush(FileHandle_t file)
|
|
{
|
|
fflush((FILE*)file);
|
|
}
|
|
|
|
bool CBaseFileSystem::Precache(const char* pFileName, const char* pPathID)
|
|
{
|
|
NOTE_UNUSED(pPathID);
|
|
NOTE_UNUSED(pFileName);
|
|
return true;
|
|
}
|
|
|
|
bool CBaseFileSystem::FileExists(const char* pFileName, const char* pPathID)
|
|
{
|
|
char fullPath[1024];
|
|
snprintf(fullPath, sizeof(fullPath), "%s", pFileName);
|
|
|
|
V_FixSlashes(fullPath);
|
|
|
|
FILE* fp = fopen(fullPath, "rb");
|
|
if (!fp)
|
|
return false;
|
|
|
|
fclose(fp);
|
|
return true;
|
|
}
|
|
|
|
bool CBaseFileSystem::IsFileWritable(char const* pFileName, const char* pPathID)
|
|
{
|
|
char fullPath[1024];
|
|
snprintf(fullPath, sizeof(fullPath), "%s", pFileName);
|
|
|
|
V_FixSlashes(fullPath);
|
|
|
|
FILE* fp = fopen(fullPath, "a");
|
|
if (!fp)
|
|
return false;
|
|
|
|
fclose(fp);
|
|
return true;
|
|
}
|
|
|
|
bool CBaseFileSystem::SetFileWritable(char const* pFileName, bool writable, const char* pPathID)
|
|
{
|
|
NOTE_UNUSED(pPathID);
|
|
NOTE_UNUSED(writable);
|
|
NOTE_UNUSED(pFileName);
|
|
return true;
|
|
}
|
|
|
|
long long CBaseFileSystem::GetFileTime(const char* pFileName, const char* pPathID)
|
|
{
|
|
char fullPath[1024];
|
|
snprintf(fullPath, sizeof(fullPath), "%s", pFileName);
|
|
|
|
V_FixSlashes(fullPath);
|
|
|
|
struct stat result;
|
|
|
|
// On POSIX systems, st_mtime is the time of last
|
|
// modification in seconds since the epoch.
|
|
if (stat(fullPath, &result) == NULL)
|
|
return (long)result.st_mtime;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
bool CBaseFileSystem::GetOptimalIOConstraints(FileHandle_t hFile, uint64_t* pOffsetAlign, uint64_t* pSizeAlign, uint64_t* pBufferAlign)
|
|
{
|
|
if (pOffsetAlign)
|
|
*pOffsetAlign = 1;
|
|
if (pSizeAlign)
|
|
*pSizeAlign = 1;
|
|
if (pBufferAlign)
|
|
*pBufferAlign = 1;
|
|
return false;
|
|
}
|
|
|
|
bool CBaseFileSystem::ReadToBuffer(FileHandle_t hFile, CUtlBuffer& buf, ssize_t nMaxBytes, FSAllocFunc_t pfnAlloc)
|
|
{
|
|
ssize_t nBytesToRead = Size(hFile);
|
|
if (nBytesToRead == 0)
|
|
{
|
|
// no data in file
|
|
return true;
|
|
}
|
|
|
|
if (nMaxBytes > 0)
|
|
{
|
|
// can't read more than file has
|
|
nBytesToRead = MIN(nMaxBytes, nBytesToRead);
|
|
}
|
|
|
|
ssize_t nBytesRead = 0;
|
|
ptrdiff_t nBytesOffset = 0;
|
|
|
|
ptrdiff_t iStartPos = Tell(hFile);
|
|
|
|
if (nBytesToRead != 0)
|
|
{
|
|
ssize_t nBytesDestBuffer = nBytesToRead;
|
|
uint64_t nSizeAlign = 0, nBufferAlign = 0, nOffsetAlign = 0;
|
|
|
|
bool bBinary = !(buf.IsText() && !buf.ContainsCRLF());
|
|
|
|
if (bBinary && !IsPosix() && !buf.IsExternallyAllocated() && !pfnAlloc &&
|
|
(buf.TellPut() == 0) && (buf.TellGet() == 0) && (iStartPos % 4 == 0) &&
|
|
GetOptimalIOConstraints(hFile, &nOffsetAlign, &nSizeAlign, &nBufferAlign))
|
|
{
|
|
// correct conditions to allow an optimal read
|
|
if (iStartPos % nOffsetAlign != 0)
|
|
{
|
|
// move starting position back to nearest alignment
|
|
nBytesOffset = (iStartPos % nOffsetAlign);
|
|
Assert((iStartPos - nBytesOffset) % nOffsetAlign == 0);
|
|
Seek(hFile, -nBytesOffset, FILESYSTEM_SEEK_CURRENT);
|
|
|
|
// going to read from aligned start, increase target buffer size by offset alignment
|
|
nBytesDestBuffer += nBytesOffset;
|
|
}
|
|
|
|
// snap target buffer size to its size alignment
|
|
// add additional alignment slop for target pointer adjustment
|
|
nBytesDestBuffer = AlignValue(nBytesDestBuffer, nSizeAlign) + nBufferAlign;
|
|
}
|
|
|
|
AssertMsg(!pfnAlloc, "Custom allocators not yet supported!");
|
|
|
|
//if (!pfnAlloc)
|
|
{
|
|
buf.EnsureCapacity(nBytesDestBuffer + buf.TellPut());
|
|
}
|
|
//else
|
|
//{
|
|
// // caller provided allocator
|
|
// void* pMemory = (*pfnAlloc)(g_pszReadFilename, nBytesDestBuffer);
|
|
// buf.SetExternalBuffer(pMemory, nBytesDestBuffer, 0, buf.GetFlags() & ~CUtlBuffer::EXTERNAL_GROWABLE);
|
|
//}
|
|
|
|
ssize_t seekGet = -1;
|
|
if (nBytesDestBuffer != nBytesToRead)
|
|
{
|
|
// doing optimal read, align target pointer
|
|
ssize_t nAlignedBase = AlignValue((byte*)buf.Base(), nBufferAlign) - (byte*)buf.Base();
|
|
buf.SeekPut(CUtlBuffer::SEEK_HEAD, nAlignedBase);
|
|
|
|
// the buffer read position is slid forward to ignore the addtional
|
|
// starting offset alignment
|
|
seekGet = nAlignedBase + nBytesOffset;
|
|
}
|
|
|
|
nBytesRead = ReadEx(buf.PeekPut(), nBytesDestBuffer - nBufferAlign, nBytesToRead + nBytesOffset, hFile);
|
|
buf.SeekPut(CUtlBuffer::SEEK_CURRENT, nBytesRead);
|
|
|
|
if (seekGet != -1)
|
|
{
|
|
// can only seek the get after data has been put, otherwise buffer sets overflow error
|
|
buf.SeekGet(CUtlBuffer::SEEK_HEAD, seekGet);
|
|
}
|
|
|
|
Seek(hFile, iStartPos + (nBytesRead - nBytesOffset), FILESYSTEM_SEEK_HEAD);
|
|
}
|
|
|
|
return (nBytesRead != 0);
|
|
}
|
|
|
|
bool CBaseFileSystem::ReadFile(const char* pFileName, const char* pPath, CUtlBuffer& buf, ssize_t nMaxBytes, ptrdiff_t nStartingByte, FSAllocFunc_t pfnAlloc)
|
|
{
|
|
//CHECK_DOUBLE_SLASHES(pFileName);
|
|
|
|
bool bBinary = !(buf.IsText() && !buf.ContainsCRLF());
|
|
|
|
FileHandle_t fp = Open(pFileName, (bBinary) ? "rb" : "rt", pPath);
|
|
if (!fp)
|
|
return false;
|
|
|
|
if (nStartingByte != 0)
|
|
{
|
|
Seek(fp, nStartingByte, FILESYSTEM_SEEK_HEAD);
|
|
}
|
|
|
|
AssertMsg(!pfnAlloc, "Custom allocators not yet supported!");
|
|
|
|
//if (pfnAlloc)
|
|
//{
|
|
// g_pszReadFilename = (char*)pFileName;
|
|
//}
|
|
|
|
bool bSuccess = ReadToBuffer(fp, buf, nMaxBytes, pfnAlloc);
|
|
|
|
Close(fp);
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
bool CBaseFileSystem::WriteFile(const char* pFileName, const char* pPath, CUtlBuffer& buf)
|
|
{
|
|
const char* pWriteFlags = "wb";
|
|
if (buf.IsText() && !buf.ContainsCRLF())
|
|
{
|
|
pWriteFlags = "wt";
|
|
}
|
|
|
|
FileHandle_t fp = Open(pFileName, pWriteFlags, pPath);
|
|
if (!fp)
|
|
return false;
|
|
|
|
ssize_t nBytesWritten = Write(buf.Base(), buf.TellMaxPut(), fp);
|
|
|
|
Close(fp);
|
|
return (nBytesWritten != 0);
|
|
}
|
|
|
|
ssize_t CBaseFileSystem::ReadEx(void* pOutput, ssize_t /*destSize*/, ssize_t size, FileHandle_t file)
|
|
{
|
|
if (!file)
|
|
{
|
|
assert(0); // Tried to Read NULL file handle!
|
|
return 0;
|
|
}
|
|
if (size < 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
const ssize_t nRet = fread(pOutput, sizeof(uint8_t), size, (FILE*)file);
|
|
return nRet;
|
|
|
|
}
|
|
|
|
int CBaseFileSystem::CreateDirHierarchy(const char* pFileName, const char* pPathID)
|
|
{
|
|
char fullPath[1024];
|
|
int results;
|
|
|
|
//if (pPathID)
|
|
// snprintf(fullPath, sizeof(fullPath), "%s/%s", pPathID, pFileName);
|
|
//else
|
|
snprintf(fullPath, sizeof(fullPath), "%s", pFileName);
|
|
|
|
V_FixSlashes(fullPath);
|
|
|
|
char* pFullPath = fullPath;
|
|
while ((pFullPath = strchr(pFullPath, CORRECT_PATH_SEPARATOR)) != NULL)
|
|
{
|
|
// Temporarily turn the slash into a null
|
|
// to get the current directory.
|
|
*pFullPath = '\0';
|
|
|
|
results = _mkdir(fullPath);
|
|
|
|
if (results && errno != EEXIST)
|
|
return results;
|
|
|
|
*pFullPath++ = CORRECT_PATH_SEPARATOR;
|
|
}
|
|
|
|
// Try to create the final directory in the path.
|
|
return _mkdir(fullPath);
|
|
}
|
|
|
|
bool CBaseFileSystem::IsDirectory(const char* path, const char* pathID)
|
|
{
|
|
if (_access(path, 0) == 0)
|
|
{
|
|
struct stat status;
|
|
stat(path, &status);
|
|
|
|
return (status.st_mode & S_IFDIR) != 0;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
char* CBaseFileSystem::ReadLine(char* maxChars, ssize_t maxOutputLength, FileHandle_t file)
|
|
{
|
|
return fgets(maxChars, (int)maxOutputLength, (FILE*)file);
|
|
}
|
|
|
|
CUtlString CBaseFileSystem::ReadString(FileHandle_t pFile)
|
|
{
|
|
CUtlString result;
|
|
char c = '\0';
|
|
|
|
do
|
|
{
|
|
Read(&c, sizeof(char), pFile);
|
|
|
|
if (c)
|
|
result += c;
|
|
|
|
} while (c);
|
|
|
|
return result;
|
|
}
|