Reverse engineering Diablo v1.09b (last patch)
#1
Hello.

Does anyone here have a compiled list of any sort of data regarding Diablo? It does not have to be 'of interest' (which is relative from person-to-person).

For instance:
Code:
004575D5 | 55                       | push    ebp                                       | Spell-casting related
004575D6 | 8B EC                    | mov     ebp,esp                                   |
004575D8 | 51                       | push    ecx                                       |
004575D9 | 51                       | push    ecx                                       |
004575DA | 8B 45 18                 | mov     eax,dword ptr ss:[ebp+0x18]               |
004575DD | 53                       | push    ebx                                       |
004575DE | 33 DB                    | xor     ebx,ebx                                   |
004575E0 | 56                       | push    esi                                       |
004575E1 | 2B C3                    | sub     eax,ebx                                   |
004575E3 | 89 4D FC                 | mov     dword ptr ss:[ebp-0x4],ecx                |
004575E6 | 74 11                    | je      diablo_copy.4575F9                        |
004575E8 | 48                       | dec     eax                                       |
004575E9 | 75 30                    | jne     diablo_copy.45761B                        |
004575EB | 69 C9 E4 00 00 00        | imul    ecx,ecx,0xE4                              |
004575F1 | 8B 81 78 D3 64 00        | mov     eax,dword ptr ds:[ecx+0x64D378]           |
004575F7 | EB 1F                    | jmp     diablo_copy.457618                        |
004575F9 | 8B C1                    | mov     eax,ecx                                   |
004575FB | 89 5D 18                 | mov     dword ptr ss:[ebp+0x18],ebx               | ebp+18:LocalFree
004575FE | 69 C0 D8 54 00 00        | imul    eax,eax,0x54D8                            | Setting EDX here modifies the casted spell
00457604 | 83 FA 06                 | cmp     edx,0x6                                   | EDX = Spell index
00457607 | 8B 88 B8 64 68 00        | mov     ecx,dword ptr ds:[eax+0x6864B8]           | ECX = Position in relation to the character
0045760D | 89 4D F8                 | mov     dword ptr ss:[ebp-0x8],ecx                |
00457610 | 75 09                    | jne     diablo_copy.45761B                        |
00457612 | 8B 80 24 66 68 00        | mov     eax,dword ptr ds:[eax+0x686624]           | Special case: Firewall only
00457618 | 89 45 F8                 | mov     dword ptr ss:[ebp-0x8],eax                |
0045761B | 6B D2 38                 | imul    edx,edx,0x38                              | Multiplies EDX by 0x38
0045761E | 57                       | push    edi                                       |
0045761F | 33 FF                    | xor     edi,edi                                   | edi:PeekMessageA
00457621 | 8D B2 E9 23 4A 00        | lea     esi,dword ptr ds:[edx+0x4A23E9]           |
00457627 | 38 1E                    | cmp     byte ptr ds:[esi],bl                      |
00457629 | 74 2E                    | je      diablo_copy.457659                        | Prevents spell casting (JMP)
0045762B | 83 FF 03                 | cmp     edi,0x3                                   | EDX is the spell index?
0045762E | 7D 29                    | jge     diablo_copy.457659                        | Prevents spells from having any effect (minus audio/character "spell animation")
00457630 | FF 75 1C                 | push    dword ptr ss:[ebp+0x1C]                   | "In-town check" not received yet
00457633 | 8B 55 0C                 | mov     edx,dword ptr ss:[ebp+0xC]                |
00457636 | 0F B6 04 3E              | movzx   eax,byte ptr ds:[esi+edi]                 | Loads Charged Bolt?
0045763A | 53                       | push    ebx                                       |
0045763B | 8B 4D 08                 | mov     ecx,dword ptr ss:[ebp+0x8]                |
0045763E | FF 75 FC                 | push    dword ptr ss:[ebp-0x4]                    |
00457641 | FF 75 18                 | push    dword ptr ss:[ebp+0x18]                   | ebp+18:LocalFree
00457644 | 50                       | push    eax                                       |
00457645 | FF 75 F8                 | push    dword ptr ss:[ebp-0x8]                    |
00457648 | FF 75 14                 | push    dword ptr ss:[ebp+0x14]                   |
0045764B | FF 75 10                 | push    dword ptr ss:[ebp+0x10]                   |
0045764E | E8 87 5D FD FF           | call    <diablo_copy.sub_42D3DA>                  | Spell animation / Casting
00457653 | 47                       | inc     edi                                       | edi:PeekMessageA
00457654 | 38 1C 3E                 | cmp     byte ptr ds:[esi+edi],bl                  |
00457657 | 75 D2                    | jne     diablo_copy.45762B                        |
00457659 | 80 3E 0A                 | cmp     byte ptr ds:[esi],0xA                     | A:'\n'
0045765C | 5F                       | pop     edi                                       | edi:PeekMessageA
0045765D | 75 0B                    | jne     diablo_copy.45766A                        | ECX holds spell index
0045765F | 8B 4D FC                 | mov     ecx,dword ptr ss:[ebp-0x4]                |
00457662 | 6A 07                    | push    0x7                                       |
00457664 | 5A                       | pop     edx                                       | EDX holds spell index again
00457665 | E8 D0 FE FF FF           | call    <diablo_copy.sub_45753A>                  |
0045766A | 80 3E 34                 | cmp     byte ptr ds:[esi],0x34                    | 34:'4'
0045766D | 75 3C                    | jne     diablo_copy.4576AB                        |
0045766F | 8B 4D FC                 | mov     ecx,dword ptr ss:[ebp-0x4]                | Special case: Charged Bolt
00457672 | 6A 1E                    | push    0x1E                                      | Charged Bolt spell index (1E)
00457674 | 5A                       | pop     edx                                       | push/pop set EDX to Charged Bolt spell index (1E)
00457675 | E8 C0 FE FF FF           | call    <diablo_copy.sub_45753A>                  |
0045767A | 8B 45 1C                 | mov     eax,dword ptr ss:[ebp+0x1C]               |
0045767D | D1 F8                    | sar     eax,0x1                                   |
0045767F | 83 C0 03                 | add     eax,0x3                                   | Sets EAX = 10, then copies to ESI
00457682 | 3B C3                    | cmp     eax,ebx                                   |
00457684 | 7E 25                    | jle     diablo_copy.4576AB                        | JMP = 1x1 Charged Bolt
00457686 | 8B F0                    | mov     esi,eax                                   |
00457688 | FF 75 1C                 | push    dword ptr ss:[ebp+0x1C]                   |
0045768B | 8B 55 0C                 | mov     edx,dword ptr ss:[ebp+0xC]                |
0045768E | 8B 4D 08                 | mov     ecx,dword ptr ss:[ebp+0x8]                |
00457691 | 53                       | push    ebx                                       |
00457692 | FF 75 FC                 | push    dword ptr ss:[ebp-0x4]                    |
00457695 | FF 75 18                 | push    dword ptr ss:[ebp+0x18]                   |
00457698 | 6A 06                    | push    0x6                                       | Flag for Charged Bolt?
0045769A | FF 75 F8                 | push    dword ptr ss:[ebp-0x8]                    |
0045769D | FF 75 14                 | push    dword ptr ss:[ebp+0x14]                   |
004576A0 | FF 75 10                 | push    dword ptr ss:[ebp+0x10]                   |
004576A3 | E8 32 5D FD FF           | call    <diablo_copy.sub_42D3DA>                  |
004576A8 | 4E                       | dec     esi                                       |
004576A9 | 75 DD                    | jne     diablo_copy.457688                        | ESI = Loop counter (# of Charged Bolts?)
004576AB | 5E                       | pop     esi                                       |
004576AC | 5B                       | pop     ebx                                       |
004576AD | C9                       | leave                                             |
004576AE | C2 18 00                 | ret     0x18                                      |

This entire function pertains to spell casting, but this is before(?) the "in-town" check is performed AFAIK.

You can manually edit the EAX register for the one-time-call on Charged Bolt (0x34), then modify the actual function call in the loop (push XX). The loop counter is directly affected by the level of the Charged Bolt (which is 'protected' I believe (the value is constantly written to in two different locations)).

You can in essence create a multi-shot Firebolt (spawn 15+ Firebolts that are all stacked). It'll "pierce" through a target (visual effect) and hit the next target and so on and so forth. The movzx is what sets the EAX register to 0x34, which is read inside the function (0045764E | E8 87 5D FD FF | call <diablo_copy.sub_42D3DA>).

If you are new to reverse engineering or are simply interested in learning: Feel free to ask questions and I will provide as many in-depth explanations as I can.
If you are very familiar with reverse engineering Diablo: I'd love to pick your brain on a few topics of how Diablo was written.

Thanks in advance! Smile
Reply
#2
Define sort of data?

What are you looking for?
Reply
#3
(01-29-2018, 07:54 AM)Sir Krist Wrote: Define sort of data?

What are you looking for?

Anything and everything!

Right now: I am looking into how maps are generated. Including player position.
EG: When you join a new game.

It seems I was actually right on the spot. I ended up toggling the hack that shows the answer (I was 9 offsets away).

The function entry (my break-point / comment):
Code:
00440A05 | 53                       | push    ebx                                       | Spawn location (join-game only?)

My variable break-point:
Code:
00440A7B | C6 80 87 65 68 00 01     | mov     byte ptr ds:[eax+0x686587],0x1            | Flag to force displaying "Player XXXX joined?"

The actual answer was here:
Code:
00440A30 | 8B 0C 85 F8 0E 4A 00     | mov     ecx,dword ptr ds:[eax*4+0x4A0EF8]         | Player X (spawn location)
00440A37 | 8B 14 85 1C 0F 4A 00     | mov     edx,dword ptr ds:[eax*4+0x4A0F1C]         | Player Y (spawn location)

EDIT: To add just a smidge more of information:
The town's dimensions are: [00, 00] to [5F, 5F].

This flips the negative value to a positive (from: xFFFFFFD9 for example).
Code:
00440A44 | 83 C1 4B                 | add     ecx,0x4B                                  |
00440A47 | 83 C2 44                 | add     edx,0x44                                  |

Code:
00440A30 | B9 D9 FF FF FF           | mov     ecx,0xFFFFFFD9                            | Player X (spawn location)
00440A35 | 90                       | nop                                               |
00440A36 | 90                       | nop                                               |
00440A37 | BA D2 FF FF FF           | mov     edx,0xFFFFFFD2                            | Player Y (spawn location)
00440A3C | 90                       | nop                                               |
00440A3D | 90                       | nop                                               |

This will spawn you on the graveyard (on the tombstone that fell over).
I cannot seem to find what causes the X/Y to lap over, so I can calculate where you will spawn on the map.

Looks like I'll have to dig into the program deeper. Smile

You can explicitly set ECX / EDX to the exact desired position (range: B5, BC is the 'far upper left corner').

I do not have a copy of the technical manual that Zamal had on the forums.

To clarify: I do not have any particular set objective. I am just enjoying myself, learning how to reverse engineer games / programs again (since I have some free time right now).

I figured I could compile a more comprehensive and up-to-date list for Diablo, for all the old fans (like myself!).
There are many bugs listed in this site and I am sure the majority of them could be patched unofficially.

You could also add items (I can build utilities easily, given I have some data to work with).
I noticed there were a lot of unused spells (audio / graphics) that function just fine on Diablo v1.09.
Even with the game hacks: A lot of the spells and potential options were never used.

In fact: There's a "Golem" you can spawn (they are all attached internally to the same AI mechanism, acting as a 'singular' unit). It charges like an Elemental, then roams freely like a typical Golem (there is no quantity delimiter).

Fixing that (or modifying it even) would be very small.
Reply
#4
This is a prime example of reverse engineering (although technically hacking: It changes absolutely nothing about game play):
Code:
0041B1DF | 56                       | push    esi                                       | Updates "loading screen" (bar on bottom)
0041B1E0 | E8 BB FF FF FF           | call    <diablo_copy.sub_41B1A0>                  |
0041B1E5 | 83 05 B0 4C 63 00 0F     | add     dword ptr ds:[0x634CB0],0xF               |
0041B1EC | BE 16 02 00 00           | mov     esi,0x216                                 |
0041B1F1 | 39 35 B0 4C 63 00        | cmp     dword ptr ds:[0x634CB0],esi               |
0041B1F7 | 76 06                    | jbe     diablo_copy.41B1FF                        |
0041B1F9 | 89 35 B0 4C 63 00        | mov     dword ptr ds:[0x634CB0],esi               |
0041B1FF | 83 3D A8 4C 63 00 00     | cmp     dword ptr ds:[0x634CA8],0x0               |
0041B206 | 74 05                    | je      diablo_copy.41B20D                        |
0041B208 | E8 0B 00 00 00           | call    <diablo_copy.sub_41B218>                  |
0041B20D | 39 35 B0 4C 63 00        | cmp     dword ptr ds:[0x634CB0],esi               |
0041B213 | 5E                       | pop     esi                                       |

If you NOP this entire segment (displayed here): You will no longer have the intermediate 'loading screen' (which adds a pointless delay) while changing levels in Diablo! Town Portal, stairs of any kind.

Alternatively: You can just modify the JZ/JE to JMP (avoiding the function call that is directly responsible for updating the screen?).
Code:
0041B206 | EB 05                    | jmp     diablo_copy.41B20D                        |

EDIT: This actually affects joining/creating games on Battle.net as well.

By by-passing the loading screen (which used to cause issues on Windows XP on my old machine for some reason): The game loads WAY faster!

The reverse engineering tool (debugger) I am using (which is profoundly extensive and powerful) is: x32dbg (100% free! It also has a x64dbg version).

You are able to:
  • View 5 dumps of the .data (tabs)
  • View the stack with a specified calling convention (levels deep are modifiable with 1 click! Default value is "5 deep")
  • "Watch dog" (during a break-point: Any listed expressions / addresses are updated; True/False, Changed/Not changed flags)
  • Comments anywhere (very useful)
  • Labels (instead of "module.randomSymbol" you insert your OWN label! Such as: diablo.exe_loadingScreen())
  • Enable/Disable break-points (and an entire screen of disabled break-points)
  • String references (seems very accurate) for the specific module/module(s)/etc
  • Built-in C-code generator ("Snowman"); Which is equally very accurate at generating C-structs when they appear
  • Provides endless plugins for anything missing
  • Attempts to hide the debugger if you want (toggled: "Debug -> Advanced -> Hide debugger (PEB)")
Reply
#5
Here's an explicit question:

No matter how the new Level 1 Dungeon (Cathedral entrance): These two X/Y coordinates are set to x1D and x19.

Player Y coordinate - 006864A4 = 0x1D
Player X coordinate - 006864A0 = 0x19

8 bytes difference between all variables listed:
Code:
00686488 -- Modified per tile walked, every tile (iteration?)
00686490 -- Modified when clicking on a tile (clicked on tile value?)
00686498 -- Modified when clicking on a tile (saves previous Y tile?)
006864A0 -- Modified with a delay (after the animation 100% completes?) -- first and only variable modified upon a new dungeon level

Ideas? Suggestions?

Are 0x1D and 0x19 hard-coded values describing the "entry" of the maze-generation? To guarantee that the stairs themselves are in a room with possible exiting cells?
Based on the fact that 006864A0 is the first initially updated X variable: Is it safe to assume that this variable is the one connected to network play (sends/receives to other players)?

If this is the case: You could potentially fix the 'lag' experienced on Battle.net by following the variable around and increasing/decreasing the latency in-game.
Of course: Both players would want to use the same latency marker. This way: Neither player experiences lag when moving across tiles!

Smile

EDIT:

When you have no legitimate messages from another player in the game: There's a default crash (which is odd, but at least Blizzard made sure to eliminate any issues) message.
[Image: wT4c2di.png]
Reply
#6
In regards to "items" (inventory + what you're wearing are the "same" as far as I can see):
Code:
00686D7C - 00 00 00 00 00 00 00 00 D1 38 52 11 01 01 00 00

I assume this is somehow tied to the monster (or NPC?) that spawned the item.

Code:
00686D8C - 03 00 00 00

This represents the graphic image to load for your character (00 = no weapon, 01 = sword, 02 = axe, 03 = bow, 04 = mace; I guess?).

Code:
00686D90 - 2D 00 00 00 2D 00 00 00

This describes the X/Y map that you picked up the item from (I do not see why this is stored with the item data); This is apart of preventing duplicate items?

There's also some sort of fail-safe for 'morphed' items(?) that are defaulted into an icon of a blue Potion of Full Mana (without any text to display, no effects when consumed).

EDIT: The actual item X/Y from the map is changed every time you drop the item and pick it up (it only changes when dropped again).

The item's "current" durability (X / 255) is here:
Code:
00686E70

The item's "maximum" durability (255 / X) is here:
Code:
00686E74

I should use the player base structure + offset to describe this, but for now: This will work.
This is for the item that is currently in the "weapon slot" of the player's character.

When both variables are set to 0xFF: The item is listed as "indestructible."

If the item's current durability is set to "0": When the item loses a durability, it wraps around to -1 (0xFF 0xFF 0xFF 0xFF). The item no longer 'disappears.'
The maximum negative value: 0x00 0x00 0x00 0xFF.

A screen shot:
[Image: 27tHcKg.png]

Item minimum damage (255):
Code:
00686E50 - FF 00 00 00

Item maximum damage (255):
Code:
00686E54 - FF 00 00 00

Item maximum charges (255):
Code:
00686E68 - 0xFF

Item charges type (???):
Code:
00686E5C - 17 00 00 00 02 00 00 00

Item STR / MAG / DEX requirement:
Code:
00686EE4 - 17 11 13 00

It appears the last byte is unused.

Unusable item flag (set when identifying a previously used item, that you no longer match the requirements with/for? Shrines?); 01 = Usable, 00 = Unusable:
Code:
00686EE8 - 01

Item graphics index:
Code:
00686E44 - 01

This graphics index appears to modify how the item graphics are drawn when the item is thrown on the floor; It also directly effects the item size.

Item flag denoting it is a "2-handed weapon?" (Graphics routine draws a ghosted image on the shield slot and other modifications via the game); 01 = 1-handed, 02 = 2-handed:
Code:
00686E41 - 01

Item string reference index?
Code:
00686E60 - 02

Item charge item (01 = Firebolt; Seems to use another spell index than the usual one for "book based spells"):
Code:
00686E64 - 01

Current experience:
Code:
00686604 - 0xAD 0xEF 0x00 0x00

First item in your character's file:
Code:
0x00686448 + 0x374

This is the very first! Byte in the item. It describes the item's individual ID (and should be 'unique' to prevent duplication).
The total item size is: 0x170 (368) bytes long.

The actual item itself can be described as a structure, I believe. There are too many potential variables and flags to say it is composed of raw integer/C-string declarations.
How books are generated are odd. That may be uniquely hacked together by the Blizzard team (as are many things in Diablo, that I've seen so far).

Scrolls, Potions, Staves and other items, though, look quite commonly similar in structure.

It should go something like:
Code:
006867C4 (8 bytes) unique item ID - F4 E6 D9 65 12 08 00 00
006867CC (4 bytes) item graphics / item equipped flag (FF FF FF FF = no item found) - 07 00 00 00
006867D0 (4 bytes) item Y coordinate (where you picked it up at) - 39 00 00 00
006867D4 (4 bytes) item X coordinate (where you picked it up at) - 41 00 00 00
006867D8 (4 bytes) ???? (all 00) - 00 00 00 00
006867DC (4 bytes) ???? (does not affect duplicating items) - BC 93 E7 05
006867E0 (4 bytes) ???? (when picking up a dropped item: This rewrites itself) - 0D 00 00 00
006867E4 (4 bytes) ???? (when picking up a dropped item: This rewrites itself) - 0D 00 00 00
006867E8 (4 bytes) ???? (when picking up a dropped item: This rewrites itself) - 60 00 00 00
006867EC (8 bytes) ???? (when picking up a dropped item: This rewrites itself) - 10 00 00 00 00 00 00 00
006867F4 (8 bytes) ???? (when picking up a dropped item: This rewrites itself) - 01 00 00 00 00 00 00 00
006867FC (4 bytes) magic item identified (01 = identified, 00 = unidentified) - 01 00 00 00
00686800 (1 byte) (flag depicting item type) - 01
00686801 (?? bytes) (item title ("Crown," "Dagger," etc)
00686841 (?? bytes) (item name)
00686880 (4 bytes) (item placement) - 00 04 02 00 (Helm)
00686884 (4 bytes) (item graphics) - 5D 00 00 00 (Helm "skull cap")
00686888 (4 bytes) ???? (item does not rewrite value) - 19 00 00 00
0068688C (4 bytes) ???? (item does not rewrite value) - 19 00 00 00
00686890 (8 bytes) ???? (reserved for special attributes?) - 00 00 00 00 00 00 00 00
00686898 (4 bytes) ("armor" attribute added) - FF 00 00 00
0068689C (20 bytes) ???? (reserved for special attributes?) - 00
006868B0 (4 bytes) (item current durability) - FF 00 00 00
006868B4 (4 bytes)  (item maximum durability) - FF 00 00 00
006868B8 (48 bytes) ???? (reserved for special attributes?) - 00
006868F4 (4 bytes) ???? (when picking up a dropped item: This rewrites itself) - 00 01 00 00
006868F8 (24 bytes) ???? (reserved for special attributes?) - 00
00686910 (4 bytes) ???? (item flags for modifiers?) - FF FF 00 00
00686914 (16 bytes) ???? (reserved for special attributes?) - 00
00686928 (4 bytes) (invalid item flag) - 01 00 00 00
0068692C (4 bytes) ???? (item does not rewrite value) - 31 00 00 00

Item held by the mouse(?):
Code:
0068B700

repe movsd uses ECX as the counter, EDI as the base entry mark and ESI as the stop copying marker.
Translation: "repe movsd" copies X amount of bytes (ECX) starting from EDI to ESI.

This is the beginning offset of the item (0068B700) that the item data is written to. repe movsd copies the item's exact data to the location (Helm, Armor, Weapon, Shield, Jewelry).

This seems important as it pertains to 'item morphing.'

If you directly modify an item's armor (EG: Helm). Upon saving the game (or possibly when you load it): The item's armor is changed back, miraculously!
The instruction "repe movsd" is the culprit. I am about to start digging deeper into it, to find out where this is loading it's information from.

... I can definitely see why a lot of the old hacks simply left it as: "Item morphing will/may occur." Although I am still confused as to why item drops from monsters will/do morph.

I am thoroughly confused now! There is one segment of Diablo.exe that writes the magical/unique properties to items (including any modifiers they may contain).
0068B700 is the beginning address. It is 92 bytes in size. I've seen repe movsd "add" more than 5C (from the push 0x5C statement). It added 0x1C after 0x5C was there initially.

Beginning of user belt?
Code:
0068AB66
Reply
#7
This is unrelated to items, but my posts are all over the wall (for now).

Here are the spell indices that I've found:
Code:
NOTE: 11 (XX 00 00 00 ) is hilarious!
004A27A0 -> 1F 02 2C 2A

Spells are cut up into 4 parts(?)
1st - Audio file to play
2nd - Spell attack
3rd - Spell attack
4th - Spell attack

(ALL TESTS DONE AS: 00 XX 00 00 -- Latest patch: v1.09b)
004A27A0 (Blood Ritual)
00 = NULL (no spell effect)
01 = Firebolt
02 = Guardian
03 = Phasing (random Teleport)
04 = "Lightning bolt" (Lightning without the train of lightning bolts)
05 = "Self-immolation" (sets the tile you're on, on fire (Firewall); Long timer)
06 = Fireball
07 = Lightning
08 = "Self-electrocution" (sets a 'lightning bolt' under the tile you're on)
09 = Meteor (1x1 tile of fire damage appears at random (once))
10 = CRASH
0A = Town Portal
0B = Flash (south; audio)
0C = Flash (north; no audio)
0D = Mana shield
0E = Firestorm (1x1 tile of Firewall)
0F = Chain Lightning
11 = Bubbles (audio only; No known damage)
12 = "Bone Spirit particles" (1x1 animated tile of skeletal parts?)
13 = "Stone Curse particles" (1x1 short cast of stone parts falls?)
14 = audio only?
15 = "Invisible Firebolt" (light effects with weapon-fire-damage hit at end point)
16 = Lightning
17 = audio only?
18 = Bloodstar (with odd audio file)
19 = Bloodstar (only casts when by a door; No audio)
1A = Teleport
1B = Arrow with fire damage
1C = CRASH
1D = "Self-immolation" (sets the tile you're on, on fire (Firewall); Short timer)
1E = Stone Curse
1F = audio only?
20 = CRASH
21 = "Fire spell" audio
22 = Etheralize
23 = "Bloodbath" (spews a pool of blood from yourself)
24 = "Aimed nuke" (1x1 tile 'Apocalypse' spell)
25 = Heal
26 = Firewall
27 = Infravision
28 = Identify
29 = Guardian
2A = Nova
2B = NULL(?)
2C = Apocalypse
2D = Warrior skill
2E = Sorcerer skill
2F = Rogue skill
30 = "Self-Inferno" (1x1 Inferno on self; No damage dealt self; A monster can walk on the tile and die)
31 = Inferno
32 = "Projectile Stone Golem" (Stone Golem flings out like a Bone Spirit/Elemental, bounces off a wall, and attacks the nearest monster (can spawn multiple))
33 = NULL(?)
34 = Charged Bolt
35 = Holy Bolt
36 = Resurrect (no audio)
37 = Telekinesis
38 = Arrow with lightning damage
39 = Acid Spitter audio (kills monster in one hit; 1x1 tile distance; No graphics; Buggy (RAM/CPU spikes))
3A = audio only?
3B = audio only?
3C = Heal other (no audio)
3D = Elemental
3E = Resurrect (aimable; No target; No audio)
3F = Bone Spirit (causes no damage to target monster)
40 = "Self-electrocution" (Charged Bolt)
41 = [Red] Portal to Unholy Altar (spawns on-self)
42 = audio only?
43 = audio only?
44-70 = CRASH (assumed all the way to 0xFF is "CRASH" syndrome)
Reply
#8
Hello.

Is anyone aware of how Diablo deals with a player's health?

I've clearly found the 20 bytes (yes: 20!) that pertain directly the the character's health / effective health & maximum health / effective maximum health.

The last 4 bytes I am not sure how is used (that may be apart of what's causing my issues).

Here's what I've gathered (first: A good laugh): Math is killing me!!!
[Image: xFEW4fA.png]

I had Diablo in the background while I was trying to input what I believe will produce 110HP! Only to find my Sorcerer dead on the floor in town.

Desired HP: 100
100(HP) / 4 = 25
25 = 0x00 0x19

Desired HP: 108 (+4 HP more)
108(HP) / 4 = "27"
"27" = 0x00 0x1B

Desired HP: 112 (+4 HP more)
108(HP) / 4 = "27"
"27" = 0x00 0x1B

I can clearly predict how to apply new health using the 2nd byte (divisible by 4).

How would one apply the health of 111? Or 110 (even number)? I thought perhaps this would work:

Desired HP: 110
110(HP) / 2 = 55
55 = 0x37 0x00

Unfortunately, this produces some odd side effects.

The health orb displays: 1 / 0 HP (alive in town).
The lying character screen displays: 0 / 1 HP (alive in town).

The 20th byte went from a usual 0x50 (80); Which usually represents 100% health to: 0x5D (93).

Any ideas? Thanks! Confused

EDIT: I just thought of something real fast... If I can maintain "0x50" on the 20th byte: It should work!
I was close... I modified my health values (all 4) to this:

Code:
006865D8 - 37 0D 00 00 37 0D 00 00 37 0D 00 00 37 0D 00 00  7...7...7...7...

Unfortunately: It produced half of my desired target of 110HP (52HP generated). Angry

EDIT #2: Now I am REALLY confused! I doubled the first byte and it added +1HP (52 -> 53)????

(Me)
Quote:When the 1st byte is: 0
The 2nd byte is treated as divisible by 4.

When the first byte is NOT 0: It is divisible by 2
The second byte increments by +1

EDIT #3: OK! I think I've figured this out almost all the way (taking quite a bit)...

Code:
006865D8 - A1 1B 00 00 A1 1B 00 00 A1 1B 00 00 A1 1B 00 00  Â¡...¡...¡...¡...

0xA1 = 26
0x1B = 27

A1 / 2 = 80
A1 + 1B = 107 (rounded: 110?)

OR

0x1B 0xA1 = 7073
(7073 / 8) / 8 = 110

The latter seems to yield more predictable results.

0x1C 0xA1 = 7329
(7329 / 8) / 8 = 114

EDIT #4: Yes... It seems that is the formula. I am just doing something wrong. I almost have this down! Smile

I predicted that the target "113" HP would be:
0x37 0x1C

It yielded 112HP (off by 1).

The correct answer:
0x40 0x1C!

EDIT #5: Yup! Close, but just off by 1! Frustrating.
Target HP: 133 (odd number)
Idea: Multiply target HP by 4
Split value into two bytes represented in hex

EG: 0x214 = 0x04 0x21
Turns out (maybe I read it wrong?) the answer is: 0x40 0x21!

EDIT #6: Yes! I absolutely have the answer now. The only problem is: I do not know how to mathematically (programatically) explain what I am doing.

Code:
Is the desired HP divisible by 4 evenly?
Step 1: Divide target HP by 4 (100 -> 25)
Step 2: Write 00 to the first byte; Write quotient (25 or 0x19) to the second byte

Is the desired HP an odd number (not divisible ending in a whole number for a quotient)?
Step 1: Multiply target HP by 4 (123 (7B) -> 492 (1EC))
Step 2: Divide by 16 (quotient: 30 (0x1E))
Step 3: Write quotient to the 2nd byte (30 (0x1E))
Step 4: Write the ???? to the 1st byte (12 (0x1C))

The 20th byte remains at 0x50 (80); I am not sure how "80" represents 100% health, but I may be incorrect on what it's purpose is.

... In fact: You can use the latter formula to calculate evenly divisible numbers (it comes out all the same).
100 -> 400 -> 25 (0x19 to the 2nd byte)

... This is actually incorrect. Although it is the closest thing to an accurate solution I've come up with yet.
I feel stuck on figuring out what the formula is or how to extract it. Any pointers (no jokes please!) or help would be appreciated.

EDIT #???: Oh... You can just simply multiply the desired HP by 4 and use only base-16 (no division necessary). Angry

EDIT: I would love to know how to solve this mathematically and pragmatically. I am assuming in C: You would solve this with either bit shifting or the modulus operator.
However: Using only pencil and paper: I am failing to come up with a solution here.

123 * 4 = 492
492 / 16 = 30

This is the first value we need to extract (to write to the 2nd byte). 0x1E
How do you extract the other digit? 0x0C! This has me stumped. I can almost describe what it is I am trying to do, but I cannot quite put my tongue on it.

Another (with a calculator, of course) method that I've been successfully using is to do this:
Target HP = 983
983 * 4 = 3932 (0xF5C)
3932 * 16 = 0xF5 0xC0

0xC0 0xF5 0x00 0x00 yields our results in-game! 983.
Reply
#9
Hello!

This is what I have so far (I feel as if I'm missing a myriad of information (even on the player structure's header portion)):
Code:
struct sPlayer {
    DWORD base = 0x00686448;

struct sPlayer {
    DWORD base = 0x00686448;

    struct item {
        // 23 * 4 = 92 bytes (QWORD) per item
        DWORD base = 0x006867C4; // Beginning of first item

        // base += (16C * item.slot)

        // 40 empty slots in inventory
        // 7 items to wear
        // 8 belt slots
        // 55 slots total

        int* getBeltSlot(const int slot) { return *(base += (slot * 0x16C)); };
    };

    // When you right click to cast a spell: It is copied here
    int rightClickX = 0x00686448 + 0x28;
    int rightClickY = 0x00686448 + 0x2C;

    DWORD dungeonLevel = 0x00686448 = 0x34;

    char* name = 0x00686448 + 0x140; // Null-terminated

    struct cTile {
        DWORD currentX = 0x00686448 + 0x38;
        DWORD currentY = 0x00686448 + 0x3C;

        DWORD previousX = 0x00686448 + 0x40;
        DWORD previousY = 0x00686448 + 0x44;

        DWORD futureX = 0x00686448 + 0x48;
        DWORD futureY = 0x00686448 + 0x4C;

        DWORD departingX = 0x00686448 + 0x50;
        DWORD departingY = 0x00686448 + 0x54;

        DWORD endingX = 0x00686448 + 0x58;
        DWORD endingY = 0x00686448 + 0x5C;
    };

    DWORD facing = 0x00686448 + 0x70; // 0-8 facing (up, down, left, right + (two angles * 2))
    DWORD idleFlag = 0x00686448 + 0x80; // 03 = idle, 00 = active

    DWORD level = 0x00686448 + 0x1B8;
    DWORD experience = 0x00686448 + 0x1BC;
    DWORD reqExperience = 0x00686448 + 0x1C4;

    struct cStat {
        DWORD STR = 0x00686448 + 0x164;
        DWORD baseSTR = 0x00686448 + 0x168;

        DWORD MAG = 0x00686448 + 0x16C;
        DWORD baseMAG = 0x00686448 + 0x170;

        DWORD DEX = 0x00686448 + 0x174;
        DWORD baseDEX = 0x00686448 + 0x178;

        DWORD VIT = 0x00686448 + 0x17C;
        DWORD baseVIT = 0x00686448 + 0x180;

        DWORD points = 0x00686448 + 0x184;

        /////////////////////////////////////////////////////////////////////
        // NOTE
        //     How to calculate health in Diablo:
        //     Multiply your desired HP/MP by 4
        //     Multiply that sum by 16
        //     View the value in a base-16 format
        //     Copy the first two digits (MSB to LSB) to the 2nd byte address
        //
        //     EXAMPLE
        //         500 * 4
        //         2000 * 16
        //         7D00 (32,000) | 7D = MSB, 00 = LSB
        //         (0x00686448 + 0x19C) = 0x00 0x7D 0x00 0x00
        /////////////////////////////////////////////////////////////////////

        DWORD baseHP = 0x00686448 + 0x190; // Before items are applied
        DWORD baseMaxHP = 0x00686448 + 0x194; // Before items are applied

        DWORD HP = 0x00686448 + 0x198; // Effective HP (100 / XXX)
        DWORD maxHP = 0x00686448 + 0x19C; // Effective maximum HP (XXX / 500)

        DWORD orb_HP = 0x00686448 + 0x1A0; // Percentage of HP orb to display(?)

        DWORD baseMP = 0x00686448 + 0x1A4; // Before items are applied
        DWORD baseMaxMP = 0x00686448 + 0x1A8; // Before items are applied

        DWORD MP = 0x00686448 + 0x1AC; // Effective HP (100 / XXX)
        DWORD maxMP = 0x00686448 + 0x1B0; // Effective maximum HP (XXX / 500)

        DWORD orb_MP = 0x00686448 + 0x1B4; // Percentage of HP orb to display(?)
        
        DWORD gold = 0x00686448 + 0x1CC; // Player's total gold
    };

    int spell_rightClickX = 0x00686448 + 0x1D4;
    int spell_rightClickY = 0x00686448 + 0x1D8;

    int FLAG_isCastingSpell = 0x00686448 + 0x1E0; // 0 = Not casting, 0x0F = casting
    int spell_is_casting_UNKNOWN = 0x00686448 + 1F0; // No idea what this is for; Values change when casting spells

    DWORD FLAG_traveledToLevel = 0x00686448 + 1F4; // 01 01 01 01 = First four stairs used; 20 bytes used
};

If anyone has anything else to add: That'd be awesome! Smile

Reverse engineering Diablo is quite the thrill for me. I'm not even sure why. What a great game.

I am sure it's full of errors (especially since I'm using "DWORD" as a place-holder for now and am aiming towards using only C).
It's just a general concept to help find/locate addresses / offsets / what they're used for in the video game.
Reply
#10
Hello.

I suppose I'll start focusing a bit more on how item generation works in Diablo.

Namely: How items are assembled. The structure (object?) size, the modifiers, flags, etc.

Code:
ITEM TYPE EXAMINED: Helm, Magic
08 bytes: ITEM MARKER: Randomized ID
04 bytes: ITEM FLAG: Type // FF FF FF FF = No item
04 bytes: ITEM MARKER: Picked up at X
04 bytes: ITEM MARKER: Picked up at Y
04 bytes: ???? (00 00 00 00)
04 bytes: ???? (rewrites self) dungeon ID it spawned on?
04 bytes: ???? (rewrites self) 0D 00 00 00
04 bytes: ???? (rewrites self) 0D 00 00 00
04 bytes: ???? (rewrites self) 60 00 00 00
04 bytes: ???? (rewrites self) 10 00 00 00
04 bytes: ???? 00 00 00 00
04 bytes: ???? (rewrites self) 01 00 00 00
04 bytes: ???? 00 00 00 00
04 bytes: ITEM FLAG: Identification 01 00 00 00
64 bytes: ITEM STRING: Item name (unidentified)
64 bytes: ITEM STRING: Item name (identified)
04 bytes: ITEM FLAG / MODIFIER: Item slot / Modifier 00 04 02 00
04 bytes: ITEM FLAG: Graphics index (5F 00 00 00)
04 bytes: ???? (does not rewrite self) 19 00 00 00
04 bytes: ITEM MODIFIER: Selling price FF FF FF FF
04 bytes: ???? 00 00 00 00
04 bytes: ???? 00 00 00 00
04 bytes: ???? 00 00 00 00
04 bytes: ITEM ATTRIBUTE: Armor FF 00 00 00
20 bytes: ???? 00 00 00 00
04 bytes: ITEM ATTRIBUTE: Durability FF 00 00 00
04 bytes: ITEM ATTRIBUTE: Max durability FF 00 00 00 // NOTE: If both the durability and maximum durability of an item are listed as "0xFF": The item is "Indestructible"
12 bytes: ???? 00 00 00 00
04 bytes: ITEM ATTRIBUTE MODIFIER: +Strength FF 00 00 00
44 bytes: ???? 00 00 00 00
04 bytes: ???? (rewrites self) 00 01 00 00
24 bytes: ???? 00 00 00 00
04 bytes: ITEM ATTRIBUTE MODIFIER: FF 13 00 00
04 bytes: ???? (does not rewrite self) FF FF 00 00
04 bytes: ???? (does not rewrite self) 03 00 00 00
12 bytes: ???? 00 00 00 00
04 bytes: ITEM FLAG: Unusable item 01 00 00 00
04 bytes: ???? (does not rewrite self) FF 00 00 00
04 bytes: ???? 00 00 00 00

This is just the output of the first one I've looked at (minus earlier in this thread).
This is a much better layout, making it far easier to read and work with.

... Although I feel as if I missed some tid-bits regarding the "isMagic" or "isWhite" or "isUnique" flag.

TODO: Rewrite with localized offset per byte segment (easier modifying / comparing with future items).
Reply
#11
Hello.

I've been looking further into how items are generated, saved and loaded from player files.
I will post more of my findings, but before I do that (hopefully later today)! There is this (which I am about to start working on again):

Code:
ITEM STRING    - ASCII string
     ITEM STRING
         - UNIDENTIFIED (String shown when the item is unidentified)
         - IDENTIFIED (String shown when the item is identified)

ITEM DATA      - Misc. data pertaining to the item-only (saving/loading for example)
     ITEM DATUM
         - ITEM HEADER (4 bytes stored in save files)
         - ITEM MODIFIER (4 byte prefix/suffix string index look-up?)

ITEM FLAG      - Optional routes for item data operations ("identified" for example)
     ITEM FLAGS
         - ITEM TYPE (FF FF FF FF = No item, 02 = Weapon, 04 = Helm)
         - ITEM SPECIAL TYPE (00 = Normal, 01 = Magic, 02 = Unique)
         - IDENTIFIED (00 = Unidentified, 01 = Identified)
         - USABLE ITEM (00 = Unusable, 01 = Usable)

ITEM ATTRIBUTE - Modifies the item
     ITEM ATTRIBUTES
         - ARMOR
         - DURABILITY
         - MAXIMUM DURABILITY
         - STR
         - MAG
         - DEX
         - VIT
         - LIGHT RADIUS (PERCENTAGE)
         - CHANCE TO HIT (PERCENTAGE)
         - DAMAGE (INTEGER)
         - DAMAGE (ELEMENTAL?)
         - DAMAGE (PERCENTAGE)
         - SPELL LEVELS
         - SPELL CHARGES
         - HIT POINTS
         - MANA POINTS
         - RESISTANCES
         - ATTACKER TAKES DAMAGE
         - DAMAGE'S TARGETS ARMOR
         - LOSE HIT POINTS OVER TIME

Code:
ITEM TYPE EXAMINED: Helm, Magic
04 bytes: ITEM DATA: Randomized ID
04 bytes: ITEM DATA: Magic prefix/postfix
04 bytes: ITEM FLAG: Type // FF FF FF FF = No item
04 bytes: ITEM DATA: Picked up at X
04 bytes: ITEM DATA: Picked up at Y
04 bytes: ???? (00 00 00 00)
04 bytes: ???? (rewrites self) dungeon ID it spawned on?
04 bytes: ???? (rewrites self) 0D 00 00 00
04 bytes: ???? (rewrites self) 0D 00 00 00
04 bytes: ???? (rewrites self) 60 00 00 00
04 bytes: ???? (rewrites self) 10 00 00 00
04 bytes: ???? 00 00 00 00
04 bytes: ???? (rewrites self) 01 00 00 00
04 bytes: ???? 00 00 00 00
04 bytes: ITEM FLAG: isIdentified 01 00 00 00
01 bytes: ITEM FLAG: Unique/Magic/Normal 01 = Magic, 02 = Unique, 03 = Normal
63 bytes: ITEM STRING: Item name (unidentified)
64 bytes: ITEM STRING: Item name (identified)
02 bytes: ITEM DATA: Item slot (Weapon, Shield, Jewelery, Helm) 00 04
02 bytes: ITEM ATTRIBUTE: 02 00 (damage/damage %/Chance-To-Hit %/Light %/etc)
04 bytes: ITEM DATA: Graphics index (5F 00 00 00)
04 bytes: ???? (does not rewrite self) 19 00 00 00
04 bytes: ITEM DATA: Selling price FF FF FF FF
04 bytes: ???? 00 00 00 00
04 bytes: ???? 00 00 00 00
04 bytes: ???? 00 00 00 00
04 bytes: ITEM ATTRIBUTE (Armor): FF 00 00 00
20 bytes: ???? 00 00 00 00
04 bytes: ITEM ATTRIBUTE (Durability): FF 00 00 00
04 bytes: ITEM ATTRIBUTE (Max durability): FF 00 00 00 // NOTE: If both the durability and maximum durability of an item are listed as "0xFF": The item is "Indestructible"
12 bytes: ???? 00 00 00 00
04 bytes: ITEM ATTRIBUTE: FF 00 00 00 (STR)
44 bytes: ???? 00 00 00 00
04 bytes: ???? (rewrites self) 00 01 00 00
24 bytes: ???? 00 00 00 00
04 bytes: ITEM ATTRIBUTE: FF 13 00 00
04 bytes: ???? (does not rewrite self) FF FF 00 00
04 bytes: ???? (does not rewrite self) 03 00 00 00
12 bytes: ???? 00 00 00 00
04 bytes: ITEM FLAG: Unusable item 01 00 00 00
04 bytes: ???? (does not rewrite self) FF 00 00 00
04 bytes: ???? 00 00 00 00

Slightly more organized. Hopefully it will help in explaining how items are generated and loaded in Diablo (patch v1.09).
I am not 100% clear on what I initially thought to be a 'randomized ID' tag; As it is clearly saved in the *.sv (SaVe file?).

It also seems that you are able to copy only the first 4 bytes of a Magic Helm and over-write another Helm successfully.
The "duplication" will not take place until the player has reloaded their file (the game saves it and writes to the hdd upon exiting a session).
Reply
#12
Hello.

I noticed that the 'site' as a whole is no longer functioning, but the sub-addresses seem to load:
-> https://a3n.home.xs4all.nl/Item.htm

This is from patch v1.07 or v1.08 (I cannot remember which). It seems to hint that the first 3 bytes of an item (what I've been calling a "randomized ID" or "item header") is actually some sort of stored pointer address referencing the magic prefix/suffix and the 4th byte is the item's base type (I think)?

In v1.09: It would appear that the item's graphics (in every respect) is at a totally different offset.

Based on random trial-and-error (with no forethought, planning or prediction): The first 3 bytes refer to a magic item's prefix and the latter 3 bytes refer to a magic item's suffix.

Code:
Magic Club = Club of the Mind
87 E4 3C 78 C1 00

Editing this to a new value:
Code:
Magic Club = Club of the Sky
00 00 00 78 C1 00

I do not know if it's still some sort of function pointer or variable pointer or what.

What little I do know is this:
The item (upon loading a game) is generated at a different offset (initially) before being copied to the player inventory (seems logical).

0x18F948 is the temporary buffer.
EAX appears to have been loaded (by the time I saw the value) with the item's header.

Any ideas would be greatly appreciated!

EDIT: I am still trying to figure this one out, but I think I have an idea (or partial explanation as to what's really going on)...

This is the function called when items are spawned (Barrels, monsters, new player file, etc):
Code:
0042084A | 55                       | push    ebp                                       |

There seems to be some magic number to multiply by (EDI is multiplied by a full item size of 76 bytes; ESI I do not know what is used for 100%; It's multiplied by 368).
Anyway!

There seems to be a collection (v-table of pointers?) of data resting at the static address of:
Code:
0x00491F10

Specifically, at this address there are two 4-byte pointers side-by-side with a possible flag value of 01 (Normal / White item).
Code:
00491F10  08 2C 49 00 70 25 49 00 01 00 00 00 1E 00 00 00  .,I.p%I.........

The following "1E" I do not understand what it represents (possible item data for the graphics of the bow?). Shortly after, there is a "4" (declaring it's of a weapon slot type) and some more misc. data.
Reply
#13
O.K! SO! This is now where I'm at in my journey towards trying to understand how Diablo loads items and deals with that 'magic' item header (definitely 4-bytes in length) + 4-bytes for an alternate item header (appears to affect both magic items and unique items).

Upon loading a game in Diablo (minus normal routines; which I need to do more research on), the jist of program flow goes like this:
  • Recursively call 'itemGeneration()' function 7-times (copying to a separate character/item buffer); "ActiveItems" (worn items)
  • Recursively call 'itemGeneration()' function 40-times (copying to a separate character/item buffer) "InactiveItems" (inventory items)
  • Recursively call 'itemGeneration()' function 8-times (copying to character/item buffer); "ActiveItems" (worn items) + belt(?)
  • Recursively call 'itemGeneration()' function 40-times (copying to character/item buffer) "InactiveItems" (inventory items)
END LIST

At this point: I am still completely lost as to how it's generating the affixes (prefixes/suffixes) for magical items.
I can definitively say that it is using some sort of linked-list structure of pointers to items (which are structures of pointers? with the item name (string) and respective static data for that type of item).

Hopefully I'll have an update later today.

The counter for these iterations is stored in the .data:
Code:
0018F7C4 = counter for looping

Beginning of epoch:
Code:
00448C44 | 8B 55 FC                 | mov     edx,dword ptr ss:[ebp-0x4]                | Pointer to empty data segment
00448C47 | 8B 4D 08                 | mov     ecx,dword ptr ss:[ebp+0x8]                | Pointer to empty data segment (item???)
00448C4A | E8 F9 00 00 00           | call    <diablo_copy.itemGenerationFromFile()>    |
00448C4F | 83 45 08 13              | add     dword ptr ss:[ebp+0x8],0x13               | Address of pointer to: ????
00448C53 | 01 5D FC                 | add     dword ptr ss:[ebp-0x4],ebx                | Address of pointer to: Item header (4 bytes)
00448C56 | FF 4D F8                 | dec     dword ptr ss:[ebp-0x8]                    |
00448C59 | 75 E9                    | jne     diablo_copy.448C44                        |

...

00448D48 | 56                       | push    esi                                       | itemGenerationFromFile()

As far as I understand: An individual item is 76 bytes in size; This appears to be circumstantial.
Worn items seem to be 0x170 (368 bytes) apart from one another. I am not sure why.

Code:
00448D48 | 56                       | push    esi                                       | -- EBX = 0x170 (368 bytes)
This coincides between the "no item" value of 0xFF 0xFF 0xFF 0xFF (which comes after the ~8 byte item header).

Code:
0x00686934: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF ; No item in the character's helm(?) slot

EDIT: WOW! Is all I can say.

Diablo is INCREDIBLY[I] horrible with dealing with writing data. Horrendously inefficient in terms of senselessly, constantly rechecking the [I]same value, then writing over it, then changing it right back again without reason, only to move forward...

Also: It's been about ~11hrs consecutive hours since I made this post.
THAT is how long it took me to step through the generation of only one item from a saved character file.

What did I learn regarding the item header? It'd appear (from everything I saw) to be 100% random.
Which flings back to the idea of 'unique item ID' and not prefix/suffix setter.

I am confused, yet again! Sad

Perhaps there were optional flag jumps that were skipped due to the item header being 0x00 0x00 0x00 0x00 and the 5th byte 0xCF ("of Crimson" suffix).

I did have some fun adding comments and documenting what these (inline, as far as I can tell) functions are though.

Code:
00469B10 | 83 EC 20                 | sub     esp,0x20                                  | ConvertTokensToStrings()
0046CF90 | 53                       | push    ebx                                       | CopyPointerStringToBuffer()
0042084A | 55                       | push    ebp                                       | SpawnItem()
0041FD98 | 53                       | push    ebx                                       | CreateItem()
0042254A | 55                       | push    ebp                                       | TempItemGeneration() (from player file)
0046B970 | 51                       | push    ecx                                       | ConvertToAllLowercase()
00448D48 | 56                       | push    esi                                       | ItemGenerationFromFile()
00405295 | 33 D2                    | xor     edx,edx                                   | WriteStringToBuffer()
00469D80 | 57                       | push    edi                                       | WriteString()
Reply
#14
Hello, I would like to say thanks for your work on this. I am a fairly experienced modder for Diablo II and have been getting back into the first game recently.

I noticed there were a couple remakes floating around (like freeablo). There is also an HD remake titled "Belzebub". Unfortunately the first one is far from usable and the latter is closed source.

I figured we could respark the D1 modding community by reversing the entire game back into the original source code. Once that is done, we can fix bugs and update the UI/resolution. Would you be interested in this?

For starts, IDA pro would be used to create the struct/enums, and then decompile into Pseudo C++ code. After which, the code will be cleaned up and branched until it assembles a working game. Then, bugs will be squashed.
Reply
#15
(02-02-2018, 09:31 PM)GalaXyHaXz Wrote: Hello, I would like to say thanks for your work on this. I am a fairly experienced modder for Diablo II and have been getting back into the first game recently.

Hi there, GalaXyHaXz! Welcome to the forums.

You're experienced with creating mods for Diablo II? That sounds exciting! I used to reverse engineer Diablo II way back in 2005. I customized some of the leaked TPPK hacks for myself. The source code was terrible to look at, but it worked.

I also updated (in the past few years, for that matter) some of the more popular bots' scripts (back when mmBot was still online: I also wrote customized scripts for my characters and updated the more generic scripts that were provided).

RedVex was O.K. to use (the content was... Not really heavy, we'll say).

As far as "mods" on Diablo II go: I do not believe I ever actually used a mod (other than TMCBPK's IAS/FHR/FCR editor). That was more or less strictly for PvM though. I never felt a reason to use it in PvP.


(02-02-2018, 09:31 PM)GalaXyHaXz Wrote: I noticed there were a couple remakes floating around (like freeablo). There is also an HD remake titled "Belzebub". Unfortunately the first one is far from usable and the latter is closed source.

Yes! I noticed there were some very old mods floating around (including some of the older more popular ones). Apparently a lot of people play, but nobody's on the public servers anymore (Battle.net; which is where I still roam). I see the occasional post here and there from stragglers inquiring for assistance.


(02-02-2018, 09:31 PM)GalaXyHaXz Wrote: I figured we could respark the D1 modding community by reversing the entire game back into the original source code. Once that is done, we can fix bugs and update the UI/resolution. Would you be interested in this?

'Respark?' With all due respect: Diablo has dwindled down into the hundredths of persons percentile. There's literally less than 5,000 players world-wide (globally).

Reverse engineering the game is a behemoth of an undertaking (that's in reference to a professional programmer / reverse engineering specialist). You would be better off creating your own Diablo clone with a popularly supported API! Such as OpenGL, SDL, SMFL, DirectX, Allegro or another.

Fixing the bugs in Diablo? In v1.09: There aren't honestly that many bugs (especially note worthy ones, to me). If you'd like to address bugs and patch them: That might take a month or two. Depending on how busy your day-to-day life is.


(02-02-2018, 09:31 PM)GalaXyHaXz Wrote: For starts, IDA pro would be used to create the struct/enums, and then decompile into Pseudo C++ code. After which, the code will be cleaned up and branched until it assembles a working game. Then, bugs will be squashed.

IDA Pro? Isn't that as old as W32Dasm? Wow! SoftIce. Memories. I used to use OllyDbg (32-bit version) for the longest of times. After it recently became obvious that it was not going to be supported (64-bit version is buggy): I decided to find something new. x32dbg is more or less a clone of OllyDbg, but with a vast more set of tools and an open plugin (which is AWESOME). x32dbg and x64dbg, I should say.

The decompiler ("Snowman") that comes with them is fairly decent at identifying structures (accurately, from what I've witnessed). It sometimes defers to inline assembly with the C code though. Diablo is quite old though. I know next to nothing about compiler theory.

As far as: "Am I interested?" No. Unfortunately. Thank you, though.

You're more than welcome to post any fun information in this thread here though. Smile
Reply
#16
I have a bunch of lists from my days playing with the code, a little remnant knowledge, idk how much help it would be.
Reply
#17
(02-05-2018, 03:31 AM)Mike55520 Wrote: I have a bunch of lists from my days playing with the code, a little remnant knowledge, idk how much help it would be.

Anything would help, IMO! It should be a group (community) effort.

There's all these mods/hacks floating around with no explanations or data to share!
Reply
#18
slvl offsets
68650A = FireBolt
68650B = Healing
68650C = Lightning
68650D = Flash
68650E = Identify
68650F = Fire Wall
686510 = Town Portal
686511 = Stone Curse
686512 = Infravision
686513 = Phasing
686514 = Mana Shield
686515 = Fire Ball
686516 = Guardian
686517 = Chain Lightning
686518 = Flame Wave
686519 = Doom Serpents
68651A = Blood Ritual
68651B = Nova
68651C = Invisibility
68651D = Inferno
68651E = Golem
68651F = Blood Boil
686520 = Teleport
686521 = Apocalypse
686522 = Etherealize
686523 = Item Repair
686524 = Staff Recharge
686525 = Trap Disarm
686526 = Elemental
686527 = Charged Bolt
686528 = Holy Bolt
686529 = Resurrect
68652A = Telekenesis
68652B = Heal Other
68652C = Blood Star
68652D = Bone Spirit

Spell IDs for use with the spells packet
Elemental 3d

Golem 21

Chargebolt 34

holybolt 35

Resurect 36

heal 25

Lightning 07

Telekiness 37

Teleport 1a

Nova 2a

Heal Other 3c

Firebolt 01

Fireball 06

Gardian 02

Blood Star 18

Firewall 26

Flamewave 29

Small Flamewave 0e

Inferno 31

Apocalypes 2c

Bone Spirit 3f

Phaseing 03

Stone Curse 1e

Town Portal 0a

mana shield 0d

Chain Lightning 0f

------------------------------
Unused data with effects when added to packet 0x17 data
07AF04 Firewall

07ED04 Key story (woman) (sound)

07D004 Firebolt

07B004 Flamewave (shity)

07EF04 Mushroom WOO (Sound)

07EC04 Portal (sound)

07AC04 Blood Explosion

07CA04 Golem (Sound)

07CF04 Firebolt

07CC04 Griswold pissed off (sound)

07F004 Some funky noise (Sound)


07B304 Spooky Voice (Sound)


07c804 Woman Important (Sound)

07E804 Future (Sound)

07E904 Future2 (Sound)

07F904 Wood (sound)

07D104 Firebolt v3

07C804 Lightning + annoying chic

07C904 Somthing Breaking

07B904 Button Sounds

07DD87 Blood star\recharge

07D187 BLood star

07CC87 Charge bolt
07ec87

07D487 Good lightning

07D587 Armor sound

07E487 Bow sound

07E587 Griswold BUtcher

07E787 Red Portal

07EC87 Magic Rock\Firewall

07ED87 Ear\firewall

07EF87 Blood star

07F687 Hit warrior

07FD87 Equalizer

07FE87 Cool Noise

07D095 red portal

072D38 Elemntal

07DC2E blood star\griswold
Reply
#19
Packet 01 - Walk*
Packet 02 - Data of players in game
Packet 08 - Pick up item (Inventory open)
Packet 09 - Pick up item*
Packet 0a - Drop item
Packet 0c - Drop ear*
Packet 0e - Cast spell
Packet 10 - Select enemy target*
Packet 12 - Attack
Packet 14 - Attack with bow
Packet 16 - Attack with spell
Packet 1a - Resurrect
Packet 1c - InvalidMonster (not sure of it's use)
Packet 1d - Talking to townsfolk*
Packet 1e - Update to everyone that a level was loaded
Packet 1f - Enter Town Portal 1f xx xx 1f0100 1f player slot
Packet 23 - Cast spell /w damage*
Packet 24 - Monster death*
Packet 25 - Hit target and cause damage
Packet 27 - Move to pick up item (Inventory open)*
packet 28 - Move to pick up item*
Packet 29 - Select item on ground (Inventory open)*
Packet 2a - Select item on ground*
Packet 2b - Open door
Packet 2c - Close door
PACKET 2D - open sarcophogus/trapp
Packet 2e - Open chest
Packet 2f - Break barrel
Packet 30(graphic) - Equipping an item* i30 xx xx xx xx itemslot, itemtype? xx xx itemslot item graphic?
300646003F08A2B1250001
Sending that packet in game will make it appear that you're wearing armor.

Packet 31 - Unequipping an item*
Packet 33 - Level update*
Packet 34 - Monster death w/ Item drop*
Packet 35 - Join game notification/information
Packet 36 - Data of players currently joining game
Packet 38 - Portal Exit Point, Dungeon side 38 xxxx xxxx xxxx xxxx
Packet 4c - Used in game joins
packet 4d - Heal other
Packet 4e - Talking*
Packet 53 - Restart in town
Packet 58 - Quest updated*
Packet 59 - Mana shield*
Packet 5a - Golem*
Packet 5c - Mana shield*
Packet 5d - Mana shield*
Packet 38 - TP destination

this is just the packet IDs, i never got around to figuring out the data and syntax for most, i can tell you about 38, 17, and probably a few others, but the truth is i havent touched this game in years, so ive forgotten most of it. these are notes i have still.
Reply
#20
Packet 38 controls the portal exit point, 0x38 xxxx xxxx xxxx xxxx, the data is in this order: 0x38 xycoordinates level(hex) graphic special.
(gfx -0000 is town, 0100 is church, 0200 is cata, 0300 is cave, 0400 is hell)

Packet 17 is really small, it is simply 17 spID Slot Spcl
Reply


Forum Jump:


Users browsing this thread: 5 Guest(s)