Author Topic: Though of the moment  (Read 3388 times)

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Though of the moment
« on: December 10, 2004, 02:39:02 AM »
Well, I don't really want to wait until I've refined my knowledge on this stuff so here it goes.


I've figured out what a few of the (internal) unit vtbl entries do. One of them is for drawing the unit to the screen. The unit is responsible for drawing itself. I (RET 4)ed the routine and units driving from that base type became invisible.

Heh, kindof an interesting way of visually inspecting the unit hierarchy. Anyways, it seems all the base buildings units might derive from the same base class while all the tanks derive from another. Turrets are seperate from the rest of the vehicles/buildings too.

Anyways, looks kinda funny to see the unit selection boxes ghostly slide around the map. The rollover help also still shows so you can still see enemy units.

I've also found out which routine controls firing weapons. It's led me to discover one of the flag bits is used to control the dual turret fire rate. (bitmaks 0x20). By setting it, Lynxes, Panthers, and GPs will all fire as fast as Tigers. The graphics doesn't change though, nor does the source of the weapon fire slide a few pixels each time the weapon is fired.

When I changed the vtbl of the Panther to match that of the Tiger, it's graphics changed to that of a tiger too (including dual turret). I left all the unit data the same though. Guess it's somehow hardcoded into one of those routines, or a least hardcoded based on the enum map_id value for the unit (which is essentially hardcoded into one of the vtbl functions).


Well, here's a quick description of a few I know.
Vtbl entry #   Description
--------------   ------------
0   UnitTypeInfo* GetUnitTypeInfo()
4   int FireWeapon()
6   void DrawUnit()
15 int GetUnitTypeName(char *destBuffer, int bufferSize)
27 void GetLocation(unknown, unknown, int *tileX, int *tileY)

Also, weapon fire also counts as a unit internally to OP2. So if you were to fire a laser weapon, the FireWeapon routine will call an internal CreateUnit function and pass it a value of map_Laser as the type of unit to create.

The GetUnitTypeName controls the name that appears in rollover text.

I may have mentioned the GetUnitTypeInfo function before. That'll return a UnitTypeInfo structure (I might have named it slightly simpler before). From that structure, you can read all the info for that type of unit. Including the enum map_id. By replacing that function (or the vtble and hence that function) you change the unit's type.


So, any thoughts for new units? Disasters!  (thumbsup)
 

Offline BlackBox

  • Administrator
  • Hero Member
  • *****
  • Posts: 3093
Though of the moment
« Reply #1 on: December 10, 2004, 02:38:57 PM »
Pretty interesting so far. The way it draws units seems sorta dumb (lot of duplicate code?) but, heh, if we wanted to we could change it.

Also interesting that it creates a unit internally for fire. (But that makes sense, I think most other RTS'es do it that way, or not far from it)

Is the derived unit class responsible for actually loading the graphics and blitting ? or does it simply hand the work off to the game again?

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Though of the moment
« Reply #2 on: December 10, 2004, 09:49:22 PM »
Quote
Is the derived unit class responsible for actually loading the graphics and blitting ? or does it simply hand the work off to the game again?
I don't know enough about it yet to answer that. Maybe in the next few days.

That reminds me, I'm probably gonna have to figure out how the .prt files work. Maybe write a specification for that format too.

Anyways, the unit class does make a call to some code and I'm pretty sure it's that call that handles the actual drawing, so it may largely be handled by the game engine.

Also, there isn't really must wasted/duplicate code. Most units share the same code and it seems the drawing is handled by a function which they might all call. I haven't yet compared the different routines much but I think it was the same function. Anyways, one of the beautiful things about vtbls is that even if it's for a different class, they can share the same addresses for functions if the code is the same. Plus it avoids having to implement really generic or slow code for things that don't need it. Although, the way it's done, some of it is kinda wasteful. Like how every unit type needs it's own vtbl to properly handle that first function.


This is all good news for us though since we can always replace functions we want. Heck, to derive from one of the existing units, we create our own vtbl, copy the entries of the one we are deriving from, and selectively change the addresses of the routines we want to change/rewrite. All of our data and functions can reside in our own DLL, say the level DLL. The vtbl would exists in the DLL's memory, but initialized by a memcopy from the exe's address space. It's actually dirt simple in some ways.



Edit: All the drawing functions call the same function internally. Address 0x403F20 seems to be the common routine that they all call. I'll work on it a little later.
 
« Last Edit: December 10, 2004, 09:57:17 PM by Hooman »

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Though of the moment
« Reply #3 on: December 11, 2004, 09:47:36 PM »
Ok, said routine handles drawing unit graphics. For vehicles, there is a call for the chasis of the vehicle followed by a call for the turret. Changing the values can also make the unit look like a building. As a simple edit, I changed the push register to the one holding the y coordinate and watched the unit go through some interesting animations while moving up and down.  (Blowing up, becoming a damaged building, etc.) :)

