diff --git a/bin/D2RMH.ini b/bin/D2RMH.ini index 0178ce2..a56f08c 100644 --- a/bin/D2RMH.ini +++ b/bin/D2RMH.ini @@ -131,11 +131,11 @@ msg_position = 0.95,0.25,2 ; {gamename} current game name ; {gamepass} current game password ; {region} region code of the server which current game creates on -; {serverip} ip address of the server which current game creates on +; {season} current battle.net season string text_panel_pattern = ;text_panel_pattern = {duration} ;text_panel_pattern = {difficulty}{n}{act}-{mapname}{n}{time}{n}{duration} -;text_panel_pattern = {gamename}{n}{gamepass}{n}{region} {serverip}{n}{difficulty}{n}{act}-{mapname}{n}{time}{n}{duration} +;text_panel_pattern = {gamename}{n}{gamepass}{n}{region} {season}{n}{difficulty}{n}{act}-{mapname}{n}{time}{n}{duration} ; text panel position, same as `msg_position` text_panel_position = 0.93,0.015,2 diff --git a/doc/ChangeLog.md b/doc/ChangeLog.md index d2f6fe8..bbf55e8 100644 --- a/doc/ChangeLog.md +++ b/doc/ChangeLog.md @@ -1,3 +1,20 @@ +# v0.10.0 +* (#102,#105,#106) update codes to support v1.2.69324 +* (#84) add game room name/password, server region and season patterns to `text_panel_pattern` +* (#97) don't draw other players not in same ACT + * change `full_line` to `line_style` in D2RMH.ini, and you can set it to 2 for displaying walkable path to target through new path finding functions, please not this would increase CPU usage. +* refactoring TTF rendering to fix glitches on text display +* add `object_size_minimal` to D2RMH.ini for minimal size of object quads +* add `draw_on_game_bar` to D2RMH.ini +* load all data from D2R Casc Storage directly now, so `gendata` is removed +* add support to read D2/D2R's TBL/DC6 font, as well as refactoring TTF rendering to suite fonts with fixed pixel size + * add support to load fonts from D2R Casc Storage, and is the default option now +* read language of D2R from registry if `language` is left empty in D2RMH.ini +* add keyboard/mouse input and delay functions to plugin system +* hide overlay window while game is not running +* (d2mapapi_mod) add support for D2Legacy client v1.11a +* (d2mapapi_mod) add simple BFS path finding + # v0.9.4 * fix support for D2R 1.1.67554 diff --git a/src/d2r/d2rdefs.h b/src/d2r/d2rdefs.h index 0e59e40..0f5833c 100644 --- a/src/d2r/d2rdefs.h +++ b/src/d2r/d2rdefs.h @@ -451,15 +451,15 @@ struct StaticPath { }; struct DrlgAct { - uint64_t unk0[2]; + uint64_t unk0[3]; uint32_t unk1; uint32_t seed; - /* 0x18 DrlgRoom1 *room1 */ + /* 0x20 DrlgRoom1 *room1 */ uint64_t room1Ptr; uint32_t actId; uint32_t unk2; uint64_t unk3[9]; - /* DrlgMisc *misc */ + /* 0x78 DrlgMisc *misc */ uint64_t miscPtr; }; @@ -573,7 +573,8 @@ struct Skill { uint32_t tagets; uint32_t targetType; uint32_t targetId; - uint64_t unk1; + uint32_t unk1; + uint64_t unk2; uint32_t skillLevel; uint32_t levelBonus; uint32_t quantity; @@ -669,22 +670,21 @@ struct VectorData { }; struct GameInfo { - VectorData session; - char unk0[0x18]; + uint64_t unk0[5]; VectorData gameName; - char gameNameBuffer[0x18]; + char gameNameBuffer[0x40]; VectorData gamePass; - char gamePassBuffer[0x18]; + char gamePassBuffer[0x40]; VectorData region; char regionBuffer[0x18]; - uint64_t unk1[31]; - VectorData gameIP; - char gameIPBuffer[0x18]; + uint64_t unk1[32]; + VectorData season; + char seasonBuffer[0x18]; }; struct RosterUnit { char name[16]; - uint64_t unk0; + uint64_t unk0[7]; uint32_t unitId; uint32_t lifePercent; uint32_t kills; @@ -696,17 +696,16 @@ struct RosterUnit { uint32_t posY; uint32_t flags; /* 0x01-normal, 0x02-invited */ uint32_t unk3; - uint64_t pvpInfoPtr; - uint64_t unk4[6]; - uint16_t unk5; - char name2[16]; - uint64_t unk6; - wchar_t wideName[16]; - uint16_t unk7[3]; - uint64_t unk8[8]; - uint32_t order; - uint32_t unk9[3]; - uint64_t nextPtr; + uint64_t pvpInfoPtr; // 70 + uint64_t unk4[6]; // 78 + uint16_t unk5; // A8 + char name2[16]; // AA + uint16_t unk6[3]; // BA + uint64_t unk7[7]; // C0 + uint8_t unk8[7]; // F8 + char utf8name[65]; // FF + uint64_t unk9; // 0x140 + uint64_t nextPtr; // 0x148 }; #pragma pack(pop) diff --git a/src/d2r/processdata.cpp b/src/d2r/processdata.cpp index c1be39b..1ad7a24 100644 --- a/src/d2r/processdata.cpp +++ b/src/d2r/processdata.cpp @@ -81,7 +81,7 @@ void ProcessData::resetData() { gameName.clear(); gamePass.clear(); region.clear(); - gameIP.clear(); + season.clear(); realTombLevelId = 0; superUniqueTombLevelId = 0; @@ -116,6 +116,19 @@ void ProcessData::updateData() { mapItems.clear(); } + /* Address from MapAssist: read obfuscated map seed */ + uint64_t mapSeedPtr; + if (!READ(mapSeedAddr, mapSeedPtr)) { + return; + } + uint32_t seedCheck; + if (!READ(mapSeedPtr + 0x110, seedCheck)) { + return; + } + if (!READ(mapSeedPtr + (seedCheck ? 0x840 : 0x10C0), currSeed)) { + return; + } + readRosters(); readUnitHashTable(hashTableBaseAddr, [this, lastDifficulty, lastSeed, lastAct, lastLevelId](const UnitAny &unit) { if (!unit.unitId || !unit.actPtr || !unit.inventoryPtr) { return; } @@ -132,7 +145,7 @@ void ProcessData::updateData() { READ(unit.unionPtr, player.name); player.levelChanged = false; player.act = act.actId; - player.seed = act.seed; + player.seed = currSeed; READ(act.miscPtr + 0x830, player.difficulty); player.stats.fill(0); readPlayerStats(unit, [&player](uint16_t statId, int32_t value) { @@ -168,8 +181,8 @@ void ProcessData::updateData() { if (!READ(unit.actPtr, act)) { return; } READ(act.miscPtr + 0x830, player.difficulty); player.levelChanged = false; - player.seed = act.seed; - if (lastDifficulty != player.difficulty || lastSeed != act.seed) { + player.seed = currSeed; + if (lastDifficulty != player.difficulty || lastSeed != currSeed) { readGameInfo(); player.levelChanged = true; knownItems.clear(); @@ -266,8 +279,8 @@ inline size_t searchMem(const uint8_t *mem, size_t sz, const uint8_t *search, co void ProcessData::updateOffset() { auto *mem = new(std::nothrow) uint8_t[size_t(baseSize)]; if (mem && READN(baseAddr, mem, uint32_t(baseSize))) { - const uint8_t search0[] = {0x48, 0x8D, 0, 0, 0, 0, 0, 0x8B, 0xD1}; - const uint8_t mask0[] = {0xFF, 0xFF, 0, 0, 0, 0, 0, 0xFF, 0xFF}; + const uint8_t search0[] = {0x48, 0x8D, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x48, 0xC1, 0xE0, 0x0A, 0x48, 0x03, 0xC1, 0xC3, 0xCC}; + const uint8_t mask0[] = {0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; auto off = searchMem(mem, size_t(baseSize), search0, mask0, sizeof(search0)); if (off != size_t(-1)) { int32_t rel; @@ -276,27 +289,23 @@ void ProcessData::updateOffset() { } } - const uint8_t search1[] = {0x41, 0x0F, 0xB6, 0xAC, 0x3F, 0x00, 0x00, 0x00, 0x00}; - const uint8_t mask1[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0}; + const uint8_t search1[] = {0x45, 0x8B, 0xD7, 0x4C, 0x8D, 0x05, 0, 0, 0, 0}; + const uint8_t mask1[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0}; off = searchMem(mem, size_t(baseSize), search1, mask1, sizeof(search1)); if (off != size_t(-1)) { int32_t rel; - if (READ(baseAddr + off + 5, rel)) { - uiBaseAddr = baseAddr + rel; + if (READ(baseAddr + off + 6, rel)) { + uiBaseAddr = baseAddr + off + 10 + rel; } } - const uint8_t search2[] = - {0xC7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x85, 0xC0, 0x0F, 0x84, 0x00, 0x00, 0x00, - 0x00, 0x83, 0x78, 0x5C, 0x00, 0x0F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x33, 0xD2, 0x41}; - const uint8_t mask2[] = - {0xFF, 0xFF, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF}; + const uint8_t search2[] = {0x48, 0x8B, 0x05, 0, 0, 0, 0, 0x48, 0x8B, 0xD9, 0xF3, 0x0F, 0x10, 0x50}; + const uint8_t mask2[] = {0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; off = searchMem(mem, size_t(baseSize), search2, mask2, sizeof(search2)); if (off != size_t(-1)) { int32_t rel; - if (READ(baseAddr + off - 4, rel)) { - isExpansionAddr = baseAddr + off + rel; + if (READ(baseAddr + off + 3, rel)) { + isExpansionAddr = baseAddr + off + 7 + rel; } } @@ -310,15 +319,23 @@ void ProcessData::updateOffset() { } } - const uint8_t search4[] = - {0xE8, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8D, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x44, 0x88, 0x2D, 0x00, 0x00, 0x00, - 0x00}; - const uint8_t mask4[] = {0xFF, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0}; + const uint8_t search4[] = {0x44, 0x88, 0x25, 0, 0, 0, 0, 0x66, 0x44, 0x89, 0x25}; + const uint8_t mask4[] = {0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF}; off = searchMem(mem, size_t(baseSize), search4, mask4, sizeof(search4)); if (off != size_t(-1)) { int32_t rel; - if (READ(baseAddr + off + 8, rel)) { - gameInfoAddr = baseAddr + off - 244 + rel; + if (READ(baseAddr + off + 3, rel)) { + gameInfoAddr = baseAddr + off - 0x121 + rel; + } + } + + const uint8_t search5[] = {0x41, 0x8B, 0xF9, 0x48, 0x8D, 0x0D, 0, 0, 0, 0}; + const uint8_t mask5[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0}; + off = searchMem(mem, size_t(baseSize), search5, mask5, sizeof(search5)); + if (off != size_t(-1)) { + int32_t rel; + if (READ(baseAddr + off + 6, rel)) { + mapSeedAddr = baseAddr + off + 0xEA + rel; } } } @@ -352,7 +369,7 @@ void ProcessData::readRosters() { p.level = mem.level; p.party = mem.partyId; memcpy(p.name, mem.name, 16); - if (/* Battle.Net */ mem.wideName[0] || /* Single-player */ (!mem.posX && mem.unk9[2] == uint32_t(-1))) { + if (/* Battle.Net */ mem.utf8name[0] || /* Single-player */ (!mem.posX && mem.unk9 == uint64_t(-1))) { focusedPlayer = mem.unitId; currPlayer = &p; } else { @@ -429,7 +446,7 @@ void ProcessData::readUnitPlayer(const UnitAny &unit) { READ(unit.unionPtr, player.name); player.levelChanged = false; player.act = act.actId; - player.seed = act.seed; + player.seed = currSeed; READ(act.miscPtr + 0x830, player.difficulty); player.stats.fill(0); readPlayerStats(unit, [&player](uint16_t statId, int32_t value) { @@ -626,12 +643,12 @@ void ProcessData::readGameInfo() { gameName.assign(gameInfo.gameNameBuffer, gameInfo.gameNameBuffer + gameInfo.gameName.size); gamePass.assign(gameInfo.gamePassBuffer, gameInfo.gamePassBuffer + gameInfo.gamePass.size); region.assign(gameInfo.regionBuffer, gameInfo.regionBuffer + gameInfo.region.size); - gameIP.assign(gameInfo.gameIPBuffer, gameInfo.gameIPBuffer + gameInfo.gameIP.size); + season.assign(gameInfo.seasonBuffer, gameInfo.seasonBuffer + gameInfo.season.size); } else { gameName.clear(); gamePass.clear(); region.clear(); - gameIP.clear(); + season.clear(); } } diff --git a/src/d2r/processdata.h b/src/d2r/processdata.h index 96350e3..d583d7a 100644 --- a/src/d2r/processdata.h +++ b/src/d2r/processdata.h @@ -55,6 +55,8 @@ class ProcessData final { uint64_t isExpansionAddr = 0; uint64_t rosterDataAddr = 0; uint64_t gameInfoAddr = 0; + uint64_t mapSeedAddr = 0; + uint32_t currSeed = 0; uint8_t mapEnabled = 0; uint32_t panelEnabled = 0; @@ -62,7 +64,7 @@ class ProcessData final { uint32_t focusedPlayer = 0; const MapPlayer *currPlayer = nullptr; - std::wstring gameName, gamePass, region, gameIP; + std::wstring gameName, gamePass, region, season; uint32_t realTombLevelId = 0; uint32_t superUniqueTombLevelId = 0; diff --git a/src/d2r/processmanager.h b/src/d2r/processmanager.h index e589017..e00fa24 100644 --- a/src/d2r/processmanager.h +++ b/src/d2r/processmanager.h @@ -54,7 +54,7 @@ class ProcessManager final { [[nodiscard]] inline const std::wstring &gameName() const { return currProcess_->gameName; } [[nodiscard]] inline const std::wstring &gamePass() const { return currProcess_->gamePass; } [[nodiscard]] inline const std::wstring ®ion() const { return currProcess_->region; } - [[nodiscard]] inline const std::wstring &gameIP() const { return currProcess_->gameIP; } + [[nodiscard]] inline const std::wstring &season() const { return currProcess_->season; } void reloadConfig() { loadFromCfg(); currProcess_ = nullptr; } diff --git a/src/ui/maprenderer.cpp b/src/ui/maprenderer.cpp index f133fa5..11b9fa8 100644 --- a/src/ui/maprenderer.cpp +++ b/src/ui/maprenderer.cpp @@ -921,8 +921,8 @@ void MapRenderer::updatePanelText() { target += d2rProcess_.gamePass(); } else if (sv == L"region") { target += d2rProcess_.region(); - } else if (sv == L"serverip") { - target += d2rProcess_.gameIP(); + } else if (sv == L"season") { + target += d2rProcess_.season(); } } else { target += pat[pos];