News:

11 March 2016 - Forum Rules

Main Menu

Getting Started on a VWF?

Started by justin3009, April 11, 2011, 10:57:08 AM

Previous topic - Next topic

justin3009

#120
I'll worry about making the font 2bpp after I get a grasp of understanding on this at the moment.  It's not too big of a deal right now, more-so understanding is the key.

I've got a good idea of what's going on and I'm PRETTY sure I know the problem at the moment.  The error I'm seeing is that the 'b' is not being shifted correctly to the 'gray' area on that little explanation.  So when it ORS everything, it's literally combining the two letters onto one plane which is screwing everything up.  I'm not exactly sure why it's not storing there though.  I 'THINK' I have the code correct?  I'll start tracing it now and post what I have going on.

$83/99ED 22 40 ED 83 JSL $83ED40[$83:ED40]   A:001A X:9B73 Y:006D P:envMxdIzC


$83/ED40 85 02       STA $02    [$00:0002]   A:001A X:9B73 Y:006D P:envMxdIzC ;Store current letter to 7E:0002
$83/ED42 A9 7F       LDA #$7F                A:001A X:9B73 Y:006D P:envMxdIzC ;Set bank to be $7F
$83/ED44 48          PHA                     A:007F X:9B73 Y:006D P:envMxdIzC
$83/ED45 AB          PLB                     A:007F X:9B73 Y:006D P:envMxdIzC
$83/ED46 C2 30       REP #$30                A:007F X:9B73 Y:006D P:envMxdIzC
$83/ED48 A5 02       LDA $02    [$00:0002]   A:007F X:9B73 Y:006D P:envmxdIzC ;Load current letter
$83/ED4A 29 FF 00    AND #$00FF              A:001A X:9B73 Y:006D P:envmxdIzC ;AND value
$83/ED4D 85 02       STA $02    [$00:0002]   A:001A X:9B73 Y:006D P:envmxdIzC ;Store back to 7E:0002
$83/ED4F A0 00 00    LDY #$0000              A:001A X:9B73 Y:006D P:envmxdIzC ;Set Y to 0000
$83/ED52 20 70 ED    JSR $ED70  [$83:ED70]   A:001A X:9B73 Y:0000 P:envmxdIZC ;Load 'Letter Writing' routine


$83/ED70 A5 02       LDA $02    [$00:0002]   A:001A X:9B73 Y:0000 P:envmxdIZC ;Load current letter
$83/ED72 38          SEC                     A:001A X:9B73 Y:0000 P:envmxdIzC ;Set carry flag
$83/ED73 E9 10 00    SBC #$0010              A:001A X:9B73 Y:0000 P:envmxdIzC ;Subtract 10 from accumulator
$83/ED76 85 02       STA $02    [$00:0002]   A:000A X:9B73 Y:0000 P:envmxdIzC ;Store value back to 7E:0002
$83/ED78 0A          ASL A                   A:000A X:9B73 Y:0000 P:envmxdIzC ;Double accumulator
$83/ED79 0A          ASL A                   A:0014 X:9B73 Y:0000 P:envmxdIzc ;Double accumulator
$83/ED7A 0A          ASL A                   A:0028 X:9B73 Y:0000 P:envmxdIzc ;Double accumulator
$83/ED7B 0A          ASL A                   A:0050 X:9B73 Y:0000 P:envmxdIzc ;Double accumulator
$83/ED7C 18          CLC                     A:00A0 X:9B73 Y:0000 P:envmxdIzc ;Clear carry flag
$83/ED7D 69 00 F2    ADC #$F200              A:00A0 X:9B73 Y:0000 P:envmxdIzc ;Add #$F200 current accumulator value
$83/ED80 AA          TAX                     A:F2A0 X:9B73 Y:0000 P:eNvmxdIzc ;Transfer A to X
$83/ED81 BF 00 00 81 LDA $810000,x[$81:F2A0] A:F2A0 X:F2A0 Y:0000 P:eNvmxdIzc ;Load letter graphics
$83/ED85 99 00 FF    STA $FF00,y[$7F:FF00]   A:0606 X:F2A0 Y:0000 P:envmxdIzc ;Store to 7F:FF00 (Loops until Y is 8)
$83/ED88 E8          INX                     A:0606 X:F2A0 Y:0000 P:envmxdIzc ;Increase X
$83/ED89 E8          INX                     A:0606 X:F2A1 Y:0000 P:eNvmxdIzc ;Increase X
$83/ED8A C8          INY                     A:0606 X:F2A2 Y:0000 P:eNvmxdIzc ;Increase Y
$83/ED8B C8          INY                     A:0606 X:F2A2 Y:0001 P:envmxdIzc ;Increase Y
$83/ED8C C0 08 00    CPY #$0008              A:0606 X:F2A2 Y:0002 P:envmxdIzc ;Check if Y is 08
$83/ED8F D0 F0       BNE $F0    [$ED81]      A:0606 X:F2A2 Y:0002 P:eNvmxdIzc ;If not, send back to loading letter graphics.
$83/ED91 60          RTS                     A:2626 X:F2A8 Y:0008 P:envmxdIZC


