## RotW
Tested on v105.
Character section offset 232 (absolute file offset 248) now contains information for whether the character is in classic (1), expansion (2), or RotW (3) mode.
## Legacy D2R
The following is a list of my own observations from testing the editor on D2R (v99).
## Attributes
Saved is col 7
CSvSigned is col 8
CSvBits# is col 9
| Strength | 0 | 1 | 0 | 10 |
| Energy | 1 | 1 | 0 | 10 |
| Dexterity | 2 | 1 | 0 | 10 |
| Vitality | 3 | 1 | 0 | 10 |
| Stat Points Left | 4 | 1 | 0 | 10 |
| Skill Points Left | 5 | 1 | 0 | 8 |
| Hit Points (Current) | 6 | 1 | 0 | 21 |
| Hit Points (Max) | 7 | 1 | 0 | 21 |
| Mana (Current) | 8 | 1 | 0 | 21 |
| Mana (Max) | 9 | 1 | 0 | 21 |
| Stamina (Current) | 10 | 1 | 0 | 21 |
| Stamina (Max) | 11 | 1 | 0 | 21 |
| Level | 12 | 1 | 0 | 7 |
| Experience | 13 | 1 | 0 | 32 |
| Gold (Inventory) | 14 | 1 | 0 | 25 |
| Gold (Stash) | 15 | 1 | 0 | 25 |
## Character
The name now appears to fill 48 bytes. The names are still limited to 15 graphemes, but not 15 bytes. E.g using japanese kana that each take up 3 bytes, you may fill up 45 bytes of the name section.
You can't mix and match languages, てすと is valid but てtoす is not.
Setting the act/difficulty bytes in the Character section to e.g Hell on a fresh level 1 character won't allow them to enter Hell if they haven't unlocked it. However, setting the act will allow you to access an act you haven't unlocked yet within the same difficulty.
Loading a single player file with "Ladder" bit set to 1 in Character Status does nothing (duh).
The character level shown in the menu preview is the from the attributes section, but in-game it gets overridden by the level in character section.
Assigned skills have a default value of 0xFF 0xFF 0x00 0x00 before they are set (65535 in lower endian).
Theoretical skill values can go up to 255 in the save data, but the game clamps them to 99 in-game.
Observed in testing: switching character class while class-specific or otherwise incompatible items are equipped does not prevent the save from loading. The game removes those incompatible equipped items and still loads the save normally.
#### Legacy Character Menu Appearance
32 bytes starting at offset 136.
Default value is 0xFF.
Byte 141: Weapon
Some codes gleaned from testing:
* 04: Hand Axe
* 09: Wand
* 0D: Morning Star
* 0F: Flail
* 12: Sabre/Scimitar
* 13: Falchion
* 14: Crystal Sword
* 15: Broad Sword
* 1B: Short Spear/Knife
**Bytes 137..141 and 144..146: Armor**
As far as I can tell, bytes 137..141 encode the visuals of the body armor, and 144..145 decide the shoulderpads.
Good resource: http://paul.siramy.free.fr/_divers2/Extracting%20Diablo%20II%20Animations.pdf (Page 11)
Bytes 144..145 could be S1 and S2, and 137..141 obviously contain TR and probably RA and LA. More testing needed.
### Mercenary
Starts at offset 177
**Mercenary ID**
Offset 179, 4 bytes
Appears to be 0 if you have never hired a merc, otherwise randomly generated.
Can be edited with seemingly no impact.
**Mercenary Name**
Appears unchanged from the list at http://user.xmission.com/~trevin/DiabloIIv1.09_Mercenaries.html except for a typo, the first Barbarian name should be Vardakha.
**Mercenary Variant**
| 0 | 00 00 | Normal | A1 | Fire |
| 1 | 01 00 | Normal | A1 | Cold |
| 2 | 02 00 | Nightmare | A1 | Fire |
| 3 | 03 00 | Nightmare | A1 | Cold |
| 4 | 04 00 | Hell | A1 | Fire |
| 5 | 05 00 | Hell | A1 | Cold |
| 6 | 06 00 | Normal | A2 | Prayer |
| 7 | 07 00 | Normal | A2 | Defiance |
| 8 | 08 00 | Normal | A2 | Blessed Aim |
| 9 | 09 00 | Nightmare | A2 | Thorns |
| 10 | 0A 00 | Nightmare | A2 | Holy Freeze |
| 11 | 0B 00 | Nightmare | A2 | Might |
| 12 | 0C 00 | Hell | A2 | Prayer |
| 13 | 0D 00 | Hell | A2 | Defiance |
| 14 | 0E 00 | Hell | A2 | Blessed Aim |
| 15 | 0F 00 | Normal | A3 | Fire |
| 16 | 10 00 | Normal | A3 | Cold |
| 17 | 11 00 | Normal | A3 | Lightning |
| 18 | 12 00 | Nightmare | A3 | Fire |
| 19 | 13 00 | Nightmare | A3 | Cold |
| 20 | 14 00 | Nightmare | A3 | Lightning |
| 21 | 15 00 | Hell | A3 | Fire |
| 22 | 16 00 | Hell | A3 | Cold |
| 23 | 17 00 | Hell | A3 | Lightning |
| 24 | 18 00 | Normal | A5 | Bash |
| 25 | 19 00 | Normal | A5 | Bash |
| 26 | 1A 00 | Nightmare | A5 | Bash |
| 27 | 1B 00 | Nightmare | A5 | Bash |
| 28 | 1C 00 | Hell | A5 | Bash |
| 29 | 1D 00 | Hell | A5 | Bash |
| 30 | 1E 00 | Nightmare | A2 | Prayer |
| 31 | 1F 00 | Nightmare | A2 | Defiance |
| 32 | 20 00 | Nightmare | A2 | Blessed Aim |
| 33 | 21 00 | Hell | A2 | Thorns |
| 34 | 22 00 | Hell | A2 | Holy Freeze |
| 35 | 23 00 | Hell | A2 | Might |
| 36 | 24 00 | Normal | A5 | Frenzy |
| 37 | 25 00 | Nightmare | A5 | Frenzy |
| 38 | 26 00 | Hell | A5 | Frenzy |
The codes have not been changed since 1.09: http://user.xmission.com/~trevin/DiabloIIv1.09_Mercenaries.html#code
Instead, the new codes added with patch 2.4 of D2R (Nightmare A2 mercs with Prayer/Defiance/Blessed Aim, Hell A2 mercs with Thorns/Holy Freeze/ Might, and Frenzy Barbarians) have been appended to the table.
This also explains why Qual-Kehk usually has more Bash barbs than Frenzy: the two old codes per difficulty still mean bash, whereas there is only one of the new frenzy code per difficulty.
Mercenaries require different amounts of XP to get to a certain level, depending on both their type and the last difficulty beaten by the character they were recruited by.
| A1 | Fire | 100 | 110 | 120 |
| A1 | Cold | 105 | 115 | 125 |
| A2 | All | 110 | 120 | 130 |
| A3 | Fire | 110 | 120 | 130 |
| A3 | Lightning | 110 | 120 | 130 |
| A3 | Cold | 120 | 130 | 140 |
| A5 | All | 120 | 130 | 140 |
The formula to calculate experience based on level and XP Rate is as follows: $XP Rate * (Level + 1) * (Level^2)$
Inversely, getting the mercenary level based on current experience and XP Rate requires solving the following cubic polynomial:
$(level + 1)(level^2) = \dfrac{Experience}{XP Rate}$, or using $x$ for $level$: $x³ + x² = \dfrac{Experience}{XP Rate}$
Since x > 0 (the possible values for levels are 1-98), we know that $x^3 < x^3 + x^2 < (x+1)^3$ .
Therefore, $\sqrt[3]{x^3} < \sqrt[3]{x^3 + x^2} < \sqrt[3]{(x + 1)^3}$ or more simply $x < \sqrt[3]{x^3 + x^2} < x + 1$.
Since we know $x^3 + x^2 = \dfrac{Experience}{XP Rate}$, we get the final expression:
$Level < \sqrt[3]{ \dfrac{Experience}{XP Rate}} < Level + 1$
A possible candidate for $x$ is the floor of the cubic root of our experience/xp rate, $s = \lfloor\sqrt[3]{\dfrac{Experience}{XP Rate}}\rfloor$ . However, it is possible that $s^2 + s^3 > \dfrac{Experience}{XP Rate}$. In that case, we need to take $s - 1$.
The final algorithm is as follows:
```
Let s = floor((experience/xp_rate)**1/3)
if experience/xp_rate < s^3 + s^2:
return s - 1
else:
return s
```
#### Example:
A2 Hell mercenary with 99040759 XP.
```
experience/xp_rate = 99040759/130 = 761851.992308
s = floor(761851.992308^(1/3)) = 91
91^3 + 91^2 = 761852
experience/xp_rate < 761852, therefore level = s - 1 = 90
```
Observed in testing: switching mercenary type while the mercenary has items equipped that are invalid for the new type does not prevent the save from loading. The game removes those incompatible items and still loads the save normally.
### Halbu Implementation Note
When no mercenary is hired, the entire 14-byte mercenary block must be zeroed. Saves with `merc_id = 0` but nonzero merc fields (name, variant, experience) are invalid and may fail to load.
Halbu does not currently rewrite the jf mercenary item subsection in the post-skills tail. That section is preserved as raw bytes.
As a result, changing `mercenary.id` between `0` and nonzero is reported as a blocking compatibility issue when `CompatibilityChecks::Enforce` is used. Callers can still force encoding with `CompatibilityChecks::Ignore`.
Because merc hire-state also affects the presence and shape of that subsection, changing `mercenary.id` across the 0/nonzero boundary is unsafe unless the caller explicitly forces encoding.
## Quests
The data for every quest is held in 2 bytes. The quests structure contains 8 quests for every act except act 5, which has a different structure and padding at the end.
The first "quest" is the prologue to a new act, then come 6*2 bytes for every quest (3 in act 4 + 3 unused), and a completion quest.
The prologue and completion quest only ever take a single flag, QFLAG_REWARDGRANTED, to signal true/false, which is also bit 0, so you can effectively treat them as 0/1.
The prologue controls whether your character has been introduced to a given act, e.g has spoken to Warriv in Act I. The completion quest controls whether your character can use the waypoint to the next act, except for Act IV which requires you instead to have beaten Q2 (Terror's End).
Act V's completion quest also holds the information for Akara's skill/stat reset. The flags used are QFLAG_REWARDPENDING if a reset is available in this difficulty and QFLAG_REWARDGRANTED if it has been used. QFLAG_COMPLETEDBEFORE seems to be set if either the reset has been used or the difficulty has been completed. Trying to set QFLAG_REWARDPENDING without completing Den of Evil seems to clear this quest's flags.
**Warning**: There are differences between the order the quests are stored in and the order they appear in the game for Act I, III and IV. Refer to this table when editing quests.
#### Act I
| 1 | Den of Evil | 1 |
| 2 | Sisters' Burial Grounds | 2 |
| 3 | Tools of the Trade | 5 |
| 4 | The Search For Cain | 3 |
| 5 | The Forgotten Tower | 4 |
| 6 | Sisters to the Slaughter | 6 |
#### Act III
| 1 | Lam Esen's Tome | 4 |
| 2 | Khalim's Will | 3 |
| 3 | Blade of the old Religion | 2 |
| 4 | The Golden Bird | 1 |
| 5 | The Blackened Temple | 5 |
| 6 | The Guardian | 6 |
#### Act IV
| 1 | The Fallen Angel | 1 |
| 2 | Terror's End | 3 |
| 3 | Hell's Forge | 2 |
Some of the flags are constant, others depend on the quest.
Here is the list, from [D2MOO](https://github.com/ThePhrozenKeep/D2MOO/blob/57dcc6ceb493a33dfba82461bd96dd04adb471fe/source/D2CommonDefinitions/include/D2Constants.h#L587).
*Note: The names are straight from D2MOO and may be changed yet.*
| QFLAG_REWARDGRANTED | 0 |
| QFLAG_REWARDPENDING | 1 |
| QFLAG_STARTED | 2 |
| QFLAG_LEAVETOWN | 3 |
| QFLAG_ENTERAREA | 4 |
| QFLAG_CUSTOM1 | 5 |
| QFLAG_CUSTOM2 | 6 |
| QFLAG_CUSTOM3 | 7 |
| QFLAG_CUSTOM4 | 8 |
| QFLAG_CUSTOM5 | 9 |
| QFLAG_CUSTOM6 | 10 |
| QFLAG_CUSTOM7 | 11 |
| QFLAG_UPDATEQUESTLOG | 12 |
| QFLAG_PRIMARYGOALDONE | 13 |
| QFLAG_COMPLETEDNOW | 14 |
| QFLAG_COMPLETEDBEFORE | 15 |
Example of some stages of Den of Evil:
| Quest not started | 0x00 0x00 | 0000 0000 0000 0000 | None |
| Quest started (Talked to Akara) | 0x04 0x00 | 0000 0000 0000 0100 | QFLAG_STARTED |
| Cleared Den of Evil (Return to Akara for reward) | 0x1C 0x00 | 0000 0000 0001 1100 | QFLAG_STARTED QFLAG_LEAVETOWN QFLAG_ENTERAREA |
| Talked to Akara (Completed quest) | 0x01 0x30 | 0011 0000 0000 0001 | QFLAG_REWARDGRANTED QFLAG_UPDATEQUESTLOG QFLAG_PRIMARYGOALDONE |
Akara reset (offset 82 out of 96) seems to be set to 2 if unlocked but not used, and to 1 if used.
## Waypoints
A new character will have three waypoints unlocked by default: Rogue encampment in normal, nightmare and hell.
## Items
The JM header at the top of every item has been removed in D2R.