News:

Herr Otto Partz says you're all nothing but pipsqueaks!

Main Menu

The truth about fast grass

Started by Duplode, March 14, 2010, 12:24:03 AM

Previous topic - Next topic

Duplode

A while ago I asked dstien and clvn for the toolkit they are using for the reverse engineering stuff so that I could fool around a bit with the disassembled code. Goals of doing so include understanding the basics of assembly, performing some analysis which might some day be relevant to their porting efforts and, if possible, finding possible immediate targets for modding in the .EXE itself. As for the latter of these goals, there some interesting and maybe even useful modifications which are possible already, but I won't give details until they actually come into fruition in order to avoid creating vaporware. Thing is, inserting any piece of code into an existing executable may require fixing thousands of memory addresses, and even trivial modifications can become complicated due to issues such as the way the code is optimized, which reduce a lot the flexibility of the "dirty" approach to hacking I am forced to take (as I can't code neither C nor assembly properly).

There is, however, one very amusing discovery which was made possible by understanding how track elements are built. To make a long story very short, after the game reads a tile value from the .TRK it uses it to load a chunk of data which contains all relevant information about the track element. Within this piece of data, there is a number which tells the game what the physical model of the element should be. The number points to one of many branches of code in one particular place of the .EXE. When you drive through a track element, the game (among many many other things) runs through the branch assigned to the element, where it uses the car coordinates to decide what the car will have to face (whether there will be a sloped surface at that particular position, or ice, or a wall...).

These branches of code (there are around 50 of them IIRC) are a royal mess to understand for even slightly complex track elements. But for flat roads and corners they are not complicated at all. For a straight road all it does is check whether the x coordinate of the car is between +120 and -120 (the limits of the paved part of the track). If so, it sets a flag which assigns the correct paving (asphalt, dirt or ice). If not, it will just default to grass. Corners (both small and large) are pretty much the same thing, except that before checking the boundaries the game calls a helper routine to trace a circumference to be used as centerline instead of a straight line, and then checks the +-120 limits around the relevant part of the circumference.

Now, something funny happens when you read the code for chicanes. Initially I expected to find either some glaring obvious bug to account for fast grass or nothing at all. Instead, there is a neat little bit of code which makes use of the large corner routine. It looks somewhat like this:



"Very clever!", one might think. By mirroring the tile along x and z axes whenever you cross to the right half of the element you should get two symmetrical half-corners which, when connected, make a chicane. A nice solution in theory. But there is a slight problem, which can be better explained with this pic...



This is the result of a quick n' dirty Stressed experiment in which I mirrored the four tarmac segments at the end of a corner along x and z and strapped them at the other end, which is roughly what the code I described is supposed to do. As you can see, it doesn't quite match...

But, if the code is broken, whatever happened to our chicanes? The answer is discreetly nested in the "few extra lines" I mentioned in the diagram. In fact, I can even picture how the developing team came to the actual solution. They probably were in the final stages of design and decided to code the chicane as an afterthought, after someone came up with the idea of mirroring a half-corner. Soon enough, however, they noticed it wasn't working as expected. By that point they must have been in a rush to finish the game already, and so instead of trying to figure out how to fix it they chose the "screw it" route and added this little coding gem, which looks like this in assembly:

Quote from: Stunts developers feeling funnymov     al, [bp+var_12]
mov     byte_44D47, al

What does it do? Simple answer. [bp+var_12] is the flag which indicates the track surface associated with the OWOOT part of the chicane (asphalt in this case), and byte_44D47 is the flag which tells the game on which kind of surface the car is currently on. By copying one to the other this early in the code they assured the whole four-tile area of a chicane would behave as asphalt, regardless of whatever broken code they left behind.  :D And thus fast grass was born - not as a true bug, but rather as a, hmm... "crude"  ;D improvised solution to avoid having an obviously broken track element.

(In case you are wondering, I am trying to work out a way to disable the fast grass "feature". But more on that later on... ;))

zaqrack

