From 6635139646fb3f69332b38e59c8ec4db4e0444f0 Mon Sep 17 00:00:00 2001 From: oskros <53574923+oskros@users.noreply.github.com> Date: Thu, 22 Oct 2020 23:09:46 +0200 Subject: [PATCH] Try to build item name loader, but failing so far.. --- media/stat_map.csv | 2 +- memory_reader/reader.py | 92 ++++++++++++++++++++++++++++++++-- memory_reader/stat_mappings.py | 44 ++++++++-------- 3 files changed, 111 insertions(+), 27 deletions(-) diff --git a/media/stat_map.csv b/media/stat_map.csv index dad5c744..38a6e261 100644 --- a/media/stat_map.csv +++ b/media/stat_map.csv @@ -84,7 +84,7 @@ ID,Name,Div,Mul,Abbr,Show,Perc,ShowValue,Negate 82,TimeDuration,1,1,,1,0,1,0 83,ClassSkills,1,1,,1,0,1,0 84,UnsentParameter,1,1,,1,0,1,0 -85,AddExperience,1,1,Experience Gained,1,1,1,0 +85,AddExperience,1,1,Exp Gained,1,1,1,0 86,LifeAfterEachKill,1,1,LAEK,1,0,1,0 87,ReduceVendorPrices,1,1,Vendor Prices,1,1,1,1 88,DoubleHerbDuration,1,1,,1,0,1,0 diff --git a/memory_reader/reader.py b/memory_reader/reader.py index f49123fc..8423ebd5 100644 --- a/memory_reader/reader.py +++ b/memory_reader/reader.py @@ -29,6 +29,7 @@ def __init__(self, process_name=D2_GAME_EXE): self.d2client = None self.d2game = None self.d2net = None + self.d2lang = None # print([x.name for x in self.pm.list_modules()]) for mod in self.pm.list_modules(): mod_str = mod.name.lower() @@ -42,6 +43,8 @@ def __init__(self, process_name=D2_GAME_EXE): self.d2net = mod.lpBaseOfDll elif mod_str == 'd2common.dll': self.d2common = mod.lpBaseOfDll + elif mod_str == 'd2lang.dll': + self.d2lang = mod.lpBaseOfDll if self.is_d2se or self.d2_ver in ['1.13c', '1.13d']: if self.d2client is None or self.d2game is None or self.d2net is None: @@ -56,6 +59,12 @@ def __init__(self, process_name=D2_GAME_EXE): self.monster_add_adr = None self.hovered_item = None self.item_descripts = None + self.str_indexer_table = None + self.str_address_table = None + self.patch_str_indexer_table = None + self.patch_str_address_table = None + self.exp_str_indexer_table = None + self.exp_str_address_table = None def map_ptrs(self): if self.d2_ver == '1.13c': @@ -67,6 +76,12 @@ def map_ptrs(self): self.monster_add_adr = 0x0 self.hovered_item = self.d2client + 0x11BC38 self.item_descripts = self.d2common + 0x9FB94 + self.str_indexer_table = self.d2lang + 0x10A64 + self.str_address_table = self.d2lang + 0x10a68 + self.patch_str_indexer_table = self.d2lang + 0x10A80 + self.patch_str_address_table = self.d2lang + 0x10A6C + self.exp_str_indexer_table = self.d2lang + 0x10A84 + self.exp_str_address_table = self.d2lang + 0x10A70 elif self.d2_ver == '1.13d': self.world_ptr = self.d2game + 0x111C10 self.players_x_ptr = self.d2game + 0x111C44 @@ -176,7 +191,10 @@ def get_stats(self, unit, translate_stat=False): cur_addr = stat_array_addr + i * 8 histatid = self.pm.read_short(cur_addr + 0x0) lostatid = self.pm.read_short(cur_addr + 0x2) - value = self.pm.read_int(cur_addr + 0x4) + if lostatid == 13: + value = self.pm.read_uint(cur_addr + 0x4) + else: + value = self.pm.read_int(cur_addr + 0x4) if translate_stat: vals.append(reader_utils.translate_stat(histatid=histatid, lostatid=lostatid, value=value, stat_map=stat_mappings.STAT_MAP)) @@ -221,6 +239,68 @@ def process_unit(self, uadr): else: self.observed_guids.add(game_guid) + def get_string_table_by_identifier(self, identifier): + if identifier >= 0x4E20: # 20.000 + return {'index': self.exp_str_indexer_table, + 'address': self.exp_str_address_table, + 'offset': 0x4E20} + elif identifier >= 0x2710: # 10.000 + return {'index': self.patch_str_indexer_table, + 'address': self.patch_str_address_table, + 'offset': 0x2710} + else: + return {'index': self.str_indexer_table, + 'address': self.str_address_table, + 'offset': 0x0} + + def lookup_string_table(self, identifier): + str_table = self.get_string_table_by_identifier(identifier) + identifier -= str_table['offset'] + + indexer_table = self.pm.read_uint(str_table['index']) + address_table = self.pm.read_ushort(str_table['address']) + + identifier_count = self.pm.read_ushort(indexer_table + 0x2) + if identifier >= identifier_count: + identifier = 0x1F4 + + str_data_region = indexer_table + 0x15 + get_address_index_location = lambda index: str_data_region + index * 2 # sizeof(ushort) = 2 + address_table_index = self.pm.read_ushort(get_address_index_location(identifier)) + + if address_table_index >= self.pm.read_uint(indexer_table + 0x4): + return None + + string_info_block = get_address_index_location(identifier_count) + string_info_address = string_info_block + address_table_index * 0x11 + + end_test = indexer_table + self.pm.read_ushort(indexer_table + 0x11) + if string_info_address >= end_test: + return None + + if not self.pm.read_ushort(string_info_address): + return None + + string_address = self.pm.read_uint(address_table + address_table_index * 4) # sizeof(uint) = 4 + if not string_address: + return None + + return self.get_null_terminated_string(string_address, 0x100, 0x4000) + + def get_null_terminated_string(self, address, size, max_size): + buffer_size = size + value = None + while buffer_size <= max_size: + value = self.pm.read_string(address, buffer_size) + null_terminator_index = value.index('\0') + if null_terminator_index >= 0: + value = value.replace('\0', '') + return value + buffer_size *= 2 + return value + + + if __name__ == '__main__': # print(elevate_access(lambda: eval('D2Reader().in_game()'))) @@ -228,6 +308,8 @@ def process_unit(self, uadr): # print(r.d2_ver) r.map_ptrs() + + import tkinter as tk root = tk.Tk() root.wm_attributes("-topmost", 1) @@ -239,9 +321,11 @@ def update_hovered(cur_unit): if p_unit > 0 and p_unit != cur_unit: cur_unit = p_unit e_class = r.pm.read_uint(p_unit + 0x4) - item_descr_len = 0x1A8 - - # r.pm.read_string(r.item_descripts + item_descr_len * e_class + 0xF4) + item_descr_len = r.pm.read_uint(r.item_descripts) + item_descr_adr = r.pm.read_uint(r.item_descripts + 0x4) + specific_item_adr = r.pm.read_uint(item_descr_adr + item_descr_len*e_class) + string_index = r.pm.read_ushort(specific_item_adr + 0xF4) + name = r.lookup_string_table(string_index) vals = r.get_stats(p_unit, translate_stat=True) vals = reader_utils.group_and_hide_stats(vals) diff --git a/memory_reader/stat_mappings.py b/memory_reader/stat_mappings.py index 22b2a5a5..508f23e6 100644 --- a/memory_reader/stat_mappings.py +++ b/memory_reader/stat_mappings.py @@ -5,34 +5,34 @@ def load_stat_map(): - lib_path = os.path.join(getattr(sys, '_MEIPASS', os.path.abspath('..')), media_path + 'stat_map.csv') + lib_path = os.path.join(getattr(sys, '_MEIPASS', os.path.abspath('.')), media_path + 'stat_map.csv') with open(lib_path, 'r') as fo: out = {int(row['ID']): row for row in csv.DictReader(fo)} return out SKILLTABS = { - 0: 'Bow skills (Ama)', - 1: 'PM skills (Ama)', - 2: 'Java skills (Ama)', - 8: 'Fire skills (Sorc)', - 9: 'Light skills (Sorc)', - 10: 'Cold skills (Sorc)', - 16: 'Curse skills (Nec)', - 17: 'PB skills (Nec)', - 18: 'Summon skills (Nec)', - 24: 'Combat skills (Pala)', - 25: 'Offensive skills (Pala)', - 26: 'Defensive skills (Pala)', - 32: 'Combat skills (Barb)', - 33: 'Mastery skills (Barb)', - 34: 'Warcry skills (Barb)', - 40: 'Summon skills (Druid)', - 41: 'Shapeshifting skills (Druid)', - 42: 'Ele skills (Druid)', - 48: 'Trap skills (Assa)', - 49: 'Shadow skills (Assa)', - 50: 'Martial skills (Assa)' + 0: 'Bow Skills (Ama)', + 1: 'PM Skills (Ama)', + 2: 'Java Skills (Ama)', + 8: 'Fire Skills (Sorc)', + 9: 'Light Skills (Sorc)', + 10: 'Cold Skills (Sorc)', + 16: 'Curse Skills (Nec)', + 17: 'PB Skills (Nec)', + 18: 'Summon Skills (Nec)', + 24: 'Combat Skills (Pala)', + 25: 'Offensive Skills (Pala)', + 26: 'Defensive Skills (Pala)', + 32: 'Combat Skills (Barb)', + 33: 'Mastery Skills (Barb)', + 34: 'Warcry Skills (Barb)', + 40: 'Summon Skills (Druid)', + 41: 'Shapeshifting Skills (Druid)', + 42: 'Ele Skills (Druid)', + 48: 'Trap Skills (Assa)', + 49: 'Shadow Skills (Assa)', + 50: 'Martial Skills (Assa)' } CLASSSKILLS = {