peterthepigeon Posted July 28, 2013 Share Posted July 28, 2013 A repost of what I posted on dune2k forum. From start to finish? Maybe 4 hours of on and off work to put it all together. Nyerguds weapons.ini had the indices which made testing easier. So let's get to work. Where is the weapons' data stored? You can find it in the .data section of c&c, specifically in C&C95, at address 0x5034D8. 0044114A |. 8A84C1 D834500>MOV AL,BYTE PTR DS:[ECX+EAX*8+5034D8] To walk the entries, it's important to remember there are a total of 24(?) weapons. #define MAX_WEAPONS 24 typedef struct { BYTE m_bProjectile; BYTE m_bDamage; BYTE m_bRateOfFire; DWORD m_dwRange; // BYTE m_bRange * 0x100, bRange = m_bRange >> 8; BYTE m_bReportSound; }Weapon_t; Weapon_t* pWeaponBase = ( Weapon_t* )0x5034D8; for ( iCounter = 0; iCounter < MAX_WEAPONS; iCounter++ ) { SetupWeapon ( &pWeaponBase [ iCounter ], iCounter ); } Keep in mind that Rate of Fire has 3 added to it by the game. To truly have it set to 0 you either: C&C95 location: 004B1766 004B1766 |. 83C0 03 ADD EAX,3 1) Byte patch 1a) Byte patch immediate to 0 2) Set Rate of Fire to FD to cause overflow. This works because game only uses bytes for weapon data as documented above. Link to comment Share on other sites More sharing options...
peterthepigeon Posted July 28, 2013 Author Share Posted July 28, 2013 Continuing on, there are a max of 18 projectiles. 004B62FD |. 8B1495 BC295000 MOV EDX,DWORD PTR DS:[EDX*4+5029BC] 5029BC is the projectile base. Projectile 00 - 50cal Projectile 01 - 50cal Projectile 02 - 120mm Projectile 03 - 120mm Projectile 04 - DRAGON Projectile 05 - DRAGON Projectile 06 - MISSILE Projectile 07 - DRAGON Projectile 08 - FLAME Projectile 09 - FLAME Projectile 10 - BOMBLET Projectile 11 - BOMB Projectile 12 - Laser Projectile 13 - ATOMICUP Projectile 14 - ATOMICDN Projectile 15 - MISSILE Projectile 16 - 50cal Projectile 17 - GORE Projectile 18 - CHEW Link to comment Share on other sites More sharing options...
Nyerguds Posted July 29, 2013 Share Posted July 29, 2013 The struct for weapons is not exactly correct. Those m_bUnknown/m_bRange/m_bUnknown2/m_bUnknown3 bytes are actually simply one dword containing one value, which is the weapon range * 100h. Note that the Bullet types (projectiles) aren't really identifiable by those names, since those are just the ptrs to the filename strings of the SHP graphics they use. They can't really be uniquely identified by anything except ID, unless they're given new names. I've got a list of those, with better names, in my bullets.ini, by the way. Link to comment Share on other sites More sharing options...
peterthepigeon Posted July 29, 2013 Author Share Posted July 29, 2013 The struct for weapons is not exactly correct. Those m_bUnknown/m_bRange/m_bUnknown2/m_bUnknown3 bytes are actually simply one dword containing one value, which is the weapon range * 100h. Note that the Bullet types (projectiles) aren't really identifiable by those names, since those are just the ptrs to the filename strings of the SHP graphics they use. They can't really be uniquely identified by anything except ID, unless they're given new names. I've got a list of those, with better names, in my bullets.ini, by the way. I reversed the struct that way because I felt it would make things clearer. The bitshifting done by the game to obtain data is a bit odd. As far as the naming convention goes, I find it odd that WW didn't think to more descriptively name their files. Also the range is just a BYTE which is bitshifted out of the DWORD. Link to comment Share on other sites More sharing options...
Nyerguds Posted July 29, 2013 Share Posted July 29, 2013 Uhh, I don't think they use any bit shifting for that value... the function at 0x004B1F78 is the weapons range getter. With the struct properly configured, it gives this code: 004B1FA5 mov eax, array_weapons.Range_x100h[ebx+eax*8] 004B1FC0 mov eax, array_weapons.Range_x100h[ebx+eax*8] (with the *8 being there because that's the length of the struct) Link to comment Share on other sites More sharing options...
peterthepigeon Posted July 29, 2013 Author Share Posted July 29, 2013 Uhh, I don't think they use any bit shifting for that value... the function at 0x004B1F78 is the weapons range getter. With the struct properly configured, it gives this code: 004B1FA5 mov eax, array_weapons.Range_x100h[ebx+eax*8] 004B1FC0 mov eax, array_weapons.Range_x100h[ebx+eax*8] (with the *8 being there because that's the length of the struct) 004B0E52 |. 89C2 MOV EDX,EAX 004B0E54 |. C1FA 1F SAR EDX,1F 004B0E57 |. C1E2 08 SHL EDX,8 004B0E5A |. 1BC2 SBB EAX,EDX 004B0E5C |. C1F8 08 SAR EAX,8 SAR EAX, 8 Link to comment Share on other sites More sharing options...
Nyerguds Posted July 29, 2013 Share Posted July 29, 2013 I'm... not exactly sure where this fits in with the range calling code, since I can't trace the "call dword ptr [ebx+168h]" stuff without debugging.... but that seems to be related to the fact that the stored value is the range x100h They probably need to shift it down at some point to do by-cell range calculations, or something. Link to comment Share on other sites More sharing options...
peterthepigeon Posted July 29, 2013 Author Share Posted July 29, 2013 I'm... not exactly sure where this fits in with the range calling code, since I can't trace the "call dword ptr [ebx+168h]" stuff without debugging.... but that seems to be related to the fact that the stored value is the range x100h They probably need to shift it down at some point to do by-cell range calculations, or something. Right on the money. Now do you see why I did it as BYTE? I think at any rate, now that I think about it. Maybe they wanted to use bitshifting to pack data in the struct? Link to comment Share on other sites More sharing options...
Nyerguds Posted July 29, 2013 Share Posted July 29, 2013 It's clearly stored as dword in the struct though. There's no need to treat it as anything else when it's clearly retrieved as dword, and the data in the structs clearly shows that it's indeed just the range accurate to two figures after the hexadecimal point, and, to store that as pure int, simply multiplied by 100h. The only thing that really matters in a struct is how data is stored and retrieved. What exactly that data is irrelevant; only the grouping really counts. Well, unless they're bit flags or something, in which case you could define them as boolean bits in a theoretical struct (though not in a real implementation), but that's not the case here at all. Well, anyway, I'm not going to argue with you about what appears to be a "style choice" Link to comment Share on other sites More sharing options...
peterthepigeon Posted July 30, 2013 Author Share Posted July 30, 2013 Quibbling over style is what OCD infested people do Link to comment Share on other sites More sharing options...
Nyerguds Posted July 30, 2013 Share Posted July 30, 2013 My point is mainly just that these things are not "unknown". Especially not the first unknown, which is the fraction of the range. Link to comment Share on other sites More sharing options...
peterthepigeon Posted July 30, 2013 Author Share Posted July 30, 2013 My point is mainly just that these things are not "unknown". Especially not the first unknown, which is the fraction of the range. I've reversed games where they've done similar things to this, but the data was packed in HIWORD and LOWORD, or they would pack the data as DWORD and shift it out. I've amended it, but at the end of the day we can both say that it's stored as a DWORD but used as a BYTE. Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now