Author
|
Topic: At my wit's end - any way to trick a NES game to load from a different bank? (Read 1 times)
|
Kitsune Sniper
Guest
|
|
« on: June 23, 2009, 03:49:10 am » |
|
Well, I give up. Even after a lot of script optimization, there is simply no way I can get the Samurai Pizza Cats scripts to fit back in the game. Using a combination of Klarth's ScriptCrunch and my own frequency analysis of the script (meaning: Choosing large word sections or phrases that repeat a lot and using them as substrings), I can't get the script any smaller than this:
Script size 9122 Bytes Inserted 7255 (Space the game originally used - the other stuff is substrings and substring pointer data.) Script Overflowed 1867 Space Remaining 0
The -original- English script was around 12k bytes.
The big problem with this is that it uses LOTS of two byte codes to control text speed and text pauses. And these byte codes mess up any sort of compression I try to use. Bongo` tried his hand at coding a compression routine for the text, but he said that these byte codes made it very difficult for anything he could make to lower the space used. This was a very long time ago, though.
So the only solution I can think of is to use some empty space elsewhere in the ROM and store the final 1.8k of the script there, somehow tricking the game into loading that particular data bank instead of the normal one. Of course, I know nothing about programming the NES so I'm asking for help with that. This is the last thing I need to do before finishing the game. (Well, that and the title screens, but I can handle those.)
So uh. Yeah. Is such a feat possible? Or do I have to doom this project to cancelation? Can anyone please help me out with this?
|
|
|
|
Lenophis
Guest
|
|
« Reply #1 on: June 23, 2009, 04:25:41 am » |
|
I'm not well versed in the workings of the NES, but this is pretty much what you should do:
Assume all of the pointers are in their original location (this make things easier). Hook the dialogue loading to check a pointer that's in the cut-off point (that text is to be in your new spot).
Now, if you're in the hardwired bank (no, I don't know how to check), it'll be easiest since the expanded dialogue will be in a bank swapped-in. Simply bank swap in (most games that swap will usually be a JSR with an LDA before it, but not always) and load the text (afterwards swap back if necessary). The pointers may need to be adjusted to account for the swapping. Clear as mud?
Now, if you're already in a swapped in bank, it gets mucky. You'll probably have to copy a bunch of code just to load the expanded dialogue.
Given my lack of experience with the NES, there may even be an easier way to do this. I'm going off of what little experience I have, hopefully someone else can chime in that has real experience.
|
|
|
|
KingMike
Guest
|
|
« Reply #2 on: June 23, 2009, 08:55:14 am » |
|
This is an MMC3 game we're talking about.
IIRC, one of $A000-BFFF or $C000-DFFF is fixed, depending on register state, and $E000-FFFF is hard-wired. (thus bankswapping is LDA #$06 : STA $8000 : LDA #$bank_number : STA $8001 to swap into $8000-9FFF, or LDA #$07: (same) for the other swappable bank. Though you may have to consider the possibility of NMI/IRQ occuring during the process. Then again, the game probably has a bankswap routine that does that.)
It might be possible with ASM hacking. Depending on the available space in other banks, it might be necessary to expand the ROM, depending on if there is free space in the fixed bank (there's a block of zeroes near the end of the ROM).
If the script routine is in the fixed bank it might be easier. You (well, somebody, since I don't think Kit knows ASM) could find where the instruction where the pointer table is read, and make it jump to a routine to bankswap if it detects a pointer to the bankswap region of the script. And then swap back if necessary at the end.
If the script routine is in a swappable bank, then it's much harder. At that point, it may be necessary to expand the ROM, and swap to expanded space, read a character, then swap back. Having two-byte control codes doesn't sound like it'd be fun either, since the routine for each code would probably have to be modified.
|
|
|
|
Guadozoku
Guest
|
|
« Reply #3 on: June 23, 2009, 08:57:50 am » |
|
I don't know ASM, but a thought: If those codes just change text speed, why not have all text at a uniform speed that gets changed once (or once per text box) and see if t he text fits then?
|
|
|
|
Kitsune Sniper
Guest
|
|
« Reply #4 on: June 23, 2009, 09:19:57 am » |
|
If the script routine is in a swappable bank, then it's much harder. At that point, it may be necessary to expand the ROM, and swap to expanded space, read a character, then swap back. Having two-byte control codes doesn't sound like it'd be fun either, since the routine for each code would probably have to be modified.
The control codes are like this: $F7XX - Text speed. Once F7 is read, the next byte determines the text speed. $FAXX - Pause. Once FA is read, the next byte determines the length of the pause. $FD - Begin animation (when characters are talking) $FE - End animation (ditto) That's pretty much it when it comes to control codes. I don't know ASM, but a thought: If those codes just change text speed, why not have all text at a uniform speed that gets changed once (or once per text box) and see if t he text fits then?
Because the game has cutscenes (like Ninja Gaiden - hell the game IS a Ninja Gaiden clone) and there's no way to change the length of each particular section. The game just advances to the next scene even if the text isn't done being on the screen. So I have to add pauses and change the speed of the text so it can be read, scene after scene. Someone already changed the game so it uses one byte codes instead of two to call the substrings the game uses to save space. I don't remember who, but it should be in my email.
|
|
|
|
Tauwasser
Guest
|
|
« Reply #5 on: June 23, 2009, 09:40:44 am » |
|
Could you post a full table and a somewhat longer string's hex data? This should contain some control codes.
cYa,
Tauwasser
|
|
|
|
tummai
Guest
|
|
« Reply #6 on: June 23, 2009, 11:12:20 am » |
|
Here's another solution you could try:
Assuming you can fit the excess script in another bank, you could write a little routine to copy it all into SRAM ($6000-$7FFF) on startup. Then the data will always be available to you regardless of your current bank. Update the relevant pointers to point to this data and you're done. This is assuming your game doesn't already use SRAM, or if it does it has about a fourth of it unused (SRAM is about 8000 bytes total).
|
|
|
|
Kitsune Sniper
Guest
|
|
« Reply #7 on: June 23, 2009, 01:53:09 pm » |
|
Here's another solution you could try:
Assuming you can fit the excess script in another bank, you could write a little routine to copy it all into SRAM ($6000-$7FFF) on startup. Then the data will always be available to you regardless of your current bank. Update the relevant pointers to point to this data and you're done. This is assuming your game doesn't already use SRAM, or if it does it has about a fourth of it unused (SRAM is about 8000 bytes total).
The game doesn't use SRAM, it uses passwords. So that data should be available, at least in theory. Tauwasser: This is from the super optimized script: [$F7][$03][$FD][$B3]s, you won[$25] be[line] taking on [$B1][line] alone! The Rescue Team[line] wi[$1D] help you out![$FE][$FA][$05][end page] In stuff that you can understand: [$F7][$03][$FD]Nyankiis, you won't be[line] taking on Ko'on No Kami[line] alone! The Rescue Team[line] will help you out![$FE][$FA][$05][end page] As for the table, it's a mess. I have two tables, one super optimized, and one, uh, not so much.
|
|
|
|
KingMike
Guest
|
|
« Reply #8 on: June 23, 2009, 02:07:50 pm » |
|
SRAM/WRAM, same thing (difference is whether there's a battery). Does the game use $6000-7FFF? Easiest way to check is to use FCEUX and look at that area in the hex editor mid-game.
|
|
|
|
Kitsune Sniper
Guest
|
|
« Reply #9 on: June 23, 2009, 02:21:35 pm » |
|
SRAM/WRAM, same thing (difference is whether there's a battery). Does the game use $6000-7FFF? Easiest way to check is to use FCEUX and look at that area in the hex editor mid-game. It doesn't. All FF.
|
|
|
|
Tauwasser
Guest
|
|
« Reply #10 on: June 23, 2009, 03:35:17 pm » |
|
It would be really good to see the table though it may be ugly, just to see what ranges you use most (bytewise). Only in that way an effective solution can be built (if you have to keep the space constraint that is)
cYa,
Tauwasser
|
|
|
|
Kitsune Sniper
Guest
|
|
« Reply #11 on: June 23, 2009, 04:06:50 pm » |
|
Fine. Don't say I didn't warn you. 01=a 02=b 03=c 04=d 05=e 06=f 07=g 08=h 09=i 0A=j 0B=k 0C=l 0D=m 0E=n 0F=o 10=p 11=q 12=r 13=s 14=t 15=u 16=v 17=w 18=x 19=y 1A=z 1B=. 1C=, 1D=ll 1E='s 1F=' 20= 21=ii 22=il 23=li 24='r 25='t 26='v 28=3 2d=6 34=<note> 39=7 3C=8 55=! 56=? 57=... 5D=B 5E=- 5F=~ 74=ƒz 7A='m 7B='o 7C=4 7D=5 7E=ƒB 7F=* 80=0x 81=1x 82=2x 83=3x 84=4x 85=5x 86=6x 87=7x 88=8x 89=9x 8A=A 8B=B 8C=C 8D=D 8E=E 8F=F 90=G 91=H 92=I 93=J 94=K 95=L 96=M 97=N 98=O 99=P 9A=Q 9B=R 9C=S 9D=T 9E=U 9F=V A0=W A1=X A2=Y A3=Z A4=0 A5=1 A6=2 a7=9 F8=<line> F9=<end page> FF=<end block> B0= Stage B1=Ko<$7B>n no Kami B2=Edoropo<$23>s B3=Nyank<$21> B4=* Fight! Secret Ninja B5= Yattarou B6=<$FD>Another victory in the<line>name of Justice!<$FE> B7=e B8= th B9= BA=ha BB=ou BC=t BD=in BE=s BF=ohoho C0=an C1=to C2=d C3=on C4=ar C5=Omi-c C6=er C7=ea C8=, C9=ee CA=for CB=tim CC=at CD=th CE=oo CF=en D0= s D1=es
B0 through D1 are substrings. Everything else are single letters or squishy tiles. $7F is the Narrator symbol.
|
|
|
|
Tauwasser
Guest
|
|
« Reply #12 on: June 23, 2009, 04:48:48 pm » |
|
I know a compression scheme that might be feasible, however, it would involve some ordering in your table as well as asm hacking skills and some ram to work with. Basically, everything (including squishy stuff, control codes etc.) will have to be ordered according to usage frequency. Then you can actually encode all strings as a nifty bit stream that is devised to save enough space. It's from Pokemon RGBY, too I only have the docu up in German tho. Basically, it would involve blanking out half of each byte (lower half first, then upper half, or vice versa). Then compressing the stuff into two streams. When the rom wants a string, it gets the data, decompresses two streams to ram, or's the streams and points the text routine to that or'd data. It sound much more complicated than it really is, but it should save a lot of space (I just tried with your test sentence by hand with my own special table) and it looked pretty good - though only one sample cannot make a good ratio prediction. cYa, Tauwasser
|
|
|
|
Kitsune Sniper
Guest
|
|
« Reply #13 on: June 23, 2009, 05:06:18 pm » |
|
Can this routine be configured to handle two different scripts? I haven't done the Pizza Cats edit of the script yet, so I have to do that before turning anything over.
The frequency thing is easy to get, ScriptCrunch spits out a list of them when it does its thing.
|
|
|
|
BRPXQZME
Guest
|
|
« Reply #14 on: June 23, 2009, 05:14:41 pm » |
|
I know a compression scheme that might be feasible, however, it would involve some ordering in your table as well as asm hacking skills and some ram to work with. Basically, everything (including squishy stuff, control codes etc.) will have to be ordered according to usage frequency. Then you can actually encode all strings as a nifty bit stream that is devised to save enough space. It's from Pokemon RGBY, too I only have the docu up in German tho. Basically, it would involve blanking out half of each byte (lower half first, then upper half, or vice versa). Then compressing the stuff into two streams. When the rom wants a string, it gets the data, decompresses two streams to ram, or's the streams and points the text routine to that or'd data. It sound much more complicated than it really is, but it should save a lot of space (I just tried with your test sentence by hand with my own special table) and it looked pretty good - though only one sample cannot make a good ratio prediction. cYa, Tauwasser Pretty ingenious, actually. It’s almost like swapping out smaller dictionaries on the fly.
|
|
|
|
|