The Scroll Shop, or Scribe, can turn your spare rune crystals into spell scrolls for a fee. It performs this function admirably. The bug with the shop is that it displays the names and descriptions of mystery items, such as vases, paintings, and ornaments in its menu. Ordinarily, these items can only be identified by an appraiser at a cost of a few hundred potch.
Affected Versions
All versions are affected by this bug.
Cause
The Scroll Shop is handled by the module
/CDROM/140_HONP/FUDAZUK.BIN. It seems that whoever was responsible for coding it didn't get the memo covering changes in handling item data, or at least it wasn't updated along with all the other shops. It unintentionally identifies antiques because it uses routines that cannot under any circumstances handle unidentified items. The functions it calls will always return data for the actual item, instead of generalized data.
The main executable contains two functions that are used for getting item data. The first is at 0x80070348. It takes an address to an array of item data, the item type, and the actual item number as arguments. The reason for needing that first pointer is an open question. Most of the shop modules contain copies of the item data, and all of them have code that calls this function, passing a pointer to their own copy of the item data. This function does not take into account the possibility of unidentified items. It expects to receive a type from 1-7, with no quantity or unidentified flag included.
What is strange is that passing a zero, instead of a pointer to a "local" array of item data causes a more global set of data to be used. Why two copies of this data is desirable is not known for certain. It appears that the "global" data could have been a late addition, and Konami initially placed the array only in modules that needed it. This may have been a space-saving measure that turned into more trouble than it was worth.
The second function is at 0x80088D74. This function takes a type/quantity (including the unidentified flag) and an item ID. If the item is unidentified, it returns a dummy entry for the appropriate type of antique. When the item is identified, it actually calls the function previously described with a zero pointer. So it uses the global array by default.
Most of the shops contain two sets of routines. One that calls only the 0x80070348 function, and refers to the local item data. These routines appear to be dead code for the most part. Routines in the second set all call the 0x80088D74 function. For some reason, the Scroll Shop only includes the first set of routines, and no references at all exist for the more appropriate function.
RAM:80111480 li $s7, 0x8008DB48
RAM:80111488 li $s6, 0x80070348
RAM:80111490 li $s5, 0x8008D390
RAM:80111498 li $fp, 1
RAM:8011149C li $s4, 0x8008E024
RAM:801114A4
RAM:801114A4 loc_801114A4: # CODE XREF: sub_801113AC+1B8j
RAM:801114A4 lbu $v1, 0x17($s2)
RAM:801114A8 nop
RAM:801114AC addiu $v1, 1
RAM:801114B0 sll $v0, $v1, 3
RAM:801114B4 subu $v0, $v1
RAM:801114B8 slt $v0, $s0, $v0
RAM:801114BC beqz $v0, loc_8011156C
RAM:801114C0 move $a1, $0
RAM:801114C4 lw $a0, 0x1C4($s2)
RAM:801114C8 move $a2, $s1
RAM:801114CC jalr $s7
RAM:801114D0 sw $s7, -0x8AC($s3)
RAM:801114D4 li $v1, 0x80061000
RAM:801114DC la $a0, unk_80114A44
RAM:801114E4 sll $v0, $s0, 1
RAM:801114E8 addu $v0, $v1
RAM:801114EC li $v1, 0x8000
RAM:801114F0 addu $v0, $v1
RAM:801114F4 sw $s6, -0x8AC($s3)
RAM:801114F8 lbu $a1, 0x14AF($v0)
RAM:801114FC lbu $a2, 0x14AE($v0)
RAM:80111500 andi $a1, 0x70
RAM:80111504 jalr $s6
RAM:80111508 srl $a1, 4
The above is a snippet of code from the Scroll Shop. When it needs to retrieve the item name and description for display, it calls 0x80070348, which cannot return things like "? vase".
Fix
The routines are in the main executable, and thus are always present. Both functions return references to the same type of data structure, and the only difference between calling them is that the better routine doesn't require us to supply the location of the item array. A fix can be accomplished by simply changing the code to call 0x80088D74, changing where the type/quantity and id are loaded (eliminating the code that sets up the pointer as a consequence), and removing the code that isolates the type bits.
.psx
.align 4
.openfile FUDAZUK.BIN, 0x8010DC50
.headersize 0
.org 0x80111480
.area 0x80111510-.
li $s7, 0x8008DB48
li $s6, 0x80088D74 ; replace sub 0x80070348
li $s5, 0x8008D390
li $fp, 1
li $s4, 0x8008E024
lbu $v1, 0x17($s2)
nop
addiu $v1, 1
sll $v0, $v1, 3
subu $v0, $v1
slt $v0, $s0, $v0
beqz $v0, 0x8011156C
move $a1, $0
lw $a0, 0x1C4($s2)
move $a2, $s1
jalr $s7
sw $s7, -0x8AC($s3)
li $v1, 0x80061000
li $a0, 0x80114A44
sll $v0, $s0, 1
addu $v0, $v1
li $v1, 0x8000
addu $v0, $v1
sw $s6, -0x8AC($s3)
lbu $a0, 0x14AF($v0) ; $a0 is type/quantity
lbu $a1, 0x14AE($v0) ; $a1 is item
nop ; no longer need or want the andi
jalr $s6
nop ; or the srl that isolated the type bits
move $a1, $v0
.endarea
.org 0x80111E60
.area 0x80111EE0-.
li $t0, 0x8008DB48
sw $t0, 0x20($sp)
li $fp, 0x80088D74 ; replace sub 0x80070348
li $s7, 0x8008D390
li $s6, 0x8008E024
lbu $v0, 0x16($s2)
nop
beq $s0, $v0, 0x80111F4C
move $a1, $0
andi $s1, $s5, 0xFF
lw $a0, 0x1C4($s2)
lw $t0, 0x20($sp)
move $a2, $s1
jalr $t0
sw $t0, -0x8AC($s4)
li $v1, 0x80069000
li $a0, 0x80114A44
sll $v0, $s0, 1
or $v0, $v1
sw $fp, -0x8AC($s4)
lbu $a0, 0x14AF($v0) ; $a0 is type/quantity
lbu $a1, 0x14AE($v0) ; $a1 is item
nop ; no longer need or want the andi
jalr $fp
nop ; or the srl that isolated the type bits
move $a1, $v0
.endarea
.close
Patch
Find your version on the
Patch Files page.