diff --git a/.gitignore b/.gitignore index 30fa6ef..63383d8 100644 --- a/.gitignore +++ b/.gitignore @@ -15,11 +15,11 @@ **/msvc/*.suo **/msvc/*.db **/msvc/*.opendb +**/msvc/PublishPath*.txt **/msvc/ipch **/msvc/.vs rehlds/version/appversion.h -rehlds/msvc/PublishPath*.txt rehlds/_rehldsTestImg rehlds/_dev publish diff --git a/publish.gradle b/publish.gradle index ee6d0bd..da624f1 100644 --- a/publish.gradle +++ b/publish.gradle @@ -2,11 +2,25 @@ import org.doomedsociety.gradlecpp.GradleCppUtils import org.apache.commons.io.FilenameUtils void _copyFileToDir(String from, String to) { + if (!project.file(from).exists()) { + println 'WARNING: Could not find: ' + from; + return; + } + + if (!project.file(to).exists()) { + project.file(to).mkdirs(); + } + def dst = new File(project.file(to), FilenameUtils.getName(from)) GradleCppUtils.copyFile(project.file(from), dst, false) } void _copyFile(String from, String to) { + if (!project.file(from).exists()) { + println 'WARNING: Could not find: ' + from; + return; + } + GradleCppUtils.copyFile(project.file(from), project.file(to), false) } @@ -22,19 +36,21 @@ task publishPrepareFiles { pubRootDir.mkdirs() - //bugfixed binaries - project.file('publish/publishRoot/bin/bugfixed').mkdirs() + // bugfixed binaries _copyFileToDir('publish/releaseRehldsFixes/swds.dll', 'publish/publishRoot/bin/bugfixed/') _copyFileToDir('publish/releaseRehldsFixes/swds.pdb', 'publish/publishRoot/bin/bugfixed/') _copyFile('publish/releaseRehldsFixes/libengine_i486.so', 'publish/publishRoot/bin/bugfixed/engine_i486.so') - //pure binaries - project.file('publish/publishRoot/bin/pure').mkdirs() + // pure binaries _copyFileToDir('publish/releaseRehldsNofixes/swds.dll', 'publish/publishRoot/bin/pure/') _copyFileToDir('publish/releaseRehldsNofixes/swds.pdb', 'publish/publishRoot/bin/pure/') _copyFile('publish/releaseRehldsNofixes/libengine_i486.so', 'publish/publishRoot/bin/pure/engine_i486.so') - //hlsdk + // dedicated binaries + _copyFile('publish/hlds.exe', 'publish/publishRoot/bin/hlds.exe') + _copyFile('publish/hlds_linux', 'publish/publishRoot/bin/hlds_linux') + + // hlsdk project.file('publish/publishRoot/hlsdk').mkdirs() copy { from 'rehlds/common' @@ -58,8 +74,7 @@ task publishPrepareFiles { into 'publish/publishRoot/hlsdk/engine' } - //flightrecorder - + // flightrecorder def flightRecJarTask = project(':flightrec/decoder').tasks.getByName('uberjar') println flightRecJarTask println flightRecJarTask.class.name diff --git a/rehlds/common/BaseSystemModule.cpp b/rehlds/common/BaseSystemModule.cpp new file mode 100644 index 0000000..2082d0b --- /dev/null +++ b/rehlds/common/BaseSystemModule.cpp @@ -0,0 +1,140 @@ +#include "BaseSystemModule.h" +#include + +BaseSystemModule::BaseSystemModule() +{ + m_State = MODULE_INACTIVE; +} + +BaseSystemModule::~BaseSystemModule() +{ + ; +} + +char *BaseSystemModule::GetName() +{ + return m_Name; +} + +char *BaseSystemModule::GetType() +{ + return "GenericModule"; +} + +char *BaseSystemModule::GetStatusLine() +{ + return "No status available.\n"; +} + +void BaseSystemModule::ExecuteCommand(int commandID, char *commandLine) +{ + m_System->DPrintf("WARNING! Undeclared ExecuteCommand().\n"); +} + +extern int COM_BuildNumber(); + +int BaseSystemModule::GetVersion() +{ + return COM_BuildNumber(); +} + +int BaseSystemModule::GetSerial() +{ + return m_Serial; +} + +IBaseSystem *BaseSystemModule::GetSystem() +{ + return m_System; +} + +bool BaseSystemModule::Init(IBaseSystem *system, int serial, char *name) +{ + if (!system) + return false; + + m_State = MODULE_PENDING; + m_System = system; + m_Serial = serial; + m_SystemTime = 0; + + if (name) + { + strncpy(m_Name, name, sizeof m_Name - 1); + m_Name[sizeof m_Name - 1] = '\0'; + } + + return true; +} + +void BaseSystemModule::RunFrame(double time) +{ + m_SystemTime = time; +} + +void BaseSystemModule::ShutDown() +{ + if (m_State == MODULE_UNLOAD) + return; + + m_Listener.Clear(); + m_State = MODULE_UNLOAD; + + // TODO: Check me! + if (!m_System->RemoveModule(this)) + { + m_System->DPrintf("ERROR! BaseSystemModule::ShutDown: faild to remove module %s.\n", m_Name); + } +} + +void BaseSystemModule::RegisterListener(ISystemModule *module) +{ + ISystemModule *listener = (ISystemModule *)m_Listener.GetFirst(); + while (listener) + { + if (listener->GetSerial() == module->GetSerial()) + { + m_System->DPrintf("WARNING! BaseSystemModule::RegisterListener: module %s already added.\n", module->GetName()); + return; + } + + listener = (ISystemModule *)m_Listener.GetNext(); + } + + m_Listener.Add(module); +} + +void BaseSystemModule::RemoveListener(ISystemModule *module) +{ + ISystemModule *listener = (ISystemModule *)m_Listener.GetFirst(); + while (listener) + { + if (listener->GetSerial() == module->GetSerial()) + { + m_Listener.Remove(module); + return; + } + + listener = (ISystemModule *)m_Listener.GetNext(); + } +} + +void BaseSystemModule::FireSignal(unsigned int signal, void *data) +{ + ISystemModule *listener = (ISystemModule *)m_Listener.GetFirst(); + while (listener) + { + listener->ReceiveSignal(this, signal, data); + listener = (ISystemModule *)m_Listener.GetNext(); + } +} + +void BaseSystemModule::ReceiveSignal(ISystemModule *module, unsigned int signal, void *data) +{ + m_System->DPrintf("WARNING! Unhandled signal (%i) from module %s.\n", signal, module->GetName()); +} + +int BaseSystemModule::GetState() +{ + return m_State; +} diff --git a/rehlds/common/BaseSystemModule.h b/rehlds/common/BaseSystemModule.h new file mode 100644 index 0000000..dc76fa7 --- /dev/null +++ b/rehlds/common/BaseSystemModule.h @@ -0,0 +1,55 @@ +#ifndef BASESYSTEMMODULE_H +#define BASESYSTEMMODULE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "ObjectList.h" +#include "IBaseSystem.h" + +#define SIGNAL_MODULE_CLOSE 8 // closing down module + +enum ModuleState +{ + MODULE_INACTIVE, + MODULE_PENDING, + MODULE_RUNNING, + MODULE_LOAD, + MODULE_UNLOAD +}; + +// C4250 - 'class1' : inherits 'BaseSystemModule::member' via dominance +#pragma warning(disable:4250) + +class BaseSystemModule: virtual public ISystemModule { +public: + BaseSystemModule(); + virtual ~BaseSystemModule(); + + virtual bool Init(IBaseSystem *system, int serial, char *name); + virtual void RunFrame(double time); + virtual void ReceiveSignal(ISystemModule *module, unsigned int signal, void *data); + virtual void ExecuteCommand(int commandID, char *commandLine); + virtual void RegisterListener(ISystemModule *module); + virtual void RemoveListener(ISystemModule *module); + virtual IBaseSystem *GetSystem(); + virtual int GetSerial(); + virtual char *GetStatusLine(); + virtual char *GetType(); + virtual char *GetName(); + virtual int GetState(); + virtual int GetVersion(); + virtual void ShutDown(); + virtual char *COM_GetBaseDir() { return ""; } + void FireSignal(unsigned int signal, void *data); + +protected: + IBaseSystem *m_System; + ObjectList m_Listener; + char m_Name[255]; + unsigned int m_State; + unsigned int m_Serial; + double m_SystemTime; +}; + +#endif // BASESYSTEMMODULE_H diff --git a/rehlds/common/IAdminServer.h b/rehlds/common/IAdminServer.h new file mode 100644 index 0000000..41a5d21 --- /dev/null +++ b/rehlds/common/IAdminServer.h @@ -0,0 +1,33 @@ +#ifndef IADMINSERVER_H +#define IADMINSERVER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" + +// handle to a game window +typedef unsigned int ManageServerUIHandle_t; +class IManageServer; + +// Purpose: Interface to server administration functions +class IAdminServer: public IBaseInterface +{ +public: + // opens a manage server dialog for a local server + virtual ManageServerUIHandle_t OpenManageServerDialog(const char *serverName, const char *gameDir) = 0; + + // opens a manage server dialog to a remote server + virtual ManageServerUIHandle_t OpenManageServerDialog(unsigned int gameIP, unsigned int gamePort, const char *password) = 0; + + // forces the game info dialog closed + virtual void CloseManageServerDialog(ManageServerUIHandle_t gameDialog) = 0; + + // Gets a handle to the interface + virtual IManageServer *GetManageServerInterface(ManageServerUIHandle_t handle) = 0; +}; + +#define ADMINSERVER_INTERFACE_VERSION "AdminServer002" + + +#endif // IAdminServer_H diff --git a/rehlds/common/IBaseSystem.h b/rehlds/common/IBaseSystem.h new file mode 100644 index 0000000..20239d1 --- /dev/null +++ b/rehlds/common/IBaseSystem.h @@ -0,0 +1,59 @@ +#ifndef IBASESYSTEM_H +#define IBASESYSTEM_H +#ifdef _WIN32 +#pragma once +#endif + +#include "ISystemModule.h" + +class Panel; +class ObjectList; +class IFileSystem; +class IVGuiModule; + +class IBaseSystem: virtual public ISystemModule { +public: + virtual ~IBaseSystem() {} + + virtual double GetTime() = 0; + virtual unsigned int GetTick() = 0; + virtual void SetFPS(float fps) = 0; + + virtual void Printf(char *fmt, ...) = 0; + virtual void DPrintf(char *fmt, ...) = 0; + + virtual void RedirectOutput(char *buffer, int maxSize) = 0; + + virtual IFileSystem *GetFileSystem() = 0; + virtual unsigned char *LoadFile(const char *name, int *length) = 0; + virtual void FreeFile(unsigned char *fileHandle) = 0; + + virtual void SetTitle(char *text) = 0; + virtual void SetStatusLine(char *text) = 0; + + virtual void ShowConsole(bool visible) = 0; + virtual void LogConsole(char *filename) = 0; + + virtual bool InitVGUI(IVGuiModule *module) = 0; + +#ifdef _WIN32 + virtual Panel *GetPanel() = 0; +#endif // _WIN32 + + virtual bool RegisterCommand(char *name, ISystemModule *module, int commandID) = 0; + virtual void GetCommandMatches(char *string, ObjectList *pMatchList) = 0; + virtual void ExecuteString(char *commands) = 0; + virtual void ExecuteFile(char *filename) = 0; + virtual void Errorf(char *fmt, ...) = 0; + + virtual char *CheckParam(char *param) = 0; + + virtual bool AddModule(ISystemModule *module, char *name) = 0; + virtual ISystemModule *GetModule(char *interfacename, char *library, char *instancename = nullptr) = 0; + virtual bool RemoveModule(ISystemModule *module) = 0; + + virtual void Stop() = 0; + virtual char *COM_GetBaseDir() = 0; +}; + +#endif // IBASESYSTEM_H diff --git a/rehlds/common/IObjectContainer.h b/rehlds/common/IObjectContainer.h new file mode 100644 index 0000000..d44ffa3 --- /dev/null +++ b/rehlds/common/IObjectContainer.h @@ -0,0 +1,25 @@ +#ifndef IOBJECTCONTAINER_H +#define IOBJECTCONTAINER_H +#ifdef _WIN32 +#pragma once +#endif + +class IObjectContainer { +public: + virtual ~IObjectContainer() {} + + virtual void Init() = 0; + + virtual bool Add(void *newObject) = 0; + virtual bool Remove(void *object) = 0; + virtual void Clear(bool freeElementsMemory) = 0; + + virtual void *GetFirst() = 0; + virtual void *GetNext() = 0; + + virtual int CountElements() = 0;; + virtual bool Contains(void *object) = 0; + virtual bool IsEmpty() = 0; +}; + +#endif // IOBJECTCONTAINER_H diff --git a/rehlds/common/ISystemModule.h b/rehlds/common/ISystemModule.h new file mode 100644 index 0000000..334ffea --- /dev/null +++ b/rehlds/common/ISystemModule.h @@ -0,0 +1,35 @@ +#ifndef ISYSTEMMODULE_H +#define ISYSTEMMODULE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" + +class IBaseSystem; +class ISystemModule; + +class ISystemModule: public IBaseInterface { +public: + virtual ~ISystemModule() {} + virtual bool Init(IBaseSystem *system, int serial, char *name) = 0; + + virtual void RunFrame(double time) = 0; + virtual void ReceiveSignal(ISystemModule *module, unsigned int signal, void *data) = 0; + virtual void ExecuteCommand(int commandID, char *commandLine) = 0; + virtual void RegisterListener(ISystemModule *module) = 0; + virtual void RemoveListener(ISystemModule *module) = 0; + + virtual IBaseSystem *GetSystem() = 0; + + virtual int GetSerial() = 0; + virtual char *GetStatusLine() = 0; + virtual char *GetType() = 0; + virtual char *GetName() = 0; + + virtual int GetState() = 0; + virtual int GetVersion() = 0; + virtual void ShutDown() = 0; +}; + +#endif // ISYSTEMMODULE_H diff --git a/rehlds/common/IVGuiModule.h b/rehlds/common/IVGuiModule.h new file mode 100644 index 0000000..e95263a --- /dev/null +++ b/rehlds/common/IVGuiModule.h @@ -0,0 +1,54 @@ +#ifndef IVGUIMODULE_H +#define IVGUIMODULE_H +#ifdef _WIN32 +#pragma once +#endif + +#include +#include "interface.h" + +// Purpose: Standard interface to loading vgui modules +class IVGuiModule: public IBaseInterface +{ +public: + // called first to setup the module with the vgui + // returns true on success, false on failure + virtual bool Initialize(CreateInterfaceFn *vguiFactories, int factoryCount) = 0; + + // called after all the modules have been initialized + // modules should use this time to link to all the other module interfaces + virtual bool PostInitialize(CreateInterfaceFn *modules, int factoryCount) = 0; + + // called when the module is selected from the menu or otherwise activated + virtual bool Activate() = 0; + + // returns true if the module is successfully initialized and available + virtual bool IsValid() = 0; + + // requests that the UI is temporarily disabled and all data files saved + virtual void Deactivate() = 0; + + // restart from a Deactivate() + virtual void Reactivate() = 0; + + // called when the module is about to be shutdown + virtual void Shutdown() = 0; + + // returns a handle to the main module panel + virtual vgui2::VPANEL GetPanel() = 0; + + // sets the parent of the main module panel + virtual void SetParent(vgui2::VPANEL parent) = 0; + + // messages sent through through the panel returned by GetPanel(): + // + // "ConnectedToGame" "ip" "port" "gamedir" + // "DisconnectedFromGame" + // "ActiveGameName" "name" + // "LoadingStarted" "type" "name" + // "LoadingFinished" "type" "name" +}; + +#define VGUIMODULE_INTERFACE_VERSION "VGuiModuleAdminServer001" + +#endif // IVGUIMODULE_H diff --git a/rehlds/common/ObjectDictionary.cpp b/rehlds/common/ObjectDictionary.cpp new file mode 100644 index 0000000..3fbf34b --- /dev/null +++ b/rehlds/common/ObjectDictionary.cpp @@ -0,0 +1,483 @@ +#include "ObjectDictionary.h" +#include "maintypes.h" + +ObjectDictionary::ObjectDictionary() : + maxSize(0), + size(0), + entries(nullptr) +{ +} + +ObjectDictionary::~ObjectDictionary() +{ + if (entries) { + free(entries); + } +} + +void ObjectDictionary::Clear(bool freeObjectssMemory) +{ + if (freeObjectssMemory) + { + for (int i = 0; i < size; i++) + { + void *obj = entries[i].object; + if (obj) { + free(obj); + } + } + } + + size = 0; + CheckSize(); + ClearCache(); +} + +bool ObjectDictionary::Add(void *object, float key) +{ + if (size == maxSize && !CheckSize()) + return false; + + entry_t *p; + if (size && key < entries[size - 1].key) + { + p = &entries[FindClosestAsIndex(key)]; + + entry_t *e1 = &entries[size]; + entry_t *e2 = &entries[size - 1]; + + while (p->key <= key) { p++; } + while (p != e1) + { + e1->object = e2->object; + e1->key = e2->key; + + e1--; + e2--; + } + } + else + p = &entries[size]; + + p->key = key; + p->object = object; + size++; + + ClearCache(); + AddToCache(p); + + return true; +} + +int ObjectDictionary::FindClosestAsIndex(float key) +{ + UNTESTED + + if (size <= 0) + return -1; + + if (key <= entries->key) + return 0; + + int index = FindKeyInCache(key); + if (index >= 0) + return index; + + int middle; + int first = 0; + int last = size - 1; + float keyMiddle, keyNext; + + if (key == entries[last].key) + { + while (true) + { + middle = (last + first) >> 1; + keyMiddle = entries[middle].key; + + if (keyMiddle == key) + break; + + if (keyMiddle < key) + { + if (entries[middle + 1].key != key) + { + if (entries[middle + 1].key - key < key - keyMiddle) + ++middle; + break; + } + + first = (last + first) >> 1; + } + else + { + last = (last + first) >> 1; + } + } + } + else + { + middle = last; + } + + keyNext = entries[middle - 1].key; + while (keyNext == key) { + keyNext = entries[middle--].key; + } + + AddToCache(&entries[middle], key); + return middle; +} + +void ObjectDictionary::ClearCache() +{ + memset(cache, 0, sizeof(cache)); + cacheIndex = 0; +} + +bool ObjectDictionary::RemoveIndex(int index, bool freeObjectMemory) +{ + if (index < 0 || index >= size) + return false; + + entry_t *p = &entries[size - 1]; + entry_t *e1 = &entries[size]; + entry_t *e2 = &entries[size + 1]; + + if (freeObjectMemory && e1->object) + free(e1->object); + + while (p != e1) + { + e1->object = e2->object; + e1->key = e2->key; + + e1++; + e2++; + } + + p->object = nullptr; + p->key = 0; + size--; + + CheckSize(); + ClearCache(); + + return false; +} + +bool ObjectDictionary::RemoveIndexRange(int minIndex, int maxIndex) +{ + if (minIndex > maxIndex) + { + if (maxIndex < 0) + maxIndex = 0; + + if (minIndex >= size) + minIndex = size - 1; + } + else + { + if (minIndex < 0) + minIndex = 0; + + if (maxIndex >= size) + maxIndex = size - 1; + } + + int offset = minIndex + maxIndex - 1; + size -= offset; + CheckSize(); + return true; +} + +bool ObjectDictionary::Remove(void *object) +{ + bool found = false; + for (int i = 0; i < size; ++i) + { + if (entries[i].object == object) { + RemoveIndex(i); + found = true; + } + } + + return found ? true : false; +} + +bool ObjectDictionary::RemoveSingle(void *object) +{ + for (int i = 0; i < size; ++i) + { + if (entries[i].object == object) { + RemoveIndex(i); + return true; + } + } + + return false; +} + +bool ObjectDictionary::RemoveKey(float key) +{ + int i = FindClosestAsIndex(key); + if (entries[i].key == key) + { + int j = i; + do { + ++j; + } + while (key == entries[j + 1].key); + + return RemoveIndexRange(i, j); + } + + return false; +} + +bool ObjectDictionary::CheckSize() +{ + int newSize = maxSize; + if (size == maxSize) + { + newSize = 1 - (int)(maxSize * -1.25f); + } + else if (maxSize * 0.5f >size) + { + newSize = (int)(maxSize * 0.75f); + } + + if (newSize != maxSize) + { + entry_t *newEntries = (entry_t *)malloc(sizeof(entry_t)); + if (!newEntries) + return false; + + memset(&newEntries[size], 0, sizeof(entry_t) * (newSize - size)); + + if (entries && size) + { + memcpy(newEntries, entries, sizeof(entry_t) * size); + free(entries); + } + + entries = newEntries; + maxSize = newSize; + } + + return true; +} + +void ObjectDictionary::Init() +{ + size = 0; + maxSize = 0; + entries = nullptr; + + CheckSize(); + ClearCache(); +} + +void ObjectDictionary::Init(int baseSize) +{ + size = 0; + maxSize = 0; + entries = (entry_t *)Mem_ZeroMalloc(sizeof(entry_t) * baseSize); + + if (entries) { + maxSize = baseSize; + } +} + +bool ObjectDictionary::Add(void *object) +{ + return Add(object, 0); +} + +int ObjectDictionary::CountElements() +{ + return size; +} + +bool ObjectDictionary::IsEmpty() +{ + return (size == 0) ? true : false; +} + +bool ObjectDictionary::Contains(void *object) +{ + if (FindObjectInCache(object) >= 0) + return true; + + for (int i = 0; i < size; i++) + { + entry_t *e = &entries[i]; + if (e->object == object) { + AddToCache(e); + return true; + } + } + + return false; +} + +void *ObjectDictionary::GetFirst() +{ + currentEntry = 0; + return GetNext(); +} + +void *ObjectDictionary::GetLast() +{ + return (size > 0) ? entries[size - 1].object : nullptr; +} + +bool ObjectDictionary::ChangeKey(void *object, float newKey) +{ + int pos = FindObjectInCache(object); + if (pos < 0) + { + for (pos = 0; pos < size; pos++) + { + if (entries[pos].object == object) { + AddToCache(&entries[pos]); + break; + } + } + + if (pos == size) { + return false; + } + } + + entry_t *p, *e; + + p = &entries[pos]; + if (p->key == newKey) + return false; + + int newpos = FindClosestAsIndex(newKey); + e = &entries[newpos]; + if (pos < newpos) + { + if (e->key > newKey) + e--; + + entry_t *e2 = &entries[pos + 1]; + while (p < e) + { + p->object = e2->object; + p->key = e2->key; + + p++; + e2++; + } + } + else if (pos > newpos) + { + if (e->key > newKey) + e++; + + entry_t *e2 = &entries[pos - 1]; + while (p > e) + { + p->object = e2->object; + p->key = e2->key; + + p--; + e2--; + } + } + + p->object = object; + p->key = newKey; + ClearCache(); + + return true; +} + +bool ObjectDictionary::UnsafeChangeKey(void *object, float newKey) +{ + int pos = FindObjectInCache(object); + if (pos < 0) + { + for (pos = 0; pos < size; pos++) + { + if (entries[pos].object == object) { + break; + } + } + + if (pos == size) { + return false; + } + } + + entries[pos].key = newKey; + ClearCache(); + return true; +} + +void ObjectDictionary::AddToCache(entry_t *entry) +{ + int i = (cacheIndex % 32); + + cache[i].object = entry; + cache[i].key = entry->key; + cacheIndex++; +} + +void ObjectDictionary::AddToCache(entry_t *entry, float key) +{ + int i = (cacheIndex % 32); + + cache[i].object = entry; + cache[i].key = key; + cacheIndex++; +} + +int ObjectDictionary::FindKeyInCache(float key) +{ + for (auto& ch : cache) + { + if (ch.object && ch.key == key) { + return (entry_t *)ch.object - entries; + } + } + + return -1; +} + +int ObjectDictionary::FindObjectInCache(void *object) +{ + for (auto& ch : cache) + { + if (ch.object && ch.object == object) { + return (entry_t *)ch.object - entries; + } + } + + return -1; +} + +void *ObjectDictionary::FindClosestKey(float key) +{ + currentEntry = FindClosestAsIndex(key); + return GetNext(); +} + +void *ObjectDictionary::GetNext() +{ + if (currentEntry < 0 || currentEntry >= size) + return nullptr; + + return &entries[currentEntry++]; +} + +void *ObjectDictionary::FindExactKey(float key) +{ + if ((currentEntry = FindClosestAsIndex(key)) < 0) + return nullptr; + + return (entries[currentEntry].key == key) ? GetNext() : nullptr; +} diff --git a/rehlds/common/ObjectDictionary.h b/rehlds/common/ObjectDictionary.h new file mode 100644 index 0000000..d72d89b --- /dev/null +++ b/rehlds/common/ObjectDictionary.h @@ -0,0 +1,73 @@ +#ifndef OBJECTDICTIONARY_H +#define OBJECTDICTIONARY_H +#ifdef _WIN32 +#pragma once +#endif + +#include "IObjectContainer.h" + +typedef struct entry_s { + void *object; + float key; +} entry_t; + +class ObjectDictionary: public IObjectContainer { +public: + ObjectDictionary(); + virtual ~ObjectDictionary(); + + void Init(); + void Init(int baseSize); + + bool Add(void *object); + bool Contains(void *object); + bool IsEmpty(); + int CountElements(); + + void Clear(bool freeObjectssMemory = false); + + bool Add(void *object, float key); + bool ChangeKey(void *object, float newKey); + bool UnsafeChangeKey(void *object, float newKey); + + bool Remove(void *object); + bool RemoveSingle(void *object); + bool RemoveKey(float key); + bool RemoveRange(float startKey, float endKey); + + void *FindClosestKey(float key); + void *FindExactKey(float key); + + void *GetFirst(); + void *GetLast(); + void *GetNext(); + + int FindKeyInCache(float key); + int FindObjectInCache(void *object); + + void ClearCache(); + bool CheckSize(); + + void AddToCache(entry_t *entry); + void AddToCache(entry_t *entry, float key); + + bool RemoveIndex(int index, bool freeObjectMemory = false); + bool RemoveIndexRange(int minIndex, int maxIndex); + int FindClosestAsIndex(float key); + +protected: + + int currentEntry; + float findKey; + + enum { MAX_OBJECT_CACHE = 32 }; + + entry_t *entries; + entry_t cache[MAX_OBJECT_CACHE]; + + int cacheIndex; + int size; + int maxSize; +}; + +#endif // OBJECTDICTIONARY_H diff --git a/rehlds/common/ObjectList.cpp b/rehlds/common/ObjectList.cpp new file mode 100644 index 0000000..58bba64 --- /dev/null +++ b/rehlds/common/ObjectList.cpp @@ -0,0 +1,232 @@ +#include "ObjectList.h" + +ObjectList::ObjectList() +{ + head = tail = current = nullptr; + number = 0; +} + +ObjectList::~ObjectList() +{ + Clear(false); +} + +bool ObjectList::AddHead(void *newObject) +{ + // create new element + element_t *newElement = (element_t *)Mem_ZeroMalloc(sizeof(element_t)); + + // out of memory + if (!newElement) + return false; + + // insert element + newElement->object = newObject; + + if (head) + { + newElement->next = head; + head->prev = newElement; + } + + head = newElement; + + // if list was empty set new tail + if (!tail) + tail = head; + + number++; + + return true; +} + +void *ObjectList::RemoveHead() +{ + void *retObj; + + // check head is present + if (head) + { + retObj = head->object; + element_t *newHead = head->next; + if (newHead) + newHead->prev = nullptr; + + // if only one element is in list also update tail + // if we remove this prev element + if (tail == head) + tail = nullptr; + + free(head); + head = newHead; + + number--; + } + else + retObj = nullptr; + + return retObj; +} + +bool ObjectList::AddTail(void *newObject) +{ + // create new element + element_t *newElement = (element_t *)Mem_ZeroMalloc(sizeof(element_t)); + + // out of memory + if (!newElement) + return false; + + // insert element + newElement->object = newObject; + + if (tail) + { + newElement->prev = tail; + tail->next = newElement; + } + + tail = newElement; + + // if list was empty set new tail + if (!head) + head = tail; + + number++; + return true; +} + +void *ObjectList::RemoveTail() +{ + void *retObj; + + // check tail is present + if (tail) + { + retObj = tail->object; + element_t *newTail = tail->prev; + if (newTail) + newTail->next = nullptr; + + // if only one element is in list also update tail + // if we remove this prev element + if (head == tail) + head = nullptr; + + free(tail); + tail = newTail; + + number--; + + } + else + retObj = nullptr; + + return retObj; +} + +bool ObjectList::IsEmpty() +{ + return (head == nullptr); +} + +int ObjectList::CountElements() +{ + return number; +} + +bool ObjectList::Contains(void *object) +{ + element_t *e = head; + + while (e && e->object != object) { e = e->next; } + + if (e) + { + current = e; + return true; + } + else + { + return false; + } +} + +void ObjectList::Clear(bool freeElementsMemory) +{ + element_t *ne; + element_t *e = head; + + while (e) + { + ne = e->next; + + if (freeElementsMemory && e->object) + free(e->object); + + free(e); + e = ne; + } + + head = tail = current = nullptr; + number = 0; +} + +bool ObjectList::Remove(void *object) +{ + element_t *e = head; + + while (e && e->object != object) { e = e->next; } + + if (e) + { + if (e->prev) e->prev->next = e->next; + if (e->next) e->next->prev = e->prev; + + if (head == e) head = e->next; + if (tail == e) tail = e->prev; + if (current == e) current= e->next; + + free(e); + number--; + } + + return (e != nullptr); +} + +void ObjectList::Init() +{ + head = tail = current = nullptr; + number = 0; +} + +void *ObjectList::GetFirst() +{ + if (head) + { + current = head->next; + return head->object; + } + else + { + current = nullptr; + return nullptr; + } +} + +void *ObjectList::GetNext() +{ + void *retObj = nullptr; + if (current) + { + retObj = current->object; + current = current->next; + } + + return retObj; +} + +bool ObjectList::Add(void *newObject) +{ + return AddTail(newObject); +} diff --git a/rehlds/common/ObjectList.h b/rehlds/common/ObjectList.h new file mode 100644 index 0000000..821a82a --- /dev/null +++ b/rehlds/common/ObjectList.h @@ -0,0 +1,43 @@ +#ifndef OBJECTLIST_H +#define OBJECTLIST_H +#ifdef _WIN32 +#pragma once +#endif + +#include "IObjectContainer.h" + +class ObjectList: public IObjectContainer { +public: + void Init(); + bool Add(void *newObject); + void *GetFirst(); + void *GetNext(); + + ObjectList(); + virtual ~ObjectList(); + + void Clear(bool freeElementsMemory = false); + int CountElements(); + void *RemoveTail(); + void *RemoveHead(); + + bool AddTail(void *newObject); + bool AddHead(void *newObject); + bool Remove(void *object); + bool Contains(void *object); + bool IsEmpty(); + + typedef struct element_s { + element_s *prev; // pointer to the last element or NULL + element_s *next; // pointer to the next elemnet or NULL + void *object; // the element's object + } element_t; + +protected: + element_t *head; // first element in list + element_t *tail; // last element in list + element_t *current; // current element in list + int number; +}; + +#endif // OBJECTLIST_H diff --git a/rehlds/common/SteamAppStartUp.cpp b/rehlds/common/SteamAppStartUp.cpp new file mode 100644 index 0000000..ae38d12 --- /dev/null +++ b/rehlds/common/SteamAppStartUp.cpp @@ -0,0 +1,181 @@ +#ifdef _WIN32 +#include "SteamAppStartup.h" + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include + +#define STEAM_PARM "-steam" + +bool FileExists(const char *fileName) +{ + struct _stat statbuf; + return (_stat(fileName, &statbuf) == 0); +} + +// Handles launching the game indirectly via steam +void LaunchSelfViaSteam(const char *params) +{ + // calculate the details of our launch + char appPath[MAX_PATH]; + ::GetModuleFileName((HINSTANCE)GetModuleHandle(NULL), appPath, sizeof(appPath)); + + // strip out the exe name + char *slash = strrchr(appPath, '\\'); + if (slash) + { + *slash = '\0'; + } + + // save out our details to the registry + HKEY hKey; + if (ERROR_SUCCESS == RegOpenKey(HKEY_CURRENT_USER, "Software\\Valve\\Steam", &hKey)) + { + DWORD dwType = REG_SZ; + DWORD dwSize = static_cast( strlen(appPath) + 1 ); + RegSetValueEx(hKey, "TempAppPath", NULL, dwType, (LPBYTE)appPath, dwSize); + dwSize = static_cast( strlen(params) + 1 ); + RegSetValueEx(hKey, "TempAppCmdLine", NULL, dwType, (LPBYTE)params, dwSize); + // clear out the appID (since we don't know it yet) + dwType = REG_DWORD; + int appID = -1; + RegSetValueEx(hKey, "TempAppID", NULL, dwType, (LPBYTE)&appID, sizeof(appID)); + RegCloseKey(hKey); + } + + // search for an active steam instance + HWND hwnd = ::FindWindow("Valve_SteamIPC_Class", "Hidden Window"); + if (hwnd) + { + ::PostMessage(hwnd, WM_USER + 3, 0, 0); + } + else + { + // couldn't find steam, find and launch it + + // first, search backwards through our current set of directories + char steamExe[MAX_PATH] = ""; + char dir[MAX_PATH]; + + if (::GetCurrentDirectoryA(sizeof(dir), dir)) + { + char *slash = strrchr(dir, '\\'); + while (slash) + { + // see if steam_dev.exe is in the directory first + slash[1] = 0; + strcat(slash, "steam_dev.exe"); + FILE *f = fopen(dir, "rb"); + if (f) + { + // found it + fclose(f); + strcpy(steamExe, dir); + break; + } + + // see if steam.exe is in the directory + slash[1] = 0; + strcat(slash, "steam.exe"); + f = fopen(dir, "rb"); + if (f) + { + // found it + fclose(f); + strcpy(steamExe, dir); + break; + } + + // kill the string at the slash + slash[0] = 0; + + // move to the previous slash + slash = strrchr(dir, '\\'); + } + } + + if (!steamExe[0]) + { + // still not found, use the one in the registry + HKEY hKey; + if (ERROR_SUCCESS == RegOpenKey(HKEY_CURRENT_USER, "Software\\Valve\\Steam", &hKey)) + { + DWORD dwType; + DWORD dwSize = sizeof(steamExe); + RegQueryValueEx(hKey, "SteamExe", NULL, &dwType, (LPBYTE)steamExe, &dwSize); + RegCloseKey(hKey); + } + } + + if (!steamExe[0]) + { + // still no path, error + ::MessageBox(NULL, "Error running game: could not find steam.exe to launch", "Fatal Error", MB_OK | MB_ICONERROR); + return; + } + + // fix any slashes + for (char *slash = steamExe; *slash; slash++) + { + if (*slash == '/') + { + *slash = '\\'; + } + } + + // change to the steam directory + strcpy(dir, steamExe); + char *delimiter = strrchr(dir, '\\'); + if (delimiter) + { + *delimiter = 0; + _chdir(dir); + } + + // exec steam.exe, in silent mode, with the launch app param + char *args[4] = { steamExe, "-silent", "-applaunch", '\0' }; + _spawnv(_P_NOWAIT, steamExe, args); + } +} + +// Launches steam if necessary +bool ShouldLaunchAppViaSteam(const char *lpCmdLine, const char *steamFilesystemDllName, const char *stdioFilesystemDllName) +{ + // see if steam is on the command line + const char *steamStr = strstr(lpCmdLine, STEAM_PARM); + + // check the character following it is a whitespace or null + if (steamStr) + { + const char *postChar = steamStr + strlen(STEAM_PARM); + if (*postChar == 0 || isspace(*postChar)) + { + // we're running under steam already, let the app continue + return false; + } + } + + // we're not running under steam, see which filesystems are available + if (FileExists(stdioFilesystemDllName)) + { + // we're being run with a stdio filesystem, so we can continue without steam + return false; + } + + // make sure we have a steam filesystem available + if (!FileExists(steamFilesystemDllName)) + { + return false; + } + + // we have the steam filesystem, and no stdio filesystem, so we must need to be run under steam + // launch steam + LaunchSelfViaSteam(lpCmdLine); + return true; +} + +#endif // _WIN32 diff --git a/rehlds/common/SteamAppStartUp.h b/rehlds/common/SteamAppStartUp.h new file mode 100644 index 0000000..c30048e --- /dev/null +++ b/rehlds/common/SteamAppStartUp.h @@ -0,0 +1,15 @@ +#ifndef STEAMAPPSTARTUP_H +#define STEAMAPPSTARTUP_H +#ifdef _WIN32 +#pragma once +#endif + +// Call this first thing at startup +// Works out if the app is a steam app that is being ran outside of steam, +// and if so, launches steam and tells it to run us as a steam app +// +// if it returns true, then exit +// if it ruturns false, then continue with normal startup +bool ShouldLaunchAppViaSteam(const char *cmdLine, const char *steamFilesystemDllName, const char *stdioFilesystemDllName); + +#endif // STEAMAPPSTARTUP_H diff --git a/rehlds/common/TextConsoleUnix.cpp b/rehlds/common/TextConsoleUnix.cpp new file mode 100644 index 0000000..411d625 --- /dev/null +++ b/rehlds/common/TextConsoleUnix.cpp @@ -0,0 +1,294 @@ +#if !defined(_WIN32) + +#include "TextConsoleUnix.h" +#include "icommandline.h" + +#include +#include +#include +#include +#include +#include +#include + +CTextConsoleUnix console; + +CTextConsoleUnix::~CTextConsoleUnix() +{ + ShutDown(); +} + +bool CTextConsoleUnix::Init(IBaseSystem *system) +{ + static struct termios termNew; + sigset_t block_ttou; + + sigemptyset(&block_ttou); + sigaddset(&block_ttou, SIGTTOU); + sigprocmask(SIG_BLOCK, &block_ttou, NULL); + + tty = stdout; + + // this code is for echo-ing key presses to the connected tty + // (which is != STDOUT) + if (isatty(STDIN_FILENO)) + { + tty = fopen(ctermid(NULL), "w+"); + if (!tty) + { + printf("Unable to open tty(%s) for output\n", ctermid(NULL)); + tty = stdout; + } + else + { + // turn buffering off + setbuf(tty, NULL); + } + } + else + { + tty = fopen("/dev/null", "w+"); + if (!tty) + { + tty = stdout; + } + } + + tcgetattr(STDIN_FILENO, &termStored); + + memcpy(&termNew, &termStored, sizeof(struct termios)); + + // Disable canonical mode, and set buffer size to 1 byte + termNew.c_lflag &= (~ICANON); + termNew.c_cc[ VMIN ] = 1; + termNew.c_cc[ VTIME ] = 0; + + // disable echo + termNew.c_lflag &= (~ECHO); + + tcsetattr(STDIN_FILENO, TCSANOW, &termNew); + sigprocmask(SIG_UNBLOCK, &block_ttou, NULL); + + return CTextConsole::Init(); +} + +void CTextConsoleUnix::ShutDown() +{ + sigset_t block_ttou; + + sigemptyset(&block_ttou); + sigaddset(&block_ttou, SIGTTOU); + sigprocmask(SIG_BLOCK, &block_ttou, NULL); + tcsetattr(STDIN_FILENO, TCSANOW, &termStored); + sigprocmask(SIG_UNBLOCK, &block_ttou, NULL); + + CTextConsole::ShutDown(); +} + +// return 0 if the kb isn't hit +int CTextConsoleUnix::kbhit() +{ + fd_set rfds; + struct timeval tv; + + // Watch stdin (fd 0) to see when it has input. + FD_ZERO(&rfds); + FD_SET(STDIN_FILENO, &rfds); + + // Return immediately. + tv.tv_sec = 0; + tv.tv_usec = 0; + + // Must be in raw or cbreak mode for this to work correctly. + return select(STDIN_FILENO + 1, &rfds, NULL, NULL, &tv) != -1 && FD_ISSET(STDIN_FILENO, &rfds); +} + +char *CTextConsoleUnix::GetLine() +{ + // early return for 99.999% case :) + if (!kbhit()) + return NULL; + + escape_sequence_t es; + + es = ESCAPE_CLEAR; + sigset_t block_ttou; + + sigemptyset(&block_ttou); + sigaddset(&block_ttou, SIGTTOU); + sigaddset(&block_ttou, SIGTTIN); + sigprocmask(SIG_BLOCK, &block_ttou, NULL); + + while (true) + { + if (!kbhit()) + break; + + int nLen; + char ch = 0; + int numRead = read(STDIN_FILENO, &ch, 1); + if (!numRead) + break; + + switch (ch) + { + case '\n': // Enter + es = ESCAPE_CLEAR; + + nLen = ReceiveNewline(); + if (nLen) + { + sigprocmask(SIG_UNBLOCK, &block_ttou, NULL); + return m_szConsoleText; + } + break; + + case 127: // Backspace + case '\b': // Backspace + es = ESCAPE_CLEAR; + ReceiveBackspace(); + break; + + case '\t': // TAB + es = ESCAPE_CLEAR; + ReceiveTab(); + break; + + case 27: // Escape character + es = ESCAPE_RECEIVED; + break; + + case '[': // 2nd part of escape sequence + case 'O': + case 'o': + switch (es) + { + case ESCAPE_CLEAR: + case ESCAPE_BRACKET_RECEIVED: + es = ESCAPE_CLEAR; + ReceiveStandardChar(ch); + break; + + case ESCAPE_RECEIVED: + es = ESCAPE_BRACKET_RECEIVED; + break; + } + break; + case 'A': + if (es == ESCAPE_BRACKET_RECEIVED) + { + es = ESCAPE_CLEAR; + ReceiveUpArrow(); + } + else + { + es = ESCAPE_CLEAR; + ReceiveStandardChar(ch); + } + break; + case 'B': + if (es == ESCAPE_BRACKET_RECEIVED) + { + es = ESCAPE_CLEAR; + ReceiveDownArrow(); + } + else + { + es = ESCAPE_CLEAR; + ReceiveStandardChar(ch); + } + break; + case 'C': + if (es == ESCAPE_BRACKET_RECEIVED) + { + es = ESCAPE_CLEAR; + ReceiveRightArrow(); + } + else + { + es = ESCAPE_CLEAR; + ReceiveStandardChar(ch); + } + break; + case 'D': + if (es == ESCAPE_BRACKET_RECEIVED) + { + es = ESCAPE_CLEAR; + ReceiveLeftArrow(); + } + else + { + es = ESCAPE_CLEAR; + ReceiveStandardChar(ch); + } + break; + default: + // Just eat this char if it's an unsupported escape + if (es != ESCAPE_BRACKET_RECEIVED) + { + // dont' accept nonprintable chars + if ((ch >= ' ') && (ch <= '~')) + { + es = ESCAPE_CLEAR; + ReceiveStandardChar(ch); + } + } + break; + } + + fflush(stdout); + } + + sigprocmask(SIG_UNBLOCK, &block_ttou, NULL); + return NULL; +} + +void CTextConsoleUnix::PrintRaw(char *pszMsg, int nChars) +{ + if (nChars == 0) + { + printf("%s", pszMsg); + } + else + { + for (int nCount = 0; nCount < nChars; nCount++) + { + putchar(pszMsg[ nCount ]); + } + } +} + +void CTextConsoleUnix::Echo(char *pszMsg, int nChars) +{ + if (nChars == 0) + { + fputs(pszMsg, tty); + } + else + { + for (int nCount = 0; nCount < nChars; nCount++) + { + fputc(pszMsg[ nCount ], tty); + } + } +} + +int CTextConsoleUnix::GetWidth() +{ + struct winsize ws; + int nWidth = 0; + + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0) + { + nWidth = (int)ws.ws_col; + } + + if (nWidth <= 1) + { + nWidth = 80; + } + + return nWidth; +} + +#endif // !defined(_WIN32) diff --git a/rehlds/common/TextConsoleUnix.h b/rehlds/common/TextConsoleUnix.h new file mode 100644 index 0000000..cd06735 --- /dev/null +++ b/rehlds/common/TextConsoleUnix.h @@ -0,0 +1,37 @@ +#ifndef TEXTCONSOLE_UNIX_H +#define TEXTCONSOLE_UNIX_H +#ifdef _WIN32 +#pragma once +#endif + +#include +#include "textconsole.h" + +enum escape_sequence_t +{ + ESCAPE_CLEAR, + ESCAPE_RECEIVED, + ESCAPE_BRACKET_RECEIVED +}; + +class CTextConsoleUnix: public CTextConsole { +public: + virtual ~CTextConsoleUnix(); + + bool Init(IBaseSystem *system = nullptr); + void ShutDown(); + void PrintRaw(char *pszMsg, int nChars = 0); + void Echo(char *pszMsg, int nChars = 0); + char *GetLine(); + int GetWidth(); + +private: + int kbhit(); + + struct termios termStored; + FILE *tty; +}; + +extern CTextConsoleUnix console; + +#endif // TEXTCONSOLE_UNIX_H diff --git a/rehlds/common/TextConsoleWin32.cpp b/rehlds/common/TextConsoleWin32.cpp new file mode 100644 index 0000000..c875740 --- /dev/null +++ b/rehlds/common/TextConsoleWin32.cpp @@ -0,0 +1,250 @@ +#if defined(_WIN32) +#include "TextConsoleWin32.h" + +CTextConsoleWin32 console; + +BOOL WINAPI ConsoleHandlerRoutine(DWORD CtrlType) +{ + // TODO ? + /*if (CtrlType != CTRL_C_EVENT && CtrlType != CTRL_BREAK_EVENT) + { + // don't quit on break or ctrl+c + m_System->Stop(); + }*/ + + return TRUE; +} + +// GetConsoleHwnd() helper function from MSDN Knowledge Base Article Q124103 +// needed, because HWND GetConsoleWindow(VOID) is not avaliable under Win95/98/ME +HWND GetConsoleHwnd() +{ + HWND hwndFound; // This is what is returned to the caller. + char pszNewWindowTitle[1024]; // Contains fabricated WindowTitle + char pszOldWindowTitle[1024]; // Contains original WindowTitle + + // Fetch current window title. + GetConsoleTitle(pszOldWindowTitle, sizeof(pszOldWindowTitle)); + + // Format a "unique" NewWindowTitle. + wsprintf(pszNewWindowTitle, "%d/%d", GetTickCount(), GetCurrentProcessId()); + + // Change current window title. + SetConsoleTitle(pszNewWindowTitle); + + // Ensure window title has been updated. + Sleep(40); + + // Look for NewWindowTitle. + hwndFound = FindWindow(nullptr, pszNewWindowTitle); + + // Restore original window title. + SetConsoleTitle(pszOldWindowTitle); + + return hwndFound; +} + +CTextConsoleWin32::~CTextConsoleWin32() +{ + ShutDown(); +} + +bool CTextConsoleWin32::Init(IBaseSystem *system) +{ + if (!AllocConsole()) + m_System = system; + + SetTitle(m_System ? m_System->GetName() : "Console"); + + hinput = GetStdHandle(STD_INPUT_HANDLE); + houtput = GetStdHandle(STD_OUTPUT_HANDLE); + + if (!SetConsoleCtrlHandler(&ConsoleHandlerRoutine, TRUE)) + { + Print("WARNING! TextConsole::Init: Could not attach console hook.\n"); + } + + Attrib = FOREGROUND_GREEN | FOREGROUND_INTENSITY | BACKGROUND_INTENSITY; + SetWindowPos(GetConsoleHwnd(), HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOREPOSITION | SWP_SHOWWINDOW); + + return CTextConsole::Init(system); +} + +void CTextConsoleWin32::ShutDown() +{ + FreeConsole(); + CTextConsole::ShutDown(); +} + +void CTextConsoleWin32::SetVisible(bool visible) +{ + ShowWindow(GetConsoleHwnd(), visible ? SW_SHOW : SW_HIDE); + m_ConsoleVisible = visible; +} + +char *CTextConsoleWin32::GetLine() +{ + while (true) + { + INPUT_RECORD recs[1024]; + unsigned long numread; + unsigned long numevents; + + if (!GetNumberOfConsoleInputEvents(hinput, &numevents)) + { + if (m_System) + m_System->Errorf("CTextConsoleWin32::GetLine: !GetNumberOfConsoleInputEvents"); + + return nullptr; + } + + if (numevents <= 0) + break; + + if (!ReadConsoleInput(hinput, recs, ARRAYSIZE(recs), &numread)) + { + if (m_System) + m_System->Errorf("CTextConsoleWin32::GetLine: !ReadConsoleInput"); + return nullptr; + } + + if (numread == 0) + return nullptr; + + for (int i = 0; i < (int)numread; i++) + { + INPUT_RECORD *pRec = &recs[i]; + if (pRec->EventType != KEY_EVENT) + continue; + + if (pRec->Event.KeyEvent.bKeyDown) + { + // check for cursor keys + if (pRec->Event.KeyEvent.wVirtualKeyCode == VK_UP) + { + ReceiveUpArrow(); + } + else if (pRec->Event.KeyEvent.wVirtualKeyCode == VK_DOWN) + { + ReceiveDownArrow(); + } + else if (pRec->Event.KeyEvent.wVirtualKeyCode == VK_LEFT) + { + ReceiveLeftArrow(); + } + else if (pRec->Event.KeyEvent.wVirtualKeyCode == VK_RIGHT) + { + ReceiveRightArrow(); + } + else + { + int nLen; + char ch = pRec->Event.KeyEvent.uChar.AsciiChar; + switch (ch) + { + case '\r': // Enter + nLen = ReceiveNewline(); + if (nLen) + { + return m_szConsoleText; + } + break; + case '\b': // Backspace + ReceiveBackspace(); + break; + case '\t': // TAB + ReceiveTab(); + break; + default: + // dont' accept nonprintable chars + if ((ch >= ' ') && (ch <= '~')) + { + ReceiveStandardChar(ch); + } + break; + } + } + } + } + } + + return nullptr; +} + +void CTextConsoleWin32::PrintRaw(char *pszMsg, int nChars) +{ +#ifdef LAUNCHER_FIXES + char outputStr[2048]; + WCHAR unicodeStr[1024]; + + DWORD nSize = MultiByteToWideChar(CP_UTF8, 0, pszMsg, -1, NULL, 0); + if (nSize > sizeof(unicodeStr)) + return; + + MultiByteToWideChar(CP_UTF8, 0, pszMsg, -1, unicodeStr, nSize); + DWORD nLength = WideCharToMultiByte(CP_OEMCP, 0, unicodeStr, -1, 0, 0, NULL, NULL); + if (nLength > sizeof(outputStr)) + return; + + WideCharToMultiByte(CP_OEMCP, 0, unicodeStr, -1, outputStr, nLength, NULL, NULL); + WriteFile(houtput, outputStr, nChars ? nChars : strlen(outputStr), NULL, NULL); +#else + WriteFile(houtput, pszMsg, nChars ? nChars : strlen(pszMsg), NULL, NULL); +#endif +} + +void CTextConsoleWin32::Echo(char *pszMsg, int nChars) +{ + PrintRaw(pszMsg, nChars); +} + +int CTextConsoleWin32::GetWidth() +{ + CONSOLE_SCREEN_BUFFER_INFO csbi; + int nWidth = 0; + + if (GetConsoleScreenBufferInfo(houtput, &csbi)) { + nWidth = csbi.dwSize.X; + } + + if (nWidth <= 1) + nWidth = 80; + + return nWidth; +} + +void CTextConsoleWin32::SetStatusLine(char *pszStatus) +{ + strncpy(statusline, pszStatus, sizeof(statusline) - 1); + statusline[sizeof statusline - 2] = '\0'; + UpdateStatus(); +} + +void CTextConsoleWin32::UpdateStatus() +{ + COORD coord; + DWORD dwWritten = 0; + WORD wAttrib[ 80 ]; + + for (int i = 0; i < 80; i++) + { + wAttrib[i] = Attrib; // FOREGROUND_GREEN | FOREGROUND_INTENSITY | BACKGROUND_INTENSITY; + } + + coord.X = coord.Y = 0; + + WriteConsoleOutputAttribute(houtput, wAttrib, 80, coord, &dwWritten); + WriteConsoleOutputCharacter(houtput, statusline, 80, coord, &dwWritten); +} + +void CTextConsoleWin32::SetTitle(char *pszTitle) +{ + SetConsoleTitle(pszTitle); +} + +void CTextConsoleWin32::SetColor(WORD attrib) +{ + Attrib = attrib; +} + +#endif // defined(_WIN32) diff --git a/rehlds/common/TextConsoleWin32.h b/rehlds/common/TextConsoleWin32.h new file mode 100644 index 0000000..5ad91b2 --- /dev/null +++ b/rehlds/common/TextConsoleWin32.h @@ -0,0 +1,39 @@ +#ifndef TEXTCONSOLE_WIN32_H +#define TEXTCONSOLE_WIN32_H +#ifdef _WIN32 +#pragma once +#endif + +#include +#include "TextConsole.h" + +class CTextConsoleWin32: public CTextConsole { +public: + virtual ~CTextConsoleWin32(); + + bool Init(IBaseSystem *system = nullptr); + void ShutDown(); + + void SetTitle(char *pszTitle); + void SetStatusLine(char *pszStatus); + void UpdateStatus(); + + void PrintRaw(char * pszMsz, int nChars = 0); + void Echo(char * pszMsz, int nChars = 0); + char *GetLine(); + int GetWidth(); + + void SetVisible(bool visible); + void SetColor(WORD); + +private: + HANDLE hinput; // standard input handle + HANDLE houtput; // standard output handle + WORD Attrib; // attrib colours for status bar + + char statusline[81]; // first line in console is status line +}; + +extern CTextConsoleWin32 console; + +#endif // TEXTCONSOLE_WIN32_H diff --git a/rehlds/common/TokenLine.cpp b/rehlds/common/TokenLine.cpp new file mode 100644 index 0000000..e74eb0b --- /dev/null +++ b/rehlds/common/TokenLine.cpp @@ -0,0 +1,131 @@ +#include "TokenLine.h" +#include + +TokenLine::TokenLine() +{ + +} + +TokenLine::TokenLine(char *string) +{ + SetLine(string); +} + +TokenLine::~TokenLine() +{ + +} + +bool TokenLine::SetLine(const char *newLine) +{ + m_tokenNumber = 0; + + if (!newLine || (strlen(newLine) >= (MAX_LINE_CHARS - 1))) + { + memset(m_fullLine, 0, MAX_LINE_CHARS); + memset(m_tokenBuffer, 0, MAX_LINE_CHARS); + return false; + } + + strncpy(m_fullLine, newLine, MAX_LINE_CHARS - 1); + m_fullLine[MAX_LINE_CHARS - 1] = '\0'; + + strncpy(m_tokenBuffer, newLine, MAX_LINE_CHARS - 1); + m_tokenBuffer[MAX_LINE_CHARS - 1] = '\0'; + + // parse tokens + char *charPointer = m_tokenBuffer; + while (*charPointer && (m_tokenNumber < MAX_LINE_TOKENS)) + { + while (*charPointer && ((*charPointer <= 32) || (*charPointer > 126))) + charPointer++; + + if (*charPointer) + { + m_token[m_tokenNumber] = charPointer; + + // special treatment for quotes + if (*charPointer == '\"') + { + charPointer++; + m_token[m_tokenNumber] = charPointer; + while (*charPointer && (*charPointer != '\"')) + charPointer++; + } + else + { + m_token[m_tokenNumber] = charPointer; + while (*charPointer && ((*charPointer > 32) && (*charPointer <= 126))) + charPointer++; + } + + m_tokenNumber++; + + if (*charPointer) + { + *charPointer = '\0'; + charPointer++; + } + } + } + + return (m_tokenNumber != MAX_LINE_TOKENS); +} + +char *TokenLine::GetLine() +{ + return m_fullLine; +} + +char *TokenLine::GetToken(int i) +{ + if (i >= m_tokenNumber) + return NULL; + + return m_token[i]; +} + +// if the given parm is not present return NULL +// otherwise return the address of the following token, or an empty string +char *TokenLine::CheckToken(char *parm) +{ + for (int i = 0; i < m_tokenNumber; i++) + { + if (!m_token[i]) + continue; + + if (!strcmp(parm, m_token[i])) + { + char *ret = m_token[i + 1]; + + // if this token doesn't exist, since index i was the last + // return an empty string + if (m_tokenNumber == (i + 1)) + ret = ""; + + return ret; + } + } + + return NULL; +} + +int TokenLine::CountToken() +{ + int c = 0; + for (int i = 0; i < m_tokenNumber; i++) + { + if (m_token[i]) + c++; + } + + return c; +} + +char *TokenLine::GetRestOfLine(int i) +{ + if (i >= m_tokenNumber) + return NULL; + + return m_fullLine + (m_token[i] - m_tokenBuffer); +} diff --git a/rehlds/common/TokenLine.h b/rehlds/common/TokenLine.h new file mode 100644 index 0000000..dfa1792 --- /dev/null +++ b/rehlds/common/TokenLine.h @@ -0,0 +1,29 @@ +#ifndef TOKENLINE_H +#define TOKENLINE_H +#ifdef _WIN32 +#pragma once +#endif + +class TokenLine { +public: + TokenLine(); + TokenLine(char *string); + virtual ~TokenLine(); + + char *GetRestOfLine(int i); // returns all chars after token i + int CountToken(); // returns number of token + char *CheckToken(char *parm); // returns token after token parm or "" + char *GetToken(int i); // returns token i + char *GetLine(); // returns full line + bool SetLine(const char *newLine); // set new token line and parses it + +private: + enum { MAX_LINE_CHARS = 2048, MAX_LINE_TOKENS = 128 }; + + char m_tokenBuffer[MAX_LINE_CHARS]; + char m_fullLine[MAX_LINE_CHARS]; + char *m_token[MAX_LINE_TOKENS]; + int m_tokenNumber; +}; + +#endif // TOKENLINE_H diff --git a/rehlds/common/netapi.cpp b/rehlds/common/netapi.cpp new file mode 100644 index 0000000..7c69449 --- /dev/null +++ b/rehlds/common/netapi.cpp @@ -0,0 +1,208 @@ +#include +#include + +#ifdef _WIN32 + #include "winsock.h" +#else + #include + #include + #include + #include + #include + #include +#endif // _WIN32 + +#include "netapi.h" + +class CNetAPI: public INetAPI { +public: + virtual void NetAdrToSockAddr(netadr_t *a, struct sockaddr *s); + virtual void SockAddrToNetAdr(struct sockaddr *s, netadr_t *a); + + virtual char *AdrToString(netadr_t *a); + virtual bool StringToAdr(const char *s, netadr_t *a); + + virtual void GetSocketAddress(int socket, netadr_t *a); + virtual bool CompareAdr(netadr_t *a, netadr_t *b); + virtual void GetLocalIP(netadr_t *a); +}; + +// Expose interface +CNetAPI g_NetAPI; +INetAPI *net = (INetAPI *)&g_NetAPI; + +void CNetAPI::NetAdrToSockAddr(netadr_t *a, struct sockaddr *s) +{ + memset(s, 0, sizeof(*s)); + + if (a->type == NA_BROADCAST) + { + ((struct sockaddr_in *)s)->sin_family = AF_INET; + ((struct sockaddr_in *)s)->sin_port = a->port; + ((struct sockaddr_in *)s)->sin_addr.s_addr = INADDR_BROADCAST; + } + else if (a->type == NA_IP) + { + ((struct sockaddr_in *)s)->sin_family = AF_INET; + ((struct sockaddr_in *)s)->sin_addr.s_addr = *(int *)&a->ip; + ((struct sockaddr_in *)s)->sin_port = a->port; + } +} + +void CNetAPI::SockAddrToNetAdr(struct sockaddr *s, netadr_t *a) +{ + if (s->sa_family == AF_INET) + { + a->type = NA_IP; + *(int *)&a->ip = ((struct sockaddr_in *)s)->sin_addr.s_addr; + a->port = ((struct sockaddr_in *)s)->sin_port; + } +} + +char *CNetAPI::AdrToString(netadr_t *a) +{ + static char s[64]; + memset(s, 0, sizeof(s)); + + if (a) + { + if (a->type == NA_LOOPBACK) + { + sprintf(s, "loopback"); + } + else if (a->type == NA_IP) + { + sprintf(s, "%i.%i.%i.%i:%i", a->ip[0], a->ip[1], a->ip[2], a->ip[3], ntohs(a->port)); + } + } + + return s; +} + +bool StringToSockaddr(const char *s, struct sockaddr *sadr) +{ + struct hostent *h; + char *colon; + char copy[128]; + struct sockaddr_in *p; + + memset(sadr, 0, sizeof(*sadr)); + + p = (struct sockaddr_in *)sadr; + p->sin_family = AF_INET; + p->sin_port = 0; + + strcpy(copy, s); + + // strip off a trailing :port if present + for (colon = copy ; *colon ; colon++) + { + if (*colon == ':') + { + // terminate + *colon = '\0'; + + // Start at next character + p->sin_port = htons((short)atoi(colon + 1)); + } + } + + // Numeric IP, no DNS + if (copy[0] >= '0' && copy[0] <= '9' && strstr(copy, ".")) + { + *(int *)&p->sin_addr = inet_addr(copy); + } + else + { + // DNS it + if (!(h = gethostbyname(copy))) + { + return false; + } + + // Use first result + *(int *)&p->sin_addr = *(int *)h->h_addr_list[0]; + } + + return true; +} + +bool CNetAPI::StringToAdr(const char *s, netadr_t *a) +{ + struct sockaddr sadr; + if (!strcmp(s, "localhost")) + { + memset(a, 0, sizeof(*a)); + a->type = NA_LOOPBACK; + return true; + } + + if (!StringToSockaddr(s, &sadr)) + { + return false; + } + + SockAddrToNetAdr(&sadr, a); + return true; +} + +// Lookup the IP address for the specified IP socket +void CNetAPI::GetSocketAddress(int socket, netadr_t *a) +{ + char buff[512]; + struct sockaddr_in address; + int namelen; + + memset(a, 0, sizeof(*a)); + gethostname(buff, sizeof(buff)); + + // Ensure that it doesn't overrun the buffer + buff[sizeof buff - 1] = '\0'; + StringToAdr(buff, a); + + namelen = sizeof(address); + if (getsockname(socket, (struct sockaddr *)&address, (int *)&namelen) == 0) + { + a->port = address.sin_port; + } +} + +bool CNetAPI::CompareAdr(netadr_t *a, netadr_t *b) +{ + if (a->type != b->type) + { + return false; + } + + if (a->type == NA_LOOPBACK) + { + return true; + } + + if (a->type == NA_IP && + a->ip[0] == b->ip[0] && + a->ip[1] == b->ip[1] && + a->ip[2] == b->ip[2] && + a->ip[3] == b->ip[3] && + a->port == b->port) + { + return true; + } + + return false; +} + +void CNetAPI::GetLocalIP(netadr_t *a) +{ + char s[64]; + if(!::gethostname(s, 64)) + { + struct hostent *localip = ::gethostbyname(s); + if(localip) + { + a->type = NA_IP; + a->port = 0; + memcpy(a->ip, localip->h_addr_list[0], 4); + } + } +} diff --git a/rehlds/common/netapi.h b/rehlds/common/netapi.h new file mode 100644 index 0000000..8401c92 --- /dev/null +++ b/rehlds/common/netapi.h @@ -0,0 +1,25 @@ +#ifndef NETAPI_H +#define NETAPI_H +#ifdef _WIN32 +#pragma once +#endif + +#include "netadr.h" + +class INetAPI { +public: + virtual void NetAdrToSockAddr(netadr_t *a, struct sockaddr *s) = 0; // Convert a netadr_t to sockaddr + virtual void SockAddrToNetAdr(struct sockaddr *s, netadr_t *a) = 0; // Convert a sockaddr to netadr_t + + virtual char *AdrToString(netadr_t *a) = 0; // Convert a netadr_t to a string + virtual bool StringToAdr(const char *s, netadr_t *a) = 0; // Convert a string address to a netadr_t, doing DNS if needed + virtual void GetSocketAddress(int socket, netadr_t *a) = 0; // Look up IP address for socket + virtual bool CompareAdr(netadr_t *a, netadr_t *b) = 0; + + // return the IP of the local host + virtual void GetLocalIP(netadr_t *a) = 0; +}; + +extern INetAPI *net; + +#endif // NETAPI_H diff --git a/rehlds/common/textconsole.cpp b/rehlds/common/textconsole.cpp new file mode 100644 index 0000000..929784a --- /dev/null +++ b/rehlds/common/textconsole.cpp @@ -0,0 +1,344 @@ +#include +#include + +#include "textconsole.h" +#include "ObjectList.h" + +bool CTextConsole::Init(IBaseSystem *system) +{ + // NULL or a valid base system interface + m_System = system; + + memset(m_szConsoleText, 0, sizeof(m_szConsoleText)); + m_nConsoleTextLen = 0; + m_nCursorPosition = 0; + + memset(m_szSavedConsoleText, 0, sizeof(m_szSavedConsoleText)); + m_nSavedConsoleTextLen = 0; + + memset(m_aszLineBuffer, 0, sizeof(m_aszLineBuffer)); + m_nTotalLines = 0; + m_nInputLine = 0; + m_nBrowseLine = 0; + + // these are log messages, not related to console + Sys_Printf("\n"); + Sys_Printf("Console initialized.\n"); + + m_ConsoleVisible = true; + + return true; +} + +void CTextConsole::SetVisible(bool visible) +{ + m_ConsoleVisible = visible; +} + +bool CTextConsole::IsVisible() +{ + return m_ConsoleVisible; +} + +void CTextConsole::ShutDown() +{ + ; +} + +void CTextConsole::Print(char *pszMsg) +{ + if (m_nConsoleTextLen) + { + int nLen = m_nConsoleTextLen; + while (nLen--) + { + PrintRaw("\b \b"); + } + } + + PrintRaw(pszMsg); + + if (m_nConsoleTextLen) + { + PrintRaw(m_szConsoleText, m_nConsoleTextLen); + } + + UpdateStatus(); +} + +int CTextConsole::ReceiveNewline() +{ + int nLen = 0; + + Echo("\n"); + + if (m_nConsoleTextLen) + { + nLen = m_nConsoleTextLen; + + m_szConsoleText[ m_nConsoleTextLen ] = '\0'; + m_nConsoleTextLen = 0; + m_nCursorPosition = 0; + + // cache line in buffer, but only if it's not a duplicate of the previous line + if ((m_nInputLine == 0) || (strcmp(m_aszLineBuffer[ m_nInputLine - 1 ], m_szConsoleText))) + { + strncpy(m_aszLineBuffer[ m_nInputLine ], m_szConsoleText, MAX_CONSOLE_TEXTLEN); + m_nInputLine++; + + if (m_nInputLine > m_nTotalLines) + m_nTotalLines = m_nInputLine; + + if (m_nInputLine >= MAX_BUFFER_LINES) + m_nInputLine = 0; + + } + + m_nBrowseLine = m_nInputLine; + } + + return nLen; +} + +void CTextConsole::ReceiveBackspace() +{ + int nCount; + + if (m_nCursorPosition == 0) + { + return; + } + + m_nConsoleTextLen--; + m_nCursorPosition--; + + Echo("\b"); + + for (nCount = m_nCursorPosition; nCount < m_nConsoleTextLen; ++nCount) + { + m_szConsoleText[ nCount ] = m_szConsoleText[ nCount + 1 ]; + Echo(m_szConsoleText + nCount, 1); + } + + Echo(" "); + + nCount = m_nConsoleTextLen; + while (nCount >= m_nCursorPosition) + { + Echo("\b"); + nCount--; + } + + m_nBrowseLine = m_nInputLine; +} + +void CTextConsole::ReceiveTab() +{ + if (!m_System) + return; + + ObjectList matches; + m_szConsoleText[ m_nConsoleTextLen ] = '\0'; + m_System->GetCommandMatches(m_szConsoleText, &matches); + + if (matches.IsEmpty()) + return; + + if (matches.CountElements() == 1) + { + char *pszCmdName = (char *)matches.GetFirst(); + char *pszRest = pszCmdName + strlen(m_szConsoleText); + + if (pszRest) + { + Echo(pszRest); + strcat(m_szConsoleText, pszRest); + m_nConsoleTextLen += strlen(pszRest); + + Echo(" "); + strcat(m_szConsoleText, " "); + m_nConsoleTextLen++; + } + } + else + { + int nLongestCmd = 0; + int nCurrentColumn; + int nTotalColumns; + + char *pszCurrentCmd = (char *)matches.GetFirst(); + while (pszCurrentCmd) + { + if ((int)strlen(pszCurrentCmd) > nLongestCmd) + nLongestCmd = strlen(pszCurrentCmd); + + pszCurrentCmd = (char *)matches.GetNext(); + } + + nTotalColumns = (GetWidth() - 1) / (nLongestCmd + 1); + nCurrentColumn = 0; + + Echo("\n"); + + // Would be nice if these were sorted, but not that big a deal + pszCurrentCmd = (char *)matches.GetFirst(); + + while (pszCurrentCmd) + { + char szFormatCmd[256]; + if (++nCurrentColumn > nTotalColumns) + { + Echo("\n"); + nCurrentColumn = 1; + } + + _snprintf(szFormatCmd, sizeof(szFormatCmd), "%-*s ", nLongestCmd, pszCurrentCmd); + Echo(szFormatCmd); + + pszCurrentCmd = (char *)matches.GetNext(); + } + + Echo("\n"); + Echo(m_szConsoleText); + + // TODO: + // Tack on 'common' chars in all the matches, i.e. if I typed 'con' and all the + // matches begin with 'connect_' then print the matches but also complete the + // command up to that point at least. + } + + m_nCursorPosition = m_nConsoleTextLen; + m_nBrowseLine = m_nInputLine; +} + +void CTextConsole::ReceiveStandardChar(const char ch) +{ + int nCount; + + // If the line buffer is maxed out, ignore this char + if (m_nConsoleTextLen >= (sizeof(m_szConsoleText) - 2)) + { + return; + } + + nCount = m_nConsoleTextLen; + while (nCount > m_nCursorPosition) + { + m_szConsoleText[ nCount ] = m_szConsoleText[ nCount - 1 ]; + nCount--; + } + + m_szConsoleText[ m_nCursorPosition ] = ch; + + Echo(m_szConsoleText + m_nCursorPosition, m_nConsoleTextLen - m_nCursorPosition + 1); + + m_nConsoleTextLen++; + m_nCursorPosition++; + + nCount = m_nConsoleTextLen; + while (nCount > m_nCursorPosition) + { + Echo("\b"); + nCount--; + } + + m_nBrowseLine = m_nInputLine; +} + +void CTextConsole::ReceiveUpArrow() +{ + int nLastCommandInHistory = m_nInputLine + 1; + if (nLastCommandInHistory > m_nTotalLines) + nLastCommandInHistory = 0; + + if (m_nBrowseLine == nLastCommandInHistory) + return; + + if (m_nBrowseLine == m_nInputLine) + { + if (m_nConsoleTextLen > 0) + { + // Save off current text + strncpy(m_szSavedConsoleText, m_szConsoleText, m_nConsoleTextLen); + // No terminator, it's a raw buffer we always know the length of + } + + m_nSavedConsoleTextLen = m_nConsoleTextLen; + } + + m_nBrowseLine--; + if (m_nBrowseLine < 0) + { + m_nBrowseLine = m_nTotalLines - 1; + } + + // delete old line + while (m_nConsoleTextLen--) + { + Echo("\b \b"); + } + + // copy buffered line + Echo(m_aszLineBuffer[ m_nBrowseLine ]); + + strncpy(m_szConsoleText, m_aszLineBuffer[ m_nBrowseLine ], MAX_CONSOLE_TEXTLEN); + + m_nConsoleTextLen = strlen(m_aszLineBuffer[ m_nBrowseLine ]); + m_nCursorPosition = m_nConsoleTextLen; +} + +void CTextConsole::ReceiveDownArrow() +{ + if (m_nBrowseLine == m_nInputLine) + return; + + if (++m_nBrowseLine > m_nTotalLines) + m_nBrowseLine = 0; + + // delete old line + while (m_nConsoleTextLen--) + { + Echo("\b \b"); + } + + if (m_nBrowseLine == m_nInputLine) + { + if (m_nSavedConsoleTextLen > 0) + { + // Restore current text + strncpy(m_szConsoleText, m_szSavedConsoleText, m_nSavedConsoleTextLen); + // No terminator, it's a raw buffer we always know the length of + + Echo(m_szConsoleText, m_nSavedConsoleTextLen); + } + + m_nConsoleTextLen = m_nSavedConsoleTextLen; + } + else + { + // copy buffered line + Echo(m_aszLineBuffer[ m_nBrowseLine ]); + strncpy(m_szConsoleText, m_aszLineBuffer[ m_nBrowseLine ], MAX_CONSOLE_TEXTLEN); + m_nConsoleTextLen = strlen(m_aszLineBuffer[ m_nBrowseLine ]); + } + + m_nCursorPosition = m_nConsoleTextLen; +} + +void CTextConsole::ReceiveLeftArrow() +{ + if (m_nCursorPosition == 0) + return; + + Echo("\b"); + m_nCursorPosition--; +} + +void CTextConsole::ReceiveRightArrow() +{ + if (m_nCursorPosition == m_nConsoleTextLen) + return; + + Echo(m_szConsoleText + m_nCursorPosition, 1); + m_nCursorPosition++; +} diff --git a/rehlds/common/textconsole.h b/rehlds/common/textconsole.h new file mode 100644 index 0000000..e205f17 --- /dev/null +++ b/rehlds/common/textconsole.h @@ -0,0 +1,71 @@ +#ifndef TEXTCONSOLE_H +#define TEXTCONSOLE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "IBaseSystem.h" + +#define MAX_CONSOLE_TEXTLEN 256 +#define MAX_BUFFER_LINES 30 + +class CTextConsole { +public: + virtual ~CTextConsole() {} + + virtual bool Init(IBaseSystem *system = nullptr); + virtual void ShutDown(); + virtual void Print(char *pszMsg); + + virtual void SetTitle(char *pszTitle) {} + virtual void SetStatusLine(char *pszStatus) {} + virtual void UpdateStatus() {} + + // Must be provided by children + virtual void PrintRaw(char *pszMsg, int nChars = 0) = 0; + virtual void Echo(char *pszMsg, int nChars = 0) = 0; + virtual char *GetLine() = 0; + virtual int GetWidth() = 0; + + virtual void SetVisible(bool visible); + virtual bool IsVisible(); + +protected: + char m_szConsoleText[MAX_CONSOLE_TEXTLEN]; // console text buffer + int m_nConsoleTextLen; // console textbuffer length + int m_nCursorPosition; // position in the current input line + + // Saved input data when scrolling back through command history + char m_szSavedConsoleText[MAX_CONSOLE_TEXTLEN]; // console text buffer + int m_nSavedConsoleTextLen; // console textbuffer length + + char m_aszLineBuffer[MAX_BUFFER_LINES][MAX_CONSOLE_TEXTLEN]; // command buffer last MAX_BUFFER_LINES commands + int m_nInputLine; // Current line being entered + int m_nBrowseLine; // current buffer line for up/down arrow + int m_nTotalLines; // # of nonempty lines in the buffer + + bool m_ConsoleVisible; + + IBaseSystem *m_System; + + int ReceiveNewline(); + void ReceiveBackspace(); + void ReceiveTab(); + void ReceiveStandardChar(const char ch); + void ReceiveUpArrow(); + void ReceiveDownArrow(); + void ReceiveLeftArrow(); + void ReceiveRightArrow(); +}; + +#include "SteamAppStartUp.h" + +#if defined(_WIN32) + #include "TextConsoleWin32.h" +#else + #include "TextConsoleUnix.h" +#endif // defined(_WIN32) + +void Sys_Printf(char *fmt, ...); + +#endif // TEXTCONSOLE_H diff --git a/rehlds/dedicated/build.gradle b/rehlds/dedicated/build.gradle new file mode 100644 index 0000000..47aff3a --- /dev/null +++ b/rehlds/dedicated/build.gradle @@ -0,0 +1,137 @@ +import org.doomedsociety.gradlecpp.cfg.ToolchainConfigUtils +import org.doomedsociety.gradlecpp.msvc.MsvcToolchainConfig +import org.doomedsociety.gradlecpp.gcc.GccToolchainConfig +import org.doomedsociety.gradlecpp.toolchain.icc.Icc +import org.doomedsociety.gradlecpp.toolchain.icc.IccCompilerPlugin +import org.doomedsociety.gradlecpp.GradleCppUtils +import org.gradle.nativeplatform.NativeExecutableSpec +import org.gradle.nativeplatform.NativeExecutableBinarySpec + +apply plugin: 'cpp' +apply plugin: 'windows-resources' +apply plugin: IccCompilerPlugin + +List getRcCompileTasks(NativeBinarySpec binary) +{ + def linkTask = GradleCppUtils.getLinkTask(binary) + def res = linkTask.taskDependencies.getDependencies(linkTask).findAll { Task t -> t instanceof WindowsResourceCompile } + return res as List +} + +void setupToolchain(NativeBinarySpec b) { + def cfg = rootProject.createToolchainConfig(b); + cfg.projectInclude(project, '/src', '/../engine', '/../common', '/../public', '/../public/rehlds'); + + cfg.singleDefines 'USE_BREAKPAD_HANDLER', 'DEDICATED', 'LAUNCHER_FIXES', '_CONSOLE' + + if (cfg instanceof MsvcToolchainConfig) { + cfg.compilerOptions.pchConfig = new MsvcToolchainConfig.PrecompiledHeadersConfig( + enabled: true, + pchHeader: 'precompiled.h', + pchSourceSet: 'dedicated_pch' + ); + + cfg.singleDefines('_CRT_SECURE_NO_WARNINGS'); + cfg.compilerOptions.args '/Ob2', '/Oi', '/GF', '/GR-'; + cfg.extraLibs "ws2_32.lib", "winmm.lib", "user32.lib", "advapi32.lib", "shell32.lib" + } + else if (cfg instanceof GccToolchainConfig) { + cfg.compilerOptions.pchConfig = new GccToolchainConfig.PrecompilerHeaderOptions( + enabled: true, + pchSourceSet: 'dedicated_pch' + ); + + cfg.compilerOptions.languageStandard = 'c++0x' + cfg.defines([ + '_strdup': 'strdup', + '_stricmp': 'strcasecmp', + '_strnicmp': 'strncasecmp', + '_vsnprintf': 'vsnprintf', + '_snprintf': 'snprintf', + ]); + + cfg.linkerOptions.stripSymbolTable = false; + cfg.linkerOptions.staticLibStdCpp = false; + cfg.compilerOptions.args '-Qoption,cpp,--treat_func_as_string_literal_cpp', '-fno-rtti', '-fno-exceptions' + } + + ToolchainConfigUtils.apply(project, cfg, b); +} + +model { + buildTypes { + release + } + + platforms { + x86 { + architecture "x86" + } + } + + toolChains { + visualCpp(VisualCpp) { + } + icc(Icc) { + } + } + + components { + dedicated(NativeExecutableSpec) { + targetPlatform 'x86' + baseName GradleCppUtils.windows ? 'hlds' : 'hlds_linux' + + sources { + dedicated_main(CppSourceSet) { + source { + srcDir "src" + include "**/*.cpp" + exclude "precompiled.cpp" + + if (GradleCppUtils.windows) { + exclude "sys_linux.cpp" + } else { + exclude "sys_window.cpp", "conproc.cpp" + } + } + + exportedHeaders { + srcDirs "vgui" + } + } + dedicated_pch(CppSourceSet) { + source { + srcDir "src" + include "precompiled.cpp" + } + } + + rc { + source { + srcDir "msvc" + include "dedicated.rc" + } + exportedHeaders { + srcDirs "msvc" + } + } + } + + binaries.all { + NativeExecutableBinarySpec b -> project.setupToolchain(b) + } + } + } +} + +task buildFixes { + dependsOn binaries.withType(NativeExecutableBinarySpec).matching { NativeExecutableBinarySpec blib -> + blib.buildable && blib.buildType.name == 'release' + } +} + +task buildRelease { + dependsOn binaries.withType(NativeExecutableBinarySpec).matching { NativeExecutableBinarySpec blib -> + blib.buildable && blib.buildType.name == 'release' + } +} diff --git a/rehlds/dedicated/msvc/PostBuild.bat b/rehlds/dedicated/msvc/PostBuild.bat new file mode 100644 index 0000000..8583878 --- /dev/null +++ b/rehlds/dedicated/msvc/PostBuild.bat @@ -0,0 +1,39 @@ +@echo OFF +:: +:: Post-build auto-deploy script +:: Create and fill PublishPath.txt file with path to deployment folder +:: I.e. PublishPath.txt should contain one line with a folder path +:: Call it so: +:: IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") +:: + +SET targetDir=%~1 +SET targetName=%~2 +SET targetExt=%~3 +SET projectDir=%~4 +SET destination= + +IF NOT EXIST "%projectDir%\PublishPath.txt" ( + ECHO No deployment path specified. Create PublishPath.txt near PostBuild.bat with paths on separate lines for auto deployment. + exit /B 0 +) + +FOR /f "tokens=* delims= usebackq" %%a IN ("%projectDir%\PublishPath.txt") DO ( + ECHO Deploying to: %%a + IF NOT "%%a" == "" ( + copy /Y "%targetDir%%targetName%%targetExt%" "%%a" + IF NOT ERRORLEVEL 1 ( + IF EXIST "%targetDir%%targetName%.pdb" ( + copy /Y "%targetDir%%targetName%.pdb" "%%a" + ) + ) ELSE ( + ECHO PostBuild.bat ^(27^) : warning : Can't copy '%targetName%%targetExt%' to deploy path '%%a' + ) + ) +) + +IF "%%a" == "" ( + ECHO No deployment path specified. +) + +exit /B 0 \ No newline at end of file diff --git a/rehlds/dedicated/msvc/dedicated.rc b/rehlds/dedicated/msvc/dedicated.rc new file mode 100644 index 0000000..94af5ed Binary files /dev/null and b/rehlds/dedicated/msvc/dedicated.rc differ diff --git a/rehlds/dedicated/msvc/dedicated.sln b/rehlds/dedicated/msvc/dedicated.sln new file mode 100644 index 0000000..bd19320 --- /dev/null +++ b/rehlds/dedicated/msvc/dedicated.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dedicated", "dedicated.vcxproj", "{D49883F3-5C5C-4B9F-B9C7-B31518F228D4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D49883F3-5C5C-4B9F-B9C7-B31518F228D4}.Debug|Win32.ActiveCfg = Debug|Win32 + {D49883F3-5C5C-4B9F-B9C7-B31518F228D4}.Debug|Win32.Build.0 = Debug|Win32 + {D49883F3-5C5C-4B9F-B9C7-B31518F228D4}.Release|Win32.ActiveCfg = Release|Win32 + {D49883F3-5C5C-4B9F-B9C7-B31518F228D4}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/rehlds/dedicated/msvc/dedicated.vcxproj b/rehlds/dedicated/msvc/dedicated.vcxproj new file mode 100644 index 0000000..c0e2592 --- /dev/null +++ b/rehlds/dedicated/msvc/dedicated.vcxproj @@ -0,0 +1,178 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {D49883F3-5C5C-4B9F-B9C7-B31518F228D4} + Win32Proj + HLDSLauncher + dedicated + 8.1 + + + + Application + true + v140_xp + MultiByte + + + Application + false + v140_xp + true + MultiByte + + + + + + + + + + + + + true + hlds + + + false + hlds + + + + Use + Level3 + Disabled + WIN32;LAUNCHER_FIXES;_DEBUG;DEDICATED;_CRT_SECURE_NO_WARNINGS;USE_BREAKPAD_HANDLER;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + MultiThreadedDebug + $(ProjectDir)..\;$(ProjectDir)..\src\;$(ProjectDir)..\..\common;$(ProjectDir)..\..\engine;$(ProjectDir)..\..\public;$(ProjectDir)..\..\public\rehlds;%(AdditionalIncludeDirectories) + precompiled.h + + + Windows + true + ws2_32.lib;winmm.lib;%(AdditionalDependencies) + + + IF EXIST "$(ProjectDir)PreBuild.bat" (CALL "$(ProjectDir)PreBuild.bat" "$(ProjectDir)..\version\" "$(ProjectDir)..\") + + + Setup version from Git revision + + + IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") + + + Automatic deployment script + + + echo Empty Action + + + Force build to run Pre-Build event + + + git.always.run + + + git.always.run + + + + + Level3 + Use + Full + true + true + WIN32;LAUNCHER_FIXES;NDEBUG;DEDICATED;_CRT_SECURE_NO_WARNINGS;USE_BREAKPAD_HANDLER;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + $(ProjectDir)..\;$(ProjectDir)..\..\common;$(ProjectDir)..\..\engine;$(ProjectDir)..\..\public;$(ProjectDir)..\..\public\rehlds;%(AdditionalIncludeDirectories) + MultiThreaded + false + StreamingSIMDExtensions2 + + + AnySuitable + true + true + precompiled.h + + + Windows + true + true + true + ws2_32.lib;winmm.lib;%(AdditionalDependencies) + + + IF EXIST "$(ProjectDir)PreBuild.bat" (CALL "$(ProjectDir)PreBuild.bat" "$(ProjectDir)..\version\" "$(ProjectDir)..\") + + + Setup version from Git revision + + + IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") + + + Automatic deployment script + + + echo Empty Action + + + subversion.always.run + + + subversion.always.run + Force build to run Pre-Build event + + + + + + + + + + + + + + + + + + + + + Create + Create + + + + + true + true + + + + + + + + + + + \ No newline at end of file diff --git a/rehlds/dedicated/msvc/dedicated.vcxproj.filters b/rehlds/dedicated/msvc/dedicated.vcxproj.filters new file mode 100644 index 0000000..a6dcaad --- /dev/null +++ b/rehlds/dedicated/msvc/dedicated.vcxproj.filters @@ -0,0 +1,71 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {711de26d-f116-48c0-96c0-5d4189574ad1} + + + + + + src + + + src + + + src + + + src + + + src + + + src\vgui + + + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src\vgui + + + src + + + + + + + + + \ No newline at end of file diff --git a/rehlds/dedicated/msvc/icon.ico b/rehlds/dedicated/msvc/icon.ico new file mode 100644 index 0000000..8b080d2 Binary files /dev/null and b/rehlds/dedicated/msvc/icon.ico differ diff --git a/rehlds/dedicated/msvc/resource.h b/rehlds/dedicated/msvc/resource.h new file mode 100644 index 0000000..8a97719 Binary files /dev/null and b/rehlds/dedicated/msvc/resource.h differ diff --git a/rehlds/dedicated/src/commandline.cpp b/rehlds/dedicated/src/commandline.cpp new file mode 100644 index 0000000..86e0b25 --- /dev/null +++ b/rehlds/dedicated/src/commandline.cpp @@ -0,0 +1,354 @@ +#include "precompiled.h" + +class CCommandLine: public ICommandLine { +public: + CCommandLine(); + virtual ~CCommandLine(); + + void CreateCmdLine(const char *commandline); + void CreateCmdLine(int argc, const char *argv[]); + const char *GetCmdLine() const; + + // Check whether a particular parameter exists + const char *CheckParm(const char *psz, char **ppszValue = nullptr) const; + void RemoveParm(const char *pszParm); + void AppendParm(const char *pszParm, const char *pszValues); + + void SetParm(const char *pszParm, const char *pszValues); + void SetParm(const char *pszParm, int iValue); + + // When the commandline contains @name, it reads the parameters from that file + void LoadParametersFromFile(const char *&pSrc, char *&pDst, int maxDestLen); + +private: + // Copy of actual command line + char *m_pszCmdLine; +}; + +CCommandLine g_CmdLine; +ICommandLine *cmdline = &g_CmdLine; + +ICommandLine *CommandLine() +{ + return &g_CmdLine; +} + +CCommandLine::CCommandLine() +{ + m_pszCmdLine = nullptr; +} + +CCommandLine::~CCommandLine() +{ + if (m_pszCmdLine) + { + delete [] m_pszCmdLine; + m_pszCmdLine = nullptr; + } +} + +char *CopyString(const char *src) +{ + if (!src) + return nullptr; + + char *out = (char *)new char[strlen(src) + 1]; + strcpy(out, src); + return out; +} + +// Creates a command line from the arguments passed in +void CCommandLine::CreateCmdLine(int argc, const char *argv[]) +{ + char cmdline[4096] = ""; + const int MAX_CHARS = sizeof(cmdline) - 1; + + for (int i = 0; i < argc; ++i) + { + if (strchr(argv[i], ' ')) + { + strncat(cmdline, "\"", MAX_CHARS); + strncat(cmdline, argv[i], MAX_CHARS); + strncat(cmdline, "\"", MAX_CHARS); + } + else + { + strncat(cmdline, argv[i], MAX_CHARS); + } + + strncat(cmdline, " ", MAX_CHARS); + } + + cmdline[strlen(cmdline)] = '\0'; + CreateCmdLine(cmdline); +} + +void CCommandLine::LoadParametersFromFile(const char *&pSrc, char *&pDst, int maxDestLen) +{ + // Suck out the file name + char szFileName[ MAX_PATH ]; + char *pOut; + char *pDestStart = pDst; + + // Skip the @ sign + pSrc++; + pOut = szFileName; + + while (*pSrc && *pSrc != ' ') + { + *pOut++ = *pSrc++; +#if 0 + if ((pOut - szFileName) >= (MAX_PATH - 1)) + break; +#endif + } + + *pOut = '\0'; + + // Skip the space after the file name + if (*pSrc) + pSrc++; + + // Now read in parameters from file + FILE *fp = fopen(szFileName, "r"); + if (fp) + { + char c; + c = (char)fgetc(fp); + while (c != EOF) + { + // Turn return characters into spaces + if (c == '\n') + c = ' '; + + *pDst++ = c; + +#if 0 + // Don't go past the end, and allow for our terminating space character AND a terminating null character. + if ((pDst - pDestStart) >= (maxDestLen - 2)) + break; +#endif + + // Get the next character, if there are more + c = (char)fgetc(fp); + } + + // Add a terminating space character + *pDst++ = ' '; + + fclose(fp); + } + else + { + printf("Parameter file '%s' not found, skipping...", szFileName); + } +} + +// Purpose: Create a command line from the passed in string +// Note that if you pass in a @filename, then the routine will read settings from a file instead of the command line +void CCommandLine::CreateCmdLine(const char *commandline) +{ + if (m_pszCmdLine) + { + delete[] m_pszCmdLine; + m_pszCmdLine = nullptr; + } + + char szFull[4096]; + + char *pDst = szFull; + const char *pSrc = commandline; + + bool allowAtSign = true; + + while (*pSrc) + { + if (*pSrc == '@') + { + if (allowAtSign) + { + LoadParametersFromFile(pSrc, pDst, sizeof(szFull) - (pDst - szFull)); + continue; + } + } + + allowAtSign = isspace(*pSrc) != 0; + +#if 0 + // Don't go past the end. + if ((pDst - szFull) >= (sizeof(szFull) - 1)) + break; +#endif + *pDst++ = *pSrc++; + } + + *pDst = '\0'; + + int len = strlen(szFull) + 1; + m_pszCmdLine = new char[len]; + memcpy(m_pszCmdLine, szFull, len); +} + +// Purpose: Remove specified string ( and any args attached to it ) from command line +void CCommandLine::RemoveParm(const char *pszParm) +{ + if (!m_pszCmdLine) + return; + + if (!pszParm || *pszParm == '\0') + return; + + // Search for first occurrence of pszParm + char *p, *found; + char *pnextparam; + int n; + int curlen; + + p = m_pszCmdLine; + while (*p) + { + curlen = strlen(p); + found = strstr(p, pszParm); + + if (!found) + break; + + pnextparam = found + 1; + while (pnextparam && *pnextparam && (*pnextparam != '-') && (*pnextparam != '+')) + pnextparam++; + + if (pnextparam && *pnextparam) + { + // We are either at the end of the string, or at the next param. Just chop out the current param. + // # of characters after this param. + n = curlen - (pnextparam - p); + + memcpy(found, pnextparam, n); + found[n] = '\0'; + } + else + { + // Clear out rest of string. + n = pnextparam - found; + memset(found, 0, n); + } + } + + // Strip and trailing ' ' characters left over. + while (1) + { + int curpos = strlen(m_pszCmdLine); + if (curpos == 0 || m_pszCmdLine[ curpos - 1 ] != ' ') + break; + + m_pszCmdLine[curpos - 1] = '\0'; + } +} + +// Purpose: Append parameter and argument values to command line +void CCommandLine::AppendParm(const char *pszParm, const char *pszValues) +{ + int nNewLength = 0; + char *pCmdString; + + // Parameter. + nNewLength = strlen(pszParm); + + // Values + leading space character. + if (pszValues) + nNewLength += strlen(pszValues) + 1; + + // Terminal 0; + nNewLength++; + + if (!m_pszCmdLine) + { + m_pszCmdLine = new char[ nNewLength ]; + strcpy(m_pszCmdLine, pszParm); + if (pszValues) + { + strcat(m_pszCmdLine, " "); + strcat(m_pszCmdLine, pszValues); + } + + return; + } + + // Remove any remnants from the current Cmd Line. + RemoveParm(pszParm); + + nNewLength += strlen(m_pszCmdLine) + 1 + 1; + + pCmdString = new char[ nNewLength ]; + memset(pCmdString, 0, nNewLength); + + strcpy(pCmdString, m_pszCmdLine); // Copy old command line. + strcat(pCmdString, " "); // Put in a space + strcat(pCmdString, pszParm); + + if (pszValues) + { + strcat(pCmdString, " "); + strcat(pCmdString, pszValues); + } + + // Kill off the old one + delete[] m_pszCmdLine; + + // Point at the new command line. + m_pszCmdLine = pCmdString; +} + +void CCommandLine::SetParm(const char *pszParm, const char *pszValues) +{ + RemoveParm(pszParm); + AppendParm(pszParm, pszValues); +} + +void CCommandLine::SetParm(const char *pszParm, int iValue) +{ + char buf[64]; + _snprintf(buf, sizeof(buf), "%d", iValue); + SetParm(pszParm, buf); +} + +// Purpose: Search for the parameter in the current commandline +const char *CCommandLine::CheckParm(const char *psz, char **ppszValue) const +{ + static char sz[128] = ""; + + if (!m_pszCmdLine) + return nullptr; + + char *pret = strstr(m_pszCmdLine, psz); + if (!pret || !ppszValue) + return nullptr; + + *ppszValue = nullptr; + + // find the next whitespace + char *p1 = pret; + do { + ++p1; + } while (*p1 != ' ' && *p1); + + int i = 0; + char *p2 = p1 + 1; + + do { + if (p2[i] == '\0' || p2[i] == ' ') + break; + + sz[i++] = p2[i]; + } + while (i < sizeof(sz)); + + sz[i] = '\0'; + *ppszValue = sz; + return pret; +} + +const char *CCommandLine::GetCmdLine() const +{ + return m_pszCmdLine; +} diff --git a/rehlds/dedicated/src/conproc.cpp b/rehlds/dedicated/src/conproc.cpp new file mode 100644 index 0000000..962bd32 --- /dev/null +++ b/rehlds/dedicated/src/conproc.cpp @@ -0,0 +1,312 @@ +#include "precompiled.h" + +static HANDLE heventDone; +static HANDLE hfileBuffer; +static HANDLE heventChildSend; +static HANDLE heventParentSend; +static HANDLE hStdout; +static HANDLE hStdin; + +BOOL SetConsoleCXCY(HANDLE hStdout, int cx, int cy) +{ + CONSOLE_SCREEN_BUFFER_INFO info; + COORD coordMax; + + coordMax = GetLargestConsoleWindowSize(hStdout); + + if (cy > coordMax.Y) + cy = coordMax.Y; + + if (cx > coordMax.X) + cx = coordMax.X; + + if (!GetConsoleScreenBufferInfo(hStdout, &info)) + return FALSE; + + // height + info.srWindow.Left = 0; + info.srWindow.Right = info.dwSize.X - 1; + info.srWindow.Top = 0; + info.srWindow.Bottom = cy - 1; + + if (cy < info.dwSize.Y) + { + if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow)) + return FALSE; + + info.dwSize.Y = cy; + + if (!SetConsoleScreenBufferSize(hStdout, info.dwSize)) + return FALSE; + } + else if (cy > info.dwSize.Y) + { + info.dwSize.Y = cy; + + if (!SetConsoleScreenBufferSize(hStdout, info.dwSize)) + return FALSE; + + if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow)) + return FALSE; + } + + if (!GetConsoleScreenBufferInfo(hStdout, &info)) + return FALSE; + + // width + info.srWindow.Left = 0; + info.srWindow.Right = cx - 1; + info.srWindow.Top = 0; + info.srWindow.Bottom = info.dwSize.Y - 1; + + if (cx < info.dwSize.X) + { + if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow)) + return FALSE; + + info.dwSize.X = cx; + + if (!SetConsoleScreenBufferSize(hStdout, info.dwSize)) + return FALSE; + } + else if (cx > info.dwSize.X) + { + info.dwSize.X = cx; + + if (!SetConsoleScreenBufferSize(hStdout, info.dwSize)) + return FALSE; + + if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow)) + return FALSE; + } + + return TRUE; +} + +LPVOID GetMappedBuffer(HANDLE hfileBuffer) +{ + return MapViewOfFile(hfileBuffer, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); +} + +void ReleaseMappedBuffer(LPVOID pBuffer) +{ + UnmapViewOfFile(pBuffer); +} + +BOOL GetScreenBufferLines(int *piLines) +{ + CONSOLE_SCREEN_BUFFER_INFO info; + + BOOL bRet = GetConsoleScreenBufferInfo(hStdout, &info); + if (bRet) + *piLines = info.dwSize.Y; + + return bRet; +} + +BOOL SetScreenBufferLines(int iLines) +{ + return SetConsoleCXCY(hStdout, 80, iLines); +} + +BOOL ReadText(LPTSTR pszText, int iBeginLine, int iEndLine) +{ + COORD coord; + DWORD dwRead; + BOOL bRet; + + coord.X = 0; + coord.Y = iBeginLine; + + bRet = ReadConsoleOutputCharacter(hStdout, pszText, 80 * (iEndLine - iBeginLine + 1), coord, &dwRead); + + // Make sure it's null terminated. + if (bRet) + pszText[dwRead] = '\0'; + + return bRet; +} + +int CharToCode(char c) +{ + char upper = toupper(c); + switch (c) + { + case 13: + return 28; + default: + break; + } + + if (isalpha(c)) + return (30 + upper - 65); + + if (isdigit(c)) + return (1 + upper - 47); + + return c; +} + +BOOL WriteText(LPCTSTR szText) +{ + DWORD dwWritten; + INPUT_RECORD rec; + char upper, *sz; + + sz = (LPTSTR)szText; + + while (*sz) + { + // 13 is the code for a carriage return (\n) instead of 10. + if (*sz == '\n') + *sz = '\r'; + + upper = toupper(*sz); + + rec.EventType = KEY_EVENT; + rec.Event.KeyEvent.bKeyDown = TRUE; + rec.Event.KeyEvent.wRepeatCount = 1; + rec.Event.KeyEvent.wVirtualKeyCode = upper; + rec.Event.KeyEvent.wVirtualScanCode = CharToCode(*sz); + rec.Event.KeyEvent.uChar.AsciiChar = *sz; + rec.Event.KeyEvent.uChar.UnicodeChar = *sz; + rec.Event.KeyEvent.dwControlKeyState = isupper(*sz) ? 0x80 : 0x0; + + WriteConsoleInput(hStdin, &rec, 1, &dwWritten); + rec.Event.KeyEvent.bKeyDown = FALSE; + WriteConsoleInput(hStdin, &rec, 1, &dwWritten); + sz++; + } + + return TRUE; +} + +unsigned _stdcall RequestProc(void *arg) +{ + int *pBuffer; + DWORD dwRet; + HANDLE heventWait[2]; + int iBeginLine, iEndLine; + + heventWait[0] = heventParentSend; + heventWait[1] = heventDone; + + while (1) + { + dwRet = WaitForMultipleObjects(2, heventWait, FALSE, INFINITE); + + // heventDone fired, so we're exiting. + if (dwRet == WAIT_OBJECT_0 + 1) + break; + + pBuffer = (int *)GetMappedBuffer(hfileBuffer); + + // hfileBuffer is invalid. Just leave. + if (!pBuffer) + { + sys->Printf("Request Proc: Invalid -HFILE handle\n"); + break; + } + + switch (pBuffer[0]) + { + case CCOM_WRITE_TEXT: + pBuffer[0] = WriteText((LPCTSTR)(pBuffer + 1)); + break; + + case CCOM_GET_TEXT: + iBeginLine = pBuffer[1]; + iEndLine = pBuffer[2]; + pBuffer[0] = ReadText((LPTSTR)(pBuffer + 1), iBeginLine, iEndLine); + break; + + case CCOM_GET_SCR_LINES: + pBuffer[0] = GetScreenBufferLines(&pBuffer[1]); + break; + + case CCOM_SET_SCR_LINES: + pBuffer[0] = SetScreenBufferLines(pBuffer[1]); + break; + } + + ReleaseMappedBuffer(pBuffer); + SetEvent(heventChildSend); + } + + _endthreadex(0); + return 0; +} + +void DeinitConProc() +{ + if (heventDone) + { + SetEvent(heventDone); + } +} + +void InitConProc() +{ + unsigned threadAddr; + HANDLE hFile = (HANDLE)0; + HANDLE heventParent = (HANDLE)0; + HANDLE heventChild = (HANDLE)0; + int WantHeight = 50; + char *p; + + // give external front ends a chance to hook into the console + if (CommandLine()->CheckParm("-HFILE", &p) && p) + { + hFile = (HANDLE)atoi(p); + } + + if (CommandLine()->CheckParm("-HPARENT", &p) && p) + { + heventParent = (HANDLE)atoi(p); + } + + if (CommandLine()->CheckParm("-HCHILD", &p) && p) + { + heventChild = (HANDLE)atoi(p); + } + + // ignore if we don't have all the events. + if (!hFile || !heventParent || !heventChild) + { + //sys->Printf ("\n\nNo external front end present.\n"); + return; + } + + sys->Printf("\n\nInitConProc: Setting up external control.\n"); + + hfileBuffer = hFile; + heventParentSend = heventParent; + heventChildSend = heventChild; + + // So we'll know when to go away. + heventDone = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!heventDone) + { + sys->Printf("InitConProc: Couldn't create heventDone\n"); + return; + } + + if (!_beginthreadex(NULL, 0, RequestProc, NULL, 0, &threadAddr)) + { + CloseHandle(heventDone); + sys->Printf("InitConProc: Couldn't create third party thread\n"); + return; + } + + // save off the input/output handles. + hStdout = GetStdHandle(STD_OUTPUT_HANDLE); + hStdin = GetStdHandle(STD_INPUT_HANDLE); + + if (CommandLine()->CheckParm("-conheight", &p) && p) + { + WantHeight = atoi(p); + } + + // Force 80 character width, at least 25 character height + SetConsoleCXCY(hStdout, 80, WantHeight); +} diff --git a/rehlds/dedicated/src/conproc.h b/rehlds/dedicated/src/conproc.h new file mode 100644 index 0000000..c0bd3ff --- /dev/null +++ b/rehlds/dedicated/src/conproc.h @@ -0,0 +1,17 @@ +#ifndef CONPROC_H +#define CONPROC_H +#ifdef _WIN32 +#pragma once +#endif + +#include + +#define CCOM_WRITE_TEXT 0x2 // Param1 : Text +#define CCOM_GET_TEXT 0x3 // Param1 : Begin line, Param2 : End line +#define CCOM_GET_SCR_LINES 0x4 // No params +#define CCOM_SET_SCR_LINES 0x5 // Param1 : Number of lines + +void InitConProc(); +void DeinitConProc(); + +#endif // CONPROC_H diff --git a/rehlds/dedicated/src/dbg.cpp b/rehlds/dedicated/src/dbg.cpp new file mode 100644 index 0000000..f0b6b43 --- /dev/null +++ b/rehlds/dedicated/src/dbg.cpp @@ -0,0 +1,171 @@ +#include "precompiled.h" + +class CPerformanceCounter +{ +public: + CPerformanceCounter(); + + void InitializePerformanceCounter(); + double GetCurTime(); + +private: + int m_iLowShift; + double m_flPerfCounterFreq; + double m_flCurrentTime; + double m_flLastCurrentTime; +}; + +inline CPerformanceCounter::CPerformanceCounter() : + m_iLowShift(0), + m_flPerfCounterFreq(0), + m_flCurrentTime(0), + m_flLastCurrentTime(0) +{ + InitializePerformanceCounter(); +} + +inline void CPerformanceCounter::InitializePerformanceCounter() +{ +#ifdef _WIN32 + + LARGE_INTEGER performanceFreq; + QueryPerformanceFrequency(&performanceFreq); + + // get 32 out of the 64 time bits such that we have around + // 1 microsecond resolution + unsigned int lowpart, highpart; + lowpart = (unsigned int)performanceFreq.LowPart; + highpart = (unsigned int)performanceFreq.HighPart; + m_iLowShift = 0; + + while (highpart || (lowpart > 2000000.0)) + { + m_iLowShift++; + lowpart >>= 1; + lowpart |= (highpart & 1) << 31; + highpart >>= 1; + } + + m_flPerfCounterFreq = 1.0 / (double)lowpart; + +#endif // _WIN32 +} + +inline double CPerformanceCounter::GetCurTime() +{ +#ifdef _WIN32 + + static int sametimecount; + static unsigned int oldtime; + static int first = 1; + LARGE_INTEGER PerformanceCount; + unsigned int temp, t2; + double time; + + QueryPerformanceCounter(&PerformanceCount); + if (m_iLowShift == 0) + { + temp = (unsigned int)PerformanceCount.LowPart; + } + else + { + temp = ((unsigned int)PerformanceCount.LowPart >> m_iLowShift) | + ((unsigned int)PerformanceCount.HighPart << (32 - m_iLowShift)); + } + + if (first) + { + oldtime = temp; + first = 0; + } + else + { + // check for turnover or backward time + if ((temp <= oldtime) && ((oldtime - temp) < 0x10000000)) + { + // so we can't get stuck + oldtime = temp; + } + else + { + t2 = temp - oldtime; + + time = (double)t2 * m_flPerfCounterFreq; + oldtime = temp; + + m_flCurrentTime += time; + + if (m_flCurrentTime == m_flLastCurrentTime) + { + if (++sametimecount > 100000) + { + m_flCurrentTime += 1.0; + sametimecount = 0; + } + } + else + { + sametimecount = 0; + } + + m_flLastCurrentTime = m_flCurrentTime; + } + } + + return m_flCurrentTime; + +#else // _WIN32 + + struct timeval tp; + static int secbase = 0; + + gettimeofday(&tp, NULL); + + if (!secbase) + { + secbase = tp.tv_sec; + return (tp.tv_usec / 1000000.0); + } + + return ((tp.tv_sec - secbase) + tp.tv_usec / 1000000.0); + +#endif // _WIN32 +} + +void _LogFunctionTrace(const char *pFunctionName, const char *param) +{ + //{ + // char *entry; + //} +} + +double _StartFunctionTimer() +{ + //CPerformanceCounter::GetCurTime(); + return 0; +} + +void _LogFunctionTraceMaxTime(const char *pFunctionName, double startTime, double maxTime) +{ + //{ + // double timeDiff; + // CPerformanceCounter::GetCurTime(); + // _LogFunctionTrace(const char *pFunctionName, const char *param); + //} +} + +void ClearErrorLogs() +{ +} + +void Error(const char *pMsg, ...) +{ + //{ + // char logName; + // FILE *f; + // va_list args; + // { + // int i; + // } + //} +} diff --git a/rehlds/dedicated/src/dbg.h b/rehlds/dedicated/src/dbg.h new file mode 100644 index 0000000..4256ca5 --- /dev/null +++ b/rehlds/dedicated/src/dbg.h @@ -0,0 +1,13 @@ +#ifndef DBG_H +#define DBG_H +#ifdef _WIN32 +#pragma once +#endif + +void _LogFunctionTrace(const char *pFunctionName, const char *param); +double _StartFunctionTimer(); +void _LogFunctionTraceMaxTime(const char *pFunctionName, double startTime, double maxTime); +void ClearErrorLogs(); +void Error(const char *pMsg, ...); + +#endif // DBG_H diff --git a/rehlds/dedicated/src/dedicated.h b/rehlds/dedicated/src/dedicated.h new file mode 100644 index 0000000..37067f5 --- /dev/null +++ b/rehlds/dedicated/src/dedicated.h @@ -0,0 +1,13 @@ +#ifndef DEDICATED_H +#define DEDICATED_H +#ifdef _WIN32 +#pragma once +#endif + +typedef void (*NET_Sleep_t)(); +typedef void (*SleepType)(int msec); + +extern bool g_bVGui; +extern IDedicatedServerAPI *engineAPI; + +#endif // DEDICATED_H diff --git a/rehlds/dedicated/src/dedicated_exports.cpp b/rehlds/dedicated/src/dedicated_exports.cpp new file mode 100644 index 0000000..956fb15 --- /dev/null +++ b/rehlds/dedicated/src/dedicated_exports.cpp @@ -0,0 +1,13 @@ +#include "precompiled.h" + +class CDedicatedExports: IDedicatedExports { +public: + void Sys_Printf(char *text); +}; + +EXPOSE_SINGLE_INTERFACE(CDedicatedExports, IDedicatedExports, VENGINE_DEDICATEDEXPORTS_API_VERSION); + +void CDedicatedExports::Sys_Printf(char *text) +{ + ::Sys_Printf_Safe(text); +} diff --git a/rehlds/dedicated/src/icommandline.h b/rehlds/dedicated/src/icommandline.h new file mode 100644 index 0000000..3e65189 --- /dev/null +++ b/rehlds/dedicated/src/icommandline.h @@ -0,0 +1,25 @@ +#ifndef ICOMMANDLINE_H +#define ICOMMANDLINE_H +#ifdef _WIN32 +#pragma once +#endif + +// Interface to engine command line +class ICommandLine { +public: + virtual void CreateCmdLine(const char *commandline) = 0; + virtual void CreateCmdLine(int argc, const char **argv) = 0; + virtual const char *GetCmdLine() const = 0; + + // Check whether a particular parameter exists + virtual const char *CheckParm(const char *psz, char **ppszValue = nullptr) const = 0; + virtual void RemoveParm(const char *pszParm) = 0; + virtual void AppendParm(const char *pszParm, const char *pszValues) = 0; + + virtual void SetParm(const char *pszParm, const char *pszValues) = 0; + virtual void SetParm(const char *pszParm, int iValue) = 0; +}; + +ICommandLine *CommandLine(); + +#endif // ICOMMANDLINE_H diff --git a/rehlds/dedicated/src/isys.h b/rehlds/dedicated/src/isys.h new file mode 100644 index 0000000..1e67bcc --- /dev/null +++ b/rehlds/dedicated/src/isys.h @@ -0,0 +1,31 @@ +#ifndef ISYS_H +#define ISYS_H +#ifdef _WIN32 +#pragma once +#endif + +class ISys { +public: + virtual ~ISys() {} + + virtual void Sleep(int msec) = 0; + virtual bool GetExecutableName(char *out) = 0; + virtual void ErrorMessage(int level, const char *msg) = 0; + + virtual void WriteStatusText(char *szText) = 0; + virtual void UpdateStatus(int force) = 0; + + virtual long LoadLibrary(char *lib) = 0; + virtual void FreeLibrary(long library) = 0; + + virtual bool CreateConsoleWindow(void) = 0; + virtual void DestroyConsoleWindow(void) = 0; + + virtual void ConsoleOutput(char *string) = 0; + virtual char *ConsoleInput(void) = 0; + virtual void Printf(char *fmt, ...) = 0; +}; + +extern ISys *sys; + +#endif // ISYS_H diff --git a/rehlds/dedicated/src/precompiled.cpp b/rehlds/dedicated/src/precompiled.cpp new file mode 100644 index 0000000..5f656a4 --- /dev/null +++ b/rehlds/dedicated/src/precompiled.cpp @@ -0,0 +1 @@ +#include "precompiled.h" diff --git a/rehlds/dedicated/src/precompiled.h b/rehlds/dedicated/src/precompiled.h new file mode 100644 index 0000000..27a19de --- /dev/null +++ b/rehlds/dedicated/src/precompiled.h @@ -0,0 +1,21 @@ +#pragma once + +#include "osconfig.h" +#include "archtypes.h" +#include "mathlib.h" +#include "FileSystem.h" + +#include "common.h" + +#include "engine_hlds_api.h" +#include "idedicatedexports.h" +#include "icommandline.h" + +#include "sys_ded.h" +#include "icommandline.h" +#include "textconsole.h" +#include "vgui/vguihelpers.h" + +#ifdef _WIN32 + #include "conproc.h" +#endif // _WIN32 diff --git a/rehlds/dedicated/src/public_amalgamation.cpp b/rehlds/dedicated/src/public_amalgamation.cpp new file mode 100644 index 0000000..1d3ef03 --- /dev/null +++ b/rehlds/dedicated/src/public_amalgamation.cpp @@ -0,0 +1,11 @@ +#include "precompiled.h" + +#include "mem.cpp" +#include "interface.cpp" +#include "SteamAppStartUp.cpp" + +#include "ObjectList.cpp" + +#include "textconsole.cpp" +#include "TextConsoleUnix.cpp" +#include "TextConsoleWin32.cpp" diff --git a/rehlds/dedicated/src/sys_ded.cpp b/rehlds/dedicated/src/sys_ded.cpp new file mode 100644 index 0000000..0b96f2d --- /dev/null +++ b/rehlds/dedicated/src/sys_ded.cpp @@ -0,0 +1,201 @@ +#include "precompiled.h" + +long hDLLThirdParty = 0L; + +bool g_bVGui = false; +char *gpszCvars = nullptr; +bool gbAppHasBeenTerminated = false; + +CSysModule *g_pEngineModule = nullptr; +IDedicatedServerAPI *engineAPI = nullptr; + +IFileSystem *g_pFileSystemInterface = nullptr; +CSysModule *g_pFileSystemModule = nullptr; + +CreateInterfaceFn g_FilesystemFactoryFn; + +void Sys_Printf_Safe(char *text) +{ + if (sys) + { + sys->Printf("%s", text); + } +} + +void Sys_Printf(char *fmt, ...) +{ + // Dump text to debugging console. + va_list argptr; + char szText[1024]; + + va_start(argptr, fmt); + _vsnprintf(szText, sizeof(szText), fmt, argptr); + va_end(argptr); + + // Get Current text and append it. + if (g_bVGui) + { + VGUIPrintf(szText); + } + else + { + console.Print(szText); + } +} + +void Load3rdParty() +{ + // Only do this if the server operator wants the support. + // ( In case of malicious code, too ) + if (CommandLine()->CheckParm("-usegh")) + { + hDLLThirdParty = sys->LoadLibrary("ghostinj.dll"); + } +} + +void ProcessConsoleInput() +{ + if (!engineAPI) + return; + + char *inputLine = console.GetLine(); + if (inputLine) + { + char szBuf[256]; + _snprintf(szBuf, sizeof(szBuf), "%s\n", inputLine); + engineAPI->AddConsoleText(szBuf); + } +} + +char *UTIL_GetBaseDir() +{ + return "."; +} + +// Server Frame +int RunServer() +{ +#ifdef _WIN32 + // TODO: finish me! + /*if (g_bVGui) + { + vgui::ivgui()->SetSleep(0); + }*/ +#endif + + while (true) + { + if (gbAppHasBeenTerminated) + break; + + CreateInterfaceFn engineFactory = Sys_GetFactory(g_pEngineModule); + RunVGUIFrame(); + + if (engineFactory) + engineAPI = (IDedicatedServerAPI *)engineFactory(VENGINE_HLDS_API_VERSION, NULL); + + RunVGUIFrame(); + if (!engineAPI) + return -1; + + if (!engineAPI->Init(UTIL_GetBaseDir(), (char *)CommandLine()->GetCmdLine(), Sys_GetFactoryThis(), g_FilesystemFactoryFn)) { + return -1; + } + + RunVGUIFrame(); + + // TODO: finish me! + /*if (g_bVGui) + { + g_pFileSystemInterface->AddSearchPath("platform", "PLATFORM"); + + // find our configuration directory + char szConfigDir[512]; + const char *steamPath = getenv("SteamInstallPath"); + if (steamPath) + { + // put the config dir directly under steam + _snprintf(szConfigDir, sizeof(szConfigDir), "%s/config", steamPath); + } + else + { + // we're not running steam, so just put the config dir under the platform + strncpy(szConfigDir, "platform/config", sizeof(szConfigDir)); + } + }*/ + + RunVGUIFrame(); + + if (gpszCvars) { + engineAPI->AddConsoleText(gpszCvars); + } + + VGUIFinishedConfig(); + RunVGUIFrame(); + + bool bDone = false; + while (1) + { + if (bDone) + break; + + // Running really fast, yield some time to other apps + sys->Sleep(1); + +#ifdef _WIN32 + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) + { + if (!GetMessage(&msg, NULL, 0, 0)) + { + bDone = true; + break; + } + + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + if (gbAppHasBeenTerminated) + break; + + + if (g_bVGui) + { + RunVGUIFrame(); + } + else +#endif // _WIN32 + { + ProcessConsoleInput(); + } + + if (!engineAPI->RunFrame()) + { + bDone = true; + } + + sys->UpdateStatus(0 /* don't force */); + } + + if (g_bVGui) + { + RunVGUIFrame(); + StopVGUI(); + } + else + { + sys->DestroyConsoleWindow(); + console.ShutDown(); + } + + auto iret = engineAPI->Shutdown(); + Sys_UnloadModule(g_pEngineModule); + VGUIFinishedConfig(); + + if (iret == DLL_CLOSE) + return iret; + } + + return 0; +} diff --git a/rehlds/dedicated/src/sys_ded.h b/rehlds/dedicated/src/sys_ded.h new file mode 100644 index 0000000..425089d --- /dev/null +++ b/rehlds/dedicated/src/sys_ded.h @@ -0,0 +1,33 @@ +#ifndef SYS_DED_H +#define SYS_DED_H +#ifdef _WIN32 +#pragma once +#endif + +#include "isys.h" +#include "dedicated.h" +#include "dll_state.h" + +#if !defined(_WIN32) +#include +#endif // !defined(_WIN32) + +int RunServer(); +void Load3rdParty(); +void Sys_Printf_Safe(char *text); +void Sys_Printf(char *fmt, ...); +void _log(const char *fmt, ...); + +extern long hDLLThirdParty; +extern char *gpszCvars; +extern bool gbAppHasBeenTerminated; +extern IFileSystem *g_pFileSystemInterface; +extern CSysModule *g_pEngineModule; +extern CSysModule *g_pFileSystemModule; +extern CreateInterfaceFn g_FilesystemFactoryFn; + +#ifdef _WIN32 +BOOL WINAPI MyHandlerRoutine(DWORD CtrlType); +#endif // _WIN32 + +#endif // SYS_DED_H diff --git a/rehlds/dedicated/src/sys_linux.cpp b/rehlds/dedicated/src/sys_linux.cpp new file mode 100644 index 0000000..e278730 --- /dev/null +++ b/rehlds/dedicated/src/sys_linux.cpp @@ -0,0 +1,334 @@ +#include "precompiled.h" + +class CSys: public ISys { +public: + virtual ~CSys(); + + void Sleep(int msec); + bool GetExecutableName(char *out); + NORETURN void ErrorMessage(int level, const char *msg); + + void WriteStatusText(char *szText); + void UpdateStatus(int force); + + long LoadLibrary(char *lib); + void FreeLibrary(long library); + + bool CreateConsoleWindow(); + void DestroyConsoleWindow(); + + void ConsoleOutput(char *string); + char *ConsoleInput(); + void Printf(char *fmt, ...); +}; + +CSys g_Sys; +ISys *sys = &g_Sys; +char g_szEXEName[MAX_PATH]; + +SleepType Sys_Sleep; +NET_Sleep_t NET_Sleep_Timeout = NULL; + +CSys::~CSys() +{ + sys = nullptr; +} + +// this checks if pause has run yet, +// tell the compiler it can change at any time +volatile bool g_bPaused = false; + +void CSys::Sleep(int msec) +{ + usleep(msec * 1000); +} + +void Sleep_Old(int msec) +{ + usleep(msec * 1000); +} + +void Sleep_Select(int msec) +{ + struct timeval tv; + + // Assumes msec < 1000 + tv.tv_sec = 0; + tv.tv_usec = 1000 * msec; + + select(1, NULL, NULL, NULL, &tv); +} + +void Sleep_Net(int msec) +{ + if (NET_Sleep_Timeout) + { + NET_Sleep_Timeout(); + return; + } + + // NET_Sleep_Timeout isn't hooked yet, fallback to the old method + Sleep_Old(msec); +} + +// linux runs on a 100Hz scheduling clock, so the minimum latency from +// usleep is 10msec. However, people want lower latency than this.. +// +// There are a few solutions, one is to use the realtime scheduler in the +// kernel BUT this needs root privelleges to start. It also can play +// unfriendly with other programs. +// +// Another solution is to use software timers, they use the RTC of the +// system and are accurate to microseconds (or so). +// +// timers, via setitimer() are used here +void Sleep_Timer(int msec) +{ + struct itimerval tm; + + tm.it_value.tv_sec = msec / 1000; // convert msec to seconds + tm.it_value.tv_usec = (msec % 1000) * 1E3; // get the number of msecs and change to micros + tm.it_interval.tv_sec = 0; + tm.it_interval.tv_usec = 0; + + g_bPaused = false; + + // set the timer to trigger + if (setitimer(ITIMER_REAL, &tm, NULL)) { + // wait for the signal + pause(); + } + + g_bPaused = true; +} + +void alarmFunc(int num) +{ + // reset the signal handler + signal(SIGALRM, alarmFunc); + + // paused is 0, the timer has fired before the pause was called... Lets queue it again + if (!g_bPaused) + { + struct itimerval itim; + itim.it_interval.tv_sec = 0; + itim.it_interval.tv_usec = 0; + itim.it_value.tv_sec = 0; + itim.it_value.tv_usec = 1000; // get it to run again real soon + setitimer(ITIMER_REAL, &itim, 0); + } + +} + +bool CSys::GetExecutableName(char *out) +{ + strcpy(out, g_szEXEName); + return true; +} + +// Engine is erroring out, display error in message box +void CSys::ErrorMessage(int level, const char *msg) +{ + puts(msg); + exit(-1); +} + +void CSys::WriteStatusText(char *szText) +{ +} + +void CSys::UpdateStatus(int force) +{ +} + +long CSys::LoadLibrary(char *lib) +{ + char cwd[1024]; + char absolute_lib[1024]; + + if (!getcwd(cwd, sizeof(cwd))) + ErrorMessage(1, "Sys_LoadLibrary: Couldn't determine current directory."); + + if (cwd[strlen(cwd) - 1] == '/') + cwd[strlen(cwd) - 1] = '\0'; + + _snprintf(absolute_lib, sizeof(absolute_lib), "%s/%s", cwd, lib); + + void *hDll = dlopen(absolute_lib, RTLD_NOW); + if (!hDll) + { + ErrorMessage(1, dlerror()); + } + + return (long)hDll; +} + +void CSys::FreeLibrary(long library) +{ + if (!library) + return; + + dlclose((void *)library); +} + +bool CSys::CreateConsoleWindow() +{ + return true; +} + +void CSys::DestroyConsoleWindow() +{ +} + +// Print text to the dedicated console +void CSys::ConsoleOutput(char *string) +{ + printf("%s", string); + fflush(stdout); +} + +char *CSys::ConsoleInput() +{ + return console.GetLine(); +} + +void CSys::Printf(char *fmt, ...) +{ + // Dump text to debugging console. + va_list argptr; + char szText[1024]; + + va_start(argptr, fmt); + _vsnprintf(szText, sizeof(szText), fmt, argptr); + va_end(argptr); + + // Get Current text and append it. + ConsoleOutput(szText); +} + +#define MAX_LINUX_CMDLINE 2048 +static char linuxCmdline[ MAX_LINUX_CMDLINE ]; + +void BuildCmdLine(int argc, char **argv) +{ + int len; + int i; + + for (len = 0, i = 1; i < argc; i++) + { + len += strlen(argv[i]) + 1; + } + + if (len > MAX_LINUX_CMDLINE) + { + printf("command line too long, %i max\n", MAX_LINUX_CMDLINE); + exit(-1); + return; + } + + linuxCmdline[0] = '\0'; + for (i = 1; i < argc; i++) + { + if (i > 1) { + strcat(linuxCmdline, " "); + } + + strcat(linuxCmdline, argv[ i ]); + } +} + +char *GetCommandLine() +{ + return linuxCmdline; +} + +int main(int argc, char *argv[]) +{ + _snprintf(g_szEXEName, sizeof(g_szEXEName), "%s", argv[0]); + BuildCmdLine(argc, argv); + + CommandLine()->CreateCmdLine(::GetCommandLine()); + CommandLine()->AppendParm("-steam", NULL); + + // Load engine + g_pEngineModule = Sys_LoadModule(ENGINE_LIB); + if (!g_pEngineModule) + { + sys->ErrorMessage(1, "Unable to load engine, image is corrupt."); + return -1; + } + + Sys_Sleep = Sleep_Old; + + char *pPingType; + int type; + if (CommandLine()->CheckParm("-pingboost", &pPingType) && pPingType) + { + type = atoi(pPingType); + switch (type) + { + case 1: + signal(SIGALRM, alarmFunc); + Sys_Sleep = Sleep_Timer; + break; + case 2: + Sys_Sleep = Sleep_Select; + break; + case 3: + Sys_Sleep = Sleep_Net; + + // we Sys_GetProcAddress NET_Sleep() from + //engine_i486.so later in this function + NET_Sleep_Timeout = (NET_Sleep_t)Sys_GetProcAddress(g_pEngineModule, "NET_Sleep_Timeout"); + break; + // just in case + default: + Sys_Sleep = Sleep_Old; + break; + } + } + + char *fsmodule; + if (CommandLine()->CheckParm("-pidfile", &fsmodule) && fsmodule) + { + FILE *pidFile = fopen(fsmodule, "w"); + if (pidFile) { + fprintf(pidFile, "%i\n", getpid()); + fclose(pidFile); + } + else + printf("Warning: unable to open pidfile (%s)\n", pPingType); + } + + g_pFileSystemModule = Sys_LoadModule(STDIO_FILESYSTEM_LIB); + + // Get FileSystem interface + g_FilesystemFactoryFn = Sys_GetFactory(g_pFileSystemModule); + if (!g_FilesystemFactoryFn) + return -1; + + IFileSystem *pFullFileSystem = (IFileSystem *)g_FilesystemFactoryFn(FILESYSTEM_INTERFACE_VERSION, NULL); + if (!pFullFileSystem) + return -1; + + pFullFileSystem->Mount(); + + if (!console.Init()) { + puts("Failed to initilise console."); + return 0; + } + + gbAppHasBeenTerminated = false; + RunServer(); + + if (gpszCvars) + free(gpszCvars); + + if (pFullFileSystem) + pFullFileSystem->Unmount(); + + Sys_UnloadModule(g_pFileSystemModule); + + exit(0); + return 0; +} diff --git a/rehlds/dedicated/src/sys_window.cpp b/rehlds/dedicated/src/sys_window.cpp new file mode 100644 index 0000000..77f90ce --- /dev/null +++ b/rehlds/dedicated/src/sys_window.cpp @@ -0,0 +1,266 @@ +#include "precompiled.h" + +class CSys: public ISys { +public: + virtual ~CSys(); + + void Sleep(int msec); + bool GetExecutableName(char *out); + void ErrorMessage(int level, const char *msg); + + void WriteStatusText(char *szText); + void UpdateStatus(int force); + + long LoadLibrary(char *lib); + void FreeLibrary(long library); + + bool CreateConsoleWindow(); + void DestroyConsoleWindow(); + + void ConsoleOutput(char *string); + char *ConsoleInput(); + void Printf(char *fmt, ...); +}; + +CSys g_Sys; +ISys *sys = &g_Sys; + +CSys::~CSys() +{ + sys = nullptr; +} + +void CSys::Sleep(int msec) +{ + ::Sleep(msec); +} + +bool CSys::GetExecutableName(char *out) +{ + if (!::GetModuleFileName((HINSTANCE)GetModuleHandle(NULL), out, 256)) + return false; + + return true; +} + +void CSys::ErrorMessage(int level, const char *msg) +{ + MessageBox(NULL, msg, "Half-Life Dedicated Server Error", MB_OK); + PostQuitMessage(0); +} + +void CSys::WriteStatusText(char *szText) +{ + SetConsoleTitle(szText); +} + +void CSys::UpdateStatus(int force) +{ + static double tLast = 0.0; + double tCurrent; + char szStatus[256]; + int n, nMax; + char szMap[32]; + float fps; + + if (!engineAPI) + return; + + tCurrent = (double)timeGetTime() * 0.001; + engineAPI->UpdateStatus(&fps, &n, &nMax, szMap); + + if (!force) + { + if ((tCurrent - tLast) < 0.5f) + return; + } + + tLast = tCurrent; + _snprintf(szStatus, sizeof(szStatus), "%.1f fps %2i/%2i on %16s", fps, n, nMax, szMap); + + console.SetStatusLine(szStatus); + console.UpdateStatus(); +} + +long CSys::LoadLibrary(char *lib) +{ + void *hDll = ::LoadLibrary(lib); + return (long)hDll; +} + +void CSys::FreeLibrary(long library) +{ + if (!library) + return; + + ::FreeLibrary((HMODULE)library); +} + +bool CSys::CreateConsoleWindow() +{ +#if 0 + if (!AllocConsole()) + { + return false; + } +#endif + + InitConProc(); + return true; +} + +void CSys::DestroyConsoleWindow() +{ + FreeConsole(); + + // shut down QHOST hooks if necessary + DeinitConProc(); +} + +void CSys::ConsoleOutput(char *string) +{ + if (g_bVGui) + { + VGUIPrintf(string); + } + else + { + console.Print(string); + } +} + +char *CSys::ConsoleInput() +{ + return console.GetLine(); +} + +void CSys::Printf(char *fmt, ...) +{ + // Dump text to debugging console. + va_list argptr; + char szText[1024]; + + va_start(argptr, fmt); + _vsnprintf(szText, sizeof(szText), fmt, argptr); + va_end(argptr); + + // Get Current text and append it. + ConsoleOutput(szText); +} + +int StartServer() +{ + // Startup winock + WORD version = MAKEWORD(2, 0); + WSADATA wsaData; + WSAStartup(version, &wsaData); + + CommandLine()->CreateCmdLine(GetCommandLine()); + + // Load engine + g_pEngineModule = Sys_LoadModule(ENGINE_LIB); + if (!g_pEngineModule) + { + MessageBox(NULL, "Unable to load engine, image is corrupt.", "Half-Life Dedicated Server Error", MB_OK); + return -1; + } + + g_pFileSystemModule = Sys_LoadModule(STDIO_FILESYSTEM_LIB); + + // Get FileSystem interface + g_FilesystemFactoryFn = Sys_GetFactory(g_pFileSystemModule); + if (!g_FilesystemFactoryFn) + return -1; + + IFileSystem *pFullFileSystem = (IFileSystem *)g_FilesystemFactoryFn(FILESYSTEM_INTERFACE_VERSION, NULL); + if (!pFullFileSystem) + return -1; + + pFullFileSystem->Mount(); + + char *pszValue = nullptr; + if (CommandLine()->CheckParm("-steam") + || (CommandLine()->CheckParm("-console", &pszValue) && !pszValue)) { + g_bVGui = true; + StartVGUI(); + } + else + { + if (!console.Init()) { + MessageBox(NULL, "Failed to initialize console.", "Half-Life Dedicated Server Error", MB_OK); + return 0; + } + + if (!sys->CreateConsoleWindow()) { + return 0; + } + + console.SetColor(FOREGROUND_RED | FOREGROUND_INTENSITY); + + if (!SetConsoleCtrlHandler(MyHandlerRoutine, TRUE)) { + MessageBox(NULL, "Unable to set control handler", "Half-Life Dedicated Server Error", MB_OK); + return 0; + } + } + + gbAppHasBeenTerminated = false; + Load3rdParty(); + + // TODO: finish me! + /*// run vgui + if (g_bVGui) + { + while (VGUIIsInConfig() && VGUIIsRunning()) + { + RunVGUIFrame(); + } + } + else*/ + { + RunServer(); + } + + if (gpszCvars) + free(gpszCvars); + + if (pFullFileSystem) + pFullFileSystem->Unmount(); + + Sys_UnloadModule(g_pFileSystemModule); + + if (hDLLThirdParty) + { + Sys_UnloadModule((CSysModule *)hDLLThirdParty); + hDLLThirdParty = 0L; + } + + WSACleanup(); + return 0; +} + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) +{ + if (ShouldLaunchAppViaSteam(lpCmdLine, STDIO_FILESYSTEM_LIB, STDIO_FILESYSTEM_LIB)) + return 0; + + auto command = CommandLineToArgvW(GetCommandLineW(), (int *)&lpCmdLine); + auto ret = StartServer(); + LocalFree(command); + return ret; +} + +BOOL WINAPI MyHandlerRoutine(DWORD CtrlType) +{ + switch (CtrlType) + { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + case CTRL_CLOSE_EVENT: + case CTRL_LOGOFF_EVENT: + case CTRL_SHUTDOWN_EVENT: + gbAppHasBeenTerminated = true; + return TRUE; + } + + return FALSE; +} diff --git a/rehlds/dedicated/src/vgui/vguihelpers.cpp b/rehlds/dedicated/src/vgui/vguihelpers.cpp new file mode 100644 index 0000000..5f06cc1 --- /dev/null +++ b/rehlds/dedicated/src/vgui/vguihelpers.cpp @@ -0,0 +1,157 @@ +#include "precompiled.h" + +CSysModule *g_pVGUIModule = nullptr; + +bool InitializeVGui(CreateInterfaceFn *factorylist, int factorycount) +{ +#ifdef _WIN32 + // TODO: finish me! + /*if (vgui::VGuiControls_Init("DEDICATED", factoryList, numFactories)) + { + filesystem()->AddSearchPath(".", "MAIN"); + return true; + }*/ +#endif + + return false; +} + +// Starts up the VGUI system and loads the base panel +int StartVGUI() +{ + // TODO: finish me! + /*g_pVGUIModule = Sys_LoadModule("vgui2.dll"); + + CreateInterfaceFn ifaceFactory = Sys_GetFactoryThis(); + CreateInterfaceFn adminFactory = Sys_GetFactory(g_pVGUIModule); + CreateInterfaceFn filesystemFactory = Sys_GetFactory(g_pFileSystemModule); + CreateInterfaceFn dedicatedFactory = Sys_GetFactory(g_pEngineModule); + + const int numFactories = 4; + if (!InitializeVGui(&ifaceFactory, numFactories)) + { + MessageBox(NULL, "Fatal Error: Could not initialize vgui.", "Steam - Fatal Error", MB_OK | MB_ICONERROR); + return -1; + } + + filesystem()->AddSearchPath("platform", "PLATFORM"); + + // find our configuration directory + char szConfigDir[512]; + const char *steamPath = getenv("SteamInstallPath"); + if (steamPath) + { + // put the config dir directly under steam + _snprintf(szConfigDir, sizeof(szConfigDir), "%s/config", steamPath); + } + else + { + // we're not running steam, so just put the config dir under the platform + strncpy(szConfigDir, "platform/config", sizeof(szConfigDir)); + } + + mkdir(szConfigDir); + filesystem()->AddSearchPath(szConfigDir, "CONFIG"); + + vgui::system()->SetUserConfigFile("DialogConfig.vdf", "CONFIG"); + + // Init the surface + g_pMainPanel = new CMainPanel(); + g_pMainPanel->SetVisible(true); + + vgui::surface()->SetEmbeddedPanel(g_pMainPanel->GetVPanel()); + + // load the scheme + vgui::scheme()->LoadSchemeFromFile("Resource/TrackerScheme.res", NULL); + + // localization + vgui::localize()->AddFile("Resource/platform_%language%.txt"); + vgui::localize()->AddFile("Resource/vgui_%language%.txt"); + vgui::localize()->AddFile("Admin/server_%language%.txt"); + + // Start vgui + vgui::ivgui()->Start(); + + // load the module + g_pFullFileSystem->GetLocalCopy("Platform/Admin/AdminServer.dll"); + g_hAdminServerModule = Sys_LoadModule("Platform/Admin/AdminServer.dll"); + Assert(g_hAdminServerModule != NULL); + CreateInterfaceFn adminFactory = NULL; + + if (!g_hAdminServerModule) + { + vgui::ivgui()->DPrintf2("Admin Error: module version (Admin/AdminServer.dll, %s) invalid, not loading\n", IMANAGESERVER_INTERFACE_VERSION); + } + else + { + // make sure we get the right version + adminFactory = Sys_GetFactory(g_hAdminServerModule); + g_pAdminServer = (IAdminServer *)adminFactory(ADMINSERVER_INTERFACE_VERSION, NULL); + g_pAdminVGuiModule = (IVGuiModule *)adminFactory("VGuiModuleAdminServer001", NULL); + Assert(g_pAdminServer != NULL); + Assert(g_pAdminVGuiModule != NULL); + if (!g_pAdminServer || !g_pAdminVGuiModule) + { + vgui::ivgui()->DPrintf2("Admin Error: module version (Admin/AdminServer.dll, %s) invalid, not loading\n", IMANAGESERVER_INTERFACE_VERSION); + } + } + + // finish initializing admin module + g_pAdminVGuiModule->Initialize(&dedicatedFactory, 1); + g_pAdminVGuiModule->PostInitialize(&adminFactory, 1); + g_pAdminVGuiModule->SetParent(g_pMainPanel->GetVPanel()); + + // finish setting up main panel + g_pMainPanel->Initialize(); + g_pMainPanel->Open(); +*/ + return 0; +} + +// Run a single VGUI frame +void StopVGUI() +{ +} + +void RunVGUIFrame() +{ + // TODO: finish me! + //vgui::ivgui()->RunFrame(); +} + +bool VGUIIsRunning() +{ + //return vgui::ivgui()->IsRunning(); + return false; +} + +bool VGUIIsStopping() +{ + //return g_pMainPanel->Stopping(); + return false; +} + +bool VGUIIsInConfig() +{ + //return g_pMainPanel->IsInConfig(); + return false; +} + +void VGUIFinishedConfig() +{ + /*Assert(g_pMainPanel); + + // engine is loaded, pass the message on + if (g_pMainPanel) + { + SetEvent(g_pMainPanel->GetShutdownHandle()); + }*/ +} + +void VGUIPrintf(const char *msg) +{ + /*if (g_pMainPanel) + { + g_pMainPanel->AddConsoleText(msg); + }*/ +} diff --git a/rehlds/dedicated/src/vgui/vguihelpers.h b/rehlds/dedicated/src/vgui/vguihelpers.h new file mode 100644 index 0000000..9710eeb --- /dev/null +++ b/rehlds/dedicated/src/vgui/vguihelpers.h @@ -0,0 +1,10 @@ +#pragma once + +int StartVGUI(); +void StopVGUI(); +void RunVGUIFrame(); +bool VGUIIsRunning(); +bool VGUIIsStopping(); +bool VGUIIsInConfig(); +void VGUIFinishedConfig(); +void VGUIPrintf(const char *msg); diff --git a/rehlds/engine/filesystem.cpp b/rehlds/engine/filesystem.cpp index 92fe9cb..b5c1bcc 100644 --- a/rehlds/engine/filesystem.cpp +++ b/rehlds/engine/filesystem.cpp @@ -51,7 +51,7 @@ bool FileSystem_LoadDLL(CreateInterfaceFn filesystemFactory) { if (!filesystemFactory) { - g_pFileSystemModule = Sys_LoadModule(FILESYSTEM_DLL_NAME); + g_pFileSystemModule = Sys_LoadModule(STDIO_FILESYSTEM_LIB); if (g_pFileSystemModule) { diff --git a/rehlds/engine/filesystem_.h b/rehlds/engine/filesystem_.h index 513237c..d548bda 100644 --- a/rehlds/engine/filesystem_.h +++ b/rehlds/engine/filesystem_.h @@ -36,12 +36,6 @@ #include "iregistry.h" #include "utlvector.h" -#ifdef _WIN32 -#define FILESYSTEM_DLL_NAME "filesystem_stdio.dll" -#else -#define FILESYSTEM_DLL_NAME "filesystem_stdio.so" -#endif - #ifdef HOOK_ENGINE #define g_fallbackLocalizationFiles (*pg_fallbackLocalizationFiles) #define s_pBaseDir (*ps_pBaseDir) diff --git a/rehlds/public/FileSystem.h b/rehlds/public/FileSystem.h index 4a3c621..41d26b5 100644 --- a/rehlds/public/FileSystem.h +++ b/rehlds/public/FileSystem.h @@ -1,6 +1,6 @@ //========= Copyright � 1996-2001, Valve LLC, All rights reserved. ============ // -// Purpose: +// Purpose: // // $NoKeywords: $ //============================================================================= @@ -15,6 +15,13 @@ #include #include +#ifdef _WIN32 + #define STDIO_FILESYSTEM_LIB "filesystem_stdio.dll" + #define STEAM_FILESYSTEM_LIB "filesystem_steam.dll" +#else + #define STDIO_FILESYSTEM_LIB "filesystem_stdio.so" + #define STEAM_FILESYSTEM_LIB "filesystem_steam.so" +#endif // _WIN32 //----------------------------------------------------------------------------- // Forward declarations @@ -77,7 +84,7 @@ public: // Add paths in priority order (mod dir, game dir, ....) // If one or more .pak files are in the specified directory, then they are // added after the file system path - // If the path is the relative path to a .bsp file, then any previous .bsp file + // If the path is the relative path to a .bsp file, then any previous .bsp file // override is cleared and the current .bsp is searched for an embedded PAK file // and this file becomes the highest priority search path ( i.e., it's looked at first // even before the mod's file system path ). @@ -121,7 +128,7 @@ public: // direct filesystem buffer access // returns a handle to a buffer containing the file data - // this is the optimal way to access the complete data for a file, + // this is the optimal way to access the complete data for a file, // since the file preloader has probably already got it in memory virtual void *GetReadBuffer( FileHandle_t file, int *outBufferSize, bool failIfNotInCache ) = 0; virtual void ReleaseReadBuffer( FileHandle_t file, void *readBuffer ) = 0; @@ -174,7 +181,7 @@ public: // interface for custom pack files > 4Gb virtual bool AddPackFile( const char *fullpath, const char *pathID ) = 0; - + // open a file but force the data to come from the steam cache, NOT from disk virtual FileHandle_t OpenFromCacheForRead( const char *pFileName, const char *pOptions, const char *pathID = 0L ) = 0; diff --git a/rehlds/public/engine_hlds_api.h b/rehlds/public/engine_hlds_api.h index 97fd29f..75708fd 100644 --- a/rehlds/public/engine_hlds_api.h +++ b/rehlds/public/engine_hlds_api.h @@ -31,6 +31,12 @@ #include "maintypes.h" #include "interface.h" +#ifdef _WIN32 + #define ENGINE_LIB "swds.dll" +#else + #define ENGINE_LIB "engine_i486.so" +#endif // _WIN32 + class IDedicatedServerAPI : public IBaseInterface { public: diff --git a/rehlds/public/steam/steam_api.h b/rehlds/public/steam/steam_api.h index 10f3037..d4f4609 100644 --- a/rehlds/public/steam/steam_api.h +++ b/rehlds/public/steam/steam_api.h @@ -1,6 +1,6 @@ //========= Copyright Valve Corporation, All rights reserved. ============// // -// Purpose: +// Purpose: // //============================================================================= @@ -28,35 +28,34 @@ #include "steamps3params.h" #endif - - // Steam API export macro #if defined( _WIN32 ) && !defined( _X360 ) #if defined( STEAM_API_EXPORTS ) - #define S_API extern "C" __declspec( dllexport ) + #define S_API extern "C" __declspec( dllexport ) #elif defined( STEAM_API_NODLL ) #define S_API extern "C" #else - #define S_API extern "C" __declspec( dllimport ) + #define S_API extern "C" __declspec( dllimport ) #endif // STEAM_API_EXPORTS #elif defined( GNUC ) #if defined( STEAM_API_EXPORTS ) - #define S_API extern "C" __attribute__ ((visibility("default"))) + #define S_API extern "C" __attribute__ ((visibility("default"))) #else - #define S_API extern "C" + #define S_API extern "C" #endif // STEAM_API_EXPORTS #else // !WIN32 #if defined( STEAM_API_EXPORTS ) - #define S_API extern "C" + #define S_API extern "C" #else - #define S_API extern "C" + #define S_API extern "C" #endif // STEAM_API_EXPORTS #endif class CCallbackBase; +#ifdef REHLDS_SELF #include "rehlds/platform.h" - +#endif //----------------------------------------------------------------------------------------------------------------------------------------------------------// // Steam API setup & shutdown @@ -68,10 +67,10 @@ class CCallbackBase; // S_API void SteamAPI_Init(); (see below) S_API void SteamAPI_Shutdown(); -// checks if a local Steam client is running +// checks if a local Steam client is running S_API bool SteamAPI_IsSteamRunning(); -// Detects if your executable was launched through the Steam client, and restarts your game through +// Detects if your executable was launched through the Steam client, and restarts your game through // the client if necessary. The Steam client will be started if it is not running. // // Returns: true if your executable was NOT launched through the Steam client. This function will @@ -96,10 +95,10 @@ S_API ISteamClient *SteamClient(); // VERSION_SAFE_STEAM_API_INTERFACES is usually not necessary, but it provides safety against releasing // new steam_api.dll's without recompiling/rereleasing modules that use it. // -// If you use VERSION_SAFE_STEAM_API_INTERFACES, then you should call SteamAPI_InitSafe(). Also, to get the +// If you use VERSION_SAFE_STEAM_API_INTERFACES, then you should call SteamAPI_InitSafe(). Also, to get the // Steam interfaces, you must create and Init() a CSteamAPIContext (below) and use the interfaces in there. // -// If you don't use VERSION_SAFE_STEAM_API_INTERFACES, then you can use SteamAPI_Init() and the SteamXXXX() +// If you don't use VERSION_SAFE_STEAM_API_INTERFACES, then you can use SteamAPI_Init() and the SteamXXXX() // functions below to get at the Steam interfaces. // #ifdef VERSION_SAFE_STEAM_API_INTERFACES @@ -132,7 +131,7 @@ S_API ISteamPS3OverlayRender * SteamPS3OverlayRender(); //----------------------------------------------------------------------------------------------------------------------------------------------------------// // steam callback helper functions // -// The following classes/macros are used to be able to easily multiplex callbacks +// The following classes/macros are used to be able to easily multiplex callbacks // from the Steam API into various objects in the app in a thread-safe manner // // These functors are triggered via the SteamAPI_RunCallbacks() function, mapping the callback @@ -152,7 +151,7 @@ S_API void SteamAPI_UnregisterCallResult( class CCallbackBase *pCallback, SteamA //----------------------------------------------------------------------------- -// Purpose: base for callbacks, +// Purpose: base for callbacks, // used only by CCallback, shouldn't be used directly //----------------------------------------------------------------------------- class CCallbackBase @@ -221,7 +220,7 @@ public: SteamAPI_UnregisterCallResult( this, m_hAPICall ); m_hAPICall = k_uAPICallInvalid; } - + } ~CCallResult() @@ -234,14 +233,14 @@ private: virtual void Run( void *pvParam ) { m_hAPICall = k_uAPICallInvalid; // caller unregisters for us - (m_pObj->*m_Func)( (P *)pvParam, false ); + (m_pObj->*m_Func)( (P *)pvParam, false ); } void Run( void *pvParam, bool bIOFailure, SteamAPICall_t hSteamAPICall ) { if ( hSteamAPICall == m_hAPICall ) { m_hAPICall = k_uAPICallInvalid; // caller unregisters for us - (m_pObj->*m_Func)( (P *)pvParam, bIOFailure ); + (m_pObj->*m_Func)( (P *)pvParam, bIOFailure ); } } int GetCallbackSizeBytes() @@ -271,7 +270,7 @@ public: // ::Register() for your object // Or, just call the regular constructor with (NULL, NULL) // CCallback() {} - + // constructor for initializing this object in owner's constructor CCallback( T *pObj, func_t func ) : m_pObj( pObj ), m_Func( func ) { @@ -301,13 +300,23 @@ public: m_pObj = pObj; m_Func = func; // SteamAPI_RegisterCallback sets k_ECallbackFlagsRegistered + +#ifdef REHLDS_SELF CRehldsPlatformHolder::get()->SteamAPI_RegisterCallback(this, P::k_iCallback); +#else + SteamAPI_RegisterCallback(this, P::k_iCallback); +#endif // REHLDS_SELF } void Unregister() { // SteamAPI_UnregisterCallback removes k_ECallbackFlagsRegistered + +#ifdef REHLDS_SELF CRehldsPlatformHolder::get()->SteamAPI_UnregisterCallback(this); +#else + SteamAPI_UnregisterCallback(this); +#endif // REHLDS_SELF } void SetGameserverFlag() { m_nCallbackFlags |= k_ECallbackFlagsGameServer; } @@ -380,7 +389,7 @@ S_API HSteamUser GetHSteamUser(); #ifdef VERSION_SAFE_STEAM_API_INTERFACES //----------------------------------------------------------------------------------------------------------------------------------------------------------// -// VERSION_SAFE_STEAM_API_INTERFACES uses CSteamAPIContext to provide interfaces to each module in a way that +// VERSION_SAFE_STEAM_API_INTERFACES uses CSteamAPIContext to provide interfaces to each module in a way that // lets them each specify the interface versions they are compiled with. // // It's important that these stay inlined in the header so the calling module specifies the interface versions @@ -488,7 +497,7 @@ inline bool CSteamAPIContext::Init() m_pSteamUserStats = SteamClient()->GetISteamUserStats( hSteamUser, hSteamPipe, STEAMUSERSTATS_INTERFACE_VERSION ); if ( !m_pSteamUserStats ) return false; - + m_pSteamApps = SteamClient()->GetISteamApps( hSteamUser, hSteamPipe, STEAMAPPS_INTERFACE_VERSION ); if ( !m_pSteamApps ) return false; diff --git a/rehlds/public/vgui/VGUI.h b/rehlds/public/vgui/VGUI.h new file mode 100644 index 0000000..50cf372 --- /dev/null +++ b/rehlds/public/vgui/VGUI.h @@ -0,0 +1,31 @@ +#ifndef VGUI_H +#define VGUI_H +#ifdef _WIN32 +#pragma once +#endif + +namespace vgui2 +{ + +// handle to an internal vgui panel +// this is the only handle to a panel that is valid across dll boundaries +typedef unsigned int VPANEL; + +// handles to vgui objects +// NULL values signify an invalid value +typedef unsigned long HScheme; +typedef unsigned long HTexture; +typedef unsigned long HCursor; + +typedef int HContext; +typedef unsigned long HPanel; +typedef unsigned long HFont; + +// the value of an invalid font handle +const VPANEL NULL_PANEL = 0; +const HFont INVALID_FONT = 0; +const HPanel INVALID_PANEL = 0xffffffff; + +} // namespace vgui + +#endif // VGUI_H diff --git a/settings.gradle b/settings.gradle index 0da4d36..4879f1d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,4 +2,5 @@ rootProject.name = 'rehlds' include 'dep/cppunitlite' include 'dep/bzip2' include 'rehlds' +include 'rehlds/dedicated' include 'flightrec/decoder_api', 'flightrec/decoder'