byuu wrote:
I have a hard time believing Hudson was worried about people pirating Same Game character data packs by reprogramming BS-X packs. Especially given the game shipped with one, and the other only had a release of ~100 copies. And then no downloadable content was ever released anyway.
After a bit of disassembling, I've figured it out, and my second guess was more or less right. I'm not sure if it's intentional protection or not, but Same Game
does treat mask ROM and Flash ROM data packs differently, with the result that the mask ROM data packs won't work if you emulate them as Flash ROM (or if you copy them byte-for-byte onto a Flash pack and run it on hardware)
The mask ROM data packs contain a fake Flash chip detection block at offset $FF00:
Code:
0000ff00 4d 00 50 00 00 00 79 00 00 00 00 00 00 00 00 00 |M.P...y.........|
Notice that the fake info has a type code of "7". BS-X Flash packs all have a type of either 1, 2, 3 or 4 (each with a slightly different protocol).
Same Game reads the chip detection block and copies it to RAM at $0590-$0599. If the data pack responds to writes, it'll be the actual Flash chip info; if the data pack ignores writes, it'll be the fake info from the ROM. Then it validates the info, extracts the "type" and "size" fields, converts the size from logarithmic to linear units of 128KB, and stores the converted size and type at $058D and $058E respectively:
Code:
80/AD12: 9C 8D 05 stz $058D
80/AD15: 9C 8E 05 stz $058E
80/AD18: 22 6E AD 80 jsl l80AD6E ; read Flash info block
80/AD1C: 90 3F bcc aAD5D ; bail if above routine timed out (can only happen with a defective Flash chip)
80/AD1E: AD 90 05 lda $0590
80/AD21: C9 4D cmp #$4D
80/AD23: D0 38 bne aAD5D ; first byte must be "M"
80/AD25: AD 91 05 lda $0591
80/AD28: C9 50 cmp #$50
80/AD2A: D0 31 bne aAD5D ; second byte must be "P"
80/AD2C: AD 92 05 lda $0592
80/AD2F: 29 81 and #$81
80/AD31: D0 2A bne aAD5D ; third byte bits 7 and 0 must be clear
80/AD33: AD 93 05 lda $0593
80/AD36: 4A lsr
80/AD37: 4A lsr
80/AD38: 4A lsr
80/AD39: 4A lsr ; extract type
80/AD3A: F0 21 beq aAD5D
80/AD3C: C9 05 cmp #$05
80/AD3E: 90 04 bcc aAD44
80/AD40: C9 07 cmp #$07
80/AD42: D0 19 bne aAD5D ; type must be 1, 2, 3, 4 or 7
aAD44:
80/AD44: 8D 8E 05 sta $058E
80/AD47: 22 EF AD 80 jsl l80ADEF ; terminate status mode
80/AD4B: AD 93 05 lda $0593
80/AD4E: 29 0F and #$0F ; extract size
80/AD50: AA tax
80/AD51: BF 5E AD 80 lda f:FlashSizeLUT,x ; linearize size
80/AD55: 8D 8D 05 sta $058D
80/AD58: D0 03 bne aAD5D
80/AD5A: 9C 8E 05 stz $058E ; looked-up size must be nonzero
aAD5D:
80/AD5D: 6B rtl
FlashSizeLUT:
80/AD5E: 00 00 00 00 00 00 00 01 02 04 08 10 00 00 00 00
tl;dr version: after this routine, $058D contains the size of the data pack in units of 128KB, and $058E contains the Flash chip type (1-4 or 7). If anything is wrong with the chip info, $058D and $058E are both set to 0 and the game will treat the slot as empty.
Some time later, the following routine runs (which, amusingly, has to convert the data pack size
back to logarithmic representation before using it as an index into another LUT):
Code:
80/AB35: AD 8E 05 lda $058E
80/AB38: C9 07 cmp #$07
80/AB3A: D0 15 bne NotMaskROM
80/AB3C: 20 53 AB jsr LogarithmizeFlashSize
80/AB3F: C2 20 rep #$20
80/AB41: BD 62 AB lda FakeBlockMapLUT,x
80/AB44: 8D 2C 05 sta $052C
80/AB47: BD 64 AB lda FakeBlockMapLUT+2,x
80/AB4A: 8D 2E 05 sta $052E
80/AB4D: E2 20 sep #$20
80/AB4F: 38 sec
80/AB50: 60 rts
NotMaskROM:
80/AB51: 18 clc
80/AB52: 60 rts
LogarithmizeFlashSize:
80/AB53: A2 00 ldx #$00
80/AB55: AD 8D 05 lda $058D
aAB58:
80/AB58: 4A lsr
80/AB59: F0 06 beq aAB61
80/AB5B: E8 inx
80/AB5C: E8 inx
80/AB5D: E8 inx
80/AB5E: E8 inx
80/AB5F: 80 F7 bra aAB58
aAB61:
80/AB61: 60 rts
FakeBlockMapLUT:
80/AB62: 01 00 00 00 .dword $00000001
80/AB66: 03 00 00 00 .dword $00000003
80/AB6A: 0F 00 00 00 .dword $0000000F
80/AB6E: FF 00 00 00 .dword $000000FF
80/AB72: FF FF 00 00 .dword $0000FFFF
80/AB76: FF FF FF FF .dword $FFFFFFFF
This routine stores a fake block map at $052C indicating that whatever size the data pack is, it's completely occupied by a single file--but only if the data pack is type 7 (i.e. mask ROM). If the data pack isn't type 7, a different routine ends up loading $052C with the
actual block map from offset $FFD0 of the data pack (see
here).
If you look at offset $FFD0 in the mask ROM data pack, it's a bunch of zeroes. So if the above override routine doesn't override it, the game thinks that the data pack is empty. And that's why the mask ROM data packs won't work if copied to or emulated as Flash ROM. To make them work, just change the byte at offset $FFD0 in the .bs file from $00 to $0F (tested and confirmed to work with both data packs).
By the way, the mask ROM data pack for SD Gundam G Next contains an identical fake Flash chip detection block, only it's at offset $7F00 rather than $FF00.