$83/ED55 20 A0 ED    JSR $EDA0  [$83:EDA0]   A:2626 X:F2A8 Y:0008 P:envmxdIZC ;Load routine to grab the width of letter


$83/EDA0 A5 02       LDA $02    [$00:0002]   A:2626 X:F2A8 Y:0008 P:envmxdIZC ;Load current letter
$83/EDA2 AA          TAX                     A:000A X:F2A8 Y:0008 P:envmxdIzC ;Transfer A to X
$83/EDA3 BF 40 EE 83 LDA $83EE40,x[$83:EE4A] A:000A X:000A Y:0008 P:envmxdIzC ;Load width of letter from table
$83/EDA7 29 FF 00    AND #$00FF              A:FF07 X:000A Y:0008 P:eNvmxdIzC ;AND
$83/EDAA 85 02       STA $02    [$00:0002]   A:0007 X:000A Y:0008 P:envmxdIzC ;Store back to 7E:0002
$83/EDAC A2 00 00    LDX #$0000              A:0007 X:000A Y:0008 P:envmxdIzC ;Set X to 0000
$83/EDAF 60          RTS                     A:0007 X:0000 Y:0008 P:envmxdIZC


$83/ED58 20 B0 ED    JSR $EDB0  [$83:EDB0]   A:0007 X:0000 Y:0008 P:envmxdIZC ;Load routine to shift/merge letters.


$83/EDB0 A0 00 00    LDY #$0000              A:0007 X:0000 Y:0008 P:envmxdIZC ;Set Y to 0000
$83/EDB3 E2 20       SEP #$20                A:0007 X:0000 Y:0000 P:envmxdIZC
$83/EDB5 AD F0 FE    LDA $FEF0  [$7F:FEF0]   A:0007 X:0000 Y:0000 P:envMxdIZC ;Load PixelOffset
$83/EDB8 F0 36       BEQ $36    [$EDF0]      A:0000 X:0000 Y:0000 P:envMxdIZC ;Go to 83:EDF0 if value is 00
$83/EDF0 BD 00 FF    LDA $FF00,x[$7F:FF00]   A:0000 X:0000 Y:0000 P:envMxdIZC ;Load Font workspace
$83/EDF3 1D 10 FF    ORA $FF10,x[$7F:FF10]   A:0006 X:0000 Y:0000 P:envMxdIzC ;OR VWF workspace
$83/EDF6 9D 10 FF    STA $FF10,x[$7F:FF10]   A:0006 X:0000 Y:0000 P:envMxdIzC ;Store to VWF workspace
$83/EDF9 E8          INX                     A:0006 X:0000 Y:0000 P:envMxdIzC ;Increase X
$83/EDFA E0 10 00    CPX #$0010              A:0006 X:0001 Y:0000 P:envMxdIzC ;Check if X is 10, if not, send back to 'Load Font workspace'
$83/EDFD D0 F1       BNE $F1    [$EDF0]      A:0006 X:0001 Y:0000 P:eNvMxdIzc
$83/EDFF A5 02       LDA $02    [$00:0002]   A:0000 X:0010 Y:0000 P:envMxdIZC ;Load width of letter
$83/EE01 18          CLC                     A:0007 X:0010 Y:0000 P:envMxdIzC ;Clear carry flag
$83/EE02 6D F0 FE    ADC $FEF0  [$7F:FEF0]   A:0007 X:0010 Y:0000 P:envMxdIzc ;Add PixelOffset to current value
$83/EE05 8D F0 FE    STA $FEF0  [$7F:FEF0]   A:0007 X:0010 Y:0000 P:envMxdIzc ;Store back to PixelOffset
$83/EE08 60          RTS                     A:0007 X:0010 Y:0000 P:envMxdIzc


