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.
When copying Suikoden I data, imported text can be magled badly, most notably the Hero's name.
McDohl Name and Castle Name Bug
Bug Details
System(s) Playstation,
PSN (POPS)
Bug TypeTranslation
Region Introduced
Patch Version2.01.065b

Regions Affected
This was one of the first bugs discovered in Suikoden II. When you load save data from Suikoden, you get certain bonuses, including a side quest that lets you add the hero from the first game to your party. Anyone loading this data will most likely be disappointed to find that the name is horribly mangled, containing some combination of the name they used and characters from the string "McDohl".

This bug was not present in the Japanese version, because the character sets are entirely different. It was fixed in the European versions.

Cause
During the load, the game must copy and re-encode the name, since the original used a different character encoding. By default, the destination string allows for up to 7 bytes (8 including the terminating null), and contains "McDohl". During the copy, a coding error results in all lowercase letters being bypassed, and the routine neither copies nor inserts a terminating null. The result is that the name contains any capital letters from the player's chosen name, and whatever characters from the default string that were left unmodified. Famously, the result is "TcDohl", as the Suikoden novelization gives the protagonist the name "Tir".

Below is the routine responsible, with a few comments. They have to make a number of adjustments, because of the odd ways in which they modified the character set from game to game.

RAM:80110E0C strcpyS1toS2:                            # CODE XREF: RAM:8010F92Cp
RAM:80110E0C                                          # RAM:8010F944p ...
RAM:80110E0C                 blez    $a2, locret_80110E70
RAM:80110E10                 move    $a3, $0
RAM:80110E14
RAM:80110E14 loc_80110E14:                            # CODE XREF: strcpyS1toS2+5Cj
RAM:80110E14                 addu    $v0, $a0, $a3    # This whole thing fails to take terminating nulls into account.
RAM:80110E18                 lbu     $v1, 0($v0)
RAM:80110E1C                 nop
RAM:80110E20                 addiu   $v0, $v1, 0xFFF0
RAM:80110E24                 sltiu   $v0, 0x1B        # Check for lower case.
RAM:80110E28                 bnez    $v0, loc_80110E60  # #Skip it, if lowercase.  Oops.
RAM:80110E2C                 addiu   $v0, $v1, 0xFFD5
RAM:80110E30                 sltiu   $v0, 0x1A
RAM:80110E34                 bnez    $v0, loc_80110E5C
RAM:80110E38                 addiu   $v0, $v1, 0x10
RAM:80110E3C                 addiu   $v0, $v1, 0xFFBB
RAM:80110E40                 sltiu   $v0, 0x13
RAM:80110E44                 bnez    $v0, loc_80110E5C
RAM:80110E48                 addiu   $v0, $v1, 0x53
RAM:80110E4C                 addiu   $v0, $v1, 0xFFA8
RAM:80110E50                 sltiu   $v0, 0x15
RAM:80110E54                 beqz    $v0, loc_80110E60
RAM:80110E58                 addiu   $v0, $v1, 0x10
RAM:80110E5C
RAM:80110E5C loc_80110E5C:                            # CODE XREF: strcpyS1toS2+28j
RAM:80110E5C                                          # strcpyS1toS2+38j
RAM:80110E5C                 sb      $v0, 0($a1)
RAM:80110E60
RAM:80110E60 loc_80110E60:                            # CODE XREF: strcpyS1toS2+1Cj
RAM:80110E60                                          # strcpyS1toS2+48j
RAM:80110E60                 addiu   $a3, 1
RAM:80110E64                 slt     $v0, $a3, $a2
RAM:80110E68                 bnez    $v0, loc_80110E14
RAM:80110E6C                 addiu   $a1, 1
RAM:80110E70
RAM:80110E70 locret_80110E70:                         # CODE XREF: strcpyS1toS2j
RAM:80110E70                 jr      $ra
RAM:80110E74                 nop

Essentially, they branch 1 operation too far after determining that the character is lowercase. The problem that remains is the lack of support for terminating nulls.

Fix
Ideally, the destination string would be initialized to binary zeroes before even attempting to copy. Unfortunately, the code is pretty tight here, and cannot be easily modified to make a function call. In the end, the best solution was to insure that the terminating nulls from the Suikoden string are copied like any other character. Even that required rewriting the entire routine, so that the necessary operations could be safely inserted.

The fix eliminates the check on the destination pointer. This would be potentially dangerous, except for the fact that this code is only called from three places in one module, and in all cases the destination is a non-zero address.