Adding new TXT files to this game is a must for more extensive changes because we'd otherwise be committing the severe crime of needless hardcoding. When something should be softcoded, hardcoded or softcoded and then cached should follow common sense. Things that are done over and over every frame are best not put into a database format that needs to be looked up constantly, it's better to cache the relevant data on game start instead of quering anew every time.
First of all, TXT file engine has linker fields, linker fields are used to resolve Id pointers ('names') to the matching Id number ('index').
Code: Select all
LINKER_BodyLocs = D2CommonBase+0xA079C;
LINKER_Colors = D2CommonBase+0xA1144;
LINKER_CompCode = D2CommonBase+0xA0810;
LINKER_ElemTypes = D2CommonBase+0xA07AC;
LINKER_Events = D2CommonBase+0xA0808;
LINKER_HireDesc = D2CommonBase+0xA0848;
LINKER_HitClass = D2CommonBase+0xA07B4;
LINKER_ItemFormula = D2CommonBase+0x43160;
LINKER_Items = D2CommonBase+0xA0824;
LINKER_ItemStatCost = D2CommonBase+0xA1360;
LINKER_ItemTypes = D2CommonBase+0xA1384;
LINKER_MissCalc = D2CommonBase+0xA07EC;
LINKER_MissFormula = D2CommonBase+0x17F20;
LINKER_Missiles = D2CommonBase+0xA12F8;
LINKER_MonAi = D2CommonBase+0xA081C;
LINKER_MonCompData = D2CommonBase+0x2DC90;
LINKER_MonMode = D2CommonBase+0xA07BC;
LINKER_MonPlace = D2CommonBase+0xA1230;
LINKER_MonProp = D2CommonBase+0xA13BC;
LINKER_MonSounds = D2CommonBase+0xA1218;
LINKER_MonStats = D2CommonBase+0xA120C;
LINKER_MonStats2 = D2CommonBase+0xA1224;
LINKER_MonTypes = D2CommonBase+0xA13C8;
LINKER_MonUMod = D2CommonBase+0xA13DC;
LINKER_Overlay = D2CommonBase+0xA1348;
LINKER_PetType = D2CommonBase+0xA137C;
LINKER_PlayerClass = D2CommonBase+0xA0794;
LINKER_PlrMode = D2CommonBase+0xA07C4;
LINKER_Properties = D2CommonBase+0xA0838;
LINKER_PropFormula = D2CommonBase+0x42E70;
LINKER_Sets = D2CommonBase+0xA1398;
LINKER_SkillCalc = D2CommonBase+0xA07CC;
LINKER_SkillCodes = D2CommonBase+0xA0800;
LINKER_SkillDesc = D2CommonBase+0xA1320;
LINKER_SkillFormula = D2CommonBase+0x100F0;
LINKER_Skills = D2CommonBase+0xA132C;
LINKER_Sounds = D2CommonBase+0xA0928;
LINKER_States = D2CommonBase+0xA0850;
LINKER_StorePage = D2CommonBase+0xA07A4;
LINKER_Strings = D2CommonBase+0x0E2D0;
LINKER_SuperUniques = D2CommonBase+0xA1268;
LINKER_TreasureClassEx = D2CommonBase+0xA1148;
Of course, linker fields alone are not enough, what about fields that don't hold other record Ids? The game has several types of fields in its database files, identified by a ENUM (as far as I can say this was a enum, not just entering numbers from memory).
Code: Select all
FIELDTYPE_DATA_ASCII = 1,
FIELDTYPE_DATA_DWORD = 2,
FIELDTYPE_DATA_WORD = 3,
FIELDTYPE_DATA_BYTE = 4,
FIELDTYPE_UNKNOWN_5 = 5,
FIELDTYPE_DATA_BYTE_2 = 6,
FIELDTYPE_DATA_DWORD_2 = 8,
FIELDTYPE_DATA_RAW = 9,
FIELDTYPE_ASCII_TO_CODE = 10,
FIELDTYPE_UNKNOWN_11 = 11,
FIELDTYPE_UNKNOWN_12 = 12,
FIELDTYPE_CODE_TO_BYTE = 13,
FIELDTYPE_UNKNOWN_14 = 14,
FIELDTYPE_CODE_TO_WORD = 15,
FIELDTYPE_UNKNOWN_16 = 16,
FIELDTYPE_NAME_TO_INDEX = 17,
FIELDTYPE_NAME_TO_INDEX_2 = 18,
FIELDTYPE_NAME_TO_DWORD = 19,
FIELDTYPE_NAME_TO_WORD = 20,
FIELDTYPE_NAME_TO_WORD_2 = 21,
FIELDTYPE_KEY_TO_WORD = 22,
FIELDTYPE_MONSTER_COMPS = 23,
FIELDTYPE_UNKNOWN_24 = 24,
FIELDTYPE_CALC_TO_DWORD = 25,
FIELDTYPE_DATA_BIT = 26,
Of course, not all of these field types are compatible with all linker fields, below is a list of linker functions vs. field types.
These fields are storing actual raw data in specific formats (raw ASCII text, conversions to numbers [aka atoi] and so on), this is the actual data stored in the files, and what you'll be using most of the time.
FIELDTYPE_DATA_ASCII (1)
This will read n-characters (n being defined in BinField -> FieldSize, see below) into the binary image of the text file. Say you want to store the path anf filename of a DC6 file, you would have a char array holding the path (like szPath[32]) then you'd use FIELDTYPE_ASCII_STRING as your field type, and set BinField -> FieldSize to 31 (why 31?, so that the last character remains 0 of course).
FIELDTYPE_DATA_BYTE (4)
This tells the game that this field stores only 1 byte of data (this applies both to actual bytes, unsigned bytes and bools, it just depends on how your code uses it later!). This will convert numbers to their values (aka the ASCII string '10' becomes the value 10). FIELDTYPE_DATA_BYTE_2 seams to be the signed variant. But this makes no real difference, this makes more of a difference in the original code because of compiler warnings I guess.
FIELDTYPE_DATA_WORD (3)
This is like the above, but it tells the game this field stores bytes of data (aka 1 word). This will convert numbers to their values (aka the ASCII string '10' becomes the value 10).
FIELDTYPE_DATA_DWORD (2)
Again like the above, but this field holds 4 bytes instead of 2 (aka 1 DWORD). This will convert numbers to their values (aka the ASCII string '10' becomes the value 10).
FIELDTYPE_DATA_BIT (26)
This field is used to store a single bit, the minimum size of a bitmask in this game is one DWORD (aka 4 bytes, thus storing 32 individual flags). If you were to use 33 bit flags in a single file, the field grows to 8 bytes in length (and so on and so forth). This is a more efficient way to store booleans then allocating a single byte to each. In this case BinField -> FieldSize indicates the bit in the mask this column symbolizes. That is, say column 'warp' would be the 4th bit in a bitmask, then you'd set BinField -> FieldSize to 3 (and not 4). As you probably expect, any value other then 0 and 'blank' will be treated as true (aka the bit will be set).
FIELDTYPE_DATA_RAW (9)
This field type is used to store 4-letter codes without building a linkage table from the field (like fieldtype 10, FIELDTYPE_ASCII_TO_CODE would). Use this when having item codes in a file. UPDATE: This field type converts blanks to 0x20 also (aka space instead of 0x00), so a blank field in the *.txt becomes 0x20202020.
These fields build linkage tables, linkage tables are used by other linker functions to link a 'name' to a 'index', you should leave your fingers of these until you don't need this topic for reference anymore.
FIELDTYPE_ASCII_TO_CODE (10)
Fields: LINKER_CompCode, LINKER_PlayerClass, LINKER_BodyLoc, LINKER_StorePage, LINKER_ElemTypes, LINKER_HitClass, LINKER_Colors, LINKER_HireDesc, LINKER_MonMode, LINKER_PlrMode, LINKER_SkillCalc, LINKER_MissCalc, LINKER_ItemTypes, LINKER_Items
This field type reads 4 ASCII characters and outputs a DWORD, and does not attempt to do any conversions, that is ABCD becomes 0x44434241, these are the famous four-letter-codes you can find everywhere through the game.
FIELDTYPE_NAME_TO_INDEX (17)
Fields: LINKER_MonAi, LINKER_MonPlace, LINKER_SkillCodes, LINKER_Events, LINKER_MonTypes, LINKER_Overlay, LINKER_ItemStatCost, LINKER_Properties, LINKER_Missiles, LINKER_States, LINKER_MonStats, LINKER_SkillDesc, LINKER_Skills, LINKER_MonProp, LINKER_MonSounds, LINKER_MonStats2, LINKER_MonUMod, LINKER_Properties, LINKER_Sets, LINKER_SuperUniques, LINKER_TreasureClassEx
This field type is similar to the first, however it reads a complete ASCII character string and builds a conversion table composed of record numbers. Each index in this conversion table is of WORD size (thus anything using this field for linkage will at most support 65536 individual entries, but to be on the safe side, always consider 32767 as the maximum). In all the above cases, a value of -1 (or 0xFFFF) is considered an INVALID_CLASS.
Note: LINKER_SkillCodes builds a internal-only reference table of skill codes (actually the bin image is just overwritten later by the real skills.bin) so that other files can link to skills.bin before skills.bin itself is ever compiled.
FIELDTYPE_NAME_TO_INDEX_2 (18)
Fields: LINKER_PetTypes
This field type is only used by PetTypes.txt and nothing else, it functions like the above in general, exact difference not known, probably related to default values. Don't use it unless you know why, and if you do know why, go ahead and post 'why' here so that I can correct this entry properly.
FIELDTYPE_MONSTER_COMPS (23)
Fields: LINKER_MonCompData
This is a extremely special field, used solely for MonStats2.bin component data, it would go way too far to explain this here and this cannot be used anywhere else (hardcoded offsets in the linker function and all). For the record it puts at +0x15 of the bin image an array of 16 bytes, each holding the number of random components assigned to the unit and at +0x26 of the bin image 16 arrays of 12 arrays holding the individual components. (aka arr[16][12]).
These functions are of key importance, they allow us to link text records together using a human understandable symbolic code or name, instead of only Id numbers (like the game used to have before v1.10 in most files).
FIELDTYPE_CODE_TO_BYTE (13)
Fields: LINKER_CompCode, LINKER_PlayerClass, LINKER_BodyLoc, LINKER_StorePage, LINKER_ElemTypes, LINKER_HitClass, LINKER_Colors, LINKER_HireDesc, LINKER_MonMode, LINKER_PlrMode, LINKER_SkillCalc, LINKER_MissCalc, LINKER_Items
This field type converts a 4-letter code to a id number occupying one BYTE, the default value of these fields depends on the linker function used. For PlayerClass it is 7 (0-6 being valid classes, and 7 being CLASS_NONE). In vanilla all the above fields use this besides LINKER_ItemTypes
FIELDTYPE_CODE_TO_WORD (15)
Fields: LINKER_ItemTypes (also LINKER_CompCode, LINKER_PlayerClass, LINKER_BodyLoc, LINKER_StorePage, LINKER_ElemTypes, LINKER_HitClass, LINKER_Colors, LINKER_HireDesc, LINKER_MonMode, LINKER_PlrMode, LINKER_SkillCalc, LINKER_MissCalc, LINKER_Items)
This field type converts a 4-letter code to a id number occupying one WORD, the default value depends on the linker function used, but only ItemTypes fields use this in vanilla, and IIRC the default for those is -1.
FIELDTYPE_NAME_TO_WORD (20)
Fields: LINKER_MonAi, LINKER_MonPlace, LINKER_SkillCodes, LINKER_Events, LINKER_MonTypes, LINKER_Overlay, LINKER_ItemStatCost, LINKER_Properties, LINKER_Missiles, LINKER_States, LINKER_MonStats, LINKER_SkillDesc, LINKER_Skills, LINKER_MonProp, LINKER_MonSounds, LINKER_MonStats2, LINKER_MonUMod, LINKER_Properties, LINKER_Sets, LINKER_SuperUniques, LINKER_TreasureClassEx
This is another variant of the above, the main difference is that this handles links which are entire ASCII strings and not just 4-character strings, in addition this will output the default value -1 if the link is invalid. This is also used for the range column in skills.txt for example. LINKER_TreasureClassEx is a very special case, because TreasureClassEx.txt gets compiled in old fashion (aka pure ASCII -> BIN dump), but later this gets replaced by TreasureClassExShort, which is a internal structure where fields are properly linked with indexes rather then names.
FIELDTYPE_NAME_TO_WORD_2 (21)
Fields: LINKER_PetTypes
This is like the above, but designed for PetTypes links only, like the special field used to generate PetTypes links in the first place.
FIELDTYPE_KEY_TO_WORD (22)
Fields: LINKER_Strings
This is a special field type, it is designed for strings from the *.tbl files, this field holds a textual string key in *.txt form, which is converted to a string index by the game using this fieldtype + linker function, the main special thing here is that nonexistant strings are replaced by the 'An Evil Force' string. The size of these fields is one WORD.
FIELDTYPE_CALC_TO_DWORD (25)
Fields: LINKER_MissFormula, LINKER_ItemFormula, LINKER_PropFormula, LINKER_SkillFormula
This field is used to encode formulae (aka calc fields), this is used for all calc fields in all files, what type of formula this turns out to be depends on the linker functions. The size of these fields is always a DWORD, the default value (when the formula is invalid or blank), is -1 (which the interpretor automatically disposes as 0 when evaluated). The fact this occupies a DWORD is why formulae are limited to 255 characters.
Calling the custom text file loader is probably the most important thing, otherwise how are the new files going to be compiled
Code: Select all
6FD662F3 |. /74 3B JE SHORT D2Common.6FD66330
6FD662F5 |. |A1 90EEDE6F MOV EAX,DWORD PTR DS:[6FDEEE90]
6FD662FA |. |33C9 XOR ECX,ECX
6FD662FC |. |85C0 TEST EAX,EAX
6FD662FE |. |51 PUSH ECX
6FD662FF |. |74 0A JE SHORT D2Common.6FD6630B
6FD66301 |. |68 F6080000 PUSH 8F6
6FD66306 |. |83C2 FC ADD EDX,-4
6FD66309 |. |EB 05 JMP SHORT D2Common.6FD66310
6FD6630B |> |68 F8080000 PUSH 8F8
6FD66310 |> |68 3C6EDE6F PUSH D2Common.6FDE6E3C
6FD66315 |. |E8 F22EFFFF CALL <JMP.&Fog.#10046>
6FD6631A |. |8B15 2809DF6F MOV EDX,DWORD PTR DS:[6FDF0928]
6FD66320 |. |52 PUSH EDX
6FD66321 |. |E8 462FFFFF CALL <JMP.&Fog.#10212>
6FD66326 |. |C705 2C09DF6F 00000000 MOV DWORD PTR DS:[6FDF092C],0
[color=red]6FD66330 |> \E8 ######## CALL ########
6FD66335 |. 81C4 08010000 ADD ESP,108
6FD6633B \. C2 0C00 RETN 0C[/color]
The structure looks like so:
Code: Select all
struct BinField
{
char* szFieldName;
int eFieldType;
int nFieldLength;
int nFieldOffset;
void* pLinkField;
};
eFieldType holds the enum value of the field type discussed above, aka FIELDTYPE_DATA_DWORD and so on.
nFieldLength holds special data, this is used only by FIELDTYPE_DATA_BITS and FIELDTYPE_DATA_ASCII for bit position and string length.
nFieldOffset holds the offset of the data within the bin structure.
pLinkField holds a pointer to the link field, for field types that need it.
A BinImage is compiled / loaded by calling D2Common.10496.
The prototype of this function looks like this: typedef void* (__stdcall * D2COMMON_10496)(void*,char*,BinField*,int*,DWORD);
You would normally call this function like this: D2Common10496(NULL,"NewTextFileName",BinFieldArray,&nRecords,sizeof(BinImageStructure));
The last entry in the BinField array must have szFieldName set to "end" for the game to know the previous column was indeed the last one. If you forget this you're welcomed to take a trip to crashisthan.
Here is an example:
Code: Select all
BinField AiParamsTable[] =
{
{"stall",FIELDTYPE_DATA_WORD,0,0,0},
{"stall (N)",FIELDTYPE_DATA_WORD,0,2,0},
{"stall (H)",FIELDTYPE_DATA_WORD,0,4,0},
{"health",FIELDTYPE_DATA_WORD,0,6,0},
{"health (N)",FIELDTYPE_DATA_WORD,0,8,0},
{"health (H)",FIELDTYPE_DATA_WORD,0,10,0},
{"approach",FIELDTYPE_DATA_WORD,0,12,0},
{"approach (N)",FIELDTYPE_DATA_WORD,0,14,0},
{"approach (H)",FIELDTYPE_DATA_WORD,0,16,0},
{"wander steps",FIELDTYPE_DATA_WORD,0,18,0},
{"wander steps (N)",FIELDTYPE_DATA_WORD,0,20,0},
{"wander steps (H)",FIELDTYPE_DATA_WORD,0,22,0},
{"steps",FIELDTYPE_DATA_WORD,0,24,0},
{"steps (N)",FIELDTYPE_DATA_WORD,0,26,0},
{"steps (H)",FIELDTYPE_DATA_WORD,0,28,0},
{"run speed",FIELDTYPE_DATA_WORD,0,30,0},
{"run speed (N)",FIELDTYPE_DATA_WORD,0,32,0},
{"run speed (H)",FIELDTYPE_DATA_WORD,0,34,0},
{"run",FIELDTYPE_DATA_WORD,0,36,0},
{"run (N)",FIELDTYPE_DATA_WORD,0,38,0},
{"run (H)",FIELDTYPE_DATA_WORD,0,40,0},
{"escape",FIELDTYPE_DATA_WORD,0,42,0},
{"escape (N)",FIELDTYPE_DATA_WORD,0,44,0},
{"escape (H)",FIELDTYPE_DATA_WORD,0,46,0},
{"curse dist",FIELDTYPE_DATA_WORD,0,48,0},
{"curse dist (N)",FIELDTYPE_DATA_WORD,0,50,0},
{"curse dist (H)",FIELDTYPE_DATA_WORD,0,52,0},
{"safe dist",FIELDTYPE_DATA_WORD,0,54,0},
{"safe dist (N)",FIELDTYPE_DATA_WORD,0,56,0},
{"safe dist (H)",FIELDTYPE_DATA_WORD,0,58,0},
{"escape dist",FIELDTYPE_DATA_WORD,0,60,0},
{"escape dist (N)",FIELDTYPE_DATA_WORD,0,62,0},
{"escape dist (H)",FIELDTYPE_DATA_WORD,0,64,0},
{"attack",FIELDTYPE_DATA_WORD,0,66,0},
{"attack (N)",FIELDTYPE_DATA_WORD,0,68,0},
{"attack (H)",FIELDTYPE_DATA_WORD,0,70,0},
{"attack2",FIELDTYPE_DATA_WORD,0,72,0},
{"attack2(N)",FIELDTYPE_DATA_WORD,0,74,0},
{"attack2 (H)",FIELDTYPE_DATA_WORD,0,76,0},
{"skill melee",FIELDTYPE_DATA_WORD,0,78,0},
{"skill melee (N)",FIELDTYPE_DATA_WORD,0,80,0},
{"skill melee (H)",FIELDTYPE_DATA_WORD,0,82,0},
{"skill range",FIELDTYPE_DATA_WORD,0,84,0},
{"skill range (N)",FIELDTYPE_DATA_WORD,0,86,0},
{"skill range (H)",FIELDTYPE_DATA_WORD,0,88,0},
{"skill strong",FIELDTYPE_DATA_WORD,0,90,0},
{"skill strong (N)",FIELDTYPE_DATA_WORD,0,92,0},
{"skill strong (H)",FIELDTYPE_DATA_WORD,0,94,0},
{"skill dist",FIELDTYPE_DATA_WORD,0,96,0},
{"skill dist (N)",FIELDTYPE_DATA_WORD,0,98,0},
{"skill dist (H)",FIELDTYPE_DATA_WORD,0,100,0},
{"skill curse",FIELDTYPE_DATA_WORD,0,102,0},
{"skill curse (N)",FIELDTYPE_DATA_WORD,0,104,0},
{"skill curse (H)",FIELDTYPE_DATA_WORD,0,106,0},
{"skill buff",FIELDTYPE_DATA_WORD,0,108,0},
{"skill buff (N)",FIELDTYPE_DATA_WORD,0,110,0},
{"skill buff (H)",FIELDTYPE_DATA_WORD,0,112,0},
{"skill heal",FIELDTYPE_DATA_WORD,0,114,0},
{"skill heal (N)",FIELDTYPE_DATA_WORD,0,116,0},
{"skill heal (H)",FIELDTYPE_DATA_WORD,0,118,0},
{"skill corpse",FIELDTYPE_DATA_WORD,0,120,0},
{"skill corpse (N)",FIELDTYPE_DATA_WORD,0,122,0},
{"skill corpse (H)",FIELDTYPE_DATA_WORD,0,124,0},
{"skill cmd",FIELDTYPE_DATA_WORD,0,126,0},
{"skill cmd (N)",FIELDTYPE_DATA_WORD,0,128,0},
{"skill cmd (H)",FIELDTYPE_DATA_WORD,0,130,0},
{"apply other",FIELDTYPE_DATA_WORD,0,132,0},
{"apply other (N)",FIELDTYPE_DATA_WORD,0,134,0},
{"apply other (H)",FIELDTYPE_DATA_WORD,0,136,0},
{"other dist",FIELDTYPE_DATA_WORD,0,138,0},
{"other dist (N)",FIELDTYPE_DATA_WORD,0,140,0},
{"other dist (H)",FIELDTYPE_DATA_WORD,0,142,0},
{"spawn",FIELDTYPE_DATA_WORD,0,144,0},
{"spawn (N)",FIELDTYPE_DATA_WORD,0,146,0},
{"spawn (H)",FIELDTYPE_DATA_WORD,0,148,0},
{"limit",FIELDTYPE_DATA_WORD,0,150,0},
{"limit (N)",FIELDTYPE_DATA_WORD,0,152,0},
{"limit (H)",FIELDTYPE_DATA_WORD,0,154,0},
{"delay",FIELDTYPE_DATA_WORD,0,156,0},
{"delay (N)",FIELDTYPE_DATA_WORD,0,158,0},
{"delay (H)",FIELDTYPE_DATA_WORD,0,160,0},
{"fixed spawn",FIELDTYPE_DATA_DATA,0,162,0},
{"endless spawn",FIELDTYPE_DATA_DATA,0,163,0},
{"wander idle",FIELDTYPE_DATA_DATA,0,164,0},
{"skill melee first",FIELDTYPE_DATA_DATA,0,165,0},
{"skill melee last",FIELDTYPE_DATA_DATA,0,166,0},
{"skill range first",FIELDTYPE_DATA_DATA,0,167,0},
{"skill range last",FIELDTYPE_DATA_DATA,0,168,0},
{"skill strong first",FIELDTYPE_DATA_DATA,0,169,0},
{"skill strong last",FIELDTYPE_DATA_DATA,0,170,0},
{"skill buff first",FIELDTYPE_DATA_DATA,0,171,0},
{"skill buff last",FIELDTYPE_DATA_DATA,0,172,0},
{"curse first",FIELDTYPE_DATA_DATA,0,173,0},
{"curse last",FIELDTYPE_DATA_DATA,0,174,0},
{"heal skill",FIELDTYPE_DATA_DATA,0,175,0},
{"corpse skill",FIELDTYPE_DATA_DATA,0,176,0},
{"cmd skill",FIELDTYPE_DATA_DATA,0,177,0},
{"spawn skill",FIELDTYPE_DATA_DATA,0,178,0},
{"run dist",FIELDTYPE_DATA_WORD,0,179,0},
{"run dist (N)",FIELDTYPE_DATA_WORD,0,181,0},
{"run dist (H)",FIELDTYPE_DATA_WORD,0,183,0},
{"escape steps",FIELDTYPE_DATA_WORD,0,185,0},
{"escape steps (N)",FIELDTYPE_DATA_WORD,0,187,0},
{"escape steps (H)",FIELDTYPE_DATA_WORD,0,189,0},
{"escape speed",FIELDTYPE_DATA_WORD,0,191,0},
{"escape speed (N)",FIELDTYPE_DATA_WORD,0,193,0},
{"escape speed (H)",FIELDTYPE_DATA_WORD,0,195,0},
{"health other",FIELDTYPE_DATA_WORD,0,197,0},
{"health other (N)",FIELDTYPE_DATA_WORD,0,199,0},
{"health other (H)",FIELDTYPE_DATA_WORD,0,201,0},
{"wander",FIELDTYPE_DATA_WORD,0,203,0},
{"wander (N)",FIELDTYPE_DATA_WORD,0,205,0},
{"wander (H)",FIELDTYPE_DATA_WORD,0,207,0},
{"curse frame",FIELDTYPE_DATA_WORD,0,209,0},
{"curse frame (N)",FIELDTYPE_DATA_WORD,0,211,0},
{"curse frame (H)",FIELDTYPE_DATA_WORD,0,213,0},
{"buff frame",FIELDTYPE_DATA_WORD,0,215,0},
{"buff frame (N)",FIELDTYPE_DATA_WORD,0,217,0},
{"buff frame (H)",FIELDTYPE_DATA_WORD,0,219,0},
{"teleport skill",FIELDTYPE_DATA_DATA,0,221,0},
{"tele dist",FIELDTYPE_DATA_WORD,0,222,0},
{"tele dist (N)",FIELDTYPE_DATA_WORD,0,224,0},
{"tele dist (H)",FIELDTYPE_DATA_WORD,0,226,0},
{"teleport",FIELDTYPE_DATA_WORD,0,228,0},
{"teleport (N)",FIELDTYPE_DATA_WORD,0,230,0},
{"teleport (H)",FIELDTYPE_DATA_WORD,0,232,0},
{"err range",FIELDTYPE_DATA_WORD,0,234,0},
{"err range (N)",FIELDTYPE_DATA_WORD,0,236,0},
{"err range (H)",FIELDTYPE_DATA_WORD,0,238,0},
{"strong dist",FIELDTYPE_DATA_WORD,0,240,0},
{"strong dist (N)",FIELDTYPE_DATA_WORD,0,242,0},
{"strong dist (H)",FIELDTYPE_DATA_WORD,0,244,0},
{"buff flag",FIELDTYPE_DATA_DWORD,0,246,0},
{"heal flag",FIELDTYPE_DATA_DWORD,0,250,0},
{"corpse flag",FIELDTYPE_DATA_DWORD,0,254,0},
{"spawn dist",FIELDTYPE_DATA_WORD,0,258,0},
{"spawn dist (N)",FIELDTYPE_DATA_WORD,0,260,0},
{"spawn dist (H)",FIELDTYPE_DATA_WORD,0,262,0},
{"set boss",FIELDTYPE_DATA_DATA,0,264,0},
{"charge skill",FIELDTYPE_DATA_DATA,0,265,0},
{"charge ticks",FIELDTYPE_DATA_WORD,0,266,0},
{"charge ticks (N)",FIELDTYPE_DATA_WORD,0,268,0},
{"charge ticks (H)",FIELDTYPE_DATA_WORD,0,270,0},
{"regen bonus",FIELDTYPE_DATA_WORD,0,272,0},
{"charge state",FIELDTYPE_NAME_TO_WORD,0,274,LINKER_States},
{"charge overlay",FIELDTYPE_NAME_TO_WORD,0,276,LINKER_Overlay},
{"needs target",FIELDTYPE_DATA_DATA,0,278,0},
{"hurt buff",FIELDTYPE_DATA_WORD,0,279,0},
{"hurt buff (N)",FIELDTYPE_DATA_WORD,0,281,0},
{"hurt buff (H)",FIELDTYPE_DATA_WORD,0,283,0},
{"activate dist",FIELDTYPE_DATA_WORD,0,285,0},
{"activate dist (N)",FIELDTYPE_DATA_WORD,0,287,0},
{"activate dist (H)",FIELDTYPE_DATA_WORD,0,289,0},
{"strong error",FIELDTYPE_DATA_DATA,0,291,0},
{"range error",FIELDTYPE_DATA_DATA,0,292,0},
{"die on end",FIELDTYPE_DATA_DATA,0,293,0},
{"dis-engage",FIELDTYPE_DATA_WORD,0,294,0},
{"dis-engage (N)",FIELDTYPE_DATA_WORD,0,296,0},
{"dis-engage (H)",FIELDTYPE_DATA_WORD,0,298,0},
{"cmd dist",FIELDTYPE_DATA_WORD,0,300,0},
{"cmd dist (N)",FIELDTYPE_DATA_WORD,0,302,0},
{"cmd dist (H)",FIELDTYPE_DATA_WORD,0,304,0},
{"hurt buff skill",FIELDTYPE_DATA_DATA,0,306,0},
{"rand tele",FIELDTYPE_DATA_WORD,0,307,0},
{"rand tele (N)",FIELDTYPE_DATA_WORD,0,309,0},
{"rand tele (H)",FIELDTYPE_DATA_WORD,0,311,0},
{"rand tele dist",FIELDTYPE_DATA_WORD,0,313,0},
{"rand tele dist (N)",FIELDTYPE_DATA_WORD,0,315,0},
{"rand tele dist (H)",FIELDTYPE_DATA_WORD,0,317,0},
{"anim length",FIELDTYPE_DATA_WORD,0,319,0},
{"end",0,0,0,0},
};
int nRecords = 0;
AiParams* pAiParams = (AiParams*)Common10496(NULL,"monaiparam",AiParamsTable,&nRecords,sizeof(AiParams));
And likewise, we can't just load things and 'hope for the best', we also have to unload them later
Code: Select all
6FD66104 |. /74 2F JE SHORT D2Common.6FD66135
6FD66106 |. |A1 90EEDE6F MOV EAX,DWORD PTR DS:[6FDEEE90]
6FD6610B |. |33C9 XOR ECX,ECX
6FD6610D |. |3BC6 CMP EAX,ESI
6FD6610F |. |56 PUSH ESI
6FD66110 |. |74 14 JE SHORT D2Common.6FD66126
6FD66112 |. |68 F6080000 PUSH 8F6
6FD66117 |. |83C2 FC ADD EDX,-4
6FD6611A |. |68 3C6EDE6F PUSH D2Common.6FDE6E3C
6FD6611F |. |E8 E830FFFF CALL <JMP.&Fog.#10046>
6FD66124 |. |EB 0F JMP SHORT D2Common.6FD66135
6FD66126 |> |68 F8080000 PUSH 8F8
6FD6612B |. |68 3C6EDE6F PUSH D2Common.6FDE6E3C
6FD66130 |. |E8 D730FFFF CALL <JMP.&Fog.#10046>
[color=red]6FD66135 |> \E8 ######## CALL ########
6FD6613A |. 5E POP ESI
6FD6613B \. C3 RETN[/color]
To unload your BIN image, you need to call Fog.10046, which is blizzards wrapper for GlobalFree, you could just pass it to globalfree and be done with it, but there are all kinds of memory management gadgets in the game, so we better don't think but do what they've done before.
The prototype for this function is: typedef BOOL (__fastcall * FOG10046)(void*,void*,char*,int,DWORD);
To use it: Fog10046(NULL,pMemoryToFree,__FILE__,__LINE__,0); The first argument is the memory pool used, but D2 never uses pools other then none, so just pass NULL here (thats what they also do).