For 1.0.8a
I'll post copy pasted code, this will not work if you paste it into your project.
If you can't understand it, please don't ask about it, because I've no time to respond.
What you need:
- advanced understanding of C#
Note:
- MR is my memory reader class
- Please credit me in your tool using this code. Thanks!
Data
Levelarea codes and names
Gamebalance table
Attributes
Code:
public readonly int ObjectManagerAddress = 0x18CE394;
public readonly int ObjectManager_ofs_storage = 0x7CC;
public readonly int ObjectManagerStorage_ofs__local = 0x1B8;
public readonly int ObjectManagerStorage_ofs__data = 0x0A8;
public readonly int ObjectManagerStorage_ofs__attrib_groups = 0x0C8;
public readonly int player_data_size = 0x8598;
public readonly int player_ofs__area = 0x78F8;
public readonly int player_ofs__seed = 0x7448;
public readonly int player_ofs__name = 0x7450;
public readonly int player_ofs__class = 0x78FC;
public readonly int player_ofs__actor_id = 0x8;
public readonly int acd_ofs__id = 0x000;
public readonly int acd_ofs__gb_type = 0x0B0;
public readonly int acd_ofs__gb_id = 0x0B4;
public readonly int acd_ofs__attrib_group_id = 0x120;
public readonly int acd_ofs__sno = 0x090;
public readonly int acd_ofs__item_location = 0x114;
public readonly int acd_ofs__item_x = 0x118;
public readonly int acd_ofs__item_y = 0x11C;
public readonly int acd_ofs__pos_x = 0x0D0;
public readonly int acd_ofs__pos_y = 0x0D4;
public readonly int acd_ofs__pos_z = 0x0D8;
public readonly int acd_ofs__monster_type = 0x0B8;
public readonly int attrib_link_ofs__next = 0x000;
public readonly int attrib_link_ofs__id = 0x004;
public readonly int attrib_link_ofs__value = 0x008;
public readonly int AttribGroupsContainer_ofs__FastAttribGroups = 0x070;
Reading Player's Location
Code:
int ObjectManagerPtr = MR.ReadInt(ObjectManagerAddress);
int ObjectManagerStoragePtr = ObjectManagerPtr + ObjectManager_ofs_storage;
int ObjectManagerStorageDataPtr = MR.ReadInt(ObjectManagerStoragePtr + ObjectManagerStorage_ofs__data);
int ObjectManagerStorageLocalPtr = MR.ReadInt(ObjectManagerStoragePtr + ObjectManagerStorage_ofs__local);
int MyPlayerIndex = MR.ReadInt(ObjectManagerStorageLocalPtr);
for (int i = 0; i < 4; i++)
{
uint seed = MR.ReadUInt(ObjectManagerStorageDataPtr + i * player_data_size + 0x58 + player_ofs__seed);
if (seed == 0) continue;
string CharacterName = MR.ReadString(ObjectManagerStorageDataPtr + i * player_data_size + 0x58 + player_ofs__name, 12 + 1, Encoding.ASCII, true);
uint class_idx = MR.ReadUInt(ObjectManagerStorageDataPtr + i * player_data_size + 0x58 + player_ofs__class); // 0=DH, 1=barb, 2=wiz, 3=wd, 4=monk
uint levelarea_id = MR.ReadUInt(ObjectManagerStorageDataPtr + i * player_data_size + 0x58 + player_ofs__area);
if (MyPlayerIndex == i)
{
// tadamm: this is our player's levelarea_id and other stuff
}
uint ActorID = MR.ReadUInt(ObjectManagerStorageDataPtr + i * player_data_size + 0x58 + player_ofs__actor_id);
if ((ActorID == 0) || (ActorID == uint.MaxValue))
{
// our player's actor is always valid, but party members too far from us will "disappear"
}
}
Reading an UI Component
What you need:
- the UI component's path ("Name", don't ask me for it)
Note:
- figuring out the idiot internal coodinate -> screen coordinate conversion took me 4 days, and it is not working with letterbox, but pixel-perfect on every resolution and screen ratios
The basics
Code:
ObjectManagerPtr = MR.ReadInt(ObjectManagerAddress);
ObjectManagerStoragePtr = ObjectManagerPtr + ObjectManager_ofs_storage;
ui_mgr_ptr = MR.ReadInt(ObjectManagerStoragePtr + 0x1A8);
ui_component_map_hashtable = MR.ReadInt(ui_mgr_ptr + 0x000);
ui_component_table_start = MR.ReadInt(ui_component_map_hashtable + 0x008);
ui_component_table_size = MR.ReadInt(ui_component_map_hashtable + 0x010);
ui_component_table = new int[ui_component_table_size];
ui_component_table_mask = MR.ReadInt(ui_component_map_hashtable + 0x040);
MR.ReadMem(ui_component_table_start, ui_component_table, ui_component_table_size * 4); // blocked memory read
1) Dumping all components
Code:
for (int i = 0; i < ui_component_table_size; i++)
{
int link = MR.ReadInt(ui_component_table_start + i * 4);
while (link != 0)
{
int ui_component_ptr = MR.ReadInt(link + 0x210);
bool visible = MR.ReadInt(ui_component_ptr + 0x028) == 1;
ulong hash = MR.ReadULong(ui_component_ptr + 0x030 + 0x00);
string name = MR.ReadString(ui_component_ptr + 0x030 + 0x08, 256, Encoding.ASCII, true);
float X = MR.ReadFloat(ui_component_ptr + 0x508);
float Y = MR.ReadFloat(ui_component_ptr + 0x50C);
float R = MR.ReadFloat(ui_component_ptr + 0x510);
float B = MR.ReadFloat(ui_component_ptr + 0x514);
int TextPtr = MR.ReadInt(ui_component_ptr + 0xAE0);
string Text = null;
if (TextPtr != 0) Text = MR.ReadString(TextPtr, 256, Encoding.ASCII, true); // true means trimming at \0
if (visible)
{
float fourthreewidth = (float)D3ClientWindowSize.Height / 3.0f * 4.0f;
float mb_ratio = 600.0f / D3ClientWindowSize.Height;
float mb = (D3ClientWindowSize.Width - fourthreewidth) * mb_ratio;
float SX = (X + mb) / (1200.0f / D3ClientWindowSize.Height);
float SR = (R + mb) / (1200.0f / D3ClientWindowSize.Height);
float SY = Y * (D3ClientWindowSize.Height / 1200.0f);
float SB = (B - 1) * (D3ClientWindowSize.Height / 1200.0f);
Rect.X = Convert.ToInt32(SX);
Rect.Y = Convert.ToInt32(SY);
Rect.Width = Convert.ToInt32(SR - SX - 1);
Rect.Height = Convert.ToInt32(SB - SY - 1);
}
// do something with the variables here...
link = MR.ReadInt(link + 0x000);
}
}
2. Reading an UI component with a specific path (Name)
Code:
Hash = FNVHash(Name.ToLower());
Bucket = (int)(((Hash >> 32) ^ (uint)Hash) & (uint)ui_component_table_mask);
pair_address = ui_component_table[Bucket];
msUIPair pair = new msUIPair();
int n = 0;
while ((pair_address != 0) && (n < 10))
{
MR.ReadMem(pair_address, pair, 16);
if (pair.UIReference_Hash == this.Hash)
{
int ui_component_ptr = MR.ReadInt(pair_address + msUIPair.ValueOfs); // pair.value
// found it, use this ptr to read and process UI component!
return;
}
pair_address = pair.NextPair;
n++;
}
The used structures:
Code:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public sealed class msUIPair
{
public int NextPair; // 0x000:4
public int UIReference_Unknown; // 0x004:4
public ulong UIReference_Hash; // 0x008:8
// char[200] UIReference_Path... // 0x010:200
public static int ValueOfs = 0x210;
}
Hash generator:
Code:
private ulong FNVHash(string s)
{
ulong FNVHash = 0xCBF29CE484222325;
for (int i = 0; i < s.Length; i++)
{
FNVHash = FNVHash ^ (byte)s[i];
FNVHash = FNVHash * 0x100000001B3;
FNVHash = FNVHash & 0xFFFFFFFFFFFFFFFF;
}
return FNVHash;
}
Reading Attributes
What you need:
- the attribute's ID and mask
The basics
You can find attributes by their Index (the number), and their mask.
Most attributes has no mask (no mask = 0xFFFFF).
Examples:
- Resistance_All has no mask, so it's "FullID" = 0xFFFFF << 12 | 99
- "lightning resist" is contained in "Resistance" attribute with mask 0x2, so it's FullID = 0x2 << 12 | 96
Code:
public readonly int ObjectManagerStorage_ofs__acds = 0xD4;
ObjectManagerPtr = MR.ReadInt(ObjectManagerAddress);
ObjectManagerStoragePtr = ObjectManagerPtr + ObjectManager_ofs_storage;
addr_acds = MR.ReadInt(ObjectManagerStoragePtr + ObjectManagerStorage_ofs__acds);
addr_attrib_groups = MR.ReadInt(ObjectManagerStoragePtr + ObjectManagerStorage_ofs__attrib_groups);
addr_acds_container = MR.ReadInt(addr_acds);
acd_data_list_ofs = MR.ReadInt(addr_acds_container + 0x148); //container.List, double pointer
acd_data_start_ofs = MR.ReadInt(acd_data_list_ofs); //container.List
acd_sizeof = MR.ReadInt(addr_acds_container + 0x104); //container.SizeOf
acd_bits = MR.ReadInt(addr_acds_container + 0x18C); //container.Bits
acd_count_ofs = addr_acds_container + 0x108; //container.Count
addr_attrib_groups_container = MR.ReadInt(addr_attrib_groups + AttribGroupsContainer_ofs__FastAttribGroups); // AttribGroupsContainer.FastAttribGroups
attrib_data_list_ofs = MR.ReadInt(addr_attrib_groups_container + 0x148); //container.List, double pointer
attrib_sizeof = MR.ReadInt(addr_attrib_groups_container + 0x104); //container.SizeOf
attrib_bits = MR.ReadInt(addr_attrib_groups_container + 0x18C); //container.Bits
Enumerating All Actors and their Attributes
Code:
ACDCount = MR.ReadInt(acd_count_ofs);
for (int i = 0; i <= ACDCount; i++)
{
int acd_address = acd_data_start_ofs + i * acd_sizeof;
uint AcdID = MR.ReadUInt(acd_address + acd_ofs__id);
if (AcdID == uint.MaxValue) continue;
int gb_type = MR.ReadInt(acd_address + acd_ofs__gb_type); // gamebalance type
uint gb_id = MR.ReadUInt(acd_address + acd_ofs__gb_id); // gamebalance id
// gb_type = 2 means this is an item
int group_id = MR.ReadInt(acd_address + acd_ofs__attrib_group_id);
int full_id = group_id & 0xFFFF;
int a = full_id >> attrib_bits;
int b = full_id & ((1 << attrib_bits) - 1);
int dwFirst = MR.ReadInt((int)(attrib_data_list_ofs) + a * 4);
int group_ofs = (int)(dwFirst + attrib_sizeof * b);
int group_formula_ofs = MR.ReadInt(group_ofs + 0x010);
int acd_FormulaMapData = MR.ReadInt(group_formula_ofs + 0x008 + 0x000);
if (acd_FormulaMapData == 0) continue;
int acd_FormulaMapMask = MR.ReadInt(group_formula_ofs + 0x418);
uint SNO = MR.ReadUInt(acd_address + acd_ofs__sno);
int Location = MR.ReadInt(acd_address + acd_ofs__item_location);
// Inventory = 0, Head = 1, Torso = 2, RightHand = 3, LeftHand = 4, Hands = 5, Waist = 6, Feet = 7, Shoulders = 8, Legs = 9, Bracers = 10, LeftRing = 11, RightRing = 12, Neck = 13, Stash = 15, Merchant = 18
// regarding location you can read item_x (int), item_y (int) OR pos_x (float), pos_y (float), pos_z (float) values here
// item_x and item_y means the coordinates in stash or inventory
// pos_x, pos_y, pos_z are world coordinates for things on the floor
// if this is a monster, you can read the int @ acd_address + acd_ofs__monster_type
// monster types: normal = 0, elite = 1, yellow? = 2, purple? = 3, keywarden = 4
for (int link_index = 0; link_index <= 255; link_index++)
{
int link_root_address = acd_FormulaMapData + link_index * 4;
int link_address = MR.ReadInt(link_root_address);
int n = 0; // safety counter against infinite loops
while ((link_address != 0) && (n < 20))
{
uint id = MR.ReadUInt(link_address + attrib_link_ofs__id);
int attribute_index = (int)(id & 0xFFF);
if (attribute_index > 0)
{
uint mask = id >> 12;
int value = MR.ReadInt(link_address + attrib_link_ofs__value);
// do whatever you want with the attribute_index, mask, value here
// NOTE: you have to cast float type attributes to float here
}
link_address = MR.ReadInt(link_address + attrib_link_ofs__next);
n += 1;
}
}
}
}
Fast reading a specific attribute:
Code:
public int GetAttribHelper(int acd_FormulaMapData, int acd_FormulaMapMask, int AttributeIndex, uint AttributeMask, int Default = -1)
{
uint full_id = AttributeIndex | (AttributeMask << 12);
uint idxmask = full_id ^ (full_id >> 16);
int idx = (int)(acd_FormulaMapMask & idxmask);
int link_root_address = acd_FormulaMapData + idx * 4;
int link_address = MR.ReadInt(link_root_address);
int n = 0;
while ((link_address != 0) && (n < 20))
{
uint id = MR.ReadUInt(link_address + attrib_link_ofs__id);
if (id == full_id)
{
int v = MR.ReadInt(link_address + attrib_link_ofs__value);
return v;
}
link_address = MR.ReadInt(link_address + attrib_link_ofs__next);
n += 1;
}
return Default;
}
Example of reading a skill's cooldown
First you need the current "time" of the current game:
Code:
public readonly int ObjectManagerStorage_ofs__ticks = 0x94;
int CurrentTick = MR.ReadInt(ObjectManagerStoragePtr + ObjectManagerStorage_ofs__ticks);
Then you just read the Power_Cooldown (0x12D) attribute masked by the selected skill (sweeping wind = 96090)
Code:
int CooldownFinishTicks = GetAttribHelper(your_player_characters_acd_FormulaMapData, your_player_characters_acd_FormulaMapMask, 0x12D, 96090, 0);
double seconds_left = (CooldownFinishTicks > CurrentTick) ? (CooldownFinishTicks - CurrentTick) / 60.0d : 0;
List of the skills (ID's are not changed with patches, they are constants) (1.07 offsets)