I've also found out from the graphics routine a few other things. It uses the same call to display the sticky foam and the EMP animation and that display was controlled by two flags. I've also found that both stickyfoam and EMP have seperate timers in the Unit record.

offset  size  Description
------  -----  -------------
0x4E    0x2   timerStickyfoam - number of game ticks before stickyfoam wears off
0x50   0x2   timerEMP - number of game ticks before EMP wears off

bit flags (at offset 0x44)
---------
bit #   bitmask   Description
-----    ---------   ------------
0x12    0x00040000    IsStickyfoamed - Set if unit is locked down by stickyfoam (Also needs timer set properly, timer>0)
0x13    0x00080000    IsEMPed - Set if unit is disabled by EMP (timer will cound down until worn off)


Note: There is a difference in how the timer values are handled between stickyfoam and EMP. It appeasr the EMP timer is handled properly as an unsigned value but the stickyfoam timer needs to be a positive signed short. Also, if the timer is initially zero when the flag is set, the EMP timer will wrap around and the unit will be EMPed for a very long time. But for stickyfoam, the timer wraps around to a garbage negative number and causes graphics glitches. (My Lynx looked like a university with a turret). You also lose control of the unit and the game may eventually crash.


As for that graphics function, there are 6 arguments. I've figured out a few of them.
0x403F20 DrawGraphic(int graphic, int unknown1, int pixelX, int pixelY, int unknown2, int unknown3)

Note: The pixelX and pixelY values are relative to the upper left corner of the map, and not the upper left corner of the visible portion of the map. Hence they should be treated as absolute map coordinates and not relative to the view pane.

I also suspect which graphic in an animation sequence in controlled by one of the parameters. Most likely unknown1 if that is the case. I'll look into it eventually.





The Vol loading code contains a bug! For the Index section ("voli"), it calculates the number of index entries by dividing the section size by 14 (sizeof(IndexEntry) = 14). It then allocates numIndexEntries*sizeof(IndexEntry) bytes to store the data. But, the problem is, it still reads in sectionSize bytes. This is actually an issue since the entries are not DWORD size aligned and the original Vol files contain an odd number of index entries in some of them. (Like in Sheets.vol, 15 entries). In those cases, it'll read more bytes than it's allocated buffer space for. In this case, it only reads an extra 2 bytes but it could read up to 13 bytes past the end of the buffer.

Well, looks like there is another possible exploit.  :ph34r: Actually, you probably can't do much with this one since it probably just overwrites unallocated memory that really means nothing anyways. But still, it has the potential to cause problems.

Edit: My bad. The extra byets are padding bytes and the section size doesn't include them (Section size IS a multiple of 14). So there shouldn't be any problems..., with a properly structured file. If the section size isn't a multiple of 14 then that problem will exist.
 
« Last Edit: December 11, 2004, 11:25:05 PM by Hooman »

Offline BlackBox

  • Administrator
  • Hero Member
  • *****
  • Posts: 3093
Though of the moment
« Reply #4 on: December 12, 2004, 11:01:59 AM »
This might help to explain why my vol reader/writer writes vol's that OP2 won't load. (I think it is an alignment issue, the program had trouble at one point reading the vol back into memory)

