Why is the address offset of Item ModelID 44 and not 42?

05/25/2018 06:19 3vangelist#1
EXAMPLE:

Code:
Local $interaction = DllStructGetData($aItemStruct,'Interaction')
...fetches the value for the Iteration as defined when the struct was created. If we read through the struct...

Code:
long id;long agentId;byte unknown1[4];ptr bag;ptr modstruct;long modstructsize;ptr customized;byte unknown2[4];byte type;byte unknown3;short extraId;short value;byte unknown4[2];short interaction;long modelId;ptr modString;byte unknown5[4];ptr NameString;byte unknown6[15];byte quantity;byte equipped;byte unknown7[1];byte slot
...and count the byte offset for each data type as described [Only registered and activated users can see links. Click Here To Register...], the "Iteration" value is found in offset 40. This means that if we had a POINTER to the item instead of a struct, we can call...

Code:
Local $interaction = MemoryRead($aItemPtr + 40,'short')
...to get the same value. This is fine, and it works.

THE PROBLEM:

I'm trying to programatically figure out the offset (and type) based on the above Struct string, but its not working for ModelID. According to the above method, ModelID is directly after Interaction, meaning an offset of 42, right?

Well its actually 44, and I don't know where the extra 2 bytes have come from - if we added another "byte unknown[2]" in there, then the DllStruct itself would be wrong.

Thanks for reading, any help or explanation on this one would be appreciated!
05/25/2018 10:18 DerMoench14#2
Well Offset 40 (0x28) is right for Interaction but the size is 4 Bytes.
Code:
		WORD ExtraId;						// 0x22
		WORD Value;						// 0x24
		BYTE h0026[2];						// 0x26
		DWORD Interaction;					// 0x28
		DWORD ModelId;						// 0x2C
I think the "short" in the struct has been set when only Flags < 0x10000 have been discovered.
But you did a math-error anywhere else :)
05/25/2018 13:32 3vangelist#3
Starting to understand the error, thanks.

How does AutoIt know that Interaction is a 4 byte DWORD and that ExtraID is a 2 byte WORD? They're both defined as being "short" data type in the code

Update: Is it because the "long" of ModelID needs to be at an offset of a multiple of 4 bytes? i.e. because 4 doesn't divide into 42, the offset needs to be incremented until it can fit?

Below is my function that seems to be working - is this just coincidence, or does that solve the issue for other structs in a similar layout?

Code:
; Item Struct Info
Local $mItemStructStr = 'long id;long agentId;byte unknown1[4];ptr bag;ptr modstruct;long modstructsize;ptr customized;byte unknown2[4];byte type;byte unknown3;short extraId;short value;byte unknown4[2];short interaction;long modelId;ptr modString;byte unknown5[4];ptr NameString;byte unknown6[15];byte quantity;byte equipped;byte unknown7[1];byte slot'
Local $mItemStructSize = DllStructGetSize(DllStructCreate($mItemStructStr))
DeclareStructOffsets($mItemStructStr,'mItemStructInfo_')

Func DeclareStructOffsets($aStructString,$aVarPrefix) ; NOTE: Struct elements MUST have names for this function to work properly!
	Local $lSplit = StringSplit($aStructString,';'), $lSplit2,$lElementName,$lElementType,$lElementOffset=0,$lElementSize=1, $lArrayMatch, $lDebug=1
	Local $lDebugArr=''
	For $i=1 to $lSplit[0]
		$lSplit2 = StringSplit($lSplit[$i],' ') ; Split on type and name
		$lArrayMatch = StringRegExp($lSplit[$i],"\[([0-9]+)\]$",2) ; Is this type an array of values?
		$lElementType = StringRegExpReplace($lSplit2[1],"\[[0-9]+\]$",'') ; byte[10] = byte (remove array declaration)
		$lElementName = StringRegExpReplace(($lSplit2[0] > 1 ? $lSplit2[2] : ''),"\[[0-9]+\]$",'') ; unknown2[4] = unknown2 (remove array declaration)
		; Calculate offset.
		$lElementSize=1
		Switch $lElementType ; Add other case statements when relevent
			Case 'long','ptr','float'
				$lElementSize=4
			Case 'short'
				$lElementSize=2
		EndSwitch
		While Mod( $lElementOffset , $lElementSize ) > 0 ; Make sure the offset is valid for this size i.e. long datatype needs to be within multiple of 4 (e.g. 42 is invalid, 44 is OK)
			$lElementOffset+=1		
		WEnd 
		If UBound($lArrayMatch) Then ; This is an array of values.
			$lElementSize *= Number($lArrayMatch[1]) ; Multiply by array count.
			$lElementType &= $lArrayMatch[0] ; Add the array count to the type field.
		EndIf
		If $lElementName Then ; Element has a name - declare it as a global variable using the prefix.
			Local $lElementArr[2] = [$lElementType,$lElementOffset]
			Assign($aVarPrefix&$lElementName,$lElementArr,2) ; Assign this definition to global variable. Used later for GetItemProperty etc.
			If $lDebug Then $lDebugArr &= $aVarPrefix&$lElementName&' , '&$lElementType&' , '&$lElementOffset&@CRLF
		EndIf
		$lElementOffset+=$lElementSize
	Next
	If $lDebug Then
		MsgBox(0,'DeclareStructOffsets for '&$aVarPrefix,$lDebugArr)
	EndIf