$83/ED5B A9 83       LDA #$83                A:0007 X:0010 Y:0000 P:envMxdIzc ;Set bank back to #$83 after the entire routine is done
$83/ED5D 48          PHA                     A:0083 X:0010 Y:0000 P:eNvMxdIzc
$83/ED5E AB          PLB                     A:0083 X:0010 Y:0000 P:eNvMxdIzc
$83/ED5F 6B          RTL                     A:0083 X:0010 Y:0000 P:eNvMxdIzc


$83/EDBA 85 04       STA $04    [$00:0004]   A:0007 X:0000 Y:0000 P:envMxdIzC (This is when 7F:FEF0 =/= 00) ;Stores Pixeloffset to 7E:0004
$83/EDBC 64 05       STZ $05    [$00:0005]   A:0007 X:0000 Y:0000 P:envMxdIzC ;Set 7E:0005 to 00
$83/EDBE 5E 00 FF    LSR $FF00,x[$7F:FF00]   A:0007 X:0000 Y:0000 P:envMxdIzC ;LSR 7F:FF00
$83/EDC1 7E 08 FF    ROR $FF08,x[$7F:FF08]   A:0007 X:0000 Y:0000 P:envMxdIzc ;Shift right to 7F:FF08
$83/EDC4 E8          INX                     A:0007 X:0000 Y:0000 P:envMxdIZc ;Increase X
$83/EDC5 C8          INY                     A:0007 X:0001 Y:0000 P:envMxdIzc ;Increase Y
$83/EDC6 C4 04       CPY $04    [$00:0004]   A:0007 X:0001 Y:0001 P:envMxdIzc ;Check if Y is == to Pixeloffset
$83/EDC8 D0 F4       BNE $F4    [$EDBE]      A:0007 X:0001 Y:0001 P:eNvMxdIzc ;If not, send back to LSR 7F:FF00
$83/EDCA A2 00 00    LDX #$0000              A:0007 X:0007 Y:0007 P:envMxdIZC ;Set X to 0000
$83/EDCD BD 00 FF    LDA $FF00,x[$7F:FF00]   A:0007 X:0000 Y:0007 P:envMxdIZC ;Load Font workspace
$83/EDD0 1D 10 FF    ORA $FF10,x[$7F:FF10]   A:0010 X:0000 Y:0007 P:envMxdIzC ;OR with VWF workspace
$83/EDD3 9D 1F FF    STA $FF1F,x[$7F:FF1F]   A:0016 X:0000 Y:0007 P:envMxdIzC ;Store to area to be transfered
$83/EDD6 E8          INX                     A:0016 X:0000 Y:0007 P:envMxdIzC ;Increase X
$83/EDD7 E0 10 00    CPX #$0010              A:0016 X:0001 Y:0007 P:envMxdIzC ;Check if X == 10
$83/EDDA D0 F1       BNE $F1    [$EDCD]      A:0016 X:0001 Y:0007 P:eNvMxdIzc ;If not, send back to 'Load Font workspace'
$83/EDDC 80 21       BRA $21    [$EDFF]      A:0016 X:0010 Y:0007 P:envMxdIZC ;Always Branch to 83:EDFF to set PixelOffset to what it should be


