First pass idLib conversion from hex rays4.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
@@ -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(); }
|
||||
};
|
||||
@@ -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*) {}
|
||||
@@ -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(); }
|
||||
};
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user