1.09x Tutorial How to Make Charm Zone in Inventory

This forum is for discussions on how to edit what can not be edited through the txt files, needless to say this isn't about battle net hacking.

Moderators: Nefarius, Havvoric

Post Reply
Nasty Nate
Posts: 11
Joined: Sun May 03, 2020 4:05 am
United States of America

1.09x Tutorial How to Make Charm Zone in Inventory

Post by Nasty Nate » Mon May 18, 2020 6:07 pm

Hello All,

Here is a step-by-step guide on how to implement a charm zone in 1.09x. I'm writing this guide since a lot of mods and other modders like to increase inventory size but you may not want to allow charms to work in the new larger inventory. There are some guides out there on how to do this in 1.10+ but none for 1.09x and the code is different enough that the 1.10+ guides won't work for 1.09x.

GOAL:
Implement two areas in your inventory space, one that allows charm bonuses to be applied, and one that does not. This allows the character to carry a full compliment of charms without removing the ability to pick up drops. I will lay this out in a step-by-step format that anyone should be able to do, regardless of prior knowledge.

Assumptions:
This guide assumes that you have already successfully increased your inventory size. There are other guides for that as well as individual mods that specifically do that for you. In order to not make this guide a novel, I won't be covering the process of changing inventory size. However if you want a stand alone adjustment mod to increase your inventory size, you can use this mod https://www.moddb.com/games/diablo-2/do ... -inventory This mod includes cube size increase and merc equippables, but you can exclude that stuff if you just want to increase inventory size. Note: this mod is for 1.10+ txt files but can easily be adjusted for 1.09. If you can't do those last steps, then I'm assuming you won't be looking to do code edits.

Files you will need:
D2Common.dll

Tools You Will Need:
OllyDBG or some other dll editor. The images in this guide will be for Olly as that is what I used.

Without further ado, let's get started.

To give you an idea of where I'm starting from, below is my inventory space. I've included column and row numbers, this will make sense later.

Image

Here you can see that I doubled the height of original inventory. I want my charms to work in the top half, the highlighted zone. And I don't want them to work in the bottom half. I want the bottom half to be my drops pick up zone.

So let's fire up OllyDBG and get this working. This will literally be step-by-step on how I know it will work. You may have more experience with OllyDBG and have a more efficient way to go about it. If so, hats off to you and I hope to be at that level some day.

1. Open OllyDBG as administrator. If you don't know how to do that, just right click the executable and click run as administrator.
2. Open D2Common.dll by clicking File->Open. Then navigate to your Diablo 2 root folder (where diablo 2 is installed). You will then need to change the Files Type to Dynamic Link Library in order to see the dll values.
Image

3. This will bring up the CPU main thread window and an executable modules window. However, we want to open up the D2Common executable. To do that click the execute till return button (Ctrl+F9 hotkey) This will refresh the executables module showing D2Common
Image

4. Double click the D2Commmon.dll line in the executables module. This line should show in red.
5. This shows us the D2Common code in a format that looks familiar. However it is massive and doesn't tell us much. Maybe you want to sift through tens of thousands of lines of assembly code, but I don't. Fortunately for us, I know where we need to go. What we want to do is navigate to the #10840 function. This is the CharmRequirements function. What it does is check whether a charm is usable or not. To find this function do the following Right Click -> Search For -> Name (label) in Current Module
Image

6. Scroll down in the new window until you find #10840 and double click on it. This takes you to the function and if you widen the address bar on the right side of the main thread you will see #10840 there
Image

Alternatively you can just search for this address but that will only work for 1.09b dll file. The method I laid out above will work for any 1.09 dll file and will also work for finding any desired function within a dll file from any version. e.g. #10840 is also the CharmRequirements function in 1.10 but obviously sits at a different address.

Code: Select all

