I've made compatible my old sro stuffs with vSro client 1.188 and coalesced into this small library. It provides hooking packet receive, sending packet and some utility functions.
Documentation
Example code:
Example code for appending data to end of the login packet:
thanks to pushedx for his attentive helps
Documentation
Example code:
Code:
#include "SroPP.h"
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
void printUIText(const wchar_t* codename)
{
wchar_t buffer[1024];
swprintf_s(buffer, L"%s -> '%s'", codename, SroPP::Game::GetUIText(codename));
SroPP::Game::WriteChat(SroPP::ChatGroup::GM, buffer, 255 << 8);
}
//copied from: https://github.com/ProjectHax/ServerStats/blob/master/ServerStats/shared/stream_utility.cpp
#include <sstream>
#include <cctype>
#include <iomanip>
std::string DumpToString(const void* stream, size_t size)
{
std::stringstream ss;
auto buffer = static_cast<const unsigned char*>(stream);
int fields[16];
size_t index = 0;
size_t cur = 0;
size_t total = size;
if (total == 0 || total % 16)
{
total += (16 - (total % 16));
}
for (size_t x = 0; x < total; x++)
{
fields[index++] = cur < size ? buffer[x] : 0;
++cur;
if (index == 16)
{
for (size_t y = 0; y < 16; y++)
{
if (cur - 16 + y < size)
{
ss << std::hex << std::setfill('0') << std::setw(2) << fields[y] << ' ';
}
else
{
ss << " ";
}
}
ss << " ";
for (size_t y = 0; y < 16; y++)
{
if (cur - 16 + y < size)
{
int ch = fields[y];
if (std::isprint(ch) && !std::isspace(ch))
{
ss << (char)ch;
}
else
{
ss << '.';
}
}
else
{
ss << '.';
}
}
ss << std::endl;
index = 0;
}
}
return ss.str();
}
void printPacketDump(const SroPP::Packet& packet)
{
std::string dump;
auto appendf = [&dump](const char* format, ...)
{
char buf[256];
va_list ap;
va_start(ap, format);
vsprintf_s(buf, format, ap);
va_end(ap);
dump.append(buf);
};
appendf("[Opcode: %4X] [Size: %Iu bytes]\n", packet.Opcode(), packet.Size());
char packetBuffer[256];
size_t bytesToDump = (std::min)(packet.Size(), sizeof(packetBuffer));
if (bytesToDump > 0)
{
packet.Peek(packetBuffer, bytesToDump);
dump.append(DumpToString(packetBuffer, bytesToDump));
}
if (size_t remaining = packet.Size() - bytesToDump)
appendf("... + %lu bytes\n", remaining);
dump.append("\n");
OutputDebugStringA(dump.c_str());
}
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, void*)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hModule);
try
{
SroPP::Library::BeginInitialize();
SroPP::PacketHook::RegisterReceive([](const SroPP::Packet& packet) -> bool
{
printPacketDump(packet);
switch (packet.Opcode())
{
case 0xb045:
{
bool succeed = packet.Read<bool>();
if (succeed)
{
uint32_t uniqueId = packet.Read<uint32_t>();
wchar_t buffer[256];
swprintf_s(buffer, L"Selected target ID: %lu", uniqueId);
SroPP::Game::WriteChat(SroPP::ChatGroup::All, buffer, 0xFF00D8D8);
}
}
break;
case 0x3026:
{
uint8_t type = packet.Read<uint8_t>();
if (type == 1)
{
uint32_t sender = packet.Read<uint32_t>();
auto message = packet.ReadStringA();
if (message == ".1")
{
printUIText(L"UIIT_MSG_GUILD_JOIN_RESULT");
printUIText(L"SN_TALK_QTUTORIAL_CH_27");
printUIText(L"SN_MOB_CH_GYO_CLON");
}
}
}
break;
}
return false;
});
SroPP::Library::EndInitialize();
}
catch (std::exception &ex)
{
MessageBoxA(NULL, ex.what(), "exception", MB_ICONERROR);
}
}
return TRUE;
}
Code:
std::vector<char> onLoginRequest()
{
#pragma pack(push, 1)
struct
{
uint32_t val1;
} s;
#pragma pack(pop)
s.val1 = 0x12345678;
return SroPP::MakeVector(s);
}
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, void*)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hModule);
try
{
SroPP::Library::BeginInitialize();
SroPP::PacketHook::RegisterSendLoginRequest(onLoginRequest);
SroPP::Library::EndInitialize();
}
catch (std::exception &ex)
{
MessageBoxA(NULL, ex.what(), "exception", MB_ICONERROR);
return FALSE;
}
}
return TRUE;
}