From 3f76ed399caccc3a1364d09fb7cd569d487a806c Mon Sep 17 00:00:00 2001 From: Sam V Date: Sun, 28 Nov 2021 19:04:12 +0100 Subject: [PATCH] Provide access to engine filesystem Resolves #86 --- cl_dll/cdll_int.cpp | 6 + cl_dll/input.cpp | 2 + dlls/cbase.cpp | 1 + dlls/game.cpp | 13 ++ dlls/game.h | 1 + game_shared/filesystem_utils.cpp | 149 +++++++++++++++++++++ game_shared/filesystem_utils.h | 164 ++++++++++++++++++++++++ projects/vs2017/hl_cdll.vcxproj | 3 + projects/vs2017/hl_cdll.vcxproj.filters | 12 ++ projects/vs2017/hldll.vcxproj | 4 + projects/vs2017/hldll.vcxproj.filters | 21 +++ projects/vs2019/hl_cdll.vcxproj | 3 + projects/vs2019/hl_cdll.vcxproj.filters | 12 ++ projects/vs2019/hldll.vcxproj | 4 + projects/vs2019/hldll.vcxproj.filters | 21 +++ public/FileSystem.h | 2 +- 16 files changed, 417 insertions(+), 1 deletion(-) create mode 100644 game_shared/filesystem_utils.cpp create mode 100644 game_shared/filesystem_utils.h diff --git a/cl_dll/cdll_int.cpp b/cl_dll/cdll_int.cpp index 6efe052b4..37d9c3aae 100644 --- a/cl_dll/cdll_int.cpp +++ b/cl_dll/cdll_int.cpp @@ -34,6 +34,7 @@ #include "tri.h" #include "vgui_TeamFortressViewport.h" +#include "filesystem_utils.h" cl_enginefunc_t gEngfuncs; CHud gHUD; @@ -145,6 +146,11 @@ int DLLEXPORT Initialize(cl_enginefunc_t* pEnginefuncs, int iVersion) EV_HookEvents(); CL_LoadParticleMan(); + if (!FileSystem_LoadFileSystem()) + { + return 0; + } + // get tracker interface, if any return 1; } diff --git a/cl_dll/input.cpp b/cl_dll/input.cpp index ee10055bc..648f81601 100644 --- a/cl_dll/input.cpp +++ b/cl_dll/input.cpp @@ -19,6 +19,7 @@ #include "Exports.h" #include "vgui_TeamFortressViewport.h" +#include "filesystem_utils.h" extern bool g_iAlive; @@ -1029,5 +1030,6 @@ void DLLEXPORT HUD_Shutdown() ClearEventList(); #endif + FileSystem_FreeFileSystem(); CL_UnloadParticleMan(); } diff --git a/dlls/cbase.cpp b/dlls/cbase.cpp index 3e2f46e4b..632309cd4 100644 --- a/dlls/cbase.cpp +++ b/dlls/cbase.cpp @@ -100,6 +100,7 @@ static DLL_FUNCTIONS gFunctionTable = NEW_DLL_FUNCTIONS gNewDLLFunctions = { OnFreeEntPrivateData, //pfnOnFreeEntPrivateData + GameDLLShutdown, }; static void SetObjectCollisionBox(entvars_t* pev); diff --git a/dlls/game.cpp b/dlls/game.cpp index 46ff56f4c..14802346d 100644 --- a/dlls/game.cpp +++ b/dlls/game.cpp @@ -16,6 +16,7 @@ #include "eiface.h" #include "util.h" #include "game.h" +#include "filesystem_utils.h" cvar_t displaysoundlist = {"displaysoundlist", "0"}; @@ -467,6 +468,13 @@ void GameDLLInit() g_footsteps = CVAR_GET_POINTER("mp_footsteps"); g_psv_cheats = CVAR_GET_POINTER("sv_cheats"); + if (!FileSystem_LoadFileSystem()) + { + //Shut the game down as soon as possible. + SERVER_COMMAND("quit\n"); + return; + } + CVAR_REGISTER(&displaysoundlist); CVAR_REGISTER(&allow_spectators); @@ -899,3 +907,8 @@ void GameDLLInit() SERVER_COMMAND("exec skill.cfg\n"); } + +void GameDLLShutdown() +{ + FileSystem_FreeFileSystem(); +} diff --git a/dlls/game.h b/dlls/game.h index a67d3f74d..c59fd099c 100644 --- a/dlls/game.h +++ b/dlls/game.h @@ -16,6 +16,7 @@ #pragma once extern void GameDLLInit( void ); +void GameDLLShutdown(); extern cvar_t displaysoundlist; diff --git a/game_shared/filesystem_utils.cpp b/game_shared/filesystem_utils.cpp new file mode 100644 index 000000000..d895eb323 --- /dev/null +++ b/game_shared/filesystem_utils.cpp @@ -0,0 +1,149 @@ +/*** +* +* Copyright (c) 1996-2002, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* Use, distribution, and modification of this source code and/or resulting +* object code is restricted to non-commercial enhancements to products from +* Valve LLC. All other use, distribution, or modification is prohibited +* without written permission from Valve LLC. +* +****/ + +#include +#include + +#include "Platform.h" + +#include "extdll.h" +#include "util.h" + +#include "interface.h" + +#include "filesystem_utils.h" + +static CSysModule* g_pFileSystemModule = nullptr; + +bool FileSystem_LoadFileSystem() +{ + if (nullptr != g_pFileSystem) + { + //Already loaded. + return true; + } + + // Determine which filesystem to use. +#if defined ( _WIN32 ) + const char* szFsModule = "filesystem_stdio.dll"; +#elif defined(OSX) + const char* szFsModule = "filesystem_stdio.dylib"; +#elif defined(LINUX) + const char* szFsModule = "filesystem_stdio.so"; +#else +#error +#endif + + // Get filesystem interface. + // The library is located next to the game exe, so there is no need to resolve the path first. + g_pFileSystemModule = Sys_LoadModule(szFsModule); + + assert(nullptr != g_pFileSystemModule); + + if (nullptr == g_pFileSystemModule) + { + return false; + } + + CreateInterfaceFn fileSystemFactory = Sys_GetFactory(g_pFileSystemModule); + + if (nullptr == fileSystemFactory) + { + return false; + } + + g_pFileSystem = reinterpret_cast(fileSystemFactory(FILESYSTEM_INTERFACE_VERSION, nullptr)); + + assert(nullptr != g_pFileSystem); + + if (nullptr == g_pFileSystem) + { + return false; + } + + return true; +} + +void FileSystem_FreeFileSystem() +{ + if (nullptr != g_pFileSystem) + { + g_pFileSystem = nullptr; + } + + if (nullptr != g_pFileSystemModule) + { + Sys_UnloadModule(g_pFileSystemModule); + g_pFileSystemModule = nullptr; + } +} + +std::vector FileSystem_LoadFileIntoBuffer(const char* fileName, const char* pathID) +{ + assert(nullptr != g_pFileSystem); + + if (nullptr == fileName) + { + return {}; + } + + if (FSFile file{fileName, "rb", pathID}; file) + { + const auto size = file.Size(); + + //Null terminate it in case it's actually text. + std::vector buffer; + + buffer.resize(size + 1); + + file.Read(buffer.data(), size); + + buffer[size] = std::byte{'\0'}; + + return buffer; + } + + ALERT(at_console, "FileSystem_LoadFileIntoBuffer: couldn't open file \"%s\" for reading\n", fileName); + return {}; +} + +bool FileSystem_WriteTextToFile(const char* fileName, const char* text, const char* pathID) +{ + assert(nullptr != g_pFileSystem); + + if (nullptr == fileName || nullptr == text) + { + return false; + } + + const std::size_t length = std::strlen(text); + + if (length > static_cast(std::numeric_limits::max())) + { + ALERT(at_console, "FileSystem_WriteTextToFile: text too long\n"); + return false; + } + + if (FSFile file{fileName, "w", pathID}; file) + { + file.Write(text, length); + + return true; + } + + ALERT(at_console, "FileSystem_WriteTextToFile: couldn't open file \"%s\" for writing\n", fileName); + + return false; +} diff --git a/game_shared/filesystem_utils.h b/game_shared/filesystem_utils.h new file mode 100644 index 000000000..2b0e5aec5 --- /dev/null +++ b/game_shared/filesystem_utils.h @@ -0,0 +1,164 @@ +/*** +* +* Copyright (c) 1996-2002, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* Use, distribution, and modification of this source code and/or resulting +* object code is restricted to non-commercial enhancements to products from +* Valve LLC. All other use, distribution, or modification is prohibited +* without written permission from Valve LLC. +* +****/ + +#pragma once + +/** +* @file +* +* Functions, types and globals to load and use the GoldSource engine filesystem interface to read and write files. +* See the VDC for information on which search paths exist to be used as path IDs: +* https://developer.valvesoftware.com/wiki/GoldSource_SteamPipe_Directories +*/ + +#include +#include + +#include "Platform.h" +#include "FileSystem.h" + +inline IFileSystem* g_pFileSystem = nullptr; + +bool FileSystem_LoadFileSystem(); +void FileSystem_FreeFileSystem(); + +/** +* @brief Loads a file from disk into a buffer. +* +* @details If the returned buffer contains text data it is safe to cast the data pointer to char*: +* @code{.cpp} +* auto text = reinterpret_cast(buffer.data()); +* @endcode +* +* @param fileName Name of the file to load. +* @param pathID If not null, only looks for the file in this search path. +* @return If the file was successfully loaded, the contents of the buffer, with a zero byte (null terminator) appended to it in case it's a text file. +* If the file could not be loaded an empty buffer is returned. +*/ +std::vector FileSystem_LoadFileIntoBuffer(const char* fileName, const char* pathID = nullptr); + +/** +* @brief Writes a text file to disk. +* @param fileName Name of the file to write to. +* @param text Null-terminated text to write. The null terminator is not written to disk. +* @param pathID If not null, writes to a writable location assigned to the given search path. +* Otherwise the first writable location will be used (in practice this will be the mod directory). +* If no writable location exists no file will be written to. +* @return True if the file was written, false if an error occurred. +*/ +bool FileSystem_WriteTextToFile(const char* fileName, const char* text, const char* pathID = nullptr); + +/** +* @brief Helper class to automatically close the file handle associated with a file. +*/ +class FSFile +{ +public: + FSFile() noexcept = default; + FSFile(const char* fileName, const char* options, const char* pathID = nullptr); + + FSFile(FSFile&& other) noexcept + : _handle(other._handle) + { + other._handle = FILESYSTEM_INVALID_HANDLE; + } + + FSFile& operator=(FSFile&& other) noexcept + { + if (this != &other) + { + Close(); + _handle = other._handle; + other._handle = FILESYSTEM_INVALID_HANDLE; + } + + return *this; + } + + FSFile(const FSFile&) = delete; + FSFile& operator=(const FSFile&) = delete; + + ~FSFile(); + + constexpr bool IsOpen() const { return _handle != FILESYSTEM_INVALID_HANDLE; } + + std::size_t Size() const { return static_cast(g_pFileSystem->Size(_handle)); } + + bool Open(const char* filename, const char* options, const char* pathID = nullptr); + void Close(); + + void Seek(int pos, FileSystemSeek_t seekType); + + int Read(void* dest, int size); + + int Write(const void* input, int size); + + template + int Printf(const char* format, Args&&... args) + { + return g_pFileSystem->FPrintf(_handle, format, std::forward(args)...); + } + + constexpr operator bool() const { return IsOpen(); } + +private: + FileHandle_t _handle = FILESYSTEM_INVALID_HANDLE; +}; + +inline FSFile::FSFile(const char* filename, const char* options, const char* pathID) +{ + Open(filename, options, pathID); +} + +inline FSFile::~FSFile() +{ + Close(); +} + +inline bool FSFile::Open(const char* filename, const char* options, const char* pathID) +{ + Close(); + + _handle = g_pFileSystem->Open(filename, options, pathID); + + return IsOpen(); +} + +inline void FSFile::Close() +{ + if (IsOpen()) + { + g_pFileSystem->Close(_handle); + _handle = FILESYSTEM_INVALID_HANDLE; + } +} + +inline void FSFile::Seek(int pos, FileSystemSeek_t seekType) +{ + if (IsOpen()) + { + g_pFileSystem->Seek(_handle, pos, seekType); + } +} + +inline int FSFile::Read(void* dest, int size) +{ + return g_pFileSystem->Read(dest, size, _handle); +} + +inline int FSFile::Write(const void* input, int size) +{ + return g_pFileSystem->Write(input, size, _handle); +} diff --git a/projects/vs2017/hl_cdll.vcxproj b/projects/vs2017/hl_cdll.vcxproj index 71e0c90d4..ad6c11daa 100644 --- a/projects/vs2017/hl_cdll.vcxproj +++ b/projects/vs2017/hl_cdll.vcxproj @@ -179,6 +179,7 @@ + @@ -267,6 +268,7 @@ + @@ -277,6 +279,7 @@ + diff --git a/projects/vs2017/hl_cdll.vcxproj.filters b/projects/vs2017/hl_cdll.vcxproj.filters index 08fb28f91..79a66f525 100644 --- a/projects/vs2017/hl_cdll.vcxproj.filters +++ b/projects/vs2017/hl_cdll.vcxproj.filters @@ -55,6 +55,9 @@ {bd9d5958-4e67-4bff-bbd8-6ba6dfcdb720} + + {243d59b1-51fd-4488-ae96-9d338f92e340} + @@ -297,6 +300,9 @@ Source Files\_hl\dlls + + Source Files\game_shared + @@ -551,5 +557,11 @@ Header Files\common + + Header Files\public + + + Header Files\game_shared + \ No newline at end of file diff --git a/projects/vs2017/hldll.vcxproj b/projects/vs2017/hldll.vcxproj index 2d71cb432..3ebdafcbb 100644 --- a/projects/vs2017/hldll.vcxproj +++ b/projects/vs2017/hldll.vcxproj @@ -208,10 +208,12 @@ + + @@ -291,12 +293,14 @@ + + diff --git a/projects/vs2017/hldll.vcxproj.filters b/projects/vs2017/hldll.vcxproj.filters index bc2f22fd5..a3bc653f6 100644 --- a/projects/vs2017/hldll.vcxproj.filters +++ b/projects/vs2017/hldll.vcxproj.filters @@ -40,6 +40,15 @@ {42fd860c-8851-42ea-81e1-ffda08a6174d} + + {fec47406-1d33-44ea-b44a-8547409067eb} + + + {c0417a7f-fc16-4cfd-a098-9ae375cf7175} + + + {c207f46b-3759-43d1-8952-13d3f8c2d5fa} + @@ -357,6 +366,12 @@ Source Files\dlls + + Source Files\public + + + Source Files\game_shared + @@ -608,5 +623,11 @@ Header Files\common + + Header Files\public + + + Header Files\game_shared + \ No newline at end of file diff --git a/projects/vs2019/hl_cdll.vcxproj b/projects/vs2019/hl_cdll.vcxproj index fc366bcca..3e3fe9c39 100644 --- a/projects/vs2019/hl_cdll.vcxproj +++ b/projects/vs2019/hl_cdll.vcxproj @@ -181,6 +181,7 @@ + @@ -269,6 +270,7 @@ + @@ -279,6 +281,7 @@ + diff --git a/projects/vs2019/hl_cdll.vcxproj.filters b/projects/vs2019/hl_cdll.vcxproj.filters index 47fd6ef4d..e0c948693 100644 --- a/projects/vs2019/hl_cdll.vcxproj.filters +++ b/projects/vs2019/hl_cdll.vcxproj.filters @@ -58,6 +58,9 @@ {ebcb0f66-49b6-4e55-9a97-f6720cade9ec} + + {01f1d541-3bac-4b2c-9a83-2cadcb76ff8d} + @@ -300,6 +303,9 @@ Source Files\_hl\dlls + + Source Files\game_shared + @@ -554,5 +560,11 @@ Header Files\common + + Header Files\game_shared + + + Header Files\public + \ No newline at end of file diff --git a/projects/vs2019/hldll.vcxproj b/projects/vs2019/hldll.vcxproj index ace3dc5fd..ae43abd59 100644 --- a/projects/vs2019/hldll.vcxproj +++ b/projects/vs2019/hldll.vcxproj @@ -210,10 +210,12 @@ + + @@ -293,12 +295,14 @@ + + diff --git a/projects/vs2019/hldll.vcxproj.filters b/projects/vs2019/hldll.vcxproj.filters index bc2f22fd5..1dd30f94a 100644 --- a/projects/vs2019/hldll.vcxproj.filters +++ b/projects/vs2019/hldll.vcxproj.filters @@ -40,6 +40,15 @@ {42fd860c-8851-42ea-81e1-ffda08a6174d} + + {ab296c57-2b16-4290-bcb3-8f2e7b5c3f08} + + + {540eec11-a239-4a81-9a06-40491b9bdde3} + + + {ccda2445-0302-44ae-a537-be89f5811329} + @@ -357,6 +366,12 @@ Source Files\dlls + + Source Files\game_shared + + + Source Files\public + @@ -608,5 +623,11 @@ Header Files\common + + Header Files\game_shared + + + Header Files\public + \ No newline at end of file diff --git a/public/FileSystem.h b/public/FileSystem.h index 337d5941a..87e0f6f94 100644 --- a/public/FileSystem.h +++ b/public/FileSystem.h @@ -11,7 +11,7 @@ #pragma once #endif -#include "archtypes.h" // DAL +#include "steam/steamtypes.h" // DAL #include "interface.h" //-----------------------------------------------------------------------------