News: 11 March 2016 - Forum Rules
Current Moderators - DarkSol, KingMike, MathOnNapkins, Azkadellia, Danke

Author Topic: General NES Hacking Questions  (Read 24415 times)

Choppasmith

  • Full Member
  • ***
  • Posts: 110
    • View Profile
Re: General NES Hacking Questions
« Reply #60 on: March 10, 2019, 05:24:29 pm »
Nice!
Remember how we expanded the DW1 ROM by adding 4 new banks just before the final fixed bank? Enix did basically the same thing (except in hardware) when porting the game to English; they had to add 8 banks, but didn't end up using 3 of those.

In bank 5, you've got $17FE7 - $14010 = $3FD7 bytes, and bank 12 ($0C) gives you another $4000 bytes for a total of $7FD7 = 32,727 bytes, which should leave you with about 350 bytes to spare if you managed to get your script down to 32,378 bytes. Is that 32,378 bytes the script size after being converted to DW2's 5/10-bit encoding, or is that the file size when encoded as UTF-8?

Well, as said a few posts ago, I split my script into two parts to determine how much of it would fit. I took the bytes remaining as reported by abcde and subtracted from the total space available and did it again for the second part then combined the values which is how I got the 32,378 bytes. I had done the same thing before and I was getting over 34,000 bytes, so even if my math is somehow off it's still a significant decrease.

Nice!
Remember how we expanded the DW1 ROM by adding 4 new banks just before the final fixed bank? Enix did basically the same thing (except in hardware) when porting the game to English; they had to add 8 banks, but didn't end up using 3 of those.

In bank 5, you've got $17FE7 - $14010 = $3FD7 bytes, and bank 12 ($0C) gives you another $4000 bytes for a total of $7FD7 = 32,727 bytes, which should leave you with about 350 bytes to spare if you managed to get your script down to 32,378 bytes. Is that 32,378 bytes the script size after being converted to DW2's 5/10-bit encoding, or is that the file size when encoded as UTF-8?

If you're still having space issues, don't forget about the single-character dictionary entries - if you're e.g. using "p" more than "w", you could kick "w" into the 10-bit range and move "p" into the 5-bit range to get better compression. For English text, you can probably also replace the "q" entry with "qu".
Yeah that could work. I would assume, in layman's terms, that the first set of entries use less data than the 4 "C" tables? Or does each table use extra bits of data the further you go down? (Edit : forget that I'm dumb)I honestly still don't quite understand how those work, I initially thought it was like a special table where'd you use two bytes to load a dictionary value, and pretty much every word/letter from the dictionary wound be preceded by the corresponding value and then stuff from the first table fills in the rest with no dictionary value needed.

Edit : Just read the Wiki, I guess it works like I said above but in bits, not bytes?
« Last Edit: March 10, 2019, 05:41:47 pm by Choppasmith »

abw

  • Sr. Member
  • ****
  • Posts: 253
    • View Profile
Re: General NES Hacking Questions
« Reply #61 on: March 10, 2019, 06:12:36 pm »
Yeah that could work. I would assume, in layman's terms, that the first set of entries use less data than the 4 "C" tables? Or does each table use extra bits of data the further you go down? (Edit : forget that I'm dumb)I honestly still don't quite understand how those work, I initially thought it was like a special table where'd you use two bytes to load a dictionary value, and pretty much every word/letter from the dictionary wound be preceded by the corresponding value and then stuff from the first table fills in the rest with no dictionary value needed.

Edit : Just read the Wiki, I guess it works like I said above but in bits, not bytes?
Basically, yeah. I had this typed up before I saw your edit, so I'll post it anyway just in case it's still helpful:
--
Sort of, except as far as the game's concerned, the individual tokens are 5 bits each, not 8, and 4 of the entries from the first 5-bit table are used to switch to the corresponding "C" table. You could split that single table file into multiple table files if you wanted to; I just prefer having all the entries in a single table.

You can tell how much space each entry takes up by looking at its left-hand side. In a hexadecimal table, "80" takes up one byte (or 2 nybbles or 8 bits, depending on how you want to think of it), "80FF" takes up two bytes (/4 nybbles/16 bits), and so on; in a binary table, "11011" takes up 5 bits, "1110101111" takes up 10 bits, and so on. This will be an even bigger issue if you decide to tackle DW4, since it uses a Huffman encoding where the binary representation of individual characters ranges between 3 bits (e.g. for "e") and 18 bits (e.g. for "8").

Choppasmith

  • Full Member
  • ***
  • Posts: 110
    • View Profile
Re: General NES Hacking Questions
« Reply #62 on: March 11, 2019, 03:18:44 pm »
Basically, yeah. I had this typed up before I saw your edit, so I'll post it anyway just in case it's still helpful:
--
Sort of, except as far as the game's concerned, the individual tokens are 5 bits each, not 8, and 4 of the entries from the first 5-bit table are used to switch to the corresponding "C" table. You could split that single table file into multiple table files if you wanted to; I just prefer having all the entries in a single table.

You can tell how much space each entry takes up by looking at its left-hand side. In a hexadecimal table, "80" takes up one byte (or 2 nybbles or 8 bits, depending on how you want to think of it), "80FF" takes up two bytes (/4 nybbles/16 bits), and so on; in a binary table, "11011" takes up 5 bits, "1110101111" takes up 10 bits, and so on. This will be an even bigger issue if you decide to tackle DW4, since it uses a Huffman encoding where the binary representation of individual characters ranges between 3 bits (e.g. for "e") and 18 bits (e.g. for "8").

