Table of Contents

Suikoden II Bug: Castle Farm Bug
Affected Versions
Cause
Fix
GameShark Codes
Patch

Suikoden II Bug: Castle Farm Bug


Throughout the game, the player can collect farm animals that can be given to Yuzu in the castle garden. Likewise, plant seeds can be collected and turned in to the gardener, Tony. When these items are handed in, they should be shown wandering the screen, in the case of animals, or growing in the garden, the next time the player returns to the screen. This is what happens as long as you turn in only one of a particular type of item. If, for instance, two Cabbage Seeds are turned in together, only one new set of cabbage plants will be shown.

Affected Versions


All versions are affected by this bug.

Cause


There are several copies of each item type that can be found throughout the game. The caretakers have some of them by default (Tony starts with a Cabbage and Potato Seed), but in general there are three available to find. When you turn in a seed, a count is updated, to track how many have been handed in. After the count is updated, the bit flags for the item are set based on the count. The problem is that when the count is 3, it only sets the bit for the third copy of the item. If you've handed in all three at once, that means it will immediately set the flag for the third set, leaving the other two unset. It would have been simple to set all the flags for a particular item, had Konami not shared space for seeds and animals, and handled the flags as an array of bytes.

Here's how the data breaks down:
Item Counts
Type of Item
Address
Cabbage
0x8006AA2B
Potato
0x8006AA2C
Spinach
0x8006AA2D
Tomato
0x8006AA2E
Chick
0x8006AA2F
Pig
0x8006AA30
Sheep
0x8006AA31
Cow
0x8006AA31

Bit Flags

Position 0
Position 1
Position 2
Position 3
Position 4
Position 5
Position 6
Position 7
0x8006A91C
Cabbage
Cabbage
Cabbage
Potato
Potato
Potato
Spinach
Spinach
0x8006A91D
Spinach
Tomato
Tomato
Tomato
Chick
Chick
Chick
Pig
0x8006A91E
Pig
Pig
Sheep
Sheep
Sheep
Sheep
Cow
Cow
0x8006A91F
Cow
None
None
None
None
None
None
None