Oh, and regarding the PRT file, I think I'll save you some work on that (at least for the beginning of the file). I know how the palettes are structured (actually, I have a program (albeit incomplete) that can load the palette structures and save them as a RIFF (microsoft) palette).

Header / (whole file):
Len / Description
-----------------
4   / ASCII "CPAL"
4   / numPalettes
X   / array of palette data, described below
X   / animation, other data? (I don't know anything about it yet, I stopped after the palette files)

Palette Data:
4   / ASCII "PPAL"
4   / palSize ; Length of the palette? I don't know if it includes this + "PPAL" marker or not
4   / ASCII "head"
4   / unknown1 ; Can't remember either of these, it might be the number of entries? all the palettes I saw had 256 entries
4   / unknown2 ; ditto above
4   / ASCII "data"
1   / null byte ; maybe it means something? probably
1   / terminator ; Each color entry is terminated by this number. guessing they must have to match?
2   / null word ; again, i don't know ?
X   / array of color data

Color Data:
1   / blue
1   / green
1   / red
1   / that termination byte specified in the other section

------------

This is all I could find in my old op2 hacking files. (Interesting to see how infantile they are, all these text files with asm dumps where I tried to figure out the parameters to functions like CreateUnit (it was all guess and test, that's the scary thing).).
« Last Edit: December 12, 2004, 11:05:19 AM by op2hacker »

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Though of the moment
« Reply #5 on: December 13, 2004, 12:59:07 AM »
Isn't that "terminator" byte just the "reserved" byte of an RGBQUAD? Or the Alpha channel anyways.

Oh, and here's what I've dug up on that 20 byte structure from an old VB project
Code: [Select]
Private Type GraphicsFrame
    NumScanLines As Long
    DataPtr As Long
    height As Long
    width As Long
    unk As Integer
    Palette As Integer
End Type
An array of those follows the palettes. There should be a count of the number of these structures between the palette data and the actual structure array. DataPtr is an offset into the OP2_ART.BMP pixel data. Palette is the index of the palette (zero based) loaded earlier in the PRT file. Each of these structures defines a frame of a unit image. (Building - full heath, building - damaged, explosion frame, etc.). It's interesting how the height comes before the width in the structure. That had me puzzled for quite some time. Seemed very odd.




Well, I've worked on and tested my COMponent.  :whistle:

Looks like it's actually at a very useable stage right now. It can access most things now. Certainly everything that's immediately important. I have yet to integrate the decompression code. Currently it fails for compressed data pretty much as if the data wasn't there. That can be fixed with a simple backend upgrade without any recompiles to any frontend code. I also don't have support for the sound format built in yet. Comming soon. Anyways, I suspect it's about a day or two of work for each one.

As for the uncompressed data. That's completely accessible. Still no archive writing but I don't really consider that of immediate importance.

It might help if you were to take a look at it and suggest any interface changes/improvements to be done. At any rate, I feel like I'm pretty close to calling this version 1.0. Err, btw. It might need a splash screen. It takes almost 2 seconds to load all the data. But then again, it's nearly 20 megabytes that it needs to load.


So, are you gonna have time to work on the front end anytime soon? Or am I gonna have to sit here stewing in my own juices for a while?  :blink:
« Last Edit: December 13, 2004, 01:07:07 AM by Hooman »

Offline BlackBox

  • Administrator
  • Hero Member
  • *****
  • Posts: 3093
Though of the moment
« Reply #6 on: December 13, 2004, 08:56:51 AM »
I have a front end mostly ready. (Yes, it does have a splash screen, because the tileset code I used before also took about 2 seconds to load the tileset into memory (had to decompress each tile, save info about it into an array, and blit it onto a dib section)

I can get the map editing stuff together mostly today. (Course there is no workspace 'module' yet so it will be a sort of 'hack' to have a button that can force the map editor window to come up, but anyway)

I don't have school today so I have more time to work on this. (They cancelled it. Too much snow. :P)

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Though of the moment
« Reply #7 on: December 16, 2004, 09:29:26 PM »
Interesting discovery. The prt file uses the same loading code for the palettes as the tilesets. It just passes the code a pointer to the stream object and it'll read in a palette and return a palette object.

This means that it can accept all the same formats. That is, it checks for "PPAL", "RIFF", "head", "data", "pspl", "ptpl" tags. This is what I referred to before as the inner level parser.

The more I look at the prt file with a hex editor, the more I think it should be width before height, but when I tried it months ago, I remember I couldn't get it working any other way. Same for Cynex. Anyways, weird but I guess whatever works.

Right after the palettes, there is a DWORD describing the number of 20 bytes structures and then immediately afterwards, there is the array of 20 bytes structures. Looks like another count for the next section may follow that.

 

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Though of the moment
« Reply #8 on: December 18, 2004, 12:51:40 AM »
Ok, updates and corrections to the PRT file format.

Code: [Select]
Private Type ImageInfo
    ScanLinesByteWidth As Long
    DataPtr As Long
    Height As Long
    Width As Long
    Type As Integer
    PaletteNum As Integer
End Type

' The field "Type" has the following values/meanings
' 0 - menu graphic
' 1 - in game graphic
' 2
' 3
' 4 - unit shadows - 1 bit graphics
' 5 - unit shadows - 1 bit graphics
' ... more, lots more? Saw a 33, and 65 somewhere


As you can probably guess from the code, I've written a test app in VB. It'll load the palette info from the PRT and the image info section after that. Anyways, the first field is a correction. I noticed it was similar to the width value but DWORD aligned. Having it marked as "NumScanlines" before made it seem really odd that Width comes after Height since the "NumScanlines" value was similar to width and not height. Anyways, looks like it's just a DWORD aligned width, which windows seems to require for the in memory storage of bitmaps.

I took a quick scroll through all the images. Seems not all of them share the same format. Certain ones still display as garbage. It seems to depend on the type field. I'm gonna see if they might be 16 bit images. I've also noticed the type field changes depending on where the graphic is used. Menu buttons all seem to be marked as 0 while all the unit graphics are marked as 1.


Anyways, the PRT loading coding gets pretty messy. It also uses a tripply nested loop with lots of floating point instructions in it. Doesn't load any data in that loop so I've largely been ignoring it. Kinda hard to tell how the rest of the file is structured at the moment but it seems that after the image info there is a number of counts. Looks like 4 of them. Corresponding MemAllocs follow that allocate a multiple of these values (?, 14, 6, 4). Only saw 3 MemAllocs.



Btw, I saw some things that looked kinda like microbe units.  :blink:
Sorta similar to how spiders/scorpions looked, but more cellular like. They appeared to be part of a walking/attacking animation sequence.

Edit: Doh. It was just the sparks from a repair vehicle repairing something.   <_<  Well, maybe we can get away with using them and other people won't catch on.  :whistle:
« Last Edit: December 18, 2004, 02:47:11 PM by Hooman »

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Though of the moment
« Reply #9 on: December 30, 2004, 12:14:56 AM »
Here's a really simple way to change the server OP2 tries to connect to when you choose Multiplayer->SIGS. Just edit C:\Windows\Sierra.ini

Code: [Select]
[SIGS]
ValidateIP=<IP Address>

I've also noticed it has a MachineID in there too. Anyways, maybe it was for some anti piracy stuff.

Either that or change the text string in SNWValid.dll in the System32 directory. Hmm, didn't realize OP2 had files there.  :blink:

Anyways, the default value was "smc.sierra.com".
 

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Though of the moment
« Reply #10 on: January 16, 2005, 10:53:54 PM »
Ok, what happened to all the other threads in here?

I've completed the major sections of my project that you'll need. You should now have access to all the groups and they're completely useable now. I still have a few minor functions left to implement. Mostly just simple details so it shouldn't take much longer hopefully. (Except for a few unknowns: 1 DWORD, and a few flag bits).

Anyways, it's looking Really really cool.  (thumbsup)  

Offline BlackBox

  • Administrator
  • Hero Member
  • *****
  • Posts: 3093
Though of the moment
« Reply #11 on: January 17, 2005, 02:59:08 PM »
What the f*** ?

I don't know where they went. Gonna check.

Well anyway, kudos to the project :)

EDIT: Oh, it's the forum cut off date. Use the drop downs at the bottom of the list. (They are more than 30 days old). I changed the default setting to "Show All" in the admin CP.
« Last Edit: January 17, 2005, 03:01:20 PM by op2hacker »

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Though of the moment
« Reply #12 on: February 18, 2005, 01:52:31 AM »
K, I've looked at some of the flag bits a little more closely. The blight only really uses 1 bit for it's presence, but that second bit I was talking about is used for expanding. Lava also uses the same expansion bit. Guess that bit isn't just for virus after all. Anyways, Lava also uses a bit for the areas it can expand to, and a bit for the areas where it is present. A map editor should be capable of setting the areas where lava can expand. No more cheap hacks of doing that in a DLL!

Anyways, I finally got some lava going in a controllable way. The last parameter seems to be the lava spread speed, but it only comes into effect after the actual eruption. So even if you set the lava spread speed immediately before or after calling SetEruption, it'll be ignored/overwritten once the eruption occurs. You need the initial eruption point to allow lava to be on that tile or the eruption won't really happen. You also have to set which tiles can have lava flow onto them. This is usually done in DLLs with SetLavaPossible.

I also found how the DLLs keep track of all the palces where lava can flow. They used an array of structures, where each structure specified one scanline of the lava. Well, actually, it stored it in vertical strips, with one x coordinate and a range for the y coordinate. There were a few other fields in the structure but they were all set to -1 which the code took as unused. (Although, it kinda looked like the code would have treated it like 3 vertical strips with the same x coordinate, all in one structure). But then all this info is essentially useless since we have no real need to do things the same way.

So yeah, it might be nice if your editor could eventually allow setting where lava flows. It could probably be done just like the cell type editor.
 

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Though of the moment
« Reply #13 on: March 06, 2005, 10:35:44 AM »
I have some idea how units are created now. Very interesting.

Code: [Select]
unitTypeInfo.CreateUnit(int pixelX, int pixelY, int unitIndex?)

It uses a virtual function on the UnitTypeInfo object to create an instance of a Unit object. The first virtual function in the UnitTypeInfo's vftable is the creation function.


Try chasing through memory from a unit to it's unit creation function. It's kind fun and takes a little while.

(Unit* => Unit.vftable => Unit.GetUnitTypeInfo => UnitTypeInfo* => UnitTypeInfo.vftable => UnitTypeInfo.CreateUnit)
« Last Edit: March 06, 2005, 11:21:18 AM by Hooman »

Offline HaXtOr

  • Sr. Member
  • ****
  • Posts: 423
    • http://www.wtfmoogle.com
Though of the moment
« Reply #14 on: December 04, 2005, 01:57:56 AM »
so you can create a acid cloud and have it show up as a fart cannon or somthing funny?

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Though of the moment
« Reply #15 on: December 04, 2005, 02:32:18 AM »
Wow, 9 months of no activity.

It's slightly within reason to edit units and create new ones, but not very feasible. If you wanted it to show up differently, you'd need new graphics. A simple graphics edit isn't so bad.
 

Offline Eddy-B

  • Hero Member
  • *****
  • Posts: 1186
    • http://www.eddy-b.com
Though of the moment
« Reply #16 on: December 04, 2005, 06:06:49 AM »
Is the bitmap-file limited, or can new images be added ?
Rule #1:  Eddy is always right
Rule #2: If you think he's wrong, see rule #1
--------------------

Outpost : Renegades - Eddy-B.com - Electronics Pit[/siz

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Though of the moment
« Reply #17 on: December 04, 2005, 06:34:02 PM »
The exe appears to use fixed sized buffers in certain areas. So in that sense, if you added too much to the file, it might break the game. The file format itself doesn't seem to be limited. Anyways, I think the limit allowed for some expansion, but not soo much that it wouldn't be an issue.