Thanks, once I saw that the programmer's calculator had a binary converter, it just clicked.

Anyway, just from doing the above suggestion I'm down another 400 bytes! And just after some needed editing of the intro I'm now down to 760 bytes over the limit. I was really loose and liberal with my initial script (like gratuitous line breaks, pauses, and spaces), so it should be easy to trim the fat without sacrificing much. Although when I tried dumping with the new second address, I'd get horrible garbage dialog. The intro is only the 8th pointer in bank 5, so I'm not too sure what happened there. I know I have to update the ROM with the new second bank, but I'm not sure why dialog in the beginning wouldn't work properly. Did I miss something (other than updating the second bank?)

A couple other weird issues

Still having trouble with closing single quotes for some reason.



I double checked the ROM and I do indeed have $66 in the dictionary. I also have an entry with "!’" but that has Apostrophes ($67) work fine though

Also



Not sure what's going on here. There's a couple of windows like this with the oddly premature uncalled for line break. I'm assuming the window counts how many bits are used per line as opposed to individual letters/spaces/characters? Any suggestions? Or is it just a weird game programming/abcde limitation?

abw

  • Sr. Member
  • ****
  • Posts: 253
    • View Profile
Re: General NES Hacking Questions
« Reply #63 on: March 11, 2019, 08:03:16 pm »
Hurray! The proper choice of dictionary entries makes a huge difference to the compression ratio. Just make sure you leave the first 5 end tokens and the 4 "C" table switches alone (since the game cares about those) and fill out the rest of the main 5-bit table with your 23 best tokens.

As for your issues, it's kind of hard to say for sure without seeing the files. If dumping the inserted data didn't work but the game still displays it correctly, then something is wrong with your dump script. For the closing single quotes, my next guess would be an error in the dictionary lengths, though a length error would also mess up all the dictionary entries following the one with the wrong length. The random line breaks is definitely strange too, and not an issue I've come across myself; I've definitely inserted longer strings than that without getting inappropriate line breaks. I haven't looked at the code for it, but just based on observation, DW2's line wrapping algorithm seems pretty solid. Any chance it always happens around the words "What" or "is"? If so, that could indicate that you've got a line break instead of a space in one of the dictionary entries - just a guess!

Choppasmith

  • Full Member
  • ***
  • Posts: 110
    • View Profile
Re: General NES Hacking Questions
« Reply #64 on: March 16, 2019, 06:27:57 pm »
I got it! I had used Pointer Tables to extract, edit, and insert the dictionary entries. Yeah, I know it's clunky, but easy for me. It was changing blank spaces (5F) to [no voice] (59). Once I changed it in the ROM it was fine. Similar problem with the closing single quotes and I fixed that too. I was hoping that this could've shrunk the script size, but, alas, it didn't. Oh well, more editing work for me!

Congrats on your Latin translation release, abw! I had looked at the Read Me and you said one of your improvements was editing the main party status windows to show (was this battle or field or both?) full character names instead of just showing the first four letters.

When working on DW1 menus, the thought of doing that for later entries struck me as a "would be nice if possible". You mentioned it was more than a one byte hack. Were there other entries for the window you had to change besides length? Or is it like monster/item/spell/etc names where you had to find that value that affected display length? Btw, I know it'd be easy to tell the magic byte that affects monster name length like DW1, but you did explain how you did it and I have every intention to try myself as part of my learning.

abw

  • Sr. Member
  • ****
  • Posts: 253
    • View Profile
Re: General NES Hacking Questions
« Reply #65 on: March 17, 2019, 05:53:07 pm »
I got it!
Congrats!

Congrats on your Latin translation release, abw! I had looked at the Read Me and you said one of your improvements was editing the main party status windows to show (was this battle or field or both?) full character names instead of just showing the first four letters.

When working on DW1 menus, the thought of doing that for later entries struck me as a "would be nice if possible". You mentioned it was more than a one byte hack. Were there other entries for the window you had to change besides length? Or is it like monster/item/spell/etc names where you had to find that value that affected display length?
Thanks! If you check the screenshots, you'll see that full character names are displayed both in and out of battle; I think I made changes for displaying full names in a total of 37 different menus, which typically also involved widening and repositioning them and sometimes also involved making updates to the menu wiping process; the WEAPON/ARMOR/SHILD/HELMET menus were particularly irritating to deal with in that respect. For the code changes, the basic problem is that DW2 stores the first 4 characters of each hero's name in one spot and the last 4 characters in a completely different spot (probably due to the English version's extra 4 characters being bolted on to the original 4 characters present in the Japanese version), so you can't just find the code that says "read 4 bytes from X" and update one byte to say "read 8 bytes from X", you need to add more code (which needs more space) to read 4 bytes from X and glue them together with 4 bytes from Y.

For the later games in the series, the mini status menu was rearranged into 1 column per hero instead of 1 row per hero, so widening the columns probably wouldn't work out very well due to lack of screen space if everybody has a long name, but you could switch the layout back to rows if you wanted. It's just a question of how much work would be involved.

Btw, I know it'd be easy to tell the magic byte that affects monster name length like DW1, but you did explain how you did it and I have every intention to try myself as part of my learning.
Trace logger is your friend there too!

Choppasmith

  • Full Member
  • ***
  • Posts: 110
    • View Profile
Re: General NES Hacking Questions
« Reply #66 on: April 16, 2019, 03:08:52 pm »
Basck from vacation and ready to tackle this!

Okay, I'm having a strange pointer issue (at least I think it's Pointer related). I got my script (with around 19 bytes to spare!)

