Remote linking to avatars
Do not link to Suikosource images on forums, webpages, or anyplace else. If you wish to use the images, place them in your own webspace. We do not allow you to use them from our server.
An explanation of the notorious Recipes Bug.
Recipes Bug
Ever since the initial release of Suikoden II, people have had trouble acquiring all of the 40 recipes available within the game. In particular, Recipe #34 is well-known for becoming unavailable, unless the player is very careful. It has variously been rumored that only the yellow Doremi Elf drops it, and only very rarely; that getting Recipe #12 first will somehow block it; that handing in certain recipes to Hai Yo prevents it from dropping; that having certain recipes in your bag or storage is the cause; and there have been many other similar versions on the theme. This guide will attempt to (finally) explain the true cause of the bug, in terms that a layman can understand.

The Problem
In the simplest terms possible, the problem is Hai Yo. You can happily receive every "impossible" recipe in the game, if you just play without ever giving him any of the recipes you acquire. More specifically, the problem is a programming error in the after-battle module.

Every enemy in the game has space for three "drops", items that they may drop after being killed. These drops all have a probability from 1/255 to 254/255—it should be 255/255, but that's another bug. Enemies that drop a Recipe item, are only allowed to drop it once, but it will always be possible for the Recipe drop to trigger. It isn't removed or replaced; receipt of the item is simply prevented after the fact by checking to see if it has been dropped previously. Apparently to save space in the Playstation's precious RAM, Konami decided not to flag the occurrence of a drop, and instead opted to search for the item everywhere a player might put it. That includes the party's bag, storage, and Hai Yo's "cookbook".

The searches through various inventories actually work correctly. The bug mostly manifests when Hai Yo has recipes. However, the error in the check can also cause data unrelated to recipes to be examined in some cases. Luckily for Konami, this mostly occurs for recipes given as part of an event, like a Cook-off, or found in towns, chests, or shops. Since the after-battle module is not used in those cases, the bug has no opportunity to ruin it for you.

Hai Yo's Treachery
This is not truly Hai Yo's fault at all. His cookbook is really just a series of bit-flags that indicate whether or not he has been given a recipe, but when the after battle module decides which flag to check, it computes the position horribly wrong.

Picture it like this: You need to check whether there is mail for a particular unit in a post office. The office has five buildings, numbered 0-4, and each building has 8 mailboxes numbered 0-7. When a box contains mail, a display on the front will be lit. For simplicity's sake, people use numbers 1-40 to address these boxes. So to everyone else, mail is in P.O. Box 1, but to you and your co-workers it's in building 0, box 0. And P.O. Box 40 would be building 4, box 7. When a customer calls in, and gives his P.O. Box number, you subtract 1 from it, and divide by 8. The quotient is the building number, and the remainder is the box number

Box 1 : (1 - 1)/8 = 0, remainder 0. Building 0, box 0.
Box 2 : (2 - 1)/8 = 0, remainder 1. Building 0, box 1.
Box 34: (34- 1)/8 = 4, remainder 1. Building 4, box 1.

Simple enough, right? This is essentially what the after-battle module should do, in order to check whether or not Hai Yo has a particular recipe. The only difference is that it needs to go to a byte instead of a building, and look at a bit instead of a box. Unfortunately, the numbers are computed wrong. To start with, Konami determines the recipe number from the item digit, and never subtracts 1 from it as above. Even if everything else was correct, they would examine position (4,2) for Recipe #34, which would be the flag for Recipe #35. Every check would be one bit (box) off, and the last recipe would be checked against postion (5,0), which is data that is not related to Hai Yo's cookbook. The error is further compounded when they flip the position values. So for Recipe #34, they incorrectly determine (4,2), and then check (2,4) for good measure. Those of you who are good at math probably already know that that positon corresponds to Recipe #21. The table below shows what byte and bit are checked for each Recipe, and what the flag at that position actually indicates. Thanks to the swapping of parameters, 3 bytes unrelated to Hai Yo's cookbook (bytes 5-7) can be examined. These appear to be event flags for other purposes.

In reality, the bit number should ascend 0-7, and the byte number 0-4, increasing by 1 each time the bit number maxes out. So Recipe #9 should be byte 1, bit 0; Recipe #17 should be byte 2, bit 0, and so on.

