Hex Editing - Guide

From Heroes 3 wiki
Jump to navigation Jump to search
This page is a work-in-progress. Details listed here may not be 100% accurate.

The following article describes some basics of hex edition. Offsets (and code itself in some locations) may vary based on game version.

Heroes of Might and Magic III is coded in Assembly x86. Each byte (a set of 2 hexadecimal characters) corresponds to either a function, or a value for an already determined function. The only exception is byte 0x90, which is non-coding and may be used to f.e. fill empty space.

Upper majority of creature stats, abilities, etc. is coded within the *.exe. While it may not be readable or understandable to a layman, in short time one can comprehend a lot of the code and find reason and rhyme in it (or at least, parts of it).

To open (and edit) the *.exe files, one needs a hex editor, f.e. frhed or another similar software. I recommend ImHex, as it comes with a handy disassembler function built in, as well as a base converter, and similar useful tools.

Some data is located with the *.lod files. To open them, you need to use MMArchive.

Basics[edit | hide | hide all]

Majority of HotA Horn of the Abyss additions are not directly in the h3hota.exe (or h3hota HD.exe), but instead in Hota.dat (and partly in HotA.dll).

Numbers[edit | hide]

All (or almost all) numbers are written in Little Endian - the bytes are placed in reverse order. As an example, 0xA624C (0x before a number signifies it's hexadecimal) in Little Endian is written as 4C 62 0A 00. Note, that the system recognizes that a number is negative based on the fact that it's greater than half the number range: greater than 0x80 for a single byte, and greater than 0x80000000 for a 4-byte value.

Some numbers use a IEE-754 coding. It is recommended to use a calculator for such values, such as the one available on save-editor.com.

Keep in mind that most (but, surprisingly, not all) lists start at 0, not at 1, so the first element is element 0 and not element 1.

QWORD and DWORD pointers are Little Endian representations of an address within the *.exe file, with 0x400000 added to them because of the way the game reads the code. Therefore if you want the DWORD pointer to find 0x27e484, you write it as 84 e4 27 00. DWORDs use IEE-754, while QWORDs use an 8-byte double-precision IEE-754 value.

Values followed by zeroes in the same block are 4-byte numbers - this essentially increases their available number range from (in decimal) 0-127 to 0-2147483647.

Heroes[edit | hide]

Heroes are very easy to edit. Non-Hota hero data is stored in h3hota HD.exe as two sets, one containing general hero data and the other containing only hero specialties. Heroes are generally ordered by their faction (Castle, Rampart, Tower, etc.) and then their class (might or magic). Following standard heroes, are all campaign heroes.

Hero Data[edit | hide]

Hex String (Start: 0x279DD0, End: 0x27D5DF, Bytes per hero: 0x5c = 92)
GG000000 RR000000 HH000000 SO000000 OL000000 ST000000 TL000000 SB000000 SP000000 U1000000 U2000000 U3000000 PS000000 PL000000 R0 AS CO 00
Description
GG Gender: 00: male, 01: female.
RR Race. These are listed alphabetically, from Demon to Vampire. This has no in-game effect or visibility and the extensions preferred to default everyone to human, except Gelu who gets to be an elf.
HH Class: Classes go in order of factions, and within a faction the might class is listed first. Therefore, 00: Knight, 01: Cleric, 02: Ranger, 03: Druid, etc. Note, that HotA classes essentially "follow" this encoding.
SO 1st Secondary skill (ref. ID).
OL 1st Secondary skill's proficiency level (00: Basic, 01: Advanced, 02: Expert).
ST 2nd Secondary skill (ref. ID). If there is no second skill, instead, FFFFFFFF is used, replacing ST and the zeroes following it.
TL 2nd Secondary skill level. If there is no second skill, 00 is used (Basic).
SB Spell Book. 00: absent, 01: present.
SP Starting Spell (ref. ID). If no spell is present, FFFFFFFF is used instead.
U1
U2
U3
Starting creature reference IDs.
PS Small Portrait DWORD pointer, which leads to plain text name of the portrait in the H3bitmap.lod.
PL Large Portrait, same as above.
R0 Present by default in all RoE maps. 00: false, 01: true.
AS Present by default in all non-RoE maps. 00: false, 01: true.
CO Campaign-only. 00: false, 01: true.

Editing HotA Heroes[edit | hide]

Hota heroes are coded in the HotA.dat. Names written in quotes are coded in plain text. Note, that for editing text itself it's best to use programs such as HotA Editor, instead of a direct hex-editing.

Hex String (Start: 2406, End: 0x9886)
<span_stle="color:blue">"<Length1>" "hero<ref_id>"<span_stle="color:blue">"<Length2>" "Heroes\hero_<ref_id>.str" FACTION <span_stle="color:blue">"<Length3>" "#large_portrait_file_name.pcx" <span_stle="color:blue">"<Length4>" "#small_portrait_file_name.pcx" <span_stle="color:blue">"<Length5>" "Specialty_Name" <span_stle="color:blue">"<Length6>" "Specialty Bonus: Object" <span_stle="color:blue">"<Length7>" "{Full Object}" 0d 0a 0d 0a (number of 0a 0d repetitions may differ, and they correspond to line spacing in text) "specialty_description" Length8 "HeroName" Length9 "Biography" 00 00 00 00 01 5C GG000000 RR000000 HH000000 SO000000 OL000000 ST000000 TL000000 SB000000 SP000000 U1000000 U2000000 U3000000 PS000000 PL000000 RR AS C0 00 00000000 00000000 1m000000 1M000000 2m000000 2M000000 3m000000 3M000000 08000000 I8000000 TT000000 ID000000 AA000000 DD000000 DM000000 U4000000 U5000000
Description
Length1 Length of the hero<ref_id> string
hero<ref_id> The hero's reference id in decimal, f.e. hero154
Length2 Length of the Heroes\hero_<ref_id>.str.string
Heroes\hero_<ref_id>.str ???
FACTION Cove heroes: 09 000000 00000000
Factory heroes: 09 000000 00000000 00000000

{{HEXrow|c=blue |Length3| Length of the large hero portrait name (including the .pcx extension)

#large_portrait_file_name.pcx # and the appropriate file name, e.g. #HPLP06.pcx. Note that some heroes don't have the portraits specified in the hero data, and instead in the portrait sections, described below.
Length4 Length of the small hero portrait name (including the .pcx extension)
# and the appropriate file name, e.g. #HPLP06.pcx. Note that some heroes don't have the portraits specified in the hero data, and instead in the portrait sections, described below. {{{2}}}
Length5 Length of the specialty name
Specialty_Name e.g. "Sea Dogs" or "Nix"
Length6 Length of the specialty bonus
Specialty Bonus: Object Refers to the following types of text: "Spell Bonus: Air Shield", "Creature Bonus: Sea Dogs", etc.
Length7 Length of the Full Object and specialty description strings, including the line-spacing coded using "0Dh 0Ah"
{Full Object} Text in curly parentheses {} stating again the specialty, e.g. "{Estates}", or "{Pirates, Corsairs and Sea Dogs}".
specialty_description Text string describing the hero specialty.
Length8 Hero's name's length.
HeroName Text string of the hero's name.
Length9 Hero's biography's length.
Biography Text string containing the hero's biography.
GG Gender: 00: male, 01: female.
RR Race. These are listed alphabetically, from Demon to Vampire. This has no in-game effect or visibility and the extensions preferred to default everyone to human, except Gelu who gets to be an elf.
HH Class: Classes go in order of factions, and within a faction the might class is listed first. Therefore, 00: Knight, 01: Cleric, 02: Ranger, 03: Druid, etc. Note, that HotA classes essentially "follow" this encoding.
SO 1st Secondary skill (ref. ID).
OL 1st Secondary skill's proficiency level (00: Basic, 01: Advanced, 02: Expert).
ST 2nd Secondary skill (ref. ID). If there is no second skill, instead, FFFFFFFF is used, replacing ST and the zeroes following it.
TL 2nd Secondary skill level. If there is no second skill, 00 is used (Basic).
SB Spell Book. 00: absent, 01: present.
SP Starting Spell (ref. ID). If no spell is present, FFFFFFFF is used instead.
U1
U2
U3
Starting creature reference IDs.
PS Small Portrait DWORD pointer, which leads to plain text name of the portrait in the H3bitmap.lod.
PL Large Portrait, same as above.
R0 Present by default in all RoE maps. 00: false, 01: true.
AS Present by default in all non-RoE maps. 00: false, 01: true.
CO Campaign-only. 00: false, 01: true.
1m minimum number of the first creature type in hero's starting army
1M maximum number of the first creature type in hero's starting army
2m minimum number of the second creature type in hero's starting army
2M maximum number of the second creature type in hero's starting army
3m minimum number of the third creature type in hero's starting army
3M maximum number of the third creature type in hero's starting army
I8 reference ID + 8 for heroes before ref ID 178 and reference ID + 7 for heroes after ref ID 178 (essentially, 0xb9 is skipped)

{{HEXrow|c=red |TT| specialty type} {{HEXrow|c=red |ID| relevant ID (skill, spell, unit, resource, etc.)}

AA attack bonus for static unit specialties
DD defense bonus for static unit specialties
DM damage bonus for static unit specialties
U4 2nd unit to be upgraded with the upgrade specialty type
U5 unit obtained from the upgrade specialty

Hero Specialties[edit | hide]

Hero specialties are written one by one in the same order as heroes appear in. Note: Many "possible" specialties don't exist and simply won't do anything; examples include Tactics specialty, Teleport specialty, etc.

Hex String (Start: 0x00278420, End: 0x279C73, Bytes per hero: 0x28 = 40)
TT000000 ID000000 AA000000 DD000000 DM000000 U4000000 U5000000
Description
TT Specialty type. Specialty types are described in greater detail in the sections below. The following specialty types exist:
  • 00: Skill specialty (+5% skill effect per level)
  • 01: Basic Unit specialty (+1 speed, +1 Attack and Defense every <unit level> levels)
  • 02: Resource (+1 gems per day, etc.)
  • 03: Spell (+3% efficiency per level for most spells, sometimes special bonuses instead)
  • 04: Static Unit specialty (static bonus to attack, defense, damage, speed, or any combination of them), e.g. Xeron, Kalt, Haart Lich, etc.
  • 05: Speed (Sir Mullich only)
  • 06: Unit Upgrade specialty (Gelu, Dracon, Bidley, etc.)
  • 07: Dragon Specialty (Mutare, Mutare Drake)
  • 08: Frederick's Automaton Explosion specialty.

Note: Setting the first byte (TT000000) to FFFFFFFF will ensure the hero has no specialty. This is only used for Adrienne (yes, she technically has no hero specialty).

ID Reference ID of the specialized in Skill, Unit, Resource or Spell. (Not needed for Dragon and Speed specialties, where it is 00 and an unnecessary, unused 02, respectively).
AA Attack bonus for Static Unit specialists (and Mutare).
DD Defense bonus for Static Unit specialists (and Mutare).
DM Damage bonus for Static Unit specialists (and probably Mutare).
U4 Second unit (ref. ID) that can be upgraded (only used by Unit Upgrade specialists; otherwise left as 00. Note, that using the same unit twice for Unit Upgrade specialty essentially removes second unit from being upgradable; meanwhile using 00 sets the ID to Pikemen.
Note: When using a Unit Upgrade specialist, the reference ID of un-upgraded creatures ought to be used. e.g. Gelu references Archers and Wood Elves, but code naturally allows also for their upgrades to be improved with his specialty. This does not occur the other way around; specifying ID or U2 as an upgraded creature, will make the upgrade impossible for un-upgraded creatures. )
U5 Resulting unit ref. ID from the upgrade (only for Unit Upgrade specialists).

00 - Skill Specialty[edit | hide]

The code for skill specialties works as follows: XXXXXXXX D805 YYYYYYYY D8 4D FC where XXXXXXXX is a DWORD pointer to a 5% value (in IEEE-754), YYYYYYYY is a DWORD pointer to a value of 1 (again, in IEEE-754).

How it works[edit | hide]

The number of levels is multiplied by the XXXXXXXX DWORD pointer, added to the YYYYYYYY DWORD pointer and then the skill bonus is multiplied by it (D8 4D FC).

If we want to change the % gain per level, we only need to change the XX value. If we want to instead make the bonus additive (say x% per level, regardless of how much skill itself adds), we can replace the D805YYYYYYYY with 909090909090 (NOP) and replace the D8 4D FC with D8 45 FC.

Therefore it is possible to have a different bonus for one skill than another.

Offsets[edit | hide]

The offsets for each skill specialty bonus are as follows:

Archery - 0x0E4420
Armorer - 0x0E45C9
Diplomacy - 0x0E483C
Eagle Eye - 0x0E46E0
Estates - 0x0E464F
First Aid - 0x0E4BD9
Intelligence - 0x0E4B69
Leadership - 0x0E3C81
Learning - 0x0E4AF9
Logistics - 0x0E4F1E
Luck - 0x0E3A2B
Mysticism - 0x0E41FE
Necromancy - 0x0E3F92
Offense - 0x0E4569
Resistance - 0x0E499C
Scouting - 0x0E432E
Sorcery - 0x0E5B61

Exceptions[edit | hide]

Specialties for integer-bonuses (Luck, Logistics, Scouting) only have their effect when a full integer is gained from their % bonus.

Diplomacy specialty only affects surrender costs.

Learning specialty is fully over-written by the Hota.dll. ???

01 - Unit Specialty[edit | hide]

The scaling factor for a unit specialty is a DWORD pointing to a 5% value (in IEEE-754), located at 0x0e6548. Note that Hota.dll is hooked at this point, replacing the DWORD pointer with a unit level test followed by a different DWORD for level 1 than the one for level 2-7 units, all somewhere in HotA.dll.

Setting the unit IDs to an upgraded unit will ensure only the upgraded units are affected by the specialty. This is the cause for the Shadow of Death The Shadow of Death bugged Catherine and Roland specialties.

Ballista and Cannon specialty are Unit specialties.

02 - Resource Specialty[edit | hide]

The amount of resources obtained from a gold specialty is saved at 0x0E4681 (as 350).

The code for other resource specialties is possible to edit, but it checks for each resource together and then adds 1 to their growth. Therefore, it is not possible to make a single type of resource be given in a different amount by a specific specialist, without significant changes to the code.

03 - Spell Specialists[edit | hide]

Spell specialty types[edit | hide]

Spell specialty type table starts at 0x0e6358 (with Fire Wall specialty). Each consecutive byte is then the next spell by spell ID (Firewall, Earthquake, Magic Arrow, etc.) up to Slayer. Each spell specialty can take 6 types:

00 - +25% damage (Luna)
01 - +50% damage (Ciele).
02 - Tier bonus A, such as Haste, Bloodlust or Stone skin specialties. Yes, all three of these types of specialties have the same bonus here; they are then overwritten in the Hota.dll. Their tables are relatively easy to find in said file, since they just include all bonuses listed one by one, for units level 1-7.
03 - Static bonus +10 (Aenain)
04 - This one is only used by Fortune (Daremyth, Melodia). In contrary to what BTB claims, it sets the value of the spell's effect to 3, regardless of the spell's level or the values in SPTRAITS.txt. It can be used to f.e. set the value of extra Counterstrikes from the Counterstrike spell to 3, or, should its value be increased, affect another value used by a spell.
05 - Tier bonus B (+20/+20/+16/+16/+12/+12/+8), only used by Coronius as far as I know. Specific value of the bonus is determined by a table in Hota.dll
06 - Scaling bonus (10% per level, overwritten for Astra and Uland to be 10% per 8-n, where n is the level, in the Hota.dll).

HotA.dll changes almost all of these bonuses, meaning that while the specific spell's specialty type can be changed, an entire specialty type can't be edited without editing HotA.dll

If a spell does not belong to the spell table, the scaling bonus is applied (for example this applies to the Hypnotize specialist, Astral).

"But what if we want to change the specialty type for a spell like Hypnotize?"

We can extend the table by changing 0x0e6296 (equal to 0x2a by default) to a different value, which corresponds to the number of spells in the table -1. If we extend the table, the next specialties will be those for the next spells by spell ID. We have a total of 13 bytes of nop (90), so the last spell specialty we can code this way is Summon Air Elemental. Remember to update all specialties created this way! You shouldn't just leave the nops (90) in the middle of the table!

"But what about Victoria, the Land Mine specialist?", you may ask. Well, I'm here to give answers:

The command lea eax, [esi-0Dh] located at 0x0e6291 finds the spell ID of firewall (0d) and chooses it as the first spell on the list of specialties (remember? The one that starts at 0x0e6358?). By changing it to lea eax, [esi-0Bh], we can change the first spell ID to 0x0B, or Land Mine. Notice, that this reduces the last spell specialty to Summon Earth Elemental, but I doubt anyone will miss the Summon Water Elemental and Summon Air Elemental specialties.

Next, we have to increase the number of spells in the list by 2 (located at 0x0e6296), and then move the entire spell specialty table, 2 bytes each, because now it starts at 0x0e6358 with Land Mine (and still follows by spell ID).

"And what about Eovacius? And Zilare?"

Haven't looked at them yet but I believe they may be hard-coded.

The specialty types can be changed by altering their commands. Each specialty type (00, 01, and so on) have a DWORD pointer to their appropriate code located in a 28-byte long table starting at 0x0e633c.

Changing Tier bonuses[edit | hide]

Let's say we want to make Olema's Weakness even stronger. The table for the bonus, defined at 0x23eaa8, is overwritten in Hota.dll, where each 4 bytes correspond to respective unit's level.

A similar table for Coronius' specialty type starts at 0x23eac4 (immediately afterwards) and works the same way (and is also overwritten by Hota.dll).

Changing the static bonus[edit | hide]

At 0x0e62e6 the value 0x02 determines the static bonus (such as the one Disrupting Ray specialists get). It is overwritten in Hota.dll

Changing the scaling bonus[edit | hide]

At 0x0e631f the DWORD pointer finds an IEEE-754 double precision value for 3% (base game). The value is overwritten in Hota.dll.

If you don't want the scaling specialties to divide their bonus by unit level, replace f7 f9 at 0xe630b with 90 90 (nop-ing out the division).

Specialties that don't work[edit | hide]

Several specialties don't work because no specialty type can increase their bonus:

  • Curse
  • Anti-Magic
  • Dispel
  • Slow ???, but it would probably be way too strong anyway;
  • Berserk
  • Blind ???
  • Teleport
  • Remove Obstacle.

04 - Unit (static)[edit | hide]

Only Xeron is specified to add speed thanks to his static unit specialty.

Setting the unit IDs to an upgraded unit will ensure only the upgraded units are affected by the specialty.

05 - Speed specialty[edit | hide]

Sir Mullich's unit specialty amount (0x02) is located at 0x0E6669.

06 - Unit Upgrade specialty[edit | hide]

These specialties calculate the upgrade cost automatically, with the same function that calculates upgrade costs in all other cases. If "unupgraded" unit is more expensive than the upgraded one, the message about necessary costs wil appear, but no cost will be stated and the conversion will be free.

Setting the unit IDs to an upgraded unit will ensure only the upgraded units are affected by the specialty.

07 - Dragon specialty[edit | hide]

I believe Mutare's bonus used to be specified within her specialty block, and now is overwritten by Hota.dll

Creatures[edit | hide]

Editing HotA Creatures[edit | hide]

While most statistics of base game creatures can be edited rather effortlessly (using MMArchive to unpack H3bitmap.lod or HotA_lng.lod), HotA creatures cannot be edited this way. Their data is stored at the beginning of HotA.dat. When using custom functions or editing original code, note that all HotA (as well as some SoD monsters) have reference ID above 80, and therefore cannot be checked for using a simple check command, such as 83 FA ID. Instead, you have to use a longer command that accepts a 4-byte value (for EDX that would be 81 FA ID000000).

Hex String (Start: ???, End: ???)
08 000000 "monst<ref_id>" 17 000000 "Monsters\monster<ref_id>.str" 09 000000 00000000 04 000000 "name_abbreviation" KK000000 "<animation_filename>.def" L1000000 "Monster_name (singular)" L2000000 "Monster name (plural)" 00000000 00000000 00000000 00000000 01 74 0000 TT 000000 UL 000000 0c556700 00556700 F1 F2 F3 F4 80da9e03 60da9e03 30da9e03 WC000000 MC000000 OC000000 SC000000 CC000000 GeC000000 GC000000 FV000000 AV000000 GV000000 HG000000 HP000000 SP000000 AT000000 DF000000 DmgL000000 DmgH000000 SH000000 SN000000 OO000000 mm000000 MM000000 RR000000 VV000000 WW000000 XX000000 YY000000 ZZ000000
Description
monst<ref_id> The monster's reference id in decimal, f.e. monst154
Monsters\monster<ref_id>.str ???
name_abbreviation The 4 letter abbreviation of the monster name. Never appears anywhere ever again. Note that not all abbreviations are just beginning or consonants, f.e. Haspids are written as "aspi" and nix as "nixx". This abbreviation doesn't seem to have to be unique, as Sea Serpent and Haspids have the same 4 letters here. It is worth pointing out, that base game creatures also use 4-letter abbreviations in a similar table located in h3hota HD.exe around 0x275500
KK The length of the animation_filename + 4 (and therefore the length of the entire "animation_filename.def"
<animation_filename>.def The file name of the set of animations, saved as .def in HotA.lod or HotA.lod.
L1 The length of the monster name (singular)
Monster_name (singular) Text string of the singular monster's name.
L2 The length of the monster name (plural)
Monster_name (plural) Text string of the plural monster's name.
TT The reference id of the town the monster belongs to. 0x09 for Cove, 0x0a for Factory, FFFFFFFF for Neutrals and I bet if HotA ever releases a new town, it will have 0x0b as its faction code.
UL The unit level -1 (sidenote: this is an example of numbers being used in a list with a zero-based numbering). FFFFFFFF for monsters with no level (f.e. Citadel / Castle Towers)
0c556700
(and 00556700)
These point to a similar table with creature sprite, text and other references in h3hota HD.exe and is probably used to insert the HotA.dat's creature table into this part of the exe.
F1 The first creature ability flag
  • 01: 2-hex unit
  • 02: Flying
  • 04: Ranged Attack
  • 08: Fire breath
  • 10: Living
  • 20: Can attack city walls (cyclops, cannon)
  • 40: War machine
  • 80: Slayer (Basic)
F2 The second creature ability flag
  • 01: Slayer (advanced)
  • 02: Slayer (expert)
  • 04: Mind immunity
  • 08: shoots beam (?)
  • 10: No melee penalty
  • 20: ???
  • 40: Fire immunity
  • 80: Attacks twice
F3 The third creature ability flag
  • 01: No enemy retaliation
  • 02: Morale has no effect
  • 04: Undead
  • 08: AOE melee attack (Hydra)
  • 10: AOE ranged attack (Magog), AI flag only.
  • 20: ???
  • 40: ???
  • 80: ???
F4 The fourth creature ability flag
  • 01: ???
  • 02: ???
  • 04: ???
  • 08: ???
  • 10: ???
  • 20: ???
  • 40: ???
  • 80: Dragon (for the sake of Mutare's specialty and vial of dragon blood)
80da9e03
60da9e03
30da9e03
???
WC Wood Cost to recruit the creature
MC Mercury Cost to recruit the creature
OC Ore Cost to recruit the creature
SC Sulfur Cost to recruit the creature
CC Crystal Cost to recruit the creature
GeC Gem Cost to recruit the creature
GC Gold Cost to recruit the creature. Note that all aforementioned cost values have 4 bytes to play with, so the maximum unit cost is 2147483647 gold (or of any resource).
FV Fight Value
AV AI Value
GV Growth
HG Horde Growth
HP Hit Points
SP Speed
AT Attack
DF Defense
DmgL The low end of the damage range
DmgH The high end of the damage range
SH Number of shots per battle
SN Number of spell casts per battle
OO ???
mm Minimum number of units when randomly spawned by the map generator
MM Maximum number of units when randomly spawned by the map generator.
RR
VV
WW
XX
YY
ZZ
???. Sometimes they take up 28 (!) bytes (such as for Ayssids), and sometimes don't appear at all. I suspect they reference some sort of abilities the creatures have, or their descriptions.

Editing Creature Abilities[edit | hide]

Magic Channel (Familiars)[edit | hide]

Magic channel's applicable unit ID is defined at 0x1a24f6. The game uses a frankly ridiculous method of dividing by 5 (let's just say that it purposefully calls and uses a value 1717986919 to calculate 20%). Btb2 analyzed it and found a way to alter the percentage to 50% or 100% (by ignoring the horrible function before it and simply taking the mana value or moving it by 1 byte). Replace:

  • 0x1A24B4 -> 8b 55 98 d1 ea (for 50%, since d1 ea is a single bitwise shift to the right
  • 0x1A24b4 -> 8b 55 98 90 90 (no division).

Let's take a look at the function. The following offset reads:

  • 0x1A249F: b8 *67666666* c7 45 1c 02000000 f7 e9 8b 4d 9c *d1 fa* 8b c2 c1 e8 1f 03 d0

The b8 XXXXXXXX (bolded above) function calls a hexadecimal value (note that it's in Little Endian) and saves it. The code then uses "d1 fa" (emboldened above) to divide edx by 2 before it is further used. C1 E8 1F is simply a division by 2^31 of the value. Yes, that is why the rounding may be a bit off for Magic Channel. Now let's consider replacing:

  • 67666666 with our custom value (for now let's imagine it's 56555555)
  • d1 fa with 90 90 (two nop or non-coding functions, so we basically removed this part of the code)

The result is... Division by 3. Now the Magic Channel grants 33% mana return.

To obtain a different value, we need to replace XXXXXXXX with another number. This has to be (2^33 / D) +1, where D is our divisor (3 for 1/3, 5 for 1/5, etc.). The 2^33 is multiplied with the variable (mana cost), but is then divided by 2^31 and moves by two bits elsewhere to account for the differences. The number is greater than the quotient by 1, to avoid the rounding downwards removing 1 mana point incorrectly. Note, that XXXXXXXX cannot be equal to or greater than 00000080 (0x80000000), as then the value will be negative.

The function also calls 0x1A2498 for the minimum mana value for which Magic Channel may apply. This is equal to the value of D (0x05 in base game, 0x03 in the example above).

Buildings

Building names and descriptions (excluding HotA) are saved in BldgSpec.txt and BldgNeut.txt. Building requirements can be altered by changing their dependency tables. Address of each dependency table is located at:

  • 0x0EB816 - Castle
  • 0x0EB8B3 - Rampart
  • 0x0EB971 - Tower
  • 0x0EBA39 - Inferno
  • 0x0EBA48 - Necropolis
  • 0x0EBA5C - Dungeon
  • 0x0EBA70 - Stronghold
  • 0x0EBA84 - Fortress
  • 0x0EBA98 - Conflux

Note, that some building requirements are changed in HotA.dll. While it shouldn't be too difficult to determine their location in that file, (look for appropriate DWORDs in HotA.dll) for now these offsets are unknown. The requirement table for Cove and Factory may be coded differently and as far as I know, haven't been found yet.

Building costs can be changed in Building.txt.

HotA buildings[edit | hide]

Building names and descriptions for HotA are saved in HotA.dat and can be edited as a text edit using f.e. the aforementioned HotA Editor.

Building costs for HotA are saved in HotA.dat. The building names appear before the building costs. Each building cost contains of 28 bytes, each 4 bytes representing a 4-byte little endian number of cost for each resource (Wood, Mercury, Ore, Sulfur, Crystal, Gems, Gold). Note, that some buildings have text and description, but no written cost (either replaced with 0s or simply not having any space devoted to them). An example is Mana Generator. For now it is not known how to edit the cost of these buildings (and therefore the cost of the Mana Generator remains a mystery).

Resource Silos[edit | hide]

Resource Silo yield tables are located at 0x288F04. Each resource value is mentioned for each town, one by one, in 4-byte values (order of resources is Wood, Mercury, Ore, Sulphur, Crystal, Gems, Gold) with towns going in order.

Cove's Resource Silo simply copies the one from the Dungeon, while Factory copies Rampart.

The Resource Silo table is called for in exe a few times, but HotA.dll overwrites all (or most) of these calls at 0xd37ae and 0xd37ce. As of 12:19, 19 September 2024 (UTC), it is not known how to alter the Resource Silo values for Cove and Factory. No specific offset or function have been identified. ???.

Artifacts[edit | hide]

All stats given by artifacts are written into a table at 0x23e758, 4 bytes for each artifact (Attack, Defense, Power, Knowledge).

The artifact traits are specified in artraits.txt, while artifact pick-up events are defined in artevent.txt (both in Hota_lng.lod).

Interference amount of the Plate of the Dying Light is specified as a IEE-754 floating point value at 0x1d40c8 in Hota.dll

Combination Artifacts[edit | hide]

Combination artifact's components are specified at a few functions, starting at 0x04c63d. For further reading see BTB2's guide (linked below, as External Link #3). Be aware! BTB2's guide is either wrong or outdated! His method of removing components seems to work correctly, however, most likely due to HotA putting their combination artifacts in HotA.dll to push them in between base game combination artifacts, or for another reason, his method of increasing the number of items does NOT work.

However, not all is lost. There's 8 bytes of nop (90) commands starting at 0x4c8c8, and we can use them to add an extra item (to showcase it, I'll present a code for adding an extra item to the Ring of the Magi's requirements. This seems a safe method, since only a short push is used, and therefore I advise changes to 3-piece combination artifacts to simply copy this method and swap Ring of the Magi with their edited artifact).

0x4c85b 6a XX (ref item ID) -> eb 6d (jump 109 bytes forward)
0x4c8c8 90909090 90909090 -> eb 06..(skip following code)...6a XX (ref item ID) 6a YY (2nd ref item ID).....eb 8d (jump backwards 115 bytes)

Replace emboldened text with your appropriate artifact IDs. Remember to also update the number of components (in this example at 0x04C861).

Note, that the method described above only works for a jump of 125 bytes or less. Gladly, there's a few other empty spaces that can be used for adding extra components:

  • 0x4c25c (11 bytes of nop)
  • 0x4c2d6 (10 bytes)
  • 0x4c347 (9 bytes)
  • 0x4c3c7 (9 bytes)
  • 0x4c622 (14 bytes)
  • 0x4c8c8 (8 bytes)
  • 0x4ca15 (11 bytes)
  • 0x4d008 (8 bytes)

Simply use the available space within 125 bytes of the definition of any of the original components. If this can't be done, swap your artifact with another, which can be edited.

Note, that if you're using HotA, it's much safer to just edit appropriate artifact reference IDs and add other appropriate components (or replace the resulting artifact) by editing Hota.dat (see: Artifacts - artsinfo0

Making new Combination Artifacts[edit | hide]

For now it is wholly unknown how to do it. HotA devs seem to know their way around it.??? See below.

What might work is repeating a function identical to that of another artifact with different properties using some empty space. Requires further testing.

HotA Artifacts[edit | hide]

Hota's new artifacts are coded in the Hota.dat. To edit their properties, you need to edit their text (or use hexadecimal values for ASCII to match their text in hex editing). Artifacts are coded as follows:

artsinfo0[edit | hide]

This part of the file includes partial data on all HotA artifacts, written as follows:

#Artifact Name
<ref_ID>
X Y Z # Description:
P Q R # Dif abl, S points
-1

Note, that the ref_ID above is written in decimal system.

The X Y Z applies a buff to the artifact, seemingly by coding first the bonus type with X, amount with Y and optional specification with Z.

  • 0 - Increase Attack / Defense
  • 1 - Add Spell Power
  • 2 - Add Knowledge
  • 7 - Different Ability, Y = AI value ??? (it's described as "points")
  • 8 - Movement artifact?
  • 13 - Restrict spells; Y = 1 for Cloak of Silence, 4 for Ring of Oblivion
  • 16 - Add daily resource growth, Y = amount, Z = resource type (0 = wood, 6 = gold)
  • 24 - Enemy morale bonus
  • 25 - Enemy luck bonus

The P Q R are coded in the same way as above and refer to a second ability.

However, the method above seems to suggest some artifacts have different bonuses than they actually possess: Ironfist of the Ogre doesn't increase either attack or defense; Trident of Dominion increases attack by 6, etc.

This part of the file also includes definitions of all combo artifacts and their components. F.e. Diplomat's Cloak:

# Diplomat's Cloak
12 141 3 66 67 68
  • 12 is the number of the combination artifact (including SoD ones)
  • 141 is the decimal reference ID of the Diplomat's Cloak
  • 3 is the number of components
  • 66 67 68 are the reference IDs of components (decimal)

Note, that this can be used to effectively alter any base-game combination artifact! Simply add a line before these artifact definitions

# Sample Text
N RIF C R+ R++ R+++ R++++,

where N is the number of combination artifact (counting the way they're placed in the .exe code), RIF is the reference ID of the final artifact, R+, R++ and so on are the additional components. To showcase adding an extra requirement to the Ring of the Magi (since a similar concept was used above) and replacing the Ring of the Magi with the AB blade and requiring a Sword of Hellfire:

# Armageddon's Blade
10 128 1 11

(11 is the reference ID of the new item - Sword of Hellfire).

Sadly, new combination artifacts can't simply be added this way.

art<ref_ID>[edit | hide]

art<ref_ID> entries in HotA.dat (as visible in the Hota.editor.exe) can be used to alter an artifact's name, adventure map event text, description, slot type and rarity class (treasure, minor, major, relic). Here is also the true artifact bonus data coded. Note that the Reference ID is in decimal.

gold_price # cost - (cost of 0 is used if an artifact cannot normally appear, f.e. if it's a Combination Artifact.
slot # slot type (0 - none, 1 - head, 2 - shoulders, 3 - neck, 4 - right hand, 5 - left hand, 6 - torso, 7 - ring, 8 - feet, 9 - misc, 10 - ballista, 11 - ammo cart, 12 - first aid tent, 13 - catapult, 14 - spell book)
class # type (1 - none, 2 - treasure, 4 - minor, 8 - major, 16 - relic)
0 # disabled as defaults (0 - no, 1 - yes)
0 # add new spells (0 - no, 1 - yes)
0 # attack bonus (_int8_)
0 # defense bonus (_int8_)
0 # spell power bonus (_int8_)
0 # knowledge bonus (_int8_)

Creature Banks[edit | hide]

The order the banks appear in is as follows:

  1. Cyclops Stockpile
  2. Dwarven Treasury
  3. Griffin Conservatory
  4. Imp Cache
  5. Medusa Stores
  6. Naga Bank
  7. Dragon Fly Hive
  8. Shipwreck
  9. Derelict Ship
  10. Crypt
  11. Dragon Utopia

Note, that after changing these values, you still have to edit the remaining bank data in CrBanks.txt. HotA banks, except for the Ancient Altar, are all defined within the Hota.dat file, as crbank21, 22, and so on.

Hex String (Start: 0x2702A0, End: ???)
U1000000 U2000000 U3000000 U4000000 ... FFFFFFFF 00000000 00000000 00000000
Description
U1
U2
U3
U4...
Unit IDs for guards. U2-U4 etc. are optional. Note, that the 12 bytes of 00 do not appear after the last defined creature bank (Dragon Utopia).

After all of the creature banks have been defined, the reward creatures (and creatures only!) are defined as follows:

Hex String (Start: ???, End: ???)
FFFFFFFF R1000000 R2000000 R3000000 ...
Description
R1
R2
R3...
Unit IDs for reward creature for the first bank. R2-R3 etc. are the reward creatures for subsequent creature banks.

The Pyramid[edit | hide]

The pyramid is defined separately:

0xa3fa2:6a 02 6a 14 6a 7d (52 8d4508 53 50) 6a 74;
0xa3fb6: 28 00 00 00

Where:

  • 02 is the number of stacks of Diamond Golems
  • 14 is the number of Diamond Golems (in total)
  • 7d is the unit ID of Diamond Golems,
  • 74 is the unit ID of Gold Golems and
  • 28 is the number of Gold Golems.

BTB2 suggested replacing Diamond Golems with Mummies - this however, requires using a creature ID above 80, and therefore a long push (6B 8D000000 for mummies). However, to do that we would need to use some empty space. He suggested removing the -2 Luck on empty Pyramid subroutine. I decided to instead use some empty space I got from a different change, keeping the -2 Luck penalty. The result is as follows:

0x0A3FA6 → ebdf (jumps to overwrite -2 luck on empty Pyramid),
0x0A3F82 → e9 89c10100 (jump to free space 0xc0110)
68 8d000000 (Mummies)
EB 1A 90 90 (jump back to A3FA8),
0xc0110 (Free) → 8a 87 1b 01 00 00 04 fe 88 87 1b 01 00 00 (displaced code: -2 Luck on empty Pyramid),
0xc011d (Free) → E9 6b3efeff(jump to 0x0A3F8F (end of F8E))

Note, that this method assumes you have empty space at 0xc0110 (by forcing rumors to always show rumors defined by the mapmaker). If you have empty space elsewhere, simply move the function, altering the jumps appropriately.

Editing h3hota_maped

All heroes are defined one by one in the h3hota_maped starting from 0x186800.

GG000000 RR000000 HH000000 SO000000 OL000000 ST000000 TL000000 SB 000000 SP000000 U1000000 U2000000 U3000000 PS000000 PL000000 R0 AS CO 00

Note, that some HotA heroes use some data from HotA.dat

Game Mechanics[edit | hide]

Starting Resources[edit | hide]

Table with the starting resources starts at 0x278170. Each 4 bytes are the next resource (Wood, Mercury, Ore, Sulfur, Crystal, Gems, Gold). Each 28 bytes is the next difficulty - Easy, Normal, Expert, etc. At 0x2781FC the AI resources start and follow the same order of resources and difficulties. The whole table thus takes 280 bytes.

Secondary skills[edit | hide]

In the basic game, how secondary skills work is only determined by the .exe file (i.e. heroes3.exe). In HotA, the skills are both in the h3hota.exe and in HotA.dll files. The former is typically used in single player games, and the latter for multiplayer. When starting a multiplayer game the game loads the HotA DLL, and overrides parts of the hota EXE.

This section is intended to guide how to modify how secondary skills in multiplayer HotA.

Estates[edit | hide]

The daily gold awarded by estates in HotA multiplayer (as of 1.7.3) is in HotA.dll, offset 19A100 there is the byte sequence:

63 00 FA 00 00 00 C7 05 20 EA 63 00 F4 01 00 00

C7 05 24 EA 63 00 E8 03 00 00 D9 15 8C EA 63 00

The highlighted values are the amounts in hexadecimal (FA == 250, F401 == 500 and C803 == 1000).

Also note that the message you get when you right click on Estates skill is separate from the actual money given. It will still say 250 per day, since the text help is in different files, but you will get what you edited (check under kingdom income for example).

Mysticism[edit | hide]

The percentage of maximum mana regenerated with Mysticism is located at 0x001EC46C in HotA.dll (version 1.7.3). It is stored as four IEEE 32-bit floating point numbers, in little endian format.
00 00 00 00 CD CC CC 3D CD CC 4C 3E 9A 99 99 3E
where the first four bytes corresponds to the percentage gained without Mysticism, the next four bytes for basic level followed by advanced and expert.

00 00 00 00 == 0.0 (0%)
CD CC CC 3D == 0.1 (10%)
CD CC 4C 3E == 0.2 (20%)
9A 99 99 3E == 0.3 (30%)

The fixed number of points are located also in HotA.dll, offsets hota.dll, basic: 0x19a15E (05), advanced: 0x19a16a (0A), expert: 0x19a174 (0F).
(Shoutout to Csaros for finding some of these offsets).

Like with Estates, the right click help will still state 10%, 20% or 30% etc, but the actual mana regeneration depends on the numbers encoded in the DLL.

Pathfinding[edit | hide]

Pathfinding is spread out over several parts of the code. The part that allows for spending less than 100 MP per terrain is mostly in HotA.dll:

Caption text
File offset opcodes instruction comment
0x019a22b c7 40 04 5f 00 00 00 mov DWORD PTR [eax+0x4],0x5f ; 95 - dirt
0x019a232 a1 90 36 20 10 mov eax,ds:0x10203690
0x019a237 c7 40 08 5a 00 00 00 mov DWORD PTR [eax+0x8],0x5a ; 90
0x019a23e a1 90 36 20 10 mov eax,ds:0x10203690
0x019a243 c7 40 0c 55 00 00 00 mov DWORD PTR [eax+0xc],0x55 ; 85
0x019a24a a1 90 36 20 10 mov eax,ds:0x10203690
0x019a24f c7 40 14 7d 00 00 00 mov DWORD PTR [eax+0x14],0x7d ; 125 - sand
0x019a256 a1 90 36 20 10 mov eax,ds:0x10203690
0x019a25b c7 40 18 64 00 00 00 mov DWORD PTR [eax+0x18],0x64 ; 100
0x019a262 a1 90 36 20 10 mov eax,ds:0x10203690
0x019a267 c7 40 1c 55 00 00 00 mov DWORD PTR [eax+0x1c],0x55 ; 85
0x019a26e a1 90 36 20 10 mov eax,ds:0x10203690
0x019a273 c7 40 24 5f 00 00 00 mov DWORD PTR [eax+0x24],0x5f ; 95 - grass
0x019a27a a1 90 36 20 10 mov eax,ds:0x10203690
0x019a27f c7 40 28 5a 00 00 00 mov DWORD PTR [eax+0x28],0x5a ; 90
0x019a286 a1 90 36 20 10 mov eax,ds:0x10203690
0x019a28b c7 40 2c 55 00 00 00 mov DWORD PTR [eax+0x2c],0x55 ; 85
0x019a292 a1 90 36 20 10 mov eax,ds:0x10203690
0x019a297 c7 40 34 7d 00 00 00 mov DWORD PTR [eax+0x34],0x7d ; 125 - snow
0x019a29e a1 90 36 20 10 mov eax,ds:0x10203690
0x019a2a3 c7 40 38 64 00 00 00 mov DWORD PTR [eax+0x38],0x64 ; 100
0x019a2aa a1 90 36 20 10 mov eax,ds:0x10203690
0x019a2af c7 40 3c 55 00 00 00 mov DWORD PTR [eax+0x3c],0x55 ; 85
0x019a2b6 a1 90 36 20 10 mov eax,ds:0x10203690
0x019a2bb c7 40 44 96 00 00 00 mov DWORD PTR [eax+0x44],0x96 ; 150 - swamp
0x019a2c2 a1 90 36 20 10 mov eax,ds:0x10203690
0x019a2c7 c7 40 48 7d 00 00 00 mov DWORD PTR [eax+0x48],0x7d ; 125
0x019a2ce a1 90 36 20 10 mov eax,ds:0x10203690
0x019a2d3 c7 40 4c 64 00 00 00 mov DWORD PTR [eax+0x4c],0x64 ; 100
0x019a2da a1 90 36 20 10 mov eax,ds:0x10203690
0x019a2df c7 40 54 64 00 00 00 mov DWORD PTR [eax+0x54],0x64 ; 100 - rough
0x019a2e6 a1 90 36 20 10 mov eax,ds:0x10203690
0x019a2eb c7 40 58 5a 00 00 00 mov DWORD PTR [eax+0x58],0x5a ; 90
0x019a2f2 a1 90 36 20 10 mov eax,ds:0x10203690
0x019a2f7 68 f1 55 30 4f push 0x4f3055f1
0x019a2fc c7 40 5c 55 00 00 00 mov DWORD PTR [eax+0x5c],0x55 ; 85
0x019a303 a1 90 36 20 10 mov eax,ds:0x10203690
0x019a308 c7 40 64 5f 00 00 00 mov DWORD PTR [eax+0x64],0x5f ; 95 - subterranean
0x019a30f a1 90 36 20 10 mov eax,ds:0x10203690
0x019a314 c7 40 68 5a 00 00 00 mov DWORD PTR [eax+0x68],0x5a ; 90
0x019a31b a1 90 36 20 10 mov eax,ds:0x10203690
0x019a320 c7 40 6c 55 00 00 00 mov DWORD PTR [eax+0x6c],0x55 ; 85
0x019a327 a1 90 36 20 10 mov eax,ds:0x10203690
0x019a32c c7 40 74 5f 00 00 00 mov DWORD PTR [eax+0x74],0x5f ; 95 - lava
0x019a333 a1 90 36 20 10 mov eax,ds:0x10203690
0x019a338 c7 40 78 5a 00 00 00 mov DWORD PTR [eax+0x78],0x5a ; 90
0x019a33f a1 90 36 20 10 mov eax,ds:0x10203690
0x019a344 c7 40 7c 55 00 00 00 mov DWORD PTR [eax+0x7c],0x55 ; 85

To change the pathfinding minimum movement, one has to modify the values for each terrain, for each level of the skill. The new terrains introduced by HotA, wasteland and highlands, are not affected by this. Their movement costs are stored as plain ASCII text in HotA.dat.

Starting Bonuses[edit | hide]

Gold Bonus[edit | hide]

The starting gold bonus chooses a random value between the value written at 0x0C0002 and the value at 0x0BFFFD, and then multiplies it by 100. To change the multiplication, simply edit 0x0C000B (multiplying by 25) and 0x0C0016 (moving the value by 2 bits, so multiplying it by 4).

Resource Bonuses[edit | hide]

The starting resource bonuses use DWORD pointers to find their appropriate code to run:

  • Wood and Ore - AF FF 4B 00
  • Crystals - D6 FF 4B 00
  • Gems - E1 FF 4B 00
  • Mercury - EC FF 4B 00
  • Sulfur - F4 FF 4B 00

These DWORD pointers are located at the following addresses:

  • Castle - 0x0C01B4
  • Rampart - 0x0C01B8
  • Tower - 0x0C01BC
  • Inferno - 0x0C01C0
  • Necropolis - 0x0C01C4
  • Dungeon - 0x0C01C8
  • Stronghold - 0x0C01CC
  • Fortress - 0x0C01D0
  • Conflux - 0x0C01D4

Note, that you need to edit ScnrStar.def to reflect any altered resources.

The amount for each rare resource is most likely determined by the function at 0xc009e: ???

minimum: c745f0 33333333 (mov [ebp-0x10], 0x33333333) c745f0 3333EB3F (mov [ebp-0x1c], 0x3feb3333)
break between the two: eb 0e (jmp 0x0e)
maximum: c745f0 66666666 (mov [ebp-0x10], 0x66666666) c745f0 6666E63F (mov [ebp-0x10], 0x3FE66666)

The amount of wood and ore are most likely specified at 0xbffaf: BA 0a000000 (mov edx, 0x0a) B9 05000000 (mov ecx, 0x05)

Movement point reminder[edit | hide]

The movement point reminder is located at 0x9c91. It checks if movement points are 0 (using a 2-byte test eax, eax) and then proceeds to different code if movement points are (or not) zero. If one wants to change it to a higher number (to avoid reminding of movement for heroes that can't actually move anymore), the following code can be used:

0x9c91:       a1 c8876900 mov eax, [0x6987c8] e9 849d0d00 (jump to some arbitrary free space) 90909090
free space:  3d XY000000 cmp eax, XY - compare movement points with value XY.
   7F 0A jg 0x0a - jump 10 bytes if the value is greater.
   A1 C4 5d 6A 00 - displaced code
   e9 6f62f2ff - jump to 0x9c9f (or 49C9F)
   e9 a462f2ff - jump to 0x9cd9 (or 49CD9).

Hex value tables[edit | hide]

See Reference IDs.

External links

  1. heroescommunity.com - Editing heroes in memory - Includes a large number of various Reference IDs
  2. heroescommunity.com - How to edit Hota? - Thread with majority of useful information, scrambled across 112 forum pages.
  3. BTB2's hacking guide - primarily detailing creation of his own mod, but including tips, explanations and some of the Reference IDs
  4. void_17's database - this database describes most or all of the functions in the base game of heroes 3.