1
0

RCONClient: Initial implementation.

Fix #79.
This commit is contained in:
madmaxoft 2013-11-24 14:35:35 +01:00
parent c10daa8530
commit dbb76ef9fe
8 changed files with 920 additions and 0 deletions

7
Tools/RCONClient/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
Debug/
logs/
Release/
*.suo
*.user
*.ncb
*.sdf

View File

@ -0,0 +1,10 @@
// Globals.cpp
// This file is used for precompiled header generation in MSVC environments
#include "Globals.h"

208
Tools/RCONClient/Globals.h Normal file
View File

@ -0,0 +1,208 @@
// Globals.h
// This file gets included from every module in the project, so that global symbols may be introduced easily
// Also used for precompiled header generation in MSVC environments
// Compiler-dependent stuff:
#if defined(_MSC_VER)
// MSVC produces warning C4481 on the override keyword usage, so disable the warning altogether
#pragma warning(disable:4481)
// Disable some warnings that we don't care about:
#pragma warning(disable:4100)
#define OBSOLETE __declspec(deprecated)
// No alignment needed in MSVC
#define ALIGN_8
#define ALIGN_16
#elif defined(__GNUC__)
// TODO: Can GCC explicitly mark classes as abstract (no instances can be created)?
#define abstract
// TODO: Can GCC mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class)
#define override
#define OBSOLETE __attribute__((deprecated))
#define ALIGN_8 __attribute__((aligned(8)))
#define ALIGN_16 __attribute__((aligned(16)))
// Some portability macros :)
#define stricmp strcasecmp
#else
#error "You are using an unsupported compiler, you might need to #define some stuff here for your compiler"
/*
// Copy and uncomment this into another #elif section based on your compiler identification
// Explicitly mark classes as abstract (no instances can be created)
#define abstract
// Mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class)
#define override
// Mark functions as obsolete, so that their usage results in a compile-time warning
#define OBSOLETE
// Mark types / variables for alignment. Do the platforms need it?
#define ALIGN_8
#define ALIGN_16
*/
#endif
// Integral types with predefined sizes:
typedef long long Int64;
typedef int Int32;
typedef short Int16;
typedef unsigned long long UInt64;
typedef unsigned int UInt32;
typedef unsigned short UInt16;
// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for any class that shouldn't allow copying itself
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName &); \
void operator=(const TypeName &)
// A macro that is used to mark unused function parameters, to avoid pedantic warnings in gcc
#define UNUSED(X) (void)(X)
// OS-dependent stuff:
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x501 // We want to target WinXP and higher
#include <Windows.h>
#include <winsock2.h>
#include <Ws2tcpip.h> // IPv6 stuff
// Windows SDK defines min and max macros, messing up with our std::min and std::max usage
#undef min
#undef max
// Windows SDK defines GetFreeSpace as a constant, probably a Win16 API remnant
#ifdef GetFreeSpace
#undef GetFreeSpace
#endif // GetFreeSpace
#else
#include <sys/types.h>
#include <sys/stat.h> // for mkdir
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
#include <dirent.h>
#include <errno.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <pthread.h>
#include <semaphore.h>
#include <errno.h>
#include <fcntl.h>
#if !defined(ANDROID_NDK)
#include <tr1/memory>
#endif
#endif
#if !defined(ANDROID_NDK)
#define USE_SQUIRREL
#endif
#if defined(ANDROID_NDK)
#define FILE_IO_PREFIX "/sdcard/mcserver/"
#else
#define FILE_IO_PREFIX ""
#endif
// CRT stuff:
#include <sys/stat.h>
#include <assert.h>
#include <stdio.h>
#include <math.h>
#include <stdarg.h>
#include <time.h>
// STL stuff:
#include <vector>
#include <list>
#include <deque>
#include <string>
#include <map>
#include <algorithm>
#include <memory>
// Common headers (part 1, without macros):
#include "StringUtils.h"
#include "OSSupport/CriticalSection.h"
#include "OSSupport/File.h"
#include "MCLogger.h"
// Common definitions:
/// Evaluates to the number of elements in an array (compile-time!)
#define ARRAYCOUNT(X) (sizeof(X) / sizeof(*(X)))
/// Allows arithmetic expressions like "32 KiB" (but consider using parenthesis around it, "(32 KiB)" )
#define KiB * 1024
/// Faster than (int)floorf((float)x / (float)div)
#define FAST_FLOOR_DIV( x, div ) ( (x) < 0 ? (((int)x / div) - 1) : ((int)x / div) )
// Own version of assert() that writes failed assertions to the log for review
#ifdef NDEBUG
#define ASSERT(x) ((void)0)
#else
#define ASSERT assert
#endif
// Pretty much the same as ASSERT() but stays in Release builds
#define VERIFY( x ) ( !!(x) || ( LOGERROR("Verification failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), exit(1), 0 ) )