6FD92820 #10840             /$ 56             PUSH ESI
6FD92821                    |. 8B7424 08      MOV ESI,DWORD PTR SS:[ESP+8]
6FD92825                    |. 85F6           TEST ESI,ESI
6FD92827                    |. 0F84 85000000  JE D2Common.6FD928B2
6FD9282D                    |. 833E 04        CMP DWORD PTR DS:[ESI],4
6FD92830                    |. 75 5B          JNZ SHORT D2Common.6FD9288D
6FD92832                    |. 8B46 70        MOV EAX,DWORD PTR DS:[ESI+70]
6FD92835                    |. 85C0           TEST EAX,EAX
6FD92837                    |. 75 1E          JNZ SHORT D2Common.6FD92857
6FD92839                    |. 68 E3030000    PUSH 3E3
6FD9283E                    |. 68 AC72DD6F    PUSH D2Common.6FDD72AC                   ;  ASCII "C:\Projects\Diablo2\Source\D2Common\ITEMS\Items.cpp"
6FD92843                    |. 68 4472DD6F    PUSH D2Common.6FDD7244                   ;  ASCII "ptItemData"
6FD92848                    |. E8 25110200    CALL <JMP.&Fog.#10023>
6FD9284D                    |. 83C4 0C        ADD ESP,0C
6FD92850                    |. 6A FF          PUSH -1
6FD92852                    |. E8 44120200    CALL D2Common.6FDB3A9B
6FD92857                    |> 8B40 18        MOV EAX,DWORD PTR DS:[EAX+18]
6FD9285A                    |. 25 00010000    AND EAX,100
6FD9285F                    |. 85C0           TEST EAX,EAX
6FD92861                    |. 75 4F          JNZ SHORT D2Common.6FD928B2
6FD92863                    |> 833E 04        CMP DWORD PTR DS:[ESI],4
6FD92866                    |. 75 50          JNZ SHORT D2Common.6FD928B8
6FD92868                    |. 8B46 70        MOV EAX,DWORD PTR DS:[ESI+70]
6FD9286B                    |. 85C0           TEST EAX,EAX
6FD9286D                    |. 75 37          JNZ SHORT D2Common.6FD928A6
6FD9286F                    |. 68 E3030000    PUSH 3E3
6FD92874                    |. 68 AC72DD6F    PUSH D2Common.6FDD72AC                   ;  ASCII "C:\Projects\Diablo2\Source\D2Common\ITEMS\Items.cpp"
6FD92879                    |. 68 4472DD6F    PUSH D2Common.6FDD7244                   ;  ASCII "ptItemData"
6FD9287E                    |. E8 EF100200    CALL <JMP.&Fog.#10023>
6FD92883                    |. 83C4 0C        ADD ESP,0C
6FD92886                    |. 6A FF          PUSH -1
6FD92888                    |. E8 0E120200    CALL D2Common.6FDB3A9B
6FD9288D                    |> 68 9C140000    PUSH 149C
6FD92892                    |. 68 AC72DD6F    PUSH D2Common.6FDD72AC                   ;  ASCII "C:\Projects\Diablo2\Source\D2Common\ITEMS\Items.cpp"
6FD92897                    |. 68 0074DD6F    PUSH D2Common.6FDD7400                   ;  ASCII " ----- JONM NOTE: Would have crashed, see code at ITEMSDataTestFlags. From FILE: %s  LINE: %d"
6FD9289C                    |. E8 25110200    CALL <JMP.&Fog.#10029>
6FD928A1                    |. 83C4 0C        ADD ESP,0C
6FD928A4                    |.^EB BD          JMP SHORT D2Common.6FD92863
6FD928A6                    |> 8B40 18        MOV EAX,DWORD PTR DS:[EAX+18]
6FD928A9                    |. 25 00400000    AND EAX,4000
6FD928AE                    |. 85C0           TEST EAX,EAX
6FD928B0                    |. 74 1D          JE SHORT D2Common.6FD928CF
6FD928B2                    |> 33C0           XOR EAX,EAX
6FD928B4                    |. 5E             POP ESI
6FD928B5                    |. C2 0800        RETN 8
6FD928B8                    |> 68 9D140000    PUSH 149D
6FD928BD                    |. 68 AC72DD6F    PUSH D2Common.6FDD72AC                   ;  ASCII "C:\Projects\Diablo2\Source\D2Common\ITEMS\Items.cpp"
6FD928C2                    |. 68 0074DD6F    PUSH D2Common.6FDD7400                   ;  ASCII " ----- JONM NOTE: Would have crashed, see code at ITEMSDataTestFlags. From FILE: %s  LINE: %d"
6FD928C7                    |. E8 FA100200    CALL <JMP.&Fog.#10029>
6FD928CC                    |. 83C4 0C        ADD ESP,0C
6FD928CF                    |> 6A 0D          PUSH 0D
6FD928D1                    |. 56             PUSH ESI
6FD928D2                    |. E8 E9D7FFFF    CALL D2Common.#10731
6FD928D7                    |. 85C0           TEST EAX,EAX
6FD928D9                    |. 75 04          JNZ SHORT D2Common.6FD928DF
6FD928DB                    |. 5E             POP ESI
6FD928DC                    |. C2 0800        RETN 8
6FD928DF                    |> 833E 04        CMP DWORD PTR DS:[ESI],4
6FD928E2                    |. 75 32          JNZ SHORT D2Common.6FD92916
6FD928E4                    |. 8B46 70        MOV EAX,DWORD PTR DS:[ESI+70]
6FD928E7                    |. 85C0           TEST EAX,EAX
6FD928E9                    |. 75 1E          JNZ SHORT D2Common.6FD92909
6FD928EB                    |. 68 90040000    PUSH 490
6FD928F0                    |. 68 AC72DD6F    PUSH D2Common.6FDD72AC                   ;  ASCII "C:\Projects\Diablo2\Source\D2Common\ITEMS\Items.cpp"
6FD928F5                    |. 68 4472DD6F    PUSH D2Common.6FDD7244                   ;  ASCII "ptItemData"
6FD928FA                    |. E8 73100200    CALL <JMP.&Fog.#10023>
6FD928FF                    |. 83C4 0C        ADD ESP,0C
6FD92902                    |. 6A FF          PUSH -1
6FD92904                    |. E8 92110200    CALL D2Common.6FDB3A9B
6FD92909                    |> 8A40 41        MOV AL,BYTE PTR DS:[EAX+41]
6FD9290C                    |. 84C0           TEST AL,AL
6FD9290E                    |. 74 23          JE SHORT D2Common.6FD92933
6FD92910                    |. 33C0           XOR EAX,EAX
6FD92912                    |. 5E             POP ESI
6FD92913                    |. C2 0800        RETN 8
6FD92916                    |> 68 8B040000    PUSH 48B
6FD9291B                    |. 68 AC72DD6F    PUSH D2Common.6FDD72AC                   ;  ASCII "C:\Projects\Diablo2\Source\D2Common\ITEMS\Items.cpp"
6FD92920                    |. 68 C474DD6F    PUSH D2Common.6FDD74C4                   ;  ASCII " ----- JONM NOTE: Would have crashed, see code at ITEMSDataGetPage. From FILE: %s  LINE: %d"
6FD92925                    |. E8 9C100200    CALL <JMP.&Fog.#10029>
6FD9292A                    |. 83C4 0C        ADD ESP,0C
6FD9292D                    |. 33C0           XOR EAX,EAX
6FD9292F                    |. 5E             POP ESI
6FD92930                    |. C2 0800        RETN 8
6FD92933                    |> 8B4424 0C      MOV EAX,DWORD PTR SS:[ESP+C]
6FD92937                    |. 6A 00          PUSH 0                                   ; /Arg6 = 00000000
6FD92939                    |. 6A 00          PUSH 0                                   ; |Arg5 = 00000000
6FD9293B                    |. 6A 00          PUSH 0                                   ; |Arg4 = 00000000
6FD9293D                    |. 6A 00          PUSH 0                                   ; |Arg3 = 00000000
6FD9293F                    |. 50             PUSH EAX                                 ; |Arg2
6FD92940                    |. 56             PUSH ESI                                 ; |Arg1
6FD92941                    |. E8 6A88FFFF    CALL D2Common.#10756                     ; \#10756
6FD92946                    |. F7D8           NEG EAX
6FD92948                    |. 1BC0           SBB EAX,EAX
6FD9294A                    |. 5E             POP ESI
6FD9294B                    |. F7D8           NEG EAX
6FD9294D                    \. C2 0800        RETN 8
7. What we see is a function that can look a bit intimidating but is going to allow us to make a charm zone. In order to do that we need to add some additional checks within this function. However, if you look around, there are no empty lines to do that. So what we have to do is add a jump down to slack space that has room to add code. In order to do that here we actually have to replace the last 2 lines of code. The NEG EAX and the RETN 8. The reason for this is that a JMP command takes up more lines in memory than a Return command. So if we just replace the RETN 8 command, we will interfere with the function right below, the #10776 function, which we don't want to do because then your game will crash when it tries to call that function. So, replace the RETN 8 and NEG EAX with NOPs so that it looks like below. To do this, just double click on the line, type NOP, and click assemble.