Hopefully this is understandable.  This is what I have going on right now.

Edit: When it stores the letters in their areas, this is the result.


When it tries merging, this is the result.


Pretty sure it's because the 'b' is not being shifted properly. 
'We have to find some way to incorporate the general civilians in the plot.'

'We'll kill off children in the Juuban district with an infection where they cough up blood and are found hanging themselves from cherry blossom trees.'

Gemini

You should post some code from your VWF assembly files. That trace dump doesn't exactly make things clear.

Quote from: justin3009 on July 12, 2015, 01:38:14 PMI'll worry about making the font 2bpp after I get a grasp of understanding on this at the moment.  It's not too big of a deal right now, more-so understanding is the key.
Barely a change. You simply loop twice the height of a tile in 2 BPP. SNES formats make it simple for you, so I guess you can even easily upgrade.

justin3009

#122
Edit: It's working fully except for the big issue now.

Whenever my PixelOffset hits 0, it stops writing anything.  I can't seem to find a way around it since it'll happen quite often :/

Edit 2: Yeah, seems the only problem I'm technically seeing is that when it hits Pixeloffset 0, it's not actually ORing it and sending it to the proper area that'd be transferred to VRAM... But if I do anything else while it's 0, it'll completely screw up the Pixeloffset resulting in nothing working properly.  I'm really struggling with this bit.
'We have to find some way to incorporate the general civilians in the plot.'

'We'll kill off children in the Juuban district with an infection where they cough up blood and are found hanging themselves from cherry blossom trees.'

justin3009

#123
Edit: SUCCESS!  I GOT IT!

I decided to scrap the entire routine I had before and write my own!  As far as I can see, it's working flawlessly! :)  All that's left is to update VRAM and the tilemap, otherwise it's good to go!  I fully wrote out Abracadabra and it stored fine, even did a sentence of 'This is a test' and it came out perfectly!  I'll post a screenshot once I have it fully ready!

Edit 2: And we're ready to go! :)  Thank you so much for the help!  It's really not that complicated actually.  The only spot that was really killing me was the shifting.  I've never ever studied that nor really seen or understood how it worked.

'We have to find some way to incorporate the general civilians in the plot.'

'We'll kill off children in the Juuban district with an infection where they cough up blood and are found hanging themselves from cherry blossom trees.'

Nightcrawler

Congrats! Perseverance pays off in ROM hacking. Add another skill to your bag. :)
TransCorp - Over 20 years of community dedication.
Dual Orb 2, Wozz, Emerald Dragon, Tenshi No Uta, Glory of Heracles IV SFC/SNES Translations

justin3009

#125
As far as I can see, the VWF itself works quite well.  There's a few kinks with the tilemapping still but otherwise I think it's okay.  The big issue I had with the VWF right now is when there was a constant string of big letters that were repeated, such as 'MMMMMMMMMMMMMMMMM', it'd actually start printing half letters per tile instead of the whole like it should.  I'll have to figure out some way to work around a few of these kinks.

August 08, 2015, 12:11:05 PM - (Auto Merged - Double Posts are not allowed before 7 days.)

One more question!  It doesn't have anything to do with the VWF itself but moreso updating the Tile Map.

How would you handle updating the Tile Map when it comes to a VWF?  The first letter of the entire sentence structure I assume always updates the tile map right away just so there's something to display on that single letter.  Then should it update only when the 8x8 is full?  Right now it's ALWAYS two blocks ahead on the tile map no matter what I do but it's at least displaying one letter at a time like it should.  If I somehow manage to not display those two blocks, it cuts off the 2nd line's two blocks and starts desynching the writing where it's trying to display cut up letters and such.

