Reverse engineering JABIA items
Having reverse engineered the character data structure and how weapon durability is calculated, I wanted to reverse engineer data structures of weapons, ammunition and items. These are defined in text files like weapons.txt, ammo.txt and items.txt.
Reconnaissance
I know that the maximum weapon durability of the currently used weapon is accessed here:
.text:005420C4 cvtsi2ss xmm0, dword ptr [eax+0Ch] ; get max durability of weapon and convert it to float
So, eax points to weapon data structure, with the durability 0xC bytes from the begging of the structure.
I set the merc's weapon to FN FAL. Using cheat engine, I started dissecting the data structure, comparing the values I saw to one's found in weapons.txt.
Here is what cheat engine found automatically:
Looking at which function access the weapon id, I saw this:
00431554 - 0FB7 4E 04 - movzx ecx,word ptr [esi+04]
004240B4 - 0FB7 48 04 - movzx ecx,word ptr [eax+04]
00417258 - 0FB7 4A 04 - movzx ecx,word ptr [edx+04]
00417280 - 0FB7 49 04 - movzx ecx,word ptr [ecx+04]
So, weapon data structure starts 4 bytes before the id. To determine the size of the data structure, I added a few different offsets to the weapon pointer till I could see the ID of the next weapon. I found that it is 128 bytes long.
Once weapons are parsed from config files, their addresses don't change. At this point, I then started pocking around with the unknown fields. Here are some of the things I did:
- Set two weapons to have the same exact configs. Only the thing that changed was the last pointer.
- Set two weapons to be the same except for one not being deliverable. Figured out which was deliverable bit.
- Set two weapons to be the same except for one with DisableAttachments. Figured out which was disalbeattachments bit.
Also, weapons.txt had some useful comments. This led me to figure out the Armament field.
#define LAUNCHER 0
#define RIFLE 1
#define HANDGUN 2
#define KNIFE 3
#define NONE 4
Ammunition types
#define NONE 0
#define 357cal 1
#define 38cal 3
and so on
Here is the more complete dissection of the data structure:
Experimenting further, I kept on adding 128 bytes to the offset until I went pas the weapons. Turns out, right after the weapons lie attachments, then clothing, then items, then ammo, then consumables, then collectibles.
typdef struct Attachment {
uint32_t Class; // = 4 for attachments, 0 for weapons
uint32_t ID;
uint32_t ResourceID;
bool Deliverable;
uint32_t Price;
uint32_t Weight;
uint32_t unknown;
uint32_t ui_equipment_icons_# (Icon);
uint16_t icon_x_upper_left;
uint16_t icon_y_upper_left;
uint16_t icon_x_lower_right;
uint16_t icon_y_lower_right;
uint32_t ui_equipment_pictures_# (Picture);
uint16_t picture_x_upper_left;
uint16_t picture_y_upper_left;
uint16_t picture_x_lower_right;
uint16_t picture_y_lower_right;
uint32_t Armament;
uint32_t Accuracy;
uint32_t Silencing;
uint32_t Aimtime;
... enough to fill 128 bytes
}
enum Slots {Cap=0, Attachment=1, Glasses=2, Torso=3, Vest=4, Legs=5, Feet=6}
enum Property {NightVision=0, GasProtection=1}
typdef struct Clothing {
uint32_t Class; // = 2 for cloths
uint32_t EquipmentId;
uint32_t ResourceID;
bool Deliverable;
uint32_t Price;
uint32_t Weight;
uint32_t unknown;
uint32_t ui_equipment_icons_# (Icon);
uint16_t icon_x_upper_left;
uint16_t icon_y_upper_left;
uint16_t icon_x_lower_right;
uint16_t icon_y_lower_right;
uint32_t ui_equipment_pictures_# (Picture);
uint16_t picture_x_upper_left;
uint16_t picture_y_upper_left;
uint16_t picture_x_lower_right;
uint16_t picture_y_lower_right;
Slot Slot
uint32_t Armor;
uint32_t CamoNight;
uint32_t CamoUrban;
uint32_t CamoWoods;
uint32_t CamoDesert;
Property Property;
... enough to fill 128 bytes
}
enum ItemTypes = {Medikit=0, Grenade=1, Mine=2, Defuse=3, Explosive=4, Opener=5, Tool=6}
typdef struct Items {
uint32_t Class; // = 3 for items
uint32_t EquipmentId;
uint32_t ResourceID;
bool Deliverable;
uint32_t Price;
uint32_t Weight;
uint32_t unknown;
uint32_t ui_equipment_icons_# (Icon);
uint16_t icon_x_upper_left;
uint16_t icon_y_upper_left;
uint16_t icon_x_lower_right;
uint16_t icon_y_lower_right;
uint32_t ui_equipment_pictures_# (Picture);
uint16_t picture_x_upper_left;
uint16_t picture_y_upper_left;
uint16_t picture_x_lower_right;
uint16_t picture_y_lower_right;
ItemTypes ItemTypes
SubType SubType;
// SubTypes:
// for Medikit hp (1) bleeding (2) wounds (3)
// for Grenade smoke (1), stun (2), frag (3), gas (4)
// for C4 timer (1), remote (2), clock (3), remote control (4)
// for Openers key (1), crowbar (2), lockpick (3)
uint32_t Charges;
uint32_t Requirement;
uint32_t Efficiency;
float Range; // in decimeter, default 1
... enough to fill 128 bytes
}
hehe, can get a list of all the keys :)
typdef struct Ammo {
uint32_t Class; // = 1 for ammo
uint32_t EquipmentId;
uint32_t ResourceID;
bool Deliverable;
uint32_t Price;
uint32_t Weight;
uint32_t unknown;
uint32_t ui_equipment_icons_# (Icon);
uint16_t icon_x_upper_left;
uint16_t icon_y_upper_left;
uint16_t icon_x_lower_right;
uint16_t icon_y_lower_right;
uint32_t ui_equipment_pictures_# (Picture);
uint16_t picture_x_upper_left;
uint16_t picture_y_upper_left;
uint16_t picture_x_lower_right;
uint16_t picture_y_lower_right;
uint32_t Ammunition;
uint32_t unknown;
uint32_t Projectiles;
uint32_t BoxSize;
float ArmorFactor;
... enough to fill 128 bytes
}
enum Type {Food=0, Drink=1};
enum FoodSubType {Fruits=5, Meats=20, Cereals=5, Vegetables=5, Sweets=10};
enum DrinkSubType {Softdrink=3, Alcohol=6};
typdef struct Ammo {
uint32_t Class; // = 5 for consumable
uint32_t EquipmentId;
uint32_t ResourceID;
bool Deliverable;
uint32_t Price;
uint32_t Weight;
uint32_t FoodSubType;
uint32_t ui_equipment_icons_# (Icon);
uint16_t icon_x_upper_left;
uint16_t icon_y_upper_left;
uint16_t icon_x_lower_right;
uint16_t icon_y_lower_right;
uint32_t ui_equipment_pictures_# (Picture);
uint16_t picture_x_upper_left;
uint16_t picture_y_upper_left;
uint16_t picture_x_lower_right;
uint16_t picture_y_lower_right;
uint32_t Type;
uint32_t Stacksize;
int32_t Efficiency;
... enough to fill 128 bytes
}
typdef struct Collectible {
uint32_t Class; // = 6 for collectible
uint32_t EquipmentId;
uint32_t ResourceID;
bool Deliverable;
uint32_t Price;
uint32_t Weight;
uint32_t StackSize;
uint32_t ui_equipment_icons_# (Icon);
uint16_t icon_x_upper_left;
uint16_t icon_y_upper_left;
uint16_t icon_x_lower_right;
uint16_t icon_y_lower_right;
uint32_t ui_equipment_pictures_# (Picture);
uint16_t picture_x_upper_left;
uint16_t picture_y_upper_left;
uint16_t picture_x_lower_right;
uint16_t picture_y_lower_right;
uint32_t StackSize;
uint8_t ConvertToMoney;
... enough to fill 128 bytes
}