From f1bdb5d5df3109d1ecf360d573cad51f5887ec70 Mon Sep 17 00:00:00 2001 From: ragnarok-clanker Date: Tue, 27 Oct 2026 21:06:04 +0000 Subject: [PATCH] Decompile 16 functions in Source/Contents/ClientItemLists.cpp Batch #1154 by eAthena: 0 byte-identical, 16 confirmed by peer review. - fn_23232 ?Decode@EquipSlotDecoder@?A0x7377eef8@@QBE?AUEquipSlots@2@H@Z: logic confirmed by peer review - fn_23284 ClientItemLists::AddItem1768: logic confirmed by peer review - fn_23285 ClientItemLists::AddItem1740: logic confirmed by peer review - fn_23286 ClientItemLists::AddItem1750: logic confirmed by peer review - fn_23287 ClientItemLists::AddItem1758: logic confirmed by peer review - fn_23288 ClientItemLists::AddItem1760: logic confirmed by peer review - fn_23289 ClientItemLists::AddItem1748: logic confirmed by peer review - fn_23290 ClientItemLists::Equip: logic confirmed by peer review - fn_23292 ClientItemLists::AppendItem1718: logic confirmed by peer review - fn_23293 ClientItemLists::MergeItem1718ByName: logic confirmed by peer review - fn_23294 ClientItemLists::MergeItem1720ByName: logic confirmed by peer review - fn_23295 ClientItemLists::MergeItem1720: logic confirmed by peer review - fn_23296 ClientItemLists::AddItem1730: logic confirmed by peer review - fn_23298 ClientItemLists::MergeItem1718: logic confirmed by peer review - fn_23299 ClientItemLists::AddItem1738: logic confirmed by peer review - fn_23300 ?GetEquipSlots@ClientItemLists@@QAE?AUEquipSlots@?A0x7377eef8@@H@Z: logic confirmed by peer review Reviewed-by: ragnarok-clanker Adds to the build: Source/Contents/ClientItemLists.cpp --- Source/Contents/ClientItemLists.cpp | 392 ++++++++++++++++++++++++++++ data/compile.json | 1 + 2 files changed, 393 insertions(+) create mode 100644 Source/Contents/ClientItemLists.cpp diff --git a/Source/Contents/ClientItemLists.cpp b/Source/Contents/ClientItemLists.cpp new file mode 100644 index 0000000..1941464 --- /dev/null +++ b/Source/Contents/ClientItemLists.cpp @@ -0,0 +1,392 @@ +#include "Client/Replay.h" +#include "Contents/Items/ItemInfo.h" +#include "UI/Window/UIWindow.h" +#include "UI/Window/UIWindowMgr.h" + +#include +#include +#include + +namespace { + struct EquipSlots { + /* 0000 */ int values[3]; + }; + + struct EquipSlotDecoder { + EquipSlots Decode(int mask) const; + }; +} + +struct ClientItemLists { + /* 0000 */ unsigned char unknown_0000[0x1710]; + /* 1710 */ std::list inventory; + /* 1718 */ std::list items_1718; + /* 1720 */ std::list items_1720; + /* 1728 */ std::list items_1728; + /* 1730 */ std::list items_1730; + /* 1738 */ std::list items_1738; + /* 1740 */ std::list items_1740; + /* 1748 */ std::list items_1748; + /* 1750 */ std::list items_1750; + /* 1758 */ std::list items_1758; + /* 1760 */ std::list items_1760; + /* 1768 */ std::list items_1768; + /* 1770 */ unsigned char unknown_1770[0xA30]; + /* 21A0 */ ItemInfo equipped[10]; + + bool AddItem1768(const ItemInfo& item, unsigned char alternate, unsigned char refresh); + bool AddItem1740(const ItemInfo& item); + bool AddItem1750(const ItemInfo& item, int type, unsigned char refresh); + void AddItem1758(const ItemInfo& item); + void AddItem1760(const ItemInfo& item); + void AddItem1748(const ItemInfo& item); + bool Equip(const ItemInfo& item); + void AppendItem1718(const ItemInfo& item); + void MergeItem1718ByName(const ItemInfo& item); + void MergeItem1720ByName(const ItemInfo& item); + void MergeItem1720(const ItemInfo& item); + void AddItem1730(const ItemInfo& item); + void MergeItem1718(const ItemInfo& item); + bool AddItem1738(const ItemInfo& item, unsigned char refresh); + EquipSlots GetEquipSlots(int location); +}; + +#if defined(_WIN32) && !defined(_WIN64) +static_assert(offsetof(ClientItemLists, inventory) == 0x1710); +static_assert(offsetof(ClientItemLists, items_1768) == 0x1768); +static_assert(offsetof(ClientItemLists, equipped) == 0x21A0); +#endif + +// OFFSET: 0x00DD43A0 +// FUNCTION: fn_23232 +EquipSlots EquipSlotDecoder::Decode(int mask) const { + std::vector slots; + + for (int index = 0; index < 10; ++index) { + if (mask & 1) { + slots.push_back(index); + } + mask >>= 1; + } + + if (slots.empty()) { + return {-1, -1, -1}; + } + if (slots.size() == 1) { + return {-1, -1, slots[0]}; + } + if (slots.size() == 2) { + return {-1, slots[1], slots[0]}; + } + return {slots[2], slots[1], slots[0]}; +} + +// OFFSET: 0x00DD7210 +// FUNCTION: fn_23284 +bool ClientItemLists::AddItem1768(const ItemInfo& item, unsigned char alternate, unsigned char refresh) { + for (ItemInfo& value : this->items_1768) { + if (value.unknown_0000[1] == item.unknown_0000[1]) { + if (!alternate) { + value.quantity += item.quantity; + if (refresh == 1) { + if (UIWindow* window = reinterpret_cast(g_windowMgr.unknown_02FC)) { + window->SendMsg(nullptr, 23, 0, 0, 0, 0); + } + } + } + return false; + } + } + + this->items_1768.push_back(item); + if (refresh == 1) { + UIWindow* window = reinterpret_cast(alternate ? g_windowMgr.unknown_040C : g_windowMgr.unknown_02FC); + if (window) { + window->SendMsg(nullptr, 23, 0, 0, 0, 0); + } + } + return true; +} + +// OFFSET: 0x00DD72C0 +// FUNCTION: fn_23285 +bool ClientItemLists::AddItem1740(const ItemInfo& item) { + for (ItemInfo& value : this->items_1740) { + if (value.unknown_0000[1] == item.unknown_0000[1]) { + value.quantity += item.quantity; + if (UIWindow* window = reinterpret_cast(g_windowMgr.unknown_02F4)) { + window->SendMsg(nullptr, 23, 0, 0, 0, 0); + } + return false; + } + } + + this->items_1740.push_back(item); + if (UIWindow* window = reinterpret_cast(g_windowMgr.unknown_02F4)) { + window->SendMsg(nullptr, 23, 0, 0, 0, 0); + } + return true; +} + +// OFFSET: 0x00DD7350 +// FUNCTION: fn_23286 +bool ClientItemLists::AddItem1750(const ItemInfo& item, int type, unsigned char refresh) { + for (ItemInfo& value : this->items_1750) { + if (value.unknown_0000[1] == item.unknown_0000[1]) { + value.quantity += item.quantity; + if (refresh == 1) { + std::uint32_t window; + if (type == 1) { + window = g_windowMgr.unknown_0410; + } else { + window = type == 2 ? g_windowMgr.unknown_0420 : g_windowMgr.unknown_02F8; + } + if (window) { + reinterpret_cast(window)->SendMsg(nullptr, 23, 0, 0, 0, 0); + } + } + return false; + } + } + + this->items_1750.push_back(item); + if (refresh == 1) { + std::uint32_t window; + if (type == 1) { + window = g_windowMgr.unknown_0410; + } else { + window = type == 2 ? g_windowMgr.unknown_0420 : g_windowMgr.unknown_02F8; + } + if (window) { + reinterpret_cast(window)->SendMsg(nullptr, 23, 0, 0, 0, 0); + } + } + return true; +} + +// OFFSET: 0x00DD7420 +// FUNCTION: fn_23287 +void ClientItemLists::AddItem1758(const ItemInfo& item) { + for (ItemInfo& value : this->items_1758) { + if (value.unknown_0000[1] == item.unknown_0000[1]) { + value.quantity += item.quantity; + return; + } + } + + this->items_1758.push_back(item); +} + +// OFFSET: 0x00DD7470 +// FUNCTION: fn_23288 +void ClientItemLists::AddItem1760(const ItemInfo& item) { + for (ItemInfo& value : this->items_1760) { + if (value.unknown_0000[1] == item.unknown_0000[1]) { + value.quantity += item.quantity; + return; + } + } + + this->items_1760.push_back(item); +} + +// OFFSET: 0x00DD74C0 +// FUNCTION: fn_23289 +void ClientItemLists::AddItem1748(const ItemInfo& item) { + for (ItemInfo& value : this->items_1748) { + if (value.unknown_0000[1] == item.unknown_0000[1]) { + value.quantity += item.quantity; + return; + } + } + + this->items_1748.push_back(item); +} + +// OFFSET: 0x00DD7510 +// FUNCTION: fn_23290 +bool ClientItemLists::Equip(const ItemInfo& item) { + const EquipSlots slots = this->GetEquipSlots(item.unknown_0000[3]); + const int order[3] = {slots.values[2], slots.values[1], slots.values[0]}; + + for (int index = 0; index < 3; ++index) { + const int slot = order[index]; + if (slot != -1) { + ItemInfo& equipped = this->equipped[slot]; + equipped = item; + equipped.quantity = 1; + equipped.unknown_0068 = item.unknown_0068; + equipped.unknown_0064 = item.unknown_0064; + equipped.unknown_0070 = item.unknown_0070; + } + } + + return true; +} + +// OFFSET: 0x00DD76C0 +// FUNCTION: fn_23292 +void ClientItemLists::AppendItem1718(const ItemInfo& item) { + this->items_1718.push_back(item); +} + +// OFFSET: 0x00DD76D0 +// FUNCTION: fn_23293 +void ClientItemLists::MergeItem1718ByName(const ItemInfo& item) { + for (ItemInfo& value : this->items_1718) { + if (value.itemId == item.itemId) { + value.quantity = value.quantity == -1 ? -1 : value.quantity + item.quantity; + return; + } + } + + this->items_1718.push_back(item); +} + +// OFFSET: 0x00DD7770 +// FUNCTION: fn_23294 +void ClientItemLists::MergeItem1720ByName(const ItemInfo& item) { + for (ItemInfo& value : this->items_1720) { + if (value.itemId == item.itemId) { + value.quantity += item.quantity; + return; + } + } + + this->items_1720.push_back(item); +} + +// OFFSET: 0x00DD77F0 +// FUNCTION: fn_23295 +void ClientItemLists::MergeItem1720(const ItemInfo& item) { + for (ItemInfo& value : this->items_1720) { + const bool same = item.unknown_0074[3] ? value.unknown_0000[1] == item.unknown_0000[1] : value.itemId == item.itemId; + if (same) { + value.quantity += item.quantity; + return; + } + } + + this->items_1720.push_back(item); +} + +// OFFSET: 0x00DD7890 +// FUNCTION: fn_23296 +void ClientItemLists::AddItem1730(const ItemInfo& item) { + const ItemInfo owned = LookupClientItemByIdentity(this->inventory, item.unknown_0000[1]); + if (owned.unknown_0000[1] > 0) { + for (ItemInfo& value : this->items_1730) { + if (value.unknown_0000[1] == item.unknown_0000[1]) { + const int quantity = value.quantity + item.quantity; + if (owned.quantity >= quantity) { + value.quantity = quantity; + } + return; + } + } + + if (owned.quantity >= item.quantity) { + this->items_1730.push_back(item); + } + } +} + +// OFFSET: 0x00DD7B30 +// FUNCTION: fn_23298 +void ClientItemLists::MergeItem1718(const ItemInfo& item) { + for (ItemInfo& value : this->items_1718) { + if (value.unknown_0000[1] == item.unknown_0000[1]) { + if (value.quantity != -1) { + value.quantity += item.quantity; + } + return; + } + } + + this->items_1718.push_back(item); +} + +// OFFSET: 0x00DD7B80 +// FUNCTION: fn_23299 +bool ClientItemLists::AddItem1738(const ItemInfo& item, unsigned char refresh) { + if (g_replayAnimationScaling && !Replay::Instance().header.options.Get(ReplayRecordOption::Item)) { + return true; + } + + for (ItemInfo& value : this->items_1738) { + if (value.unknown_0000[1] == item.unknown_0000[1]) { + value.quantity += item.quantity; + if (refresh && g_windowMgr.window_0288) { + g_windowMgr.window_0288->SendMsg(nullptr, 23, 0, 0, 0, 0); + } + return false; + } + } + + this->items_1738.push_back(item); + if (refresh && g_windowMgr.window_0288) { + g_windowMgr.window_0288->SendMsg(nullptr, 23, 0, 0, 0, 0); + } + return true; +} + +// OFFSET: 0x00DD7E10 +// FUNCTION: fn_23300 +EquipSlots ClientItemLists::GetEquipSlots(int location) { + int mask = location; + switch (location) { + case 0x8000: { + EquipSlots result; + result.values[0] = -1; + result.values[1] = -1; + result.values[2] = 15; + return result; + } + case 0x400: + mask = 0x100; + break; + case 0x800: + mask = 0x200; + break; + case 0xC00: + mask = 0x300; + break; + case 0x1000: + mask = 1; + break; + case 0x1400: + mask = 0x101; + break; + case 0x1800: + mask = 0x201; + break; + case 0x1C00: + mask = 0x301; + break; + case 0x2000: + mask = 4; + break; + case 0x4000: + case 0x20000: + mask = 2; + break; + case 0x10000: + mask = 0x10; + break; + case 0x40000: + mask = 0x20; + break; + case 0x80000: + mask = 0x40; + break; + case 0x100000: + mask = 8; + break; + case 0x200000: + mask = 0x80; + break; + } + + const EquipSlots slots = EquipSlotDecoder().Decode(mask); + return slots; +} diff --git a/data/compile.json b/data/compile.json index 71b346b..5793530 100644 --- a/data/compile.json +++ b/data/compile.json @@ -281,6 +281,7 @@ "Source/Contents/Camera/CameraShake.cpp": 3, "Source/Contents/Camera/CameraSmoothing.cpp": 3, "Source/Contents/CashEmotionMgr.cpp": 4, + "Source/Contents/ClientItemLists.cpp": 4, "Source/Contents/Configuration/OptionWriter.cpp": 4, "Source/Contents/Configuration/ScriptOptions.cpp": 4, "Source/Contents/Configuration/ServiceConfiguration.cpp": 4,