Jump to content

Tiberian Dawn hit points, cost and prerequisite limitation


Mussuy
 Share

Recommended Posts

Yes and no... the problem is, the compiler optimization put those in as single bytes because they contained a low enough value, so that the final exe file would be smaller.

 

Expanding them to 4-byte values is possible if the program code at that point (part of a "create new unit blueprint" command, more or less) is moved to a place with more space, with some hacking.

 

This is very much possible with the added empty space available in the v1.06 exe. Doing that is not so easy if you're not familiar with hacking, though :(

 

Kilkakon did this stuff a few times... maybe his topics on it, and my replies to him, contain enough information to make you figure out if you really want to dig into that mess.

Link to comment
Share on other sites

I've read Kilk's topic about editing the units cost beyond the 127 limit (the solar power plant one, that btw I think it's pretty cool), and although I think I can 'understand' what I have to do, there are some things that I don't know how to do.

 

The first; Do every unit and building have a hex address and eip value related to them? In that case, how do I know which 'part' belongs to every unit and building in order to make it jump to the new location with the new code?

 

And the second; I've downloaded the asm tool, and I know I have to write the new code there (the jump and the new cost and hit points value), but what should I do with it then? How do I put the new code in the .exe?

Link to comment
Share on other sites

The first; Do every unit and building have a hex address and eip value related to them? In that case, how do I know which 'part' belongs to every unit and building in order to make it jump to the new location with the new code?

Well, that's not exactly related to units and buildings; they're addresses in the exe file, so every single byte of program code and program data has one, really. As for how to find them... I'm not sure what you use for editing the game exe in general, but if you use my UGE modules, they should list the addresses of all the data you edit, as decimal numbers. If I just gave Kilk the start addresses for the units, then he probably used my ini files (which have the stats in the right order) and counted the 'push' command bytes (6A for a 1-byte push, 68 for a 4-byte push) to see where the property he needed was.

 

And the second; I've downloaded the asm tool, and I know I have to write the new code there (the jump and the new cost and hit points value), but what should I do with it then? How do I put the new code in the .exe?

Uhh... copy the bytes, and paste them in the exe with a hex editor  :P

 

 

Oh... be warned that, no matter which editor you use to edit C&C95.exe, the data will either look severely corrupted, or the editor will fail to load altogether, after you start messing with moving values around.

Link to comment
Share on other sites

I've found the units, infantry and buildings section in the .exe and located the iep values that belong to each stat (prerequisite included) thanks to your ini files, a hex to decimal converter, and a bit of testing with tibed to make sure what iep belonged to the prerequisite stat. I've also found the hex values for MISS (400000), HOSP (4000) and BIO (40000) as prerequisites.

 

Now I'm working with the ASMTool trying to do the same that Kilk did with his tear gas infantry, but in my case I'll change the stealth tank prerequisite to MISS. That's the code I've made:

 

#eip=0044B6A65 ; A6E65h
jmp 00680000 ; jump to unused space
; label indicating the jump back location
jumpback1:

#eip=00680000 ; 1C9600
push    00400000h ; new prerequisite
jmp .jumpback1

 

I've guessed that the 'no operation' lines that Kilk wrote were for removing data that the game would not read (due to the jump), but I'm not sure.

 

What do you think? This is actually the first time I'm doing something like this and it feels like being a chimp in an airliner cockpit  XD

Link to comment
Share on other sites

You messed up that first eip; that should be 004B6A65. Think you duplicated the first '4'. But for the rest, that's indeed the location of the STNK prerequisites.

 

You have another problem, though: a far-jmp instruction is FIVE bytes. The instruction you're replacing is TWO bytes. This means that the code you would put there is overwriting the Prerequisites, the TechLevel behind it, and then the start byte of the 5-byte instruction that gives the Stealth tank's filename reference. Your jumpback will end up in the middle of this third instruction, the CPU will try to interpret these bytes as the start of an instruction, and the program will crash and burn. O_o

 

004B6A65> [2 Bytes] push    10h ; prerequisites

004B6A67> [2 Bytes] push    5 ; multiplayer tech level

004B6A69> [5 Bytes] mov    ecx, offset 004F8A42 ("STNK") ; reference to filename string

 

Basically, any instructions you damage need to be migrated to the new spot, where you execute them along with your modified one before jumping back. The 1-byte 'nop' instructions (No Operation) are there to fill up the leftover bytes of instructions you damaged, so the jump back location is at the start of the next full instruction, to avoid the aforementioned "the program will crash and burn" problem caused by jumping back halfway into an instruction. ;)

 

