diff --git a/core/Makefile b/core/Makefile index 0f0f0768..72c0e3d9 100644 --- a/core/Makefile +++ b/core/Makefile @@ -2,13 +2,12 @@ # Makefile written by David "BAILOPAN" Anderson SMSDK = .. -SRCDS_BASE = ~/srcds HL2SDK_ORIG = ../../hl2sdk HL2SDK_OB = ../../hl2sdk-ob HL2SDK_OB_VALVE = ../../hl2sdk-ob-valve HL2SDK_L4D = ../../hl2sdk-l4d HL2SDK_L4D2 = ../../hl2sdk-l4d2 -MMSOURCE17 = ../../mmsource-central +MMSOURCE = ../../mmsource-central ##################################### ### EDIT BELOW FOR OTHER PROJECTS ### @@ -48,71 +47,80 @@ CPP_GCC4_FLAGS = -fvisibility-inlines-hidden CPP = gcc override ENGSET = false + +ifneq (,$(filter original orangebox orangeboxvalve left4dead left4dead2,$(ENGINE))) + override ENGSET = true +endif + ifeq "$(ENGINE)" "original" HL2SDK = $(HL2SDK_ORIG) - HL2PUB = $(HL2SDK)/public HL2LIB = $(HL2SDK)/linux_sdk CFLAGS += -DSOURCE_ENGINE=1 - METAMOD = $(MMSOURCE17)/core-legacy + METAMOD = $(MMSOURCE)/core-legacy INCLUDE += -I$(HL2SDK)/public/dlls - SRCDS = $(SRCDS_BASE) BINARY = sourcemod.1.ep1.so - override ENGSET = true endif ifeq "$(ENGINE)" "orangebox" HL2SDK = $(HL2SDK_OB) - HL2PUB = $(HL2SDK)/public HL2LIB = $(HL2SDK)/lib/linux CFLAGS += -DSOURCE_ENGINE=3 - METAMOD = $(MMSOURCE17)/core + METAMOD = $(MMSOURCE)/core INCLUDE += -I$(HL2SDK)/public/game/server - SRCDS = $(SRCDS_BASE)/orangebox BINARY = sourcemod.2.ep2.so - override ENGSET = true endif ifeq "$(ENGINE)" "orangeboxvalve" HL2SDK = $(HL2SDK_OB_VALVE) - HL2PUB = $(HL2SDK)/public HL2LIB = $(HL2SDK)/lib/linux CFLAGS += -DSOURCE_ENGINE=4 - METAMOD = $(MMSOURCE17)/core - INCLUDE += -I$(HL2SDK)/public/game/server -I$(HL2SDK)/common - SRCDS = $(SRCDS_BASE)/orangebox + METAMOD = $(MMSOURCE)/core + INCLUDE += -I$(HL2SDK)/public/game/server +#-I$(HL2SDK)/common BINARY = sourcemod.2.ep2v.so - override ENGSET = true endif ifeq "$(ENGINE)" "left4dead" HL2SDK = $(HL2SDK_L4D) - HL2PUB = $(HL2SDK)/public HL2LIB = $(HL2SDK)/lib/linux CFLAGS += -DSOURCE_ENGINE=5 - METAMOD = $(MMSOURCE17)/core + METAMOD = $(MMSOURCE)/core INCLUDE += -I$(HL2SDK)/public/game/server - SRCDS = $(SRCDS_BASE)/l4d BINARY = sourcemod.2.l4d.so - override ENGSET = true endif ifeq "$(ENGINE)" "left4dead2" HL2SDK = $(HL2SDK_L4D2) - HL2PUB = $(HL2SDK)/public HL2LIB = $(HL2SDK)/lib/linux CFLAGS += -DSOURCE_ENGINE=6 - METAMOD = $(MMSOURCE17)/core + METAMOD = $(MMSOURCE)/core INCLUDE += -I$(HL2SDK)/public/game/server - SRCDS = $(SRCDS_BASE)/left4dead2 BINARY = sourcemod.2.l4d2.so - override ENGSET = true +endif + +HL2PUB = $(HL2SDK)/public + +OS := $(shell uname -s) + +ifeq "$(OS)" "Darwin" + LIB_EXT = dylib + HL2LIB = $(HL2SDK)/lib/mac +else + LIB_EXT = so + ifeq "$(ENGINE)" "original" + HL2LIB = $(HL2SDK)/linux_sdk + else + HL2LIB = $(HL2SDK)/lib/linux + endif +endif + +ifneq (,$(filter original orangebox left4dead,$(ENGINE))) + LIB_SUFFIX = _i486.$(LIB_EXT) +else + LIB_PREFIX = lib + LIB_SUFFIX = .$(LIB_EXT) endif CFLAGS += -DSE_EPISODEONE=1 -DSE_DARKMESSIAH=2 -DSE_ORANGEBOX=3 -DSE_ORANGEBOXVALVE=4 -DSE_LEFT4DEAD=5 -DSE_LEFT4DEAD2=6 -ifeq "$(ENGINE)" "left4dead2" -LINK += $(HL2LIB)/tier1_i486.a $(HL2LIB)/mathlib_i486.a libvstdlib.so \ - libtier0.so -static-libgcc -else -LINK += $(HL2LIB)/tier1_i486.a $(HL2LIB)/mathlib_i486.a vstdlib_i486.so \ - tier0_i486.so -static-libgcc -endif +LINK += $(HL2LIB)/tier1_i486.a $(HL2LIB)/mathlib_i486.a $(LIB_PREFIX)vstdlib$(LIB_SUFFIX) \ + $(LIB_PREFIX)tier0$(LIB_SUFFIX) -static-libgcc INCLUDE += -I. -I.. -I$(HL2PUB) -I$(HL2PUB)/engine -I$(HL2PUB)/mathlib -I$(HL2PUB)/vstdlib \ -I$(HL2PUB)/tier0 -I$(HL2PUB)/tier1 -I$(METAMOD) -I$(METAMOD)/sourcehook \ @@ -152,18 +160,14 @@ $(BIN_DIR)/%.o: %.c all: check mkdir -p $(BIN_DIR) -ifeq "$(ENGINE)" "left4dead2" - ln -sf $(SRCDS)/bin/libvstdlib.so libvstdlib.so; - ln -sf $(SRCDS)/bin/libtier0.so libtier0.so; -else - ln -sf $(SRCDS)/bin/vstdlib_i486.so vstdlib_i486.so; - ln -sf $(SRCDS)/bin/tier0_i486.so tier0_i486.so; -endif + ln -sf $(HL2LIB)/$(LIB_PREFIX)vstdlib$(LIB_SUFFIX) + ln -sf $(HL2LIB)/$(LIB_PREFIX)tier0$(LIB_SUFFIX) $(MAKE) -f Makefile sourcemod check: if [ "$(ENGSET)" = "false" ]; then \ - echo "You must supply ENGINE=left4dead or ENGINE=orangebox or ENGINE=original"; \ + echo "You must supply one of the following values for ENGINE:"; \ + echo "left4dead2, left4dead, orangeboxvalve, orangebox, or original"; \ exit 1; \ fi diff --git a/core/PluginSys.cpp b/core/PluginSys.cpp index 316c8026..c75434bb 100644 --- a/core/PluginSys.cpp +++ b/core/PluginSys.cpp @@ -2741,6 +2741,6 @@ void CPluginManager::ListPluginsToClient(CPlayer *player, const CCommand &args) /* Do we actually have more plugins? */ if (iter != m_plugins.end()) { - ClientConsolePrint(e, "To see more, type \"sm plugins %d\"", id + 1); + ClientConsolePrint(e, "To see more, type \"sm plugins %d\"", id); } } diff --git a/extensions/sdktools/vsound.cpp b/extensions/sdktools/vsound.cpp index 70c7274b..60a04f9a 100644 --- a/extensions/sdktools/vsound.cpp +++ b/extensions/sdktools/vsound.cpp @@ -630,6 +630,179 @@ static cell_t EmitSound(IPluginContext *pContext, const cell_t *params) return 1; } +static cell_t EmitSound2(IPluginContext *pContext, const cell_t *params) +{ + cell_t *addr, *cl_array; + CellRecipientFilter crf; + unsigned int numClients; + int client; + IGamePlayer *pPlayer = NULL; + + pContext->LocalToPhysAddr(params[1], &cl_array); + numClients = params[2]; + + /* Client validation */ + for (unsigned int i = 0; i < numClients; i++) + { + client = cl_array[i]; + pPlayer = playerhelpers->GetGamePlayer(client); + + if (!pPlayer) + { + return pContext->ThrowNativeError("Client index %d is invalid", client); + } else if (!pPlayer->IsInGame()) { + return pContext->ThrowNativeError("Client %d is not connected", client); + } + } + + crf.Initialize(cl_array, numClients); + + char *sample; + pContext->LocalToString(params[3], &sample); + + int entity = SoundReferenceToIndex(params[4]); + int channel = params[5]; + float level = sp_ctof(params[6]); + int flags = params[7]; + float vol = sp_ctof(params[8]); + int pitch = params[9]; + int speakerentity = params[10]; + + Vector *pOrigin = NULL, origin; + Vector *pDir = NULL, dir; + + pContext->LocalToPhysAddr(params[11], &addr); + if (addr != pContext->GetNullRef(SP_NULL_VECTOR)) + { + pOrigin = &origin; + origin.x = sp_ctof(addr[0]); + origin.y = sp_ctof(addr[1]); + origin.z = sp_ctof(addr[2]); + } + + pContext->LocalToPhysAddr(params[12], &addr); + if (addr != pContext->GetNullRef(SP_NULL_VECTOR)) + { + pDir = &dir; + dir.x = sp_ctof(addr[0]); + dir.y = sp_ctof(addr[1]); + dir.z = sp_ctof(addr[2]); + } + + bool updatePos = params[13] ? true : false; + float soundtime = sp_ctof(params[14]); + + CUtlVector *pOrigVec = NULL; + CUtlVector origvec; + if (params[0] > 14) + { + pOrigVec = &origvec; + for (cell_t i = 15; i <= params[0]; i++) + { + Vector vec; + pContext->LocalToPhysAddr(params[i], &addr); + vec.x = sp_ctof(addr[0]); + vec.y = sp_ctof(addr[1]); + vec.z = sp_ctof(addr[2]); + origvec.AddToTail(vec); + } + } + + /* If we're going to a "local player" and this is a dedicated server, + * intelligently redirect each sound. + */ + + if (entity == -2 && engine->IsDedicatedServer()) + { + for (unsigned int i = 0; i < numClients; i++) + { + cell_t player[1]; + player[0] = cl_array[i]; + crf.Reset(); + crf.Initialize(player, 1); + if (g_InSoundHook) + { + SH_CALL(enginesoundPatch, + static_cast *, bool, float, int)> + (&IEngineSound::EmitSound)) + (crf, + player[0], + channel, + sample, + vol, + level, + flags, + pitch, + pOrigin, + pDir, + pOrigVec, + updatePos, + soundtime, + speakerentity); + } + else + { + engsound->EmitSound(crf, + player[0], + channel, + sample, + vol, + level, + flags, + pitch, + pOrigin, + pDir, + pOrigVec, + updatePos, + soundtime, + speakerentity); + } + } + } else { + if (g_InSoundHook) + { + SH_CALL(enginesoundPatch, + static_cast *, bool, float, int)> + (&IEngineSound::EmitSound)) + (crf, + entity, + channel, + sample, + vol, + level, + flags, + pitch, + pOrigin, + pDir, + pOrigVec, + updatePos, + soundtime, + speakerentity); + } + else + { + engsound->EmitSound(crf, + entity, + channel, + sample, + vol, + level, + flags, + pitch, + pOrigin, + pDir, + pOrigVec, + updatePos, + soundtime, + speakerentity); + } + } + + return 1; +} + static cell_t EmitSentence(IPluginContext *pContext, const cell_t *params) { cell_t *addr; @@ -786,6 +959,7 @@ sp_nativeinfo_t g_SoundNatives[] = { {"EmitAmbientSound", EmitAmbientSound}, {"EmitSentence", EmitSentence}, + {"EmitSound2", EmitSound2}, {"EmitSound", EmitSound}, {"FadeClientVolume", FadeClientVolume}, {"GetSoundDuration", GetSoundDuration}, diff --git a/sourcepawn/compiler/Makefile b/sourcepawn/compiler/Makefile index 8c9f1653..db52ff2d 100644 --- a/sourcepawn/compiler/Makefile +++ b/sourcepawn/compiler/Makefile @@ -28,9 +28,9 @@ CPP = gcc LINK = -lgcc -static-libgcc -INCLUDE = -I. -I$(SMSDK)/public/sourcepawn +INCLUDE = -I. -I$(SMSDK)/public -I$(SMSDK)/public/sourcepawn -CFLAGS += -DLINUX -DHAVE_STDINT_H -DAMX_ANSIONLY -DENABLE_BINRELOC -m32 +CFLAGS += -DLINUX -DHAVE_STDINT_H -DAMX_ANSIONLY -DENABLE_BINRELOC -Dstricmp=strcasecmp -m32 CPPFLAGS += -Wno-non-virtual-dtor -fno-exceptions -fno-rtti ################################################ diff --git a/tools/gdc/DOCS b/tools/gdc/DOCS new file mode 100644 index 00000000..ea9bfe8e --- /dev/null +++ b/tools/gdc/DOCS @@ -0,0 +1,15 @@ +This program checks SM gamedata in the given gameconf against the given server (not engine) binary. It only checks vtable offsets and signatures. It also only checks one gameconf per invocation. + +Example usage: + + LD_LIBRARY_PATH=~/steam/left4dead2/bin/ gdc -g left4dead2 -e l4d2 -f ~/sourcemod-central/gamedata/sdktools.games/engine.l4d2.txt -b ~/steam/left4dead2/left4dead2/bin/server.so + +This will verify the gamedata in engine.l4d2.txt against the L4D2 server.so. + +The parameters taken (in any order) are: + -g + -e + -f + -b + +The file symbols.txt defines the extra information necessary to link up gamedata key names (like "CommitSuicide") with symbols (like "_ZN11CBasePlayer13CommitSuicideEv"). It also needs a "vtsym" key that contains the symbol name of the game's player class' vtable. The symbols.txt itself is a gameconf file. diff --git a/tools/gdc/GameConfigs.cpp b/tools/gdc/GameConfigs.cpp new file mode 100644 index 00000000..3ef0cf6e --- /dev/null +++ b/tools/gdc/GameConfigs.cpp @@ -0,0 +1,828 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include +#include +#include "GameConfigs.h" +//#include "sm_stringutil.h" +//#include "sourcemod.h" +//#include "sourcemm_api.h" +//#include "HalfLife2.h" +//#include "Logger.h" +//#include "ShareSys.h" +//#include "MemoryUtils.h" +//#include "LibrarySys.h" +//#include "HandleSys.h" +//#include "sm_crc32.h" + +//#if defined PLATFORM_LINUX +//#include +//#endif + +#define PSTATE_NONE 0 +#define PSTATE_GAMES 1 +#define PSTATE_GAMEDEFS 2 +#define PSTATE_GAMEDEFS_OFFSETS 3 +#define PSTATE_GAMEDEFS_OFFSETS_OFFSET 4 +#define PSTATE_GAMEDEFS_KEYS 5 +#define PSTATE_GAMEDEFS_SUPPORTED 6 +#define PSTATE_GAMEDEFS_SIGNATURES 7 +#define PSTATE_GAMEDEFS_SIGNATURES_SIG 8 +#define PSTATE_GAMEDEFS_CRC 9 +#define PSTATE_GAMEDEFS_CRC_BINARY 10 +#define PSTATE_GAMEDEFS_CUSTOM 11 + +#define WIN 0 +#define LIN 1 + +#define PLATFORM_NAME "linux" +#define PLATFORM_SERVER_BINARY "server_i486.so" + +Offset *tempOffset; +Sig *tempSig; + +unsigned int s_ServerBinCRC; +bool s_ServerBinCRC_Ok = false; + +bool /*CGameConfig::*/DoesGameMatch(const char *value) +{ + if (strcmp(value, /*m_gdcG*/game) == 0) + { + return true; + } + return false; +} + +bool /*CGameConfig::*/DoesEngineMatch(const char *value) +{ + if (strcmp(value, /*m_gdcE*/engine) == 0) + { + return true; + } + return false; +} + +CGameConfig::CGameConfig()//const char *file)//, const char *game, const char *engine) +{ +// strncopy(m_File, file, sizeof(m_File)); +// strncopy(m_gdcGame, game, sizeof(m_gdcGame)); +// strncopy(m_gdcEngine, engine, sizeof(m_gdcEngine)); + //m_pStrings = new BaseStringTable(512); + m_RefCount = 0; + + m_CustomLevel = 0; + m_CustomHandler = NULL; +} + +CGameConfig::~CGameConfig() +{ +// delete m_pStrings; +} + +SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *name) +{ + if (m_IgnoreLevel) + { + m_IgnoreLevel++; + return SMCResult_Continue; + } + + switch (m_ParseState) + { + case PSTATE_NONE: + { + if (strcmp(name, "Games") == 0) + { + m_ParseState = PSTATE_GAMES; + } else { + m_IgnoreLevel++; + } + break; + } + case PSTATE_GAMES: + { + if (strcmp(name, "*") == 0 || + strcmp(name, "#default") == 0 || + DoesGameMatch(name)) + { + bShouldBeReadingDefault = true; + m_ParseState = PSTATE_GAMEDEFS; + strncopy(m_Game, name, sizeof(m_Game)); + } else { + m_IgnoreLevel++; + } + break; + } + case PSTATE_GAMEDEFS: + { + if (strcmp(name, "Offsets") == 0) + { + m_ParseState = PSTATE_GAMEDEFS_OFFSETS; + } + else if (strcmp(name, "Keys") == 0) + { + m_ParseState = PSTATE_GAMEDEFS_KEYS; + } + else if ((strcmp(name, "#supported") == 0) && (strcmp(m_Game, "#default") == 0)) + { + m_ParseState = PSTATE_GAMEDEFS_SUPPORTED; + /* Ignore this section unless we get a game. */ + bShouldBeReadingDefault = false; + had_game = false; + matched_game = false; + had_engine = false; + matched_engine = false; + } + else if (strcmp(name, "Signatures") == 0) + { + m_ParseState = PSTATE_GAMEDEFS_SIGNATURES; + } + else if (strcmp(name, "CRC") == 0) + { +#if 0 + m_ParseState = PSTATE_GAMEDEFS_CRC; + bShouldBeReadingDefault = false; +#endif + } + else + { +#if 0 + ITextListener_SMC **pListen = g_GameConfigs.m_customHandlers.retrieve(name); + + if (pListen != NULL) + { + m_CustomLevel = 0; + m_ParseState = PSTATE_GAMEDEFS_CUSTOM; + m_CustomHandler = *pListen; + m_CustomHandler->ReadSMC_ParseStart(); + break; + } +#endif + } + break; + } + case PSTATE_GAMEDEFS_OFFSETS: + { + m_Prop[0] = '\0'; + m_Class[0] = '\0'; + tempOffset = new Offset(); + tempOffset->setName(name); + m_ParseState = PSTATE_GAMEDEFS_OFFSETS_OFFSET; + break; + } + case PSTATE_GAMEDEFS_SIGNATURES: + { + tempSig = new Sig(); + tempSig->setName(name); + m_ParseState = PSTATE_GAMEDEFS_SIGNATURES_SIG; + break; + } +#if 0 + case PSTATE_GAMEDEFS_CRC: + { + char error[255]; + error[0] = '\0'; + if (strcmp(name, "server") != 0) + { + UTIL_Format(error, sizeof(error), "Unrecognized library \"%s\"", name); + } + else if (!s_ServerBinCRC_Ok) + { + FILE *fp; + char path[PLATFORM_MAX_PATH]; + + g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "bin/" PLATFORM_SERVER_BINARY); + if ((fp = fopen(path, "rb")) == NULL) + { + UTIL_Format(error, sizeof(error), "Could not open binary: %s", path); + } else { + size_t size; + void *buffer; + + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + buffer = malloc(size); + fread(buffer, size, 1, fp); + s_ServerBinCRC = UTIL_CRC32(buffer, size); + free(buffer); + s_ServerBinCRC_Ok = true; + fclose(fp); + } + } + if (error[0] != '\0') + { + m_IgnoreLevel = 1; + g_Logger.LogError("[SM] Error while parsing CRC section for \"%s\" (%s):", m_Game, m_CurFile); + g_Logger.LogError("[SM] %s", error); + } else { + m_ParseState = PSTATE_GAMEDEFS_CRC_BINARY; + } + break; + } + case PSTATE_GAMEDEFS_CUSTOM: + { + m_CustomLevel++; + return m_CustomHandler->ReadSMC_NewSection(states, name); + break; + } +#endif + /* No sub-sections allowed: + case PSTATE_GAMEDEFS_OFFSETS_OFFSET: + case PSTATE_GAMEDEFS_KEYS: + case PSTATE_GAMEDEFS_SUPPORTED: + case PSTATE_GAMEDEFS_SIGNATURES_SIG: + case PSTATE_GAMEDEFS_CRC_BINARY: + */ + default: + { + /* If we don't know what we got, start ignoring */ + m_IgnoreLevel++; + break; + } + } + + return SMCResult_Continue; +} + +SMCResult CGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value) +{ + if (m_IgnoreLevel) + { + return SMCResult_Continue; + } + + if (m_ParseState == PSTATE_GAMEDEFS_OFFSETS_OFFSET) + { + if (strcmp(key, "class") == 0) + { + strncopy(m_Class, value, sizeof(m_Class)); + } else if (strcmp(key, "prop") == 0) { + strncopy(m_Prop, value, sizeof(m_Prop)); + } else /*if (strcmp(key, PLATFORM_NAME) == 0) */ { + int val = atoi(value); +// sm_trie_replace(m_pOffsets, m_offset, (void *)val); + if (strcmp(key, "windows") == 0) tempOffset->setWin(val); + else if (strcmp(key, "linux") == 0) tempOffset->setLin(val); + } + } else if (m_ParseState == PSTATE_GAMEDEFS_KEYS) { + m_Keys[key] = value; +// printf("Inserting %s - %s\n", key, value); +// int id = m_pStrings->AddString(value); +// sm_trie_replace(m_pKeys, key, (void *)id); + } else if (m_ParseState == PSTATE_GAMEDEFS_SUPPORTED) { + if (strcmp(key, "game") == 0) + { + had_game = true; + if (DoesGameMatch(value)) + { + matched_game = true; + } + if ((!had_engine && matched_game) || (matched_engine && matched_game)) + { + bShouldBeReadingDefault = true; + } + } + else if (strcmp(key, "engine") == 0) + { + had_engine = true; + if (DoesEngineMatch(value)) + { + matched_engine = true; + } + if ((!had_game && matched_engine) || (matched_game && matched_engine)) + { + bShouldBeReadingDefault = true; + } + } + } else if (m_ParseState == PSTATE_GAMEDEFS_SIGNATURES_SIG) { + if (strcmp(key, "windows") == 0) tempSig->setWin(value); + else if (strcmp(key, "linux") == 0) tempSig->setLin(value); + else if (strcmp(key, "library") == 0) tempSig->setLib(value); + } else if (m_ParseState == PSTATE_GAMEDEFS_CRC_BINARY) { + if (strcmp(key, PLATFORM_NAME) == 0 + && s_ServerBinCRC_Ok + && !bShouldBeReadingDefault) + { + unsigned int crc = 0; + sscanf(value, "%08X", &crc); + if (s_ServerBinCRC == crc) + { + bShouldBeReadingDefault = true; + } + } + } else if (m_ParseState == PSTATE_GAMEDEFS_CUSTOM) { + return m_CustomHandler->ReadSMC_KeyValue(states, key, value); + } + + return SMCResult_Continue; +} + +SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states) +{ + if (m_IgnoreLevel) + { + m_IgnoreLevel--; + return SMCResult_Continue; + } + + if (m_CustomLevel) + { + m_CustomLevel--; + m_CustomHandler->ReadSMC_LeavingSection(states); + return SMCResult_Continue; + } + + switch (m_ParseState) + { + case PSTATE_GAMES: + { + m_ParseState = PSTATE_NONE; + break; + } + case PSTATE_GAMEDEFS: + { + m_ParseState = PSTATE_GAMES; + break; + } + case PSTATE_GAMEDEFS_CUSTOM: + { + m_ParseState = PSTATE_GAMEDEFS; + m_CustomHandler->ReadSMC_ParseEnd(false, false); + break; + } + case PSTATE_GAMEDEFS_KEYS: + case PSTATE_GAMEDEFS_OFFSETS: + { + m_ParseState = PSTATE_GAMEDEFS; + break; + } + case PSTATE_GAMEDEFS_OFFSETS_OFFSET: + { + m_Offsets.push_back(*tempOffset); +#if 0 + /* Parse the offset... */ + if (m_Class[0] != '\0' + && m_Prop[0] != '\0') + { + SendProp *pProp = g_HL2.FindInSendTable(m_Class, m_Prop); + if (pProp) + { + int val = pProp->GetOffset(); + sm_trie_replace(m_pOffsets, m_offset, (void *)val); + sm_trie_replace(m_pProps, m_offset, pProp); + } else { + /* Check if it's a non-default game and no offsets exist */ + if (((strcmp(m_Game, "*") != 0) && strcmp(m_Game, "#default") != 0) + && (!sm_trie_retrieve(m_pOffsets, m_offset, NULL))) + { + g_Logger.LogError("[SM] Unable to find property %s.%s (file \"%s\") (mod \"%s\")", + m_Class, + m_Prop, + m_CurFile, + m_Game); + } + } + } +#endif + m_ParseState = PSTATE_GAMEDEFS_OFFSETS; + break; + } + case PSTATE_GAMEDEFS_CRC: + case PSTATE_GAMEDEFS_SUPPORTED: + { + if (!bShouldBeReadingDefault) + { + /* If we shouldn't read the rest of this section, set the ignore level. */ + m_IgnoreLevel = 1; + m_ParseState = PSTATE_GAMES; + } else { + m_ParseState = PSTATE_GAMEDEFS; + } + break; + } + case PSTATE_GAMEDEFS_CRC_BINARY: + { + m_ParseState = PSTATE_GAMEDEFS_CRC; + break; + } + case PSTATE_GAMEDEFS_SIGNATURES: + { + m_ParseState = PSTATE_GAMEDEFS; + break; + } + case PSTATE_GAMEDEFS_SIGNATURES_SIG: + { + m_Sigs.push_back(*tempSig); +#if 0 + if (s_TempSig.library[0] == '\0') + { + /* assume server */ + strncopy(s_TempSig.library, "server", sizeof(s_TempSig.library)); + } + void *addrInBase = NULL; + if (strcmp(s_TempSig.library, "server") == 0) + { + addrInBase = (void *)g_SMAPI->GetServerFactory(false); + } else if (strcmp(s_TempSig.library, "engine") == 0) { + addrInBase = (void *)g_SMAPI->GetEngineFactory(false); + } + void *final_addr = NULL; + if (addrInBase == NULL) + { + g_Logger.LogError("[SM] Unrecognized library \"%s\" (gameconf \"%s\")", + s_TempSig.library, + m_CurFile); + } else { +#if defined PLATFORM_LINUX + if (s_TempSig.sig[0] == '@') + { + Dl_info info; + /* GNU only: returns 0 on error, inconsistent! >:[ */ + if (dladdr(addrInBase, &info) != 0) + { + void *handle = dlopen(info.dli_fname, RTLD_NOW); + if (handle) + { + final_addr = dlsym(handle, &s_TempSig.sig[1]); + dlclose(handle); + } else { + g_Logger.LogError("[SM] Unable to load library \"%s\" (gameconf \"%s\")", + s_TempSig.library, + m_File); + } + } else { + g_Logger.LogError("[SM] Unable to find library \"%s\" in memory (gameconf \"%s\")", + s_TempSig.library, + m_File); + } + } + if (final_addr) + { + goto skip_find; + } +#endif + /* First, preprocess the signature */ + char real_sig[511]; + size_t real_bytes; + size_t length; + + real_bytes = 0; + length = strlen(s_TempSig.sig); + + for (size_t i=0; i= sizeof(real_sig)) + { + break; + } + real_sig[real_bytes++] = s_TempSig.sig[i]; + if (s_TempSig.sig[i] == '\\' + && s_TempSig.sig[i+1] == 'x') + { + if (i + 3 >= length) + { + continue; + } + /* Get the hex part */ + char s_byte[3]; + int r_byte; + s_byte[0] = s_TempSig.sig[i+2]; + s_byte[1] = s_TempSig.sig[i+3]; + s_byte[2] = '\0'; + /* Read it as an integer */ + sscanf(s_byte, "%x", &r_byte); + /* Save the value */ + real_sig[real_bytes-1] = r_byte; + /* Adjust index */ + i += 3; + } + } + + if (real_bytes >= 1) + { + final_addr = g_MemUtils.FindPattern(addrInBase, real_sig, real_bytes); + } + } + +#if defined PLATFORM_LINUX +skip_find: +#endif + sm_trie_replace(m_pSigs, m_offset, final_addr); +#endif + m_ParseState = PSTATE_GAMEDEFS_SIGNATURES; + + break; + } + } + + return SMCResult_Continue; +} + +#define MSTATE_NONE 0 +#define MSTATE_MAIN 1 +#define MSTATE_FILE 2 + +class MasterReader : public ITextListener_SMC +{ +public: + virtual void ReadSMC_ParseStart() + { + state = MSTATE_NONE; + ignoreLevel = 0; + } + + virtual SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name) + { + if (ignoreLevel) + { + return SMCResult_Continue; + } + + if (state == MSTATE_NONE) + { + if (strcmp(name, "Game Master") == 0) + { + state = MSTATE_MAIN; + } + else + { + ignoreLevel++; + } + } + else if (state == MSTATE_MAIN) + { + strncopy(cur_file, name, sizeof(cur_file)); + had_engine = false; + matched_engine = false; + had_game = false; + matched_game = false; + state = MSTATE_FILE; + } + else if (state == MSTATE_FILE) + { + ignoreLevel++; + } + + return SMCResult_Continue; + } + + virtual SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value) + { + if (ignoreLevel || state != MSTATE_FILE) + { + return SMCResult_Continue; + } + + if (strcmp(key, "engine") == 0) + { + had_engine = true; + if (DoesEngineMatch(value)) + { + matched_engine = true; + } + } + else if (strcmp(key, "game") == 0) + { + had_game = true; + if (DoesGameMatch(value)) + { + matched_game = true; + } + } + + return SMCResult_Continue; + } + + virtual SMCResult ReadSMC_LeavingSection(const SMCStates *states) + { + if (ignoreLevel) + { + ignoreLevel--; + return SMCResult_Continue; + } + + if (state == MSTATE_FILE) + { + /* The four success conditions: + * 1. Needed nothing. + * 2. Needed game only. + * 3. Needed engine only. + * 4. Needed both engine and game. + * Final result is minimized via k-map. + */ +#if 0 + if ((!had_engine && !had_game) || + (!had_engine && (had_game && matched_game)) || + (!had_game && (had_engine && matched_engine)) || + ((had_game && had_engine) && (matched_game && matched_engine))) +#endif + if ((!had_engine && !had_game) || + (!had_engine && matched_game) || + (!had_game && matched_engine) || + (matched_engine && matched_game)) + { + fileList->push_back(cur_file); + } + state = MSTATE_MAIN; + } + else if (state == MSTATE_MAIN) + { + state = MSTATE_NONE; + } + + return SMCResult_Continue; + } +public: + list *fileList; + unsigned int state; + unsigned int ignoreLevel; + char cur_file[PLATFORM_MAX_PATH]; + bool had_engine; + bool matched_engine; + bool had_game; + bool matched_game; +}; + +static MasterReader master_reader; + +bool CGameConfig::Reparse(char *error, size_t maxlength) +{ + /* Reset cached data */ +// m_pStrings->Reset(); + m_Offsets.clear(); + m_Sigs.clear(); + + + char path[PLATFORM_MAX_PATH]; + + /* See if we can use the extended gamedata format. */ + //TODO pass in path to gamedata somehow +// g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "gamedata/%s/master.games.txt", m_File); + + /* Otherwise, it's time to parse the master. */ + SMCError err; + SMCStates state = {0, 0}; + list fileList; + master_reader.fileList = &fileList; + + err = textparsers->ParseSMCFile(path, &master_reader, &state, error, maxlength); + if (err != SMCError_Okay) + { + const char *msg = textparsers->GetSMCErrorString(err); + + printf("[SM] Error parsing master gameconf file \"%s\":", path); + printf("[SM] Error %d on line %d, col %d: %s", + err, + state.line, + state.col, + msg ? msg : "Unknown error"); + exit(PARSE_ERROR); + } + + /* Go through each file we found and parse it. */ + list::iterator iter; + for (iter = fileList.begin(); iter != fileList.end(); iter++) + { + UTIL_Format(path, sizeof(path), "%s/%s", m_File, *iter); + if (!EnterFile(path, error, maxlength)) + { + return false; + } + } + + return true; +} + +bool CGameConfig::EnterFile(const char *file, char *error, size_t maxlength) +{ + SMCError err; + SMCStates state = {0, 0}; + + /* Initialize parse states */ + m_IgnoreLevel = 0; + bShouldBeReadingDefault = true; + m_ParseState = PSTATE_NONE; + + if ((err=textparsers->ParseSMCFile(file, this, &state, error, maxlength)) + != SMCError_Okay) + { + const char *msg; + + msg = textparsers->GetSMCErrorString(err); + + printf("[SM] Error parsing gameconfig file \"%s\":\n", file); + printf("[SM] Error %d on line %d, col %d: %s\n", + err, + state.line, + state.col, + msg ? msg : "Unknown error"); + + if (m_ParseState == PSTATE_GAMEDEFS_CUSTOM) + { + //error occurred while parsing a custom section + m_CustomHandler->ReadSMC_ParseEnd(true, true); + m_CustomHandler = NULL; + m_CustomLevel = 0; + } + + exit(PARSE_ERROR); + } + + return true; +} + +#if 0 +bool CGameConfig::GetOffset(const char *key, int *value) +{ + void *obj; + + if (!sm_trie_retrieve(m_pOffsets, key, &obj)) + { + return false; + } + + *value = (int)obj; + + return true; +} + +const char *CGameConfig::GetKeyValue(const char *key) +{ + void *obj; + if (!sm_trie_retrieve(m_pKeys, key, &obj)) + { + return NULL; + } + return m_pStrings->GetString((int)obj); +} + +SendProp *CGameConfig::GetSendProp(const char *key) +{ + SendProp *pProp; + + if (!sm_trie_retrieve(m_pProps, key, (void **)&pProp)) + { + return NULL; + } + + return pProp; +} + +bool CGameConfig::GetMemSig(const char *key, void **addr) +{ + return sm_trie_retrieve(m_pSigs, key, addr); +} + +void CGameConfig::IncRefCount() +{ + m_RefCount++; +} + +unsigned int CGameConfig::DecRefCount() +{ + m_RefCount--; + return m_RefCount; +} +#endif + +const char *CGameConfig::GetKeyValue(const char *key) +{ + map::iterator it = m_Keys.find(key); + if (it == m_Keys.end()) return NULL; + + return it->second; +} + +list CGameConfig::GetOffsets() { return m_Offsets; } +list CGameConfig::GetSigs() { return m_Sigs; } + diff --git a/tools/gdc/GameConfigs.h b/tools/gdc/GameConfigs.h new file mode 100644 index 00000000..bb00b22e --- /dev/null +++ b/tools/gdc/GameConfigs.h @@ -0,0 +1,239 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_CGAMECONFIGS_H_ +#define _INCLUDE_SOURCEMOD_CGAMECONFIGS_H_ + +#include +#include +//#include +//#include "sm_trie.h" +//#include "sm_globals.h" +//#include "sm_memtable.h" +//#include "sm_trie_tpl.h" +//#include "ThreadSupport.h" +#include +#include +#include "gdc.h" +#include +#include + +#define PLATFORM_MAX_PATH PATH_MAX + +using std::map; +using std::list; +using namespace SourceMod; +//using namespace SourceHook; + +extern char *game; +extern char *engine; + +#define PARSE_ERROR -1 +struct cmp_str +{ + bool operator()(char const *a, char const *b) + { + return std::strcmp(a, b) < 0; + } +}; + +struct Offset +{ + char *name; + char *symbol; + int lin; + int win; + + Offset() + { + name = symbol = NULL; + lin = win = -1; + } + + ~Offset() + { + delete name; + delete symbol; + } + + void reset() + { + delete name; + delete symbol; + lin = win = -1; + } + + void setName(const char *src) + { + delete name; + name = new char[strlen(src)+1]; + strcpy(name, src); + } + + void setSymbol(const char *src) + { + delete symbol; + symbol = new char[strlen(src)+1]; + strcpy(symbol, src); + } + + void setLin(int offs) + { + lin = offs; + } + + void setWin(int offs) + { + win = offs; + } +}; + +enum Library +{ + Engine, + Server +}; + +struct Sig +{ + char *name; + char *win; + char *lin; + Library lib; + + Sig() + { + name = win = lin = NULL; + lib = (Library) -1; + } + + ~Sig() + { + delete name; + delete win; + delete lin; + } + + void reset() + { + delete name; + delete win; + delete lin; + lib = (Library) -1; + } + + void setName(const char *src) + { + delete name; + name = new char[strlen(src)+1]; + strcpy(name, src); + } + + void setWin(const char *src) + { + delete win; + win = new char[strlen(src)+1]; + strcpy(win, src); + } + + void setLin(const char *src) + { + delete lin; + lin = new char[strlen(src)+1]; + strcpy(lin, src); + } + + void setLib(const char *src) + { + if (stricmp(src, "server") == 0) lib = Server; + else if (stricmp(src, "engine") == 0) lib = Engine; + else lib = (Library)-1; + } +}; + +class SendProp; + +class CGameConfig : + public ITextListener_SMC//, +// public IGameConfig +{ +public: + CGameConfig();//const char *file);//, const char *game, const char *engine); + ~CGameConfig(); +public: + bool Reparse(char *error, size_t maxlength); + bool EnterFile(const char *file, char *error, size_t maxlength); + list GetOffsets(); + list GetSigs(); +public: //ITextListener_SMC + SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name); + SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value); + SMCResult ReadSMC_LeavingSection(const SMCStates *states); +public: //IGameConfig + const char *GetKeyValue(const char *key); + bool GetOffset(const char *key, int *value); + SendProp *GetSendProp(const char *key); + bool GetMemSig(const char *key, void **addr); +public: +// void IncRefCount(); +// unsigned int DecRefCount(); +private: +// bool DoesGameMatch(const char *value); +// bool DoesEngineMatch(const char *value); +public: +// BaseStringTable *m_pStrings; + char m_File[PLATFORM_MAX_PATH]; + char m_CurFile[PLATFORM_MAX_PATH]; + list m_Offsets; + list m_Sigs; + map m_Keys; + unsigned int m_RefCount; + /* Parse states */ + int m_ParseState; + unsigned int m_IgnoreLevel; + char m_Class[64]; + char m_Prop[64]; + char m_offset[64]; + char m_Game[256]; + char m_gdcGame[256]; + char m_gdcEngine[256]; + bool bShouldBeReadingDefault; + bool had_game; + bool matched_game; + bool had_engine; + bool matched_engine; + + /* Custom Sections */ + unsigned int m_CustomLevel; + ITextListener_SMC *m_CustomHandler; +}; + +#endif //_INCLUDE_SOURCEMOD_CGAMECONFIGS_H_ diff --git a/tools/gdc/Makefile b/tools/gdc/Makefile new file mode 100644 index 00000000..1b5d9db0 --- /dev/null +++ b/tools/gdc/Makefile @@ -0,0 +1,72 @@ +# (C)2004-2008 SourceMod Development Team +# Makefile written by David "BAILOPAN" Anderson + + +##################################### +### EDIT BELOW FOR OTHER PROJECTS ### +##################################### + +OBJECTS = gdc.cpp TextParsers.cpp GameConfigs.cpp MemoryUtils.cpp + +############################################## +### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ### +############################################## + +C_OPT_FLAGS = -DNDEBUG -O3 -funroll-loops -pipe -fno-strict-aliasing +C_DEBUG_FLAGS = -D_DEBUG -DDEBUG -g -ggdb3 +C_GCC4_FLAGS = -fvisibility=hidden +CPP_GCC4_FLAGS = -fvisibility-inlines-hidden +CPP = gcc-4.1 + +BINARY = gdc + +LINK += -L. -lstdc++ -ldl -lm + +INCLUDE += -I. -I../../public -I../../public/sourcepawn + +CFLAGS += -D_LINUX -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp \ + -D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp -Wall -Werror \ + -Wno-uninitialized -mfpmath=sse -msse -DHAVE_STDINT_H -DSM_DEFAULT_THREADER -DPLATFORM_LINUX -m32 +CPPFLAGS += -Wno-non-virtual-dtor -fno-exceptions -fno-rtti + +################################################ +### DO NOT EDIT BELOW HERE FOR MOST PROJECTS ### +################################################ + +ifeq "$(DEBUG)" "true" + BIN_DIR = Debug + CFLAGS += $(C_DEBUG_FLAGS) +else + BIN_DIR = Release + CFLAGS += $(C_OPT_FLAGS) +endif + +GCC_VERSION := $(shell $(CPP) -dumpversion >&1 | cut -b1) +ifeq "$(GCC_VERSION)" "4" + CFLAGS += $(C_GCC4_FLAGS) + CPPFLAGS += $(CPP_GCC4_FLAGS) +endif + +OBJ_LINUX := $(OBJECTS:%.cpp=$(BIN_DIR)/%.o) +OBJ_LINUX := $(OBJ_LINUX:%.c=$(BIN_DIR)/%.o) + +$(BIN_DIR)/%.o: %.cpp + $(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< + +$(BIN_DIR)/%.o: %.c + $(CPP) $(INCLUDE) $(CFLAGS) -o $@ -c $< + +all: + mkdir -p $(BIN_DIR) + $(MAKE) -f Makefile gdc + +gdc: $(OBJ_LINUX) + $(CPP) $(INCLUDE) $(OBJ_LINUX) $(LINK) -m32 -o$(BIN_DIR)/$(BINARY) + +debug: + $(MAKE) -f Makefile all DEBUG=true + +default: all + +clean: + rm -rf $(BIN_DIR)/ diff --git a/tools/gdc/MemoryUtils.cpp b/tools/gdc/MemoryUtils.cpp new file mode 100644 index 00000000..0bb83c2b --- /dev/null +++ b/tools/gdc/MemoryUtils.cpp @@ -0,0 +1,615 @@ +/** + * vim: set ts=4 sw=4 tw=99 noet : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + */ + +#include "MemoryUtils.h" +#ifdef PLATFORM_LINUX +#include +#include +#include + +#define PAGE_SIZE 4096 +#define PAGE_ALIGN_UP(x) ((x + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) +#endif +#ifdef PLATFORM_APPLE +#include +#include +#include +#endif + +//MemoryUtils g_MemUtils; + +MemoryUtils::MemoryUtils() +{ +#ifdef PLATFORM_APPLE + + /* Get pointer to struct that describes all loaded mach-o images in process */ + struct nlist list[2]; + memset(list, 0, sizeof(list)); + list[0].n_un.n_name = (char *)"_dyld_all_image_infos"; + nlist("/usr/lib/dyld", list); + m_ImageList = (struct dyld_all_image_infos *)list[0].n_value; + +#endif +} + +MemoryUtils::~MemoryUtils() +{ +#if defined PLATFORM_LINUX || defined PLATFORM_APPLE + for (size_t i = 0; i < m_SymTables.size(); i++) + { + delete m_SymTables[i]; + } + m_SymTables.clear(); +#endif +} + +/*void MemoryUtils::OnSourceModAllInitialized() +{ + sharesys->AddInterface(NULL, this); +}*/ + +void *MemoryUtils::FindPattern(const void *libPtr, const char *pattern, size_t len) +{ + DynLibInfo lib; + bool found; + char *ptr, *end; + + memset(&lib, 0, sizeof(DynLibInfo)); + + if (!GetLibraryInfo(libPtr, lib)) + { + return NULL; + } + + ptr = reinterpret_cast(lib.baseAddress); + end = ptr + lib.memorySize; + + while (ptr < end) + { + found = true; + for (register size_t i = 0; i < len; i++) + { + if (pattern[i] != '\x2A' && pattern[i] != ptr[i]) + { + found = false; + break; + } + } + + if (found) + return ptr; + + ptr++; + } + + return NULL; +} + +void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol) +{ +#ifdef PLATFORM_WINDOWS + + return GetProcAddress((HMODULE)handle, symbol); + +#elif defined PLATFORM_LINUX + + struct link_map *dlmap; + struct stat dlstat; + int dlfile; + uintptr_t map_base; + Elf32_Ehdr *file_hdr; + Elf32_Shdr *sections, *shstrtab_hdr, *symtab_hdr, *strtab_hdr; + Elf32_Sym *symtab; + const char *shstrtab, *strtab; + uint16_t section_count; + uint32_t symbol_count; + LibSymbolTable *libtable; + SymbolTable *table; + AddrTable *table2; + Symbol *symbol_entry; + + dlmap = (struct link_map *)handle; + symtab_hdr = NULL; + strtab_hdr = NULL; + table = NULL; + table2 = NULL; + + /* See if we already have a symbol table for this library */ + for (size_t i = 0; i < m_SymTables.size(); i++) + { + libtable = m_SymTables[i]; + if (libtable->lib_base == dlmap->l_addr) + { + table = &libtable->table; + table2 = &libtable->table2; + break; + } + } + + /* If we don't have a symbol table for this library, then create one */ + if (table == NULL) + { + libtable = new LibSymbolTable(); + libtable->table.Initialize(); + libtable->table2.Initialize(); + libtable->lib_base = dlmap->l_addr; + libtable->last_pos = 0; + table = &libtable->table; + table2 = &libtable->table2; + m_SymTables.push_back(libtable); + } + + /* See if the symbol is already cached in our table */ + symbol_entry = table->FindSymbol(symbol, strlen(symbol)); + if (symbol_entry != NULL) + { + return symbol_entry->address; + } + + /* If symbol isn't in our table, then we have open the actual library */ + dlfile = open(dlmap->l_name, O_RDONLY); + if (dlfile == -1 || fstat(dlfile, &dlstat) == -1) + { + close(dlfile); + return NULL; + } + + /* Map library file into memory */ + file_hdr = (Elf32_Ehdr *)mmap(NULL, dlstat.st_size, PROT_READ, MAP_PRIVATE, dlfile, 0); + map_base = (uintptr_t)file_hdr; + if (file_hdr == MAP_FAILED) + { + close(dlfile); + return NULL; + } + close(dlfile); + + if (file_hdr->e_shoff == 0 || file_hdr->e_shstrndx == SHN_UNDEF) + { + munmap(file_hdr, dlstat.st_size); + return NULL; + } + + sections = (Elf32_Shdr *)(map_base + file_hdr->e_shoff); + section_count = file_hdr->e_shnum; + /* Get ELF section header string table */ + shstrtab_hdr = §ions[file_hdr->e_shstrndx]; + shstrtab = (const char *)(map_base + shstrtab_hdr->sh_offset); + + /* Iterate sections while looking for ELF symbol table and string table */ + for (uint16_t i = 0; i < section_count; i++) + { + Elf32_Shdr &hdr = sections[i]; + const char *section_name = shstrtab + hdr.sh_name; + + if (strcmp(section_name, ".symtab") == 0) + { + symtab_hdr = &hdr; + } + else if (strcmp(section_name, ".strtab") == 0) + { + strtab_hdr = &hdr; + } + } + + /* Uh oh, we don't have a symbol table or a string table */ + if (symtab_hdr == NULL || strtab_hdr == NULL) + { + munmap(file_hdr, dlstat.st_size); + return NULL; + } + + symtab = (Elf32_Sym *)(map_base + symtab_hdr->sh_offset); + strtab = (const char *)(map_base + strtab_hdr->sh_offset); + symbol_count = symtab_hdr->sh_size / symtab_hdr->sh_entsize; + + /* Iterate symbol table starting from the position we were at last time */ + for (uint32_t i = libtable->last_pos; i < symbol_count; i++) + { + Elf32_Sym &sym = symtab[i]; + unsigned char sym_type = ELF32_ST_TYPE(sym.st_info); + const char *sym_name = strtab + sym.st_name; + Symbol *cur_sym; + + /* Skip symbols that are undefined or do not refer to functions or objects */ + if (sym.st_shndx == SHN_UNDEF || (sym_type != STT_FUNC && sym_type != STT_OBJECT)) + { + continue; + } + + /* Caching symbols as we go along */ + cur_sym = table->InternSymbol(sym_name, strlen(sym_name), (void *)(dlmap->l_addr + sym.st_value)); + table2->InternSymbol(sym_name, strlen(sym_name), (void *)(dlmap->l_addr + sym.st_value)); + if (strcmp(symbol, sym_name) == 0) + { + symbol_entry = cur_sym; + libtable->last_pos = ++i; + // break; + } + } + + libtable->last_pos = symbol_count; + + munmap(file_hdr, dlstat.st_size); + return symbol_entry ? symbol_entry->address : NULL; + +#elif defined PLATFORM_APPLE + + uintptr_t dlbase, linkedit_addr; + uint32_t image_count; + struct mach_header *file_hdr; + struct load_command *loadcmds; + struct segment_command *linkedit_hdr; + struct symtab_command *symtab_hdr; + struct nlist *symtab; + const char *strtab; + uint32_t loadcmd_count; + uint32_t symbol_count; + LibSymbolTable *libtable; + SymbolTable *table; + Symbol *symbol_entry; + + dlbase = 0; + image_count = m_ImageList->infoArrayCount; + linkedit_hdr = NULL; + symtab_hdr = NULL; + table = NULL; + + /* Loop through mach-o images in process. + * We can skip index 0 since that is just the executable. + */ + for (uint32_t i = 1; i < image_count; i++) + { + const struct dyld_image_info &info = m_ImageList->infoArray[i]; + + /* "Load" each one until we get a matching handle */ + void *h = dlopen(info.imageFilePath, RTLD_NOLOAD); + if (h == handle) + { + dlbase = (uintptr_t)info.imageLoadAddress; + dlclose(h); + break; + } + + dlclose(h); + } + + if (!dlbase) + { + /* Uh oh, we couldn't find a matching handle */ + return NULL; + } + + /* See if we already have a symbol table for this library */ + for (size_t i = 0; i < m_SymTables.size(); i++) + { + libtable = m_SymTables[i]; + if (libtable->lib_base == dlbase) + { + table = &libtable->table; + break; + } + } + + /* If we don't have a symbol table for this library, then create one */ + if (table == NULL) + { + libtable = new LibSymbolTable(); + libtable->table.Initialize(); + libtable->lib_base = dlbase; + libtable->last_pos = 0; + table = &libtable->table; + m_SymTables.push_back(libtable); + } + + /* See if the symbol is already cached in our table */ + symbol_entry = table->FindSymbol(symbol, strlen(symbol)); + if (symbol_entry != NULL) + { + return symbol_entry->address; + } + + /* If symbol isn't in our table, then we have to locate it in memory */ + + file_hdr = (struct mach_header *)dlbase; + loadcmds = (struct load_command *)(dlbase + sizeof(struct mach_header)); + loadcmd_count = file_hdr->ncmds; + + /* Loop through load commands until we find the ones for the symbol table */ + for (uint32_t i = 0; i < loadcmd_count; i++) + { + if (loadcmds->cmd == LC_SEGMENT && !linkedit_hdr) + { + struct segment_command *seg = (struct segment_command *)loadcmds; + if (strcmp(seg->segname, "__LINKEDIT") == 0) + { + linkedit_hdr = seg; + if (symtab_hdr) + { + break; + } + } + } + else if (loadcmds->cmd == LC_SYMTAB) + { + symtab_hdr = (struct symtab_command *)loadcmds; + if (linkedit_hdr) + { + break; + } + } + + /* Load commands are not of a fixed size which is why we add the size */ + loadcmds = (struct load_command *)((uintptr_t)loadcmds + loadcmds->cmdsize); + } + + if (!linkedit_hdr || !symtab_hdr || !symtab_hdr->symoff || !symtab_hdr->stroff) + { + /* Uh oh, no symbol table */ + return NULL; + } + + linkedit_addr = dlbase + linkedit_hdr->vmaddr; + symtab = (struct nlist *)(linkedit_addr + symtab_hdr->symoff - linkedit_hdr->fileoff); + strtab = (const char *)(linkedit_addr + symtab_hdr->stroff - linkedit_hdr->fileoff); + symbol_count = symtab_hdr->nsyms; + + /* Iterate symbol table starting from the position we were at last time */ + for (uint32_t i = libtable->last_pos; i < symbol_count; i++) + { + struct nlist &sym = symtab[i]; + /* Ignore the prepended underscore on all symbols, so +1 here */ + const char *sym_name = strtab + sym.n_un.n_strx + 1; + Symbol *cur_sym; + + /* Skip symbols that are undefined */ + if (sym.n_sect == NO_SECT) + { + continue; + } + + /* Caching symbols as we go along */ + cur_sym = table->InternSymbol(sym_name, strlen(sym_name), (void *)(dlbase + sym.n_value)); + if (strcmp(symbol, sym_name) == 0) + { + symbol_entry = cur_sym; + libtable->last_pos = ++i; + break; + } + } + + return symbol_entry ? symbol_entry->address : NULL; + +#endif +} + +bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib) +{ + uintptr_t baseAddr; + + if (libPtr == NULL) + { + return false; + } + +#ifdef PLATFORM_WINDOWS + + MEMORY_BASIC_INFORMATION info; + IMAGE_DOS_HEADER *dos; + IMAGE_NT_HEADERS *pe; + IMAGE_FILE_HEADER *file; + IMAGE_OPTIONAL_HEADER *opt; + + if (!VirtualQuery(libPtr, &info, sizeof(MEMORY_BASIC_INFORMATION))) + { + return false; + } + + baseAddr = reinterpret_cast(info.AllocationBase); + + /* All this is for our insane sanity checks :o */ + dos = reinterpret_cast(baseAddr); + pe = reinterpret_cast(baseAddr + dos->e_lfanew); + file = &pe->FileHeader; + opt = &pe->OptionalHeader; + + /* Check PE magic and signature */ + if (dos->e_magic != IMAGE_DOS_SIGNATURE || pe->Signature != IMAGE_NT_SIGNATURE || opt->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + return false; + } + + /* Check architecture, which is 32-bit/x86 right now + * Should change this for 64-bit if Valve gets their act together + */ + if (file->Machine != IMAGE_FILE_MACHINE_I386) + { + return false; + } + + /* For our purposes, this must be a dynamic library */ + if ((file->Characteristics & IMAGE_FILE_DLL) == 0) + { + return false; + } + + /* Finally, we can do this */ + lib.memorySize = opt->SizeOfImage; + +#elif defined PLATFORM_LINUX + + Dl_info info; + Elf32_Ehdr *file; + Elf32_Phdr *phdr; + uint16_t phdrCount; + + if (!dladdr(libPtr, &info)) + { + return false; + } + + if (!info.dli_fbase || !info.dli_fname) + { + return false; + } + + /* This is for our insane sanity checks :o */ + baseAddr = reinterpret_cast(info.dli_fbase); + file = reinterpret_cast(baseAddr); + + /* Check ELF magic */ + if (memcmp(ELFMAG, file->e_ident, SELFMAG) != 0) + { + return false; + } + + /* Check ELF version */ + if (file->e_ident[EI_VERSION] != EV_CURRENT) + { + return false; + } + + /* Check ELF architecture, which is 32-bit/x86 right now + * Should change this for 64-bit if Valve gets their act together + */ + if (file->e_ident[EI_CLASS] != ELFCLASS32 || file->e_machine != EM_386 || file->e_ident[EI_DATA] != ELFDATA2LSB) + { + return false; + } + + /* For our purposes, this must be a dynamic library/shared object */ + if (file->e_type != ET_DYN) + { + return false; + } + + phdrCount = file->e_phnum; + phdr = reinterpret_cast(baseAddr + file->e_phoff); + + for (uint16_t i = 0; i < phdrCount; i++) + { + Elf32_Phdr &hdr = phdr[i]; + + /* We only really care about the segment with executable code */ + if (hdr.p_type == PT_LOAD && hdr.p_flags == (PF_X|PF_R)) + { + /* From glibc, elf/dl-load.c: + * c->mapend = ((ph->p_vaddr + ph->p_filesz + GLRO(dl_pagesize) - 1) + * & ~(GLRO(dl_pagesize) - 1)); + * + * In glibc, the segment file size is aligned up to the nearest page size and + * added to the virtual address of the segment. We just want the size here. + */ + lib.memorySize = PAGE_ALIGN_UP(hdr.p_filesz); + break; + } + } + +#elif defined PLATFORM_APPLE + + Dl_info info; + struct mach_header *file; + struct segment_command *seg; + uint32_t cmd_count; + + if (!dladdr(libPtr, &info)) + { + return false; + } + + if (!info.dli_fbase || !info.dli_fname) + { + return false; + } + + /* This is for our insane sanity checks :o */ + baseAddr = (uintptr_t)info.dli_fbase; + file = (struct mach_header *)baseAddr; + + /* Check Mach-O magic */ + if (file->magic != MH_MAGIC) + { + return false; + } + + /* Check architecture (32-bit/x86) */ + if (file->cputype != CPU_TYPE_I386 || file->cpusubtype != CPU_SUBTYPE_I386_ALL) + { + return false; + } + + /* For our purposes, this must be a dynamic library */ + if (file->filetype != MH_DYLIB) + { + return false; + } + + cmd_count = file->ncmds; + seg = (struct segment_command *)(baseAddr + sizeof(struct mach_header)); + + /* Add up memory sizes of mapped segments */ + for (uint32_t i = 0; i < cmd_count; i++) + { + if (seg->cmd == LC_SEGMENT) + { + lib.memorySize += seg->vmsize; + } + + seg = (struct segment_command *)((uintptr_t)seg + seg->cmdsize); + } + +#endif + + lib.baseAddress = reinterpret_cast(baseAddr); + + return true; +} + +const char* MemoryUtils::ResolveAddr(void *handle, void* addr) +{ + struct link_map *dlmap; + dlmap = (struct link_map *)handle; + + LibSymbolTable *libtable; + AddrTable *table2; + + for (size_t i = 0; i < m_SymTables.size(); i++) + { + libtable = m_SymTables[i]; + if (libtable->lib_base == dlmap->l_addr) + { + table2 = &libtable->table2; + break; + } + } + + if (table2 == NULL) return NULL; + Symbol *sym = table2->FindAddr(addr); + return sym ? sym->buffer() : NULL; +} diff --git a/tools/gdc/MemoryUtils.h b/tools/gdc/MemoryUtils.h new file mode 100644 index 00000000..f5a02173 --- /dev/null +++ b/tools/gdc/MemoryUtils.h @@ -0,0 +1,89 @@ +/** + * vim: set ts=4 sw=4 tw=99 noet : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + */ + +#ifndef _INCLUDE_SOURCEMOD_MEMORYUTILS_H_ +#define _INCLUDE_SOURCEMOD_MEMORYUTILS_H_ + +//#include "common_logic.h" +#include +#include +#include +#if defined PLATFORM_LINUX || defined PLATFORM_APPLE +//#include +#include "sm_symtable.h" + +//using namespace SourceHook; +#endif + +using namespace SourceMod; +using std::vector; + +struct DynLibInfo +{ + void *baseAddress; + size_t memorySize; +}; + +#if defined PLATFORM_LINUX || defined PLATFORM_APPLE +struct LibSymbolTable +{ + SymbolTable table; + AddrTable table2; + uintptr_t lib_base; + uint32_t last_pos; +}; +#endif + +class MemoryUtils //: +// public IMemoryUtils, +// public SMGlobalClass +{ +public: + MemoryUtils(); + ~MemoryUtils(); +public: // SMGlobalClass +// void OnSourceModAllInitialized(); +public: // IMemoryUtils + void *FindPattern(const void *libPtr, const char *pattern, size_t len); + void *ResolveSymbol(void *handle, const char *symbol); + const char *ResolveAddr(void *handle, void *addr); +public: + bool GetLibraryInfo(const void *libPtr, DynLibInfo &lib); +#if defined PLATFORM_LINUX || defined PLATFORM_APPLE +private: + vector m_SymTables; +#ifdef PLATFORM_APPLE + struct dyld_all_image_infos *m_ImageList; +#endif +#endif +}; + +//extern MemoryUtils g_MemUtils; + +#endif // _INCLUDE_SOURCEMOD_MEMORYUTILS_H_ diff --git a/tools/gdc/TextParsers.cpp b/tools/gdc/TextParsers.cpp new file mode 100644 index 00000000..60800453 --- /dev/null +++ b/tools/gdc/TextParsers.cpp @@ -0,0 +1,1094 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include +#include +#include +#include +#include +#include +#include "TextParsers.h" +//#include "ShareSys.h" +//#include "sm_stringutil.h" +//#include "LibrarySys.h" + +TextParsers g_TextParser; +ITextParsers *textparsers = &g_TextParser; + +static int g_ini_chartable1[255] = {0}; +static int g_ws_chartable[255] = {0}; + +bool TextParsers::IsWhitespace(const char *stream) +{ + return g_ws_chartable[(unsigned)*stream] == 1; +} + +TextParsers::TextParsers() +{ + g_ini_chartable1[(unsigned)'_'] = 1; + g_ini_chartable1[(unsigned)'-'] = 1; + g_ini_chartable1[(unsigned)','] = 1; + g_ini_chartable1[(unsigned)'+'] = 1; + g_ini_chartable1[(unsigned)'.'] = 1; + g_ini_chartable1[(unsigned)'$'] = 1; + g_ini_chartable1[(unsigned)'?'] = 1; + g_ini_chartable1[(unsigned)'/'] = 1; + g_ws_chartable[(unsigned)'\n'] = 1; + g_ws_chartable[(unsigned)'\v'] = 1; + g_ws_chartable[(unsigned)'\r'] = 1; + g_ws_chartable[(unsigned)'\t'] = 1; + g_ws_chartable[(unsigned)'\f'] = 1; + g_ws_chartable[(unsigned)' '] = 1; +} + +unsigned int TextParsers::GetUTF8CharBytes(const char *stream) +{ + return _GetUTF8CharBytes(stream); +} + +/** + * File streams + */ + +bool FileStreamReader(void *stream, char *buffer, size_t maxlength, unsigned int *read) +{ + size_t num = fread(buffer, 1, maxlength, (FILE *)stream); + + *read = static_cast(num); + + if (num == 0 && feof((FILE *)stream)) + { + return true; + } + + return (ferror((FILE *)stream) == 0); +} + +SMCError TextParsers::ParseFile_SMC(const char *file, ITextListener_SMC *smc, SMCStates *states) +{ + FILE *fp = fopen(file, "rt"); + + if (!fp) + { + if (states != NULL) + { + states->line = 0; + states->col = 0; + } + return SMCError_StreamOpen; + } + + SMCError result = ParseStream_SMC(fp, FileStreamReader, smc, states); + + fclose(fp); + + return result; +} + +SMCError TextParsers::ParseSMCFile(const char *file, + ITextListener_SMC *smc_listener, + SMCStates *states, + char *buffer, + size_t maxsize) +{ + const char *errstr; + FILE *fp = fopen(file, "rt"); + + if (fp == NULL) + { + char error[256] = "unknown"; + if (states != NULL) + { + states->line = 0; + states->col = 0; + } + //g_LibSys.GetPlatformError(error, sizeof(error)); + UTIL_Format(buffer, maxsize, "File could not be opened: %s", error); + return SMCError_StreamOpen; + } + + SMCError result = ParseStream_SMC(fp, FileStreamReader, smc_listener, states); + + fclose(fp); + + errstr = GetSMCErrorString(result); + UTIL_Format(buffer, maxsize, "%s", errstr != NULL ? errstr : "Unknown error"); + + return result; +} + +struct RawStream +{ + const char *stream; + size_t length; + size_t pos; +}; + +bool RawStreamReader(void *stream, char *buffer, size_t maxlength, unsigned int *read) +{ + RawStream *rs = (RawStream *)stream; + + if (rs->pos >= rs->length) + { + return false; + } + + size_t remaining = rs->length - rs->pos; + + /* Use the smaller of the two */ + size_t copy = (remaining > maxlength) ? maxlength : remaining; + + memcpy(buffer, &rs->stream[rs->pos], copy); + rs->pos += copy; + *read = copy; + assert(rs->pos <= rs->length); + + return true; +} + +SMCError TextParsers::ParseSMCStream(const char *stream, + size_t length, + ITextListener_SMC *smc_listener, + SMCStates *states, + char *buffer, + size_t maxsize) +{ + RawStream rs; + SMCError result; + + rs.stream = stream; + rs.length = length; + rs.pos = 0; + + result = ParseStream_SMC(&rs, RawStreamReader, smc_listener, states); + + const char *errstr = GetSMCErrorString(result); + UTIL_Format(buffer, maxsize, "%s", errstr != NULL ? errstr : "Unknown error"); + + return result; +} + +/** + * Raw parsing of streams with helper functions + */ + +struct StringInfo +{ + StringInfo() : quoted(false), ptr(NULL), end(NULL), special(false) { } + bool quoted; + char *ptr; + char *end; + bool special; +}; + +const char *FixupString(StringInfo &data) +{ + if (!data.ptr) + { + return NULL; + } + + if (data.quoted) + { + data.ptr++; + } +#if defined _DEBUG + else { + /* A string will never have beginning whitespace because we ignore it in the stream. + * Furthermore, if there is trailing whitespace, the end ptr will point to it, so it is valid + * to overwrite! Lastly, the last character must be whitespace or a comment/invalid character. + */ + } +#endif + + /* Do some extra work on strings that have special quoted characters. */ + if (data.special) + { + char *outptr = data.ptr; + size_t len = data.end - data.ptr; + if (len >= 2) + { + for (size_t i=0; i=0; i--) + { + if (info[i].ptr) + { + return info[i].ptr; + } + } + + return NULL; +} + +SMCError TextParsers::ParseStream_SMC(void *stream, + STREAMREADER srdr, + ITextListener_SMC *smc, + SMCStates *pStates) +{ + char *reparse_point = NULL; + char in_buf[4096]; + char *parse_point = in_buf; + char *line_begin = in_buf; + unsigned int read; + unsigned int curlevel = 0; + bool in_quote = false; + bool ignoring = false; + bool eol_comment = false; + bool ml_comment = false; + unsigned int i; + SMCError err = SMCError_Okay; + SMCResult res; + SMCStates states; + char c; + + StringInfo strings[3]; + StringInfo emptystring; + + states.line = 1; + states.col = 0; + + smc->ReadSMC_ParseStart(); + + /** + * The stream reader reads in as much as it can fill the buffer with. + * It then processes the buffer. If the buffer cannot be fully processed, for example, + * a line is left hanging with no newline, then the contents of the buffer is shifted + * down, and the buffer is filled from the stream reader again. + * + * What makes this particularly annoying is that we cache pointers everywhere, so when + * the shifting process takes place, all those pointers must be shifted as well. + */ + while (srdr(stream, parse_point, sizeof(in_buf) - (parse_point - in_buf) - 1, &read)) + { + if (!read) + { + break; + } + + /* Check for BOM markings, which is only relevant on the first line. + * Not worth it, but it could be moved out of the loop. + */ + if (states.line == 1 && + in_buf[0] == (char)0xEF && + in_buf[1] == (char)0xBB && + in_buf[2] == (char)0xBF) + { + /* Move EVERYTHING down :\ */ + memmove(in_buf, &in_buf[3], read - 3); + read -= 3; + } + + if (reparse_point) + { + read += (parse_point - reparse_point); + parse_point = reparse_point; + reparse_point = NULL; + } + + for (i=0; iReadSMC_RawLine(&states, line_begin)) != SMCResult_Continue) + { + err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay; + goto failed; + } + parse_point[i] = '\n'; + + /* Now we check the sanity of our staged strings! */ + if (strings[2].ptr) + { + if (!curlevel) + { + err = SMCError_InvalidProperty1; + goto failed; + } + /* Assume the next string is a property and pass the info on. */ + if ((res=smc->ReadSMC_KeyValue( + &states, + FixupString(strings[2]), + FixupString(strings[1]))) != SMCResult_Continue) + { + err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay; + goto failed; + } + scrap(strings); + } + + /* Change the states for the next line */ + states.col = 0; + states.line++; + line_begin = &parse_point[i+1]; //Note: safe because this gets relocated later + } + else if (ignoring) + { + if (in_quote) + { + /* If i was 0, we could have reparsed, so make sure there's no buffer underrun */ + if ((&parse_point[i] != in_buf) && c == '"' && parse_point[i-1] != '\\') + { + /* If we reached a quote in an ignore phase, + * we're staging a string and we must rotate it out. + */ + in_quote = false; + ignoring = false; + /* Set our info */ + strings[0].end = &parse_point[i]; + strings[0].quoted = true; + if (rotate(strings) != NULL) + { + /* If we rotated too many strings, there was too much crap on one line */ + err = SMCError_InvalidTokens; + goto failed; + } + } + else if (c == '\\') + { + strings[0].special = true; + if (i == (read - 1)) + { + reparse_point = &parse_point[i]; + break; + } + } + } + else if (ml_comment) + { + if (c == '*') + { + /* Check if we need to get more input first */ + if (i == read - 1) + { + reparse_point = &parse_point[i]; + break; + } + if (parse_point[i+1] == '/') + { + ml_comment = false; + ignoring = false; + /* We should not be staging anything right now. */ + assert(strings[0].ptr == NULL); + /* Advance the input stream so we don't choke on this token */ + i++; + states.col++; + } + } + } + } + else + { + /* Check if we're whitespace or not */ + if (!g_ws_chartable[(unsigned)c]) + { + bool restage = false; + /* Check various special tokens: + * ; + * // + * / * + * { + * } + */ + if (c == ';' || c == '/') + { + /* If it's a line-based comment (that is, ; or //) + * we will need to scrap everything until the end of the line. + */ + if (c == '/') + { + if (i == read - 1) + { + /* If we reached the end of the look-ahead, we need to re-check our input. + * Breaking out will force this to be the new reparse point! + */ + reparse_point = &parse_point[i]; + break; + } + if (parse_point[i + 1] == '/') + { + /* standard comment */ + ignoring = true; + eol_comment = true; + restage = true; + } + else if (parse_point[i+1] == '*') + { + /* inline comment - start ignoring */ + ignoring = true; + ml_comment = true; + /* yes, we restage, meaning that: + * STR/ *stuff* /ING (space because ml comments don't nest in C++) + * will not generate 'STRING', but rather 'STR' and 'ING'. + * This should be a rare occurrence and is done here for convenience. + */ + restage = true; + } + } + else + { + ignoring = true; + eol_comment = true; + restage = true; + } + } + else if (c == '{') + { + /* If we are staging a string, we must rotate here */ + if (strings[0].ptr) + { + /* We have unacceptable tokens on this line */ + if (rotate(strings) != NULL) + { + err = SMCError_InvalidSection1; + goto failed; + } + } + /* Sections must always be alone */ + if (strings[2].ptr != NULL) + { + err = SMCError_InvalidSection1; + goto failed; + } + else if (strings[1].ptr == NULL) + { + err = SMCError_InvalidSection2; + goto failed; + } + if ((res=smc->ReadSMC_NewSection(&states, FixupString(strings[1]))) + != SMCResult_Continue) + { + err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay; + goto failed; + } + strings[1] = emptystring; + curlevel++; + } + else if (c == '}') + { + /* Unlike our matching friend, this can be on the same line as something prior */ + if (rotate(strings) != NULL) + { + err = SMCError_InvalidSection3; + goto failed; + } + if (strings[2].ptr) + { + if (!curlevel) + { + err = SMCError_InvalidProperty1; + goto failed; + } + if ((res=smc->ReadSMC_KeyValue( + &states, + FixupString(strings[2]), + FixupString(strings[1]))) + != SMCResult_Continue) + { + err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay; + goto failed; + } + } + else if (strings[1].ptr) + { + err = SMCError_InvalidSection3; + goto failed; + } + else if (!curlevel) + { + err = SMCError_InvalidSection4; + goto failed; + } + /* Now it's safe to leave the section */ + scrap(strings); + if ((res=smc->ReadSMC_LeavingSection(&states)) != SMCResult_Continue) + { + err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay; + goto failed; + } + curlevel--; + } + else if (c == '"') + { + /* If we get a quote mark, we always restage, but we need to do it beforehand */ + if (strings[0].ptr) + { + strings[0].end = &parse_point[i]; + if (rotate(strings) != NULL) + { + err = SMCError_InvalidTokens; + goto failed; + } + } + strings[0].ptr = &parse_point[i]; + in_quote = true; + ignoring = true; + } + else if (!strings[0].ptr) + { + /* If we have no string, we must start one */ + strings[0].ptr = &parse_point[i]; + } + if (restage && strings[0].ptr) + { + strings[0].end = &parse_point[i]; + if (rotate(strings) != NULL) + { + err = SMCError_InvalidTokens; + goto failed; + } + } + } + else + { + /* If we're eating a string and get whitespace, we need to restage. + * (Note that if we are quoted, this is being ignored) + */ + if (strings[0].ptr) + { + /* + * The specification says the second string in a pair does not need to be quoted. + * Thus, we check if there's already a string on the stack. + * If there's a newline, we always rotate so the newline has an empty starter. + */ + if (!strings[1].ptr) + { + /* There's no string, so we must move this one down and eat up another */ + strings[0].end = &parse_point[i]; + rotate(strings); + } + else if (!strings[1].quoted) + { + err = SMCError_InvalidTokens; + goto failed; + } + } + } + } + + /* Advance which token we're on */ + states.col++; + } + + if (line_begin != in_buf) + { + /* The line buffer has advanced, so it's safe to copy N bytes back to the beginning. + * What's N? N is the lowest point we're currently relying on. + */ + char *stage = lowstring(strings); + if (!stage || stage > line_begin) + { + stage = line_begin; + } + unsigned int bytes = read - (stage - parse_point); + + /* It is now safe to delete everything before the staged point */ + memmove(in_buf, stage, bytes); + + /* Calculate the number of bytes in the new buffer */ + bytes = stage - in_buf; + /* Relocate all the cached pointers to our new base */ + line_begin -= bytes; + reloc(strings[0], bytes); + reloc(strings[1], bytes); + reloc(strings[2], bytes); + if (reparse_point) + { + reparse_point -= bytes; + } + if (parse_point) + { + parse_point = &parse_point[read]; + parse_point -= bytes; + } + } + else if (read == sizeof(in_buf) - 1) + { + err = SMCError_TokenOverflow; + goto failed; + } + } + + /* If we're done parsing and there are tokens left over... */ + if (curlevel) + { + err = SMCError_InvalidSection5; + goto failed; + } + else if (strings[0].ptr || strings[1].ptr) + { + err = SMCError_InvalidTokens; + goto failed; + } + + smc->ReadSMC_ParseEnd(false, false); + + if (pStates != NULL) + { + *pStates = states; + } + + return SMCError_Okay; + +failed: + if (pStates != NULL) + { + *pStates = states; + } + + smc->ReadSMC_ParseEnd(true, (err == SMCError_Custom)); + + return err; +} + + +/** + * INI parser + */ + +bool TextParsers::ParseFile_INI(const char *file, ITextListener_INI *ini_listener, unsigned int *line, unsigned int *col) +{ + FILE *fp = fopen(file, "rt"); + unsigned int curline = 0; + unsigned int curtok; + size_t len; + + if (!fp) + { + if (line) + { + *line = 0; + } + + return false; + } + + char buffer[2048]; + char *ptr, *save_ptr; + bool in_quote; + + while (!feof(fp)) + { + curline++; + curtok = 0; + buffer[0] = '\0'; + if (fgets(buffer, sizeof(buffer), fp) == NULL) + { + break; + } + + //:TODO: this will only run once, so find a nice way to move it out of the while loop + /* If this is the first line, check the first three bytes for BOM */ + if (curline == 1 && + buffer[0] == (char)0xEF && + buffer[1] == (char)0xBB && + buffer[2] == (char)0xBF) + { + /* We have a UTF-8 marked file... skip these bytes */ + ptr = &buffer[3]; + } else { + ptr = buffer; + } + + /*************************************************** + * We preprocess the string before parsing tokens! * + ***************************************************/ + + /* First strip beginning whitespace */ + while (*ptr != '\0' && g_ws_chartable[(unsigned)*ptr] != 0) + { + ptr++; + } + + len = strlen(ptr); + + if (!len) + { + continue; + } + + /* Now search for comment characters */ + in_quote = false; + save_ptr = ptr; + for (size_t i=0; i=0 && iReadINI_RawLine(ptr, &curtok)) + { + goto event_failed; + } + + if (*ptr == '[') + { + bool invalid_tokens = false; + bool got_bracket = false; + bool extra_tokens = false; + char c; + bool alnum; + wchar_t wc; + + for (size_t i=1; iReadINI_NewSection(&ptr[1], invalid_tokens, got_bracket, extra_tokens, &curtok)) + { + goto event_failed; + } + } else { + char *key_ptr = ptr; + char *val_ptr = NULL; + char c; + size_t first_space = 0; + bool invalid_tokens = false; + bool equal_token = false; + bool quotes = false; + bool alnum; + wchar_t wc; + + for (size_t i=0; iReadINI_KeyValue(key_ptr, val_ptr, invalid_tokens, equal_token, quotes, &curtok)) + { + curtok = 0; + goto event_failed; + } + } + } + + if (line) + { + *line = curline; + } + + fclose(fp); + + return true; + +event_failed: + if (line) + { + *line = curline; + } + + if (col) + { + *col = curtok; + } + + fclose(fp); + + return false; +} + +const char *TextParsers::GetSMCErrorString(SMCError err) +{ + static const char *s_errors[] = + { + NULL, + "Stream failed to open", + "Stream returned read error", + NULL, + "Un-quoted section has invalid tokens", + "Section declared without header", + "Section declared with unknown tokens", + "Section ending without a matching section beginning", + "Section beginning without a matching ending", + "Line contained too many invalid tokens", + "Token buffer overflowed", + "A property was declared outside of a section", + }; + + if (err < SMCError_Okay || err > SMCError_InvalidProperty1) + { + return NULL; + } + + return s_errors[err]; +} diff --git a/tools/gdc/TextParsers.h b/tools/gdc/TextParsers.h new file mode 100644 index 00000000..037a57c2 --- /dev/null +++ b/tools/gdc/TextParsers.h @@ -0,0 +1,92 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_TEXTPARSERS_H_ +#define _INCLUDE_SOURCEMOD_TEXTPARSERS_H_ + +#include +#include "gdc.h" + +using namespace SourceMod; + +/** + * @param void * IN: Stream pointer + * @param char * IN/OUT: Stream buffer + * @param size_t IN: Maximum size of buffer + * @param unsigned int * OUT: Number of bytes read (0 = end of stream) + * @return True on success, false on failure + */ +typedef bool (*STREAMREADER)(void *, char *, size_t, unsigned int *); + +class TextParsers : + public ITextParsers +{ +public: + TextParsers(); +public: + bool ParseFile_INI(const char *file, + ITextListener_INI *ini_listener, + unsigned int *line, + unsigned int *col); + + SMCError ParseFile_SMC(const char *file, + ITextListener_SMC *smc_listener, + SMCStates *states); + + SMCError ParseSMCFile(const char *file, + ITextListener_SMC *smc_listener, + SMCStates *states, + char *buffer, + size_t maxsize); + + SMCError ParseSMCStream(const char *stream, + size_t length, + ITextListener_SMC *smc_listener, + SMCStates *states, + char *buffer, + size_t maxsize); + + unsigned int GetUTF8CharBytes(const char *stream); + + const char *GetSMCErrorString(SMCError err); + bool IsWhitespace(const char *stream); +private: + SMCError ParseStream_SMC(void *stream, + STREAMREADER srdr, + ITextListener_SMC *smc, + SMCStates *states); + +}; + +extern TextParsers g_TextParser; + +#endif //_INCLUDE_SOURCEMOD_TEXTPARSERS_H_ + diff --git a/tools/gdc/gdc.cpp b/tools/gdc/gdc.cpp new file mode 100644 index 00000000..07e15f09 --- /dev/null +++ b/tools/gdc/gdc.cpp @@ -0,0 +1,266 @@ +#include +#include +#include +#include +#include "gdc.h" +#include "GameConfigs.h" +#include "MemoryUtils.h" + +MemoryUtils mu; + +char *game = NULL; +char *engine = NULL; +char *binary = NULL; + +int main(int argc, char **argv) +{ + char *gamedata = NULL; + bool debug = false; + + opterr = 0; + int opt; + while ((opt = getopt(argc, argv, "b:de:f:g:")) != -1) + switch (opt) + { + case 'd': + debug = true; + break; + case 'g': + game = optarg; + break; + + case 'e': + engine = optarg; + break; + + case 'f': + gamedata = optarg; + break; + + case 'b': + binary = optarg; + break; + + case '?': + printf("Usage: ./gdc -g -e -f -b \n"); + return 0; + + default: + printf("WHAT\n"); + return 0; + } + + if (!game || !engine || !gamedata || !binary) + { + printf("Usage: ./gdc -g -e -f -b \n"); + return 0; + } + + printf("Game: %s\nEngine: %s\nGamedata: %s\nBinary: %s\n", game, engine, gamedata, binary); + + void *handle = dlopen(binary, RTLD_LAZY); + if (!handle) + { + printf("Couldn't open %s (%s)\n", binary, dlerror()); + return 0; + } + + CGameConfig gc; + char err[512]; + if (!gc.EnterFile(gamedata, err, sizeof(err))) + { + printf("%s: %s\n", gamedata, err); + return 0; + } + + CGameConfig symbols; + if (!symbols.EnterFile("symbols.txt", err, sizeof(err))) + { + printf("symbols.txt: %s\n", err); + return 0; + } + + if (debug) + for (map::iterator it = symbols.m_Keys.begin(); it != symbols.m_Keys.end(); it++) + printf("%s - %s\n", it->first, it->second); + + const char *vtsym = symbols.GetKeyValue("vtsym"); + if (!vtsym) + { + printf("Couldn't find vtsym\n"); + return 0; + } + +#if 0 + void *lolwhat = (void*)1; + void *handle = lolwhat; + handle = dlopen(binary, RTLD_LAZY); + if (!handle) + { + printf("Couldn't open %s\n", binary); + return 0; + } +#endif + + void **vt = (void**) mu.ResolveSymbol(handle, vtsym); + if (!vt) + { + printf("Couldn't find vtable %s\n", vtsym); + dlclose(handle); + return 0; + } + + + for (list::iterator it = gc.m_Offsets.begin(); it != gc.m_Offsets.end(); it++) + { + if (debug) printf("DEBUG %s - l: %d w: %d\n", it->name, it->lin, it->win); +// else + { + const char *symbol = symbols.GetKeyValue(it->name); + if (symbol) + { + int offset = findVFunc(handle, vt, symbol); + + if (offset == it->lin) printf("%s - good\n", it->name); + else printf("%s - l: %d w: %d\n", it->name, offset, offset + it->lin - it->win); + } + else printf("%s - no Linux symbol, skipping\n", it->name); + } + } + + printf("\nWindows offsets are wild guesses!\n\n"); + + for (list::iterator it = gc.m_Sigs.begin(); it != gc.m_Sigs.end(); it++) + { + if (debug) printf("DEBUG %s - %s - l: %s\n", it->name, it->lib == Server ? "server" : "engine", it->lin); +// else + { + if (it->lib != Server) + { + printf("%s - isn't server, skipping\n", it->name); + continue; + } + + const char *symbol = it->lin; + + if (!symbol) + { + printf("%s - no Linux symbol, skipping\n", it->name); + continue; + } + + if (symbol[0] != '@') + { + printf("%s - didn't start with '@', skipping\n", it->name); + continue; + } + + void *addr = mu.ResolveSymbol(handle, symbol + 1); + + if (addr) printf("%s - good\n", it->name); + else printf("%s - %s not found\n", it->name, it->lin); + } + } + + return 0; +} + +/* + takes a mangled member function symbol and returns the position where the function name and parameters start + 01234567890123456789012345678 + _ZN9CTFPlayer12ForceRespawnEv + ^13 ^28 +*/ +void findFuncPos(const char *sym, int &func, int ¶ms) +{ + int i = 0; + while ((sym[i] < '0') || (sym[i] > '9')) i++; + int classLen = atoi(sym + i); + func = i + (int)ceil(log10(classLen)) + classLen; + int funcLen = atoi(sym + func); + params = func + (int)ceil(log10(funcLen)) + funcLen; +} + +int findVFunc(void *handle, void **vt, const char *symbol) +{ + int offset = 1; + //int overloads = 0; + int funcPos, paramPos; + findFuncPos(symbol, funcPos, paramPos); + + for (int i = 0; i < 1000; i++) + { + void *pFunc = vt[i]; + const char *s; + +// Dl_info info; +// if (!mu.ResolveAddr(pFunc, &info)) continue; + if (!(s = mu.ResolveAddr(handle, pFunc))) continue; + +// if ((i > 1) && (strncmp(info.dli_sname, "_ZTI", 4) == 0)) break; + if ((i > 1) && (strncmp(s, "_ZTI", 4) == 0)) break; + + + int tempFuncPos, tempParamPos; +// findFuncPos(info.dli_sname, tempFuncPos, tempParamPos); + findFuncPos(s, tempFuncPos, tempParamPos); + +#if 0 + if (strncmp(info.dli_sname[tempFuncPos], symbol[funcPos], paramPos - funcPos) == 0) + { + overloads++; + if (firstOverload == -1) firstOverload = i - 1; + if (strncmp(info.dli_sname[tempParamPos], symbol[paramPos], strlen(symbol) - paramPos) == 0) + { + offset = i; + matchingOverload = overloads; + } + } +#else +// if (strcmp(info.dli_sname + tempFuncPos, symbol + funcPos) == 0) + if (strcmp(s + tempFuncPos, symbol + funcPos) == 0) + { + offset = i; + break; + } +#endif + } + + return offset - 2; +} + +unsigned int strncopy(char *dest, const char *src, size_t count) +{ + if (!count) + { + return 0; + } + + char *start = dest; + while ((*src) && (--count)) + { + *dest++ = *src++; + } + *dest = '\0'; + + return (dest - start); +} + +size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + size_t len = vsnprintf(buffer, maxlength, fmt, ap); + va_end(ap); + + if (len >= maxlength) + { + buffer[maxlength - 1] = '\0'; + return (maxlength - 1); + } + else + { + return len; + } +} + diff --git a/tools/gdc/gdc.h b/tools/gdc/gdc.h new file mode 100644 index 00000000..5616df88 --- /dev/null +++ b/tools/gdc/gdc.h @@ -0,0 +1,18 @@ +#ifndef _INCLUDE_GDC_MAIN_H_ +#define _INCLUDE_GDC_MAIN_H_ + +#include +#if 0 +#include "TextParsers.h" +#include "GameConfigs.h" + +using namespace SourceMod; +#endif + +int findFuncPos(const char* sym); +int findVFunc(void *handle, void **vt, const char *symbol); + +size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...); +unsigned int strncopy(char *dest, const char *src, size_t count); + +#endif // _INCLUDE_GDC_MAIN_H_ diff --git a/tools/gdc/sm_symtable.h b/tools/gdc/sm_symtable.h new file mode 100644 index 00000000..d89f4ec5 --- /dev/null +++ b/tools/gdc/sm_symtable.h @@ -0,0 +1,356 @@ +/** + * vim: set ts=4 sw=4 tw=99 noet : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + */ + +#ifndef _INCLUDE_SOURCEMOD_CORE_SYMBOLTABLE_H_ +#define _INCLUDE_SOURCEMOD_CORE_SYMBOLTABLE_H_ + +#include +#include +#include + +#define KESTRING_TABLE_START_SIZE 65536 + +struct Symbol +{ + size_t length; + uint32_t hash; + void *address; + Symbol *tbl_next; + + inline char *buffer() + { + return reinterpret_cast(this + 1); + } +}; + +class SymbolTable +{ +public: + ~SymbolTable() + { + for (uint32_t i = 0; i < nbuckets; i++) + { + Symbol *sym = buckets[i]; + while (sym != NULL) + { + Symbol *next = sym->tbl_next; + free(sym); + sym = next; + } + } + free(buckets); + } + + bool Initialize() + { + buckets = (Symbol **)malloc(sizeof(Symbol *) * KESTRING_TABLE_START_SIZE); + if (buckets == NULL) + { + return false; + } + memset(buckets, 0, sizeof(Symbol *) * KESTRING_TABLE_START_SIZE); + + nbuckets = KESTRING_TABLE_START_SIZE; + nused = 0; + bucketmask = KESTRING_TABLE_START_SIZE - 1; + return true; + } + + static inline uint32_t HashString(const char *data, size_t len) + { + #undef get16bits + #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) + #define get16bits(d) (*((const uint16_t *) (d))) + #endif + #if !defined (get16bits) + #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\ + +(uint32_t)(((const uint8_t *)(d))[0]) ) + #endif + uint32_t hash = len, tmp; + int rem; + + if (len <= 0 || data == NULL) + { + return 0; + } + + rem = len & 3; + len >>= 2; + + /* Main loop */ + for (;len > 0; len--) { + hash += get16bits (data); + tmp = (get16bits (data+2) << 11) ^ hash; + hash = (hash << 16) ^ tmp; + data += 2 * sizeof (uint16_t); + hash += hash >> 11; + } + + /* Handle end cases */ + switch (rem) { + case 3: hash += get16bits (data); + hash ^= hash << 16; + hash ^= data[sizeof (uint16_t)] << 18; + hash += hash >> 11; + break; + case 2: hash += get16bits (data); + hash ^= hash << 11; + hash += hash >> 17; + break; + case 1: hash += *data; + hash ^= hash << 10; + hash += hash >> 1; + } + + /* Force "avalanching" of final 127 bits */ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + + return hash; + + #undef get16bits + } + + Symbol **FindSymbolBucket(const char *str, size_t len, uint32_t hash) + { + uint32_t bucket = hash & bucketmask; + Symbol **pkvs = &buckets[bucket]; + + Symbol *kvs = *pkvs; + while (kvs != NULL) + { + if (len == kvs->length && memcmp(str, kvs->buffer(), len * sizeof(char)) == 0) + { + return pkvs; + } + pkvs = &kvs->tbl_next; + kvs = *pkvs; + } + + return pkvs; + } + + void ResizeSymbolTable() + { + uint32_t xnbuckets = nbuckets * 2; + Symbol **xbuckets = (Symbol **)malloc(sizeof(Symbol *) * xnbuckets); + if (xbuckets == NULL) + { + return; + } + memset(xbuckets, 0, sizeof(Symbol *) * xnbuckets); + uint32_t xbucketmask = xnbuckets - 1; + for (uint32_t i = 0; i < nbuckets; i++) + { + Symbol *sym = buckets[i]; + while (sym != NULL) + { + Symbol *next = sym->tbl_next; + uint32_t bucket = sym->hash & xbucketmask; + sym->tbl_next = xbuckets[bucket]; + xbuckets[bucket] = sym; + sym = next; + } + } + free(buckets); + buckets = xbuckets; + nbuckets = xnbuckets; + bucketmask = xbucketmask; + } + + Symbol *FindSymbol(const char *str, size_t len) + { + uint32_t hash = HashString(str, len); + Symbol **pkvs = FindSymbolBucket(str, len, hash); + return *pkvs; + } + + Symbol *InternSymbol(const char* str, size_t len, void *address) + { + uint32_t hash = HashString(str, len); + Symbol **pkvs = FindSymbolBucket(str, len, hash); + if (*pkvs != NULL) + { + return *pkvs; + } + + Symbol *kvs = (Symbol *)malloc(sizeof(Symbol) + sizeof(char) * (len + 1)); + kvs->length = len; + kvs->hash = hash; + kvs->address = address; + kvs->tbl_next = NULL; + memcpy(kvs + 1, str, sizeof(char) * (len + 1)); + *pkvs = kvs; + nused++; + + if (nused > nbuckets && nbuckets <= INT_MAX / 2) + { + ResizeSymbolTable(); + } + + return kvs; + } +private: + uint32_t nbuckets; + uint32_t nused; + uint32_t bucketmask; + Symbol **buckets; +}; + +class AddrTable +{ +public: + ~AddrTable() + { + for (uint32_t i = 0; i < nbuckets; i++) + { + Symbol *sym = buckets[i]; + while (sym != NULL) + { + Symbol *next = sym->tbl_next; + free(sym); + sym = next; + } + } + free(buckets); + } + + bool Initialize() + { + buckets = (Symbol **)malloc(sizeof(Symbol *) * KESTRING_TABLE_START_SIZE); + if (buckets == NULL) + { + return false; + } + memset(buckets, 0, sizeof(Symbol *) * KESTRING_TABLE_START_SIZE); + + nbuckets = KESTRING_TABLE_START_SIZE; + nused = 0; + bucketmask = KESTRING_TABLE_START_SIZE - 1; + return true; + } + + //Knuth's quick 32-bit integer hash, might not be appropriate but I don't think I care much! + static inline uint32_t HashAddr(void *addr) + { + return (uint32_t)addr * (uint32_t)2654435761u; + } + + Symbol **FindSymbolBucket(void *addr, uint32_t hash) + { + uint32_t bucket = hash & bucketmask; + Symbol **pkvs = &buckets[bucket]; + + Symbol *kvs = *pkvs; + while (kvs != NULL) + { + if (kvs->address == addr) + { + return pkvs; + } + pkvs = &kvs->tbl_next; + kvs = *pkvs; + } + + return pkvs; + } + + void ResizeSymbolTable() + { + uint32_t xnbuckets = nbuckets * 2; + Symbol **xbuckets = (Symbol **)malloc(sizeof(Symbol *) * xnbuckets); + if (xbuckets == NULL) + { + return; + } + memset(xbuckets, 0, sizeof(Symbol *) * xnbuckets); + uint32_t xbucketmask = xnbuckets - 1; + for (uint32_t i = 0; i < nbuckets; i++) + { + Symbol *sym = buckets[i]; + while (sym != NULL) + { + Symbol *next = sym->tbl_next; + uint32_t bucket = sym->hash & xbucketmask; + sym->tbl_next = xbuckets[bucket]; + xbuckets[bucket] = sym; + sym = next; + } + } + free(buckets); + buckets = xbuckets; + nbuckets = xnbuckets; + bucketmask = xbucketmask; + } + + Symbol *FindAddr(void *addr) + { + uint32_t hash = HashAddr(addr); + Symbol **pkvs = FindSymbolBucket(addr, hash); + return *pkvs; + } + + Symbol *InternSymbol(const char* str, size_t len, void *address) + { + uint32_t hash = HashAddr(address); + Symbol **pkvs = FindSymbolBucket(address, hash); + if (*pkvs != NULL) + { + return *pkvs; + } + + Symbol *kvs = (Symbol *)malloc(sizeof(Symbol) + sizeof(char) * (len + 1)); + kvs->length = len; + kvs->hash = hash; + kvs->address = address; + kvs->tbl_next = NULL; + memcpy(kvs + 1, str, sizeof(char) * (len + 1)); + *pkvs = kvs; + nused++; + + if (nused > nbuckets && nbuckets <= INT_MAX / 2) + { + ResizeSymbolTable(); + } + + return kvs; + } +private: + uint32_t nbuckets; + uint32_t nused; + uint32_t bucketmask; + Symbol **buckets; +}; + +#endif //_INCLUDE_SOURCEMOD_CORE_SYMBOLTABLE_H_ diff --git a/tools/gdc/symbols.txt b/tools/gdc/symbols.txt new file mode 100644 index 00000000..3060607f --- /dev/null +++ b/tools/gdc/symbols.txt @@ -0,0 +1,57 @@ +"Games" +{ + "#default" + { + "Keys" + { + "GiveNamedItem" "_ZN11CBasePlayer13GiveNamedItemEPKci" + "RemovePlayerItem" "_ZN11CBasePlayer16RemovePlayerItemEP17CBaseCombatWeapon" + "Weapon_GetSlot" "_ZNK20CBaseCombatCharacter14Weapon_GetSlotEi" + "Ignite" "_ZN14CBaseAnimating6IgniteEfbfb" + "Extinguish" "_ZN14CBaseAnimating10ExtinguishEv" + "Teleport" "_ZN11CBaseEntity8TeleportEPK6VectorPK6QAngleS2_" + "CommitSuicide" "_ZN11CBasePlayer13CommitSuicideEv" + "GetVelocity" "_ZN11CBaseEntity11GetVelocityEP6VectorS1_" + "EyeAngles" "_ZN11CBasePlayer9EyeAnglesEv" + "DispatchKeyValue" "_ZN11CBaseEntity8KeyValueEPKcS1_" + "DispatchKeyValueFloat" "_ZN11CBaseEntity8KeyValueEPKcf" + "DispatchKeyValueVector" "_ZN11CBaseEntity8KeyValueEPKcRK6Vector" + "SetEntityModel" "_ZN11CBaseEntity8SetModelEPKc" + "AcceptInput" "_ZN11CBaseEntity11AcceptInputEPKcPS_S2_9variant_ti" + "WeaponEquip" "_ZN11CBasePlayer12Weapon_EquipEP17CBaseCombatWeapon" + "Activate" "_ZN11CBaseEntity8ActivateEv" + "PlayerRunCmd" "_ZN11CBasePlayer16PlayerRunCommandEP8CUserCmdP11IMoveHelper" + } + } + + "#default" + { + "#supported" + { + "game" "tf" + } + + "Keys" + { + "vtsym" "_ZTV9CTFPlayer" + "CommitSuicide" "_ZN9CTFPlayer13CommitSuicideEbb" + "ForceRespawn" "_ZN9CTFPlayer12ForceRespawnEv" + } + } + + "#default" + { + "#supported" + { + "game" "left4dead" + "game" "left4dead2" + } + + + "Keys" + { + "vtsym" "_ZTV13CTerrorPlayer" + "CommitSuicide" "_ZN11CBasePlayer13CommitSuicideEbb" + } + } +}