From 42741242d1ce423ed7c7025d5de54f120bbe9da6 Mon Sep 17 00:00:00 2001 From: Dargor Date: Wed, 2 Sep 2026 22:08:58 -0300 Subject: [PATCH 1/7] menushop: decompiled five menushop functions - decompiled five menushop functions: func_801E5D28 func_801E5DBC func_801E6ACC func_801E6EB0 func_801E791C - refactored three functions in order to use MenuDisplayConfig struct func_801E6F60 func_801E8B60 func_801E9554 - created menushop header file for struct, variable and function declaration --- include/menushop.h | 31 +++++ src/menu/menushop/menushop.c | 219 +++++++++++++++++++++++++++-------- 2 files changed, 203 insertions(+), 47 deletions(-) create mode 100644 include/menushop.h diff --git a/include/menushop.h b/include/menushop.h new file mode 100644 index 00000000..7a320244 --- /dev/null +++ b/include/menushop.h @@ -0,0 +1,31 @@ +#ifndef MENUSHOP_H +#define MENUSHOP_H + +typedef struct { + u8 unk0; + u8 unk1; +} Struct_801E5DBC; + +typedef struct { + u16 unk0; + u8 unk2; +} Struct_801E6ACC; + +extern u8 D_80077EBC[]; +extern u8 D_801EA3F0[]; +extern u8 D_801EA70C[]; +extern u8 D_801F7F98[]; +extern u8 D_801E9B64[]; +extern u8 D_801E9B6C[]; +extern u8 D_801EA170[]; +extern u8 D_801EAA28[]; +extern s32 D_801EAA48[]; +extern s32 D_801EAD68[]; +extern u8 D_801EB088[]; +extern s32 g_menuColor; +extern s32 D_80077E70; +extern s32 func_801E8AB0; +extern s32 func_801E5E90(s32); +extern s32 func_801E79D4; + +#endif /* MENUSHOP_H */ diff --git a/src/menu/menushop/menushop.c b/src/menu/menushop/menushop.c index c497e4c3..ce04af71 100644 --- a/src/menu/menushop/menushop.c +++ b/src/menu/menushop/menushop.c @@ -1,14 +1,7 @@ #include "common.h" - -extern u8 D_801EA70C[]; -extern u8 D_801F7F98[]; -extern u8 D_801E9B64[]; -extern u8 D_801E9B6C[]; -extern u8 g_menuDisplayCfg[]; -extern s32 g_menuColor; -extern s32 D_80077E70; -extern s32 func_801E6EB0; -extern s32 func_801E8AB0; +#include "gamestate.h" +#include "menu.h" +#include "menushop.h" /** * @brief Look up a shop item byte from a table or item data. @@ -97,9 +90,75 @@ void func_801E5BA4(s32 a0, s32 a1) { INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E5C08); -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E5D28); +s32 func_801E5D28(void) { + s32 result; + u8* ptr1; + u8* ptr2; + s32 i; + s32 val1; + s32 val2; + + if (g_gameState.mainData.partyLockFlag & 1) { + result = g_gameState.mainData.party.dreamGil; + } else { + result = g_gameState.mainData.party.gil; + } + + ptr1 = D_80077EBC; + i = 0xC7; + ptr2 = D_801EB088; + ptr2 += 0xC7; + + for (; i >= 0; i--, ptr2--) { + *ptr2 = 0; + } + + for (i = 0; i < 0xC6; i++) { + val1 = *ptr1; + ptr1++; + + val2 = *ptr1; + ptr1++; + + if (val1 != 0) { + D_801EB088[val1] = val2; + } + } + + return result; +} + +void func_801E5DBC(void) { + u8 sp[16]; + s32 counter; + s32 i; + Struct_801E5DBC *ptr; + + ptr = (Struct_801E5DBC *)D_801EAA28; + counter = 0; + + for (i = 0 ; i < 16; i++, ptr++) { + if (ptr->unk0 != 0) { + if (ptr->unk1 != 0) { + sp[counter] = i; + counter++; + } + } + } + + ptr = (Struct_801E5DBC *)D_801EAA28; + + for (i = 0; i < counter; i++) { + if (i != sp[i]) { + ptr[i] = ptr[sp[i]]; + } + } -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E5DBC); + for (; i < 16; i++) { + ptr[i].unk0 = 0; + ptr[i].unk1 = 0; + } +} void func_801E5E88(void) { } @@ -108,7 +167,42 @@ INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E5E90); INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E6A68); -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E6ACC); +void func_801E6ACC(void) { + Struct_801E6ACC *ptr; + s32 result; + s32 i; + s32 x; + s32 y; + + result = func_801F72B4(); + ptr = (Struct_801E6ACC *)D_801EA3F0; + + for (i = 0; i < 200; i++) { + if (result & 1) { + D_801EAD68[i] = (ptr[i].unk0 * 15) / 2; + } else { + D_801EAD68[i] = ptr[i].unk0 * 10; + } + + if (result & 2) { + x = ptr[i].unk2 * 10; + y = ptr[i].unk0 * x; + D_801EAA48[i] = y * 3 / 40; + } else { + x = ptr[i].unk2 * 10; + y = ptr[i].unk0 * x; + D_801EAA48[i] = y / 20; + } + + if (D_801EAA48[i] == 0) { + D_801EAA48[i] = 1; + } + if (D_801EAD68[i] == 0) { + D_801EAD68[i] = 1; + } + } +} + INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E6C3C); @@ -116,7 +210,25 @@ INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E6D54); INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E6E0C); -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E6EB0); +s32 func_801E6EB0(s32 arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { + u8 buffer[0x80]; + s32 msg; + s32 temp_s0; + s32 temp_s1; + s32 temp_v0; + s32 result; + + msg = ((s32 *)(g_menuDisplayCfg.dataPtr))[arg2]; + result = arg1; + if (msg != 0) { + temp_v0 = (arg4 + 0xA); + temp_s0 = g_menuDisplayCfg.x + temp_v0; + temp_s1 = g_menuDisplayCfg.y + 5; + decodeMessage(msg, buffer, -1); + result = func_801F0FEC(arg0, arg1, temp_s0, temp_s1, buffer, 7); + } + return result; +} /** * @brief Configure display parameters and invoke callback for shop list rendering. @@ -133,18 +245,17 @@ INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E6EB0); * @param arg5 X position for the display configuration. */ void func_801E6F60(u8 *a0, s32 a1, s32 a2, s32 a3, s32 arg5) { - s32 cfg = (s32)g_menuDisplayCfg; - *(u8 *)(cfg + 0x10) = 0; - *(u8 *)(cfg + 0x11) = 0; - *(s16 *)&g_menuDisplayCfg[0] = a3; - *(s16 *)(cfg + 0x04) = 0x144; - *(s16 *)(cfg + 0x06) = 0x14; - *(u8 *)(cfg + 0x13) = 1; - *(u8 *)(cfg + 0x16) = 0; - *(u8 *)(cfg + 0x17) = 1; - *(s16 *)(cfg + 0x02) = arg5; - *(s16 *)(cfg + 0x14) = *(u16 *)(a0 + 0x3A); - *(s32 *)(cfg + 0x20) = (s32)(a0 + 0x20); + g_menuDisplayCfg.iconType = 0; + g_menuDisplayCfg.iconSubType = 0; + g_menuDisplayCfg.x = a3; + g_menuDisplayCfg.w = 0x144; + g_menuDisplayCfg.h = 0x14; + g_menuDisplayCfg.columnCount = 1; + g_menuDisplayCfg.pageStart = 0; + g_menuDisplayCfg.pageEnd = 1; + g_menuDisplayCfg.y = arg5; + g_menuDisplayCfg.scrollOffset = *(u16 *)(a0 + 0x3A); + g_menuDisplayCfg.dataPtr = (s32)(a0 + 0x20); { func_801EFBB4(a1, a2, (s32)&func_801E6EB0); } @@ -162,7 +273,24 @@ INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E7628); INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E77EC); -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E791C); +void func_801E791C(s32 arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { + s32 var_v0; + MenuDisplayConfig *cfg; + + cfg = &g_menuDisplayCfg; + + var_v0 = arg2; + if (arg0 != 0) { + var_v0 = func_801F0FEC(arg1, var_v0, arg3 + 0xC, arg4 + 5, arg0, 7); + } + cfg->iconType = 0; + cfg->iconSubType = 0; + cfg->x = arg3; + cfg->w = 0xDA; + cfg->y = (s16) arg4; + cfg->h = 0x15; + func_801EF9AC(arg1, var_v0, 0x1000, g_menuColor); +} INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E79D4); @@ -266,18 +394,17 @@ INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E8AB0); * @param arg5 X position for the display configuration. */ void func_801E8B60(u8 *a0, s32 a1, s32 a2, s32 a3, s32 arg5) { - s32 cfg = (s32)g_menuDisplayCfg; - *(u8 *)(cfg + 0x10) = 0; - *(u8 *)(cfg + 0x11) = 0; - *(s16 *)&g_menuDisplayCfg[0] = a3; - *(s16 *)(cfg + 0x04) = 0x144; - *(s16 *)(cfg + 0x06) = 0x14; - *(u8 *)(cfg + 0x13) = 1; - *(u8 *)(cfg + 0x16) = 0; - *(u8 *)(cfg + 0x17) = 1; - *(s16 *)(cfg + 0x02) = arg5; - *(s16 *)(cfg + 0x14) = *(u16 *)(a0 + 0x36); - *(s32 *)(cfg + 0x20) = (s32)(a0 + 0x20); + g_menuDisplayCfg.iconType = 0; + g_menuDisplayCfg.iconSubType = 0; + g_menuDisplayCfg.x = a3; + g_menuDisplayCfg.w = 0x144; + g_menuDisplayCfg.h = 0x14; + g_menuDisplayCfg.columnCount = 1; + g_menuDisplayCfg.pageStart = 0; + g_menuDisplayCfg.pageEnd = 1; + g_menuDisplayCfg.y = arg5; + g_menuDisplayCfg.scrollOffset = *(u16 *)(a0 + 0x36); + g_menuDisplayCfg.dataPtr = (s32)(a0 + 0x20); { func_801EFBB4(a1, a2, (s32)&func_801E8AB0); } @@ -326,16 +453,14 @@ INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E90F8); */ void func_801E9554(s32 a0, s32 a1, s32 a2, s32 a3, s32 arg4) { s32 result; - u8 *cfg; result = func_801E90F8(a0, a1, a2, a3, arg4); - cfg = g_menuDisplayCfg; - *(u8 *)(cfg + 0x10) = 0x57; - *(u8 *)(cfg + 0x11) = 0; - *(s16 *)&g_menuDisplayCfg[0] = a3; - *(s16 *)(cfg + 0x04) = 0x150; - *(s16 *)(cfg + 0x02) = arg4; - *(s16 *)(cfg + 0x06) = 0x26; + g_menuDisplayCfg.iconType = 0x57; + g_menuDisplayCfg.iconSubType = 0; + g_menuDisplayCfg.x = a3; + g_menuDisplayCfg.w = 0x150; + g_menuDisplayCfg.y = arg4; + g_menuDisplayCfg.h = 0x26; func_801EF9AC(a1, result, 0x1000, g_menuColor); } From 69869be0344fab35bdda0de156a69e9c0079face Mon Sep 17 00:00:00 2001 From: Dargor Date: Thu, 3 Sep 2026 13:45:05 -0300 Subject: [PATCH 2/7] menushop: refactor two functions - func_801E5D28: change D_801EB088 from a u8 array to a struct - func_801E8B60: change the first argument from a u8 array to a struct --- include/menushop.h | 13 ++++++++++++- src/menu/menushop/menushop.c | 15 ++++++--------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/include/menushop.h b/include/menushop.h index 7a320244..baac0759 100644 --- a/include/menushop.h +++ b/include/menushop.h @@ -11,6 +11,17 @@ typedef struct { u8 unk2; } Struct_801E6ACC; +typedef struct { + u8 unk0[0xC7]; +} Struct_D_801EB088; + +typedef struct { + u8 pad000[0x20]; + s32 *unk20; + u8 pad024[0x12]; + u16 unk36; +} Struct_801E8B60; + extern u8 D_80077EBC[]; extern u8 D_801EA3F0[]; extern u8 D_801EA70C[]; @@ -21,7 +32,7 @@ extern u8 D_801EA170[]; extern u8 D_801EAA28[]; extern s32 D_801EAA48[]; extern s32 D_801EAD68[]; -extern u8 D_801EB088[]; +extern Struct_D_801EB088 D_801EB088; extern s32 g_menuColor; extern s32 D_80077E70; extern s32 func_801E8AB0; diff --git a/src/menu/menushop/menushop.c b/src/menu/menushop/menushop.c index ce04af71..f7c30e47 100644 --- a/src/menu/menushop/menushop.c +++ b/src/menu/menushop/menushop.c @@ -105,12 +105,9 @@ s32 func_801E5D28(void) { } ptr1 = D_80077EBC; - i = 0xC7; - ptr2 = D_801EB088; - ptr2 += 0xC7; - for (; i >= 0; i--, ptr2--) { - *ptr2 = 0; + for (i = 0; i < 0xC8; i++) { + D_801EB088.unk0[i] = 0; } for (i = 0; i < 0xC6; i++) { @@ -121,7 +118,7 @@ s32 func_801E5D28(void) { ptr1++; if (val1 != 0) { - D_801EB088[val1] = val2; + D_801EB088.unk0[val1] = val2; } } @@ -393,7 +390,7 @@ INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E8AB0); * @param a3 Y position for the display configuration. * @param arg5 X position for the display configuration. */ -void func_801E8B60(u8 *a0, s32 a1, s32 a2, s32 a3, s32 arg5) { +void func_801E8B60(Struct_801E8B60 *a0, s32 a1, s32 a2, s32 a3, s32 arg5) { g_menuDisplayCfg.iconType = 0; g_menuDisplayCfg.iconSubType = 0; g_menuDisplayCfg.x = a3; @@ -403,8 +400,8 @@ void func_801E8B60(u8 *a0, s32 a1, s32 a2, s32 a3, s32 arg5) { g_menuDisplayCfg.pageStart = 0; g_menuDisplayCfg.pageEnd = 1; g_menuDisplayCfg.y = arg5; - g_menuDisplayCfg.scrollOffset = *(u16 *)(a0 + 0x36); - g_menuDisplayCfg.dataPtr = (s32)(a0 + 0x20); + g_menuDisplayCfg.scrollOffset = a0->unk36; + g_menuDisplayCfg.dataPtr = &a0->unk20; { func_801EFBB4(a1, a2, (s32)&func_801E8AB0); } From 4648b8dbd67d7607a32ea0ef6ff1bb84328b8dd3 Mon Sep 17 00:00:00 2001 From: Dargor Date: Thu, 3 Sep 2026 22:25:45 -0300 Subject: [PATCH 3/7] menushop: decomp func_801E7B9C --- include/menumain.h | 11 ++++++++++- include/menushop.h | 4 ++-- src/menu/menushop/menushop.c | 22 +++++++++++++++++++++- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/include/menumain.h b/include/menumain.h index b98d3151..147ac00c 100644 --- a/include/menumain.h +++ b/include/menumain.h @@ -56,7 +56,16 @@ typedef struct MenuTask { s32 (*drawCb)(struct MenuTask *, s32, s32); /* 0x0C: (task, ctx, dl) -> dl (func_801F16AC). */ u16 state; /* 0x10: per-task state word, zeroed on alloc. */ s8 inUse; /* 0x12: pool slot occupied. */ - u8 pad13[0x65]; + u8 pad13[0x15]; + u32 unk28; + u8 *unk2C; + u32 unk30; + u16 pad34; + u16 unk36; + u16 unk38; + u8 pad3A[0xB]; + u8 unk45; + u8 pad46[0x32]; } MenuTask; /* 0x78 bytes */ /** diff --git a/include/menushop.h b/include/menushop.h index baac0759..1cfc120a 100644 --- a/include/menushop.h +++ b/include/menushop.h @@ -36,7 +36,7 @@ extern Struct_D_801EB088 D_801EB088; extern s32 g_menuColor; extern s32 D_80077E70; extern s32 func_801E8AB0; -extern s32 func_801E5E90(s32); -extern s32 func_801E79D4; +extern void func_801E5E90(void*); +extern s32 func_801E79D4(void*, s32, s32); #endif /* MENUSHOP_H */ diff --git a/src/menu/menushop/menushop.c b/src/menu/menushop/menushop.c index f7c30e47..233366b8 100644 --- a/src/menu/menushop/menushop.c +++ b/src/menu/menushop/menushop.c @@ -2,6 +2,7 @@ #include "gamestate.h" #include "menu.h" #include "menushop.h" +#include "menumain.h" /** * @brief Look up a shop item byte from a table or item data. @@ -291,7 +292,26 @@ void func_801E791C(s32 arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E79D4); -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E7B9C); +void func_801E7B9C(s32 a0) { + MenuTask *temp_s0; + + temp_s0 = (MenuTask *)func_801F179C((s32)func_801E5E90, (s32)func_801E79D4); + func_801F1D2C(0, (s32)"shop.bin", (s32)D_801EA170); + func_801F1D2C(0, (s32)"price.bin", (s32)D_801EA3F0); + func_801F1D2C(0, (s32)"mitem.bin", (s32)D_801EA70C); + if (temp_s0 != NULL) { + temp_s0->unk2C = D_80077EBC; + temp_s0->unk36 = 0x1000; + temp_s0->unk30 = 0; + temp_s0->unk28 = func_801E5D28(); + temp_s0->unk45 = D_801E9B6C[func_801EFFF0()]; + func_801E6D54(temp_s0->unk45); + func_801E5E90(temp_s0); + if (func_801EFFB8() == 0x17) { + func_801F1D84(); + } + } +} /** * @brief Process shop state and dispatch to appropriate handler. From d13fd0a07869e3413086d089fc782d01086294b8 Mon Sep 17 00:00:00 2001 From: Dargor Date: Fri, 4 Sep 2026 22:08:18 -0300 Subject: [PATCH 4/7] menushop: decomp and refactors - Decompiled func_801E6A68 - Renamed some structs and their attributes to be more descriptive - Added constants for the number of items in the shop, item prices, and item slots --- include/menushop.h | 35 +++++++++----- src/menu/menushop/menushop.c | 90 ++++++++++++++++++++++++++---------- 2 files changed, 88 insertions(+), 37 deletions(-) diff --git a/include/menushop.h b/include/menushop.h index 1cfc120a..fc668e34 100644 --- a/include/menushop.h +++ b/include/menushop.h @@ -1,15 +1,25 @@ #ifndef MENUSHOP_H #define MENUSHOP_H +#define SHOP_COUNT 20 +#define SHOP_ITEM_COUNT 16 +#define ITEM_PRICE_COUNT 200 +#define ITEM_SLOT_COUNT 198 + +typedef struct { + u8 itemId; /**< 0x0: item ID. */ + u8 rarity; /**< 0x1: item rarity (0x00 = Rare, 0xFF = Common). */ +} ShopItemRarity; /* 0x2 = 2 bytes */ + typedef struct { - u8 unk0; - u8 unk1; -} Struct_801E5DBC; + u8 itemId; /**< 0x0: item ID. */ + u8 visible; /**< 0x1: item visibility flag. */ +} ShopItemVisibility; /* 0x2 = 2 bytes */ typedef struct { - u16 unk0; - u8 unk2; -} Struct_801E6ACC; + u16 basePrice; /**< 0x0: base price. */ + u8 sellRate; /**< 0x2: sell rate. */ +} ShopItemPrice; /* 0x3 = 3 bytes */ typedef struct { u8 unk0[0xC7]; @@ -22,16 +32,17 @@ typedef struct { u16 unk36; } Struct_801E8B60; -extern u8 D_80077EBC[]; -extern u8 D_801EA3F0[]; +extern ShopData D_80077CC8[SHOP_COUNT]; /**< Shop data table. */ +extern u8 D_80077EBC[ITEM_SLOT_COUNT]; /**< Item slot inventory. */ +extern ShopItemPrice D_801EA3F0[ITEM_PRICE_COUNT]; /**< Item price table. */ extern u8 D_801EA70C[]; extern u8 D_801F7F98[]; extern u8 D_801E9B64[]; extern u8 D_801E9B6C[]; -extern u8 D_801EA170[]; -extern u8 D_801EAA28[]; -extern s32 D_801EAA48[]; -extern s32 D_801EAD68[]; +extern ShopItemRarity D_801EA170[SHOP_COUNT][SHOP_ITEM_COUNT]; /**< Shop item rarity tables. */ +extern ShopItemVisibility D_801EAA28[SHOP_ITEM_COUNT]; /**< Shop item visibility table. */ +extern s32 D_801EAA48[ITEM_PRICE_COUNT]; /**< Item sell prices. */ +extern s32 D_801EAD68[ITEM_PRICE_COUNT]; /**< Item buy prices. */ extern Struct_D_801EB088 D_801EB088; extern s32 g_menuColor; extern s32 D_80077E70; diff --git a/src/menu/menushop/menushop.c b/src/menu/menushop/menushop.c index 233366b8..6723752c 100644 --- a/src/menu/menushop/menushop.c +++ b/src/menu/menushop/menushop.c @@ -107,11 +107,11 @@ s32 func_801E5D28(void) { ptr1 = D_80077EBC; - for (i = 0; i < 0xC8; i++) { + for (i = 0; i < ITEM_PRICE_COUNT; i++) { D_801EB088.unk0[i] = 0; } - for (i = 0; i < 0xC6; i++) { + for (i = 0; i < ITEM_SLOT_COUNT; i++) { val1 = *ptr1; ptr1++; @@ -126,25 +126,31 @@ s32 func_801E5D28(void) { return result; } +/** + * @brief Compacts the shop item list in place. + * + * Scans the 16 entries in D_801EAA28 and keeps only entries whose + * itemId and visible fields are both non-zero. Valid entries are moved to + * the beginning of the table in their original order, while all + * remaining entries are cleared. + */ void func_801E5DBC(void) { - u8 sp[16]; + u8 sp[SHOP_ITEM_COUNT]; s32 counter; s32 i; - Struct_801E5DBC *ptr; + ShopItemVisibility *ptr; - ptr = (Struct_801E5DBC *)D_801EAA28; + ptr = D_801EAA28; counter = 0; - for (i = 0 ; i < 16; i++, ptr++) { - if (ptr->unk0 != 0) { - if (ptr->unk1 != 0) { - sp[counter] = i; - counter++; - } + for (i = 0 ; i < SHOP_ITEM_COUNT; i++, ptr++) { + if (ptr->itemId != 0 && ptr->visible != 0) { + sp[counter] = i; + counter++; } } - ptr = (Struct_801E5DBC *)D_801EAA28; + ptr = D_801EAA28; for (i = 0; i < counter; i++) { if (i != sp[i]) { @@ -152,9 +158,9 @@ void func_801E5DBC(void) { } } - for (; i < 16; i++) { - ptr[i].unk0 = 0; - ptr[i].unk1 = 0; + for (; i < SHOP_ITEM_COUNT; i++) { + ptr[i].itemId = 0; + ptr[i].visible = 0; } } @@ -163,32 +169,66 @@ void func_801E5E88(void) { INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E5E90); -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E6A68); +/** + * @brief Populate shop item visibility data for a shop. + * + * Copies item IDs from the shop's rarity table and visibility flags from + * the shop inventory into the shared visibility table. + */ +void func_801E6A68(s32 shopId) { + ShopItemVisibility *visibility; + ShopItemRarity *rarity; + ShopData *shop; + s32 i; + + visibility = D_801EAA28; + rarity = D_801EA170[0]; + rarity += shopId * 16; + + shop = &D_80077CC8[shopId]; + + for (i = 0; i < SHOP_ITEM_COUNT; i++) { + visibility->itemId = rarity[i].itemId; + visibility->visible = shop->items[i]; + visibility++; + } +} + +/** + * @brief Calculates and stores shop buy and sell prices. + * + * The flags returned by func_801F72B4 determine the price modifiers: + * - bit 0 reduces the buy price by 25%. + * - bit 1 increases the sell price by 50%. + * Buy prices are stored in D_801EAD68, and sell prices in D_801EAA48. + * Prices are calculated using integer arithmetic and are clamped to a + * minimum value of 1. + */ void func_801E6ACC(void) { - Struct_801E6ACC *ptr; + ShopItemPrice *itemPrice; s32 result; s32 i; s32 x; s32 y; result = func_801F72B4(); - ptr = (Struct_801E6ACC *)D_801EA3F0; + itemPrice = D_801EA3F0; - for (i = 0; i < 200; i++) { + for (i = 0; i < ITEM_PRICE_COUNT; i++) { if (result & 1) { - D_801EAD68[i] = (ptr[i].unk0 * 15) / 2; + D_801EAD68[i] = (itemPrice[i].basePrice * 15) / 2; } else { - D_801EAD68[i] = ptr[i].unk0 * 10; + D_801EAD68[i] = itemPrice[i].basePrice * 10; } if (result & 2) { - x = ptr[i].unk2 * 10; - y = ptr[i].unk0 * x; + x = itemPrice[i].sellRate * 10; + y = itemPrice[i].basePrice * x; D_801EAA48[i] = y * 3 / 40; } else { - x = ptr[i].unk2 * 10; - y = ptr[i].unk0 * x; + x = itemPrice[i].sellRate * 10; + y = itemPrice[i].basePrice * x; D_801EAA48[i] = y / 20; } From a782fb5da804b4e1b0c96e1417a376ae987a7037 Mon Sep 17 00:00:00 2001 From: Dargor Date: Sat, 5 Sep 2026 23:34:57 -0300 Subject: [PATCH 5/7] itemshop: five functions decompiled - func_801E5C08 - func_801E6C3C - func_801E6E0C - func_801E7374 - func_801E7508 --- include/menumain.h | 2 +- include/menushop.h | 30 ++++-- src/menu/menumain/menumain.c | 2 +- src/menu/menushop/menushop.c | 188 +++++++++++++++++++++++++++++++++-- 4 files changed, 203 insertions(+), 19 deletions(-) diff --git a/include/menumain.h b/include/menumain.h index 147ac00c..79f2e4cd 100644 --- a/include/menumain.h +++ b/include/menumain.h @@ -30,7 +30,7 @@ DR_AREA *func_801EF800(P_TAG *, DR_AREA *, u8 *); /**< Link a custom-rect DR_AREA prim. */ DR_AREA *func_801EF8D8(P_TAG *, DR_AREA *); /**< Link a clip-restore DR_AREA prim. */ s32 func_801EF9AC(s32, s32, s32, s32); /**< Panel/window render helper. */ -void func_801EFBB4(s32, s32, s32); /**< Panel/window render helper. */ +s32 func_801EFBB4(s32, s32, s32); /**< Panel/window render helper. */ s32 func_801F179C(s32, s32); /**< Allocate a menu state task (tickCb, drawCb). */ s32 func_801F6AD0(s32); /**< Party/character query helper. */ void func_801F7B60(void); /**< Menu teardown/exit helper. */ diff --git a/include/menushop.h b/include/menushop.h index fc668e34..e322166e 100644 --- a/include/menushop.h +++ b/include/menushop.h @@ -1,7 +1,8 @@ #ifndef MENUSHOP_H #define MENUSHOP_H -#define SHOP_COUNT 20 +#include "gamestate.h" + #define SHOP_ITEM_COUNT 16 #define ITEM_PRICE_COUNT 200 #define ITEM_SLOT_COUNT 198 @@ -21,17 +22,28 @@ typedef struct { u8 sellRate; /**< 0x2: sell rate. */ } ShopItemPrice; /* 0x3 = 3 bytes */ -typedef struct { - u8 unk0[0xC7]; -} Struct_D_801EB088; - typedef struct { u8 pad000[0x20]; - s32 *unk20; + s32 *unk20; /**< 0x20: pointer to data. */ u8 pad024[0x12]; - u16 unk36; + u16 unk36; /**< 0x36: scroll offset. */ } Struct_801E8B60; +typedef struct { + u8 pad000[0x3A]; + s16 unk3A; /**< 0x3A: scroll offset. */ + u8 pad03D[0x4]; + u8 unk40; /**< 0x40: page start. */ + u8 unk41; /**< 0x41: page end. */ + u8 pad041[0x4]; + u8 unk46; /**< 0x46: orientation (left if equals 0, right otherwise). */ +} Struct_801E7374; + +typedef struct { + u8 pad000[0x28]; + s32 unk28; /**< 0x28: passed to drawColorByMenuPalette as 4th argument. */ +} Struct_801E7508; + extern ShopData D_80077CC8[SHOP_COUNT]; /**< Shop data table. */ extern u8 D_80077EBC[ITEM_SLOT_COUNT]; /**< Item slot inventory. */ extern ShopItemPrice D_801EA3F0[ITEM_PRICE_COUNT]; /**< Item price table. */ @@ -43,11 +55,13 @@ extern ShopItemRarity D_801EA170[SHOP_COUNT][SHOP_ITEM_COUNT]; /**< Shop item ra extern ShopItemVisibility D_801EAA28[SHOP_ITEM_COUNT]; /**< Shop item visibility table. */ extern s32 D_801EAA48[ITEM_PRICE_COUNT]; /**< Item sell prices. */ extern s32 D_801EAD68[ITEM_PRICE_COUNT]; /**< Item buy prices. */ -extern Struct_D_801EB088 D_801EB088; +extern u8 D_801EB088[ITEM_PRICE_COUNT]; /**< Item quantities. */ extern s32 g_menuColor; extern s32 D_80077E70; extern s32 func_801E8AB0; extern void func_801E5E90(void*); extern s32 func_801E79D4(void*, s32, s32); +extern s32 func_801EFBB4(s32, s32, s32); +extern void* func_801E6FD8(void); #endif /* MENUSHOP_H */ diff --git a/src/menu/menumain/menumain.c b/src/menu/menumain/menumain.c index 4b4d7180..2af0f13a 100644 --- a/src/menu/menumain/menumain.c +++ b/src/menu/menumain/menumain.c @@ -1693,7 +1693,7 @@ s32 func_801F5E0C(s32 ctx, s32 dl, s32 x, s32 y, s32 color, s32 val, s32 flag) { } /** @brief Render number right-aligned (last param = 1). */ -void func_801F5EFC(s32 a0, s32 a1, s32 a2, s32 a3, s32 a4, s32 a5) { +s32 func_801F5EFC(s32 a0, s32 a1, s32 a2, s32 a3, s32 a4, s32 a5) { func_801F5E0C(a0, a1, a2, a3, a4, a5, 1); } diff --git a/src/menu/menushop/menushop.c b/src/menu/menushop/menushop.c index 6723752c..b18c32e3 100644 --- a/src/menu/menushop/menushop.c +++ b/src/menu/menushop/menushop.c @@ -89,8 +89,68 @@ void func_801E5BA4(s32 a0, s32 a1) { func_801F0A34(a0, 0, buf[a1] + 0x24, 0x22); } -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E5C08); +/** + * @brief Updates the player's gil and synchronizes the shop inventory. + * + * Rebuilds item counts from the temporary purchase table, clears depleted + * slots, and adds any remaining items to the inventory. + * + * @param gil The updated gil amount. + */ +void func_801E5C08(s32 gil) { + ItemSlot *constPtr; + ItemSlot *p; + s32 i; + + if (g_gameState.mainData.partyLockFlag & 1) { + g_gameState.mainData.party.dreamGil = gil; + } else { + g_gameState.mainData.party.gil = gil; + } + + constPtr = D_80077EBC; + p = D_80077EBC; + + for (i = 0; i < ITEM_SLOT_COUNT; i++, p++) { + u8 itemId = p->id; + u8 itemCount = D_801EB088[itemId]; + + if (itemCount == 0) { + p->id = 0; + } + + p->count = itemCount; + D_801EB088[itemId] = 0; + } + + p = constPtr; + for (i = 0; i < ITEM_PRICE_COUNT; i++) { + u8 itemCount = D_801EB088[i]; + + if (itemCount == 0) { + continue; + } + + while (1) { + if (p->id == 0 || p->count == 0) { + p->id = i; + p->count = itemCount; + func_800370AC(i); + break; + } + p++; + } + } +} + +/** + * @brief Loads the current gil amount and rebuilds the available item counts. + * + * Item counts are read from the shop inventory table and stored by item ID. + * + * @return The current gil amount (dream gil if party is locked, otherwise normal gil). + */ s32 func_801E5D28(void) { s32 result; u8* ptr1; @@ -108,7 +168,7 @@ s32 func_801E5D28(void) { ptr1 = D_80077EBC; for (i = 0; i < ITEM_PRICE_COUNT; i++) { - D_801EB088.unk0[i] = 0; + D_801EB088[i] = 0; } for (i = 0; i < ITEM_SLOT_COUNT; i++) { @@ -119,7 +179,7 @@ s32 func_801E5D28(void) { ptr1++; if (val1 != 0) { - D_801EB088.unk0[val1] = val2; + D_801EB088[val1] = val2; } } @@ -241,12 +301,73 @@ void func_801E6ACC(void) { } } +/** + * @brief Update shop item visibility based on rarity and flags. + * + * For each item in the shop, computes a value based on the item's rarity. + * If the computed value exceeds 0x80, the item is marked as visible; + * otherwise, it is hidden. The visibility flags are stored in both the shared + * D_801EAA28 table and the shop's own inventory. + * + * @param shopId Index of the shop to update. + */ +void func_801E6C3C(s32 shopId) { + ShopItemRarity* rarity; + ShopItemVisibility* p; + ShopData *shop; + s32 i; + s32 bitMask; + + p = D_801EAA28; + rarity = D_801EA170[0]; + rarity += shopId * 16; + shop = &D_80077CC8[shopId]; + bitMask = (func_801F72B4() << 6) & 0x100; + + for (i = 0; i< 16; i++, p++) { + if (p->itemId != 0) { + s32 aux = rarity[i].rarity; + s32 val = bitMask + aux; + + aux = 0xFF; + if (val == aux) { + val = 0x100; + } + + func_801F6A5C(); + + aux = 0x80; + if (val > aux) { + aux = 1; + p->visible = 1; + } else { + p->visible = 0; + } + + if (val == 0) { + p->visible = 0; + } + + shop->items[i] = p->visible; + } + } +} -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E6C3C); INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E6D54); -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E6E0C); +void func_801E6E0C(s32 arg0, s32 arg1, s32 arg2, s32 arg3) { + s32 result; + + result = func_801E5A8C(arg0, arg1, arg2 + 0xC, arg3 + 5, 3, D_801E9B64, 7); + g_menuDisplayCfg.iconType = 0; + g_menuDisplayCfg.iconSubType = 0; + g_menuDisplayCfg.x = arg2; + g_menuDisplayCfg.y = arg3; + g_menuDisplayCfg.w = 0x150; + g_menuDisplayCfg.h = 0x15; + func_801EF9AC(arg0, result, 0x1000, g_menuColor); +} s32 func_801E6EB0(s32 arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { u8 buffer[0x80]; @@ -263,7 +384,7 @@ s32 func_801E6EB0(s32 arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { temp_s0 = g_menuDisplayCfg.x + temp_v0; temp_s1 = g_menuDisplayCfg.y + 5; decodeMessage(msg, buffer, -1); - result = func_801F0FEC(arg0, arg1, temp_s0, temp_s1, buffer, 7); + result = func_801F0FEC(arg0, arg1, temp_s0, temp_s1, (s32)buffer, 7); } return result; } @@ -303,9 +424,58 @@ INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E6FD8); INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E722C); -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E7374); +s32 func_801E7374(Struct_801E7374* arg0, void *arg1, void* arg2, s32 arg3, s32 arg4) { + g_menuDisplayCfg.iconType = 0x4C; + g_menuDisplayCfg.iconSubType = 0; + g_menuDisplayCfg.x = arg3; + g_menuDisplayCfg.w = 0x100; + g_menuDisplayCfg.h = 0x77; + g_menuDisplayCfg.columnCount = 8; + g_menuDisplayCfg.y = arg4; + g_menuDisplayCfg.pageStart = arg0->unk40; + g_menuDisplayCfg.pageEnd = arg0->unk41; + g_menuDisplayCfg.scrollOffset = arg0->unk3A; + g_menuDisplayCfg.dataPtr = (s32)arg0; + + if (arg0->unk46 == 0) { + arg2 = (void *)func_8002FF34(arg1, arg2, 0x47, arg3 + 0xA8, arg4, g_menuColor); + arg2 = (void *)func_801F5F30((s32)arg1, (s32)arg2, arg3 + 0x1C, arg4, g_menuColor, (s32) (s8) arg0->unk40); + } + else { + arg2 = (void *)func_8002FF34(arg1, arg2, 0x47, arg3 + 0x80, arg4, g_menuColor); + arg2 = (void *)func_8002FF34(arg1, arg2, 0x4D, arg3 + 0xD6, arg4, g_menuColor); + arg2 = (void *)func_801F5EFC((s32)arg1, (s32)arg2, arg3 + 0x1C, arg4, g_menuColor, (s32) (s8) arg0->unk40); + } + + arg2 = (void *)func_801F5F60((s32)arg1, (s32)arg2, g_menuColor, 3); + return func_801EFBB4((s32)arg1, (s32)arg2, (s32)func_801E6FD8); +} + +void func_801E7508(Struct_801E7508 *arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { + s32 s6; + s32 s5; + s32 v0; + + s6 = arg4 + 6; + s5 = arg3 + 0xD0; + arg2 = func_801F0FEC(arg1, arg2, s5, s6, func_801F6AA4(0x33), 7); + + v0 = s6 << 0x10; + s5 = arg3 + 0x142; + arg2 = drawColorByMenuPalette(arg1, arg2, v0 | (s5 & 0xFFFF), arg0->unk28, 7); -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E7508); + s6 = arg4 + 8; + arg2 = func_8002FF34(arg1, arg2, 0xB, arg3 + 0x143, s6, g_menuColor); + + g_menuDisplayCfg.iconType = 0x57; + g_menuDisplayCfg.iconSubType = 0; + g_menuDisplayCfg.x = arg3; + g_menuDisplayCfg.w = 0x150; + g_menuDisplayCfg.y = arg4; + g_menuDisplayCfg.h = 0x17; + + func_801EF9AC(arg1, arg2, 0x1000, g_menuColor); +} INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E7628); @@ -461,7 +631,7 @@ void func_801E8B60(Struct_801E8B60 *a0, s32 a1, s32 a2, s32 a3, s32 arg5) { g_menuDisplayCfg.pageEnd = 1; g_menuDisplayCfg.y = arg5; g_menuDisplayCfg.scrollOffset = a0->unk36; - g_menuDisplayCfg.dataPtr = &a0->unk20; + g_menuDisplayCfg.dataPtr = (s32)&a0->unk20; { func_801EFBB4(a1, a2, (s32)&func_801E8AB0); } From 8454cb6f5733197b7e69a06a7f3d822282818ab9 Mon Sep 17 00:00:00 2001 From: Dargor Date: Sun, 6 Sep 2026 00:00:41 -0300 Subject: [PATCH 6/7] update description of items in shop inventory --- include/gamestate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/gamestate.h b/include/gamestate.h index fc2e9793..c8321dca 100644 --- a/include/gamestate.h +++ b/include/gamestate.h @@ -107,7 +107,7 @@ enum GfId { * 20 entries at g_gameState + 0x950 (0x80077CC8). */ typedef struct { - /* 0x00 */ u8 items[16]; /**< Item IDs available for sale. */ + /* 0x00 */ u8 items[16]; /**< Flags indicating whether each item is visible in the shop. */ /* 0x10 */ u8 visited; /**< Shop has been visited flag. */ /* 0x11 */ u8 pad[3]; /**< Padding. */ } ShopData; /* 0x14 = 20 bytes */ From eb622266425069c163a3c7d6eb5e471226e88408 Mon Sep 17 00:00:00 2001 From: dargor123 Date: Mon, 7 Sep 2026 18:10:32 -0300 Subject: [PATCH 7/7] menushop: remove s32 casts from inline strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Robin Engström --- src/menu/menushop/menushop.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/menu/menushop/menushop.c b/src/menu/menushop/menushop.c index b18c32e3..e45ec8f2 100644 --- a/src/menu/menushop/menushop.c +++ b/src/menu/menushop/menushop.c @@ -506,9 +506,9 @@ void func_801E7B9C(s32 a0) { MenuTask *temp_s0; temp_s0 = (MenuTask *)func_801F179C((s32)func_801E5E90, (s32)func_801E79D4); - func_801F1D2C(0, (s32)"shop.bin", (s32)D_801EA170); - func_801F1D2C(0, (s32)"price.bin", (s32)D_801EA3F0); - func_801F1D2C(0, (s32)"mitem.bin", (s32)D_801EA70C); + func_801F1D2C(0, "shop.bin", (s32)D_801EA170); + func_801F1D2C(0, "price.bin", (s32)D_801EA3F0); + func_801F1D2C(0, "mitem.bin", (s32)D_801EA70C); if (temp_s0 != NULL) { temp_s0->unk2C = D_80077EBC; temp_s0->unk36 = 0x1000;