Since "push" instructions are much easier to see in the hex editor (and you don't need a disassembler to figure out the actual command), I'd instead suggest starting one instruction earlier, and migrating three of the two-byte push instructions, meaning you take along the values indicated in the ini as Unknown42, Prerequisites and TechLevel. This will give you 6 bytes of free space in the original code; enough for your five-bytes jump out. You then align with one 'nop' instruction so you clean up the last half of the last push instruction you destroyed, neatly filling up all 6 bytes, and making sure your jumpback location is just before the next undamaged instruction.

 

#eip=004B6A63 ; A6E63
jmp 00680000 ; 5 bytes
; cleaning up the rest of the 6 bytes
nop ; 1 byte
; label indicating the jump back location
jumpback1:

#eip=00680000 ; 1C9600
push    1 ; old value for 'Unknown42'
push    00400000h ; new prerequisite
push    5 ; old value for Tech Level.
jmp .jumpback1

Link to comment
Share on other sites

Thank you Nyerguds for your explanation. It was very informative :) And you were right, I typed that '4' two times :S

 

I followed your advice, and this is the code I've made:

 

#eip=004B6A63 ; A6E63
jmp 00680000 ; 5 bytes STNK jump
nop ; 1 byte
; label indicating the jump back location
jumpback1:

#eip=004B6D45 ; A7145
jmp 0068000E ; 5 bytes SSML jump
nop ; 1 byte
; label indicating the jump back location
jumpback2:

#eip=004B696E ; A6D6E
jmp 0068001C ; 5 bytes VICE jump
nop ; 1 byte
; label indicating the jump back location
jumpback3:

#eip=0044E5A4 ; 3E9A4
jmp 0068002A ; 5 bytes E4 flamethrower jump
nop ; 1 byte
; label indicating the jump back location
jumpback4:

#eip=0044E500 ; 3E900
jmp 0068009C ; 5 bytes E2 grenadier jump
nop ; 1 byte
; label indicating the jump back location
jumpback5:

#eip=00419965 ; 9D65
jmp 006800AA ; 5 bytes TMPL jump
nop ; 1 byte
; label indicating the jump back location
jumpback6:

#eip=004199EB ; 9DEB
jmp 006800B8 ; 5 bytes EYE jump
nop ; 1 byte
; label indicating the jump back location
jumpback7:

#eip=00419B71 ; 9F71
jmp 006800C6 ; 5 bytes ATWR jump
nop ; 1 byte
; label indicating the jump back location
jumpback8:

#eip=00419BF4 ; 9FF4
jmp 006800D4 ; 5 bytes OBLI jump
nop ; 1 byte
; label indicating the jump back location
jumpback9:

#eip=00680000 ; 1C9600
push    1 ; old value for 'Unknown42'
push    00400000h ; STNK new prerequisite
push    6 ; value for Tech Level.
jmp .jumpback1

#eip=0068000E ; 1C960E
push    1 ; old value for 'Unknown42'
push    00400000h ; SSML new prerequisite
push    6 ; value for Tech Level.
jmp .jumpback2

#eip=0068001C ; 1C961C
push    0 ; old value for 'Unknown42'
push    00400000h ; VICE new prerequisite
push    6 ; value for Tech Level.
jmp .jumpback3

#eip=0068002A ; 1C962A
push    1 ; old value for 'something'
push    0 ; old value for 'no idea'
push    00040000h ; E4 new prerequisite
jmp .jumpback4

#eip=0068009C ; 1C9638
push    1 ; old value for 'something'
push    0 ; old value for 'no idea'
push    00004000h ; E2 new prerequisite
jmp .jumpback5

#eip=006800AA ; 1C9646
push    0 ; old value for 'Unknown40'
push    00400000h ; TMPL new prerequisite
push    7 ; value for Tech Level.
jmp .jumpback6

#eip=006800B8 ; 1C9654
push    0 ; old value for 'Unknown40'
push    00400000h ; EYE new prerequisite
push    7 ; value for Tech Level.
jmp .jumpback7

#eip=006800C6 ; 1C9662
push    1 ; old value for 'Unknown40'
push    00400000h ; ATWR new prerequisite
push    6 ; value for Tech Level.
jmp .jumpback8

#eip=006800D4 ; 1C9670
push    1 ; old value for 'Unknown40'
push    00400000h ; OBLI new prerequisite
push    6 ; value for Tech Level.
jmp .jumpback9

 

(The VICE is actually the 'Heavy Tank' in my 'mod')

 

That's all the code that I need :) Now I have to put it in the .exe.

 

You told me to copy the bytes and paste them in the .exe using a hex editor, but It's the first time I use a hex editor and I still don't know how to do it :S Should I push the 'assemble' button and then insert the text in the OpCode window in the .exe using some 'insert bytes' option in the hex editor?