wow, this is awesome. :) We don't have to decode how a slalom, corkscrew, etc. is coded. It's fair enough if we find a way how to implement new branches of code - and thus new elements. For example such a track part, an inverse highwway should not be hard to code - yet a lot of fun. :)

dstien

Wow, indeed! Remarkable work, Dupe! I truly envy your ability to stay focused on your research. It's also very inspiring. You should drop by #stunts@EFNet again so we could get our IDA projects synced. We could give the collabREate approach another try, starting with a fresh database, making sure all participants joins with the same .idb and matching IDA Pro versions.

Quote from: Duplode on March 14, 2010, 12:24:03 AM
[...] (as I can't code neither C nor assembly properly).
Now you're just being silly. ;) Making sense of compiler generated machine code is the hardest part of decompiling, a skill you clearly possess. C is simply portable assembly for humans. I'll help you all I can to get you started. That said, I could also implement the functions for you if you want to stick to documenting. As a bonus, that method's even less legally sketchy. :D

Quote from: Duplode on March 14, 2010, 12:24:03 AM
Within this piece of data, there is a number which tells the game what the physical model of the element should be. The number points to one of many branches of code in one particular place of the .EXE. When you drive through a track element, the game (among many many other things) runs through the branch assigned to the element, where it uses the car coordinates to decide what the car will have to face (whether there will be a sloped surface at that particular position, or ice, or a wall...).
I'm a little disappointed that DSI ended up hard-coding these properties in the code. In other parts of the game, such as the UI, they've used external resource files to its fullest extent. But if the restunts project ever succeeds we'll have to support additional track formats anyway. I'm envisioning a SimCity-style terrain editor with multi-level hills and underground tunnels.</daydream>

Quote from: Duplode on March 14, 2010, 12:24:03 AM
They probably were in the final stages of design and decided to code the chicane as an afterthought, after someone came up with the idea of mirroring a half-corner. Soon enough, however, they noticed it wasn't working as expected. By that point they must have been in a rush to finish the game already, and so instead of trying to figure out how to fix it they chose the "screw it" route and added this little coding gem, which looks like this in assembly:

Quote from: Stunts developers feeling funnymov     al, [bp+var_12]
mov     byte_44D47, al
A reasonable explanation. The hardcoded surface props also indicates -- and this is evident throughout the entire game development industry -- that deadlines trumps elegance. It's however odd that they still left the broken code in, instead of commenting it out. This wouldn't be the worst case of wasted CPU cycles in the game though; load.exe decompresses ega.cmn twice. That would account for many hours wasted on displaying the loading text, especially for us who ran the game directly from floppies...

Quote from: Duplode on March 14, 2010, 12:24:03 AM
(In case you are wondering, I am trying to work out a way to disable the fast grass "feature". But more on that later on... ;))
Translating the surface detection funcs to C would make the work a breeze. Our toolchain let us override the existing code with our implementations.

Duplode

Quote from: zaqrack on March 14, 2010, 08:00:25 AM
wow, this is awesome. :) We don't have to decode how a slalom, corkscrew, etc. is coded. It's fair enough if we find a way how to implement new branches of code - and thus new elements.

Writing the code should be possible thanks to...

Quote from: dstien on March 14, 2010, 04:26:12 PM
Translating the surface detection funcs to C would make the work a breeze. Our toolchain let us override the existing code with our implementations.

The other thing to sort out then would be where to put new data blocks for elements so that we aren't limited to replace the existing ones. I will consult dstien and co. on those matters...

Quote from: zaqrack on March 14, 2010, 08:00:25 AM
For example such a track part, an inverse highwway should not be hard to code - yet a lot of fun. :)


Nice one! The highway entrance branch is not that simple (there is some quite weird code to make the track widen gradually) but it isn't too bad either. In any case, a cheap substitute would be a narrow tunnel... :) BTW, I should mention that, were we able to code new elements, mirrored versions of cork l/r and loop would be high on my priority list. It is frustrating to be restricted to only one corner orientation when planning come combos with them.

Quote from: dstien on March 14, 2010, 04:26:12 PM
You should drop by #stunts@EFNet again so we could get our IDA projects synced. We could give the collabREate approach another try, starting with a fresh database, making sure all participants joins with the same .idb and matching IDA Pro versions.