Image

8. This gives us the space to add our Jump. So let's do that. For my addition, I jump down to slack space all the way at the bottom. If you wanted to, you could actually fill slack spack all along the way and have multiple jumps, but this gets messy and hard to follow. If you are using 1.09d, you may need to use a different address but if you go all the way to the bottom, just find some slack space that you are happy with and jump there. So change the first NOP here to JMP 6FDBC9CE

Code: Select all

6FD9294B                       E9 7EA00200    JMP D2Common.6FDBC9CE
9. Now in our new space, first thing we have to do is add back our NEG EAX that we removed to make room for our jump, and then start adding in our new code. I'm going to walk through what each line is doing but if you want the TL;DR version, this allows charms to work in the top half and to be stored in the bottom half but not give bonues.

Code: Select all

6FDBC9CE                       F7D8           NEG EAX
6FDBC9D0                       85C0           TEST EAX,EAX
6FDBC9D2                       75 03          JNZ SHORT D2Common.6FDBC9D7
6FDBC9D4                       C2 0800        RETN 8
6FDBC9D7                       8B4424 04      MOV EAX,DWORD PTR SS:[ESP+4]             ;  ptUnit
6FDBC9DB                       8B40 38        MOV EAX,DWORD PTR DS:[EAX+38]            ;  ptUnit -> hPath
6FDBC9DE                       8378 08 00     CMP DWORD PTR DS:[EAX+8],0               ;  left side x border
6FDBC9E2                       72 1A          JB SHORT D2Common.6FDBC9FE
6FDBC9E4                       8378 08 09     CMP DWORD PTR DS:[EAX+8],9               ;  right side x border
6FDBC9E8                       77 14          JA SHORT D2Common.6FDBC9FE
6FDBC9EA                       8378 0C 00     CMP DWORD PTR DS:[EAX+C],0               ;  top y border
6FDBC9EE                       72 0E          JB SHORT D2Common.6FDBC9FE
6FDBC9F0                       8378 0C 03     CMP DWORD PTR DS:[EAX+C],3               ;  bottom y border
6FDBC9F4                       77 08          JA SHORT D2Common.6FDBC9FE
6FDBC9F6                       B8 01000000    MOV EAX,1
6FDBC9FB                       C2 0800        RETN 8
6FDBC9FE                       33C0           XOR EAX,EAX
6FDBCA00                       C2 0800        RETN 8
So what this does is once a charm comes back as the character meets the requirements to use (that is the first TEST EAX,EAX) it runs it through our inventory section requirement. To do that it first grabs ptUnit (pItem in 1.10+) out of memory and loads it into EAX. ptUnit is stored at ESP+4. It then moves that to hPath which is at EAX+38. It then begins to check if it is within our zone. This is where the column and row numbers in the inventory image start to make sense. Since I want them usable in the entire top half, I want the charm to be usable in all of the x columns, and the top 4 y rows. So it goes through checking if it is in the border. First, is it in an x column greater than or equal to 0? Yes, keep moving, No, return as unusable and go about your business. This goes for each line. If my inventory was wider than 10 columns, the right most columns wouldn't work for charms. Then it looks at the Y positions. This is where we limit it to the top half. So if it comes back that the charm is in a row greater than the 3rd, it returns as unusable. These x and y borders are how you can make your charm zone as large or as small as you would like. If you made an inventory that is massive, you can accomodate that. Or if you just want to only be able to use 2 spots in inventory, you can do that. Do note that if you intend to use this in a mod that will have more than just yourself or friends playing, large and grand charms would still be used if the top spot was on row 4. This could be taken advantage of if you're not careful. This is because it is checking the top inventory spot of a charm. The fix for this would either be (easy) make the bottom half your charm zone or (hard) add another check for charm size (good luck).

9. We are almost done, but first we have to save this so it is implemented in the game. To do that Right-click -> copy -> select all. Rick-click -> copy to executable -> all modifications. Click Copy All. Right-click one more time -> save file. You can save your copy in your root folder or in a different location if you want to patch it in later.

You now have a charm zone in your 1.09x mod. I hope this has helped you and good luck.

Last note: you may want to make sure that you don't have your D2.exe running silent in the background when you do this to avoid any issues. Just check your task manager and close if it is there before you implement the new file.

Magiczny
Posts: 36
Joined: Tue Apr 21, 2020 1:35 pm
Poland

Re: 1.09x Tutorial How to Make Charm Zone in Inventory

Post by Magiczny » Tue May 26, 2020 9:07 am

Hey!

:toasting:
Worth a good beer!

Peace!

Post Reply

Return to “Code Editing”