The code below is taken from the routine that accepts seeds. It's slightly simpler, owing to the fact that there are only three of each kind of item it has to deal with. Still, it has to span bytes and handle the data in somewhat ugly ways. The snippet is after the counting of seeds in the party's inventory. There is nothing wrong with the way the count is done. All the relevant code is in /CDROM/110_ARK/VK19.BIN.
RAM:8015E198                 lbu     $a0, 0x1A2B($v1)
RAM:8015E19C                 li      $v0, 1
RAM:8015E1A0                 bne     $a0, $v0, loc_8015E1B4
RAM:8015E1A4                 nop
RAM:8015E1A8                 lbu     $v0, 0x191C($v1)
RAM:8015E1AC                 j       loc_8015E1E4
RAM:8015E1B0                 ori     $v0, 1
RAM:8015E1B4  # ---------------------------------------------------------------------------
RAM:8015E1B4
RAM:8015E1B4 loc_8015E1B4:                            # CODE XREF: TakeSeeds+134j
RAM:8015E1B4                 li      $v0, 2
RAM:8015E1B8                 bne     $a0, $v0, loc_8015E1CC
RAM:8015E1BC                 nop
RAM:8015E1C0                 lbu     $v0, 0x191C($v1)
RAM:8015E1C4                 j       loc_8015E1E4
RAM:8015E1C8                 ori     $v0, 2
RAM:8015E1CC  # ---------------------------------------------------------------------------
RAM:8015E1CC
RAM:8015E1CC loc_8015E1CC:                            # CODE XREF: TakeSeeds+14Cj
RAM:8015E1CC                 li      $v0, 3
RAM:8015E1D0                 bne     $a0, $v0, loc_8015E1E8
RAM:8015E1D4                 nop
RAM:8015E1D8                 lbu     $v0, 0x191C($v1)
RAM:8015E1DC                 nop
RAM:8015E1E0                 ori     $v0, 4
RAM:8015E1E4
RAM:8015E1E4 loc_8015E1E4:                            # CODE XREF: TakeSeeds+140j
RAM:8015E1E4                                          # TakeSeeds+158j
RAM:8015E1E4                 sb      $v0, 0x191C($v1)
RAM:8015E1E8
RAM:8015E1E8 loc_8015E1E8:                            # CODE XREF: TakeSeeds+164j
RAM:8015E1E8                 ori     $v1, $s7, 0x8000
RAM:8015E1EC                 lbu     $a0, 0x1A2C($v1)
RAM:8015E1F0                 li      $v0, 1
RAM:8015E1F4                 bne     $a0, $v0, loc_8015E208
RAM:8015E1F8                 nop
RAM:8015E1FC                 lbu     $v0, 0x191C($v1)
RAM:8015E200                 j       loc_8015E238
RAM:8015E204                 ori     $v0, 8
RAM:8015E208  # ---------------------------------------------------------------------------
RAM:8015E208
RAM:8015E208 loc_8015E208:                            # CODE XREF: TakeSeeds+188j
RAM:8015E208                 li      $v0, 2
RAM:8015E20C                 bne     $a0, $v0, loc_8015E220
RAM:8015E210                 nop
RAM:8015E214                 lbu     $v0, 0x191C($v1)
RAM:8015E218                 j       loc_8015E238
RAM:8015E21C                 ori     $v0, 0x10
RAM:8015E220  # ---------------------------------------------------------------------------
RAM:8015E220
RAM:8015E220 loc_8015E220:                            # CODE XREF: TakeSeeds+1A0j
RAM:8015E220                 li      $v0, 3
RAM:8015E224                 bne     $a0, $v0, loc_8015E23C
RAM:8015E228                 nop
RAM:8015E22C                 lbu     $v0, 0x191C($v1)
RAM:8015E230                 nop
RAM:8015E234                 ori     $v0, 0x20
RAM:8015E238
RAM:8015E238 loc_8015E238:                            # CODE XREF: TakeSeeds+194j
RAM:8015E238                                          # TakeSeeds+1ACj
RAM:8015E238                 sb      $v0, 0x191C($v1)
RAM:8015E23C
RAM:8015E23C loc_8015E23C:                            # CODE XREF: TakeSeeds+1B8j
RAM:8015E23C                 ori     $v1, $s7, 0x8000
RAM:8015E240                 lbu     $a0, 0x1A2D($v1)
RAM:8015E244                 li      $v0, 1
RAM:8015E248                 bne     $a0, $v0, loc_8015E264
RAM:8015E24C                 li      $v0, 2
RAM:8015E250                 lbu     $v0, 0x191C($v1)
RAM:8015E254                 nop
RAM:8015E258                 ori     $v0, 0x40
RAM:8015E25C                 j       loc_8015E29C
RAM:8015E260                 sb      $v0, 0x191C($v1)
RAM:8015E264  # ---------------------------------------------------------------------------
RAM:8015E264
RAM:8015E264 loc_8015E264:                            # CODE XREF: TakeSeeds+1DCj
RAM:8015E264                 bne     $a0, $v0, loc_8015E280
RAM:8015E268                 li      $v0, 3
RAM:8015E26C                 lbu     $v0, 0x191C($v1)
RAM:8015E270                 nop
RAM:8015E274                 ori     $v0, 0x80
RAM:8015E278                 j       loc_8015E298
RAM:8015E27C                 sb      $v0, 0x191C($v1)
RAM:8015E280  # ---------------------------------------------------------------------------
RAM:8015E280
RAM:8015E280 loc_8015E280:                            # CODE XREF: TakeSeeds:loc_8015E264j
RAM:8015E280                 bne     $a0, $v0, loc_8015E298
RAM:8015E284                 nop
RAM:8015E288                 lbu     $v0, 0x191D($v1)
RAM:8015E28C                 nop
RAM:8015E290                 ori     $v0, 1
RAM:8015E294                 sb      $v0, 0x191D($v1)
RAM:8015E298
RAM:8015E298 loc_8015E298:                            # CODE XREF: TakeSeeds+20Cj
RAM:8015E298                                          # TakeSeeds:loc_8015E280j
RAM:8015E298                 ori     $v1, $s7, 0x8000
RAM:8015E29C
RAM:8015E29C loc_8015E29C:                            # CODE XREF: TakeSeeds+1F0j
RAM:8015E29C                 lbu     $a0, 0x1A2E($v1)
RAM:8015E2A0                 li      $v0, 1
RAM:8015E2A4                 bne     $a0, $v0, loc_8015E2B8
RAM:8015E2A8                 nop
RAM:8015E2AC                 lbu     $v0, 0x191D($v1)
RAM:8015E2B0                 j       loc_8015E2E8
RAM:8015E2B4                 ori     $v0, 2
RAM:8015E2B8  # ---------------------------------------------------------------------------
RAM:8015E2B8
RAM:8015E2B8 loc_8015E2B8:                            # CODE XREF: TakeSeeds+238j
RAM:8015E2B8                 li      $v0, 2
RAM:8015E2BC                 bne     $a0, $v0, loc_8015E2D0
RAM:8015E2C0                 nop
RAM:8015E2C4                 lbu     $v0, 0x191D($v1)
RAM:8015E2C8                 j       loc_8015E2E8
RAM:8015E2CC                 ori     $v0, 4
RAM:8015E2D0  # ---------------------------------------------------------------------------
RAM:8015E2D0
RAM:8015E2D0 loc_8015E2D0:                            # CODE XREF: TakeSeeds+250j
RAM:8015E2D0                 li      $v0, 3
RAM:8015E2D4                 bne     $a0, $v0, loc_8015E2F0
RAM:8015E2D8                 li      $v0, 1
RAM:8015E2DC                 lbu     $v0, 0x191D($v1)
RAM:8015E2E0                 nop
RAM:8015E2E4                 ori     $v0, 8
RAM:8015E2E8
RAM:8015E2E8 loc_8015E2E8:                            # CODE XREF: TakeSeeds+244j
RAM:8015E2E8                                          # TakeSeeds+25Cj
RAM:8015E2E8                 sb      $v0, 0x191D($v1)
RAM:8015E2EC                 li      $v0, 1

