Yea, I'm pretty sure they're hardcoded.Darmani wrote:I would very much like to ultimately be able to add new units and factions and the like. I'm not that optimistic, however.
Heroes of Might and Magic II Mod
- GreatEmerald
- CH Staff
- Posts: 3330
- Joined: 24 Jul 2009
- Location: Netherlands
It's all just bytes, man.
Hardcoded isn't a problem. Unit stats are hardcoded, but we can modify them quite easily.
Adding stuff is harder. Say the information for spell costs and descriptions starts at address 0x10000. Say the information for units starts at address 0x9000. Now say we add another unit to the units table. Now the spell table gets pushed back a hundred bytes or so -- and every single piece of code which thinks the spell table starts at 0x10000 will break, and the game will crash when you walk into a town with a mage guild.
This is certainly possible to fix with the right tools. It's just a lot harder.
Also, there's probably a lot of code that looks like this:
The compiler will see "NUMBER_OF_UNITS" and replace it with a constant (65, I believe). So now we also have to go around and find all the instances of the constant "65" with the meaning "NUMBER_OF_UNITS" and replace it with "66." Of course, we have to be careful not to accidentally change other bytes that just coincidentally have the same value (like, IIRC, the Crusader's HP value).
And that's just the tip of the iceberg...
By the way, did I mention that the machine language generated by C compilers is among the easiest to reverse-engineer? We are lucky in that regard.
Modifying a couple bytes modifies a couple bytes. Adding a couple bytes moves every single byte after that. Modifying stuff is easier.
Hardcoded isn't a problem. Unit stats are hardcoded, but we can modify them quite easily.
Adding stuff is harder. Say the information for spell costs and descriptions starts at address 0x10000. Say the information for units starts at address 0x9000. Now say we add another unit to the units table. Now the spell table gets pushed back a hundred bytes or so -- and every single piece of code which thinks the spell table starts at 0x10000 will break, and the game will crash when you walk into a town with a mage guild.
This is certainly possible to fix with the right tools. It's just a lot harder.
Also, there's probably a lot of code that looks like this:
Code: Select all
for(int i = 0; i < NUMBER_OF_UNITS; i++)
{
....do stuff....
}
And that's just the tip of the iceberg...
By the way, did I mention that the machine language generated by C compilers is among the easiest to reverse-engineer? We are lucky in that regard.
Modifying a couple bytes modifies a couple bytes. Adding a couple bytes moves every single byte after that. Modifying stuff is easier.
- UndeadHalfOrc
- Titan
- Posts: 1363
- Joined: 13 Mar 2007
Thanks for the recommendation, Darmani. Is this what your friend is talking about?
http://www.hex-rays.com/idapro/
I'm gonna give it a try eventually. If it's as good as it advertises itself, it should be a breeze to change the power of fireball, cold ray and fireblast (from 10, 10, 10, to 13, 13, 15 respectively)
http://www.hex-rays.com/idapro/
I'm gonna give it a try eventually. If it's as good as it advertises itself, it should be a breeze to change the power of fireball, cold ray and fireblast (from 10, 10, 10, to 13, 13, 15 respectively)
- GreatEmerald
- CH Staff
- Posts: 3330
- Joined: 24 Jul 2009
- Location: Netherlands
Indeed, but if they are lazy, they won't even use NUMBER_OF_UNITS and just copy-paste 65, which doesn't help any.Darmani wrote:Also, there's probably a lot of code that looks like this:
The compiler will see "NUMBER_OF_UNITS" and replace it with a constant (65, I believe). So now we also have to go around and find all the instances of the constant "65" with the meaning "NUMBER_OF_UNITS" and replace it with "66." Of course, we have to be careful not to accidentally change other bytes that just coincidentally have the same value (like, IIRC, the Crusader's HP value).
i had a quick look at the IDA program and it looks like it could help get it done.
among other things, it can execute the game in parallel.
edit : i just thought of something. what are the chances that the game loads the starting secondary skills...from the game map?
i'm wondering because it's one of the few starting attributes we can modify from the editor.
among other things, it can execute the game in parallel.
edit : i just thought of something. what are the chances that the game loads the starting secondary skills...from the game map?
i'm wondering because it's one of the few starting attributes we can modify from the editor.
- UndeadHalfOrc
- Titan
- Posts: 1363
- Joined: 13 Mar 2007
If we both fail with free software, I may be willing to purchase it.
But.....woah, they also have a C decompiler! Last time I Googled for one (two and a half years ago, while reading this very thread), I just got a bunch of people saying it's impossible. And there's also a (severely limited) open-source one too, http://boomerang.sourceforge.net/index.php .
But.....woah, they also have a C decompiler! Last time I Googled for one (two and a half years ago, while reading this very thread), I just got a bunch of people saying it's impossible. And there's also a (severely limited) open-source one too, http://boomerang.sourceforge.net/index.php .
I got one more wish for your mod: Changes the difficulty options.
When I play HoMM, I want the AI to be as smart as it can be, but I still want the fight to be fair: all player and AIs should start with the same amount of resources, about the same strength and AI should not be a cheating bastard.
If you manage to modify that game any way you want to, you should make the AI level independent of the difficulty level, like in HoMM1 where you could play with genius AIs on easy difficulty, or dumb AIs on impossible difficulty.
When I play HoMM, I want the AI to be as smart as it can be, but I still want the fight to be fair: all player and AIs should start with the same amount of resources, about the same strength and AI should not be a cheating bastard.
If you manage to modify that game any way you want to, you should make the AI level independent of the difficulty level, like in HoMM1 where you could play with genius AIs on easy difficulty, or dumb AIs on impossible difficulty.
- UndeadHalfOrc
- Titan
- Posts: 1363
- Joined: 13 Mar 2007
- UndeadHalfOrc
- Titan
- Posts: 1363
- Joined: 13 Mar 2007
(Get on MSN so we can talk in more detail.)
After compilation, it's not so easy. That's why I failed in my original plan to change it to 1337.
I found that the compiled lightning bolt code, after putting the spell power into the eax register, does this:
The "load effective address" command used here is mainly used to compute the address of array elements, but it's faster than the multiplication operator, so optimizing compilers use it for multiplication by special values (like 5). I can only change the "4" in "4*eax" to 1,2, 4, or 8. Those two lines are 6 bytes, so, while I can write some assembly and insert the bytes into that spot, if I need more than 6 bytes, I'm going to have to change the entire file. IDA might be able to do that easily since it already analyzes all the memory references, but I haven't figured out how. Since I knew off the top of my head that the byte for the "do nothing" operation is the same as the ASCII for the "Z" character, I just overwrote those 6 bytes with ZZZZZZ, effectively making lightning bolt do 1*spellpower damage.
I now see why the WoG guys run H3 inside a special environment.
After compilation, it's not so easy. That's why I failed in my original plan to change it to 1337.
I found that the compiled lightning bolt code, after putting the spell power into the eax register, does this:
Code: Select all
lea eax, [eax+4*eax]
lea eax, [eax+4*eax]
I now see why the WoG guys run H3 inside a special environment.
- UndeadHalfOrc
- Titan
- Posts: 1363
- Joined: 13 Mar 2007
That will make it a bitch then, since ALL damage spells in Heroes 2 currently use a multiple of 5 as damage multiplier...Darmani wrote: so optimizing compilers use it for multiplication by special values (like 5).
Which environment it is? They didn't use IDA pro?Darmani wrote:I now see why the WoG guys run H3 inside a special environment.
Darmani, I'm leaving my appartment for a week tomorrow morning so I won't be able to provide any IDA input but I probably will as soon as I get back. In the meantime I will most likely browse these forums and check out my PMs using my mom's computer, but nothing beyond that.
EDIT: I'll try to be on MSN this evening
- UndeadHalfOrc
- Titan
- Posts: 1363
- Joined: 13 Mar 2007
There are basically three ways I see of modding code:
One is to do make a function call for every insertion you want to make a call can be made to an arbitrary location in five bytes, so this is definitely doable. You'd have to also write scripts though to make sure all headers get updated appropriately. This would be fairly cumbersome and wouldn't scale -- it would be hard to do anything more complicated than changing some multipliers around. It would also involve violating many of the principles of function calls, meaning you'd likely have to do all work in assembly.
The second is to do what doom3D described on page 4: Run the EXE inside an emulator, and add hooks to perform special behavior at certain addresses. This is probably more maintainable than option one, and allows writing modifications in languages other than assembly, but is still of limited use.
IDA can output an assembly file based on the executable. It does a pretty good job of "unlinking" the code -- that is, it sees a constant like "0x05DF12", notices that this is the memory address of the string "Sawmill", and then creates a name for that location (like "aSawmill"), and replaces the hard-coded memory-address with "offset aSawmill". This enables you to insert code into the file, thereby changing the memory address, and have it be updated approriately upon reassembling.
Using this would be somewhat-ideal approach, as we'd be able to slowly replace functions written in assembly with functions written in C, and eventually decompile enough pieces to make major changes.
The big question is whether IDA does a good enough job with the unlinking to allow you to insert code, reassemble, and have it still work. I don't know, because of a smaller problem: IDA outputs the assembly in its own format, and some nontrivial editing is necessary to get in a form recognizable by an assembler. And its output is closest to that accepted by Borland's Turbo Assembler, which, of the four most commonly used assemblers, is the only one which isn't free.
I worked a little yesterday on trying to get that to work, and will hopefully work some more tomorrow. Unfortunately, tomorrow is the last day before classes start, after which my free time will quite likely drop to near-zero (71 units, eek!).
One is to do make a function call for every insertion you want to make a call can be made to an arbitrary location in five bytes, so this is definitely doable. You'd have to also write scripts though to make sure all headers get updated appropriately. This would be fairly cumbersome and wouldn't scale -- it would be hard to do anything more complicated than changing some multipliers around. It would also involve violating many of the principles of function calls, meaning you'd likely have to do all work in assembly.
The second is to do what doom3D described on page 4: Run the EXE inside an emulator, and add hooks to perform special behavior at certain addresses. This is probably more maintainable than option one, and allows writing modifications in languages other than assembly, but is still of limited use.
IDA can output an assembly file based on the executable. It does a pretty good job of "unlinking" the code -- that is, it sees a constant like "0x05DF12", notices that this is the memory address of the string "Sawmill", and then creates a name for that location (like "aSawmill"), and replaces the hard-coded memory-address with "offset aSawmill". This enables you to insert code into the file, thereby changing the memory address, and have it be updated approriately upon reassembling.
Using this would be somewhat-ideal approach, as we'd be able to slowly replace functions written in assembly with functions written in C, and eventually decompile enough pieces to make major changes.
The big question is whether IDA does a good enough job with the unlinking to allow you to insert code, reassemble, and have it still work. I don't know, because of a smaller problem: IDA outputs the assembly in its own format, and some nontrivial editing is necessary to get in a form recognizable by an assembler. And its output is closest to that accepted by Borland's Turbo Assembler, which, of the four most commonly used assemblers, is the only one which isn't free.
I worked a little yesterday on trying to get that to work, and will hopefully work some more tomorrow. Unfortunately, tomorrow is the last day before classes start, after which my free time will quite likely drop to near-zero (71 units, eek!).
Who is online
Users browsing this forum: No registered users and 1 guest