AcquiredCheck Recipe Flag At
RecipeHow?ByteBitActual Recipe
Recipe #1
(Tomago-Yaki)
(Event) Hai Yo joins10Recipe #9
Recipe #2
(Tomato Soup)
(Event) Cook-off 120Recipe #17
Recipe #3
(Ohitashi)
(Speak) Huan in Muse30Recipe #25
Recipe #4
(Salad)
(Event) Hai Yo joins40Recipe #33
Recipe #5
(Gyoza)
(Speak) Cook in Coronet Inn50Unknown Data
Recipe #6
(Chowder)
(Event) Hai Yo joins60Unknown Data
Recipe #7
(BBQ Meat Bun)
(Event) Hai Yo joins70Unknown Data
Recipe #8
(Buttered Clams)
(Event) Cook-off 201Recipe #2
Recipe #9
(Fish Fry)
(Search) Kuskus bookshelf11Recipe #10
Recipe #10
(Ice Cream)
(Event) Hai Yo joins21Recipe #18
Recipe #11
(Quiche)
(Rare Find) Kuskus Item shop31Recipe #26
Recipe #12
(Sandwich)
(Drop) Eagle Man, Kobold Forest41Recipe #34
Recipe #13
(Meat Pie)
(Speak) Cook in Kobold Village Chief's tent51Unknown Data
Recipe #14
(Simmered Fish)
(Speak) Cook in Radat Tavern61Unknown Data
Recipe #15
(Fried Fish Balls)
(Event) Hai Yo joins71Unknown Data
Recipe #16
(Sunomono)
(Search) Lakewest, Shelf in Taki's house02Recipe #3
Recipe #17
(Cake)
(Event) Cook-off 312Recipe #11
Recipe #18
(Croquettes)
(Rare Find) Radat Item Shop22Recipe #19
Recipe #19
(Pasta)
(Event) Cook-off 432Recipe #27
Recipe #20
(Tempura)
(Speak) Cook in Greenhill Inn42Recipe #35
Recipe #21
(Grilled Fish)
(Drop) Land Sharks in Two River Sewers52Unknown Data
Recipe #22
(Gratin)
(Event) Cook-off 662Unknown Data
Recipe #23
(Rice Omelet)
(Rare Find) Two River Item Shop72Unknown Data
Recipe #24
(Fried Rice)
(Event) Cook-off 503Recipe #4
Recipe #25
(Pizza)
(Rare Find) Greenhill Item Shop13Recipe #12
Recipe #26
(Teriyaki)
(Rare Find) Highway Village Item Shop23Recipe #20
Recipe #27
(Tonkatsu)
(Event) Cook-off 833Recipe #28
Recipe #28
(Curry Rice)
(Rare Find) Gregminster Item shop43Recipe #36
Recipe #29
(Grilled Beef)
(Drop) Zombie Slug in Matilda Path53Unknown Data
Recipe #30
(Ramen)
(Event) Cook-off 763Unknown Data
Recipe #31
(Hamburger)
(Rare Find) Kobold Village Item Shop73Unknown Data
Recipe #32
(Obento)
(Speak) Kent's mother in Highway Village04Recipe #5
Recipe #33
(Sushi)
(Event) Cook-off 1014Recipe #13
Recipe #34
(Japanese Stew)
(Drop) DoReMi Elves in Greenhill Forest Path24Recipe #21
Recipe #35
(Full Course)
(Rare Find) Muse Item shop34Recipe #29
Recipe #36
(Ghengis Khan)
(Drop) Highland Soldiers in Rockaxe44Recipe #37
Recipe #37
(Steak)
(Search) Gorudo's room in Rockaxe54Unknown Data
Recipe #38
(Sashimi Combo)
(Event) Cook-off 1164Unknown Data
Recipe #39
(Special Stew)
(Speak) Gremio in Gregminster74Unknown Data
Recipe #40
(Kaiseki Dinner)
(Event) Cook-off 1205Recipe #6

Dropped recipes are indicated in red. They are the only items affected by the bug in the after-battle code. The remaining recipes have been included to obviate the pattern. It is possible that there are other bugs, that might affect recipes acquired through Rare Finds, etc, but no reports of such an issue are known at the time of writing.

As you can see, Recipe #34 becomes impossible to find if Recipe #21 has been handed in; Recipe #12 becomes impossible to find if Recipe #34 has been handed in; and Recipe #36 becomes impossible to find when Recipe #37 has been handed in. Recipe #36 is an interesting case, because it is the last dropped recipe available. If you are following a guide, and avoided handing in any dropped recipes until after you got #36, it becomes possible to get duplicate copies of it when you go back to Rockaxe to claim Recipe #37. It is also possible to get duplicates of the other dropped recipes. For example, if you hand in Recipe #34 and don't hand in #21, you will be able to get #34 again. It will be absent from your bag and storage, and the wrong flag will be checked, so the game will believe you never got it.

Most unfortunately, Recipe #21 and Recipe #29 are checked against data that serves another purpose. It is possible, though it seems unlikely, that those recipes can become inaccessible for other reasons. It is unknown what purpose those variables serve. Luckily, in watching the data throughout play, it appears that byte remains zero for much of the game, and the bits in question remain unset at least until after Muse is liberated. That means you can get these recipes without issue, even long after they first become available. It also means you can get duplicates of them for much, if not all, of the game.

