I was messing around with the AoE2HD binary for a little while, so I decided to share some of my findings.
Note: I disabled ASLR, as I couldnt be arsed with calculating the offsets, so all the function addresses have 0x400000 base.
Code:
struct Player;
struct Price;
struct Resources;
struct Unit;
struct MainClass;
struct UIMain;
struct UITextElement;
struct GameData;
struct ObjectManager;
struct UnitData;
struct TargetPtr;
struct TargetData;
struct BuildEventArgs;
struct ChatLog;
struct DrawingData;
MainClass* mainClassPointer = *(MainClass**)0x0B7A174;
ChatLog* chatLogPointer = *(ChatLog**)0x00B7A0E0;
struct MainClass
{
/*0x0000*/ char unk_0000[0x21C];
/*0x021C*/ GameData* gameMain;
/*0x0220*/ char unk_0220[0x1384];
/*0x15A4*/ UIMain* uiMain;
};
struct GameData
{
/*0x0000*/ char unk_0000[0x48];
/*0x0048*/ uint16_t playerCount;
/*0x004A*/ uint16_t unk_004A;
/*0x004C*/ Player** players;
/*0x0050*/ char unk_0050[0x48];
/*0x0098*/ uint16_t activePlayerIndex;
/*0x009A*/ uint16_t unk_009A;
/*0x009C*/ float cameraPosX;
/*0x00A0*/ float cameraPosY;
/*0x00A4*/ char unk_00A4[0xC];
/*0x00B0*/ Unit** objectArray;
/*0x00B4*/ uint32_t objectCount;
/*0x00B8*/ uint32_t objectMax;
};
struct UIMain
{
/*0x000000*/ char unk_0000[0x13B14];
/*0x013B14*/ void* unk_013B13;
/*0x013B18*/ UITextElement* chatRows[8];
/*0x013B38*/ void* unk_013B38;
/*0x013B3C*/ uint32_t chatRowsCount;
/*0x013B40*/ char unk_013B40[0x8];
/*0x013B48*/ UITextElement* pauseText; // actually different kind of ui element
};
struct UITextElement
{
/*0x0000*/ void* vtbl;
/*0x0000*/ char unk_0004[0x10];
/*0x0014*/ uint32_t xLength;
/*0x0018*/ uint32_t yLength;
/*0x001C*/ char* elementName;
/*0x0020*/ char unk_0020[0xEC];
/*0x010C*/ char text[0x200];
/*0x030C*/ BOOL isShown;
/*0x0310*/ uint32_t timeSent;
/*0x0314*/ uint32_t timeToShow;
/*0x0318*/ void* unkArg1;
/*0x031C*/ void* unkArg2;
/*0x0320*/ void* unkArg3;
/*0x0324*/ char color;
/*0x0325*/ char unk_0325[0x3];
/*0x0326*/ char unk_0326[0x1C];
/*0x0342*/ DrawingData* drawingData;
};
struct DrawingData
{
/*0x0000*/ char unk_0000[0x20];
/*0x0020*/ HDC dcHandle;
};
struct Player
{
/*0x0000*/ void* vtbl;
/*0x0004*/ char unk_0004[0x6C];
/*0x0070*/ uint32_t priceCount;
/*0x0074*/ Price** priceTable;
/*0x0078*/ ObjectManager* objectManager;
/*0x007C*/ char unk_007C[0x1C];
/*0x0098*/ char* name;
/*0x009C*/ char unk_009C[0xC];
/*0x00A8*/ Resources* resources;
/*0x00AC*/ char unk_00AC[0xC8];
/*0x0174*/ float cameraPosX;
/*0x0178*/ float cameraPosY;
/*0x017C*/ char unk_017C[0x44];
/*0x01C0*/ Unit* lastSelectedUnit;
/*0x01C4*/ Unit* selectedUnits[60];
/*0x02B4*/ void* unk_02B4;
/*0x02B8*/ uint32_t selectedUnitsCount;
};
struct Unit
{
/*0x0000*/ void* vtbl;
/*0x0004*/ uint32_t unitID;
/*0x0008*/ UnitData* unitData;
/*0x000C*/ Player* owner;
/*0x0010*/ char* name1;
/*0x0014*/ char* name2;
/*0x0018*/ char unk_0018[0x18];
/*0x0030*/ float hp;
/*0x0034*/ uint16_t unk_0034;
/*0x0036*/ uint16_t isSelected;
/*0x0038*/ float posXReadOnly;
/*0x003C*/ float posY1ReadOnly;
/*0x0040*/ void* unk_0040;
/*0x0044*/ float resourcesCarryingCount;
/*0x0048*/ char unk_0048[0x3C];
/*0x0084*/ float velocity;
/*0x0088*/ char unk_0088[0x3C];
/*0x00C4*/ float posX;
/*0x00C8*/ void* unk7;
/*0x00CC*/ float posY;
/*0x00D0*/ float rotation;
/*0x00D4*/ char unk_00D4[0x38];
/*0x010C*/ TargetPtr* targetDataPtr;
};
struct UnitData
{
/*0x0000*/ char unk_0000[0xCC];
/*0x00CC*/ float speed;
};
struct TargetPtr
{
/*0x0000*/ char unk_0000[0x8];
/*0x0008*/ TargetData** targetData;
};
struct TargetData
{
/*0x0000*/ char unk_0000[0x8];
/*0x0008*/ Unit* thisUnit;
/*0x000C*/ void* unk_000C;
/*0x0010*/ Unit* targetUnit; // 0 no target
/*0x0014*/ void* unk_0014;
/*0x0018*/ uint32_t targetID;
/*0x001C*/ void* unk_001C;
/*0x0020*/ float destinationX; // -1 no destination
/*0x0024*/ float destinationY; // -1 no destination
/*0x0028*/ float isMoving; // -1 not moving 0 moving
};
struct ObjectManager
{
/*0x0000*/ void* unk_0000;
/*0x0004*/ Unit** objects;
/*0x0008*/ uint32_t objectCount;
};
struct Price
{
/*0x0000*/ void* vtbl;
/*0x0004*/ char* name;
/*0x0008*/ char unk_0008[0x10];
/*0x0018*/ short typeID;
/*0x001A*/ char unk_001A[0x15A];
/*0x0174*/ uint16_t resourceType1;
/*0x0176*/ uint16_t cost1;
/*0x0178*/ uint16_t priceUnk1;
/*0x017A*/ uint16_t resourceType2;
/*0x017C*/ uint16_t cost2;
/*0x017E*/ uint16_t priceUnk2;
/*0x0180*/ uint16_t ResourceType3;
/*0x0182*/ uint16_t cost3;
/*0x0184*/ uint16_t priceUnk3;
/*0x0186*/ uint16_t buildTime;
};
struct Resources
{
/*0x0000*/ float food;
/*0x0004*/ float wood;
/*0x0008*/ float stone;
/*0x000C*/ float gold;
/*0x0010*/ float populationLeft;
/*0x0014*/ void* unk_0014;
/*0x0018*/ float age;
/*0x001C*/ char unk_001C[0x10];
/*0x002C*/ float population;
/*0x0030*/ char unk_0030[0x1C];
/*0x004C*/ float unitsProduced;
/*0x0050*/ char unk_0050[0x8];
/*0x0058*/ float percentageExplored;
};
struct BuildEventArgs
{
/*0x0000*/ char unk_0000;
/*0x0001*/ char builderCount;
/*0x0002*/ char playerID;
/*0x0003*/ char unk_0003;
/*0x0004*/ float posX;
/*0x0008*/ float posY;
/*0x000C*/ uint16_t buildingID;
/*0x000E*/ uint16_t unk_000E;
/*0x0010*/ char unk_0010[0x8];
/*0x0018*/ uint32_t builderID[0];
};
struct ChatLog
{
/*0x0000*/ char unk_0000[0x58];
/*0x0058*/ char* msg[0];
};
Code:
// this = unit(victim)
float(__stdcall* GetDamage)(uint32_t attackType, float unkFloat_1, float unkFloat_2, Player* attackerPlayer, Unit* attackerUnit) = 0x0061B360;
// this = unit
float(__stdcall* MoveUnit)(float posX, float posY, uint32_t unk0) = 0x004B3900;
// this = unit
void(__stdcall* ChangeOwner)(Player* newOwner) = 0x004AB0F0;
// this = building(unit)
void(__stdcall* TrainUnit)(uint32_t unitID, uint32_t unk1) = 0x005409E0;
// this = unit
void(__stdcall* SetTargetUnit)(Unit* targetUnit, float posX, float posY, uint32_t unk0, uint32_t unk0_2, uint32_t unkMinus1) = 0x0061B510;
// this = unit
void(__stdcall* SetTargetLocation)(uint32_t unk0_1, float posX, float posY, uint32_t unk0_2) = 0x00621830;
// this = price
Unit*(__stdcall* CreateBuilding) (Player* player, float posX, float posY, uint32_t unk0, uint32_t unkMinus1) = 0x0044E120;
// this = struct { uint32_t unk; GameData* ptr; }
void(__stdcall* BuildStructure) (BuildEventArgs* args) = 0x004E18A0;
// this = player
void(__stdcall* DeselectAllUnits)(void) = 0x005D2FB0;
// this = player
void(__stdcall* SelectUnit)(Unit* unit, uint32_t unk0) = 0x0047A990;
// this = UITextElement
void(__stdcall* PrintText)(uint32_t position, char* msg, char color, uint32_t unk0_1, uint32_t unk0_2, uint32_t unk0_3, uint32_t unk0_4, uint32_t timeToShow, uint32_t unk0_5, uint32_t unkMinusOne) = 0x445D90;
// this = UITextElement
void(__stdcall* DrawUIText)() = 0x5C82F0;
Example scripts:
Kill all units in game apart from yours and the GAIA ones (seems to work even in multiplayer, but would need some 3rd party to confirm.
Code:
void AnalRape()
{
uint16_t playerCount = mainClassPointer->gameMain->playerCount;
for (int i = 2; i < playerCount; i++)
{
Player* player = mainClassPointer->gameMain->players[i];
ObjectManager* mgr = player->objectManager;
int objCount = mgr->objectCount;
for (int i2 = 0; i2 < objCount; i2++)
{
Unit* unit = mgr->objects[i2];
unit->hp = 0.0;
}
}
}
"Teleport Hack" for your units (wanted to hook the movement event, but its protected from hooking by the steam):
Code:
float __stdcall MoveUnitHook(float posX, float posY, uint32_t unk0)
{
Unit* unit;
__asm mov unit, ecx
if (mainClassPointer->gameMain->players[1] == unit->owner)
{
if ((uint32_t)unit->targetDataPtr > 0x400000)
{
TargetData** target = unit->targetDataPtr->targetData;
float destX = (*target)->destinationX;
float destY = (*target)->destinationY;
if (destX > 0 && destY > 0)
{
posX = destX;
posY = destY;
}
}
}
__asm mov ecx, unit
MoveUnitTrampoline(posX, posY, unk0);
}