Sure, I will pop up there in a while  :)

BonzaiJoe

Quote from: Duplode on March 14, 2010, 12:24:03 AM
A while ago I asked dstien and clvn for the toolkit they are using for the reverse engineering stuff so that I could fool around a bit with the disassembled code. Goals of doing so include understanding the basics of assembly, performing some analysis which might some day be relevant to their porting efforts and, if possible, finding possible immediate targets for modding in the .EXE itself. As for the latter of these goals, there some interesting and maybe even useful modifications which are possible already, but I won't give details until they actually come into fruition in order to avoid creating vaporware. Thing is, inserting any piece of code into an existing executable may require fixing thousands of memory addresses, and even trivial modifications can become complicated due to issues such as the way the code is optimized, which reduce a lot the flexibility of the "dirty" approach to hacking I am forced to take (as I can't code neither C nor assembly properly).

There is, however, one very amusing discovery which was made possible by understanding how track elements are built. To make a long story very short, after the game reads a tile value from the .TRK it uses it to load a chunk of data which contains all relevant information about the track element. Within this piece of data, there is a number which tells the game what the physical model of the element should be. The number points to one of many branches of code in one particular place of the .EXE. When you drive through a track element, the game (among many many other things) runs through the branch assigned to the element, where it uses the car coordinates to decide what the car will have to face (whether there will be a sloped surface at that particular position, or ice, or a wall...).

These branches of code (there are around 50 of them IIRC) are a royal mess to understand for even slightly complex track elements. But for flat roads and corners they are not complicated at all. For a straight road all it does is check whether the x coordinate of the car is between +120 and -120 (the limits of the paved part of the track). If so, it sets a flag which assigns the correct paving (asphalt, dirt or ice). If not, it will just default to grass. Corners (both small and large) are pretty much the same thing, except that before checking the boundaries the game calls a helper routine to trace a circumference to be used as centerline instead of a straight line, and then checks the +-120 limits around the relevant part of the circumference.

Now, something funny happens when you read the code for chicanes. Initially I expected to find either some glaring obvious bug to account for fast grass or nothing at all. Instead, there is a neat little bit of code which makes use of the large corner routine. It looks somewhat like this:



"Very clever!", one might think. By mirroring the tile along x and z axes whenever you cross to the right half of the element you should get two symmetrical half-corners which, when connected, make a chicane. A nice solution in theory. But there is a slight problem, which can be better explained with this pic...



This is the result of a quick n' dirty Stressed experiment in which I mirrored the four tarmac segments at the end of a corner along x and z and strapped them at the other end, which is roughly what the code I described is supposed to do. As you can see, it doesn't quite match...

But, if the code is broken, whatever happened to our chicanes? The answer is discreetly nested in the "few extra lines" I mentioned in the diagram. In fact, I can even picture how the developing team came to the actual solution. They probably were in the final stages of design and decided to code the chicane as an afterthought, after someone came up with the idea of mirroring a half-corner. Soon enough, however, they noticed it wasn't working as expected. By that point they must have been in a rush to finish the game already, and so instead of trying to figure out how to fix it they chose the "screw it" route and added this little coding gem, which looks like this in assembly:

Quote from: Stunts developers feeling funnymov     al, [bp+var_12]
mov     byte_44D47, al

What does it do? Simple answer. [bp+var_12] is the flag which indicates the track surface associated with the OWOOT part of the chicane (asphalt in this case), and byte_44D47 is the flag which tells the game on which kind of surface the car is currently on. By copying one to the other this early in the code they assured the whole four-tile area of a chicane would behave as asphalt, regardless of whatever broken code they left behind.  :D And thus fast grass was born - not as a true bug, but rather as a, hmm... "crude"  ;D improvised solution to avoid having an obviously broken track element.

(In case you are wondering, I am trying to work out a way to disable the fast grass "feature". But more on that later on... ;))

Brilliant! Stunts Sherlock strikes again. I will think about this whenever I drive on a chicane.
But we can't be quite sure.


Argammon