I've been doing quite a bit of reversing of Halo structures and thought I would share it. As far as know, I've seen no documentation on this. I'm writing this for developers, but anyone can learn from it. I started reversing these structures while looking for a new way to make an aimbot and was fortunatly successful. Bitterbanana and Odie5533 ( who used the same method ) wrote a code cave and jmp'd to it from the function that updates players' world coordinates. The code cave then created an array of pointers to player data structures from the function. The reason for this was because the player data structures are dynamic. I found a new way to go about this without code caving or even touching any halo functions. No, this isn't a guide on how to make an aimbot, but this lead me to a lot of great information you are about to read and could be useful in many applications.
I did a bit of reversing on the Unreal Engine a few months ago. There is an array in the UE called GObjObjects. GObjObjects array has a pointer to an object table along with the number of objects and the size of the table. This table has pointers to every object structure in the entire game! Pretty cool huh? Well, I quit working on Unreal Engine since it was such a pain in the arse! I came back to Halo a few weeks after and decided to see if there was a similar table. Low and behold, I discovered it! All I did was grab the base pointer of my dynamic player structure ( searched for my world coordinates for this ), and did a pointer search. From the results, I was brought to this array table. Now, Halo's object table is very small compared to UE's so I was pretty excited. UE's object table contained over 200,000 objects compared to 100+ in Halo!!
Every data section begins with a header and sometimes a footer. The header contains some good info about the table / structures below it including the number of objects / size / max / pointer to first / etc. The object table contains every object in the game. This includes all scenery, vehicles, players, weapons, equipment, gametype flags ( flags, oddballs), and decals ( and possibly more ). There is a 12 byte array for each object in the table, which includes a pointer to that objects data structure, the size of the structure, and the object ID. I'm not sure what the other 2 shorts are, but I think it has to do with the object type or something.
The following structures and addresses in this doc are from Halo Trial. Halo PC and CE could have some changes, but for the most part they are identical besides the addresses.
Code:
struct Object_Table_Header
{
unsigned char TName[32]; // 'object'
unsigned short MaxObjects; // Maximum number of objects - 0x800(2048 objects)
unsigned short Size; // Size of each object array - 0x0C(12 bytes)
unsigned long Unknown0; // always 1?
unsigned char Data[4]; // '@t@d' - translates to 'data'?
unsigned short Unknown1; // Something to do with number of objects
unsigned short Max; // Max number of objects the game has reached (slots maybe?)
unsigned short Num; // Number of objects in the current game
unsigned short NextObjectIndex; // Index number of the next object to spawn
unsigned short NextObjectID; // ID number of the next object to spawn
unsigned long FirstObject; // Pointer to the first object in the table
}; // 0x4BB206B4
Code:
struct Object_Table_Array
{
unsigned short ObjectID; // Matches up to Object ID in static player table ( for players )
unsigned short Unknown0;
unsigned short Unknown1;
unsigned short Size; // Structure size
unsigned long Offset; // Pointer to the object data structure
};
You can do a simple loop through the object table to search for certain objects, to grab them all, or whatever else. Example:
Code:
// Declare pointers to our structures
Object_Table_Header * ObjectTableHeader;
Object_Table_Array * ObjectTableArray[2048];
// Assign the static address to the header
ObjectTableHeader = (Object_Table_Header*) 0x4BB206B4;
// Loop through the table and add each array as a new element
for( unsigned short i = 0; i < Object_Table_Header->MaxObjects; i++ )
ObjectTableArray[i] = (Object_Table_Array*)(Object_Table_Header->FirstObject + ( i * Object_Table_Header->Size));
You could do them one at a time instead of creating a huge array of structures. Then you can do what you need to and get out. All depends on what you are developing! =]
I think that about covers the object table! I haven't reversed each object type's structures. I only reversed some of the player data structure. I didn't go too deep because I really didn't need all the extra info. There is a TON of stuff in the structures though including matrices. Just as an example, here's some of the player structure I have reversed, although this is just a small chunk of it:
Code:
struct Dynamic_Player
{
unsigned short MetaIndex;
unsigned short MetaID; // matches against the map's meta table
unsigned char Unknown0[88];
float x; // world coordinate
float y; // world coordinate
float z; // world coordinate
float x2; //movement vector
float y2; //movement vector
float z2; //movement vector
float LegsPitch;
float LegsYaw;
float LegsRoll;
float ScaleX;
float ScaleY;
float ScaleZ;
unsigned char Unknown1[84];
float Health;
float Shield;
unsigned char Unknown2[48];
unsigned short WeaponIndex;
unsigned short WeaponID;
unsigned short VehicleIndex;
unsigned short VehicleID;
unsigned char Unknown3[228];
unsigned long IsInvisible; // normal = 0x41 invis = 0x51 (bitfield?)
unsigned char IsCrouching; // crouch = 1, jump = 2
};
Here's some useful info - the object indexes aren't referenced in the object table. But, this is a very useful number! Instead of looping through the object table again, you can just use the index number as the array element index. Let's assume the WeaponIndex is 8. ObjectTableArray[8] would be the array for the current players' weapon! Pretty nifty huh?
The meta index / id is a reference to the meta index which is in the map file structure. These object structures are actually for the mod2 (models). If you follow the reference, you will find the objects name, tag class heirarchy, spawn location, object type, etc.
Now onto the static player table! In addition to dynamic structures, there are static ones. Ones that are always at the same address in memory, no matter what. The static player structures have less important info and are more of a player index than anything. There is some good information though. Just like the object table, the player table has a header. I won't go into details since I commented it pretty heavily.
Code:
struct Static_Player_Header
{
unsigned char TName[32]; // 'players'
unsigned short MaxSlots; // Max number of slots/players possible
unsigned short SlotSize; // Size of each Static_Player struct
unsigned long Unknown; // always 1?
unsigned char Data[4]; // '@t@d' - translated as 'data'?
unsigned short IsInMainMenu; // 0 = in game 1 = in main menu / not in game
unsigned short SlotsTaken; // or # of players
unsigned short NextPlayerIndex; // Index # of the next player to join
unsigned shoft NextPlayerID; // ID # of the next player to join
unsigned long FirstPlayer; // Pointer to the first static player
}; //0x4BD7AF94
Code:
struct Static_Player
{
unsigned short PlayerID; // Stats at 0x70EC
unsigned short PlayerID2; // ???
wchar_t PlayerName0[12]; // Unicode / Max - 11 Chars + EOS (12 total)
long Unknown0; // Always -1 / 0xFFFFFFFF
unsigned long Team; // 0 = Red / 1 = Blue
unsigned long SwapID; // ObjectID
unsigned short SwapType; // 8 = Vehicle / 6 = Weapon
short SwapSeat; // Warthog - Driver = 0 / Passenger = 1 / Gunner = 2 / Weapon = -1
unsigned long RespawnTimer; // ?????? Counts down when dead, Alive = 0
unsigned long Unknown1; // Always 0
unsigned short ObjectIndexNum;
unsigned short ObjectID; // Matches against object table
unsigned long Unknown3; // Some sort of ID
unsigned long LocationID; // This is very, very interesting. BG is split into 25 location ID's. 1 -19
long Unknown4; // Always -1 / 0xFFFFFFFF
unsigned long BulletCount; // Something to do with bullets increases - weird.
wchar_t PlayerName1[12]; // Unicode / Max - 11 Chars + EOS (12 total)
unsigned long Unknown5; // 02 00 FF FF
unsigned long PlayerIndex;
};
I reversed some of the stats array table. It's not complete unfortunatly. Each array is 48 bytes. Here's what I got:
Code:
struct Stats_Header
{
unsigned long RecordedAnimations; // Pointer to Recorded Animations data table
unsigned char Unknown0[4]; // Zero's
float LastDecalLocation0[12]; // World coordinates of the last bullet/nade hit anywhere on map x,y, applies to BSP only, not objects
unsigned char Unknown1[48]; // Zero's
float LastDecalLocation1[12]; // Same as other one.
float Unknown2[2]; // Some floats. idk.
unsigned char Unknown3[40]; // Zero's
unsigned long DecalIDArray; // Pointer to an array of Decal ID's (correlates with LastDecalLocation)
unsigned long Unknown3; // Pointer
unsigned char Unknown4[20];
unsigned long LocationID0;
unsigned long LocationID1;
unsigned long Unknown5;
unsigned char Unknown6[130]; // Zero's
unsigned long Unknown7; // Pointer
unsigned long Unknown8; // Pointer
}; // 0x006A7DB8
Code:
struct Stats_Array
{
unsigned char IsInGame;
unsigned char Unknown0[5];
unsigned short PlayerID;
unsigned short Kills;
unsigned char Unknown1[6];
unsigned short Assists;
unsigned char Unknown2[6];
unsigned short Betrayed; // Suicides count!
unsigned short TotalDeaths; // Everytime you die, no matter what..
unsigned short Suicides;
unsigned short FlagSteals;
unsigned short FlagReturns;
unsigned short FlagScores;
unsigned char Unknown3[12];
}; //size - 48 bytes 0x30 starts at 0x006A7F30
Code:
struct Stats_Footer
{
unsigned long RedFlag; // Pointer to scenario meta data
unsigned long BlueFlag; // Pointer to scenario meta data
unsigned short RedFlagIndex; // Object Index #
unsigned short RedFlagID; // Object ID
unsigned short BlueFlagIndex; // Object Index #
unsigned short BlueFlagID; // Object ID
unsigned long BlueFlagScores; // # of flags captured
unsigned long RedFlagScores; // # of flags captured
unsigned long FlagCaptureLimit; // Num of flags to capture to win
bool RedFlagStolen; // 0 - At base 1 - Flag is stolen
bool BlueFlagStoled; // 0 - At base 1 - Flag is stolen
unsigned short Unknown; // Zero's?
unsigned long RedFlagTimer; // Respawn time?
unsigned long BlueFlagTimer; // Respawn time?
}; // 0x006A8230
Now for some map file reversing! =] I am currently working on an in-game level editor. It's pretty much like Spark Edit, but live, during game. I'm using all these structures and the map file structures to accomplish this + some 3d world -> 2d screen space math.
The map header sets up the whole map file. The TagIndexOffset is a pointer to the meta index. The meta index is just like the object table, but a different format. The meta index has all of the elements of the game including objects, sounds, bitmaps, shaders, collision, hud interface, fonts, etc. Pretty much everything you would see in HMT. The meta array's in the index contain the tag class hierarchy, a pointer to the metas name, a pointer to the meta structure and most important our meta ID. Remember the object structures? The first thing in an object structure is the meta ID which we can match up here.
The first thing in the meta index is the header. This is also a VERY important piece of the puzzle, because it sets up more parts of the map. I won't go into details. The first tag in the index is the scnr also know as the 'Scenario'. This contains a utopia of reflexives. A reflexive is just a type of array. It points to a structure with extra information. Our meta data structures all have reflexives too. It is just extra info about that tag. The scenario has all our spawns and triggers. It holds all the spawn points of every object, player, and gametype flags (flags, oddballs, koth, race, etc).
The whole entire map file is loaded into memory, but it is split into sections throughout memory. Fortunatly they are static. So without boring you any longer with all this information, here are the map structures I have reversed:
Code:
struct sMapHeader
{
unsigned short Unknown;
unsigned short Version; // 5 = Xbox, 6 = Trial, 7 = PC, 609 = CE
unsigned char Unknown2[700];
unsigned long Header; // 'head''Ehed'
unsigned long TagIndexMetaLength;
unsigned char BuildDate[32]; // Year.Month.Day.Build - I guess they use this to make sure that a certain build will only open that build's map files, because this string is in the app too
unsigned char Unknown3[672];
unsigned long MapType; // 0 = singleplayer, 1 = multiplayer, 2 = ui - this also determines the size of the cache file. UI = 35MB, multiplayer = 47MB, and singleplayer = 270MB
unsigned char MapName[32];
unsigned char Unknown4[60];
unsigned long DecompLen; // Actual len of decompressed data. Halo sticks garbage on the end so that the file is one of several fixed sizes (35, etc).
unsigned long TagIndexOffset;
unsigned long Footer; // 'foot' 'Gfot'
}; // 0x006A2000 - Trial Bloodgulch Base
Code:
struct sIndexHeader
{
unsigned long IndexMagic;
unsigned long BaseIdent;
unsigned long Unknown;
unsigned long NumOfTags;
unsigned long VertexObjectCount;
unsigned long ModelRawDataOffset;
unsigned long IndicesObjectCount;
unsigned long IndicesOffset;
unsigned long ModelRawDataSize;
}; // 0x4BF10000 - Trial Bloodgulch Base
Code:
struct sTagIndex
{
unsigned char TagClass[3][4];
unsigned long TagID;
TagName * TName;
unsigned long Offset;
unsigned long Zeros[2];
}; // Starts at 0x4BF10028 - Trial Bloogulch Base
Code:
struct sTagName
{
unsigned char Name[1];
}; // Starts at 0x4BF22D68 - Trial Bloodgulch Base
Code:
struct sScnrHeader
{
unsigned char unk_str1[16];
unsigned char unk_str2[16];
unsigned char unk_str3[16];
Reflexive SkyBox;
unsigned long unk1;
Reflexive ChildScenarios;
unsigned long unneeded1[46];
unsigned long EditorScenarioSize;
unsigned long unk2;
unsigned long unk3;
unsigned long pointertoindex;
unsigned long unneeded2[2];
unsigned long pointertoendofindex;
unsigned long zero1[57];
Reflexive ObjectNames;
Reflexive Scenery; //==============
Reflexive SceneryRef;//=================
Reflexive Biped;
Reflexive BipedRef;//===================
Reflexive Vehicle;//=============
Reflexive VehicleRef;//=================
Reflexive Equip;
Reflexive EquipRef;
Reflexive Weap;
Reflexive WeapRef;
Reflexive DeviceGroups;
Reflexive Machine;
Reflexive MachineRef;
Reflexive Control;
Reflexive ControlRef;
Reflexive LightFixture;
Reflexive LightFixtureRef;
Reflexive SoundScenery; //===============
Reflexive SoundSceneryRef; //=================
Reflexive Unknown1[7];
Reflexive PlayerStartingProfile;
Reflexive PlayerSpawn;//==============
Reflexive TriggerVolumes;
Reflexive Animations;
Reflexive MultiplayerFlags;//==============
Reflexive MpEquip;//===============
Reflexive StartingEquip;//===============
Reflexive BspSwitchTrigger;
Reflexive Decals;//================
Reflexive DecalsRef;//===============
Reflexive DetailObjCollRef;
Reflexive Unknown3[7];
Reflexive ActorVariantRef;
Reflexive Encounters;
//below this, structs still not confirmed
Reflexive CommandLists;
Reflexive Unknown2;
Reflexive StartingLocations;//===============
Reflexive Platoons;
Reflexive AiConversations;
unsigned long ScriptSyntaxDataSize;//=============
unsigned long Unknown4;
Reflexive ScriptCrap;
Reflexive Commands;
Reflexive Points;
Reflexive AiAnimationRefs;
Reflexive GlobalsVerified;
Reflexive AiRecordingRefs;
Reflexive Unknown5;
Reflexive Participants;
Reflexive Lines;
Reflexive ScriptTriggers;
Reflexive VerifyCutscenes;
Reflexive VerifyCutsceneTitle;
Reflexive SourceFiles;
Reflexive CutsceneFlags;
Reflexive CutsceneCameraPoi;
Reflexive CutsceneTitles;
Reflexive Unknown6[8];
unsigned long Unknown7[2];
Reflexive StructBsp;
}; // 0x4BF3EDA0 - Trial Bloodgulch Base
Code:
struct Reflexive
{
unsigned long Count;
unsigned long Offset;
unsigned long Zeros;
};
Code:
struct _PlayerSpawn
{
float x;
float y;
float z;
float Yaw;
unsigned long Team;
unsigned long Unknown[8];
}
Recently I have looked through some Halo 2 structures. They follow the same format, but are slightly modified. I know there is extra info in the static player table and the object table looks exactly the same. This can make your applications easily portable between versions.
I think I have gone over enough material for now. I will be updating. Hope you learned something from this! =]
Here are some examples of stuff I'm working through my research:
http://www.xfire.com/video/13f09/
http://www.xfire.com/video/13944/