Link to comment
Share on other sites

Using "assemble" in the asm tool will give you the bytes as text in the lower text pane. You just copy that, piece by piece, and in the hex editor, go to the offset you got for that piece.

 

Now, in the hex editor, typically, you see the bytes on one side, and the corresponding text on the other. Usually bytes left, characters right. First of all, make sure you set your cursor in the bytes part. Then, make ABSOLUTELY SURE that your 'insert mode' is set to overwrite, and not insert, otherwise you're adding bytes to the exe, which royally messes up the jump/call locations of any code following your edited piece, which will, once again, make the program crash and burn when you try to launch it. (Yeah. That happens a whole lot when hacking :P) So, yeah, always overwrite the existing bytes.

 

Now, as to the actual inserting process... in pretty much any hex editor I've used, you can just paste the bytes text in the bytes part of the hex editor, and it'll work. Just make sure you're at the correct byte, that you have the first value of that byte selected (byte is 2 hex values, after all), and that it indeed overwrites when you paste. Save, exit, done.

 

I looked through your code, and, well... it looks pretty good! Just one oddity: you start your custom code at 00680000, and then do +0E for every next one because each one is 0E bytes long... but after 0068002A you skip a bunch and go to 0068009C all of the sudden. Not sure why you do this.

 

By the way, there's a way you can write that in the as tool so you don't have to calculate that at all: instead of giving EIPs for the pieces to jump to, you just use labels. The only disadvantage with this is that the tool is kind of primitive, and can't use labels under #eip's it hasn't read yet, meaning that 1. you need to put the jump-to part ABOVE the original code you replace, and 2. you instead need to calculate the jump back locations manually. Though since this is just "start address + 6" every time in your edits, it's really not hard ;)

 

You'd end up with this:

 

#eip=00680000 ; 1C9600
jump1:
push    1 ; old value for 'Unknown42'
push    00400000h ; STNK new prerequisite
push    6 ; value for Tech Level.
jmp     004B6A69

jump2:
push    1 ; old value for 'Unknown42'
push    00400000h ; SSML new prerequisite
push    6 ; value for Tech Level.
jmp     004B6D4B

jump3:
push    0 ; old value for 'Unknown42'
push    00400000h ; VICE new prerequisite
push    6 ; value for Tech Level.
jmp     004B6974

jump4:
push    1 ; old value for 'something'
push    0 ; old value for 'no idea'
push    00040000h ; E4 new prerequisite
jmp     0044E5AA

jump5:
push    1 ; old value for 'something'
push    0 ; old value for 'no idea'
push    00004000h ; E2 new prerequisite
jmp     0044E506

jump6:
push    0 ; old value for 'Unknown40'
push    00400000h ; TMPL new prerequisite
push    7 ; value for Tech Level.
jmp     0041996B

jump7:
push    0 ; old value for 'Unknown40'
push    00400000h ; EYE new prerequisite
push    7 ; value for Tech Level.
jmp     004199F1

jump8:
push    1 ; old value for 'Unknown40'
push    00400000h ; ATWR new prerequisite
push    6 ; value for Tech Level.
jmp     00419B77

jump9:
push    1 ; old value for 'Unknown40'
push    00400000h ; OBLI new prerequisite
push    6 ; value for Tech Level.
jmp     00419BFA

#eip=004B6A63 ; A6E63
jmp .jump1 ; 5 bytes STNK jump
nop ; 1 byte

#eip=004B6D45 ; A7145
jmp .jump2 ; 5 bytes SSML jump
nop ; 1 byte

#eip=004B696E ; A6D6E
jmp .jump3 ; 5 bytes VICE jump
nop ; 1 byte

#eip=0044E5A4 ; 3E9A4
jmp .jump4 ; 5 bytes E4 flamethrower jump
nop ; 1 byte

#eip=0044E500 ; 3E900
jmp .jump5 ; 5 bytes E2 grenadier jump
nop ; 1 byte

#eip=00419965 ; 9D65
jmp .jump6 ; 5 bytes TMPL jump
nop ; 1 byte

#eip=004199EB ; 9DEB
jmp .jump7 ; 5 bytes EYE jump
nop ; 1 byte

#eip=00419B71 ; 9F71
jmp .jump8 ; 5 bytes ATWR jump
nop ; 1 byte

#eip=00419BF4 ; 9FF4
jmp .jump9 ; 5 bytes OBLI jump
nop ; 1 byte

 

As you see, there are no #eip definitions between the custom code at 00680000. This means the asm tool will give you one large block to paste for all your custom code in the new segment together :)

 

