diff --git a/config/ff8.yaml b/config/ff8.yaml index 4e9f71b6..2f2fb775 100644 --- a/config/ff8.yaml +++ b/config/ff8.yaml @@ -390,6 +390,7 @@ binaries: options: target_path: original/menushop.ovl src_path: src/menu/menushop + section_order: [.text, .rodata, .data, .bss] segments: - name: menushop type: code @@ -397,7 +398,9 @@ binaries: vram: 0x801E5800 subsegments: - [0x0, c, menushop] + - [0x29A4, c, menushop2] - [0x429C, .rodata, menushop] + - [0x4308, .rodata, menushop2] - [0x4364, rodata] - [0x4570, data] - [0x5228] diff --git a/include/gamestate.h b/include/gamestate.h index c8321dca..8b069694 100644 --- a/include/gamestate.h +++ b/include/gamestate.h @@ -108,8 +108,8 @@ enum GfId { */ typedef struct { /* 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. */ + /* 0x10 */ u16 visited; /**< Shop has been visited flag. */ + /* 0x12 */ u8 pad[2]; /**< Padding. */ } ShopData; /* 0x14 = 20 bytes */ #define SHOP_COUNT 20 diff --git a/include/menushop.h b/include/menushop.h index e322166e..4f25d6b1 100644 --- a/include/menushop.h +++ b/include/menushop.h @@ -23,45 +23,88 @@ typedef struct { } ShopItemPrice; /* 0x3 = 3 bytes */ typedef struct { - u8 pad000[0x20]; - s32 *unk20; /**< 0x20: pointer to data. */ - u8 pad024[0x12]; - u16 unk36; /**< 0x36: scroll offset. */ -} Struct_801E8B60; + u8 type; /**< 0x0: item type. */ + u8 flags; /**< 0x1: targeting and usability bitfield. */ + u8 param1; /**< 0x2: type-dependent (heal amount, GF id, ability id, magazine start page). */ + u8 param2; /**< 0x3: type-dependent (status mask, stat mask, compatibility amount, magazine end page). */ +} MItemEntry; /* 0x4 = 4 bytes */ 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; + u8 pad000[0xF]; + u16 state; /**< 0x10: state machine current state (0-17) */ + u8 pad12[0x8]; + s16 unk1C; + u8 pad1D[0x3]; /**< 0x12 */ + s32 field_20; /**< 0x20 */ + s32 field_24; /**< 0x24 */ + u32 gil; /**< 0x28: gil */ + u8 *unk2C; /**< 0x2C: pointer to D_80077EBC */ + union { + s32 unk30_s32; + s16 unk30_s16[2]; + } union30; /* 0x30 */ + s16 unk34; /* 0x34 */ + u16 unk36; /* 0x36 */ + u16 unk38; /* 0x38 */ + s16 unk3A; /**< 0x3A: scroll offset. */ + union { + s16 unk3C_s16[2]; + s8 unk3C_s8[4]; + } union3C; /* 0x3C */ + u8 unk40; /**< 0x40: page start. */ + u8 unk41; /**< 0x41: page end. */ + s8 unk42; /* 0x42 */ + u8 unk43; /* 0x43 */ + u8 unk44; /* 0x44 */ + u8 unk45; /* 0x45 */ + u8 unk46; /**< 0x46: orientation (left if equals 0, right otherwise). */ + s8 unk47; /* 0x47 */ + u8 unk48; /* 0x48 */ + u8 unk49; /**< 0x49: item count */ + u8 unk4A; /* 0x4A */ + u8 unk4B; /* 0x4B */ + u16 unk4C; /* 0x4C */ + u16 unk4E; /* 0x4E */ +} ShopMenuState; typedef struct { - u8 pad000[0x28]; - s32 unk28; /**< 0x28: passed to drawColorByMenuPalette as 4th argument. */ -} Struct_801E7508; + u16 unk0; + u8 unk2; + u8 unk3; + u8 unk4[8]; +} Struct_func_801E7CFC; /* 12 bytes */ + +typedef struct { + u8 pad0[4]; + u8 unk4; + u8 pad[7]; +} Struct_func_801E7F4C; /* 12 bytes */ extern ShopData D_80077CC8[SHOP_COUNT]; /**< Shop data table. */ +extern s32 D_80077E70; 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 ShopItemRarity D_801EA170[SHOP_COUNT][SHOP_ITEM_COUNT]; /**< Shop item rarity tables. */ +extern Struct_func_801E7F4C D_8007C3B8[28]; +extern u16 D_801E9B64[4]; /**< 8 bytes. */ +extern u8 D_801E9B6C[52]; /**< 52 bytes. */ +extern Struct_func_801E7CFC D_801E9BA0[33]; /**< 396 bytes. */ +extern u8 D_801E9D2C[68]; /**< 68 bytes. */ +extern ShopItemRarity D_801EA170[SHOP_COUNT][SHOP_ITEM_COUNT]; /**< Shop item rarity tables (shop.bin content). */ +extern ShopItemPrice D_801EA3F0[ITEM_PRICE_COUNT]; /**< Item price table (price.bin content). */ +extern MItemEntry D_801EA70C[199]; /**< Field-menu item table (mitem.bin content). */ 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 u8 D_801EB088[ITEM_PRICE_COUNT]; /**< Item quantities. */ +extern u8 D_801EB150[8]; +extern s32 D_801EB160; +extern s32 D_801EB260[]; +extern s32 D_801EB2E4; +extern s32 D_801EB2E8; +extern u8 D_801F7F98[]; 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 void func_801E5E90(ShopMenuState*); +extern s32 func_801E79D4(ShopMenuState*, s32, s32); extern s32 func_801EFBB4(s32, s32, s32); -extern void* func_801E6FD8(void); +extern s32 func_801E6FD8(s32, s32, s32, s32, s32); #endif /* MENUSHOP_H */ diff --git a/include/menushop2.h b/include/menushop2.h new file mode 100644 index 00000000..f937aebc --- /dev/null +++ b/include/menushop2.h @@ -0,0 +1,8 @@ +#ifndef MENUSHOP2_H +#define MENUSHOP2_H + +#include "common.h" +#include "menu.h" +#include "menushop.h" + +#endif /* MENUSHOP2_H */ diff --git a/src/menu/menushop/menushop.c b/src/menu/menushop/menushop.c index e45ec8f2..1b51a04f 100644 --- a/src/menu/menushop/menushop.c +++ b/src/menu/menushop/menushop.c @@ -16,7 +16,18 @@ * @param a2 Item index. * @return Item byte value, or 0 if out of range. */ -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E5800); +s32 func_801E5800(ShopMenuState *arg0, s32 arg1, s32 arg2) { + if (arg2 >= 198) { + return 0; + } + + if (arg1 == 0) { + return D_801EAA28[arg2].itemId; + } + + arg2 *= 2; + return arg0->unk2C[arg2]; +} /** * @brief Look up a shop item's category byte. @@ -31,7 +42,20 @@ INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E5800); * @param a2 Item index. * @return Category byte value, or 0 if not found. */ -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E583C); +s32 func_801E583C(ShopMenuState* a0, s32 a1, s32 a2) { + s32 itemId; + + if (a1 == 0) { + return D_801EAA28[a2].visible; + } + + itemId = func_801E5800(a0, a1, a2); + if (itemId == 0) { + return 0; + } + + return D_801EB088[itemId]; +} /** * @brief Look up shop item and get its description string. @@ -46,9 +70,9 @@ INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E583C); * @return Description string pointer, or 0 if invalid */ s32 func_801E58A0(s32 a0, s32 a1, s32 a2) { - s32 result = func_801E583C(a0, a1, a2); + s32 result = func_801E583C((ShopMenuState *)a0, a1, a2); if (result != 0) { - return getStatDesc(func_801E5800(a0, a1, a2)); + return getStatDesc(func_801E5800((ShopMenuState *)a0, a1, a2)); } return 0; } @@ -63,15 +87,78 @@ s32 func_801E58A0(s32 a0, s32 a1, s32 a2) { * @return Property byte value. */ s32 func_801E5904(s32 a0) { - u8 idx = *(u8 *)(D_801EA70C + a0 * 4); - return D_801F7F98[idx]; + return D_801F7F98[D_801EA70C[a0].type]; } -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E5930); +void func_801E5930(s32 arg0, s32 arg1, ShopMenuState* arg2) { + s32 unk36; + s32 aux2; + s32 aux1; + + aux1 = (arg1 % 8) * 0xD + 0x52; + + unk36 = (s16)arg2->unk36; + aux2 = D_801FA3C8[(unk36 < 0 ? -unk36 : unk36) / 64]; + aux2 = (aux2 * 0x168) / 4096; + + func_801F0A34(arg0, 0, aux2 + 0x23, aux1); +} + +s32 func_801E59D8(s16* arg0, s16* arg1, s32 arg2) { + s32 sum; + s32 count; + s16 val; + s32 aux; + + sum = 0; + count = 0; + *arg1 = 0; + arg1++; + + while (1) { + val = *arg0; + arg0++; + + if (val == -1) { + break; + } + + aux = func_801F08D4(1, arg2, (s32)val, 0); + aux = getGlyphStatusU16(aux); + aux += 0xA; + sum += aux; + *arg1 = sum; + arg1++; + count++; + } -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E59D8); + return count; +} -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E5A8C); +s32 func_801E5A8C(s32 arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4, s16* arg5, s32 arg6) { + s16 buffer[36]; + s32 i; + s32 count; + s32 tmp1; + s32 param; + s32 tmp2; + s32 arg3Val; + s32 mask; + + arg3Val = arg3; + count = func_801E59D8(arg5, buffer, arg4); + for (i = 0; i < count; i++) { + tmp1 = arg2 + buffer[i]; + mask = 1 << i; + param = 1; + if (arg6 & mask) { + param = 7; + } + tmp2 = func_801F08D4(1, arg4, arg5[i], 0); + arg1 = func_801F0FEC(arg0, arg1, tmp1, arg3Val, tmp2, param); + } + return arg1; +} /** * @brief Render a shop item at a position from a decoded table. @@ -108,8 +195,8 @@ void func_801E5C08(s32 gil) { g_gameState.mainData.party.gil = gil; } - constPtr = D_80077EBC; - p = D_80077EBC; + constPtr = (ItemSlot *)D_80077EBC; + p = constPtr; for (i = 0; i < ITEM_SLOT_COUNT; i++, p++) { u8 itemId = p->id; @@ -224,10 +311,440 @@ void func_801E5DBC(void) { } } -void func_801E5E88(void) { +void func_801E5E88(u8 arg0) { } -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E5E90); +void func_801E5E90(ShopMenuState *s) { + u16 btnFlags; + u32 cfgFlags; + u16 *statePtr; + u16 state; + u16 state2; + + statePtr = &s->state; + btnFlags = g_menuDisplayCfg.inputNew; + cfgFlags = g_menuDisplayCfg.inputRepeat; + state = s->state; +block_38: + state &= 0xFFFF; +block_40: + switch (state) { + case 0: + s->union30.unk30_s32 = func_801F6AA4(0x40); + s->unk38 = 0; + *statePtr = 1; + break; + + case 1: + s->unk38 += 0x100; + if ((s16)s->unk38 >= 0x1000) { + s->unk38 = 0x1000; + *statePtr = 2; + } + func_801E5BA4(1, (s8)s->unk42); + break; + + case 2: + s->unk42 = 0; + *statePtr = 3; + func_801E5BA4(1, (s8)s->unk42); + break; + + case 3: + if (btnFlags & 0x2000) { + sendSpuCommand(1); + s->unk42 = func_80035B28(7, (s8)s->unk42); + } + + if (btnFlags & 0x8000) { + sendSpuCommand(1); + s->unk42 = func_80035B70(7, (s8)s->unk42); + } + + func_801E5BA4(1, (s8)s->unk42); + + if (cfgFlags & 0x10) { + sendSpuCommand(3); + *statePtr = 0x10; + } + + if (!(cfgFlags & 0x40)) { + break; + } + + sendSpuCommand(2); + + if ((s8)s->unk42 == 2) { + state = 16; + goto block_38; + } + + s->unk46 = s->unk42; + if (s->unk46 == 0) { + s->union30.unk30_s32 = func_801F6AA4(0x42); + s->unk47 = 2; + } else { + block_17c: + s->union30.unk30_s32 = func_801F6AA4(0x41); + s->unk47 = 0x19; + } + + block_190: + s->unk40 = s->union3C.unk3C_s16[s->unk46] / 8; + func_801E5C08(s->gil); + s->gil = func_801E5D28(); + *statePtr = 4; + break; + + case 4: + s->unk36 = 0xF00; + *statePtr = 5; + /* fallthrough */ + + case 5: + s->unk36 -= 0x100; + if (((s16)s->unk36 << 0x10) <= 0) { + state2 = 6; + s->unk36 = 0; + goto block_894; + } + goto block_898; + + case 6: { + s32 param; + s16 dividend; + s32 quotient; + s32 rest; + + if (btnFlags != 0) { + s->unk4E = 0; + } + + param = 0x4A; + if (s->unk4E != 0) { + s->unk4E--; + s->union30.unk30_s32 = func_801F6AA4(param); + } else { + param = 0x41; + if (s->unk46 == 0) { + param = 0x42; + } + s->union30.unk30_s32 = func_801F6AA4(param); + } + + dividend = s->union3C.unk3C_s16[s->unk46]; + rest = (s16)(dividend % 8); + quotient = dividend / 8; + s->union3C.unk3C_s16[s->unk46] = func_801F6768(btnFlags, 8, rest) + quotient * 8; + + s->field_20 = func_801E58A0((s32)s, s->unk46, s->union3C.unk3C_s16[s->unk46]); + + if (btnFlags & 0x8000) { + state = 7; + goto block_38; + } + if (btnFlags & 0x2000) { + state = 9; + goto block_38; + } + func_801E5BA4(0, (s8)s->unk42); + func_801E5930(1, s->union3C.unk3C_s16[s->unk46], s); + if (cfgFlags & 0x10) { + sendSpuCommand(3); + s->field_20 = 0; + s->union30.unk30_s32 = func_801F6AA4(0x40); + *statePtr = 14; + } + if (cfgFlags & 0x40) { + if (func_801E583C(s, s->unk46, s->union3C.unk3C_s16[s->unk46])) { + if (s->unk46 == 1) { + if (s->union3C.unk3C_s16[s->unk46] >= 0xC6) { + goto block_774; + } + } + state = 11; + goto block_40; + } + goto block_774; + } + break; + } + + case 11: { + s32 index; + u32 price; + u32 count; + + index = func_801E5800(s, s->unk46, s->union3C.unk3C_s16[s->unk46]); + s->unk48 = 1; + price = D_801EAD68[index]; + + if (s->unk46 == 0) { + if (index == 0) { + goto block_774; + } + + if (s->gil < price) { + s32 tmp; + sendSpuCommand(5); + tmp = func_801F6AA4(0x49); + initSfxPlayback(0, tmp); + func_801F23D0(0, 0x68, (u8 *)tmp); + setSfxPitch(0, 0); + startSfxNormal(0); + s->unk4C = 0x258; + *statePtr = 15; + break; + } + + if (D_801EB088[index] >= 100) { + s32 tmp; + sendSpuCommand(5); + tmp = func_801F6AA4(0x48); + func_801F23D0(0, 0x68, (u8 *)tmp); + initSfxPlayback(0, tmp); + setSfxPitch(0, 0); + startSfxNormal(0); + s->unk4C = 0x258; + *statePtr = 15; + break; + } + + count = s->gil / price; + if ((s32)count + D_801EB088[index] >= 100) { + count = 100 - D_801EB088[index]; + } + + goto block_4f4; + } + + if (index == 0) { + goto block_774; + } + + count = D_801EB088[index]; + + block_4f4: + sendSpuCommand(2); + s->unk49 = count; + s->unk4A = 0x40; + if (s->unk46 == 0) { + s->union30.unk30_s32 = func_801F6AA4(0x46); + } else { + s->union30.unk30_s32 = func_801F6AA4(0x47); + } + *statePtr = 12; + break; + } + + case 15: + s->unk4C -= 1; + if (cfgFlags & 0x50) { + func_801F7BEC(cfgFlags); + s->unk4C = 0; + } + if ((s16)s->unk4C <= 0) { + fadeOutSfxFast(0); + *statePtr = 6; + } + break; + + case 12: + func_801E5BA4(0, (s8)s->unk42); + func_801E5930(0, s->union3C.unk3C_s16[s->unk46], s); + + if (btnFlags & 0x2000) { + if ((s8)s->unk48 < (s8)s->unk49) { + s->unk48++; + sendSpuCommand(1); + } + } + + if (btnFlags & 0x8000) { + if ((s8)s->unk48 >= 2) { + s->unk48--; + sendSpuCommand(1); + } + } + + if (btnFlags & 0x4000) { + if ((s8)s->unk48 >= 2) { + sendSpuCommand(1); + s->unk48 -= 10; + if (((s8)s->unk48 << 24) <= 0) { + s->unk48 = 1; + } + } + } + + if (btnFlags & 0x1000) { + if ((s8)s->unk48 < (s8)s->unk49) { + s->unk48 += 0xA; + sendSpuCommand(1); + if ((s8)s->unk48 > (s8)s->unk49) { + s->unk48 = s->unk49; + } + } + } + + if (cfgFlags & 0x10) { + sendSpuCommand(3); + *statePtr = 13; + } + + if (cfgFlags & 0x40) { + s32 index; + s32 price; + if (s->unk46 == 0) { + playSoundEffect(0x14); + index = func_801E5800(s, s->unk46, s->union3C.unk3C_s16[s->unk46]); + price = D_801EAD68[index]; + price *= (s8)s->unk48; + s->gil -= price; + D_801EB088[index] += s->unk48; + } else { + if (s->union3C.unk3C_s16[s->unk46] >= 0xC6) { + block_774: + sendSpuCommand(5); + break; + } + + playSoundEffect(0x14); + index = func_801E5800(s, s->unk46, s->union3C.unk3C_s16[s->unk46]); + price = D_801EAA48[index]; + price *= (s8)s->unk48; + s->gil += price; + if (s->gil > 99999999) { + s->gil = 99999999; + } + + D_801EB088[index] -= s->unk48; + } + s->unk4E = 0x3C; + *statePtr = 13; + } + break; + + case 13: + func_801E5BA4(0, (s8)s->unk42); + func_801E5930(0, s->union3C.unk3C_s16[s->unk46], s); + s->unk4A = 0; + *statePtr = 6; + break; + + case 14: + func_801E5BA4(0, (s8)s->unk42); + s->unk36 += 0x100; + if ((s16)s->unk36 < 0x1000) { + goto block_898; + } + s->unk36 = 0x1000; + state2 = 3; + block_894: + *statePtr = state2; + block_898: + func_801E5BA4(0, (s8)s->unk42); + func_801E5930(1, s->union3C.unk3C_s16[s->unk46], s); + break; + + case 7: { + s32 dividend; + s32 rest; + s32 quotient; + func_801E5BA4(0, (s8)s->unk42); + func_801E5930(1, s->union3C.unk3C_s16[s->unk46], s); + s->field_24 = s->field_20; + dividend = s->union3C.unk3C_s16[s->unk46]; + rest = (s16)(dividend % 8); + quotient = dividend / 8; + s->unk41 = quotient; + quotient--; + if (quotient < 0) { + quotient = (u8)s->unk47 - 1; + } + s->union3C.unk3C_s16[s->unk46] = rest + quotient * 8; + s->unk40 = quotient; + s->field_20 = func_801E58A0((s32)s, s->unk46, s->union3C.unk3C_s16[s->unk46]); + s->unk3A = -0xE67; + sendSpuCommand(1); + *statePtr = 8; + break; + } + + case 8: + func_801E5BA4(0, (s8)s->unk42); + func_801E5930(1, s->union3C.unk3C_s16[s->unk46], s); + s->unk3A += 0x199; + if (s->unk3A < 0) { + goto block_b10; + } + goto block_b0c; + + case 9: { + s32 dividend; + s32 rest; + s32 quotient; + func_801E5BA4(0, (s8)s->unk42); + func_801E5930(1, s->union3C.unk3C_s16[s->unk46], s); + s->field_24 = s->field_20; + dividend = s->union3C.unk3C_s16[s->unk46]; + rest = (s16)(dividend % 8); + quotient = dividend / 8; + s->unk41 = quotient; + quotient++; + if (quotient >= (u8)s->unk47) { + quotient = 0; + } + s->union3C.unk3C_s16[s->unk46] = rest + quotient * 8; + s->unk40 = quotient; + s->field_20 = func_801E58A0((s32)s, s->unk46, s->union3C.unk3C_s16[s->unk46]); + s->unk3A = 0x0E67; + sendSpuCommand(1); + *statePtr = 10; + break; + } + + case 10: + func_801E5BA4(0, (s8)s->unk42); + func_801E5930(1, s->union3C.unk3C_s16[s->unk46], s); + s->unk3A -= 0x199; + if (s->unk3A <= 0) { + block_b0c: + s->unk3A = 0; + *statePtr = 6; + } + block_b10: + if (cfgFlags & 0x8000) { + *statePtr = 7; + } + if (cfgFlags & 0x2000) { + *statePtr = 9; + } + break; + + case 16: + s->union30.unk30_s32 = func_801F6AA4(0x44); + *statePtr = 17; + /* fallthrough */ + + case 17: + s->unk38 -= 0x100; + if ((s16)s->unk38 << 0x10 <= 0) { + s->unk38 = 0; + func_801E5C08(s->gil); + func_801E5E88(s->unk45); + func_801F6888(); + func_801F7B60(); + func_801F18FC(s); + func_801F0BB0(); + } + func_801E5BA4(1, (s8)s->unk42); + break; + + } + + func_801F0948((s16)s->unk38); +} /** * @brief Populate shop item visibility data for a shop. @@ -353,10 +870,49 @@ void func_801E6C3C(s32 shopId) { } } +void func_801E6D54(s32 arg0) { + GameState *gs; + s32 visited; + s32 sum; + + if (arg0 != 0x15) { + gs = &g_gameState; + + sum = gs->mainData.fieldCDC; + + visited = gs->shops[arg0].visited; + + sum += gs->mainData.fieldCE2; + sum += gs->mainData.fieldCE0; + sum &= 0xFFFF; + if (sum == 0) { + sum = 1; + } -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E6D54); + gs->shops[arg0].visited = 1; -void func_801E6E0C(s32 arg0, s32 arg1, s32 arg2, s32 arg3) { + // Dead code + visited &= 0xFFFF; + if (sum < (u32)visited) { + sum += 0x10000; + } + sum -= visited; + // End of dead code + + func_801E6A68(arg0); + func_801E6C3C(arg0); + func_801E6C3C(arg0); + func_801E5DBC(); + func_801E6ACC(); + + /* FIXME: Keep `sum` live here during all function calls (jal), forcing + the compiler to allocate more space on the stack to reach desired + 0x20. */ + KEEP_ALIVE(sum); + } +} + +s32 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); @@ -366,7 +922,7 @@ void func_801E6E0C(s32 arg0, s32 arg1, s32 arg2, s32 arg3) { g_menuDisplayCfg.y = arg3; g_menuDisplayCfg.w = 0x150; g_menuDisplayCfg.h = 0x15; - func_801EF9AC(arg0, result, 0x1000, g_menuColor); + return func_801EF9AC(arg0, result, 0x1000, g_menuColor); } s32 func_801E6EB0(s32 arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { @@ -403,7 +959,7 @@ s32 func_801E6EB0(s32 arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { * @param a3 Y position for the display configuration. * @param arg5 X position for the display configuration. */ -void func_801E6F60(u8 *a0, s32 a1, s32 a2, s32 a3, s32 arg5) { +s32 func_801E6F60(ShopMenuState *s, s32 a1, s32 a2, s32 a3, s32 arg5) { g_menuDisplayCfg.iconType = 0; g_menuDisplayCfg.iconSubType = 0; g_menuDisplayCfg.x = a3; @@ -413,18 +969,122 @@ void func_801E6F60(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 + 0x3A); - g_menuDisplayCfg.dataPtr = (s32)(a0 + 0x20); + g_menuDisplayCfg.scrollOffset = s->unk3A; + g_menuDisplayCfg.dataPtr = (s32)&s->field_20; { - func_801EFBB4(a1, a2, (s32)&func_801E6EB0); + return func_801EFBB4(a1, a2, (s32)&func_801E6EB0); } } -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E6FD8); +s32 func_801E6FD8(s32 arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { + MenuDisplayConfig *cfg; + ShopMenuState *s; + s32 index; + s32 price; + s32 count; + s32 shopItemIdx; + s32 xBase; + s32 yBase; + + cfg = &g_menuDisplayCfg; + shopItemIdx = arg2 * 8 + arg3; + xBase = cfg->x + arg4; + yBase = cfg->y + arg3 * 13; + + s = (ShopMenuState *)cfg->dataPtr; + index = func_801E5800(s, s->unk46, shopItemIdx); + + if (s->unk46 == 1) { // selling + price = D_801EAA48[index]; + count = D_801EB088[index]; + if (shopItemIdx >= 198) { + return arg1; + } + } + + else { // buying + price = D_801EAD68[index]; + count = D_801EAA28[shopItemIdx].visible; + } -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E722C); + if (index != 0 && count != 0) { + s32 color; + s32 statName; + s32 x; + s32 y; + + color = 7; + if (s->gil < D_801EAD68[index] && s->unk46 == 0) { + color = 1; + } + + statName = getStatName(index); + arg2 = func_801E5904(index); + + arg1 = func_8002FF34(arg0, arg1, arg2 + 0xDF, xBase + 0xB, yBase + 8, g_menuColor); + + x = xBase + 0x19; + y = yBase + 0xA; + arg1 = func_801F0FEC(arg0, arg1, x, y, statName, color); + + if (s->unk46 == 0) { // buying + x = xBase + 0xF0; + arg1 = drawColorByMenuPalette(arg0, arg1, (y << 0x10) | (x & 0xffff), price, color); + } + + else { // selling + x = xBase + 0xC8; + arg1 = drawColorByMenuPalette(arg0, arg1, (y << 0x10) | (x & 0xffff), price, color); + x = xBase + 0xF0; + arg1 = drawColorByMenuPalette(arg0, arg1, (y << 0x10) | (x & 0xffff), count, color); + } + } -s32 func_801E7374(Struct_801E7374* arg0, void *arg1, void* arg2, s32 arg3, s32 arg4) { + return arg1; +} + +s32 func_801E722C(ShopMenuState *s, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { + MenuDisplayConfig *cfg; + s32 color; + + cfg = &g_menuDisplayCfg; + color = 7; + + if (s->unk46 == 0) { + s32 x; + s32 y; + s32 result; + s32 index; + s32 count; + + x = 8 + arg3; + y = 9 + arg4; + result = func_801F0FEC(arg1, arg2, x, y, func_801F6AA4(0x32), color); + + /* FIXME: Keep `x` live here to force `addiu` between `move` and `lbu`. */ + KEEP_ALIVE(x); + + x = 0x42 + arg3; + + /* FIXME: Keep `s` live here to force allocation to the $v0 register. */ + KEEP_ALIVE(s); + + index = func_801E5800(s, s->unk46, s->union3C.unk3C_s16[s->unk46]); + y = 0x16 + arg4; + arg2 = drawColorByMenuPalette(arg1, result, (y << 0x10) | (x & 0xFFFF), D_801EB088[index], color); + } + + cfg->iconType = 0x57; + cfg->iconSubType = 0; + cfg->x = arg3; + cfg->w = 0x50; + cfg->y = arg4; + cfg->h = 0x77; + + return func_801EF9AC(arg1, arg2, 0x1000, g_menuColor); +} + +s32 func_801E7374(ShopMenuState *arg0, void *arg1, void* arg2, s32 arg3, s32 arg4) { g_menuDisplayCfg.iconType = 0x4C; g_menuDisplayCfg.iconSubType = 0; g_menuDisplayCfg.x = arg3; @@ -451,7 +1111,7 @@ s32 func_801E7374(Struct_801E7374* arg0, void *arg1, void* arg2, s32 arg3, s32 a 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 func_801E7508(ShopMenuState *arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { s32 s6; s32 s5; s32 v0; @@ -462,7 +1122,7 @@ void func_801E7508(Struct_801E7508 *arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4 v0 = s6 << 0x10; s5 = arg3 + 0x142; - arg2 = drawColorByMenuPalette(arg1, arg2, v0 | (s5 & 0xFFFF), arg0->unk28, 7); + arg2 = drawColorByMenuPalette(arg1, arg2, v0 | (s5 & 0xFFFF), arg0->gil, 7); s6 = arg4 + 8; arg2 = func_8002FF34(arg1, arg2, 0xB, arg3 + 0x143, s6, g_menuColor); @@ -474,12 +1134,109 @@ void func_801E7508(Struct_801E7508 *arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4 g_menuDisplayCfg.y = arg4; g_menuDisplayCfg.h = 0x17; - func_801EF9AC(arg1, arg2, 0x1000, g_menuColor); + return func_801EF9AC(arg1, arg2, 0x1000, g_menuColor); } -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E7628); +s32 func_801E7628(ShopMenuState* s, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { + s32 result; + s32 x; + s32 y; + s32 index; + s32 color; + s32 price; + MenuDisplayConfig *cfg; + + color = 7; + cfg = &g_menuDisplayCfg; + if (s->unk4A == 0) { + return arg2; + } + + x = arg3 + 0xB; + y = arg4 + 9; + arg2 = func_801F0FEC(arg1, arg2, x, y, func_801F6AA4(0x37), color); + + y = arg4 + 0x23; + arg2 = func_801F0FEC(arg1, arg2, x, y, func_801F6AA4(0x38), color); + + x = arg3 + 0x60; + y = arg4 + 0x18; + arg2 = drawColorByMenuPalette(arg1, arg2, (y << 0x10) | (x & 0xFFFF), (s8)s->unk48, color); + + y = arg4 + 0x32; + + index = func_801E5800(s, s->unk46, s->union3C.unk3C_s16[s->unk46]); + if (s->unk46 == 0) { + price = D_801EAD68[index]; + } else { + price = D_801EAA48[index]; + } + + arg2 = drawColorByMenuPalette(arg1, arg2, (y << 0x10) | (x & 0xFFFF), price * (s8)s->unk48, color); + + cfg->iconType = 0x47; + cfg->w = 0x68; + cfg->iconSubType = 0; + cfg->x = arg3; + cfg->y = arg4; + cfg->h = 0x42; + + return func_801EF9AC(arg1, arg2, 0x1000, g_menuColor); +} + +s32 func_801E77EC(s32 arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { + MenuDisplayConfig *cfg; + s32 name; + s32 x; + s32 y; + s32 param1; + s32 param2; + s32 result; + + result = func_801EFFB8(); -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E77EC); + /* FIXME: Keep `arg2` live here to force allocation to the $s2 register. */ + KEEP_ALIVE(arg2); + + if (result == 0x17) { + param1 = 1; + param2 = 0x150; + } else { + param1 = 0; + param2 = 0xF4; + } + + cfg = &g_menuDisplayCfg; + + if (arg0 >= 0) { + name = func_801F6AA4(arg0 + 1); + } else { + name = func_801F6AA4(0x30); + } + + x = (param2 - getGlyphStatusU16(name)) / 2; + x += arg3; + + y = 5; + y += arg4; + + arg2 = func_801F0FEC(arg1, arg2, x, y, name, 7); + + cfg->iconType = 0x49; + cfg->iconSubType = 0; + cfg->x = arg3; + cfg->y = arg4; + + if (param1 != 0) { + cfg->w = 0x150; + cfg->h = 0x14; + } else { + cfg->w = 0xF4; + cfg->h = 0x14; + } + + return func_801EF9AC(arg1, arg2, 0x1000, g_menuColor); +} void func_801E791C(s32 arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { s32 var_v0; @@ -500,20 +1257,65 @@ void func_801E791C(s32 arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { func_801EF9AC(arg1, var_v0, 0x1000, g_menuColor); } -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E79D4); +s32 func_801E79D4(ShopMenuState *s, s32 arg1, s32 arg2) { + s32 result; + s32 x; + s32 y; + s32 val; + + result = getDisplayListHead(); + + val = (s16)s->unk36; + val = D_801FA3C8[(val < 0 ? -val : val) / 64]; + + func_801F1AFC(); + setMenuColorIntensity((s16)s->unk38); + + x = 0x18; + y = 0x1D; + arg2 = func_801E6E0C(arg1, arg2, x, y); + + x = 0x18; + y = 8; + arg2 = func_801E77EC(func_801EFFF0(), arg1, arg2, x, y); + + x = 0x1E; + y = 0x33; + result = func_801E6F60(s, arg1, result, x, y); + + x = 0x90; + y = 0x50; + result = func_801E7628(s, arg1, result, x, y); + + x = (val * 0x68 / 0x1000) + 0x118; + y = 0x48; + result = func_801E722C(s, arg1, result, x, y); + + x = (val * 0x168 / 0x1000) + 0x18; + arg2 = func_801E7374(s, (void *)arg1, (void *)arg2, x, y); + + x = (val * 0x168 / 0x1000); + x += 0x18; + y = 0xC1; + arg2 = func_801E7508(s, arg1, arg2, x, y); + + func_801F1B10(); + storeGpuPacket(result); + return arg2; +} void func_801E7B9C(s32 a0) { - MenuTask *temp_s0; + ShopMenuState *temp_s0; - temp_s0 = (MenuTask *)func_801F179C((s32)func_801E5E90, (s32)func_801E79D4); + temp_s0 = (ShopMenuState *)func_801F179C((s32)func_801E5E90, (s32)func_801E79D4); 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; - temp_s0->unk30 = 0; - temp_s0->unk28 = func_801E5D28(); + temp_s0->union30.unk30_s32 = 0; + temp_s0->gil = func_801E5D28(); temp_s0->unk45 = D_801E9B6C[func_801EFFF0()]; func_801E6D54(temp_s0->unk45); func_801E5E90(temp_s0); @@ -545,9 +1347,61 @@ void func_801E7C8C(s32 a0) { } } -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E7CFC); +u8* func_801E7CFC(s32 arg0) { + Struct_func_801E7CFC *ptr1; + u8 *ptr2; + + ptr1 = D_801E9BA0; + ptr2 = D_801E9D2C; + + ptr1 += arg0 & -0xc1; + ptr2 += ptr1->unk0; + + return ptr2; +} + +void func_801E7D30(u8* src, u8* dst) { + u8 buffer[127]; + u8* bufferPtr; + s32 srcVal; -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E7D30); + while (1) { + srcVal = *src; + src++; + + if (srcVal == 0) { + break; + } + + bufferPtr = buffer; + + if (srcVal != 0xA) { + *dst = srcVal; + dst++; + } else { + srcVal = *src; + src++; + *buffer = 0; + + switch (srcVal) { + case 38: + copyString(bufferPtr, getLevelCurveData(D_801EB2E4)); + break; + case 39: + copyString(bufferPtr, getCharName(D_801EB2E8)); + break; + } + + while (*bufferPtr != 0) { + *dst = *bufferPtr; + dst++; + bufferPtr++; + } + } + } + + *dst = 0; +} /** * @brief Compute shop item price from table. @@ -557,7 +1411,12 @@ INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E7D30); * @param a0 Shop item index. * @return Computed price value. */ -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E7E1C); +s32 func_801E7E1C(s32 arg0) { + Struct_func_801E7CFC* ptr; + ptr = D_801E9BA0; + ptr += arg0; + return ptr->unk3 * 10; +} /** * @brief Test if bit a0 is set in D_80077E70. @@ -576,123 +1435,130 @@ s32 func_801E7E68(s32 a0, u32 a1) { return a1 >= (u32)func_801E7E1C(a0); } -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E7E98); +s32 func_801E7E98(s32 arg0, s32 arg1) { + Struct_func_801E7CFC *basePtr; + u8 *ptr; + s32 i; -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E7F4C); + if (func_801E7E68(arg0, arg1) == 0) { + return 0; + } -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E8058); + basePtr = D_801E9BA0; + basePtr += arg0; -/** - * @brief Render shop item entry at computed Y position with width 0x24. - * @param a0 X position parameter - * @param a1 Row index (multiplied by 13 and offset by 0x50 for Y position) - */ -void func_801E8134(s32 a0, s32 a1) { - func_801F0A34(a0, 0, 0x24, a1 * 13 + 0x50); -} + ptr = basePtr->unk4; -/** - * @brief Render shop item quantity at computed Y position with width 0xA9. - * @param a0 X position parameter - * @param a1 Row index (multiplied by 13 and offset by 0x4F for Y position) - */ -void func_801E816C(s32 a0, s32 a1) { - func_801F0A34(a0, 0, 0xA9, a1 * 13 + 0x4F); + for (i = 0; i < 4; i++) { + s32 unk0; + s32 unk1; + + unk0 = *ptr; + ptr++; + unk1 = *ptr; + ptr++; + + if (unk0 != 0 && unk1 > D_801EB088[unk0]) { + return 0; + } + } + + g_gameState.mainData.party.unlockedWeapons |= 1 << arg0; + return 1; } -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E81A4); +s32 func_801E7F4C(s32 arg0, s32 arg1) { + s32 count; + s32 i; + u8* ptr1; + u8* ptr2; + Struct_func_801E7F4C *ptr3; + u8 value; -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E8978); + count = 0; + value = 0xFF; + i = 7; -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E8AB0); + ptr1 = D_801EB150; + ptr1 += 7; -/** - * @brief Configure display parameters and invoke callback for shop sell rendering. - * - * Sets up the g_menuDisplayCfg display configuration structure with the given - * position and size values, stores the pointer at a0+0x20 as the data source, - * reads a halfword at a0+0x36 as the display ID, then calls func_801EFBB4 - * with func_801E8AB0 as the render callback. - * - * @param a0 Pointer to source data structure. - * @param a1 First callback parameter (passed as a0 to func_801EFBB4). - * @param a2 Second callback parameter (passed as a1 to func_801EFBB4). - * @param a3 Y position for the display configuration. - * @param arg5 X position for the display configuration. - */ -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; - 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 = a0->unk36; - g_menuDisplayCfg.dataPtr = (s32)&a0->unk20; - { - func_801EFBB4(a1, a2, (s32)&func_801E8AB0); + for (; i >= 0; i--) { + *ptr1 = value; + ptr1--; + } + + ptr2 = D_801EB150; + ptr3 = D_8007C3B8; + + if ((0x3F >> arg0) & 1) { + for (i = 0; i < 28; i++) { + if (arg0 == ptr3[i].unk4) { + s32 var_s0; + var_s0 = (func_801E7E4C(i) != 0) << 6; + if (func_801E7E98(i, arg1) != 0) { + var_s0 |= 0x80; + } + if (var_s0 != 0) { + *ptr2 = i | var_s0; + ptr2++; + count++; + } + } + } } + + return count; } -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E8BD8); +s32 func_801E8058(s32 arg0) { + Struct_func_801E7F4C *ptr; + s32 temp_s0; + s32 temp_s4; + s32 ret; + s32 i; -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E8D84); + if (D_8007809A & 1) { + return 0; + } -/** @brief Return color code: 7 (equal), 3 (a0 > a1), 2 (a0 < a1). */ -s32 func_801E8FF8(s32 a0, s32 a1) { - s32 color = 7; - if (a0 > a1) color = 3; - if (a0 < a1) color = 2; - return color; -} + ptr = D_8007C3B8; + temp_s4 = func_80036EC0() & 0x3F; + ret = 0; + + for (i = 0; i < 28; i++) { + temp_s0 = 1 << ptr[i].unk4; + if (temp_s4 & temp_s0) { + s32 val; + val = func_801E7E4C(i); + if (val) { + ret |= temp_s0; + continue; + } else { + val = func_801E7E98(i, arg0); + if (val) { + ret |= temp_s0; + } + } + } + } -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E9020); + return ret; +} /** - * @brief Initialize all 30 shop item entries. - * - * Calls func_801E9020 for indices 0 through 29. + * @brief Render shop item entry at computed Y position with width 0x24. + * @param a0 X position parameter + * @param a1 Row index (multiplied by 13 and offset by 0x50 for Y position) */ -void func_801E90BC(void) { - s32 i; - for (i = 0; i < 30; i++) { - func_801E9020(i); - } +void func_801E8134(s32 a0, s32 a1) { + func_801F0A34(a0, 0, 0x24, a1 * 13 + 0x50); } -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E90F8); - /** - * @brief Configure shop display and render with g_menuDisplayCfg settings. - * - * Calls func_801E90F8 with all parameters, then sets up g_menuDisplayCfg - * display config (icon 0x57, 0x150 x 0x26, x=a3, y=arg5) and calls - * func_801EF9AC to render. - * - * @param a0 Context pointer for func_801E90F8. - * @param a1 Render context passed to func_801EF9AC. - * @param a2 Parameter for func_801E90F8. - * @param a3 X position for display config. - * @param arg4 Y position for display config. + * @brief Render shop item quantity at computed Y position with width 0xA9. + * @param a0 X position parameter + * @param a1 Row index (multiplied by 13 and offset by 0x4F for Y position) */ -void func_801E9554(s32 a0, s32 a1, s32 a2, s32 a3, s32 arg4) { - s32 result; - - result = func_801E90F8(a0, a1, a2, a3, arg4); - 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); +void func_801E816C(s32 a0, s32 a1) { + func_801F0A34(a0, 0, 0xA9, a1 * 13 + 0x4F); } - -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E95DC); - -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E9684); - -INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop", func_801E9900); diff --git a/src/menu/menushop/menushop2.c b/src/menu/menushop/menushop2.c new file mode 100644 index 00000000..57fde406 --- /dev/null +++ b/src/menu/menushop/menushop2.c @@ -0,0 +1,611 @@ +#include "common.h" +#include "menushop2.h" + +void func_801E81A4(ShopMenuState *s) { + u16 btnFlags; + u32 cfgFlags; + u16 *statePtr; + + btnFlags = g_menuDisplayCfg.inputNew; + cfgFlags = g_menuDisplayCfg.inputRepeat; + statePtr = &s->state; + + switch (*statePtr) { + case 0: + s->field_20 = (s32)NULL; + s->unk34 = 0; + *statePtr = 1; + /* fallthrough */ + case 1: + s->unk34 += 0x100; + if ((s16) s->unk34 >= 0x1000) { + s->unk34 = 0x1000U; + *statePtr = 2; + } + if (s->unk38 != 0) { + func_801E8134(1, (s32) (s8) s->unk40); + } + break; + case 2: + if (s->unk38 == 0) { + *statePtr = 0xC; + break; + } + s->unk2C = (u8*)func_801F6AA4(0x45); + *statePtr = 3; + case 3: + if (btnFlags & 0x4000) { + sendSpuCommand(1); + s->unk40++; + if ((s8) s->unk40 >= (s32) (u8)s->union3C.unk3C_s8[2]) { + s->unk40 = 0; + } + } + if (btnFlags & 0x1000) { + sendSpuCommand(1); + s->unk40--; + if ((s8)s->unk40 << 0x18 < 0) { + s->unk40 = s->union3C.unk3C_s8[2] - 1; + } + } + s->union3C.unk3C_s8[3] = func_801E7F4C(findNthSetBit(s->unk38, (s8) s->unk40), s->gil); + func_801E8134(1, (s32) (s8) s->unk40); + if (cfgFlags & 0x10) { + sendSpuCommand(3); + *statePtr = 0xF; + } + if (cfgFlags & 0x40) { + s32 result; + result = findNthSetBit(s->unk38, (s8) s->unk40); + if ((D_8007809A & 1) || !(((u16) s->unk3A >> result) & 1)) { + sendSpuCommand(5); + if (!((0x3F >> result) & 1)) { + s32 tmp; + tmp = func_801F6AA4(0x4E); + initSfxPlayback(0, tmp); + func_801F23D0(0, 0x68, (void *)tmp); + setSfxPitch(0, 0); + startSfxNormal(0); + s->unk4C = 0x258; + *statePtr = 4; + } + } else { + sendSpuCommand(2); + *statePtr = 5; + } + } + break; + case 4: + s->unk4C -= 1; + if (cfgFlags & 0x50) { + func_801F7BEC(cfgFlags); + s->unk4C = 0; + } + if ((s16) s->unk4C <= 0) { + fadeOutSfxFast(0); + *statePtr = 3; + } + break; + case 5: + s->unk43 = 1; + s->unk41 = 0; + s->union30.unk30_s16[1] = 0x1000U; + s->unk2C = (u8*)func_801F6AA4(0x4B); + *statePtr = 6; + /* fallthrough */ + case 6: + s->union30.unk30_s16[1] -= 0x100; + if ((s->union30.unk30_s16[1] << 0x10) <= 0) { + s->union30.unk30_s16[1] = 0U; + *statePtr = 7; + } + break; + case 7: + func_801E816C(1, (s8) s->unk41); + s->unk41 = func_801F6768(btnFlags, (u8) s->union3C.unk3C_s8[3], (s8) s->unk41); + s->field_20 = (s32)func_801E7CFC((s32) D_801EB150[(s8)s->unk41]); + if (cfgFlags & 0x10) { + sendSpuCommand(3); + s->unk43 = 0; + *statePtr = 8; + } + if (cfgFlags & 0x40) { + u8 val1; + val1 = D_801EB150[(s8)s->unk41]; + if (val1 & 0x80) { + s32 val2; + val2 = *(s8*)(((s32)s + (s8)s->unk40) + 0x44); + if ((val1 & 0x3F) == val2) { + s32 tmp; + sendSpuCommand(5); + tmp = func_801F6AA4(0x4C); + initSfxPlayback(0, tmp); + func_801F23D0(0, 0x68, (u8 *)tmp); + setSfxPitch(0, 0); + startSfxNormal(0); + s->unk4C = 0x258; + *statePtr = 9; + } else { + sendSpuCommand(2); + *statePtr = 0xA; + } + } else { + s32 tmp; + sendSpuCommand(5); + if (func_801E7E68(val1 & 0x3F, s->gil) != 0) { + tmp = func_801F6AA4(0x3E); + } else { + tmp = func_801F6AA4(0x3F); + } + initSfxPlayback(0, tmp); + func_801F23D0(0, 0x68, (u8 *)tmp); + setSfxPitch(0, 0); + startSfxNormal(0); + s->unk4C = 0x258; + *statePtr = 9; + } + } + break; + case 9: + s->unk4C -= 1; + if (cfgFlags & 0x50) { + func_801F7BEC(cfgFlags); + s->unk4C = 0; + } + if ((s16) s->unk4C <= 0) { + fadeOutSfxFast(0); + *statePtr = 7; + } + break; + case 8: + s->field_20 = (s32)NULL; + s->union30.unk30_s16[1] += 0x100; + if ((s16) s->union30.unk30_s16[1] >= 0x1000) { + s32 k = 0x45; + s->union30.unk30_s16[1] = 0x1000; + s->unk2C = (u8*)func_801F6AA4(k); + *statePtr = 3; + } + break; + case 10: { + s32 ret; + s32 *ptrE4; + s32 *ptrE8; + s->unk42 = 1; + ptrE4 = &D_801EB2E4; + ptrE8 = &D_801EB2E8; + ret = findNthSetBit(s->unk38, (s8) s->unk40); + *ptrE4 = D_801EB150[(s8)s->unk41] & 0x3F; + *ptrE8 = ret; + func_801E7D30((u8 *)func_801F6AA4(0x3B), (u8 *)&D_801EB160); + func_801F728C((s32) &D_801EB160, 0x4B); + *statePtr = 0xB; + /* fallthrough */ + } + case 11: + s->unk42 = func_801F6768(btnFlags, 2, (s8) s->unk42); + func_801F6F88((s32)s->unk42); + + if (cfgFlags & 0x10) { + s->unk42 = -1; + sendSpuCommand(3); + *statePtr = 7; + } + + if (cfgFlags & 0x40) { + if (s->unk42 == 0) { + u8 val1; + val1 = D_801EB150[(s8)s->unk41]; + if (val1 & 0x80) { + Struct_func_801E7CFC *basePtr; + u8 *ptr; + s32 val2; + s32 charIdx; + s32 i; + + playSoundEffect(0x19); + val2 = val1 & 0x3F; + s->gil -= ((u32)func_801E7E1C(val2) * s->union30.unk30_s16[0]) / 1000; + charIdx = findNthSetBit(s->unk38, (s8) s->unk40); + + basePtr = D_801E9BA0; + basePtr += val2; + + ptr = basePtr->unk4; + + g_gameState.chars[charIdx].weaponId = (u8) val2; + *(s8*)(((s32)s + (s8)s->unk40) + 0x44) = (s8) val2; + + for (i = 0; i < 4; i++) { + s32 unk0; + s32 unk1; + + unk0 = *ptr; + ptr++; + unk1 = *ptr; + ptr++; + + if (unk0 != 0) { + D_801EB088[unk0] -= unk1; + } + } + func_801E7F4C(charIdx, s->gil); + } else { + sendSpuCommand(5); + } + } else { + sendSpuCommand(2); + } + + s->unk42 = -1; + *statePtr = 7; + } + + break; + case 12: + sendSpuCommand(5); + s->unk43 = 2; + s->union30.unk30_s16[1] = 0x1000U; + *statePtr = 0xD; + break; + case 13: + if (cfgFlags & 0x50) { + func_801F7BEC(cfgFlags); + *statePtr = 0xE; + } + break; + case 14: + s->union30.unk30_s16[1] -= 0x100; + if ((s->union30.unk30_s16[1] << 0x10) <= 0) { + s->union30.unk30_s16[1] = 0U; + *statePtr = 0xF; + } + break; + case 15: + s->unk2C = (u8 *)func_801F6AA4(0x4D); + *statePtr = 0x10; + /* fallthrough */ + case 16: + s->unk34 -= 0x100; + if ((s->unk34 << 0x10) <= 0) { + s->unk34 = 0U; + func_801E5C08(s->gil); + func_801F7B60(); + func_801F18FC(s); + func_801F0BB0(); + } + if (s->unk38 != 0) { + func_801E8134(1, (s32) (s8) s->unk40); + } + break; + } + + func_801F0948((s32) (s16) s->unk34); +} + +s32 func_801E8978(s32 arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4, s32 arg5) { + MenuDisplayConfig *cfg; + s32 x; + s32 y; + s32 i; + s32 mask; + s32 color; + s32 charName; + + cfg = &g_menuDisplayCfg; + x = arg2 + 0xC; + y = arg3 + 9; + + for (i = 0; i < 8; i++) { + mask = 1 << i; + if (arg4 & mask) { + color = 1; + if (arg5 & mask) { + color = 7; + } + charName = getCharName(g_gameState.chars[i].characterId); + arg1 = func_801F0FEC(arg0, arg1, x, y, charName, color); + y += 0xD; + } + } + + cfg->iconType = 0x49; + cfg->iconSubType = 0; + cfg->x = arg2; + cfg->y = arg3; + cfg->w = 0x88; + cfg->h = 0x69; + + arg1 = func_801EF9AC(arg0, arg1, 0x1000, g_menuColor); + return arg1; +} + +s32 func_801E8AB0(s32 arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { + MenuDisplayConfig *cfg; + u8 buffer[128]; + s32 dataVal; + s32 x; + s32 y; + s32 tmp; + + cfg = &g_menuDisplayCfg; + dataVal = ((s32*)cfg->dataPtr)[arg2]; + + if (dataVal != 0) { + tmp = arg4 + 0xA; + x = cfg->x + tmp; + y = cfg->y + 4; + decodeMessage(dataVal, buffer, -1); + arg1 = func_801F0FEC(arg0, arg1, x, y, (s32)buffer, 7); + } + + return arg1; +} + +/** + * @brief Configure display parameters and invoke callback for shop sell rendering. + * + * Sets up the g_menuDisplayCfg display configuration structure with the given + * position and size values, stores the pointer at a0+0x20 as the data source, + * reads a halfword at a0+0x36 as the display ID, then calls func_801EFBB4 + * with func_801E8AB0 as the render callback. + * + * @param a0 Pointer to source data structure. + * @param a1 First callback parameter (passed as a0 to func_801EFBB4). + * @param a2 Second callback parameter (passed as a1 to func_801EFBB4). + * @param a3 Y position for the display configuration. + * @param a4 X position for the display configuration. + */ +s32 func_801E8B60(ShopMenuState *s, s32 a1, s32 a2, s32 a3, s32 a4) { + 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 = a4; + g_menuDisplayCfg.scrollOffset = s->unk36; + g_menuDisplayCfg.dataPtr = (s32)&s->field_20; + { + return func_801EFBB4(a1, a2, (s32)&func_801E8AB0); + } +} + +s32 func_801E8BD8(ShopMenuState* arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { + MenuDisplayConfig *cfg; + s32 x; + s32 y; + s32 i; + u8 *ptr; + s32 val; + s32 mask; + s32 color; + s32 result; + + cfg = &g_menuDisplayCfg; + + if (!(D_8007809A & 1)) { + y = arg4 + 8; + ptr = D_801EB150; + + for (i = 0; i < 8; i++) { + val = *ptr; + ptr++; + + if (val != 0xff) { + x = arg3 + 9; + mask = val & 0x40; + color = mask != 0; + if (val & 0x80) { + color = 7; + } + arg2 = func_801F0FEC(arg1, arg2, x, y, getLevelCurveData(val & 0x3F), color); + x = arg3 + 0xBD; + result = ((u32)func_801E7E1C(val & 0x3F) * arg0->union30.unk30_s16[0]) / 1000; + arg2 = drawColorByMenuPalette(arg1, arg2, (y << 0x10) | (x & 0xFFFF), result, color); + y += 0xD; + } + } + } + + cfg->iconType = 0x57; + cfg->iconSubType = 0; + cfg->x = arg3; + cfg->y = arg4; + cfg->w = 0xC8; + cfg->h = 0x69; + + arg2 = func_801EF9AC(arg1, arg2, 0x1000, g_menuColor); + return arg2; +} + +INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop2", func_801E8D84); + +/** @brief Return color code: 7 (equal), 3 (a0 > a1), 2 (a0 < a1). */ +s32 func_801E8FF8(s32 a0, s32 a1) { + s32 color = 7; + if (a0 > a1) color = 3; + if (a0 < a1) color = 2; + return color; +} + +void func_801E9020(s32 arg0) { + u8 buf[460]; + u8 charId; + u8 weaponId; + + charId = D_8007C3B8[arg0].unk4; + weaponId = g_gameState.chars[charId].weaponId; + g_gameState.chars[charId].weaponId = arg0; + func_801F537C(charId, buf); + D_801EB260[arg0] = buf[443]; + g_gameState.chars[charId].weaponId = weaponId; +} + +/** + * @brief Initialize all 30 shop item entries. + * + * Calls func_801E9020 for indices 0 through 29. + */ +void func_801E90BC(void) { + s32 i; + for (i = 0; i < 30; i++) { + func_801E9020(i); + } +} + +INCLUDE_ASM("asm/ovl/menushop/nonmatchings/menushop2", func_801E90F8); + +/** + * @brief Configure shop display and render with g_menuDisplayCfg settings. + * + * Calls func_801E90F8 with all parameters, then sets up g_menuDisplayCfg + * display config (icon 0x57, 0x150 x 0x26, x=a3, y=arg5) and calls + * func_801EF9AC to render. + * + * @param a0 Context pointer for func_801E90F8. + * @param a1 Render context passed to func_801EF9AC. + * @param a2 Parameter for func_801E90F8. + * @param a3 X position for display config. + * @param arg4 Y position for display config. + */ +s32 func_801E9554(s32 a0, s32 a1, s32 a2, s32 a3, s32 arg4) { + s32 result; + + result = func_801E90F8(a0, a1, a2, a3, arg4); + g_menuDisplayCfg.iconType = 0x57; + g_menuDisplayCfg.iconSubType = 0; + g_menuDisplayCfg.x = a3; + g_menuDisplayCfg.w = 0x150; + g_menuDisplayCfg.y = arg4; + g_menuDisplayCfg.h = 0x26; + return func_801EF9AC(a1, result, 0x1000, g_menuColor); +} + +s32 func_801E95DC(s32 arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { + if (arg0 != 0) { + arg2 = func_801F0FEC(arg1, arg2, arg3 + 0xC, arg4 + 5, arg0, 7); + } + g_menuDisplayCfg.iconType = 0; + g_menuDisplayCfg.iconSubType = 0; + g_menuDisplayCfg.x = arg3; + g_menuDisplayCfg.w = 0x150; + g_menuDisplayCfg.y = (s16) arg4; + g_menuDisplayCfg.h = 0x14; + return func_801EF9AC(arg1, arg2, 0x1000, g_menuColor); +} + +s32 func_801E9684(ShopMenuState *s, s32 arg1, s32 arg2) { + s32 val1; + s16 idx; + s32 pkt; + s32 param1; + s32 param2; + + pkt = getDisplayListHead(); + func_801F1AFC(); + setMenuColorIntensity(s->unk34); + + if ((s->unk43 & 2) && (s->union30.unk30_s16[1] != 0) && (s->unk34 == 0x1000)) { + arg2 = func_801F4168(arg1, arg2, func_801F6AA4(0x3D), 0xC0, 0x52, s->union30.unk30_s16[1], 0); + } + + param2 = 0x32; + arg2 = func_801E8B60(s, arg1, arg2, 0x1E, param2); + + param1 = 0x18; + param2 = 8; + + if (func_801EFFB8() == 0x17) { + val1 = func_801EFFF0(); + } else { + val1 = -1; + } + + arg2 = func_801E77EC(val1, arg1, arg2, param1, param2); + + param1 = 0x18; + param2 = 0x1D; + arg2 = func_801E95DC((s32)s->unk2C, arg1, arg2, param1, param2); + + if (!(s->unk43 & 2)) { + if (s->unk42 >= 0) { + arg2 = func_801F6FE4(arg1, arg2, 1, 0x1000); + } + + param1 = 0x18; + param2 = 0x47; + idx = s->union30.unk30_s16[1]; + if (idx != 0x1000) { + val1 = D_801FA3C8[idx / 64] * 0x96; + if (val1 < 0) { + val1 += 0xFFF; + } + pkt = func_801E8D84(s, arg1, pkt, param1 - (val1 >> 0xC), param2); + } + + param1 = 0x18; + param2 = 0x47; + arg2 = func_801E8978(arg1, arg2, param1, param2, s->unk38, (u16)s->unk3A); + + param1 = 0xA0; + param2 = 0x47; + arg2 = func_801E8BD8(s, arg1, arg2, param1, param2); + + param1 = 0x18; + param2 = 0xB2; + pkt = func_801E9554((s32)s, arg1, pkt, param1, param2); + } + + func_801F1B10(); + storeGpuPacket(pkt); + return arg2; +} + +void func_801E9900(void) { + u8 buffer[460]; + ShopMenuState* s; + s32 i; + u8* ptr; + + s = (ShopMenuState *)func_801F179C((s32) func_801E81A4, (s32) &func_801E9684); + func_801F1D2C(0, "mwepon.bin", (s32) D_801E9BA0); + func_801F1D2C(0, "mwepon.msg", (s32) D_801E9D2C); + func_801F0948(0); + func_801E90BC(); + if (s != NULL) { + s->gil = func_801E5D28(); + s->unk38 = func_80036EC0(); + s->unk3A = func_801E8058((s32) s->gil); + if (func_801EFFB8() == 0x17) { + s->unk38 = (u16) s->unk3A; + } + s->union3C.unk3C_s8[2] = popcount(s->unk38); + func_801E7F4C(findNthSetBit(s->unk38, 0), (s32) s->gil); + s->union30.unk30_s16[0] = 0x3E8; + s->union30.unk30_s16[1] = 0x1000; + s->unk42 = -1; + if (s->unk38 != 0) { + s->unk43 = 0; + s->unk2C = (u8 *)func_801F6AA4(0x45); + } else { + s->unk43 = 2; + s->unk2C = NULL; + } + if (func_801F72B4() & 1) { + s->union30.unk30_s16[0] = 0x2EE; + } + + for (i = 0, ptr = &s->unk44; i < 8; i++) { + if (((s32) s->unk38 >> i) & 1) { + func_801F537C(i, buffer); + *ptr = buffer[442]; + ptr++; + } + } + + func_801E81A4(s); + } +}