Fixing the Issue
The code must be modified so that it subtracts proper values when initially computing the Recipe number from the item digit, and then the bit and byte parameters must be used correctly. A patch file is available for the North American version (need link), and can be applied to an ISO or BIN of the game disc. Additionally, the fix is small, and can be applied with GameShark Codes. Since the flags are set properly, it is only necessary to use the codes when hunting one of the affected recipes.

GameShark Codes
Fix Recipe Bug 1 (25-40)
(North American Version)

D002DA20 FFC8
8002DA34 FFE0
D002DA20 FFC8
8002DA82 0274
D002DA20 FFC8
8002DA9E 0243

Fix Recipe Bug 2 (1-24)
(North American Version)

D002DA20 FFC8
8002D998 FFD5
D002DA20 FFC8
8002D9E6 0274
D002DA20 FFC8
8002DA02 0242

Technical Information
The relevant code resides in /CDROM/150_BPRG/BUFF0/BP0_AFT.BIN on the Suikoden II disc. Below is the section of the routine that checks the flags for Recipes 25-40 (item type 0). The other Recipes have a similar check, but are a different item type. The logic is essentially the same, but uses a different subtrahend to check if the item being dropped is actually a recipe.
RAM:8002DA18                 bnez    $a1, loc_8002DAB0    <- Check the item type for zero.
RAM:8002DA1C                 move    $v0, $0
RAM:8002DA20                 addiu   $v0, $a0, 0xFFC8     <-Essentially chop out the bottom of type-zero items by adding -0x38
RAM:8002DA24                 andi    $v0, 0xFF
RAM:8002DA28                 sltiu   $v0, 0x10            <- If the result is a value less than 0x10, item is one of the recipes.
RAM:8002DA2C                 beqz    $v0, loc_8002DAAC    <- Get out if it is not a recipe.
RAM:8002DA30                 andi    $v0, $v1, 0xFF       <- Clear out the high-order bits of the item index/digit.
RAM:8002DA34                 addiu   $a1, $v0, 0xFFE1     <- Add -0x1F (-31)...this gives the recipe #, but it is wrong.  It should be -32.
RAM:8002DA38                 bgez    $a1, loc_8002DA44    <- This should always be true, given previous logic.
RAM:8002DA3C                 move    $v1, $a1
RAM:8002DA40                 addiu   $v1, $v0, 0xFFE8
RAM:8002DA44
RAM:8002DA44 loc_8002DA44:
RAM:8002DA44                 sra     $s2, $v1, 3          <- Get the byte offset.  (Laymen: shifting right 3 = integer division by 8)
RAM:8002DA48                 move    $v1, $s2             <- Save the byte offset.
RAM:8002DA4C                 sll     $v0, $v1, 3          <- This line and...
RAM:8002DA50                 subu    $s2, $a1, $v0        <- This line establish the bit number within the byte (remainder).
RAM:8002DA54                 move    $s3, $v1             <- Save the byte number.
RAM:8002DA58                 la      $s1, sub_800D4CBC    <- The next few lines are all debug prints.
RAM:8002DA60                 la      $a0, aBitValDNoDShif
RAM:8002DA68                 move    $a2, $s2
RAM:8002DA6C                 move    $a3, $s3
RAM:8002DA70                 lui     $s0, 0x8004
RAM:8002DA74                 jalr    $s1
RAM:8002DA78                 sw      $s1, off_8003AEF0
RAM:8002DA7C                 sw      $s1, off_8003AEF0
RAM:8002DA80                 addu    $s0, $s2, $s4        <-Adds the bit number to the flag address.  Should be byte number.
RAM:8002DA84                 lui     $a0, 0x8003          <-More debug print screen crap coming up.
RAM:8002DA88                 lbu     $a1, 0x1E6($s0)      <-Load the flag byte for printing.
RAM:8002DA8C                 jalr    $s1
RAM:8002DA90                 la      $a0, aFoodFlg8b
RAM:8002DA94                 lbu     $v1, 0x1E6($s0)      <-Load flag byte so bit within can be checked, this address will be wrong.
RAM:8002DA98                 nop
RAM:8002DA9C                 srav    $v1, $s3             <-Shift the flags by the byte number.  Should be bit number, but even that is wrong.
RAM:8002DAA0                 andi    $v1, 1               <-Clear all bits except the flag bit we want.
RAM:8002DAA4                 bnez    $v1, loc_8002DAB0    <-Go to exit if found, return the 1 set below.
RAM:8002DAA8                 li      $v0, 1
RAM:8002DAAC
RAM:8002DAAC loc_8002DAAC:
RAM:8002DAAC
RAM:8002DAAC                 move    $v0, $0              <-Otherwise, return not found (i.e., you can get the recipe in battle).