Technically, in this case, you don't even need the 'nop' instructions in the original code, since they were for aligning the jump-back. If you just make sure to jump to the start of the next command (as I said, with three 2-byte push instructions replaced, that's always +6), it'll work. Still, it looks cleaner in the code if you leave the NOPs to clean up properly ;)

 

 

The only other remark I got is that the value before prerequisites for infantry is actually identified; it's "alternate voice (female civilian)"; indicated in the infantry ini as "FemaleCiv' :P

Link to comment
Share on other sites

It works!  :laugh: Now I have 3 tech levels that delay the appearance of powerful units and buildings like the mammoth tank and the obelisk of light, plus one infantry-only tech building (the GDI Hospital and the Nod Bio research lab.)

 

Thank you Nyerguds for all your help. I've learnt a lot thanks to your advice, and you even pointed out the mistakes I've made in my code, most of them typos  XD

 

Also, thanks for teaching me that other way to write the code. Every time I had to calculate the new 'eip' there was a chance of making a mistake (and I ended up making one), but this new method is much faster and safer :)

 

I probably will edit the .exe again eventually, as among the changes that I plan to do next are fixing the visceroid-heavy tank muzzle-flash animation (which is cenetred now), give it a 'vehicle' voice, and, as a final tweak, make the harvester leave the last level of tiberium patch instead of harvesting it all (to represent the presence of tiberium 'roots' and make possible the recovery of tiberium fields in a long match).

 

PS: I didn't even know that a female civilian voice existed xD Maybe I can give it some use...

Link to comment
Share on other sites

I probably will edit the .exe again eventually, as among the changes that I plan to do next are fixing the visceroid-heavy tank muzzle-flash animation (which is cenetred now),

Oooh. That won't be as easy as it sounds; I haven't actually found the stuff that determines muzzle flash offsets  O_o

 

give it a 'vehicle' voice

You're going to need help with that. Originally, before patch 1.06, they DID have a vehicle voice, see. I changed it because it sounded rather ridiculous on a visceroid, but the way I did that was simply by including them in the list of checked IDs for identifying units as "dinosaurs" in the "play response sound" code. I'll have to look this up to see where exactly the code is; it's probably already migrated into that new code segment.

Link to comment
Share on other sites

Found the voice overrides:

 

The indicated code needs to be replaced by 'nop' instructions. You can do it directly by replacing them by bytes '90'.

10F614: 5 bytes

10F714: 5 bytes

And, to make them smoke when damaged like other vehicles:

10F800: 5 bytes

 

Link to comment
Share on other sites

Done and works flawlessly :) Thank you again Nyerguds.

 

And regarding the muzzle flash offset, I found that it is related to the 'unknown 47' variable (in TibEd). I gave the mobile headquarters the mammoth tank gun weapon and changed its 'unknown 47' to the same '47' of the med. tank; 1, and in skirmish mode; mobile headquarters, the mob. hq had the muzzle flash in the same spot than the med. tank. The bad part is that changing the 'unknown 47' value makes the production buildings to build med. tanks instead, something that surely you already know, but maybe you didn't notice that the un-produced starting units keep their original form AND the new muzzle flash position.

 

I hope this helps to solve the muzzle flash mystery xD

Link to comment
Share on other sites

There is no "unknown47" in my inis, and for good reason; that number is the internal ID number of the unit. It's the index you see at the top of my ini files. If you change that in a unit, then the reference it has to refer to itself is wrong, just like you can't put two identical keys in an ini list without it causing problems (or, as you noticed with the construction, downright ignoring the duplicate).

 

The only thing you proved with that is something I already knew: the muzzle flash settings are looked up by unit ID number in some table in the exe.

Link to comment
Share on other sites

Oh, another note on the jump labels. If I remember correctly, the tool has problems with labels that are substrings of other labels. So having a "jump10" and a "jump1" label might mess it up. If you'd add more code to this, I advice either suffixing them with more zeroes ("jump01"), or, the way I normally do it, actually make the labels relevant descriptions ("tmpl_prereq", "eye_prereq", etc).

Link to comment
Share on other sites

By the way, the 'female voice' thing only works on civilians. Though in DOS C&C it could be used on non-civilian infantry to enable the Commando voice.

 

(You know, if I ever get around to implementing this all in ini, that'd be a neat logic to bring back, so the voice sets aren't hardcoded anymore. IsCivilian + AlternateVoice = male civilian, IsCivilian + AlternateVoice = female civilian, and IsCivilian + AlternateVoice = Commando. Though, then there's still the hardcoded voice for Moebius...)

Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...