When the game loads the intro, instead of loading the line

"This is the royal castle of the kingdom of Moonbrooke." which is Pointer 7, String 14 it loads instead, "‘We shall await thy return most eagerly!’" Which is a couple lines up at String 12. Granted I removed the "Hold Reset" dialog to save space but I double checked and I don't see any missing end-FC marks I might've missed that would cause the issues. What's weird is, in the next pointer, the first two lines load just fine but everything after is mixed up.

Did I mess up the formatting of my atlas.txt somehow? It's right here
http://www.mediafire.com/file/e7kqqhf720plzpf/atlas.txt/file

(note because of the trouble Chicken Knife had with his script mentioned earlier in this thread, I opted to extract the script with comments:off so it's just the new dialog)

I know I read your post, abw, a couple pages back about the ROM flaw that pointers could get mixed up, but I'm not sure I understand what you mean by checking them. In DW1, I could load up the ROM in Windhex, look at the start of the string, the address, then compare it with the pointer, but I'm not sure how I can do that with DW2's compression.

darthvaderx

  • Full Member
  • ***
  • Posts: 135
    • View Profile
Re: General NES Hacking Questions
« Reply #67 on: April 16, 2019, 05:26:13 pm »
Is it possible to run Lua Script in Mesen in the same way as in FCEUX? I've tried to run Metroid in every way but none worked.
« Last Edit: April 17, 2019, 06:20:44 am by darthvaderx »

abw

  • Sr. Member
  • ****
  • Posts: 253
    • View Profile
Re: General NES Hacking Questions
« Reply #68 on: April 16, 2019, 09:56:29 pm »
When the game loads the intro, instead of loading the line

"This is the royal castle of the kingdom of Moonbrooke." which is Pointer 7, String 14 it loads instead, "‘We shall await thy return most eagerly!’" Which is a couple lines up at String 12.
I'm not seeing anything immediately obvious that would cause that kind of error, and a quick test with a version of the original table file tweaked to include your new characters gets me the expected text displayed during the entire intro sequence, so I suspect your problem lies elsewhere. Getting an earlier string from the same pointer makes me think the game might be counting more end tokens than you want it to... what does your table file look like? If you altered any of the first 5 entries, that might explain this behaviour.

I know I read your post, abw, a couple pages back about the ROM flaw that pointers could get mixed up, but I'm not sure I understand what you mean by checking them.
Basically you just pop open the ROM in a hex editor, look at 0xB772-0xB7D0, and check whether any of those pointers point to $BFD6 or $BFD7. If they do, then you'll need to move the auto-jump point (0x17FE7) back a byte or two in order to force the affected pointer to point into the second script bank instead of letting it read non-script data from the first script bank and make a mess of your text.

Choppasmith

  • Full Member
  • ***
  • Posts: 110
    • View Profile
Re: General NES Hacking Questions
« Reply #69 on: April 18, 2019, 04:42:11 pm »
I'm not seeing anything immediately obvious that would cause that kind of error, and a quick test with a version of the original table file tweaked to include your new characters gets me the expected text displayed during the entire intro sequence, so I suspect your problem lies elsewhere. Getting an earlier string from the same pointer makes me think the game might be counting more end tokens than you want it to... what does your table file look like? If you altered any of the first 5 entries, that might explain this behaviour.
Basically you just pop open the ROM in a hex editor, look at 0xB772-0xB7D0, and check whether any of those pointers point to $BFD6 or $BFD7. If they do, then you'll need to move the auto-jump point (0x17FE7) back a byte or two in order to force the affected pointer to point into the second script bank instead of letting it read non-script data from the first script bank and make a mess of your text.

Okay that went better than I hoped!  :laugh:

So, when you suggested the problem (thanks for that, it's easy for me to overlook little things like that), it was indeed something I did with my table/dictionary. I thought I'd swap the standalone [FF] with the standalone Period that was further down. Once I swapped it back, the intro text displayed fine again. Lesson learned. Adding a note in my copy of the table: DO NOT MESS WITH THE FIRST 5 VALUES! Only problem was I went from just being under the limit by about 20 bytes, to being OVER the limit by about 60. So I had to scour the dictionary again to see if there was some way to get the size back down. After adding a space to the comma entry and replacing the unused ; by realizing I had a lot of words and phrases that ended in "eth" and boom that was enough to put me WELL under with about 140 bytes to spare. WOW! I'm ecstatic! I should be able to restore the little bit I had to cut (mostly consists of long gratuitous laughs, pauses, and a post-final boss NPC line).

Anyway when I asked about changing the second text bank at 3FA94, I figured having the second block at $3400F would make it Bank 10 (0A) but when you replied with an explanation of how the bank swapping work, you mentioned Bank 12 and I'm not sure if you were just giving an unrelated example, subtly correcting me, or just made a typo. I'm getting some messed up text by a couple of the Midenhall Castle NPCs using either value (either blank windows or a sparse random letter) so i figured I'll have to look at those pointers, but I want to make sure I'm not doing this with the wrong bank value.

EDIT: These are my values between b772-B7D0

16 90 73 92 7D 92 87 92 91 92 9B 92 A5 92 AF 92 B9 92 02 94 19 95 3E 96 12 98 37 99 12 9C 62 9E 35 A1 0A A4 CE A7 AB AB 5E AF 68 AF 72 AF 7C AF 86 AF 42 B4 37 BB 43 02 B1 07 93 0E 4A 13 FC 17 7D 1B 88 1E 1C 22 54 27 A0 2D 9D 31 16 35 0A 39 DF B4 EB A0 4D BB 17 F4 F9 C3 E2 57 C0 E7 8F 9D

So, nothing wrong there, then?

Edit 2: So am I understating right that my problem is probably between Pointer 32 and 35 which is pointer B7A2 and B7A8 respectively? Since B7A8 points to the new bank? Pointer 32 has a lot of the Midenhall dialog, some of which is displayed properly. Sorry to say, I'm still puzzled here.
« Last Edit: April 18, 2019, 05:14:28 pm by Choppasmith »

abw

  • Sr. Member
  • ****
  • Posts: 253
    • View Profile
Re: General NES Hacking Questions
« Reply #70 on: April 19, 2019, 12:01:43 am »
Okay that went better than I hoped!  :laugh:
[...]
Adding a note in my copy of the table: DO NOT MESS WITH THE FIRST 5 VALUES!
Hurray! To be fair, I did actually mention that a few posts back, but I'm glad you've got it working now!

Anyway when I asked about changing the second text bank at 3FA94, I figured having the second block at $3400F would make it Bank 10 (0A) but when you replied with an explanation of how the bank swapping work, you mentioned Bank 12 and I'm not sure if you were just giving an unrelated example, subtly correcting me, or just made a typo.
Heh, that one I managed to write down correctly for a change :P. Banks $0C (starting at 0x030010), $0D (0x034010), and $0E (0x038010) are DW2's 3 empty banks.

I'm getting some messed up text by a couple of the Midenhall Castle NPCs using either value (either blank windows or a sparse random letter) so i figured I'll have to look at those pointers, but I want to make sure I'm not doing this with the wrong bank value.
Yup, that makes sense. The main script's pointer table actually starts at 0xB762, so you're missing the first 8 pointers, but they're probably fine so I'm not too concerned about that. The pointer table also stops at 0xB7C1, so the other 16 bytes you included here are just the start of the original second script bank's text and aren't really relevant. If you look at the rest of the pointer values, you'll see they keep increasing from $9016 at 0xB772 up to $BB37 at 0xB7A6 and then reset (that'll be the first pointer following the auto-jump) to $0243 at 0xB7A8 and start increasing again up to $390A at 0xB7C0, so those pointer values are all off by $8000 and giving you who knows what from system RAM instead of the data you want from cartridge ROM (as an added bonus, the $2002 PPU register is sensitive to reads, so if you're really lucky, you might also get some other graphical mayhem happening :D). Checking your insert script, I see that you've updated where to jump to when the insertion point reaches 0x17FE7 but didn't update the corresponding header value, which is throwing the pointer value calculations off. Try changing that to #AUTOCMD($17FE7, #HDR($28010)) instead and you should get the right pointer values.

Choppasmith

  • Full Member
  • ***
  • Posts: 110
    • View Profile
Re: General NES Hacking Questions
« Reply #71 on: April 19, 2019, 12:05:26 pm »
It occurred to me just before reading your reply that it was probably the pointer values being off for the new bank (you did post that "formula" for pointer values a while back). Had no idea that was what the HDR command in the script was for. Lesson learned for the future, but yep, that did it, it's working beautifully. Just got to start testing and tweaking and then doing all the menu stuff. Man that was nuts but that's a huge hurdle cleared for me thank you so much! :beer:

Edit: Chicken Knife gave me his atlas.txt so I could compare translations and I realized you have the "pointer formula" right in there. Doh! XP

abw

  • Sr. Member
  • ****
  • Posts: 253
    • View Profile
Re: General NES Hacking Questions
« Reply #72 on: April 20, 2019, 12:51:11 pm »
Lesson learned for the future, but yep, that did it, it's working beautifully. Just got to start testing and tweaking and then doing all the menu stuff. Man that was nuts but that's a huge hurdle cleared for me thank you so much! :beer:
Sweet! Let's hope the testing and tweaking goes well too :).

Choppasmith

  • Full Member
  • ***
  • Posts: 110
    • View Profile
Re: General NES Hacking Questions
« Reply #73 on: April 22, 2019, 01:54:37 pm »
Okay sorry to post in this thread so soon, but I'm genuinely stumped by the plural rules (I foolishly thought it was a bunch of suffixes in a row, kinda like the dictionary)  . With new monster names need a couple of tweaks not covered by the current plural rules

New names

Mummy Boy > Boys
Hunter Mech > Mechs
Madusa > Madusae
Dragonfry > No change
Gigantes > No change (though I'm not 100% sure if a plural is even needed here, I don't think Gigantes are encountered in groups)
Cyclops > No change (actually I just read here that Square-Enix goofed and that the correct plural for a cyclops is "Cyclopes", though I suppose if need be, Cyclopses isn't entirely wrong and off limits)
Technically Man o' War is listed as "Men o' War" as its plural but I figure with the way the system counts from the end and adds to the end Man o' Wars would be just fine

The good news is that the new names also free up the need for a few rules

ch > ches (only the Hunter Mech is used and that's a case where it only needs the s added)
dead > no change
ngo > no change
f > ves (unused anyway)
i > ies
Mouse/mouse > Mice/mice (they're rats now)
rus>rii
sh>shes


I mean I get that there's essentially two parts of that code. One looks at the end letters and then points to other part that removes/adds letters at the end. Even for something like editing the "ngo" if I wanted to change it to "fry" I only see the "n" and "g" to change, I don't see the o

Code: [Select]
; -o pluralization handler: -ngo -> -ngo, -o -> -os
0x01C845|$07:$8835:BD F0 60 LDA $60F0,X ; read second-last letter of monster name
0x01C848|$07:$8838:C9 10    CMP #$10 ; "g"
0x01C84A|$07:$883A:D0 E7    BNE $8823 ; if not -go, append "s"
0x01C84C|$07:$883C:BD EF 60 LDA $60EF,X ; read third-last letter of monster name
0x01C84F|$07:$883F:C9 17    CMP #$17 ; "n"
0x01C851|$07:$8841:D0 E0    BNE $8823 ; if not -ngo, append "s"
0x01C853|$07:$8843:F0 EB    BEQ $8830 ; if -ngo, plural = singular

I guess what really confuses me are the if/if not suffix lines. How do I change THOSE?

Edit: Also doing Menus (no trouble understanding those :) ) but are you sure those pointers you listed on the wiki are right, abw? I'm getting some really broken up strings like

Nevermind, I misread the text start value as 7656 instead of 76E6 which was throwing me off.
« Last Edit: April 22, 2019, 03:22:18 pm by Choppasmith »

abw

  • Sr. Member
  • ****
  • Posts: 253
    • View Profile
Re: General NES Hacking Questions
« Reply #74 on: April 22, 2019, 08:20:23 pm »
Okay sorry to post in this thread so soon, but I'm genuinely stumped by the plural rules (I foolishly thought it was a bunch of suffixes in a row, kinda like the dictionary)  .
Nope, the pluralization rules are implemented as actual code, not just a table of suffixes or similar, so the bad news is you're going to have to roll up your sleeves and dig in to it a bit in order to make your changes. On the other hand, since you're going to be making changes anyway, you're free to make all the changes you want - you've got all the space from 0x01C801 to 0x01C8F3 to do whatever you want, and if that isn't enough space, most of the rest of the bank is empty. When this code starts running, the singular monster name has already been laid out in RAM at $6119 for you, and by the end, you just need to write the plural form to RAM starting at $60F1 and SEC before you RTS. You can use the debugger to set a breakpoint at the start and step through the code to watch exactly what it does.

For -ngo specifically, it works like this:

$87FC: LDA $60F1,X    ; read the final letter of the monster name into A (in this case that's "o", a.k.a. #$18; I think this is the part you missed)
$87FF: CMP #$18       ; among other things, this sets the Z (zero) processor flag based on whether A is #$18 ("o") or not
$8801: BEQ $8835      ; since Z is set, BEQ follows the branch

; at this point we know the last letter was "o", so we'll check the second-last letter
$8835: LDA $60F0,X    ; read the second-last letter the of monster name into A (in this case that's "g", a.k.a. #$10)
$8838: CMP #$10       ; as before, this sets the Z flag based on whether A is #$10 ("g") or not
$883A: BNE $8823      ; since A is #$10, Z is not set and we don't take this branch

; at this point we know the last two letters were "go", so we'll check the third-last letter
$883C: LDA $60EF,X    ; read the third-last letter of the monster name (in this case that's "n", a.k.a. #$17)
$883F: CMP #$17       ; as before, this sets the Z flag based on whether A is #$17 ("n") or not
$8841: BNE $8823      ; since A is #$17, Z is not set and we don't take this branch
$8843: BEQ $8830      ; ... which means we do take this branch
$8830: SEC            ; the calling code cares about whether C is set or not
$8831: RTS            ; and at this point we're done, not having changed a single byte of the monster name

Choppasmith

  • Full Member
  • ***
  • Posts: 110
    • View Profile
Re: General NES Hacking Questions
« Reply #75 on: April 22, 2019, 10:51:19 pm »
Nope, the pluralization rules are implemented as actual code, not just a table of suffixes or similar, so the bad news is you're going to have to roll up your sleeves and dig in to it a bit in order to make your changes. On the other hand, since you're going to be making changes anyway, you're free to make all the changes you want - you've got all the space from 0x01C801 to 0x01C8F3 to do whatever you want, and if that isn't enough space, most of the rest of the bank is empty. When this code starts running, the singular monster name has already been laid out in RAM at $6119 for you, and by the end, you just need to write the plural form to RAM starting at $60F1 and SEC before you RTS. You can use the debugger to set a breakpoint at the start and step through the code to watch exactly what it does.

For -ngo specifically, it works like this:

$87FC: LDA $60F1,X    ; read the final letter of the monster name into A (in this case that's "o", a.k.a. #$18; I think this is the part you missed)
$87FF: CMP #$18       ; among other things, this sets the Z (zero) processor flag based on whether A is #$18 ("o") or not
$8801: BEQ $8835      ; since Z is set, BEQ follows the branch

; at this point we know the last letter was "o", so we'll check the second-last letter
$8835: LDA $60F0,X    ; read the second-last letter the of monster name into A (in this case that's "g", a.k.a. #$10)
$8838: CMP #$10       ; as before, this sets the Z flag based on whether A is #$10 ("g") or not
$883A: BNE $8823      ; since A is #$10, Z is not set and we don't take this branch

; at this point we know the last two letters were "go", so we'll check the third-last letter
$883C: LDA $60EF,X    ; read the third-last letter of the monster name (in this case that's "n", a.k.a. #$17)
$883F: CMP #$17       ; as before, this sets the Z flag based on whether A is #$17 ("n") or not
$8841: BNE $8823      ; since A is #$17, Z is not set and we don't take this branch
$8843: BEQ $8830      ; ... which means we do take this branch
$8830: SEC            ; the calling code cares about whether C is set or not
$8831: RTS            ; and at this point we're done, not having changed a single byte of the monster name


Thanks that explains it a little better. I now have an idea of what to do, I just don't know the commands. I realize fixing "Mechs" would be the easiest thing, I would just have to remove the line that checked the second to last letter for a c $8875-8878

But I looked at your notes on the Wiki and sort of brainstormed what I WANT to do.

Fixing S rules for Gigantes, Magus
Code: [Select]
; read second-last letter of monster name
; "u"
; if not -us, append "es"
; read third-last letter of monster name
; "g"
; if not -gus, append "es"
; read second-last letter of monster name
; "e"
; if not -es, append "es"
; read third-last letter of monster name
; "t"
; if -tes, don't change $8830
; "i"
; replace -us with -i
; Go to $8830
; replace final letter with "i"
; append "es"


Fixing Mummy Boy and Dragonfry
Code: [Select]
; "i"
; read second-last letter of monster name
; "o"
; if "-oy", append "s"
; "r"
; if "-ry", don't change ($8830)

Fixing Madusa

Code: [Select]
; read second-last letter of monster name
; "s"
; if not -sa, append "s"
; read third-last letter of monster name
; "u"
; "e"append e
; Go to $8830

It's something like that right? Again I get the programming gist of it, but I'm not sure what hex notation does what.

On a different note, thankfully window editing has gone quite well. I can see what you're talking about a few posts ago with the US "addition" of extra names. By expanding the Battle Command window, I realized I could display full names at the top of the window now, but strangely any unused character spaces still "cover" the top bar of the window. That's a real shame, otherwise it's neat! I'm thinking of keeping the change.

Edit: So as brought to my attention by laser lambert, apparently the special [(s)] (F2) doesn't work the same way as DW1. If used for "points", whenever there's 1, it just cuts off the string (experience) or doesn't display the whole window at all (points of damage in battle). Don't want to be a bother (I really really don't I swear! ^^'') But even if I used the Trace Logger/Debugger to find the routine I wouldn't know what to look for, I'm mostly just curious, but is there any way to fix that?
« Last Edit: April 23, 2019, 06:55:45 pm by Choppasmith »

abw

  • Sr. Member
  • ****
  • Posts: 253
    • View Profile
Re: General NES Hacking Questions
« Reply #76 on: April 24, 2019, 12:06:32 am »
When I was doing this for Latin, I took my list of singular monster names (I excluded the unique bosses since they never need plural forms), sorted it right-to-left so that I had all the same suffixes grouped together, wrote out the correct plural form for each name, did some extra Latin-specific stuff you won't need to deal with for English, and then wrote out the singular -> plural transformation rules I needed. Once I had that, writing the code was fairly straightforward; it's a little bit long and boring, but the individual sections are pretty simple. This is an oversimplification (particularly for CMP, which does multiple other things simultaneously), but for the purposes of this exercise, you can treat CMP #$hex as "check if A is #$hex", BEQ $addr as "if it is, go to $addr", and BNE $addr as "if it isn't, go to $addr".

As with script editing, I recommend writing code as code rather than typing things in to a hex editor, so you'll want to grab an assembler (anything that works for 65816 should also work for 6502, so pick your favourite SNES or NES assembler; I was actually using Asar, but I've been meaning to give xkas-plus a try), and if you haven't done any ASM before, you'll also want some reference material (I like the handy instruction descriptions in chapter 18 of Programming the 65816, but feel free to consult other resources if that one doesn't suit your style).

Also, if you get the chance to be clever, take it! E.g. if you only have one monster name that ends in "r" and you're writing the code to handle names that end in "r", you don't have to check that the 2nd-last letter is "a" or the 3rd-last letter is "W" or anything else, you can immediately just change the 9th-last letter to "e" to get "Man o' War" pluralized to "Men o' War".

By expanding the Battle Command window, I realized I could display full names at the top of the window now, but strangely any unused character spaces still "cover" the top bar of the window. That's a real shame, otherwise it's neat! I'm thinking of keeping the change.
Check out the screenshots from my translation - I also added some code for changing trailing spaces in hero names to top borders when the names are displayed as part of a border :P.

Edit: So as brought to my attention by laser lambert, apparently the special [(s)] (F2) doesn't work the same way as DW1. If used for "points", whenever there's 1, it just cuts off the string (experience) or doesn't display the whole window at all (points of damage in battle). Don't want to be a bother (I really really don't I swear! ^^'') But even if I used the Trace Logger/Debugger to find the routine I wouldn't know what to look for, I'm mostly just curious, but is there any way to fix that?
Much like the [name] control code, [(s)] just tests the 16-bit value at $8F; the code for that is actually right before the pluralization code:
Code: [Select]
; data -> code
; if $8F-$90 == #$0001, print "s" + [end-FA] to $60F1 and SEC, else print [end-FA] and CLC
; indirect control flow target
; from $02:$BE37 via $8006
0x01C7E8|$07:$87D8:A5 90    LDA $90
0x01C7EA|$07:$87DA:D0 05    BNE $87E1 ; if $90 > 0, add "s"
0x01C7EC|$07:$87DC:A4 8F    LDY $8F
0x01C7EE|$07:$87DE:88      DEY
0x01C7EF|$07:$87DF:F0 0C    BEQ $87ED ; if $90 == 0 and $8F - 1 == 0 (i.e. $8F == 1), do not add "s"
; control flow target (from $87DA)
0x01C7F1|$07:$87E1:A9 1C    LDA #$1C ; "s"
0x01C7F3|$07:$87E3:8D F1 60 STA $60F1
0x01C7F6|$07:$87E6:A9 FA    LDA #$FA ; [end-FA]
0x01C7F8|$07:$87E8:8D F2 60 STA $60F2
0x01C7FB|$07:$87EB:38      SEC
0x01C7FC|$07:$87EC:60      RTS

; control flow target (from $87DF)
0x01C7FD|$07:$87ED:A9 FA    LDA #$FA ; [end-FA]
0x01C7FF|$07:$87EF:18      CLC
0x01C800|$07:$87F0:60      RTS
The [number] control code, which is used for both experience and gold gains, also uses $8F-$90, so I would have expected [(s)] to work for both of those situations.

Choppasmith

  • Full Member
  • ***
  • Posts: 110
    • View Profile
Re: General NES Hacking Questions
« Reply #77 on: April 24, 2019, 06:41:42 am »
When I was doing this for Latin, I took my list of singular monster names (I excluded the unique bosses since they never need plural forms), sorted it right-to-left so that I had all the same suffixes grouped together, wrote out the correct plural form for each name, did some extra Latin-specific stuff you won't need to deal with for English, and then wrote out the singular -> plural transformation rules I needed. Once I had that, writing the code was fairly straightforward; it's a little bit long and boring, but the individual sections are pretty simple. This is an oversimplification (particularly for CMP, which does multiple other things simultaneously), but for the purposes of this exercise, you can treat CMP #$hex as "check if A is #$hex", BEQ $addr as "if it is, go to $addr", and BNE $addr as "if it isn't, go to $addr".

As with script editing, I recommend writing code as code rather than typing things in to a hex editor, so you'll want to grab an assembler (anything that works for 65816 should also work for 6502, so pick your favourite SNES or NES assembler; I was actually using Asar, but I've been meaning to give xkas-plus a try), and if you haven't done any ASM before, you'll also want some reference material (I like the handy instruction descriptions in chapter 18 of Programming the 65816, but feel free to consult other resources if that one doesn't suit your style).

Also, if you get the chance to be clever, take it! E.g. if you only have one monster name that ends in "r" and you're writing the code to handle names that end in "r", you don't have to check that the 2nd-last letter is "a" or the 3rd-last letter is "W" or anything else, you can immediately just change the 9th-last letter to "e" to get "Man o' War" pluralized to "Men o' War".

Thanks. I was worried my post came across too "Wahh I don't wanna! Just do it for me please." which wasn't what I intended at all. But that's exactly the info I was hoping for!

Quote
Check out the screenshots from my translation - I also added some code for changing trailing spaces in hero names to top borders when the names are displayed as part of a border :P.

Ah, you sly dog! Guess I'll have to download your translation and see what you did.

Speaking of. I was earnestly going to try to find monster and spell name length and used your explanation in that previous DW1 thread. But looking it over it confused me more than I thought it would.

In your example you said I could look up HEAL by setting a Read Breakpoint at $B5E6. The thing is looking back at the rom, I can't see what B5E6 is supposed to be. The pointer value was something similar but different like B8B6 (sorry away from my computer right now, something like that).

Am I missing a real obvious conversion or something? I thought the magic RAM to ROM conversion number was 3FF0?

Quote
Much like the [name] control code, [(s)] just tests the 16-bit value at $8F; the code for that is actually right before the pluralization code:
Code: [Select]
; data -> code
; if $8F-$90 == #$0001, print "s" + [end-FA] to $60F1 and SEC, else print [end-FA] and CLC
; indirect control flow target
; from $02:$BE37 via $8006
0x01C7E8|$07:$87D8:A5 90    LDA $90
0x01C7EA|$07:$87DA:D0 05    BNE $87E1 ; if $90 > 0, add "s"
0x01C7EC|$07:$87DC:A4 8F    LDY $8F
0x01C7EE|$07:$87DE:88      DEY
0x01C7EF|$07:$87DF:F0 0C    BEQ $87ED ; if $90 == 0 and $8F - 1 == 0 (i.e. $8F == 1), do not add "s"
; control flow target (from $87DA)
0x01C7F1|$07:$87E1:A9 1C    LDA #$1C ; "s"
0x01C7F3|$07:$87E3:8D F1 60 STA $60F1
0x01C7F6|$07:$87E6:A9 FA    LDA #$FA ; [end-FA]
0x01C7F8|$07:$87E8:8D F2 60 STA $60F2
0x01C7FB|$07:$87EB:38      SEC
0x01C7FC|$07:$87EC:60      RTS

; control flow target (from $87DF)
0x01C7FD|$07:$87ED:A9 FA    LDA #$FA ; [end-FA]
0x01C7FF|$07:$87EF:18      CLC
0x01C800|$07:$87F0:60      RTS
The [number] control code, which is used for both experience and gold gains, also uses $8F-$90, so I would have expected [(s)] to work for both of those situations.

Thanks again for this. How strange but I guess that's why the table only uses it for Gold and nothing else, maybe the devs just couldn't get it working properly? In a way I don't find it with fretting over. I could just tweak the script and then change my "point(s)" dictionary entry and be able to save MORE space that way.

abw

  • Sr. Member
  • ****
  • Posts: 253
    • View Profile
Re: General NES Hacking Questions
« Reply #78 on: April 24, 2019, 08:09:25 pm »
Thanks. I was worried my post came across too "Wahh I don't wanna! Just do it for me please." which wasn't what I intended at all. But that's exactly the info I was hoping for!
We aim to please ;D.

Ah, you sly dog! Guess I'll have to download your translation and see what you did.
You won't be able to completely copy my code since I cannibalized the free space I created by shortening "ADVENTURE LOG" to "VOLUMEN", but the ASM for handling menu control codes $98 - $9F starts at 0x3ED8A; the original game ran the same code for all of $9A - $9F, so I stole $9B - $9F for the "names in border" code.

Speaking of. I was earnestly going to try to find monster and spell name length and used your explanation in that previous DW1 thread. But looking it over it confused me more than I thought it would.

In your example you said I could look up HEAL by setting a Read Breakpoint at $B5E6. The thing is looking back at the rom, I can't see what B5E6 is supposed to be. The pointer value was something similar but different like B8B6 (sorry away from my computer right now, something like that).

Am I missing a real obvious conversion or something? I thought the magic RAM to ROM conversion number was 3FF0?
Sorry, part of the problem here is that I can't type properly :'(. I've updated that post to use the correct breakpoint of $BE56 as shown in the debugger snapshot instead of $B5E6; after that, it goes back to the relationship between RAM and ROM addresses: the read breakpoint uses RAM address $BE56, which for DW1 corresponds to a ROM offset of 0x3E66 (not the address you're looking for), 0x7E66 (pick me because I say "HEAL"!), or 0xBE66 (also not the address you're looking for), or theoretically 0xFE66 (but this is reserved for the fixed bank, so it's also not the address you're looking for). Does it make any more sense like this?
DW1 ROM Offset     DW1 RAM Address   DW1 RAM to ROM Conversion Number
$0010 - $400F      $8000 - $BFFF     -$7FF0
$4010 - $800F      $8000 - $BFFF     -$3FF0
$8010 - $C00F      $8000 - $BFFF     +$10
$C010 - $1000F     $C000 - $FFFF     +$4010
Thanks again for this. How strange but I guess that's why the table only uses it for Gold and nothing else, maybe the devs just couldn't get it working properly? In a way I don't find it with fretting over. I could just tweak the script and then change my "point(s)" dictionary entry and be able to save MORE space that way.
Wait a minute... Am I missing something, or is getting 1 piece of gold in this game actually impossible? Slimes are worth 2 gold, unique saleable items go for 2 gold, and even buying an Antidote Herb with a Golden Card still gets you a discount of 2 gold...

Hmm, yup, that F2 is just plain broken when $8F-$90 is #$0001. Instead of
Code: [Select]
LDA #$FA
CLC
RTS
which as you've seen results in the rest of the string getting cut off, it should be doing
Code: [Select]
LDA #$FA
STA $60F1
SEC
RTS
So the good news is: it's not you, it's the game :P. Replacing the CLC/RTS with BNE $87E3 should be enough to fix that up.

Choppasmith

  • Full Member
  • ***
  • Posts: 110
    • View Profile
Re: General NES Hacking Questions
« Reply #79 on: April 25, 2019, 02:47:18 pm »
Okay, really embarrassing question but I can't seem to open the file in either program you recommended. Asar seems to ONLY accept SNES roms and xkas plus seems to only accept dissembled code. Since you've said you've been using the former, I feel like I'm missing something incredibly obvious.

Edit: Also, I thought I was getting your explanation of Read Breakpoints. I thought to find Monster Length I'd place a Read Breakpoint for Slime which the Pointer points to as memory at B718 (to avoid possible confusion I'll just say that with Chicken Knife sharing his atlas script I thought I'd use the work you guys did in moving the Item and Monster names). I figured it'd have to be CPU memory because PPU memory doesn't let me put in B718 (I assume 3ff0 is the maximum?) And yet when I try to go in to a battle with a Slime, the Debugger doesn't trip anything. And yes I made sure it's enabled before you ask :p It's an easy thing to miss, so I wouldn't blame you if you asked.
« Last Edit: April 25, 2019, 03:22:06 pm by Choppasmith »