EndFunc
05/25/2018 16:22 DerMoench14#4
Quote:
Originally Posted by 3vangelist View Post
Starting to understand the error, thanks.

How does AutoIt know that Interaction is a 4 byte DWORD and that ExtraID is a 2 byte WORD? They're both defined as being "short" data type in the code
Well ... i found a interesting Bug in GWA2 :p
Have a look on the Item-Struct and you'll see this:
Code:
Local $lItemStruct = DllStructCreate('long id;long agentId;byte unknown1[4];ptr bag;ptr modstruct;long modstructsize;ptr customized;byte unknown2[4];byte type;byte unknown3;short extraId;short value;byte unknown4[2];short interaction;long modelId;ptr modString;byte unknown5[4];ptr NameString;byte unknown6[15];byte quantity;byte equipped;byte unknown7[1];byte slot')
In a readable Format it looks:
Code:
0x0	long id;
0x4	long agentId;
0x8	byte unknown1[4];
0xC	ptr bag;
0x10	ptr modstruct;
0x14	long modstructsize;
0x18	ptr customized;
0x1C	byte unknown2[4];
0x20	byte type;
0x21	byte unknown3;
0x22	short extraId;
0x24	short value;
0x26	byte unknown4[2]; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
0x28	short interaction;
0x2A	long modelId;
0x2E	ptr modString;
0x32	byte unknown5[4];
0x36	ptr NameString;
0x3A	byte unknown6[15];
0x49	byte quantity;
0x4A	byte equipped;
0x4B	byte unknown7[1];
0x4C	byte slot')
This struct becomes wrong where i added the <<<<<<
Code:
0x26	byte unknown4[2];
must be
Code:
0x26	byte unknown4[4];
So the correct struct should be (1st Correction):
Code:
0x0	long id;
0x4	long agentId;
0x8	byte unknown1[4];
0xC	ptr bag;
0x10	ptr modstruct;
0x14	long modstructsize;
0x18	ptr customized;
0x1C	byte unknown2[4];
0x20	byte type;
0x21	byte unknown3;
0x22	short extraId;
0x24	short value;
0x26	byte unknown4[4];
0x2A	short interaction;
0x2C	long modelId;
0x30	ptr modString;
0x34	byte unknown5[4];
0x38	ptr NameString;
0x3C	byte unknown6[15];
0x4B	byte quantity;
0x4C	byte equipped;
0x4D	byte unknown7[1];
0x4E	byte slot')
The 2nd Correction is to change
Code:
0x26	byte unknown4[4];
0x2A	short interaction;
with
Code:
0x26	byte unknown4[2];
0x28	long interaction;
because Interaction Flags are way higher than 0x10000.

The fun Fact the struct was still valid is that the next entry "0x2C long modelId;" will be at a adress that can be divided by 4 what you've found out correctly :)
For users who still grab data with "DllStructGetData" it doesn't matter ... but if you try to get the correct Offset of the entry it will be ridiculous angry when they receive invalid data.
05/25/2018 16:31 3vangelist#5
Well with your last post and finding out about the modulus thing, this makes sense now - I'll leave the modulus check in my function just in case, and I'll update the struct string for my next GWA2 pull request.

Thanks for taking the time :)