don't click here

Rata's incursion on... x86 assembly!

Discussion in 'Technical Discussion' started by rata, Aug 14, 2024.

  1. rata

    rata

    Member
    706
    80
    28
    Argentina
    Trying to be useful somehow.
    So folks, we all know how I failed this community by not actually getting to learn ASM to at least try to produce something nice after having played so many good games from here. However, I did find that one thing that pushed me to do something useful, but this time instead hacking a Win32 game, Need For Speed 3.
    Note that I still don't know shit about programming, in fact when I see people talking about structures, the stack, push stack this and that, I don't understand crap. I am not a programmer, I'm a mechanic. But that doesn't matter cos there are always something easier that can be learnt.

    For anyone that may or may not know this game, it came out in two versions. The original one on Sony Playstation, and later on, the PC release for Windows 95. While the overall art direction is similar, both versions are pretty different from each other. PSX version has more attention to detail in stuff like reflections changing depending on where you are in the track, generally speaking it drives better, have two game modes (arcade and simulation, which is not like real simulation but it's a different playstyle), and the thing that pushed this entire diving into hacking the game, different police. In contrast, PC version has more detailed models (albeit some of them are proportioned like shit and look like offroaders), it allows the players to be cops, and it's very easily moddable.
    The game has 9 tracks (PSX version has 5 hidden ones as easter eggs, sort of a bonus thing), with each county having a specific local police. Tracks 1&5, 2&6, 3&7, 4&8 use the same car for each pair; track 9 has an unique cop car. Their cars were shuffled for the PC version for some reason; now tracks 1, 5 and 9 use the car from the tracks 3&6, tracks 2 and 6 use the car from track 9, tracks 3&7 use a PC exclusive car, and tracks 4&use the car from 2&6. The cars from PSX's track 1&5 and 3&7 disappeared completely.

    [​IMG] [​IMG]

    The PC versions of the pursuit Chevrolet Corvette and Lamborghini Diablo SV, both available for players, and also for the boss cops. However, the Diablo is never used for AI cops, despite being on the right folder, due to a programming error.

    [​IMG] [​IMG]
    The same cars but on the PSX release, only available for AI police because players cannot be police.

    Local police cars in PSX version, for 1&5, 2&6, 3&7, 4&8, and lastly, track 9:
    [​IMG] [​IMG]
    [​IMG] [​IMG]
    [​IMG]


    The first issue was mostly fixed by shuffling around the folders, but we still had one car slot less. More over, because of now track 9 using the same car than tracks 1 and 5, it couldn't be ever replicated. This is where I started looking what the heck the disassembler on Cheat Engine was. After being handed with some useful data, started to log some addresses, found the pointers for these cars, and made my first dirt patch, enabling me finally to separate track 9's car from 1&5:

    upload_2024-8-14_18-45-2.png

    This is the original code. After looking it, I realised that the game simply assigns a cop car for tracks 2&6, 3&7, and 4&8. Then, if the track was none of those, it uses the car #500 which is the Crown Victoria. So I replaced these two instructions with a jump to a nearby unused memory pool, performed another check so if it was track 9 it would use one car and if it was not, use another car. Simple, dirt and nasty, but hey it works. And it was my first step.
    upload_2024-8-14_18-49-12.png Ignore the number being 505, that was changed in yet another patch but that's not important.

    A bit above that, there's the part of code that picks up the fast cops. In PSX version, if you play on normal difficulty you get chased by the Corvette, and if you play in hard difficulty you get chased by the golden Diablo. The function somewhat exists in PC, but it's useless because of a programming error:
    upload_2024-8-14_18-54-52.png

    In this part we see that by default it loads up car #504 (Corvette), then it performs a check, and either replaces the car with #505 (the Diablo, in the original game) or skips that part and keeps the Corvette. Here's where I realised their mistake: the address at 55EB2E (gotta add 400000 to that) is simply loading garbage. Test for $20 makes sense on the address that stores the active cheat codes, because they are all stored as bit flags inside the same byte. Byte 5 is for a cheat called MadLand that makes AI harder. So the dev's intent was to have the Diablo chase you, instead of playing in hard difficulty, to require a cheat code. But the address for cheat codes is 6FD4FC and the dev in charge forgot to update it, it seems. So my second patch was not actually fixing the address, but rather making it akin to the PSX version and spawn on hard difficulty instead, changing both the address and the type of check.
    Later down the road I replaced that original structure with a pair of more advanced selectors, taking into account three factors: difficulty, MadLand cheat presence, and car class. Playing with higher class cars picks faster cops, while using the lowest class picks slower cops to keep it fair:
    upload_2024-8-14_19-5-30.png

    This is still pretty much a rustic aproach, but again, it works, and it allows further modifications using CheatEngine. We use it a lot for online racing. In a similar fashion, later down the road I made a very similar structure to change the folder where the police cars are read, allowing me to do an entire bank switch for when I use the MadLand code to make pursuits even harder:
    upload_2024-8-14_19-9-4.png

    I also use this loop that runs constantly in the menu to apply other fixes, like loading a mirrored body mesh to un-mirror itself when you play a race on mirror mode. This isn't very noticeable in the original game, but on non-symmetrical cars like police cars, race cars with full racing livery, or in my case any car because I remodeled all of them in far higher quality, this issue was very much noticeable by the badges.
     
    • Like Like x 2
    • Informative Informative x 1
    • List
  2. rata

    rata

    Member
    706
    80
    28
    Argentina
    Trying to be useful somehow.
    Double posting because I know there's an attachments limits or something like that.
    -------------------------------------------------------------------------------------------------

    So while all this stuff allowed me to get closer to parity to the PSX version with the cool stuff of it (actually these patches combined surpass in content amount the PSX version by a good chunk, especially because PC version has an entire class exclusive to it, with slower cars), I still had one pending issue with the cops. The sirens.
    The most well known of them is the golden Diablo, which has blue and yellow lights; but the other less noticed siren related facts are that the green Land Rover has a red and yellow lightbar, the brown Caprice has two alternating red lights, and the blue lambo rambo has one (but lights both ways so essentially two) blue lights. The other three cars have standard blue and red lightbars, no issue there.
    On the PC version, however, you cannot do that. You can only have a red light for the right side, and a blue light for the left side. So not only this means that you cannot do the blue&yellow or red&yellow sirens, but this also means you cannot do alternating red&red sirens neither, or blue&blue. Even if you do red&red or blue&blue, both lights will flash at the same time rather than alternating, but more annoyingly, the light on the road will still be blue and red. My job in this game is to make cars, but I couldn't live with this. Started digging and searching for the light names, what read on them, and I quickly found the part where it reads the sirens:
    upload_2024-8-14_19-36-32.png

    I'm gonna briefly explain how lights work in this game: you have a car mesh file; inside of it you have up to 16 body parts (though not all of them are used), and up to 16 dummies, which handle the lights. They have a 4 letters name with a simple structure:
    First digit can be H for headlights, T for taillights, or S for sirens.
    Second digit is F for front, R for rear or M for medium. It's used to set where are the lights seen from. Medium means visible from both ways.
    Third digit can be L or R for left or right. This is really only used for sirens to determine if the light is blue or red.
    Fourth digit can be N, O or E. N means no pop flash, O means flash when siren is on on left side, E flash when siren is on on the right side. Only headlights care for this.

    So this part of the code is where it reads the lights, specifically for a siren. We also notice how the game only checks if the siren is left, and will treat anything else as right. From here, I knew my plan: gonna kidnap that jump, check for a Y which will stand for yellow, and easy. No, wait, because I need yellow to be able to pair with both red and blue, which are right and left flashes respectively. Okay fine, I'll have two yellows, that's just two more checks. No, wait, I'll also need another red, because I need alternating red sirens for the Caprice. Also, alternating blue sirens for the LM002. That's uh... that's a lot of checks. The soviet on the NFS3 small modding group we have in discord (who of course can hack the game, in fact he's crushing a lot of game limits and helped me to actually start making those first patches) told me to do a list instead. Right, as if I could think how tf to do that.
    Anyways, nothing of that would be useful without finding the colours. After looking on the vecinities of that area and not finding anything, decided to search for anything that changed when I toggled sirens on and off. Found some stuff changing, here and there, looked what wrote on those addresses, followed trails and finally, I found the place where the siren colours were originating!
    upload_2024-8-14_20-1-22.png

    So of course I blasted a fully radioactive yellow and BAM!
    nfs3_0557.png
    To make things better, it was the same address the one that makes the shiny spot AND also lights up the streets, so by altering this, I could change it into anything! Only problem is, this affected the sirens globally. So by changing this to yellow and blue, I would make every car have yellow and blue sirens. Yikes.
    Nonetheless, decided to have some hope and turned on my siren, and searched for any 4 byte array that was the same. Found two of them which was nice (the instruction itself and the destination where it moved it. Then I decided to run a pursuit and make all cops engage, then search again. And that's when I found each car had its own copy of these colours. So I quickly fabricated a custom pursuit with CheatEngine, NOPed these printing instructions, and left a few jaws open wide at discord:


    Sure thing, it was manually fabricated, but it means it has to be possible somehow. So I got all hyped up, went to sleep because it was like 2AM already, and the next day started designing a powerful selector. The plan is simple, first try with a simple second check for yellow, and change one of these two values. Well that turned horribly wrong, crashed all the time upon executing, don't know why. Decided to join a discord server of ASM programmers, they told me that I cannot write on what is not called 'writable' memory. Okay no big deal, I simply load a register that's not being used, copy the colour I want in there and then the instruction simply moves a register there. Sure thing, these instructions don't fit on that space, but that just means I have to do a jump. No big deal. This time I figured out how to make a quick and efficient table so all hyped up started with that. Plan was simple: when this name is read, I make a copy of it in a stable location, then return to the normal loop, and when it goes to print these colours, I kidnap again and select the right colour for it. So first part went like:

    Code (Text):
    1.  
    2. jmp 0045CBD0
    3.  
    4. mov [006FD1C0],ebx
    5. mov ebx,[eax+00000A04]
    6. mov [006FD1C8],ebx
    7. mov ebx,[006FD1C0]
    8.  
    Make a backup of EBX, use it to save the dummy's name and restore it to normal. Then I kidnap again where it would print the colours and run the selector:

    Code (Text):
    1. mov ebx,00
    2. mov bl,[006FD1CA]  // this is an ASCII character from A to Z
    3. sub bl,41          // substract $41 so A turns into 0, B into 1, and so goes on...
    4. imul ebx,ebx,07          // multiplies by pairs lenght
    5. add ebx,[base address]  //adds the base address, the first entry in the table    
    6. jmp ebx                  // jump there
    7. ---BASE ADDRESS IS DIS--
    8. mov ebx,FF00C0FF // 5 bytes
    9. jmp left, short  // 2 bytes, 7 total
    10. mov ebx,FFFF8080
    11. jmp right, short
    12. mov ebx,FF000000
    13. jmp right, short
    14. list goes on for every letter from A to Z....
    15. mov ebx,FFFFFF20
    16. jmp right2, short
    17. mov ebx,FF808080
    18. mov [eax+000007A8],ebx
    19. mov ebx,[006FD1C0]
    20. jmp final right,long
    21. mov [eax+000007A4],ebx
    22. mov ebx,[006FD1C0]
    23. jmp final left, long
    24.  
    To note how damn lost I was, the JMP EBX I didn't know that was possible. The guys from the ASM discord taught me. The rest of the design was mine tho, I could use my stupid little brain to make it fairly compact. ASM guys told me about MOVZX instruction which can be used to read a specific byte (or word) and clear the rest, so compacting the routine a lil bit. Then tried this routine alone just to make sure the game ran well. Made it work, so the game was not crashing. However, it seemed that these two didn't work well synchronised so the stored dummy's name was constantly overwritten. And it was already 3AM. Went to sleep with a let down, but oh well, tomorrow is a new day.

    Next day I realise I don't need to store the dummy's name to a safe place because I can just read it from that dynamic place like the game already did. Also got rid of the EBX backing up cos it was not being used at all. Also because I forget to use CAPS when editing memory, had the game crashing when using a lowercase letter for the dummy's name (something that cannot happen if you're not memory editing, cos all .FCE (NFS3 and 4 mesh) writers already store dummies in all caps), but just in case anyone decides to use anything other than uppercase A-Z decided to add a failsafe. Anyways the routine looks more compact so nothing can wo grong:

    Code (Text):
    1.  
    2. movzx ebx,byte[eax+00000A06]
    3. cmp bl,41
    4. jl 0045CBE1
    5. cmp bl,5B
    6. jl 0045CBE3
    7. mov bl,5A
    8. sub bl,41
    9. imul ebx,ebx,07
    10. add ebx,0045CBF1
    11. jmp ebx
    12.  
    13. ---BASE ADDRESS IS DIS--
    14. mov ebx,FFFFA050
    15. jmp left,Ashort
    16. mov ebx,FF7070FF
    17. jmp right,Bshort
    18. ...
    19. mov ebx,FFFFE040
    20. jmp right,Yshort
    21. mov ebx,FFFFF0B0
    22. mov [006FD1C0],ebx
    23. jmp 004B8FCB
    24. mov [006FD1C4],ebx
    25. jmp 004B8F42
    26. 004B8F42 Left routine
    27. 004B8FCB Right routine
    28.  
    Plan is simple. Now I just save the colour into a safe place, and when the print function would be, I kidnap it again, read from these values and apply to the car. It's 10PM, it's not that bad.


    The system works! It reads any letter from A to Z, and it picks a colour accordingly! Wait... why are they still getting overwritten? Police get sirens changing colours all the time, it makes no sense! oh wait, it's overriding the colours. Well no big deal, I just make an anti-override system. As these destination bytes are clear upon race start, I just have to check if alpha is 00, and if it not 00, it means it already has a value thus skip the write.
    It works, nothing gets overwritten anymore, but it now looks like the print routine runs BEFORE reading up the names. As a result, once it writes something, then when it reads the next dummy it prints the old value and then it's set in stone. Damn it! I needed to find something that allows me to figure out which one of the 16 possible entities am I reading from, and then store them into a 16x2 table, so then when I can print the colour to destination, I can then easily figure out which car am I working on and print the value. After all, these destination addresses have even intervals between them.
    Make a breakpoint at dummy read time, then I fabricate a race with just me and 15 traffic cars, so I can write down all registers for each car. I know EAX is loading the dummy, so I cannot use it for decipher which car am I working with because it changes up to 16 times for each.
    upload_2024-8-14_21-42-17.png
    I write down all of them, then start deleting the ones that have inconsistent results, and some that never change no matter what. Looks like EDX is my way to go, it stays stable for each car and it's related to lights for sure. However, do a quick sub between all of them and see that their intervals are not the same. Damn it! I'm screwed! EDI is stable but doesn't look to be anywhere close to the lights! Well it's 4AM already again I'm off to sleep.
    So I have this table, with a stable EDI value, but even then, it doesn't directly correlate with the entity number, don't know why. But it's evenly spaced, so it's the only thing I can grab to. I start thinking on something to start sorting these out; in the meantime decide to look again at the final print destination. EAX+7A4 and EAX+7A8. I never did a breakpoint here, but after all I have the final destination's addresses, so I do 5E18C4 - 7A4 on windows calculator, and what do I get? 5E1120! I instantly recognise that number, it's EDI! So when it reads the dummy name, I already had the answer there on a silver plate! So now the only thing I need to do is alter the final part of the colour selector
    Code (Text):
    1.  
    2. mov ebx,FFFFE040
    3. jmp left,Wlong
    4. mov ebx,FFFFF0B0
    5. jmp aux,Xshort
    6. mov ebx,FFFFE040
    7. jmp right,Yshort
    8. mov ebx,FFFFF0B0
    9. mov [edi+7A4],ebx
    10. jmp 004B8FCB
    11. mov [edi+7A8],ebx
    12. jmp 004B8F42
    13.  
    And then, on the original final print's destination, I simply NOP both prints.


    And that's it. Its working beyond flawlessy. More over, it has a quite fun feature which before starting developing this thought it should be possible, but then I was proven wrong. In the end I was proven wrongly wrong, or just right from start:


    So yeah, now I can choose between 13 different colour options for the cop cars, for both left and right sirens, and not only that but I can even pick different colours for the same side at the same time. The last colour loaded will determine the street lighting effect. Somewhere in the future I would like to see if I can use a similar aproach to have both halogen and xenon lights, and turn all the cars into halogen except for the only car that should feature xenon lights. And if it is possible, also have the yellow fog lights, and maybe also making tail lights differenciate pos lights from brake lights. I hope they carry the same flexibility than sirens.

    But that'd be for another time. This has been dispairing at times, but actually fun to lose so many hours focusing on it and learning something. It won't be useful in my life, but at least I can do cool mods for this cool game and allow other modders to do new stuff. And the only reason why I had any minimal idea of what ASM was is because of Sonic Retro. So I thought I should share this here. Maybe I won't be doing the super sonic hack I once had in mind, but I did colours expansion for NFS3, while also mantaining 100% compatibility with the original assets. Old L and R values still work as the original game, and it is actually quite intuitive to know what letter assigns which colour at which side. I'll be posting more stuff when I do more. For now I'm still coming out of shock that I could do this thing, even if it is an actually basic thing to do, considering I am not a programmer, I take pride on it.
     
    • Like Like x 2
    • Informative Informative x 1
    • List
  3. rata

    rata

    Member
    706
    80
    28
    Argentina
    Trying to be useful somehow.
    So folks, here I am once again! The story this time is with the headlights. This game is from the nineties, an era when cool cars still had pop-up headlights. So of course this game which came complete with night racing has support for pop-up headlights.
    HOWEVER, there is one car from the original list that was a bit different from the rest. This car was the british Lister Storm.
    [​IMG]

    Not because it has the steering wheel on the other side, but because this car features instead fixed headlights, with a cover that retracts to expose them when activated:
    [​IMG]

    In the game, this is an issue. The game doesn't have support for this. There was a racing variant that had simply a plexiglass cover. But the devs didn't want to do it this way, oh no. Instead, they simply made them pop-ups!
    Lister - 1 Original (Medium).jpg

    Because we modders are all constrained by the game's limitations, we weren't left with many options. When I redone this car in my standard higher quality, I needed to make a choice just like the devs did in 1998. I was close to make them with the fixed transparent cover, but a member of my team did not allow me.

    Then I came up with an idea: if I can't make it, let's fake it! So I went for using the pop-up headlamps part, but made instead a flat polygon with this arrangement instead.
    Lister - 3 Fakes (Medium).jpg

    And so the model shipped like that, with a somewhat convincing look for what the game engine would allow me. Until yesterday, in which I decided to try a better solution.

    I'm gonna explain explain a lil bit how the parts are selected. Unlike the successor NFS4 which loads each part and treats it depending on the part's name, NFS3 instead just follows an order. This order is, in the most standard way,
    :HB - High Body (LOD0)
    :HFLW - High Front Left Wheel (LOD0)
    :HFRW - High Front Right Wheel (LOD0)
    :HRLW - High Rear Left Wheel (LOD0)
    :HRRW - High Rear Right Wheel (LOD0)
    :MB - Medium Body (LOD1)
    :MFLW
    :MFRW
    :MRLW
    :MRRW
    :LB - Low Body (LOD2)
    :TB - Tiny Body (used only for collision detection)
    :HH - High Headlamps (optional, used on all LODs)

    While I cannot add new parts because I'm nowhere near to understand the game code, there is one thing I can do. I can read the part's name, and if the 4th digit is a C -so the full part's name in a standard way would be :HHC) I can write code that simply makes the opposite: when the lights are turned off, the part will be visible, and when you turn on the lights, that's when the part disappears, exposing the mesh below.
    So for this, I would be having the same issue that took me days of thinking with the sirens: the parts names are stored dinamically. But this time I had an idea of what to do. And more important, I finally understood what the fuck that so called Stack is.
    upload_2024-9-12_0-0-41.png
    So finding this part was an easy task. I know the address for each car that holds information about the lights status (0 for off, 1 for lows, 2 for high beams). Using the debugger to search for instructions that read on that address gave me like 10 instructions, and simply by inverting the jumps I could easily discard each one. Finally, with the lights on, turning this jump from JE to JNE made the popups disappear, and appear with the lights off. This light status value is loaded into edx, from [eax+660]. This eax value is the 5E1120 that solved all my problems with the sirens. It turns out, starting in this address there is a $98C bytes long block holding all info about each of the 16 cars.

    ANYways, I spent an hour or two or so with a breakpoint enabled and walking line by line looking at the registers, searching for one instruction that would lead me to the :HH name. After some time, I found one location on the Stack that was close enough. So I loaded that value from the Stack, added one offset and I reliably found myself with :HH loaded into EBX which was not used anymore before loading a new value. So from here the following was a super simple patch. Kidnap the loop here and move somewhere close. Because I couldn't find an empty place close enough to use a short jump I had to use a near jump, so I needed to kidnap the fstp instruction as well to make space for the 5 bytes of the near jump.

    upload_2024-9-12_0-18-20.png

    So the only thing I had to do was loading the 4th digit of the part's name, and if it is a C, simply jump the usual compare and go for one that is inverted.
    Lister - 4 Covers down (Medium).jpg
    Lister - 2 Closed (Medium).jpg

    And so just like that, now the game has support for any mod car that wants to use retractile headlamps covers. This new code is 44 bytes long, and this pocket I found is 48 bytes long so it fits like a glove!

    You may or may not notice how the headlights changed from pure white to a much warmer tone between builds. Another thing that bothers me with this game is that cars feature pure white headlamps, which is far from what all these halogen lamps could do. So I changed that value easily. My next goal is to enable cars to have Xenon white as option, mainly for the only car in the game that actually had Xenon lights, the Alfa Romeo Scighera prototype.
    [​IMG]

    Among my plans, is also adding reverse lights, as well as taillights that can operate as only parking, or only brakes; in addition to the current two-in-one only option in the game. But that will be for another time. I actually have the main selector for this last part working already, but I have to find out how to make the parking lights NOT get boosted when using the brakes. And once I get that done, using a similar routine for the reverse lights to be tinted white instead of red should be easy.
     

    Attached Files:

    • Like Like x 2
    • Informative Informative x 1
    • List
  4. Cooljerk

    Cooljerk

    Professional Electromancer Oldbie
    4,984
    649
    93
    I really, truthfully don't know how anyone can make any headway into assembly programming without knowledge of things like the stack. I'm not being snarky, that is fully required knowledge to make honest headway into this level of programming, because that's how you pass parameters and how branching to subroutines work. The stack is insanely important at that level.

    Take a few moments to watch this, it'll make you a dramatically better assembly programmer, and save you a TON of headache:
     
  5. rata

    rata

    Member
    706
    80
    28
    Argentina
    Trying to be useful somehow.
    It worths mentioning I am not a programmer, I'm just a mechanic. I dove into this hell because if I don't do it, nobody will. The proof is that nobody did it in 24 years.
    I'm gonna watch that, thank you. While I don't even care about being a good programmer per se, learning how to work with that stack will be helpful. Because for now, I can only feel secure about pulling data FROM the stack, but I have no clue how to work that thing, or if I may screw everything else by writing to it.

    Now, why do I go into this thing without knowing? I don't know how to explain it in English, here in Argentina I say I'm a caradura (hard face?). Someone without sense of shame or fear, I honestly don't know how to explain it. I'll just go for it even if I don't know jack shit about it. :ruby:
     
  6. Cooljerk

    Cooljerk

    Professional Electromancer Oldbie
    4,984
    649
    93
    Programmer, mechanic, etc, they are all just labels. All you need is comprehension, ability to follow step by step processes, and a logical mind, all of which are traits a good mechanic should have anyways. Dont let imposter syndrome set in, you dont have yo be a professional programmer to write code. Programming is probably most fun when its a hobby anyways.

    That entire series - crash course comp sci - is a really good watch imo. A little long but not hard to understand, and you can always ask questions or for clarifications here.

    Being fearless is one of the best traits a programmer can have. My dad growing up always said everybody has ability, just most dont have the guts to try, so you already have the hardest part down. He was a mechanic too, from the us airforce, without a highschool education, and he could pick up programming just fine. All thats left is learning.
     
    • Agree Agree x 2
    • Like Like x 1
    • List