Skip to content

Reverse engineering JABIA items

Stan Bobovych edited this page Mar 28, 2014 · 17 revisions

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: weapon_struct

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.

clothing dissect

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
}