Just struggling for some reason on how to get this properly working.  I know the VWF code itself is fine, no issue there at all.  Just the tile map is giving me serious troubles.
'We have to find some way to incorporate the general civilians in the plot.'

'We'll kill off children in the Juuban district with an infection where they cough up blood and are found hanging themselves from cherry blossom trees.'

Nightcrawler

There are a number of ways a VWF renderer can be 'glued' into the game. The best approach will vary depending on how you did your rendering, and what the game engine was already doing.

In general though, if you're doing dialog, and you're doing letter by letter display, then I'd probably suggest one of the following:

1.) Map a large region of RAM for rendering that can contain an entire dialog line or even full text window. Then, the tilemap doesn't change much. It's mapped once to the full line or window. It will reflect the letter by letter rendering in VRAM automatically that way. You don't have to do anything to tilemap during rendering.

2.) If you don't have the RAM to do that, you can write the tilemap each character iteration, advancing the index only when the tilespace is full. If you're doing 8x8 for example, you'd write the current tile, and second potential spillover tile to the tilemap each time. It would write the same two indexes until your tile was full, then it would increment to the next index and write the two tiles there.

You can do any variation of the above as well. You can write to the tilemap only when the tile is full, only after x number of tiles are done etc. There are lots of ways to handle it. I usually pick what I think is the easiest way to massage into the game with the least amount of code.
TransCorp - Over 20 years of community dedication.
Dual Orb 2, Wozz, Emerald Dragon, Tenshi No Uta, Glory of Heracles IV SFC/SNES Translations

justin3009

#127
I'm actually trying as hard as I can on #2.

I prefer to go a tile by tile basis, feels more clean to me.  I have the RAM space in this game no problem but I don't want to force that on every other game.  I'd rather try to conserve as much space as possible and learn from that so I can apply it to other games that may have less.

#2 actually works quite well so far but it's not quite going as hoped for.

The output of the text overall is exactly right...but it's not updating the tilemap quick enough which causes the letters to appear in halves sometimes.  The whole tilemap is fully and properly written but I can't figure out how to update it properly.  It seems if I try adding anything else, either it writes too much tilemap updating, or it doesn't update enough.  It's always in a weird state of flux ;/  I'm really struggling with this.
'We have to find some way to incorporate the general civilians in the plot.'

'We'll kill off children in the Juuban district with an infection where they cough up blood and are found hanging themselves from cherry blossom trees.'

cclh12

Hello,

I figured asking for advise here instead of making a new thread would be the better option.

I am trying to write the hack that is the bane of all translations projects.  A variable width font. I cannot seem to find information on how it's done on PSX. The information I have read on the forum seems to strongly lean towards SNES and GBA. However, correct me if I'm wrong, isn't the process different since PSX deals with pixels instead of tiles? Could someone kind of 'break down' the process of how you go about doing it? For instance, on the game I'm working with, all characters are printed with the same width apart. Where in the world do I put a breakpoint to find the code responsible for that?

I have already managed to rewrite some of the routines in this game.
For some reason, it counts the Japanese text before its printed to screen but couldn't count ASCII characters. Although I'm not sure why... I rewrote that.
I also made the game accept 1-byte ASCII instead of 2-byte characters. But this variable width font is driving me nuts.  :banghead:


Hopefully I can follow justin3009 and be successful.

Gemini

There are several ways to hack a proportional font on the PSX, but it depends on the game and how it displays text. If it's a simple font already cached in VRAM and sprites to represent each character, then you find how it increments horizontal spacing and you're good to go. A similar case appears when the game caches text glyphs on demand and renders as individual sprites; again, you need to change the incremental spacing to be proportional and possibly hack in a static font rather than cache on demand. A final case is when a game uses a canvas to draw text as strips of sprites, like Xenogears or Castlevania: SOTN; there you gotta use an approach similar to what you'd have on tile-based consoles, so you do all the shifting and oring, upload, and let the game do the rest.

cclh12

