First pass idLib conversion from hex rays4.

This commit is contained in:
Justin Marshall
2026-08-08 13:54:26 -07:00
parent 3d01d735ce
commit 09a4cb91c3
120 changed files with 17718 additions and 0 deletions
+208
View File
@@ -0,0 +1,208 @@
#include "sys_alloc.h"
#include <algorithm>
#include <atomic>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <limits>
#include <malloc.h>
#include <Windows.h>
namespace {
struct allocationRecord_t {
allocationRecord_t* next;
void* pointer;
unsigned int bytes;
memTag_t tag;
heapType_t heap;
const char* location;
};
SRWLOCK allocationLock = SRWLOCK_INIT;
allocationRecord_t* allocations = nullptr;
std::atomic<unsigned long long> allocatedBytes(0);
std::atomic<idOutOfMemoryCallback> outOfMemoryCallback(nullptr);
thread_local heapType_t currentHeap = HEAP_SYSTEMHEAP;
thread_local heapType_t pushedHeap = HEAP_SYSTEMHEAP;
thread_local int heapStackDepth = 0;
const char* const tagNames[TAG_NUM_TAGS] = {
"UNSET", "STATIC_EXE", "RESOURCE_GRAPH", "DEBUG", "NEW", "IDLIST",
"TEMP", "STRING", "ATOMIC_STRING", "BITARRAY", "MATH", "LEXER",
"COMPILER", "COLLISION", "COLLISION_QUERY", "CLIPMODEL", "MORPH",
"MD6_MISC", "MD6_NODES", "MD6", "MD6_ANIMS", "MD6_LIPSYNC",
"MD6_JOINTCACHE", "MD6_MESHES", "MD6_JOINTBUFFERS", "MD6_BLENDSTACK",
"MD6_JOINTMODS", "MD6_COLLISION", "MD6_ANIMEVENTS", "MD6_PHASE_TRACK",
"ANIMATION", "ANIMATION_DEBUG", "DECL_ANIMWEB", "ANIMWEB", "IMAGE",
"DXIMAGE", "VIRTUALTEXTURE", "AAS", "SOUND", "SOUND_BSP", "SOUND_DATA",
"SOUND_STREAM", "SOUND_MULTISTREAM", "SOUND_SAMPLETABLES", "IDLIB",
"TRIANGLES", "DECL", "DECLTEXT", "FILE", "CVAR", "PAGEFILECACHE",
"IDCLASS", "PRESENTABLE", "FOLIAGE", "WATER", "MEMORY_MAPPED_FILE",
"RENDERPARM", "NETWORKING", "SCRIPT", "FXPHYSICS", "LWO", "RENDERWORLD",
"RENDERER", "AI_GAMESTATE", "EVENTS", "VOICEOVER", "VOICETRACK_EVENTS",
"VOICETRACK_FRAMEREFS", "VOICETRACK_PHONEMES", "VISEMESET_VISEMES",
"VISEMESET_PHONEMES", "AF", "SWF", "GUI", "GUI_MODEL", "FUNC_CALLBACK",
"MENU", "GAME", "HASHINDEX", "PARTICLE", "EFFECT_PARTICLE", "CLOTH",
"ANIMTAGS", "IK", "STATICMODEL", "RENDERMODEL", "DXBUFFER", "TOOLS",
"CLOUD", "AMQP", "RENDERPROG", "HASHTABLE", "AI_FSM", "AI_VISCACHE",
"AI_SEARCH", "AI_OBSTACLE", "JOBLIST", "TRANSPARENCY", "DETAIL",
"RESOURCE", "FILE_RESOURCE", "RESOURCE_BGL", "RESOURCE_BGL_RING",
"RESOURCE_BGL_OVERSIZE", "RESOURCE_MGR", "PVS", "DEFERRED_VIS", "FIBER",
"SUPERSCRIPT", "FX", "SAVEGAMES", "AI_TRANSITIONS", "AI_STATEDATA",
"AI_COMBATANIM", "TYPEINFO", "DAMAGEDECAL", "TABLE", "VIDEO", "NAVPOWER",
"LANGDICT", "SPLINE", "BINK", "FONTS", "EVENT_LISTENER", "PHYSICAL_BLOCK",
"DXOBJECT"
};
void AddRecord(void* pointer, const unsigned int bytes, const memTag_t tag,
const heapType_t heap, const char* location) {
allocationRecord_t* const record = static_cast<allocationRecord_t*>(
std::malloc(sizeof(allocationRecord_t))
);
if (record == nullptr) {
return;
}
record->pointer = pointer;
record->bytes = bytes;
record->tag = tag;
record->heap = heap;
record->location = location;
AcquireSRWLockExclusive(&allocationLock);
record->next = allocations;
allocations = record;
ReleaseSRWLockExclusive(&allocationLock);
allocatedBytes.fetch_add(bytes, std::memory_order_relaxed);
}
unsigned int RemoveRecord(void* pointer) {
unsigned int bytes = 0;
AcquireSRWLockExclusive(&allocationLock);
allocationRecord_t** link = &allocations;
while (*link != nullptr) {
if ((*link)->pointer == pointer) {
allocationRecord_t* const record = *link;
*link = record->next;
bytes = record->bytes;
std::free(record);
break;
}
link = &(*link)->next;
}
ReleaseSRWLockExclusive(&allocationLock);
if (bytes != 0) {
allocatedBytes.fetch_sub(bytes, std::memory_order_relaxed);
}
return bytes;
}
}
idMem mem;
idMemLocal memLocal;
const char* GetMemTagName(const int tag) {
return tag >= 0 && tag < TAG_NUM_TAGS ? tagNames[tag] : "<BAD TAG NAME>";
}
void* idMem::AllocWithLocation(const char* const location,
const unsigned int size, const memTag_t tag, const bool zeroBuffer,
const align_t alignment, const heapType_t requestedHeap) {
const std::size_t allocationSize = size == 0 ? 1u : size;
const std::size_t requestedAlignment = static_cast<std::size_t>(alignment);
const std::size_t safeAlignment = requestedAlignment < sizeof(void*)
? sizeof(void*) : requestedAlignment;
void* pointer = _aligned_malloc(allocationSize, safeAlignment);
if (pointer == nullptr) {
idOutOfMemoryCallback callback = outOfMemoryCallback.load(
std::memory_order_acquire
);
if (callback != nullptr && callback()) {
pointer = _aligned_malloc(allocationSize, safeAlignment);
}
}
if (pointer == nullptr) {
return nullptr;
}
if (zeroBuffer) {
std::memset(pointer, 0, allocationSize);
}
const heapType_t usedHeap = requestedHeap == HEAP_DEFAULTHEAP
? currentHeap : requestedHeap;
AddRecord(pointer, size, tag, usedHeap, location);
return pointer;
}
void idMem::Free(void* const pointer, const align_t) {
if (pointer == nullptr) {
return;
}
RemoveRecord(pointer);
_aligned_free(pointer);
}
int idMem::BytesCurrentlyAllocated() const {
const unsigned long long bytes = allocatedBytes.load(std::memory_order_relaxed);
return bytes > static_cast<unsigned long long>((std::numeric_limits<int>::max)())
? (std::numeric_limits<int>::max)() : static_cast<int>(bytes);
}
void idMem::InitMapHeap() {
}
void idMem::ResetMapHeap() {
// The PC port uses the process CRT heap for both logical heaps. Allocations
// remain individually tracked so stale map allocations can still be found.
}
void idMem::PushHeap(const heapType_t heapType) {
if (heapStackDepth++ == 0) {
pushedHeap = currentHeap;
currentHeap = heapType == HEAP_DEFAULTHEAP ? HEAP_MAPHEAP : heapType;
}
}
void idMem::PopHeap() {
if (heapStackDepth <= 0) {
heapStackDepth = 0;
return;
}
if (--heapStackDepth == 0) {
currentHeap = pushedHeap;
}
}
bool idMem::IsGlobalHeap() const {
return currentHeap == HEAP_SYSTEMHEAP;
}
void idMem::SetOutOfMemoryCallback(const idOutOfMemoryCallback callback) {
outOfMemoryCallback.store(callback, std::memory_order_release);
}
idOutOfMemoryCallback idMem::GetOutOfMemoryCallback() const {
return outOfMemoryCallback.load(std::memory_order_acquire);
}
void idMem::WriteMemoryReport(const char* const directory,
const char* const fileName) const {
char path[MAX_PATH];
const char* const safeDirectory = directory == nullptr ? "." : directory;
const char* const safeFile = fileName == nullptr ? "memory_report.txt" : fileName;
std::snprintf(path, sizeof(path), "%s\\%s", safeDirectory, safeFile);
FILE* report = nullptr;
if (fopen_s(&report, path, "w") != 0 || report == nullptr) {
return;
}
std::fprintf(report, "bytes currently allocated: %d\n", BytesCurrentlyAllocated());
AcquireSRWLockShared(&allocationLock);
for (allocationRecord_t* record = allocations; record != nullptr;
record = record->next) {
std::fprintf(report, "%p %10u %-24s heap=%d %s\n", record->pointer,
record->bytes, GetMemTagName(record->tag), record->heap,
record->location == nullptr ? "<unknown>" : record->location);
}
ReleaseSRWLockShared(&allocationLock);
std::fclose(report);
}
+133
View File
@@ -0,0 +1,133 @@
#pragma once
#include <cstdint>
enum memTag_t : int {
TAG_UNSET = 0x00, TAG_STATIC_EXE, TAG_RESOURCE_GRAPH, TAG_DEBUG,
TAG_NEW, TAG_IDLIST, TAG_TEMP, TAG_STRING, TAG_ATOMIC_STRING,
TAG_BITARRAY, TAG_MATH, TAG_LEXER, TAG_COMPILER, TAG_COLLISION,
TAG_COLLISION_QUERY, TAG_CLIPMODEL, TAG_MORPH, TAG_MD6_MISC,
TAG_MD6_NODES, TAG_MD6, TAG_MD6_ANIMS, TAG_MD6_LIPSYNC,
TAG_MD6_JOINTCACHE, TAG_MD6_MESHES, TAG_MD6_JOINTBUFFERS,
TAG_MD6_BLENDSTACK, TAG_MD6_JOINTMODS, TAG_MD6_COLLISION,
TAG_MD6_ANIMEVENTS, TAG_MD6_PHASE_TRACK, TAG_ANIMATION,
TAG_ANIMATION_DEBUG, TAG_DECL_ANIMWEB, TAG_ANIMWEB, TAG_IMAGE,
TAG_DXIMAGE, TAG_VIRTUALTEXTURE, TAG_AAS, TAG_SOUND, TAG_SOUND_BSP,
TAG_SOUND_DATA, TAG_SOUND_STREAM, TAG_SOUND_MULTISTREAM,
TAG_SOUND_SAMPLETABLES, TAG_IDLIB, TAG_TRIANGLES, TAG_DECL,
TAG_DECLTEXT, TAG_FILE, TAG_CVAR, TAG_PAGEFILECACHE, TAG_IDCLASS,
TAG_PRESENTABLE, TAG_FOLIAGE, TAG_WATER, TAG_MEMORY_MAPPED_FILE,
TAG_RENDERPARM, TAG_NETWORKING, TAG_SCRIPT, TAG_FXPHYSICS, TAG_LWO,
TAG_RENDERWORLD, TAG_RENDERER, TAG_AI_GAMESTATE, TAG_EVENTS,
TAG_VOICEOVER, TAG_VOICETRACK_EVENTS, TAG_VOICETRACK_FRAMEREFS,
TAG_VOICETRACK_PHONEMES, TAG_VISEMESET_VISEMES,
TAG_VISEMESET_PHONEMES, TAG_AF, TAG_SWF, TAG_GUI, TAG_GUI_MODEL,
TAG_FUNC_CALLBACK, TAG_MENU, TAG_GAME, TAG_HASHINDEX, TAG_PARTICLE,
TAG_EFFECT_PARTICLE, TAG_CLOTH, TAG_ANIMTAGS, TAG_IK, TAG_STATICMODEL,
TAG_RENDERMODEL, TAG_DXBUFFER, TAG_TOOLS, TAG_CLOUD, TAG_AMQP,
TAG_RENDERPROG, TAG_HASHTABLE, TAG_AI_FSM, TAG_AI_VISCACHE,
TAG_AI_SEARCH, TAG_AI_OBSTACLE, TAG_JOBLIST, TAG_TRANSPARENCY,
TAG_DETAIL, TAG_RESOURCE, TAG_FILE_RESOURCE, TAG_RESOURCE_BGL,
TAG_RESOURCE_BGL_RING, TAG_RESOURCE_BGL_OVERSIZE, TAG_RESOURCE_MGR,
TAG_PVS, TAG_DEFERRED_VIS, TAG_FIBER, TAG_SUPERSCRIPT, TAG_FX,
TAG_SAVEGAMES, TAG_AI_TRANSITIONS, TAG_AI_STATEDATA, TAG_AI_COMBATANIM,
TAG_TYPEINFO, TAG_DAMAGEDECAL, TAG_TABLE, TAG_VIDEO, TAG_NAVPOWER,
TAG_LANGDICT, TAG_SPLINE, TAG_BINK, TAG_FONTS, TAG_EVENT_LISTENER,
TAG_PHYSICAL_BLOCK, TAG_DXOBJECT, TAG_NUM_TAGS
};
enum align_t : int {
ALIGN_16 = 0x10,
ALIGN_128 = 0x80,
ALIGN_1M = 0x100000
};
enum heapType_t : int {
HEAP_DEFAULTHEAP = -1,
HEAP_SYSTEMHEAP = 0,
HEAP_MAPHEAP = 1
};
using idOutOfMemoryCallback = bool (*)();
class idMem {
public:
void* AllocWithLocation(const char* location, unsigned int size,
memTag_t tag, bool zeroBuffer = false, align_t alignment = ALIGN_16,
heapType_t heap = HEAP_DEFAULTHEAP);
void Free(void* pointer, align_t alignment = ALIGN_16);
int BytesCurrentlyAllocated() const;
void InitMapHeap();
void ResetMapHeap();
void PushHeap(heapType_t heapType);
void PopHeap();
bool IsGlobalHeap() const;
void SetOutOfMemoryCallback(idOutOfMemoryCallback callback);
idOutOfMemoryCallback GetOutOfMemoryCallback() const;
void WriteMemoryReport(const char* directory, const char* fileName) const;
};
class idMemLocal : public idMem {
};
extern idMem mem;
extern idMemLocal memLocal;
const char* GetMemTagName(int tag);
bool Sys_AllocWillUseMapHeap();
void Sys_ReportHeaps();
void ReportGlobalMemoryStatus();
void* Sys_Alloc(unsigned int size, memTag_t tag,
align_t alignment = ALIGN_16,
heapType_t heap = HEAP_DEFAULTHEAP);
void Sys_Free(void* pointer);
unsigned int Sys_GetStreamFileCacheUsage();
unsigned int Sys_GetMemoryUsage();
unsigned int Sys_GetFreeMemory();
void Sys_WriteMemoryReport(const char* mapName, const char* version);
void Sys_DumpMemory();
class idPhysicalMemoryBlock {
public:
idPhysicalMemoryBlock();
void Init(int bytesToAllocate);
void RevertToDiscreteAllocations();
void BeginResourceLoads();
void EndResourceLoads(bool neverFreeAllocatedData);
void* PhysicalAlloc(unsigned int bytes, int alignment, memTag_t tag);
void* OverlayAlloc(unsigned int bytes, const char* name);
void OverlayFree(void* pointer);
bool AddressIsInReservedPhysicalMemoryBlock(const void* pointer) const;
bool AddressIsInOverlayPhysicalMemoryBlock(const void* pointer) const;
void ReportPhysicalMemoryBlock() const;
void ReportUntouchedPhysicalMemory() const;
private:
unsigned char* reservedPhysicalMemoryBlock;
int totalBlockSize;
int commonBytes;
int overlayBytes;
int cacheBytes;
bool insideResourceBlockLoad;
int physicalBytesAllocated;
int imageBytesAllocated;
int bufferBytesAllocated;
int otherBytesAllocated;
int alignmentWaste;
int bytesForcedOutsideBlock;
};
#if INTPTR_MAX == INT32_MAX
static_assert(sizeof(idPhysicalMemoryBlock) == 48,
"Recovered idPhysicalMemoryBlock ABI changed");
#endif
class idScopedGlobalHeap {
public:
idScopedGlobalHeap() { mem.PushHeap(HEAP_SYSTEMHEAP); }
~idScopedGlobalHeap() { mem.PopHeap(); }
};
+15
View File
@@ -0,0 +1,15 @@
#include "sys_mgrd.h"
// MGRD was an optional external memory-profiler transport selected with the
// Xbox-era -mgrd switch. The PC recovery keeps the instrumentation API intact
// while making it a no-op until a desktop profiler backend is selected.
void RD_Init() {}
void RD_CreateGPUHeaps(void*, unsigned int, void*, unsigned int) {}
void RD_DestroyGPUHeaps() {}
void RD_CreateMapHeap() {}
void RD_DestroyMapHeap() {}
void RD_MemAlloc(void*, unsigned int, unsigned int, int) {}
void RD_MemFree(void*, int) {}
void RD_EventBegin(const char*) {}
void RD_EventEnd() {}
void RD_Syncpoint(const char*) {}
+19
View File
@@ -0,0 +1,19 @@
#pragma once
void RD_Init();
void RD_CreateGPUHeaps(void* gpuMemory, unsigned int gpuBytes,
void* systemMemory, unsigned int systemBytes);
void RD_DestroyGPUHeaps();
void RD_CreateMapHeap();
void RD_DestroyMapHeap();
void RD_MemAlloc(void* pointer, unsigned int size, unsigned int waste, int heap);
void RD_MemFree(void* pointer, int heap);
void RD_EventBegin(const char* name);
void RD_EventEnd();
void RD_Syncpoint(const char* name);
class idRDScopedEvent {
public:
explicit idRDScopedEvent(const char* name) { RD_EventBegin(name); }
~idRDScopedEvent() { RD_EventEnd(); }
};
+123
View File
@@ -0,0 +1,123 @@
#pragma once
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <winsock2.h>
#include <ws2tcpip.h>
#include <cstdint>
#ifndef __SYS_PUBLIC__
enum netadrtype_t {
NA_BAD = 0,
NA_LOOPBACK,
NA_BROADCAST,
NA_IP
};
struct netadr_t {
netadrtype_t type;
unsigned char ip[4];
unsigned short port;
};
#endif
class idSimpleSerializer {
public:
idSimpleSerializer(unsigned char* buffer, int bufferSize, bool write)
: data(buffer), size(bufferSize), pos(0), writing(write) {
}
bool Serialize(unsigned char& value);
bool Serialize(unsigned int& value);
bool SerializeBytes(char* bytes, unsigned int& numBytes);
bool SerializeString(char* text, int maxSize);
int GetPos() const { return pos; }
int GetSize() const { return size; }
int GetSerializedSize() const { return writing ? pos : size; }
bool IsWriting() const { return writing; }
private:
unsigned char* data;
int size;
int pos;
bool writing;
};
#ifndef __SYS_PUBLIC__
class idUDP {
public:
idUDP();
virtual ~idUDP();
bool InitForPort(int portNumber, bool useBackend = false);
void Close();
bool GetPacket(netadr_t& from, void* data, int& size, int maxSize);
bool GetPacketBlocking(netadr_t& from, void* data, int& size, int maxSize,
int timeoutMS);
void SendPacket(netadr_t to, const void* data, int size);
int GetPort() const { return bound_to.port; }
netadr_t GetAdr() const { return bound_to; }
bool IsOpen() const { return netSocket != 0; }
void SetSilent(bool value) { silent = value; }
bool GetSilent() const { return silent; }
int packetsRead;
int bytesRead;
int packetsWritten;
int bytesWritten;
private:
netadr_t bound_to;
int netSocket;
bool silent;
};
#endif
class idTCP {
public:
idTCP();
virtual ~idTCP();
bool Connect(const char* host, unsigned short port, bool nonBlocking = false,
bool silent = false, bool nagle = true);
bool Select(int timeoutMS);
bool IsOpen() const;
void Close();
int Read(void* data, int size);
int ReadBlocking(void* data, int size, int timeoutMS);
int Write(const void* data, int size);
int WriteBlocking(const void* data, int size, int timeoutMS);
bool WriteDataBlock(const char* buffer, int size, int timeoutMS);
int ReadDataBlock(char* buffer, int bufferSize, int timeoutMS);
netadr_t GetAddress() const { return address; }
private:
netadr_t address;
int fd;
};
#ifndef __SYS_PUBLIC__
void Sys_InitNetworking();
void Sys_ShutdownNetworking();
bool Sys_StringToNetAdr(const char* text, netadr_t* address, bool doDNSResolve);
const char* Sys_NetAdrToString(const netadr_t& address);
bool Sys_IsLANAddress(const netadr_t& address);
bool Sys_CompareNetAdrBase(const netadr_t& left, const netadr_t& right);
int Sys_GetLocalIPCount();
const char* Sys_GetLocalIP(int index);
#endif
#if INTPTR_MAX == INT32_MAX
static_assert(sizeof(netadr_t) == 12, "Recovered netadr_t ABI changed");
static_assert(sizeof(idSimpleSerializer) == 16,
"Recovered idSimpleSerializer ABI changed");
#ifndef __SYS_PUBLIC__
static_assert(sizeof(idUDP) == 40, "Recovered idUDP ABI changed");
#endif
static_assert(sizeof(idTCP) == 20, "Recovered idTCP ABI changed");
#endif
+115
View File
@@ -0,0 +1,115 @@
#include "sys_time.h"
#include <algorithm>
#include <chrono>
#include <cstdio>
#include <ctime>
#if defined(_WIN32)
#include <Windows.h>
#endif
namespace {
thread_local char timeString[128];
bool LocalTime(const std::time_t value, std::tm& output) {
#if defined(_WIN32)
return localtime_s(&output, &value) == 0;
#else
return localtime_r(&value, &output) != nullptr;
#endif
}
}
std::uint64_t Sys_CurrentSystemTime() {
#if defined(_WIN32)
FILETIME fileTime;
GetSystemTimeAsFileTime(&fileTime);
return (static_cast<std::uint64_t>(fileTime.dwHighDateTime) << 32)
| static_cast<std::uint64_t>(fileTime.dwLowDateTime);
#else
constexpr std::uint64_t windowsEpochOffset = 116444736000000000ULL;
const auto ticks = std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::system_clock::now().time_since_epoch()
).count() / 100;
return windowsEpochOffset + static_cast<std::uint64_t>(ticks);
#endif
}
char* Sys_TimeStampToLogFormat(const int timeStamp) {
timeString[0] = '\0';
if (timeStamp == -1) {
return timeString;
}
const std::time_t source = static_cast<std::time_t>(timeStamp);
std::tm local = {};
if (LocalTime(source, local)) {
std::snprintf(timeString, sizeof(timeString),
"%d-%02d-%02dT%02d:%02d:%02dZ",
local.tm_year + 1900, local.tm_mon + 1, local.tm_mday,
local.tm_hour, local.tm_min, local.tm_sec);
}
return timeString;
}
char* Sys_TimeStampToStr(const int timeStamp, const bool padded) {
timeString[0] = '\0';
if (timeStamp == -1) {
return timeString;
}
const std::time_t source = static_cast<std::time_t>(timeStamp);
std::tm local = {};
if (!LocalTime(source, local)) {
return timeString;
}
int hour = local.tm_hour % 12;
if (hour == 0) {
hour = 12;
}
if (padded) {
std::snprintf(timeString, sizeof(timeString),
"%02d/%02d/%d %02d:%02d:%02d%s",
local.tm_mon + 1, local.tm_mday, local.tm_year + 1900,
hour, local.tm_min, local.tm_sec,
local.tm_hour < 12 ? "am" : "pm");
} else {
std::snprintf(timeString, sizeof(timeString),
"%d/%d/%d %d:%02d:%02d%s",
local.tm_mon + 1, local.tm_mday, local.tm_year + 1900,
hour, local.tm_min, local.tm_sec,
local.tm_hour < 12 ? "am" : "pm");
}
return timeString;
}
char* Sys_DateStr(const bool padded) {
return Sys_TimeStampToStr(static_cast<int>(std::time(nullptr)), padded);
}
idStr Sys_SecToStr(int seconds) {
idStr result;
if (seconds < 0) {
seconds = 0;
}
char buffer[32];
const int weeks = seconds / 604800;
if (weeks > 0) {
std::snprintf(buffer, sizeof(buffer), "%dw, ", weeks);
result.Append(buffer);
seconds %= 604800;
}
const int days = seconds / 86400;
if (weeks > 0 || days > 0) {
std::snprintf(buffer, sizeof(buffer), "%dd, ", days);
result.Append(buffer);
seconds %= 86400;
}
const int hours = seconds / 3600;
const int minutes = (seconds % 3600) / 60;
const int remainingSeconds = seconds % 60;
std::snprintf(buffer, sizeof(buffer), "%d:%02d:%02d",
hours, minutes, remainingSeconds);
result.Append(buffer);
return result;
}
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <cstdint>
#include "../text/str.h"
std::uint64_t Sys_CurrentSystemTime();
char* Sys_TimeStampToLogFormat(int timeStamp);
char* Sys_TimeStampToStr(int timeStamp, bool padded);
char* Sys_DateStr(bool padded);
idStr Sys_SecToStr(int seconds);
+5
View File
@@ -0,0 +1,5 @@
#pragma once
const char* Sys_GetOSUserName();
const char* Sys_GetMachineName();
@@ -0,0 +1,63 @@
#include "win_fibers.h"
#include <cstdlib>
#include <cstring>
namespace {
char* CopyFiberName(const char* source) {
const char* const text = source == nullptr ? "" : source;
const std::size_t length = std::strlen(text);
char* const copy = static_cast<char*>(std::malloc(length + 1));
if (copy != nullptr) {
std::memcpy(copy, text, length + 1);
}
return copy;
}
} // namespace
idSysFiber::idSysFiber(const char* fiberName)
: name(CopyFiberName(fiberName))
, alive(true)
, fiber(nullptr)
, parent(nullptr) {
fiber = CreateFiber(0x20000u, &idSysFiber::FiberRoutine, this);
if (fiber == nullptr) {
alive = false;
}
}
idSysFiber::~idSysFiber() {
std::free(name);
if (fiber != nullptr) {
DeleteFiber(fiber);
}
}
bool idSysFiber::Execute() {
if (alive && fiber != nullptr) {
parent = GetCurrentFiber();
SwitchToFiber(fiber);
}
return alive;
}
void idSysFiber::YieldFiber() {
if (parent != nullptr) {
SwitchToFiber(parent);
}
}
void WINAPI idSysFiber::FiberRoutine(void* data) {
idSysFiber* const self = static_cast<idSysFiber*>(data);
self->Run();
self->alive = false;
self->YieldFiber();
// A finished Windows fiber must never return from its entry routine when
// its parent is expected to retain control. Match the recovered guard.
for (;;) {
self->YieldFiber();
}
}
@@ -0,0 +1,28 @@
#pragma once
#include <Windows.h>
class idSysFiber {
public:
explicit idSysFiber(const char* name);
virtual ~idSysFiber();
idSysFiber(const idSysFiber&) = delete;
idSysFiber& operator=(const idSysFiber&) = delete;
bool Execute();
virtual void Run() = 0;
protected:
void YieldFiber();
private:
static void WINAPI FiberRoutine(void* data);
char* name;
bool alive;
void* fiber;
void* parent;
};
static_assert(sizeof(idSysFiber) == 20, "Recovered idSysFiber ABI changed");
@@ -0,0 +1,23 @@
#include "../sys_alloc.h"
#include <cstdio>
#include <Windows.h>
bool Sys_AllocWillUseMapHeap() {
return !mem.IsGlobalHeap();
}
void Sys_ReportHeaps() {
std::printf("PC logical heap: %s, tracked bytes: %d\n",
mem.IsGlobalHeap() ? "global" : "map", mem.BytesCurrentlyAllocated());
}
void ReportGlobalMemoryStatus() {
MEMORYSTATUSEX status = {};
status.dwLength = sizeof(status);
if (GlobalMemoryStatusEx(&status)) {
std::printf("physical memory: %llu / %llu bytes available\n",
static_cast<unsigned long long>(status.ullAvailPhys),
static_cast<unsigned long long>(status.ullTotalPhys));
}
}
+203
View File
@@ -0,0 +1,203 @@
#include "idlib/sys/sys_alloc.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#include <psapi.h>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <limits>
namespace {
unsigned int ClampToUInt64(const unsigned long long value) {
return value > (std::numeric_limits<unsigned int>::max)()
? (std::numeric_limits<unsigned int>::max)()
: static_cast<unsigned int>(value);
}
int AlignUp(const int value, const int alignment) {
const int safeAlignment = alignment <= 0 ? 16 : alignment;
return (value + safeAlignment - 1) & ~(safeAlignment - 1);
}
} // namespace
void MapVirtualAddressSpace() {
// Win32 already supplies a flat virtual address space. Logical map/system
// heap selection remains handled by idMem.
}
unsigned int Sys_GetStreamFileCacheUsage() {
// The PC filesystem owns its cache allocations through idMem rather than
// a carved physical-memory range.
return 0;
}
unsigned int Sys_GetMemoryUsage() {
PROCESS_MEMORY_COUNTERS_EX counters = {};
counters.cb = sizeof(counters);
if (!GetProcessMemoryInfo(GetCurrentProcess(),
reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&counters),
sizeof(counters))) {
return static_cast<unsigned int>(mem.BytesCurrentlyAllocated());
}
return ClampToUInt64(counters.PrivateUsage);
}
unsigned int Sys_GetFreeMemory() {
MEMORYSTATUSEX status = {};
status.dwLength = sizeof(status);
return GlobalMemoryStatusEx(&status) ? ClampToUInt64(status.ullAvailPhys) : 0;
}
void Sys_WriteMemoryReport(const char* mapName, const char* version) {
char fileName[192] = {};
std::snprintf(fileName, sizeof(fileName), "memory_%s_%s.txt",
mapName == nullptr || mapName[0] == '\0' ? "nomap" : mapName,
version == nullptr || version[0] == '\0' ? "unknown" : version);
for (char* cursor = fileName; *cursor != '\0'; ++cursor) {
if (*cursor == '\\' || *cursor == '/' || *cursor == ':'
|| *cursor == '*' || *cursor == '?' || *cursor == '"'
|| *cursor == '<' || *cursor == '>' || *cursor == '|') {
*cursor = '_';
}
}
mem.WriteMemoryReport(".", fileName);
}
void Sys_DumpMemory() {
mem.WriteMemoryReport(".", "memory_dump.txt");
}
void AddTagStats(int, int, int, int) {
// idMem records tags per allocation, so a second accounting table would
// double count on PC.
}
void SubtractTagStats(int, int, int, int) {
}
void* XMemAlloc(const unsigned int size, const int) {
return mem.AllocWithLocation("XMemAlloc PC replacement", size, TAG_IDLIB,
false, ALIGN_16, HEAP_DEFAULTHEAP);
}
void XMemFree(unsigned char* pointer, const unsigned int) {
mem.Free(pointer);
}
void* Sys_Alloc(const unsigned int size, const memTag_t tag,
const align_t alignment, const heapType_t heap) {
return mem.AllocWithLocation("Sys_Alloc PC replacement", size, tag,
false, alignment, heap);
}
void Sys_Free(void* pointer) {
mem.Free(pointer);
}
idPhysicalMemoryBlock::idPhysicalMemoryBlock()
: reservedPhysicalMemoryBlock(nullptr), totalBlockSize(0), commonBytes(0),
overlayBytes(0), cacheBytes(0), insideResourceBlockLoad(false),
physicalBytesAllocated(0), imageBytesAllocated(0), bufferBytesAllocated(0),
otherBytesAllocated(0), alignmentWaste(0), bytesForcedOutsideBlock(0) {
}
void idPhysicalMemoryBlock::Init(const int bytesToAllocate) {
if (reservedPhysicalMemoryBlock != nullptr || bytesToAllocate <= 0) return;
totalBlockSize = AlignUp(bytesToAllocate, 65536);
reservedPhysicalMemoryBlock = static_cast<unsigned char*>(
mem.AllocWithLocation("idPhysicalMemoryBlock PC reserve",
totalBlockSize, TAG_PHYSICAL_BLOCK, true, ALIGN_16,
HEAP_SYSTEMHEAP));
if (reservedPhysicalMemoryBlock == nullptr) totalBlockSize = 0;
}
void idPhysicalMemoryBlock::RevertToDiscreteAllocations() {
if (physicalBytesAllocated != 0) return;
mem.Free(reservedPhysicalMemoryBlock);
reservedPhysicalMemoryBlock = nullptr;
totalBlockSize = 0;
commonBytes = overlayBytes = cacheBytes = 0;
}
void idPhysicalMemoryBlock::BeginResourceLoads() {
insideResourceBlockLoad = true;
}
void idPhysicalMemoryBlock::EndResourceLoads(const bool neverFreeAllocatedData) {
insideResourceBlockLoad = false;
physicalBytesAllocated = std::min(AlignUp(physicalBytesAllocated, 65536),
totalBlockSize);
if (neverFreeAllocatedData) commonBytes = physicalBytesAllocated;
cacheBytes = std::max(0, totalBlockSize - physicalBytesAllocated);
overlayBytes = 0;
}
void* idPhysicalMemoryBlock::PhysicalAlloc(const unsigned int bytes,
const int alignment, const memTag_t tag) {
const int alignedOffset = AlignUp(physicalBytesAllocated, alignment);
alignmentWaste += alignedOffset - physicalBytesAllocated;
if (reservedPhysicalMemoryBlock != nullptr
&& alignedOffset >= 0
&& bytes <= static_cast<unsigned int>(totalBlockSize - alignedOffset)) {
physicalBytesAllocated = alignedOffset + static_cast<int>(bytes);
if (tag == TAG_DXIMAGE) imageBytesAllocated += bytes;
else if (tag == TAG_DXBUFFER) bufferBytesAllocated += bytes;
else otherBytesAllocated += bytes;
return reservedPhysicalMemoryBlock + alignedOffset;
}
bytesForcedOutsideBlock += bytes;
return mem.AllocWithLocation("physical allocation fallback", bytes, tag,
false, alignment >= ALIGN_128 ? ALIGN_128 : ALIGN_16,
HEAP_SYSTEMHEAP);
}
void* idPhysicalMemoryBlock::OverlayAlloc(const unsigned int bytes,
const char*) {
// PC resources are individually reclaimable; keeping overlays discrete
// avoids the Xenon-only 64 KiB overlay fragmentation rules.
return mem.AllocWithLocation("overlay allocation PC replacement", bytes,
TAG_PHYSICAL_BLOCK, false, ALIGN_16, HEAP_SYSTEMHEAP);
}
void idPhysicalMemoryBlock::OverlayFree(void* pointer) {
if (!AddressIsInReservedPhysicalMemoryBlock(pointer)) mem.Free(pointer);
}
bool idPhysicalMemoryBlock::AddressIsInReservedPhysicalMemoryBlock(
const void* pointer) const {
const unsigned char* const address = static_cast<const unsigned char*>(pointer);
return reservedPhysicalMemoryBlock != nullptr
&& address >= reservedPhysicalMemoryBlock
&& address < reservedPhysicalMemoryBlock + totalBlockSize;
}
bool idPhysicalMemoryBlock::AddressIsInOverlayPhysicalMemoryBlock(
const void* pointer) const {
if (!AddressIsInReservedPhysicalMemoryBlock(pointer) || overlayBytes <= 0) {
return false;
}
const unsigned char* const overlayStart =
reservedPhysicalMemoryBlock + totalBlockSize - overlayBytes;
return static_cast<const unsigned char*>(pointer) >= overlayStart;
}
void idPhysicalMemoryBlock::ReportPhysicalMemoryBlock() const {
std::printf("physical block: %d/%d bytes, images=%d buffers=%d other=%d "
"alignment=%d fallback=%d\n", physicalBytesAllocated, totalBlockSize,
imageBytesAllocated, bufferBytesAllocated, otherBytesAllocated,
alignmentWaste, bytesForcedOutsideBlock);
}
void idPhysicalMemoryBlock::ReportUntouchedPhysicalMemory() const {
std::printf("physical block untouched/available: %d bytes\n",
std::max(0, totalBlockSize - physicalBytesAllocated));
}
+431
View File
@@ -0,0 +1,431 @@
#include "idlib/sys/sys_networking.h"
#include <algorithm>
#include <array>
#include <cstdio>
#include <cstring>
#include <mutex>
#include <string>
#include <vector>
namespace {
std::mutex networkMutex;
bool networkInitialized = false;
std::vector<std::string> localAddresses;
SOCKET ToSocket(const int value) {
return static_cast<SOCKET>(static_cast<unsigned int>(value));
}
int FromSocket(const SOCKET value) {
return static_cast<int>(value);
}
void ClearAddress(netadr_t& address) {
std::memset(&address, 0, sizeof(address));
address.type = NA_BAD;
}
void NetAdrToSockAdr(const netadr_t& source, sockaddr_in& destination) {
std::memset(&destination, 0, sizeof(destination));
destination.sin_family = AF_INET;
if (source.type == NA_BROADCAST) {
destination.sin_addr.s_addr = INADDR_BROADCAST;
} else {
std::memcpy(&destination.sin_addr.s_addr, source.ip, sizeof(source.ip));
}
destination.sin_port = htons(source.port);
}
void SockAdrToNetAdr(const sockaddr_in& source, netadr_t& destination) {
std::memset(&destination, 0, sizeof(destination));
std::memcpy(destination.ip, &source.sin_addr.s_addr, sizeof(destination.ip));
destination.port = ntohs(source.sin_port);
destination.type = ntohl(source.sin_addr.s_addr) == INADDR_LOOPBACK
? NA_LOOPBACK : NA_IP;
}
bool WaitForSocket(const SOCKET socketValue, const int timeoutMS,
const bool write) {
if (socketValue == INVALID_SOCKET) return false;
fd_set set;
FD_ZERO(&set);
FD_SET(socketValue, &set);
timeval timeout = {};
timeout.tv_sec = timeoutMS < 0 ? 0 : timeoutMS / 1000;
timeout.tv_usec = timeoutMS < 0 ? 0 : (timeoutMS % 1000) * 1000;
const int result = select(0, write ? nullptr : &set,
write ? &set : nullptr, nullptr, timeoutMS < 0 ? nullptr : &timeout);
return result > 0 && FD_ISSET(socketValue, &set) != 0;
}
void EnsureNetworking() {
std::lock_guard<std::mutex> guard(networkMutex);
if (networkInitialized) return;
WSADATA data = {};
if (WSAStartup(MAKEWORD(2, 2), &data) != 0) {
return;
}
networkInitialized = true;
localAddresses.clear();
char hostName[256] = {};
if (gethostname(hostName, sizeof(hostName)) == 0) {
addrinfo hints = {};
hints.ai_family = AF_INET;
addrinfo* result = nullptr;
if (getaddrinfo(hostName, nullptr, &hints, &result) == 0) {
for (addrinfo* item = result; item != nullptr; item = item->ai_next) {
const sockaddr_in* address =
reinterpret_cast<const sockaddr_in*>(item->ai_addr);
char text[INET_ADDRSTRLEN] = {};
if (inet_ntop(AF_INET, &address->sin_addr, text, sizeof(text))
!= nullptr
&& std::find(localAddresses.begin(), localAddresses.end(), text)
== localAddresses.end()) {
localAddresses.emplace_back(text);
}
}
freeaddrinfo(result);
}
}
if (localAddresses.empty()) localAddresses.emplace_back("127.0.0.1");
}
} // namespace
bool idSimpleSerializer::Serialize(unsigned char& value) {
if (data == nullptr || pos < 0 || pos + 1 > size) return false;
if (writing) data[pos] = value;
else value = data[pos];
++pos;
return true;
}
bool idSimpleSerializer::Serialize(unsigned int& value) {
if (data == nullptr || pos < 0 || pos + 4 > size) return false;
if (writing) {
data[pos + 0] = static_cast<unsigned char>(value >> 0);
data[pos + 1] = static_cast<unsigned char>(value >> 8);
data[pos + 2] = static_cast<unsigned char>(value >> 16);
data[pos + 3] = static_cast<unsigned char>(value >> 24);
} else {
value = static_cast<unsigned int>(data[pos + 0])
| (static_cast<unsigned int>(data[pos + 1]) << 8)
| (static_cast<unsigned int>(data[pos + 2]) << 16)
| (static_cast<unsigned int>(data[pos + 3]) << 24);
}
pos += 4;
return true;
}
bool idSimpleSerializer::SerializeBytes(char* bytes, unsigned int& numBytes) {
const unsigned int capacity = numBytes;
if (!Serialize(numBytes)) return false;
if (!writing && numBytes > capacity) {
numBytes = 0;
return false;
}
if (numBytes > static_cast<unsigned int>(size - pos)
|| (numBytes > 0 && bytes == nullptr)) {
return false;
}
if (writing) std::memcpy(data + pos, bytes, numBytes);
else std::memcpy(bytes, data + pos, numBytes);
pos += static_cast<int>(numBytes);
return true;
}
bool idSimpleSerializer::SerializeString(char* text, const int maxSize) {
if (text == nullptr || maxSize <= 0) return false;
unsigned int bytes = writing
? static_cast<unsigned int>(std::strlen(text))
: static_cast<unsigned int>(maxSize - 1);
if (!SerializeBytes(text, bytes)) return false;
if (!writing) text[bytes] = '\0';
return true;
}
void Sys_InitNetworking() {
EnsureNetworking();
}
void Sys_ShutdownNetworking() {
std::lock_guard<std::mutex> guard(networkMutex);
if (!networkInitialized) return;
localAddresses.clear();
WSACleanup();
networkInitialized = false;
}
bool Sys_StringToNetAdr(const char* text, netadr_t* address,
const bool doDNSResolve) {
if (text == nullptr || address == nullptr) return false;
EnsureNetworking();
std::string host(text);
unsigned short port = 0;
const std::size_t separator = host.rfind(':');
if (separator != std::string::npos) {
const long parsed = std::strtol(host.c_str() + separator + 1, nullptr, 10);
if (parsed < 0 || parsed > 65535) return false;
port = static_cast<unsigned short>(parsed);
host.resize(separator);
}
if (host == "localhost") host = "127.0.0.1";
sockaddr_in socketAddress = {};
socketAddress.sin_family = AF_INET;
socketAddress.sin_port = htons(port);
if (inet_pton(AF_INET, host.c_str(), &socketAddress.sin_addr) != 1) {
if (!doDNSResolve) return false;
addrinfo hints = {};
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
addrinfo* result = nullptr;
if (getaddrinfo(host.c_str(), nullptr, &hints, &result) != 0
|| result == nullptr) {
if (result != nullptr) freeaddrinfo(result);
return false;
}
socketAddress.sin_addr =
reinterpret_cast<sockaddr_in*>(result->ai_addr)->sin_addr;
freeaddrinfo(result);
}
SockAdrToNetAdr(socketAddress, *address);
return true;
}
const char* Sys_NetAdrToString(const netadr_t& address) {
thread_local char result[64];
const char* prefix = address.type == NA_LOOPBACK ? "127.0.0.1" : nullptr;
char ipText[INET_ADDRSTRLEN] = {};
in_addr ipAddress = {};
std::memcpy(&ipAddress.s_addr, address.ip, sizeof(address.ip));
if (prefix == nullptr) {
prefix = inet_ntop(AF_INET, &ipAddress, ipText, sizeof(ipText));
}
if (prefix == nullptr) prefix = "0.0.0.0";
if (address.port != 0) {
std::snprintf(result, sizeof(result), "%s:%u", prefix, address.port);
} else {
std::snprintf(result, sizeof(result), "%s", prefix);
}
return result;
}
bool Sys_IsLANAddress(const netadr_t& address) {
if (address.type == NA_LOOPBACK) return true;
if (address.type != NA_IP) return false;
return address.ip[0] == 10
|| (address.ip[0] == 172 && address.ip[1] >= 16 && address.ip[1] <= 31)
|| (address.ip[0] == 192 && address.ip[1] == 168)
|| address.ip[0] == 127;
}
bool Sys_CompareNetAdrBase(const netadr_t& left, const netadr_t& right) {
if (left.type != right.type) return false;
if (left.type == NA_LOOPBACK) return true;
if (left.type != NA_IP && left.type != NA_BROADCAST) return false;
return std::memcmp(left.ip, right.ip, sizeof(left.ip)) == 0;
}
int Sys_GetLocalIPCount() {
EnsureNetworking();
return static_cast<int>(localAddresses.size());
}
const char* Sys_GetLocalIP(const int index) {
EnsureNetworking();
return index >= 0 && index < static_cast<int>(localAddresses.size())
? localAddresses[index].c_str() : nullptr;
}
idUDP::idUDP()
: packetsRead(0), bytesRead(0), packetsWritten(0), bytesWritten(0),
netSocket(0), silent(false) {
ClearAddress(bound_to);
}
idUDP::~idUDP() { Close(); }
bool idUDP::InitForPort(const int portNumber, const bool) {
Close();
EnsureNetworking();
const SOCKET socketValue = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (socketValue == INVALID_SOCKET) return false;
u_long nonBlocking = 1;
ioctlsocket(socketValue, FIONBIO, &nonBlocking);
BOOL broadcast = TRUE;
setsockopt(socketValue, SOL_SOCKET, SO_BROADCAST,
reinterpret_cast<const char*>(&broadcast), sizeof(broadcast));
sockaddr_in bindAddress = {};
bindAddress.sin_family = AF_INET;
bindAddress.sin_addr.s_addr = htonl(INADDR_ANY);
bindAddress.sin_port = htons(static_cast<unsigned short>(portNumber));
if (bind(socketValue, reinterpret_cast<sockaddr*>(&bindAddress),
sizeof(bindAddress)) == SOCKET_ERROR) {
closesocket(socketValue);
return false;
}
int addressLength = sizeof(bindAddress);
getsockname(socketValue, reinterpret_cast<sockaddr*>(&bindAddress), &addressLength);
SockAdrToNetAdr(bindAddress, bound_to);
netSocket = FromSocket(socketValue);
return true;
}
void idUDP::Close() {
if (netSocket != 0) closesocket(ToSocket(netSocket));
netSocket = 0;
ClearAddress(bound_to);
}
bool idUDP::GetPacket(netadr_t& from, void* data, int& dataSize,
const int maxSize) {
if (!IsOpen() || data == nullptr || maxSize <= 0) return false;
sockaddr_in source = {};
int sourceLength = sizeof(source);
const int received = recvfrom(ToSocket(netSocket), static_cast<char*>(data),
maxSize, 0, reinterpret_cast<sockaddr*>(&source), &sourceLength);
if (received == SOCKET_ERROR) return false;
SockAdrToNetAdr(source, from);
dataSize = received;
++packetsRead;
bytesRead += received;
return true;
}
bool idUDP::GetPacketBlocking(netadr_t& from, void* data, int& dataSize,
const int maxSize, const int timeoutMS) {
return WaitForSocket(ToSocket(netSocket), timeoutMS, false)
&& GetPacket(from, data, dataSize, maxSize);
}
void idUDP::SendPacket(const netadr_t to, const void* data, const int dataSize) {
if (!IsOpen() || data == nullptr || dataSize < 0 || to.type == NA_BAD) return;
sockaddr_in destination = {};
NetAdrToSockAdr(to, destination);
const int sent = sendto(ToSocket(netSocket), static_cast<const char*>(data),
dataSize, 0, reinterpret_cast<const sockaddr*>(&destination),
sizeof(destination));
if (sent >= 0) {
++packetsWritten;
bytesWritten += sent;
}
}
idTCP::idTCP() : fd(0) { ClearAddress(address); }
idTCP::~idTCP() { Close(); }
bool idTCP::Connect(const char* host, const unsigned short port,
const bool nonBlocking, const bool, const bool nagle) {
Close();
EnsureNetworking();
if (!Sys_StringToNetAdr(host, &address, true)) return false;
if (address.port == 0) address.port = port;
address.type = address.type == NA_LOOPBACK ? NA_LOOPBACK : NA_IP;
sockaddr_in destination = {};
NetAdrToSockAdr(address, destination);
const SOCKET socketValue = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (socketValue == INVALID_SOCKET) return false;
BOOL noDelay = nagle ? FALSE : TRUE;
setsockopt(socketValue, IPPROTO_TCP, TCP_NODELAY,
reinterpret_cast<const char*>(&noDelay), sizeof(noDelay));
if (nonBlocking) {
u_long mode = 1;
ioctlsocket(socketValue, FIONBIO, &mode);
}
const int result = connect(socketValue,
reinterpret_cast<const sockaddr*>(&destination), sizeof(destination));
if (result == SOCKET_ERROR) {
const int error = WSAGetLastError();
if (!nonBlocking || (error != WSAEWOULDBLOCK
&& error != WSAEINPROGRESS && error != WSAEALREADY)) {
closesocket(socketValue);
return false;
}
}
fd = FromSocket(socketValue);
return true;
}
bool idTCP::Select(const int timeoutMS) {
return WaitForSocket(ToSocket(fd), timeoutMS, false);
}
bool idTCP::IsOpen() const { return fd != 0; }
void idTCP::Close() {
if (fd != 0) closesocket(ToSocket(fd));
fd = 0;
}
int idTCP::Read(void* data, const int dataSize) {
if (!IsOpen() || data == nullptr || dataSize < 0) return -1;
const int result = recv(ToSocket(fd), static_cast<char*>(data), dataSize, 0);
if (result > 0) return result;
if (result == 0) {
Close();
return -2;
}
if (WSAGetLastError() == WSAEWOULDBLOCK) return 0;
Close();
return -1;
}
int idTCP::ReadBlocking(void* data, const int dataSize, const int timeoutMS) {
int total = 0;
while (total < dataSize && WaitForSocket(ToSocket(fd), timeoutMS, false)) {
const int received = Read(static_cast<char*>(data) + total, dataSize - total);
if (received < 0) return received;
total += received;
}
return total;
}
int idTCP::Write(const void* data, const int dataSize) {
if (!IsOpen() || data == nullptr || dataSize < 0) return -1;
const int result = send(ToSocket(fd), static_cast<const char*>(data), dataSize, 0);
if (result >= 0) return result;
if (WSAGetLastError() == WSAEWOULDBLOCK) return 0;
Close();
return -1;
}
int idTCP::WriteBlocking(const void* data, const int dataSize, const int timeoutMS) {
int total = 0;
while (total < dataSize && WaitForSocket(ToSocket(fd), timeoutMS, true)) {
const int sent = Write(static_cast<const char*>(data) + total, dataSize - total);
if (sent < 0) return sent;
total += sent;
}
return total;
}
bool idTCP::WriteDataBlock(const char* buffer, const int dataSize,
const int timeoutMS) {
unsigned char lengthData[4] = {};
unsigned int length = static_cast<unsigned int>(dataSize);
idSimpleSerializer serializer(lengthData, sizeof(lengthData), true);
return serializer.Serialize(length)
&& WriteBlocking(lengthData, sizeof(lengthData), timeoutMS)
== sizeof(lengthData)
&& WriteBlocking(buffer, dataSize, timeoutMS) == dataSize;
}
int idTCP::ReadDataBlock(char* buffer, const int bufferSize,
const int timeoutMS) {
unsigned char lengthData[4] = {};
if (ReadBlocking(lengthData, sizeof(lengthData), timeoutMS)
!= sizeof(lengthData)) return -1;
unsigned int length = 0;
idSimpleSerializer serializer(lengthData, sizeof(lengthData), false);
if (!serializer.Serialize(length) || length > static_cast<unsigned int>(bufferSize)) {
return -1;
}
return ReadBlocking(buffer, static_cast<int>(length), timeoutMS)
== static_cast<int>(length) ? static_cast<int>(length) : -1;
}
@@ -0,0 +1,25 @@
#include "idlib/sys/sys_utils.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
const char* Sys_GetOSUserName() {
static char userName[64] = {};
DWORD length = static_cast<DWORD>(sizeof(userName));
if (!GetUserNameA(userName, &length)) {
userName[0] = '\0';
}
return userName;
}
const char* Sys_GetMachineName() {
static char machineName[64] = {};
DWORD length = static_cast<DWORD>(sizeof(machineName));
if (!GetComputerNameA(machineName, &length)) {
machineName[0] = '\0';
}
return machineName;
}