It's been a while since I've been exploring the sro_client, and I decided to share some guide of old stuff.
First of all, the COS is a normal object like character, so it should have a certain class in the sro_client like players, CICPlayer!
You guessed it? It's right, CICCos, lol.
Now, we are looking for CICCos::OnRender function, so we can hook it to play around with class defined objects while they're rendering, so it will help us to edit class objects before calling the real render function!
I am not sure how to find that in general, but you can go debug & look for things that may be related to rendering, after sometime you'll be able to find it. It's 009C57D0 and the vftable entry address for it is 00DE2144.
Now, all left is finding out offsets and labeling them in your reconstructed CICCos class and re-implementing the CICCos::OnRender function to hook.
Code:
class CICCos
{
public:
char pad_0000[248]; //0x0000
unsigned int UniqueID; //0x00F8
char pad_00FC[20]; //0x00FC
std::wstring cosname;//0x0110
char pad_012C[12]; //0x012C
unsigned int cosnamebackgroundcolor; //0x0138
unsigned int cosnameforegroundcolor; //0x013C
char pad_0140[784]; //0x0140
unsigned int CurrentHP; //0x0450
char pad_0454[4]; //0x0454
unsigned int MaxHP; //0x0458
void OnRender();
};
Code:
void CICCos::OnRender()
{
printf("Rendering CICCos with UniqueID (0x%04X), Name (%s), Pointer (0x%08X).\n", this->UniqueID, std::string(this->cosname.begin(), this->cosname.end()).c_str(), this);
this->cosnameforegroundcolor = 0xFF4CFF00;
reinterpret_cast<void(__thiscall*)(CICCos*)>(0x009C57D0)(this); //Render
}
Code:
replaceAddr(0x00DE2144, addr_from_this(&CICCos::OnRender));

You can get the object data from your client by calling a specific function with the Obj ID, there you go.
Code:
class ObjectData
{
public:
USHORT TID; //0x0000
char pad_0002[2]; //0x0002
UINT ObjID; //0x0004
std::n_wstring ObjCodeName; //0x0008
std::n_wstring ObjName; //0x0024
std::n_wstring ObjOrgCodeName; //0x0040
char pad_0064[4]; //0x005C
std::n_wstring ObjNameStrID; //0x0060
std::n_wstring DescStrID; //0x007C
UINT DecayTime; //0x0098
BYTE Country; //0x009C
BYTE UNK_009D; //0x009D | Double country? WTF?
char pad_009E[2]; //0x009E
UINT Rarity; //0x00A0
bool CanTrade; //0x00A4
bool CanSell; //0x00A5
bool CanBuy; //0x00A6
bool CanBorrow; //0x00A7
bool CanDrop; //0x00A8
bool CanPick; //0x00A9
bool CanRepair; //0x00AA
bool CanRevive; //0x00AB
bool CanUse; //0x00AC
bool CanThrow; //0x00AD
char pad_00AE[2]; //0x00AE
long long Price; //0x00B0
long long SellPrice; //0x00B8
UINT CostRepair; //0x00C0
UINT CostRevive; //0x00C4
UINT CostBorrow; //0x00C8
UINT KeepingFee; //0x00CC
int ReqLevelType1; //0x00D0
int ReqLevelType2; //0x00D4
int ReqLevelType3; //0x00D8
int ReqLevelType4; //0x00DC
int ReqLevel1; //0x00E0
int ReqLevel2; //0x00E4
int ReqLevel3; //0x00E8
int ReqLevel4; //0x00EC
int MaxContain; //0x00F0
UINT RegionID; //0x00F4
int Direction; //0x098
int OffsetX; //0x09C
int OffsetY; //0x0100
int OffsetZ; //0x0104
UINT Speed1; //0x0108
UINT Speed2; //0x010C
int Scale; //0x0110
int BCHeight; //0x0114
int BCRadius; //0x0118
UINT EventID; //0x011C
UINT ObjItemLinkID; //0x0120
std::n_wstring AssocFileObj; //0x0124
std::n_wstring AssocFileDrop; //0x0140
std::n_wstring AssocFileIcon; //0x015C
std::n_wstring AssocFile1; //0x0178
std::n_wstring AssocFile2; //0x0194
};
Code:
#pragma once
class CGlobalDataManager
{
public:
static CGlobalDataManager* GetGlobalDataManager();
class ObjectData* GetObjectData(unsigned int ObjID);
};
Code:
#include "CGlobalDataManager.h"
#include "ObjectData.h"
CGlobalDataManager* CGlobalDataManager::GetGlobalDataManager()
{
return reinterpret_cast<CGlobalDataManager*>(0xEEDF08);
}
ObjectData* CGlobalDataManager::GetObjectData(unsigned int ObjID)
{
return reinterpret_cast<ObjectData*(__thiscall*)(CGlobalDataManager*, unsigned int)>(0x0093F710)(this, ObjID);
}
Note: Structures above are for VC80 libs, back when strings were 28 bytes.
Note: Always compile on Release!
Special thanks to: florian0