#130
Quote from: Gemini on September 06, 2015, 04:09:01 PM
A similar case appears when the game caches text glyphs on demand and renders as individual sprites

Yes, this is my case exactly.



On screen, this is what is displayed.
ABCDEFGHIJKLMNO
abcdefghijklmno

Except this massive spacing between the letters.

EDIT: Can anyone tell me if this font is the PSX's internal BIOS font? It sure does look like it to me...



Quote from: Gemini on September 06, 2015, 04:09:01 PM
you need to change the incremental spacing to be proportional and possibly hack in a static font rather than cache on demand.
It's the part of finding the code responsible for the spacing that is making this difficult for me.
I don't know where to start to find it. It could be either hardcoded or have data in a file controlling each characters spacing. (which I doubt for this case, since they all use the same spacing.)
Is there a place I should put a breakpoint that (in most cases) should take me to the code responsible for it? If so, where?

As of right now, I can't see why having the full font loaded into VRAM would be beneficial to this. Maybe I will find out as I investigate more. Could you explain the benefit of having it always in VRAM as oppose to having just what is currently needed?


EDIT: So, I have made some slight progress.
Unmodified


Modified


Pretty sure I just nailed it...
Granted, it could be adjusted better. I just need to replicate it for every character...

Gemini

Quote from: cclh12 on September 06, 2015, 04:51:50 PMEDIT: Can anyone tell me if this font is the PSX's internal BIOS font? It sure does look like it to me...
It looks like it to me.

QuoteIt's the part of finding the code responsible for the spacing that is making this difficult for me.
I don't know where to start to find it. It could be either hardcoded or have data in a file controlling each characters spacing. (which I doubt for this case, since they all use the same spacing.)
Is there a place I should put a breakpoint that (in most cases) should take me to the code responsible for it? If so, where?
Usually you monitor primitives via pSX's debugger and GPU capture, find a text sprite primitive, and add a write breakpoint to its xy coordinates. Unless the game uses an abstraction layer such as libgs, that should give you the whereabouts of text sprite fillers and somewhere close by the spacing line.

QuoteAs of right now, I can't see why having the full font loaded into VRAM would be beneficial to this. Maybe I will find out as I investigate more. Could you explain the benefit of having it always in VRAM as oppose to having just what is currently needed?
Using a precached font is the way to go with any English translation for three reasons:
1) you don't need to have the full font always stored in RAM ready for upload and you can reserve that RAM chunk for your extra code (unless it's done via the BIOS, but in that case you can strip those functions instead of the non-existing font);
2) you don't need to upscale the font from 1/2 BPP to 4 BPP or higher, which can be a mess sometimes;
3) caching imposes some limitations as to how many text sprites you can have at once on screen.

cclh12

Thanks for the help Gemini. I found the code I need to modify.

Right now I kinda have a Fixed Width Font effect going on.
It's still pretty ugly, but better is better.

Original


Modified


I should easily be able to adapt this into a Variable Width Font now. If I don't jinx myself, that is.  ;)
Right now, I see this being the only way I can think of coding it.

if((CurrentPrintedLetter == 'A') & (NextLetterToBePrinted == 'B'))
{

// Customize the 'B' coordinates to make it squeeze in close next to 'A'

}


Needless to say, this will get long very fast. I would have to make the game change how it will print each letter (and number) depending on which letter or number follows it.
Is this how most people code it? It feels kinda inefficient, but it is also the only way I can think of how to code it.

Gemini

Make a table of nibbles, lower for width and higher for left adjustment. Use adjustment to move the sprite to the left (Sprt.x=x-adjust), width to increase global x when you're done adding a sprite (x+=width).

cclh12

Got it fully working.  :beer:
Now I just need to fix this "18 characters per line" rule the game has. Which will be simple.
Thanks for the advice Gemini.


obscurumlux01

Gemini, you are fantastic with the assistance.  I'm learning a ton about how difficult a VWF is just from the info you posted!

We seriously need a 'kudos' or 'like' button on posts.  ^_^