View File

@ -0,0 +1,332 @@
// RCONClient.cpp
// Implements the main app entrypoint
#include "Globals.h"
#include "OSSupport/Socket.h"
#include "ByteBuffer.h"
// If set to true, verbose messages are output to stderr. Use the "-v" or "--verbose" param to turn on
bool g_IsVerbose = false;
/// This class can read and write RCON packets to / from a connected socket
class cRCONPacketizer
{
public:
enum
{
ptCommand = 2,
ptLogin = 3,
} ;
cRCONPacketizer(cSocket & a_Socket);
/// Sends the packet to the socket and waits until the response is received.
/// Returns true if response successfully received, false if the client disconnected or protocol error.
/// Dumps the reply payload to stdout.
bool SendPacket(int a_PacketType, const AString & a_PacketPayload);
protected:
/// The socket to use for reading incoming data and writing outgoing data:
cSocket & m_Socket;
/// The RequestID of the packet that is being sent. Incremented when the reply is received
int m_RequestID;
/// Receives the full response and dumps its payload to stdout.
/// Returns true if successful, false if the client disconnected or protocol error.
bool ReceiveResponse(void);
/// Parses the received response packet and dumps its payload to stdout.
/// Returns true if successful, false on protocol error
/// Assumes that the packet length has already been read from the packet
/// If the packet is successfully parsed, increments m_RequestID
bool ParsePacket(cByteBuffer & a_Buffer, int a_PacketLength);
} ;
cRCONPacketizer::cRCONPacketizer(cSocket & a_Socket) :
m_Socket(a_Socket),
m_RequestID(0)
{
}
bool cRCONPacketizer::SendPacket(int a_PacketType, const AString & a_PacketPayload)
{
// Send the packet:
cByteBuffer bb(a_PacketPayload.size() + 30);
bb.WriteLEInt(m_RequestID);
bb.WriteLEInt(a_PacketType);
bb.WriteBuf(a_PacketPayload.data(), a_PacketPayload.size());
bb.WriteBEShort(0); // Padding
AString Packet;
bb.ReadAll(Packet);
size_t Length = Packet.size();
if (!m_Socket.Send((const char *)&Length, 4))
{
fprintf(stderr, "Network error while sending packet: %d (%s). Aborting.",
cSocket::GetLastError(), cSocket::GetLastErrorString().c_str()
);
return false;
}
if (!m_Socket.Send(Packet.data(), Packet.size()))
{
fprintf(stderr, "Network error while sending packet: %d (%s). Aborting.",
cSocket::GetLastError(), cSocket::GetLastErrorString().c_str()
);
return false;
}
return ReceiveResponse();
}
bool cRCONPacketizer::ReceiveResponse(void)
{
// Receive the response:
cByteBuffer Buffer(64 KiB);
while (true)
{
char buf[1024];
int NumReceived = m_Socket.Receive(buf, sizeof(buf), 0);
if (NumReceived == 0)
{
fprintf(stderr, "The remote end closed the connection. Aborting.");
return false;
}
if (NumReceived < 0)
{
fprintf(stderr, "Network error while receiving response: %d, %d (%s). Aborting.",
NumReceived, cSocket::GetLastError(), cSocket::GetLastErrorString().c_str()
);
return false;
}
Buffer.Write(buf, NumReceived);
Buffer.ResetRead();
// Check if the buffer contains the full packet:
if (!Buffer.CanReadBytes(14))
{
// 14 is the minimum packet size for RCON
continue;
}
int PacketSize;
VERIFY(Buffer.ReadLEInt(PacketSize));
if (!Buffer.CanReadBytes(PacketSize))
{
// The packet is not complete yet
continue;
}
// Parse the packet
return ParsePacket(Buffer, PacketSize);
}
}
bool cRCONPacketizer::ParsePacket(cByteBuffer & a_Buffer, int a_PacketLength)
{
// Check that the request ID is equal
bool IsValid = true;
int RequestID = 0;
VERIFY(a_Buffer.ReadLEInt(RequestID));
if (RequestID != m_RequestID)
{
if ((RequestID == -1) && (m_RequestID == 0))
{
fprintf(stderr, "Login failed. Aborting.");
IsValid = false;
// Continue, so that the payload is printed before the program aborts.
}
else
{
fprintf(stderr, "The server returned an invalid request ID, got %d, exp. %d. Aborting.", RequestID, m_RequestID);
return false;
}
}
// Check the packet type:
int PacketType = 0;
VERIFY(a_Buffer.ReadLEInt(PacketType));
if (PacketType != ptCommand)
{
fprintf(stderr, "The server returned an unknown packet type: %d. Aborting.", PacketType);
IsValid = false;
// Continue, so that the payload is printed before the program aborts.
}
AString Payload;
VERIFY(a_Buffer.ReadString(Payload, a_PacketLength - 10));
// Dump the payload to stdout, in a binary mode
fwrite(Payload.data(), Payload.size(), 1, stdout);
if (IsValid)
{
m_RequestID++;
return true;
}
return false;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// main:
int RealMain(int argc, char * argv[])
{
new cMCLogger; // Create a new logger
// Parse the cmdline params for server IP, port, password and the commands to send:
AString ServerAddress, Password;
int ServerPort = -1;
AStringVector Commands;
for (int i = 1; i < argc; i++)
{
if (((NoCaseCompare(argv[i], "-s") == 0) || (NoCaseCompare(argv[i], "--server") == 0)) && (i < argc - 1))
{
ServerAddress = argv[i + 1];
i++;
continue;
}
if (((NoCaseCompare(argv[i], "-p") == 0) || (NoCaseCompare(argv[i], "--port") == 0)) && (i < argc - 1))
{
ServerPort = atoi(argv[i + 1]);
i++;
continue;
}
if (((NoCaseCompare(argv[i], "-w") == 0) || (NoCaseCompare(argv[i], "--password") == 0)) && (i < argc - 1))
{
Password = argv[i + 1];
i++;
continue;
}
if (((NoCaseCompare(argv[i], "-c") == 0) || (NoCaseCompare(argv[i], "--cmd") == 0) || (NoCaseCompare(argv[i], "--command") == 0)) && (i < argc - 1))
{
Commands.push_back(argv[i + 1]);
i++;
continue;
}
if (((NoCaseCompare(argv[i], "-f") == 0) || (NoCaseCompare(argv[i], "--file") == 0)) && (i < argc - 1))
{
i++;
cFile f(argv[i], cFile::fmRead);
if (!f.IsOpen())
{
fprintf(stderr, "Cannot read commands from file \"%s\", aborting.", argv[i]);
return 2;
}
AString cmd;
f.ReadRestOfFile(cmd);
Commands.push_back(cmd);
continue;
}
if ((NoCaseCompare(argv[i], "-v") == 0) || (NoCaseCompare(argv[i], "--verbose") == 0))
{
fprintf(stderr, "Verbose output enabled\n");
g_IsVerbose = true;
continue;
}
fprintf(stderr, "Unknown parameter: \"%s\". Aborting.", argv[i]);
return 1;
} // for i - argv[]
if (ServerAddress.empty() || (ServerPort < 0))
{
fprintf(stderr, "Server address or port not set. Use the --server and --port parameters to set them. Aborting.");
return 1;
}
// Connect:
if (cSocket::WSAStartup() != 0)
{
fprintf(stderr, "Cannot initialize network stack. Aborting\n");
return 6;
}
if (g_IsVerbose)
{
fprintf(stderr, "Connecting to \"%s:%d\"...\n", ServerAddress.c_str(), ServerPort);
}
cSocket s = cSocket::CreateSocket(cSocket::IPv4);
if (!s.ConnectIPv4(ServerAddress, (unsigned short)ServerPort))
{
fprintf(stderr, "Cannot connect to \"%s:%d\": %s\n", ServerAddress.c_str(), ServerPort, cSocket::GetLastErrorString().c_str());
return 3;
}
cRCONPacketizer Packetizer(s);
// Authenticate using the provided password:
if (!Password.empty())
{
if (g_IsVerbose)
{
fprintf(stderr, "Sending the login packet...\n");
}
if (!Packetizer.SendPacket(cRCONPacketizer::ptLogin, Password))
{
// Error message has already been printed, bail out
return 4;
}
}
else
{
if (g_IsVerbose)
{
fprintf(stderr, "No password provided, not sending a login packet.\n");
}
}
for (AStringVector::const_iterator itr = Commands.begin(), end = Commands.end(); itr != end; ++itr)
{
if (g_IsVerbose)
{
fprintf(stderr, "Sending command \"%s\"...\n", itr->c_str());
}
if (!Packetizer.SendPacket(cRCONPacketizer::ptCommand, *itr))
{
return 5;
}
}
return 0;
}
int main(int argc, char * argv[])
{
// This redirection function is only so that debugging the program is easier in MSVC - when RealMain exits, it's still possible to place a breakpoint
int res = RealMain(argc, argv);
return res;
}

View File

@ -0,0 +1,20 @@

Microsoft Visual Studio Solution File, Format Version 10.00
# Visual C++ Express 2008
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RCONClient", "RCONClient.vcproj", "{1A48B032-07D0-4DDD-8362-66C0FC7F7849}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1A48B032-07D0-4DDD-8362-66C0FC7F7849}.Debug|Win32.ActiveCfg = Debug|Win32
{1A48B032-07D0-4DDD-8362-66C0FC7F7849}.Debug|Win32.Build.0 = Debug|Win32
{1A48B032-07D0-4DDD-8362-66C0FC7F7849}.Release|Win32.ActiveCfg = Release|Win32
{1A48B032-07D0-4DDD-8362-66C0FC7F7849}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,287 @@
<?xml version="1.0" encoding="windows-1250"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9,00"
Name="RCONClient"
ProjectGUID="{1A48B032-07D0-4DDD-8362-66C0FC7F7849}"
RootNamespace="RCONClient"
Keyword="Win32Proj"
TargetFrameworkVersion="196613"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="../../source"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="2"
PrecompiledHeaderThrough="Globals.h"
WarningLevel="3"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
AdditionalIncludeDirectories="../../source"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="2"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="2"
PrecompiledHeaderThrough="Globals.h"
WarningLevel="3"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath=".\Globals.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\Globals.h"
>
</File>
<File
RelativePath=".\RCONClient.cpp"
>
</File>
<Filter
Name="shared"
>
<File
RelativePath="..\..\source\ByteBuffer.cpp"
>
</File>
<File
RelativePath="..\..\source\ByteBuffer.h"
>
</File>
<File
RelativePath="..\..\source\Log.cpp"
>
</File>
<File
RelativePath="..\..\source\Log.h"
>
</File>
<File
RelativePath="..\..\source\MCLogger.cpp"
>
</File>
<File
RelativePath="..\..\source\MCLogger.h"
>
</File>
<File
RelativePath="..\..\source\StringUtils.cpp"
>
</File>
<File
RelativePath="..\..\source\StringUtils.h"
>
</File>
<Filter
Name="OSSupport"
>
<File
RelativePath="..\..\source\OSSupport\CriticalSection.cpp"
>
</File>
<File
RelativePath="..\..\source\OSSupport\CriticalSection.h"
>
</File>
<File
RelativePath="..\..\source\OSSupport\File.cpp"
>
</File>
<File
RelativePath="..\..\source\OSSupport\File.h"
>
</File>
<File
RelativePath="..\..\source\OSSupport\IsThread.cpp"
>
</File>
<File
RelativePath="..\..\source\OSSupport\IsThread.h"
>
</File>
<File
RelativePath="..\..\source\OSSupport\Socket.cpp"
>
</File>
<File
RelativePath="..\..\source\OSSupport\Socket.h"
>
</File>
</Filter>
</Filter>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -13,6 +13,25 @@
// Try to determine endianness:
#if ( \
defined(__i386__) || defined(__alpha__) || \
defined(__ia64) || defined(__ia64__) || \
defined(_M_IX86) || defined(_M_IA64) || \
defined(_M_ALPHA) || defined(__amd64) || \
defined(__amd64__) || defined(_M_AMD64) || \
defined(__x86_64) || defined(__x86_64__) || \
defined(_M_X64) || defined(__bfin__) || \
defined(__ARMEL__) || \
(defined(_WIN32) && defined(__ARM__) && defined(_MSC_VER)) \
)
#define IS_LITTLE_ENDIAN
#elif defined (__ARMEB__)
#define IS_BIG_ENDIAN
#else
#error Cannot determine endianness of this platform
#endif
// If a string sent over the protocol is larger than this, a warning is emitted to the console
#define MAX_STRING_SIZE (512 KiB)
@ -416,6 +435,25 @@ bool cByteBuffer::ReadVarUTF8String(AString & a_Value)
bool cByteBuffer::ReadLEInt(int & a_Value)
{
CHECK_THREAD;
CheckValid();
NEEDBYTES(4);
ReadBuf(&a_Value, 4);
#ifdef IS_BIG_ENDIAN
// Convert:
a_Value = ((a_Value >> 24) & 0xff) | ((a_Value >> 16) & 0xff00) | ((a_Value >> 8) & 0xff0000) | (a_Value & 0xff000000);
#endif
return true;
}
bool cByteBuffer::WriteChar(char a_Value)
{
CHECK_THREAD;
@ -572,6 +610,22 @@ bool cByteBuffer::WriteVarUTF8String(const AString & a_Value)
bool cByteBuffer::WriteLEInt(int a_Value)
{
CHECK_THREAD;
CheckValid();
#ifdef IS_LITTLE_ENDIAN
return WriteBuf((const char *)&a_Value, 4);
#else
int Value = ((a_Value >> 24) & 0xff) | ((a_Value >> 16) & 0xff00) | ((a_Value >> 8) & 0xff0000) | (a_Value & 0xff000000);
return WriteBuf((const char *)&Value, 4);
#endif
}
bool cByteBuffer::ReadBuf(void * a_Buffer, int a_Count)
{
CHECK_THREAD;

View File

@ -60,6 +60,7 @@ public:
bool ReadBEUTF16String16(AString & a_Value); // string length as BE short, then string as UTF-16BE
bool ReadVarInt (UInt32 & a_Value);
bool ReadVarUTF8String (AString & a_Value); // string length as VarInt, then string as UTF-8
bool ReadLEInt (int & a_Value);
/// Reads VarInt, assigns it to anything that can be assigned from an UInt32 (unsigned short, char, Byte, double, ...)
template <typename T> bool ReadVarInt(T & a_Value)
@ -85,6 +86,7 @@ public:
bool WriteBEUTF16String16(const AString & a_Value); // string length as BE short, then string as UTF-16BE
bool WriteVarInt (UInt32 a_Value);
bool WriteVarUTF8String (const AString & a_Value); // string length as VarInt, then string as UTF-8
bool WriteLEInt (int a_Value);
/// Reads a_Count bytes into a_Buffer; returns true if successful
bool ReadBuf(void * a_Buffer, int a_Count);