Fix


There are so many different and better ways this could have been handled. It seems like the developer was trying to develop an easily expanded routine, as if he didn't know what the final number of these items would be. Even assuming that, this is bad. It seems like it was coded by someone unaware of how to work with data at the level of bits, which has been sort of a theme with Suikoden II.

There really isn't an easy way to modify the existing routine to work correctly. So it had to be rewritten. This is actually pretty easy to do. The existing routine is huge and bloated, and can be replaced with a routine about a third of the size. Code just needs to be inserted after the inventory count, to replace the setting of flags.
.openfile VK19.BIN, 0x8015DC50
.headersize 0
.org 0x8015E198
; file location will be 0x540
 
; start with cabbage.
cabbage:
    lbu v0, 0x1A2B(v1)                ; cabbage count
    lw s4, 0x191C(v1)                ; flags (seeds and stock)
    li s6, 7                    ; mask base
    li s5, 3                    ; max count  (this will keep)
    subu v0, s5, v0                    ; shift amount
    srlv v0, s6, v0                    ; 0x7 >> (3 - cabbage count)
    or s4, s4, v0                    ; flags |= mask
potato:
    lbu v0, 0x1A2C(v1)                ; count
    li s6, 7                    ; load delay
    subu v0, s5, v0
    srlv v0, s6, v0
    sll v0, v0, 3                    ; finish mask
    or s4, s4, v0
 
spinach:
    lbu v0, 0x1A2D(v1)                ; count
    li s6, 7                    ; load delay
    subu v0, s5, v0
    srlv v0, s6, v0
    sll v0, v0, 6                    ; finish mask
    or s4, s4, v0
tomato:
    lbu v0, 0x1A2E(v1)                ; count
    li s6, 7                    ; load delay
    subu v0, s5, v0
    srlv v0, s6, v0
    sll v0, v0, 9                    ; finish mask
    or s4, s4, v0
store:
    sw s4, 0x191C(v1)
    beq zero, zero, 0x8015E2EC            ; skip the rest
    nop
.close
The above takes the counts, and uses them to construct an appropriate mask that will be used to set the required bits in the game's data. It sets all the bits indicated by the counts, each time the routine is called. In this way, you can turn in none or all the seeds at one time, and the flags will be retained or set as needed. It also treats the entire 32-bit value allocated to the farm flags (both seeds and livestock) as a single variable. Doing this, the code doesn't need to worry about moving ahead bytes. It just has to set the right bits in the data. Since the only modification done to the flags is a bitwise OR, it can never unset anything by accident.

GameShark Codes

A code for this fix would be so long that it could not be entered. It is easier to use existing codes to set the flag, after the bug has been encountered.

Patch

Find your version on the Patch Files page.