From 6d82ab4c0f94e84b49da63363821ce3ea15844c0 Mon Sep 17 00:00:00 2001 From: nullifiedcat Date: Wed, 8 Feb 2017 19:39:41 +0300 Subject: [PATCH] Lesser projaim impr, ischema --- cathook/TODO | 2 + cathook/src/common.h | 2 +- cathook/src/gui/CMenuWindow.cpp | 7 + cathook/src/hacks/Aimbot.cpp | 5 +- cathook/src/hacks/Misc.cpp | 55 + cathook/src/hacks/Misc.h | 2 + cathook/src/netvars.cpp | 1 + cathook/src/netvars.h | 1 + cathook/src/prediction.cpp | 98 +- cathook/src/prediction.h | 8 +- cathook/src/sdk/utlbuffer.cpp | 1794 +++++++++++++++++++++++++++++++ 11 files changed, 1934 insertions(+), 41 deletions(-) create mode 100644 cathook/src/sdk/utlbuffer.cpp diff --git a/cathook/TODO b/cathook/TODO index bf6d6d84..e4bc8993 100644 --- a/cathook/TODO +++ b/cathook/TODO @@ -7,6 +7,8 @@ bind l clearcond_taunt no fakelag shoot fullbright toggle +Hunter Rifle +inspect shit instant taunt TTS noise spam diff --git a/cathook/src/common.h b/cathook/src/common.h index 0f78edc2..c3cc9bf0 100644 --- a/cathook/src/common.h +++ b/cathook/src/common.h @@ -53,7 +53,7 @@ #ifndef CATHOOK_BUILD_NUMBER #define CATHOOK_BUILD_NUMBER "LATEST" #endif -#define CATHOOK_BUILD_NAME "Cinnamon Pie" +#define CATHOOK_BUILD_NAME "Butterscotch Pie" #define CON_NAME "cat" #define CON_PREFIX CON_NAME "_" diff --git a/cathook/src/gui/CMenuWindow.cpp b/cathook/src/gui/CMenuWindow.cpp index 903337bd..fd1cb530 100644 --- a/cathook/src/gui/CMenuWindow.cpp +++ b/cathook/src/gui/CMenuWindow.cpp @@ -140,6 +140,13 @@ void CMenuWindow::AddElements() { ADDCVAR(g_Settings.flForceFOV); ADDCVAR(g_Settings.sDisconnectMsg); ADDCVAR(g_phMisc->v_bCleanChat); + if (TF2) { + CBaseButton* but = new CBaseButton("schema", tab, "Load Custom Schema", [this](CBaseButton*) { + Schema_Reload(); + }); + but->Props()->SetString("tooltip", "Loads a custom item schema from ~/.cathook/\nitems_game.txt"); + tab->AddChild(but); + } if (TF2C) ADDCVAR(g_phMisc->v_bMinigunJump); //ADDCVAR(g_phMisc->v_bDebugInfo); if (HL2DM) ADDCVAR(g_phMisc->v_bFlashlightSpam); diff --git a/cathook/src/hacks/Aimbot.cpp b/cathook/src/hacks/Aimbot.cpp index 66a1b3f6..4738ce2c 100644 --- a/cathook/src/hacks/Aimbot.cpp +++ b/cathook/src/hacks/Aimbot.cpp @@ -386,7 +386,7 @@ int Aimbot::ShouldTarget(CachedEntity* entity) { if (!GetHitbox(entity, hitbox, resultAim)) return 13; if (!IsEntityVisible(entity, hitbox)) return 14; } - resultAim = ProjectilePrediction(entity, hitbox, m_flProjSpeed, m_flProjGravity); + resultAim = ProjectilePrediction(entity, hitbox, m_flProjSpeed, m_flProjGravity, PlayerGravityMod(entity)); } else { if (!GetHitbox(entity, hitbox, resultAim)) return 15; } @@ -456,7 +456,8 @@ bool Aimbot::Aim(CachedEntity* entity, CUserCmd* cmd) { m_flProjSpeed = v_fOverrideProjSpeed->GetFloat(); if (v_fOverrideProjGravity->GetBool()) m_flProjGravity = v_fOverrideProjGravity->GetFloat(); - hit = ProjectilePrediction(entity, hitbox, m_flProjSpeed, m_flProjGravity); + bool succ = false; + hit = ProjectilePrediction(entity, hitbox, m_flProjSpeed, m_flProjGravity, PlayerGravityMod(entity)); } //logging::Info("ayyming!"); Vector tr = (hit - g_pLocalPlayer->v_Eye); diff --git a/cathook/src/hacks/Misc.cpp b/cathook/src/hacks/Misc.cpp index acfa091a..18a794b8 100644 --- a/cathook/src/hacks/Misc.cpp +++ b/cathook/src/hacks/Misc.cpp @@ -9,10 +9,18 @@ #include +#include +#include +#include +#include +#include + #include "../hack.h" #include "../common.h" +#include #include "../sdk.h" #include "../netmessage.h" +#include "../copypasted/CSignature.h" DEFINE_HACK_SINGLETON(Misc); @@ -243,6 +251,35 @@ void CC_DumpConds(const CCommand& args) { logging::Info("0x%08x 0x%08x 0x%08x 0x%08x", d2.cond_0, d2.cond_1, d2.cond_2, d2.cond_3); } +void Schema_Reload() { + static uintptr_t InitSchema_s = gSignatures.GetClientSignature("89 44 24 04 89 34 24 74 7B E8 FE DC FB FF 88 45 C7 80 7D C7 00 74 7B 8B 76 0C 39 F3 0F 85 BA 00 00 00 8B 5D D4 83 EB 01 8D 34 9D 00 00 00 00 EB 15 8D 76 00") - 0x54; + typedef void(*InitSchema_t)(void*, void*, CUtlBuffer& buffer, bool byte, unsigned version); + static InitSchema_t InitSchema = (InitSchema_t)InitSchema_s; + static uintptr_t GetItemSchema_s = gSignatures.GetClientSignature("55 89 E5 83 EC 18 89 5D F8 8B 1D ? ? ? ? 89 7D FC 85 DB 74 12 89 D8 8B 7D FC 8B 5D F8 89 EC 5D C3 8D B6 00 00 00 00 C7 04 24 A8 06 00 00 E8 ? ? ? ? B9 AA 01 00 00 89 C3 31 C0 89 DF"); + typedef void*(*GetItemSchema_t)(void); + static GetItemSchema_t GetItemSchema = (GetItemSchema_t)GetItemSchema_s;//(*(uintptr_t*)GetItemSchema_s + GetItemSchema_s + 4); + void* itemschema = (GetItemSchema() + 4); + void* data; + passwd* pwd = getpwuid(getuid()); + char* user = pwd->pw_name; + char* path = strfmt("/home/%s/.cathook/items_game.txt", user); + FILE* file = fopen(path, "r"); + delete [] path; + fseek(file, 0L, SEEK_END); + char buffer[4 * 1000 * 1000]; + size_t len = ftell(file); + rewind(file); + buffer[len + 1] = 0; + fread(&buffer, sizeof(char), len, file); + fclose(file); + CUtlBuffer buf(&buffer, 4 * 1000 * 1000, 9); + InitSchema(0, itemschema, buf, false, 0xDEADCA7); +} + +void CC_Misc_Schema(const CCommand& args) { + Schema_Reload(); +} + Misc::Misc() { if (TF2C) v_bMinigunJump = new CatVar(CV_SWITCH, "minigun_jump", "0", "Minigun Jump", NULL, "Allows you to jump while with minigun spun up"); v_bDebugInfo = new CatVar(CV_SWITCH, "misc_debug", "0", "Debug info", NULL, "Log stuff to console, enable this if tf2 crashes"); @@ -269,6 +306,7 @@ Misc::Misc() { CreateConCommand(CON_PREFIX "set", CC_SetValue, "Set ConVar value (if third argument is 1 the ^'s will be converted into newlines)"); //v_bDebugCrits = new CatVar(CV_SWITCH, "debug_crits", "0", "Debug Crits", NULL, "???"); v_bCleanChat = new CatVar(CV_SWITCH, "clean_chat", "1", "Remove newlines from messages", NULL, "Removes newlines from messages, at least it should do that. Might be broken."); + if (TF2) c_Schema = CreateConCommand(CON_PREFIX "schema", CC_Misc_Schema, "Load item schema"); //interfaces::eventManager->AddListener(&listener, "player_death", false); } @@ -294,8 +332,24 @@ float RemapValClampedNC( float val, float A, float B, float C, float D) } +bool ciac_s = false; + bool Misc::CreateMove(void*, float, CUserCmd* cmd) { static bool flswitch = false; + /*static uintptr_t critsig = gSignatures.GetClientSignature("89 1C 24 E8 ? ? ? ? 0F B6 83 0E 0B 00 00 89 74 24 04 C7 04 24 ? ? ? ? 89 44 24 08 E8 ? ? ? ? 8B 03 89 1C 24 FF 90 F8 06 00 00 83 F8 12 74 0A C7 83 FC 0A 00 00 00 00 00 00"); + static uintptr_t critfnp = critsig + 4; + typedef void(*C_TFWeaponBase__CalcIsAttackCritical_t)(IClientEntity*); + static C_TFWeaponBase__CalcIsAttackCritical_t ciac = (C_TFWeaponBase__CalcIsAttackCritical_t)(*(uintptr_t*)critfnp + critfnp + 4); + if (CE_GOOD(LOCAL_W)) { + static float lastcheck = interfaces::gvars->curtime; + if (interfaces::gvars->curtime - lastcheck >= 1.0f) { + RandomSeed(cmd->random_seed); + ciac(RAW_ENT(LOCAL_W)); + //logging::Info("0x%08x", *(unsigned char*)(RAW_ENT(LOCAL_W) + 0x0B0E)); + ciac_s = !!*(unsigned char*)(RAW_ENT(LOCAL_W) + 0x0B0E); + } + //if (ciac_s) cmd->buttons |= IN_ATTACK; + }*/ g_Settings.bSendPackets->SetValue(true); if (v_iFakeLag->GetInt()) { static int fakelag = 0; @@ -400,6 +454,7 @@ void Misc::PaintTraverse(void*, unsigned int, bool, bool) { GetProjectileData(g_pLocalPlayer->weapon(), speed, gravity); AddSideString(colors::white, "Speed: %f", speed); AddSideString(colors::white, "Gravity: %f", gravity); + AddSideString(colors::white, "CIAC: %i", ciac_s); //AddSideString(colors::white, "IsZoomed: %i", g_pLocalPlayer->bZoomed); //AddSideString(colors::white, "CanHeadshot: %i", CanHeadshot()); //AddSideString(colors::white, "IsThirdPerson: %i", interfaces::iinput->CAM_IsThirdPerson()); diff --git a/cathook/src/hacks/Misc.h b/cathook/src/hacks/Misc.h index c7048dd7..e1a00133 100644 --- a/cathook/src/hacks/Misc.h +++ b/cathook/src/hacks/Misc.h @@ -41,11 +41,13 @@ public: ConCommand* c_DumpConds; ConCommand* c_Reset; ConCommand* c_Disconnect; + ConCommand* c_Schema; ConCommand* c_DisconnectVAC; CatVar* v_bCleanChat; }; +void Schema_Reload(); void CC_Misc_Disconnect_VAC(); DECLARE_HACK_SINGLETON(Misc); diff --git a/cathook/src/netvars.cpp b/cathook/src/netvars.cpp index 01f590a2..582e26a3 100644 --- a/cathook/src/netvars.cpp +++ b/cathook/src/netvars.cpp @@ -19,6 +19,7 @@ void NetVars::Init() { this->hActiveWeapon = gNetvars.get_offset("DT_BaseCombatCharacter", "m_hActiveWeapon"); this->iHitboxSet = gNetvars.get_offset("DT_BaseAnimating", "m_nHitboxSet"); this->vVelocity = gNetvars.get_offset("DT_BasePlayer", "localdata", "m_vecVelocity[0]"); + this->movetype = gNetvars.get_offset("DT_BaseEntity", "movetype"); if (TF2) { this->bGlowEnabled = gNetvars.get_offset("DT_TFPlayer", "m_bGlowEnabled"); this->iMaxBuffedHealth = gNetvars.get_offset("DT_TFPlayerResource", "m_iMaxBuffedHealth"); diff --git a/cathook/src/netvars.h b/cathook/src/netvars.h index fdadb6f9..49ca39bb 100644 --- a/cathook/src/netvars.h +++ b/cathook/src/netvars.h @@ -66,6 +66,7 @@ public: offset_t iHitboxSet; offset_t vVelocity; offset_t bGlowEnabled; + offset_t movetype; offset_t iGlowIndex; offset_t iReloadMode; offset_t iMaxHealth; diff --git a/cathook/src/prediction.cpp b/cathook/src/prediction.cpp index 6352ddf9..3b8f3dc8 100644 --- a/cathook/src/prediction.cpp +++ b/cathook/src/prediction.cpp @@ -7,8 +7,22 @@ #include "prediction.h" -#include "common.h" -#include "sdk.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "conditions.h" +#include "entitycache.h" +#include "helpers.h" +#include "interfaces.h" +#include "localplayer.h" +#include "netvars.h" +#include "trace.h" // TODO there is a Vector() object created each call. @@ -22,62 +36,72 @@ Vector SimpleLatencyPrediction(CachedEntity* ent, int hb) { return result; } -Vector ProjectilePrediction(CachedEntity* ent, int hb, float speed, float gravitymod) { +float PlayerGravityMod(CachedEntity* player) { + int movetype = CE_INT(player, netvar.movetype); + if (movetype == MOVETYPE_FLY || movetype == MOVETYPE_NOCLIP) return 0.0f; + if (HasCondition(player, TFCond_Parachute)) return 0.448f; + return 1.0f; +} + +bool PerformProjectilePrediction(CachedEntity* target, int hitbox) { + Vector src, vel, hit; ; + //src = vfunc(RAW_ENT(target), 299)(RAW_ENT(target)); + + return true; +} + +Vector ProjectilePrediction(CachedEntity* ent, int hb, float speed, float gravitymod, float entgmod) { if (!ent) return Vector(); - Vector result = ent->m_vecOrigin; + Vector result = SimpleLatencyPrediction(ent, hb); if (speed == 0.0f) return Vector(); - float dtg = DistanceToGround(result); - int flags = CE_INT(ent, netvar.iFlags); - bool ground = (flags & (1 << 0)); - if (ground) dtg = 0; - GetHitbox(ent, hb, result); - Vector vel = CE_VECTOR(ent, netvar.vVelocity); + float dtg = DistanceToGround(ent); + Vector vel = ent->m_vecVelocity; // TODO ProjAim - float ott = g_pLocalPlayer->v_Eye.DistTo(result) / speed + interfaces::engineClient->GetNetChannelInfo()->GetLatency(FLOW_OUTGOING); - float tt = ott - 0.5f; - if (tt <= 0.0f) tt = 0.01f; - float besttime = tt; + float medianTime = g_pLocalPlayer->v_Eye.DistTo(result) / speed; + float range = 1.5f; + float currenttime = medianTime - range; + if (currenttime <= 0.0f) currenttime = 0.01f; + float besttime = currenttime; float mindelta = 65536.0f; Vector bestpos = result; - int maxsteps = 100; - for (int steps = 0; steps < maxsteps; steps++, tt += ((float)1 / (float)maxsteps)) { + int maxsteps = 300; + for (int steps = 0; steps < maxsteps; steps++, currenttime += ((float)(2 * range) / (float)maxsteps)) { Vector curpos = result; - curpos += vel * tt; + curpos += vel * currenttime; if (dtg > 0.0f) { - curpos.z -= tt * tt * 400; + curpos.z -= currenttime * currenttime * 400 * entgmod; if (curpos.z < result.z - dtg) curpos.z = result.z - dtg; } - float rockettime = g_pLocalPlayer->v_Eye.DistTo(curpos) / speed + interfaces::engineClient->GetNetChannelInfo()->GetLatency(FLOW_OUTGOING); - //logging::Info("RocketTime: %.2f TT: %.2f Step: %i BestTime: %.2f", rockettime, tt, steps, besttime); - if (fabs(rockettime - tt) < mindelta) { - //if (mindelta != 65536.0) logging::Info("got better delta: %.2f at step %i (time: %.2f)", fabs(rockettime - tt), steps, tt); - besttime = tt; + float rockettime = g_pLocalPlayer->v_Eye.DistTo(curpos) / speed; + if (fabs(rockettime - currenttime) < mindelta) { + besttime = currenttime; bestpos = curpos; - mindelta = fabs(rockettime - tt); + mindelta = fabs(rockettime - currenttime); } } - - /*float dtt = g_pLocalPlayer->v_Eye.DistTo(result); - float ttt = dtt / speed + interfaces::engineClient->GetNetChannelInfo()->GetLatency(FLOW_OUTGOING) + - interfaces::engineClient->GetNetChannelInfo()->GetLatency(FLOW_INCOMING); - float oz = result.z; - int flags = CE_INT(ent, netvar.iFlags); - bool ground = (flags & (1 << 0)); - if (!ground) result.z -= ttt * ttt * 400; - result += vel * ttt; - if (!ground) if (oz - result.z > dtg) { result.z = oz - dtg; } - */ bestpos.z += (400 * besttime * besttime * gravitymod); // S = at^2/2 ; t = sqrt(2S/a)*/ return bestpos; } +float DistanceToGround(CachedEntity* ent) { + if (ent->m_Type == ENTITY_PLAYER) { + if (CE_INT(ent, netvar.iFlags) & FL_ONGROUND) return 0; + } + Vector& origin = ent->m_vecOrigin; + float v1 = DistanceToGround(origin + Vector(10.0f, 10.0f, 0.0f)); + float v2 = DistanceToGround(origin + Vector(-10.0f, 10.0f, 0.0f)); + float v3 = DistanceToGround(origin + Vector(10.0f, -10.0f, 0.0f)); + float v4 = DistanceToGround(origin + Vector(-10.0f, -10.0f, 0.0f)); + return MIN(v1, MIN(v2, MIN(v3, v4))); +} + float DistanceToGround(Vector origin) { static trace_t* ground_trace = new trace_t(); Ray_t ray; Vector endpos = origin; endpos.z -= 8192; ray.Init(origin, endpos); - interfaces::trace->TraceRay(ray, 0x4200400B, trace::g_pFilterNoPlayer, ground_trace); - return ground_trace->startpos.DistTo(ground_trace->endpos); + interfaces::trace->TraceRay(ray, MASK_PLAYERSOLID, trace::g_pFilterNoPlayer, ground_trace); + return 8192.0f * ground_trace->fraction; } diff --git a/cathook/src/prediction.h b/cathook/src/prediction.h index 0fd78add..e0df3052 100644 --- a/cathook/src/prediction.h +++ b/cathook/src/prediction.h @@ -14,8 +14,14 @@ class CachedEntity; class Vector; Vector SimpleLatencyPrediction(CachedEntity* ent, int hb); -Vector ProjectilePrediction(CachedEntity* ent, int hb, float speed, float gravitymod); +bool PerformProjectilePrediction(CachedEntity* target, int hitbox); + +Vector ProjectilePrediction(CachedEntity* ent, int hb, float speed, float gravitymod, float entgmod); + +float PlayerGravityMod(CachedEntity* player); + +float DistanceToGround(CachedEntity* ent); float DistanceToGround(Vector origin); #endif /* PREDICTION_H_ */ diff --git a/cathook/src/sdk/utlbuffer.cpp b/cathook/src/sdk/utlbuffer.cpp new file mode 100644 index 00000000..fc89d773 --- /dev/null +++ b/cathook/src/sdk/utlbuffer.cpp @@ -0,0 +1,1794 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// $Header: $ +// $NoKeywords: $ +// +// Serialization buffer +//===========================================================================// + +#pragma warning (disable : 4514) + +#include "utlbuffer.h" +#include +#include +#include +#include +#include +#include "tier1/strtools.h" +#include "tier1/characterset.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// Character conversions for C strings +//----------------------------------------------------------------------------- +class CUtlCStringConversion : public CUtlCharConversion +{ +public: + CUtlCStringConversion( char nEscapeChar, const char *pDelimiter, int nCount, ConversionArray_t *pArray ); + + // Finds a conversion for the passed-in string, returns length + virtual char FindConversion( const char *pString, int *pLength ); + +private: + char m_pConversion[256]; +}; + + +//----------------------------------------------------------------------------- +// Character conversions for no-escape sequence strings +//----------------------------------------------------------------------------- +class CUtlNoEscConversion : public CUtlCharConversion +{ +public: + CUtlNoEscConversion( char nEscapeChar, const char *pDelimiter, int nCount, ConversionArray_t *pArray ) : + CUtlCharConversion( nEscapeChar, pDelimiter, nCount, pArray ) {} + + // Finds a conversion for the passed-in string, returns length + virtual char FindConversion( const char *pString, int *pLength ) { *pLength = 0; return 0; } +}; + + +//----------------------------------------------------------------------------- +// List of character conversions +//----------------------------------------------------------------------------- +BEGIN_CUSTOM_CHAR_CONVERSION( CUtlCStringConversion, s_StringCharConversion, "\"", '\\' ) + { '\n', "n" }, + { '\t', "t" }, + { '\v', "v" }, + { '\b', "b" }, + { '\r', "r" }, + { '\f', "f" }, + { '\a', "a" }, + { '\\', "\\" }, + { '\?', "\?" }, + { '\'', "\'" }, + { '\"', "\"" }, +END_CUSTOM_CHAR_CONVERSION( CUtlCStringConversion, s_StringCharConversion, "\"", '\\' ) + +CUtlCharConversion *GetCStringCharConversion() +{ + return &s_StringCharConversion; +} + +BEGIN_CUSTOM_CHAR_CONVERSION( CUtlNoEscConversion, s_NoEscConversion, "\"", 0x7F ) + { 0x7F, "" }, +END_CUSTOM_CHAR_CONVERSION( CUtlNoEscConversion, s_NoEscConversion, "\"", 0x7F ) + +CUtlCharConversion *GetNoEscCharConversion() +{ + return &s_NoEscConversion; +} + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CUtlCStringConversion::CUtlCStringConversion( char nEscapeChar, const char *pDelimiter, int nCount, ConversionArray_t *pArray ) : + CUtlCharConversion( nEscapeChar, pDelimiter, nCount, pArray ) +{ + memset( m_pConversion, 0x0, sizeof(m_pConversion) ); + for ( int i = 0; i < nCount; ++i ) + { + m_pConversion[ (unsigned char) pArray[i].m_pReplacementString[0] ] = pArray[i].m_nActualChar; + } +} + +// Finds a conversion for the passed-in string, returns length +char CUtlCStringConversion::FindConversion( const char *pString, int *pLength ) +{ + char c = m_pConversion[ (unsigned char) pString[0] ]; + *pLength = (c != '\0') ? 1 : 0; + return c; +} + + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CUtlCharConversion::CUtlCharConversion( char nEscapeChar, const char *pDelimiter, int nCount, ConversionArray_t *pArray ) +{ + m_nEscapeChar = nEscapeChar; + m_pDelimiter = pDelimiter; + m_nCount = nCount; + m_nDelimiterLength = Q_strlen( pDelimiter ); + m_nMaxConversionLength = 0; + + memset( m_pReplacements, 0, sizeof(m_pReplacements) ); + + for ( int i = 0; i < nCount; ++i ) + { + m_pList[i] = pArray[i].m_nActualChar; + ConversionInfo_t &info = m_pReplacements[ (unsigned char) m_pList[i] ]; + Assert( info.m_pReplacementString == 0 ); + info.m_pReplacementString = pArray[i].m_pReplacementString; + info.m_nLength = Q_strlen( info.m_pReplacementString ); + if ( info.m_nLength > m_nMaxConversionLength ) + { + m_nMaxConversionLength = info.m_nLength; + } + } +} + + +//----------------------------------------------------------------------------- +// Escape character + delimiter +//----------------------------------------------------------------------------- +char CUtlCharConversion::GetEscapeChar() const +{ + return m_nEscapeChar; +} + +const char *CUtlCharConversion::GetDelimiter() const +{ + return m_pDelimiter; +} + +int CUtlCharConversion::GetDelimiterLength() const +{ + return m_nDelimiterLength; +} + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +const char *CUtlCharConversion::GetConversionString( char c ) const +{ + return m_pReplacements[ (unsigned char) c ].m_pReplacementString; +} + +int CUtlCharConversion::GetConversionLength( char c ) const +{ + return m_pReplacements[ (unsigned char) c ].m_nLength; +} + +int CUtlCharConversion::MaxConversionLength() const +{ + return m_nMaxConversionLength; +} + + +//----------------------------------------------------------------------------- +// Finds a conversion for the passed-in string, returns length +//----------------------------------------------------------------------------- +char CUtlCharConversion::FindConversion( const char *pString, int *pLength ) +{ + for ( int i = 0; i < m_nCount; ++i ) + { + if ( !Q_strcmp( pString, m_pReplacements[ (unsigned char) m_pList[i] ].m_pReplacementString ) ) + { + *pLength = m_pReplacements[ (unsigned char) m_pList[i] ].m_nLength; + return m_pList[i]; + } + } + + *pLength = 0; + return '\0'; +} + + +//----------------------------------------------------------------------------- +// constructors +//----------------------------------------------------------------------------- +CUtlBuffer::CUtlBuffer( int growSize, int initSize, int nFlags ) : + m_Error(0) +{ + MEM_ALLOC_CREDIT(); + m_Memory.Init( growSize, initSize ); + m_Get = 0; + m_Put = 0; + m_nTab = 0; + m_nOffset = 0; + m_Flags = nFlags; + if ( (initSize != 0) && !IsReadOnly() ) + { + m_nMaxPut = -1; + AddNullTermination(); + } + else + { + m_nMaxPut = 0; + } + SetOverflowFuncs( &CUtlBuffer::GetOverflow, &CUtlBuffer::PutOverflow ); +} + +CUtlBuffer::CUtlBuffer( const void *pBuffer, int nSize, int nFlags ) : + m_Memory( (unsigned char*)pBuffer, nSize ), m_Error(0) +{ + Assert( nSize != 0 ); + + m_Get = 0; + m_Put = 0; + m_nTab = 0; + m_nOffset = 0; + m_Flags = nFlags; + if ( IsReadOnly() ) + { + m_nMaxPut = nSize; + } + else + { + m_nMaxPut = -1; + AddNullTermination(); + } + SetOverflowFuncs( &CUtlBuffer::GetOverflow, &CUtlBuffer::PutOverflow ); +} + + +//----------------------------------------------------------------------------- +// Modifies the buffer to be binary or text; Blows away the buffer and the CONTAINS_CRLF value. +//----------------------------------------------------------------------------- +void CUtlBuffer::SetBufferType( bool bIsText, bool bContainsCRLF ) +{ +#ifdef _DEBUG + // If the buffer is empty, there is no opportunity for this stuff to fail + if ( TellMaxPut() != 0 ) + { + if ( IsText() ) + { + if ( bIsText ) + { + Assert( ContainsCRLF() == bContainsCRLF ); + } + else + { + Assert( ContainsCRLF() ); + } + } + else + { + if ( bIsText ) + { + Assert( bContainsCRLF ); + } + } + } +#endif + + if ( bIsText ) + { + m_Flags |= TEXT_BUFFER; + } + else + { + m_Flags &= ~TEXT_BUFFER; + } + if ( bContainsCRLF ) + { + m_Flags |= CONTAINS_CRLF; + } + else + { + m_Flags &= ~CONTAINS_CRLF; + } +} + + +//----------------------------------------------------------------------------- +// Attaches the buffer to external memory.... +//----------------------------------------------------------------------------- +void CUtlBuffer::SetExternalBuffer( void* pMemory, int nSize, int nInitialPut, int nFlags ) +{ + m_Memory.SetExternalBuffer( (unsigned char*)pMemory, nSize ); + + // Reset all indices; we just changed memory + m_Get = 0; + m_Put = nInitialPut; + m_nTab = 0; + m_Error = 0; + m_nOffset = 0; + m_Flags = nFlags; + m_nMaxPut = -1; + AddNullTermination(); +} + +//----------------------------------------------------------------------------- +// Assumes an external buffer but manages its deletion +//----------------------------------------------------------------------------- +void CUtlBuffer::AssumeMemory( void *pMemory, int nSize, int nInitialPut, int nFlags ) +{ + m_Memory.AssumeMemory( (unsigned char*) pMemory, nSize ); + + // Reset all indices; we just changed memory + m_Get = 0; + m_Put = nInitialPut; + m_nTab = 0; + m_Error = 0; + m_nOffset = 0; + m_Flags = nFlags; + m_nMaxPut = -1; + AddNullTermination(); +} + +//----------------------------------------------------------------------------- +// Makes sure we've got at least this much memory +//----------------------------------------------------------------------------- +void CUtlBuffer::EnsureCapacity( int num ) +{ + MEM_ALLOC_CREDIT(); + // Add one extra for the null termination + num += 1; + if ( m_Memory.IsExternallyAllocated() ) + { + if ( IsGrowable() && ( m_Memory.NumAllocated() < num ) ) + { + m_Memory.ConvertToGrowableMemory( 0 ); + } + else + { + num -= 1; + } + } + + m_Memory.EnsureCapacity( num ); +} + + +//----------------------------------------------------------------------------- +// Base get method from which all others derive +//----------------------------------------------------------------------------- +void CUtlBuffer::Get( void* pMem, int size ) +{ + if ( size > 0 && CheckGet( size ) ) + { + int Index = m_Get - m_nOffset; + Assert( m_Memory.IsIdxValid( Index ) && m_Memory.IsIdxValid( Index + size - 1 ) ); + + memcpy( pMem, &m_Memory[ Index ], size ); + m_Get += size; + } +} + + +//----------------------------------------------------------------------------- +// This will get at least 1 byte and up to nSize bytes. +// It will return the number of bytes actually read. +//----------------------------------------------------------------------------- +int CUtlBuffer::GetUpTo( void *pMem, int nSize ) +{ + if ( CheckArbitraryPeekGet( 0, nSize ) ) + { + int Index = m_Get - m_nOffset; + Assert( m_Memory.IsIdxValid( Index ) && m_Memory.IsIdxValid( Index + nSize - 1 ) ); + + memcpy( pMem, &m_Memory[ Index ], nSize ); + m_Get += nSize; + return nSize; + } + return 0; +} + + +//----------------------------------------------------------------------------- +// Eats whitespace +//----------------------------------------------------------------------------- +void CUtlBuffer::EatWhiteSpace() +{ + if ( IsText() && IsValid() ) + { + while ( CheckGet( sizeof(char) ) ) + { + if ( !isspace( *(const unsigned char*)PeekGet() ) ) + break; + m_Get += sizeof(char); + } + } +} + + +//----------------------------------------------------------------------------- +// Eats C++ style comments +//----------------------------------------------------------------------------- +bool CUtlBuffer::EatCPPComment() +{ + if ( IsText() && IsValid() ) + { + // If we don't have a a c++ style comment next, we're done + const char *pPeek = (const char *)PeekGet( 2 * sizeof(char), 0 ); + if ( !pPeek || ( pPeek[0] != '/' ) || ( pPeek[1] != '/' ) ) + return false; + + // Deal with c++ style comments + m_Get += 2; + + // read complete line + for ( char c = GetChar(); IsValid(); c = GetChar() ) + { + if ( c == '\n' ) + break; + } + return true; + } + return false; +} + + +//----------------------------------------------------------------------------- +// Peeks how much whitespace to eat +//----------------------------------------------------------------------------- +int CUtlBuffer::PeekWhiteSpace( int nOffset ) +{ + if ( !IsText() || !IsValid() ) + return 0; + + while ( CheckPeekGet( nOffset, sizeof(char) ) ) + { + if ( !isspace( *(unsigned char*)PeekGet( nOffset ) ) ) + break; + nOffset += sizeof(char); + } + + return nOffset; +} + + +//----------------------------------------------------------------------------- +// Peek size of sting to come, check memory bound +//----------------------------------------------------------------------------- +int CUtlBuffer::PeekStringLength() +{ + if ( !IsValid() ) + return 0; + + // Eat preceeding whitespace + int nOffset = 0; + if ( IsText() ) + { + nOffset = PeekWhiteSpace( nOffset ); + } + + int nStartingOffset = nOffset; + + do + { + int nPeekAmount = 128; + + // NOTE: Add 1 for the terminating zero! + if ( !CheckArbitraryPeekGet( nOffset, nPeekAmount ) ) + { + if ( nOffset == nStartingOffset ) + return 0; + return nOffset - nStartingOffset + 1; + } + + const char *pTest = (const char *)PeekGet( nOffset ); + + if ( !IsText() ) + { + for ( int i = 0; i < nPeekAmount; ++i ) + { + // The +1 here is so we eat the terminating 0 + if ( pTest[i] == 0 ) + return (i + nOffset - nStartingOffset + 1); + } + } + else + { + for ( int i = 0; i < nPeekAmount; ++i ) + { + // The +1 here is so we eat the terminating 0 + if ( isspace((unsigned char)pTest[i]) || (pTest[i] == 0) ) + return (i + nOffset - nStartingOffset + 1); + } + } + + nOffset += nPeekAmount; + + } while ( true ); +} + + +//----------------------------------------------------------------------------- +// Peek size of line to come, check memory bound +//----------------------------------------------------------------------------- +int CUtlBuffer::PeekLineLength() +{ + if ( !IsValid() ) + return 0; + + int nOffset = 0; + int nStartingOffset = nOffset; + + do + { + int nPeekAmount = 128; + + // NOTE: Add 1 for the terminating zero! + if ( !CheckArbitraryPeekGet( nOffset, nPeekAmount ) ) + { + if ( nOffset == nStartingOffset ) + return 0; + return nOffset - nStartingOffset + 1; + } + + const char *pTest = (const char *)PeekGet( nOffset ); + + for ( int i = 0; i < nPeekAmount; ++i ) + { + // The +2 here is so we eat the terminating '\n' and 0 + if ( pTest[i] == '\n' || pTest[i] == '\r' ) + return (i + nOffset - nStartingOffset + 2); + // The +1 here is so we eat the terminating 0 + if ( pTest[i] == 0 ) + return (i + nOffset - nStartingOffset + 1); + } + + nOffset += nPeekAmount; + + } while ( true ); +} + + +//----------------------------------------------------------------------------- +// Does the next bytes of the buffer match a pattern? +//----------------------------------------------------------------------------- +bool CUtlBuffer::PeekStringMatch( int nOffset, const char *pString, int nLen ) +{ + if ( !CheckPeekGet( nOffset, nLen ) ) + return false; + return !Q_strncmp( (const char*)PeekGet(nOffset), pString, nLen ); +} + + +//----------------------------------------------------------------------------- +// This version of PeekStringLength converts \" to \\ and " to \, etc. +// It also reads a " at the beginning and end of the string +//----------------------------------------------------------------------------- +int CUtlBuffer::PeekDelimitedStringLength( CUtlCharConversion *pConv, bool bActualSize ) +{ + if ( !IsText() || !pConv ) + return PeekStringLength(); + + // Eat preceeding whitespace + int nOffset = 0; + if ( IsText() ) + { + nOffset = PeekWhiteSpace( nOffset ); + } + + if ( !PeekStringMatch( nOffset, pConv->GetDelimiter(), pConv->GetDelimiterLength() ) ) + return 0; + + // Try to read ending ", but don't accept \" + int nActualStart = nOffset; + nOffset += pConv->GetDelimiterLength(); + int nLen = 1; // Starts at 1 for the '\0' termination + + do + { + if ( PeekStringMatch( nOffset, pConv->GetDelimiter(), pConv->GetDelimiterLength() ) ) + break; + + if ( !CheckPeekGet( nOffset, 1 ) ) + break; + + char c = *(const char*)PeekGet( nOffset ); + ++nLen; + ++nOffset; + if ( c == pConv->GetEscapeChar() ) + { + int nLength = pConv->MaxConversionLength(); + if ( !CheckArbitraryPeekGet( nOffset, nLength ) ) + break; + + pConv->FindConversion( (const char*)PeekGet(nOffset), &nLength ); + nOffset += nLength; + } + } while (true); + + return bActualSize ? nLen : nOffset - nActualStart + pConv->GetDelimiterLength() + 1; +} + + +//----------------------------------------------------------------------------- +// Reads a null-terminated string +//----------------------------------------------------------------------------- +void CUtlBuffer::GetStringInternal( char *pString, size_t maxLenInChars ) +{ + if ( !IsValid() ) + { + *pString = 0; + return; + } + + Assert( maxLenInChars != 0 ); + + if ( maxLenInChars == 0 ) + { + return; + } + + // Remember, this *includes* the null character + // It will be 0, however, if the buffer is empty. + int nLen = PeekStringLength(); + + if ( IsText() ) + { + EatWhiteSpace(); + } + + if ( nLen <= 0 ) + { + *pString = 0; + m_Error |= GET_OVERFLOW; + return; + } + + const size_t nCharsToRead = min( (size_t)nLen, maxLenInChars ) - 1; + + Get( pString, nCharsToRead ); + pString[nCharsToRead] = 0; + + if ( (size_t)nLen > (nCharsToRead + 1) ) + { + SeekGet( SEEK_CURRENT, nLen - (nCharsToRead + 1) ); + } + + // Read the terminating NULL in binary formats + if ( !IsText() ) + { + VerifyEquals( GetChar(), 0 ); + } +} + + +//----------------------------------------------------------------------------- +// Reads up to and including the first \n +//----------------------------------------------------------------------------- +void CUtlBuffer::GetLine( char* pLine, int nMaxChars ) +{ + Assert( IsText() && !ContainsCRLF() ); + + if ( !IsValid() ) + { + *pLine = 0; + return; + } + + if ( nMaxChars == 0 ) + { + nMaxChars = INT_MAX; + } + + // Remember, this *includes* the null character + // It will be 0, however, if the buffer is empty. + int nLen = PeekLineLength(); + if ( nLen == 0 ) + { + *pLine = 0; + m_Error |= GET_OVERFLOW; + return; + } + + // Strip off the terminating NULL + if ( nLen <= nMaxChars ) + { + Get( pLine, nLen - 1 ); + pLine[ nLen - 1 ] = 0; + } + else + { + Get( pLine, nMaxChars - 1 ); + pLine[ nMaxChars - 1 ] = 0; + SeekGet( SEEK_CURRENT, nLen - 1 - nMaxChars ); + } +} + + +//----------------------------------------------------------------------------- +// This version of GetString converts \ to \\ and " to \", etc. +// It also places " at the beginning and end of the string +//----------------------------------------------------------------------------- +char CUtlBuffer::GetDelimitedCharInternal( CUtlCharConversion *pConv ) +{ + char c = GetChar(); + if ( c == pConv->GetEscapeChar() ) + { + int nLength = pConv->MaxConversionLength(); + if ( !CheckArbitraryPeekGet( 0, nLength ) ) + return '\0'; + + c = pConv->FindConversion( (const char *)PeekGet(), &nLength ); + SeekGet( SEEK_CURRENT, nLength ); + } + + return c; +} + +char CUtlBuffer::GetDelimitedChar( CUtlCharConversion *pConv ) +{ + if ( !IsText() || !pConv ) + return GetChar( ); + return GetDelimitedCharInternal( pConv ); +} + +void CUtlBuffer::GetDelimitedString( CUtlCharConversion *pConv, char *pString, int nMaxChars ) +{ + if ( !IsText() || !pConv ) + { + GetStringInternal( pString, nMaxChars ); + return; + } + + if (!IsValid()) + { + *pString = 0; + return; + } + + if ( nMaxChars == 0 ) + { + nMaxChars = INT_MAX; + } + + EatWhiteSpace(); + if ( !PeekStringMatch( 0, pConv->GetDelimiter(), pConv->GetDelimiterLength() ) ) + return; + + // Pull off the starting delimiter + SeekGet( SEEK_CURRENT, pConv->GetDelimiterLength() ); + + int nRead = 0; + while ( IsValid() ) + { + if ( PeekStringMatch( 0, pConv->GetDelimiter(), pConv->GetDelimiterLength() ) ) + { + SeekGet( SEEK_CURRENT, pConv->GetDelimiterLength() ); + break; + } + + char c = GetDelimitedCharInternal( pConv ); + + if ( nRead < nMaxChars ) + { + pString[nRead] = c; + ++nRead; + } + } + + if ( nRead >= nMaxChars ) + { + nRead = nMaxChars - 1; + } + pString[nRead] = '\0'; +} + + +//----------------------------------------------------------------------------- +// Checks if a get is ok +//----------------------------------------------------------------------------- +bool CUtlBuffer::CheckGet( int nSize ) +{ + if ( m_Error & GET_OVERFLOW ) + return false; + + if ( TellMaxPut() < m_Get + nSize ) + { + m_Error |= GET_OVERFLOW; + return false; + } + + if ( ( m_Get < m_nOffset ) || ( m_Memory.NumAllocated() < m_Get - m_nOffset + nSize ) ) + { + if ( !OnGetOverflow( nSize ) ) + { + m_Error |= GET_OVERFLOW; + return false; + } + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Checks if a peek get is ok +//----------------------------------------------------------------------------- +bool CUtlBuffer::CheckPeekGet( int nOffset, int nSize ) +{ + if ( m_Error & GET_OVERFLOW ) + return false; + + // Checking for peek can't set the overflow flag + bool bOk = CheckGet( nOffset + nSize ); + m_Error &= ~GET_OVERFLOW; + return bOk; +} + + +//----------------------------------------------------------------------------- +// Call this to peek arbitrarily long into memory. It doesn't fail unless +// it can't read *anything* new +//----------------------------------------------------------------------------- +bool CUtlBuffer::CheckArbitraryPeekGet( int nOffset, int &nIncrement ) +{ + if ( TellGet() + nOffset >= TellMaxPut() ) + { + nIncrement = 0; + return false; + } + + if ( TellGet() + nOffset + nIncrement > TellMaxPut() ) + { + nIncrement = TellMaxPut() - TellGet() - nOffset; + } + + // NOTE: CheckPeekGet could modify TellMaxPut for streaming files + // We have to call TellMaxPut again here + CheckPeekGet( nOffset, nIncrement ); + int nMaxGet = TellMaxPut() - TellGet(); + if ( nMaxGet < nIncrement ) + { + nIncrement = nMaxGet; + } + return (nIncrement != 0); +} + + +//----------------------------------------------------------------------------- +// Peek part of the butt +//----------------------------------------------------------------------------- +const void* CUtlBuffer::PeekGet( int nMaxSize, int nOffset ) +{ + if ( !CheckPeekGet( nOffset, nMaxSize ) ) + return NULL; + + int Index = m_Get + nOffset - m_nOffset; + Assert( m_Memory.IsIdxValid( Index ) && m_Memory.IsIdxValid( Index + nMaxSize - 1 ) ); + + return &m_Memory[ Index ]; +} + + +//----------------------------------------------------------------------------- +// Change where I'm reading +//----------------------------------------------------------------------------- +void CUtlBuffer::SeekGet( SeekType_t type, int offset ) +{ + switch( type ) + { + case SEEK_HEAD: + m_Get = offset; + break; + + case SEEK_CURRENT: + m_Get += offset; + break; + + case SEEK_TAIL: + m_Get = m_nMaxPut - offset; + break; + } + + if ( m_Get > m_nMaxPut ) + { + m_Error |= GET_OVERFLOW; + } + else + { + m_Error &= ~GET_OVERFLOW; + if ( m_Get < m_nOffset || m_Get >= m_nOffset + Size() ) + { + OnGetOverflow( -1 ); + } + } +} + + +//----------------------------------------------------------------------------- +// Parse... +//----------------------------------------------------------------------------- + +#pragma warning ( disable : 4706 ) + +int CUtlBuffer::VaScanf( const char* pFmt, va_list list ) +{ + Assert( pFmt ); + if ( m_Error || !IsText() ) + return 0; + + int numScanned = 0; + int nLength; + char c; + char* pEnd; + while ( (c = *pFmt++) ) + { + // Stop if we hit the end of the buffer + if ( m_Get >= TellMaxPut() ) + { + m_Error |= GET_OVERFLOW; + break; + } + + switch (c) + { + case ' ': + // eat all whitespace + EatWhiteSpace(); + break; + + case '%': + { + // Conversion character... try to convert baby! + char type = *pFmt++; + if (type == 0) + return numScanned; + + switch(type) + { + case 'c': + { + char* ch = va_arg( list, char * ); + if ( CheckPeekGet( 0, sizeof(char) ) ) + { + *ch = *(const char*)PeekGet(); + ++m_Get; + } + else + { + *ch = 0; + return numScanned; + } + } + break; + + case 'i': + case 'd': + { + int* i = va_arg( list, int * ); + + // NOTE: This is not bullet-proof; it assumes numbers are < 128 characters + nLength = 128; + if ( !CheckArbitraryPeekGet( 0, nLength ) ) + { + *i = 0; + return numScanned; + } + + *i = strtol( (char*)PeekGet(), &pEnd, 10 ); + int nBytesRead = (int)( pEnd - (char*)PeekGet() ); + if ( nBytesRead == 0 ) + return numScanned; + m_Get += nBytesRead; + } + break; + + case 'x': + { + int* i = va_arg( list, int * ); + + // NOTE: This is not bullet-proof; it assumes numbers are < 128 characters + nLength = 128; + if ( !CheckArbitraryPeekGet( 0, nLength ) ) + { + *i = 0; + return numScanned; + } + + *i = strtol( (char*)PeekGet(), &pEnd, 16 ); + int nBytesRead = (int)( pEnd - (char*)PeekGet() ); + if ( nBytesRead == 0 ) + return numScanned; + m_Get += nBytesRead; + } + break; + + case 'u': + { + unsigned int* u = va_arg( list, unsigned int *); + + // NOTE: This is not bullet-proof; it assumes numbers are < 128 characters + nLength = 128; + if ( !CheckArbitraryPeekGet( 0, nLength ) ) + { + *u = 0; + return numScanned; + } + + *u = strtoul( (char*)PeekGet(), &pEnd, 10 ); + int nBytesRead = (int)( pEnd - (char*)PeekGet() ); + if ( nBytesRead == 0 ) + return numScanned; + m_Get += nBytesRead; + } + break; + + case 'f': + { + float* f = va_arg( list, float *); + + // NOTE: This is not bullet-proof; it assumes numbers are < 128 characters + nLength = 128; + if ( !CheckArbitraryPeekGet( 0, nLength ) ) + { + *f = 0.0f; + return numScanned; + } + + *f = (float)strtod( (char*)PeekGet(), &pEnd ); + int nBytesRead = (int)( pEnd - (char*)PeekGet() ); + if ( nBytesRead == 0 ) + return numScanned; + m_Get += nBytesRead; + } + break; + + case 's': + { + char* s = va_arg( list, char * ); + GetStringInternal( s, 256 ); + } + break; + + default: + { + // unimplemented scanf type + Assert(0); + return numScanned; + } + break; + } + + ++numScanned; + } + break; + + default: + { + // Here we have to match the format string character + // against what's in the buffer or we're done. + if ( !CheckPeekGet( 0, sizeof(char) ) ) + return numScanned; + + if ( c != *(const char*)PeekGet() ) + return numScanned; + + ++m_Get; + } + } + } + return numScanned; +} + +#pragma warning ( default : 4706 ) + +int CUtlBuffer::Scanf( const char* pFmt, ... ) +{ + va_list args; + + va_start( args, pFmt ); + int count = VaScanf( pFmt, args ); + va_end( args ); + + return count; +} + + +//----------------------------------------------------------------------------- +// Advance the get index until after the particular string is found +// Do not eat whitespace before starting. Return false if it failed +//----------------------------------------------------------------------------- +bool CUtlBuffer::GetToken( const char *pToken ) +{ + Assert( pToken ); + + // Look for the token + int nLen = Q_strlen( pToken ); + + int nSizeToCheck = Size() - TellGet() - m_nOffset; + + int nGet = TellGet(); + do + { + int nMaxSize = TellMaxPut() - TellGet(); + if ( nMaxSize < nSizeToCheck ) + { + nSizeToCheck = nMaxSize; + } + if ( nLen > nSizeToCheck ) + break; + + if ( !CheckPeekGet( 0, nSizeToCheck ) ) + break; + + const char *pBufStart = (const char*)PeekGet(); + const char *pFoundEnd = Q_strnistr( pBufStart, pToken, nSizeToCheck ); + if ( pFoundEnd ) + { + size_t nOffset = (size_t)pFoundEnd - (size_t)pBufStart; + SeekGet( CUtlBuffer::SEEK_CURRENT, nOffset + nLen ); + return true; + } + + SeekGet( CUtlBuffer::SEEK_CURRENT, nSizeToCheck - nLen - 1 ); + nSizeToCheck = Size() - (nLen-1); + + } while ( true ); + + SeekGet( CUtlBuffer::SEEK_HEAD, nGet ); + return false; +} + + +//----------------------------------------------------------------------------- +// (For text buffers only) +// Parse a token from the buffer: +// Grab all text that lies between a starting delimiter + ending delimiter +// (skipping whitespace that leads + trails both delimiters). +// Note the delimiter checks are case-insensitive. +// If successful, the get index is advanced and the function returns true, +// otherwise the index is not advanced and the function returns false. +//----------------------------------------------------------------------------- +bool CUtlBuffer::ParseToken( const char *pStartingDelim, const char *pEndingDelim, char* pString, int nMaxLen ) +{ + int nCharsToCopy = 0; + int nCurrentGet = 0; + + size_t nEndingDelimLen; + + // Starting delimiter is optional + char emptyBuf = '\0'; + if ( !pStartingDelim ) + { + pStartingDelim = &emptyBuf; + } + + // Ending delimiter is not + Assert( pEndingDelim && pEndingDelim[0] ); + nEndingDelimLen = Q_strlen( pEndingDelim ); + + int nStartGet = TellGet(); + char nCurrChar; + int nTokenStart = -1; + EatWhiteSpace( ); + while ( *pStartingDelim ) + { + nCurrChar = *pStartingDelim++; + if ( !isspace((unsigned char)nCurrChar) ) + { + if ( tolower( GetChar() ) != tolower( nCurrChar ) ) + goto parseFailed; + } + else + { + EatWhiteSpace(); + } + } + + EatWhiteSpace(); + nTokenStart = TellGet(); + if ( !GetToken( pEndingDelim ) ) + goto parseFailed; + + nCurrentGet = TellGet(); + nCharsToCopy = (nCurrentGet - nEndingDelimLen) - nTokenStart; + if ( nCharsToCopy >= nMaxLen ) + { + nCharsToCopy = nMaxLen - 1; + } + + if ( nCharsToCopy > 0 ) + { + SeekGet( CUtlBuffer::SEEK_HEAD, nTokenStart ); + Get( pString, nCharsToCopy ); + if ( !IsValid() ) + goto parseFailed; + + // Eat trailing whitespace + for ( ; nCharsToCopy > 0; --nCharsToCopy ) + { + if ( !isspace( (unsigned char)pString[ nCharsToCopy-1 ] ) ) + break; + } + } + pString[ nCharsToCopy ] = '\0'; + + // Advance the Get index + SeekGet( CUtlBuffer::SEEK_HEAD, nCurrentGet ); + return true; + +parseFailed: + // Revert the get index + SeekGet( SEEK_HEAD, nStartGet ); + pString[0] = '\0'; + return false; +} + + +//----------------------------------------------------------------------------- +// Parses the next token, given a set of character breaks to stop at +//----------------------------------------------------------------------------- +int CUtlBuffer::ParseToken( characterset_t *pBreaks, char *pTokenBuf, int nMaxLen, bool bParseComments ) +{ + Assert( nMaxLen > 0 ); + pTokenBuf[0] = 0; + + // skip whitespace + comments + while ( true ) + { + if ( !IsValid() ) + return -1; + EatWhiteSpace(); + if ( bParseComments ) + { + if ( !EatCPPComment() ) + break; + } + else + { + break; + } + } + + char c = GetChar(); + + // End of buffer + if ( c == 0 ) + return -1; + + // handle quoted strings specially + if ( c == '\"' ) + { + int nLen = 0; + while( IsValid() ) + { + c = GetChar(); + if ( c == '\"' || !c ) + { + pTokenBuf[nLen] = 0; + return nLen; + } + pTokenBuf[nLen] = c; + if ( ++nLen == nMaxLen ) + { + pTokenBuf[nLen-1] = 0; + return nMaxLen; + } + } + + // In this case, we hit the end of the buffer before hitting the end qoute + pTokenBuf[nLen] = 0; + return nLen; + } + + // parse single characters + if ( IN_CHARACTERSET( *pBreaks, c ) ) + { + pTokenBuf[0] = c; + pTokenBuf[1] = 0; + return 1; + } + + // parse a regular word + int nLen = 0; + while ( true ) + { + pTokenBuf[nLen] = c; + if ( ++nLen == nMaxLen ) + { + pTokenBuf[nLen-1] = 0; + return nMaxLen; + } + c = GetChar(); + if ( !IsValid() ) + break; + + if ( IN_CHARACTERSET( *pBreaks, c ) || c == '\"' || c <= ' ' ) + { + SeekGet( SEEK_CURRENT, -1 ); + break; + } + } + + pTokenBuf[nLen] = 0; + return nLen; +} + + + +//----------------------------------------------------------------------------- +// Serialization +//----------------------------------------------------------------------------- +void CUtlBuffer::Put( const void *pMem, int size ) +{ + if ( size && CheckPut( size ) ) + { + int Index = m_Put - m_nOffset; + Assert( m_Memory.IsIdxValid( Index ) && m_Memory.IsIdxValid( Index + size - 1 ) ); + if( Index >= 0 ) + { + memcpy( &m_Memory[ Index ], pMem, size ); + m_Put += size; + + AddNullTermination(); + } + } +} + + +//----------------------------------------------------------------------------- +// Writes a null-terminated string +//----------------------------------------------------------------------------- +void CUtlBuffer::PutString( const char* pString ) +{ + if (!IsText()) + { + if ( pString ) + { + // Not text? append a null at the end. + size_t nLen = Q_strlen( pString ) + 1; + Put( pString, nLen * sizeof(char) ); + return; + } + else + { + PutTypeBin( 0 ); + } + } + else if (pString) + { + int nTabCount = ( m_Flags & AUTO_TABS_DISABLED ) ? 0 : m_nTab; + if ( nTabCount > 0 ) + { + if ( WasLastCharacterCR() ) + { + PutTabs(); + } + + const char* pEndl = strchr( pString, '\n' ); + while ( pEndl ) + { + size_t nSize = (size_t)pEndl - (size_t)pString + sizeof(char); + Put( pString, nSize ); + pString = pEndl + 1; + if ( *pString ) + { + PutTabs(); + pEndl = strchr( pString, '\n' ); + } + else + { + pEndl = NULL; + } + } + } + size_t nLen = Q_strlen( pString ); + if ( nLen ) + { + Put( pString, nLen * sizeof(char) ); + } + } +} + + +//----------------------------------------------------------------------------- +// This version of PutString converts \ to \\ and " to \", etc. +// It also places " at the beginning and end of the string +//----------------------------------------------------------------------------- +inline void CUtlBuffer::PutDelimitedCharInternal( CUtlCharConversion *pConv, char c ) +{ + int l = pConv->GetConversionLength( c ); + if ( l == 0 ) + { + PutChar( c ); + } + else + { + PutChar( pConv->GetEscapeChar() ); + Put( pConv->GetConversionString( c ), l ); + } +} + +void CUtlBuffer::PutDelimitedChar( CUtlCharConversion *pConv, char c ) +{ + if ( !IsText() || !pConv ) + { + PutChar( c ); + return; + } + + PutDelimitedCharInternal( pConv, c ); +} + +void CUtlBuffer::PutDelimitedString( CUtlCharConversion *pConv, const char *pString ) +{ + if ( !IsText() || !pConv ) + { + PutString( pString ); + return; + } + + if ( WasLastCharacterCR() ) + { + PutTabs(); + } + Put( pConv->GetDelimiter(), pConv->GetDelimiterLength() ); + + int nLen = pString ? Q_strlen( pString ) : 0; + for ( int i = 0; i < nLen; ++i ) + { + PutDelimitedCharInternal( pConv, pString[i] ); + } + + if ( WasLastCharacterCR() ) + { + PutTabs(); + } + Put( pConv->GetDelimiter(), pConv->GetDelimiterLength() ); +} + + +void CUtlBuffer::VaPrintf( const char* pFmt, va_list list ) +{ + char temp[2048]; +#ifdef DBGFLAG_ASSERT + int nLen = +#endif + Q_vsnprintf( temp, sizeof( temp ), pFmt, list ); + Assert( nLen < 2048 ); + PutString( temp ); +} + +void CUtlBuffer::Printf( const char* pFmt, ... ) +{ + va_list args; + + va_start( args, pFmt ); + VaPrintf( pFmt, args ); + va_end( args ); +} + + +//----------------------------------------------------------------------------- +// Calls the overflow functions +//----------------------------------------------------------------------------- +void CUtlBuffer::SetOverflowFuncs( UtlBufferOverflowFunc_t getFunc, UtlBufferOverflowFunc_t putFunc ) +{ + m_GetOverflowFunc = getFunc; + m_PutOverflowFunc = putFunc; +} + + +//----------------------------------------------------------------------------- +// Calls the overflow functions +//----------------------------------------------------------------------------- +bool CUtlBuffer::OnPutOverflow( int nSize ) +{ + return (this->*m_PutOverflowFunc)( nSize ); +} + +bool CUtlBuffer::OnGetOverflow( int nSize ) +{ + return (this->*m_GetOverflowFunc)( nSize ); +} + + +//----------------------------------------------------------------------------- +// Checks if a put is ok +//----------------------------------------------------------------------------- +bool CUtlBuffer::PutOverflow( int nSize ) +{ + MEM_ALLOC_CREDIT(); + + if ( m_Memory.IsExternallyAllocated() ) + { + if ( !IsGrowable() ) + return false; + + m_Memory.ConvertToGrowableMemory( 0 ); + } + + while( Size() < m_Put - m_nOffset + nSize ) + { + m_Memory.Grow(); + } + + return true; +} + +bool CUtlBuffer::GetOverflow( int nSize ) +{ + return false; +} + + +//----------------------------------------------------------------------------- +// Checks if a put is ok +//----------------------------------------------------------------------------- +bool CUtlBuffer::CheckPut( int nSize ) +{ + if ( ( m_Error & PUT_OVERFLOW ) || IsReadOnly() ) + return false; + + if ( ( m_Put < m_nOffset ) || ( m_Memory.NumAllocated() < m_Put - m_nOffset + nSize ) ) + { + if ( !OnPutOverflow( nSize ) ) + { + m_Error |= PUT_OVERFLOW; + return false; + } + } + return true; +} + +void CUtlBuffer::SeekPut( SeekType_t type, int offset ) +{ + int nNextPut = m_Put; + switch( type ) + { + case SEEK_HEAD: + nNextPut = offset; + break; + + case SEEK_CURRENT: + nNextPut += offset; + break; + + case SEEK_TAIL: + nNextPut = m_nMaxPut - offset; + break; + } + + // Force a write of the data + // FIXME: We could make this more optimal potentially by writing out + // the entire buffer if you seek outside the current range + + // NOTE: This call will write and will also seek the file to nNextPut. + OnPutOverflow( -nNextPut-1 ); + m_Put = nNextPut; + + AddNullTermination(); +} + + +void CUtlBuffer::ActivateByteSwapping( bool bActivate ) +{ + m_Byteswap.ActivateByteSwapping( bActivate ); +} + +void CUtlBuffer::SetBigEndian( bool bigEndian ) +{ + m_Byteswap.SetTargetBigEndian( bigEndian ); +} + +bool CUtlBuffer::IsBigEndian( void ) +{ + return m_Byteswap.IsTargetBigEndian(); +} + + +//----------------------------------------------------------------------------- +// null terminate the buffer +//----------------------------------------------------------------------------- +void CUtlBuffer::AddNullTermination( void ) +{ + if ( m_Put > m_nMaxPut ) + { + if ( !IsReadOnly() && ((m_Error & PUT_OVERFLOW) == 0) ) + { + // Add null termination value + if ( CheckPut( 1 ) ) + { + int Index = m_Put - m_nOffset; + Assert( m_Memory.IsIdxValid( Index ) ); + if( Index >= 0 ) + { + m_Memory[ Index ] = 0; + } + } + else + { + // Restore the overflow state, it was valid before... + m_Error &= ~PUT_OVERFLOW; + } + } + m_nMaxPut = m_Put; + } +} + + +//----------------------------------------------------------------------------- +// Converts a buffer from a CRLF buffer to a CR buffer (and back) +// Returns false if no conversion was necessary (and outBuf is left untouched) +// If the conversion occurs, outBuf will be cleared. +//----------------------------------------------------------------------------- +bool CUtlBuffer::ConvertCRLF( CUtlBuffer &outBuf ) +{ + if ( !IsText() || !outBuf.IsText() ) + return false; + + if ( ContainsCRLF() == outBuf.ContainsCRLF() ) + return false; + + int nInCount = TellMaxPut(); + + outBuf.Purge(); + outBuf.EnsureCapacity( nInCount ); + + bool bFromCRLF = ContainsCRLF(); + + // Start reading from the beginning + int nGet = TellGet(); + int nPut = TellPut(); + int nGetDelta = 0; + int nPutDelta = 0; + + const char *pBase = (const char*)Base(); + int nCurrGet = 0; + while ( nCurrGet < nInCount ) + { + const char *pCurr = &pBase[nCurrGet]; + if ( bFromCRLF ) + { + const char *pNext = Q_strnistr( pCurr, "\r\n", nInCount - nCurrGet ); + if ( !pNext ) + { + outBuf.Put( pCurr, nInCount - nCurrGet ); + break; + } + + int nBytes = (size_t)pNext - (size_t)pCurr; + outBuf.Put( pCurr, nBytes ); + outBuf.PutChar( '\n' ); + nCurrGet += nBytes + 2; + if ( nGet >= nCurrGet - 1 ) + { + --nGetDelta; + } + if ( nPut >= nCurrGet - 1 ) + { + --nPutDelta; + } + } + else + { + const char *pNext = Q_strnchr( pCurr, '\n', nInCount - nCurrGet ); + if ( !pNext ) + { + outBuf.Put( pCurr, nInCount - nCurrGet ); + break; + } + + int nBytes = (size_t)pNext - (size_t)pCurr; + outBuf.Put( pCurr, nBytes ); + outBuf.PutChar( '\r' ); + outBuf.PutChar( '\n' ); + nCurrGet += nBytes + 1; + if ( nGet >= nCurrGet ) + { + ++nGetDelta; + } + if ( nPut >= nCurrGet ) + { + ++nPutDelta; + } + } + } + + Assert( nPut + nPutDelta <= outBuf.TellMaxPut() ); + + outBuf.SeekGet( SEEK_HEAD, nGet + nGetDelta ); + outBuf.SeekPut( SEEK_HEAD, nPut + nPutDelta ); + + return true; +} + +//----------------------------------------------------------------------------- +// Fast swap +//----------------------------------------------------------------------------- +void CUtlBuffer::Swap( CUtlBuffer &buf ) +{ + V_swap( m_Get, buf.m_Get ); + V_swap( m_Put, buf.m_Put ); + V_swap( m_nMaxPut, buf.m_nMaxPut ); + V_swap( m_Error, buf.m_Error ); + m_Memory.Swap( buf.m_Memory ); +} + + +//----------------------------------------------------------------------------- +// Fast swap w/ a CUtlMemory. +//----------------------------------------------------------------------------- +void CUtlBuffer::Swap( CUtlMemory &mem ) +{ + m_Get = 0; + m_Put = mem.Count(); + m_nMaxPut = mem.Count(); + m_Error = 0; + m_Memory.Swap( mem ); +} + +//--------------------------------------------------------------------------- +// Implementation of CUtlInplaceBuffer +//--------------------------------------------------------------------------- + +CUtlInplaceBuffer::CUtlInplaceBuffer( int growSize /* = 0 */, int initSize /* = 0 */, int nFlags /* = 0 */ ) : + CUtlBuffer( growSize, initSize, nFlags ) +{ +} + +bool CUtlInplaceBuffer::InplaceGetLinePtr( char **ppszInBufferPtr, int *pnLineLength ) +{ + Assert( IsText() && !ContainsCRLF() ); + + int nLineLen = PeekLineLength(); + if ( nLineLen <= 1 ) + { + SeekGet( SEEK_TAIL, 0 ); + return false; + } + + -- nLineLen; // because it accounts for putting a terminating null-character + + char *pszLine = ( char * ) const_cast< void * >( PeekGet() ); + SeekGet( SEEK_CURRENT, nLineLen ); + + // Set the out args + if ( ppszInBufferPtr ) + *ppszInBufferPtr = pszLine; + + if ( pnLineLength ) + *pnLineLength = nLineLen; + + return true; +} + +char * CUtlInplaceBuffer::InplaceGetLinePtr( void ) +{ + char *pszLine = NULL; + int nLineLen = 0; + + if ( InplaceGetLinePtr( &pszLine, &nLineLen ) ) + { + Assert( nLineLen >= 1 ); + + switch ( pszLine[ nLineLen - 1 ] ) + { + case '\n': + case '\r': + pszLine[ nLineLen - 1 ] = 0; + if ( -- nLineLen ) + { + switch ( pszLine[ nLineLen - 1 ] ) + { + case '\n': + case '\r': + pszLine[ nLineLen - 1 ] = 0; + break; + } + } + break; + + default: + Assert( pszLine[ nLineLen ] == 0 ); + break; + } + } + + return pszLine; +} +