Decompile 6 functions in Source/Client/Mode/GameModePackets.cpp, Source/Client/Session.cpp, ...

Batch #5691 by Yommy: 0 byte-identical, 6 confirmed by peer review.

- fn_21710 CGameMode::ReceiveItem1738EquipList: logic confirmed by peer review
- fn_21711 CGameMode::ReceiveItem1738EquipListOptions: logic confirmed by peer review
- fn_21712 CGameMode::ReceiveItem1738NormalList: logic confirmed by peer review
- fn_21713 CGameMode::ReceiveHomunculusProperty64: logic confirmed by peer review
- fn_21714 CGameMode::ReceiveHomunculusProperty: logic confirmed by peer review
- fn_23515 CSession::RefreshHomunculusInfo: logic confirmed by peer review

Peer-reviewed-by: ragnarok-clanker, willerthal, Millenium, SapitoSucio, Lumen, superintelligence
Accepted without human review: peers confirmed the logic ("same" ahead by at least 3, with 70% of the votes).
This commit is contained in:
ragnarok-clanker 2026-10-30 16:22:37 +00:00
commit dde9e6d72c
6 changed files with 283 additions and 3 deletions

View file

@ -13,6 +13,10 @@
#include "Contents/PlayerNameDictionary.h"
#include "Contents/World/World.h"
#include "Network/TextEncoding.h"
#include "UI/Item/UIItemStoreWnd.h"
#include "UI/Text/UIStaticText.h"
#include "UI/UIBarGraph.h"
#include "UI/Window/UIHomunInfoWnd.h"
#include "UI/Window/UIWindowMgr.h"
#include "Utility/Text/FormatString.h"
@ -30,6 +34,10 @@ struct SessionMessengerContext {
void UpdateRandomStats();
};
struct ClientItemLists {
bool AddItem1738(const ITEM_INFO& item, unsigned char refresh);
};
namespace {
#pragma pack(push, 1)
struct SimulationPacketHeader {
@ -71,8 +79,89 @@ namespace {
/* 000F */ unsigned char unknown_000F[4];
/* 0013 */ char name[1];
};
struct ItemStoreListHeader {
/* 0000 */ std::uint16_t packetType;
/* 0002 */ std::uint16_t length;
/* 0004 */ char title[24];
};
struct StoreItemOption {
/* 0000 */ std::int16_t id;
/* 0002 */ std::int16_t value;
/* 0004 */ unsigned char param;
};
struct StoreEquipRecord {
/* 0000 */ std::int16_t index;
/* 0002 */ int itemId;
/* 0006 */ unsigned char type;
/* 0007 */ std::uint32_t location;
/* 000B */ std::uint32_t wearState;
/* 000F */ unsigned char refine;
/* 0010 */ std::uint32_t cards[4];
/* 0020 */ std::uint32_t expireTime;
/* 0024 */ std::uint16_t bindOnEquip;
/* 0026 */ std::uint16_t sprite;
/* 0028 */ unsigned char flags;
};
struct StoreEquipOptionRecord {
/* 0000 */ std::int16_t index;
/* 0002 */ int itemId;
/* 0006 */ unsigned char type;
/* 0007 */ std::uint32_t location;
/* 000B */ std::uint32_t wearState;
/* 000F */ unsigned char refine;
/* 0010 */ std::uint32_t cards[4];
/* 0020 */ std::uint32_t expireTime;
/* 0024 */ std::uint16_t bindOnEquip;
/* 0026 */ std::uint16_t sprite;
/* 0028 */ unsigned char optionCount;
/* 0029 */ StoreItemOption options[5];
/* 0042 */ unsigned char flags;
};
struct StoreNormalRecord {
/* 0000 */ std::int16_t index;
/* 0002 */ int itemId;
/* 0006 */ unsigned char type;
/* 0007 */ std::int16_t amount;
/* 0009 */ std::uint32_t location;
/* 000D */ std::uint32_t cards[4];
/* 001D */ std::uint32_t expireTime;
/* 0021 */ unsigned char flags;
};
struct HomunculusProperty64Packet {
/* 0000 */ std::uint16_t packetType;
/* 0002 */ std::uint16_t type;
/* 0004 */ std::int64_t value;
};
struct HomunculusPropertyPacket {
/* 0000 */ std::uint16_t packetType;
/* 0002 */ std::uint16_t type;
/* 0004 */ int value;
};
#pragma pack(pop)
static_assert(sizeof(ItemStoreListHeader) == 0x1C);
static_assert(sizeof(StoreEquipRecord) == 0x29);
static_assert(sizeof(StoreEquipOptionRecord) == 0x43);
static_assert(sizeof(StoreNormalRecord) == 0x22);
// INLINED
int CountStoreItemOptions(const StoreItemOption* options) {
int count = 0;
for (int index = 0; index < 5; ++index) {
if (options[index].id != 0) {
++count;
}
}
return count;
}
static_assert(sizeof(SimulationPacketHeader) == 0x24);
static_assert(sizeof(EnchantSimulationRecord) == 0x1C);
static_assert(sizeof(PackageSimulationRecord) == 0x32);
@ -208,6 +297,168 @@ void CGameMode::ReceiveActorSignal(const unsigned char* packet) {
void CGameMode::IgnorePacket(const unsigned char*) {
}
// OFFSET: 0x00D2B710
// FUNCTION: fn_21710
void CGameMode::ReceiveItem1738EquipList(const unsigned char* data) {
const ItemStoreListHeader* packet = reinterpret_cast<const ItemStoreListHeader*>(data);
UIItemStoreWnd* window = static_cast<UIItemStoreWnd*>(g_windowMgr.EnsureWindow(33));
if (packet->title[0] != '\0') {
std::string title(packet->title);
window->title->SetTextFitted(title.c_str());
}
const int count = (packet->length - sizeof(ItemStoreListHeader)) / sizeof(StoreEquipRecord);
const StoreEquipRecord* record = reinterpret_cast<const StoreEquipRecord*>(data + sizeof(ItemStoreListHeader));
for (int i = 0; i < count; ++i, ++record) {
StoreItemOption options[5] = {};
ITEM_INFO item;
item.unknown_0000[0] = record->type;
item.unknown_0000[1] = record->index;
item.unknown_0000[2] = record->location;
item.unknown_0000[3] = record->wearState;
item.quantity = 1;
std::memcpy(item.unknown_001C, record->cards, sizeof(record->cards));
item.SetItemId(record->itemId);
item.identified = record->flags & 1;
item.unknown_005D = (record->flags >> 1) & 1;
item.unknown_0060 = record->refine;
item.unknown_0068 = record->expireTime;
item.unknown_0064 = record->bindOnEquip;
*reinterpret_cast<std::uint32_t*>(item.unknown_0074) = record->sprite;
*reinterpret_cast<unsigned char*>(item.unknown_0078) = (record->flags >> 2) & 1;
item.unknown_0098 = CountStoreItemOptions(options);
for (int index = 0; index < item.unknown_0098; ++index) {
reinterpret_cast<StoreItemOption*>(item.unknown_009C)[index] = options[index];
}
reinterpret_cast<ClientItemLists*>(&g_session)->AddItem1738(item, 1);
}
g_session.RefreshItem1738Window();
}
// OFFSET: 0x00D2BA50
// FUNCTION: fn_21711
void CGameMode::ReceiveItem1738EquipListOptions(const unsigned char* data) {
const ItemStoreListHeader* packet = reinterpret_cast<const ItemStoreListHeader*>(data);
UIItemStoreWnd* window = static_cast<UIItemStoreWnd*>(g_windowMgr.EnsureWindow(33));
if (packet->title[0] != '\0') {
std::string title(packet->title);
window->title->SetTextFitted(title.c_str());
}
const int count = (packet->length - sizeof(ItemStoreListHeader)) / sizeof(StoreEquipOptionRecord);
const StoreEquipOptionRecord* record = reinterpret_cast<const StoreEquipOptionRecord*>(data + sizeof(ItemStoreListHeader));
for (int i = 0; i < count; ++i, ++record) {
StoreItemOption options[5];
std::memcpy(options, record->options, sizeof(options));
ITEM_INFO item;
item.unknown_0000[0] = record->type;
item.unknown_0000[1] = record->index;
item.unknown_0000[2] = record->location;
item.unknown_0000[3] = record->wearState;
item.quantity = 1;
std::memcpy(item.unknown_001C, record->cards, sizeof(record->cards));
item.SetItemId(record->itemId);
item.identified = record->flags & 1;
item.unknown_005D = (record->flags >> 1) & 1;
item.unknown_0060 = record->refine;
item.unknown_0068 = record->expireTime;
item.unknown_0064 = record->bindOnEquip;
*reinterpret_cast<std::uint32_t*>(item.unknown_0074) = record->sprite;
*reinterpret_cast<unsigned char*>(item.unknown_0078) = (record->flags >> 2) & 1;
item.unknown_0098 = CountStoreItemOptions(options);
for (int index = 0; index < item.unknown_0098; ++index) {
reinterpret_cast<StoreItemOption*>(item.unknown_009C)[index] = options[index];
}
reinterpret_cast<ClientItemLists*>(&g_session)->AddItem1738(item, 1);
}
g_session.RefreshItem1738Window();
}
// OFFSET: 0x00D2BDF0
// FUNCTION: fn_21712
void CGameMode::ReceiveItem1738NormalList(const unsigned char* data) {
const ItemStoreListHeader* packet = reinterpret_cast<const ItemStoreListHeader*>(data);
UIItemStoreWnd* window = static_cast<UIItemStoreWnd*>(g_windowMgr.EnsureWindow(33));
if (packet->title[0] != '\0') {
std::string title(packet->title);
window->title->SetTextFitted(title.c_str());
}
const int count = (packet->length - sizeof(ItemStoreListHeader)) / sizeof(StoreNormalRecord);
const StoreNormalRecord* record = reinterpret_cast<const StoreNormalRecord*>(data + sizeof(ItemStoreListHeader));
for (int i = 0; i < count; ++i, ++record) {
ITEM_INFO item;
item.unknown_0000[0] = record->type;
item.unknown_0000[1] = record->index;
item.unknown_0000[3] = record->location;
item.unknown_0000[2] = 0;
item.quantity = record->amount;
std::memcpy(item.unknown_001C, record->cards, sizeof(record->cards));
item.SetItemId(record->itemId);
item.identified = record->flags & 1;
item.unknown_005D = 0;
item.unknown_0060 = 0;
item.unknown_0068 = record->expireTime;
item.unknown_0074[0] = (record->flags >> 1) & 1;
reinterpret_cast<ClientItemLists*>(&g_session)->AddItem1738(item, 0);
}
g_session.RefreshItem1738Window();
}
// OFFSET: 0x00D2C190
// FUNCTION: fn_21713
void CGameMode::ReceiveHomunculusProperty64(const unsigned char* data) {
if (!data) {
return;
}
const HomunculusProperty64Packet* packet = reinterpret_cast<const HomunculusProperty64Packet*>(data);
switch (packet->type) {
case 1:
g_session.m_homunExp = packet->value;
g_session.RefreshHomunculusInfo();
if (UIHomunInfoWnd* window = static_cast<UIHomunInfoWnd*>(g_windowMgr.window_03C0)) {
window->expBar->SetValues(g_session.m_homunExp, g_session.m_homunMaxExp);
}
break;
case 5:
g_session.m_homunHp = static_cast<int>(packet->value);
g_session.RefreshHomunculusInfo();
break;
case 7:
g_session.m_homunSp = static_cast<int>(packet->value);
g_session.RefreshHomunculusInfo();
break;
}
g_session.RefreshHomunculusInfo();
}
// OFFSET: 0x00D2C250
// FUNCTION: fn_21714
void CGameMode::ReceiveHomunculusProperty(const unsigned char* data) {
const HomunculusPropertyPacket* packet = reinterpret_cast<const HomunculusPropertyPacket*>(data);
switch (packet->type) {
case 5:
g_session.m_homunHp = packet->value;
g_session.RefreshHomunculusInfo();
break;
case 7:
g_session.m_homunSp = packet->value;
g_session.RefreshHomunculusInfo();
break;
case 1:
g_session.m_homunExp = packet->value;
g_session.RefreshHomunculusInfo();
break;
}
g_session.RefreshHomunculusInfo();
}
// OFFSET: 0x00D2C2B0
// FUNCTION: fn_21715
void CGameMode::ReceiveHuntingList(const unsigned char* packet) {