{"id":367,"date":"2012-02-27T12:29:56","date_gmt":"2012-02-27T11:29:56","guid":{"rendered":"http:\/\/blog.blockos.org\/?p=367"},"modified":"2015-11-30T10:50:52","modified_gmt":"2015-11-30T09:50:52","slug":"alices-bubbles-part-3","status":"publish","type":"post","link":"https:\/\/blog.blockos.org\/?p=367","title":{"rendered":"Alice\u2019s bubbles (Part 3)"},"content":{"rendered":"<p>Here comes level names. This was the most painful part of the whole translation hacking. First, they are made of sprites. Second, there is a limited number of them. And last but not least, the table that tells which sprites to use for a given level name can&#8217;t be expanded.<br \/>\nThe LSB of the pattern indices for the first level are stored at $5196 (file offset) and $519C for the second. After putting a read breakpoint on $*4f96 (remember that there&#8217;s usually a 512 bytes header to PC Engine roms) and $*4f9C, I ended up at $8E3F. A quick look at the code show me there is a address table stored at $8F81 where each address starts 1 byte before the LSB pattern index list. The first byte indicates the number of sprites. Here&#8217;s the complete index list:<\/p>\n<p><code>    8F95 : 05 00 02 04 06 08<br \/>\n    8F9B : 05 0E 02 12 06 08<br \/>\n    8FA1 : 05 18 0A 16 06 08<br \/>\n    8FA7 : 05 14 00 16 06 08<br \/>\n    8FAD : 05 1C 02 20 06 08<br \/>\n    8FB3 : 04 18 1A 06 08<br \/>\n    8FB8 : 04 22 24 06 08<br \/>\n    8FBD : 05 02 10 18 06 08<br \/>\n    8FC3 : 04 26 28 06 08<\/code><\/p>\n<p>At this point, I managed to avoid using compressed data. Unfortunatelly the whole sprites were compressed. In fact, there are 4 &#8220;decompression&#8221; schemes. The first one is a direct transfer to VRAM using the <em>tia<\/em> instruction. The second is using a simple <a href=\"http:\/\/en.wikipedia.org\/wiki\/Run-length_encoding\">RLE<\/a>. The third one and the most tricky mixes <a href=\"http:\/\/en.wikipedia.org\/wiki\/Run-length_encoding\">RLE<\/a> and some XOR. The last one simply copies the data to VRAM using a handcrafted loop.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">f796:   LDA &lt;$7d\r\n        ORA &lt;$7e\r\n        BEQ f7e1\r\n        JSR f888\r\n        CMP #$00\r\n        BNE f7ac\r\n                ; #1 Transfer to VRAM using tia\r\n                TIA $3700, $0002, $0020\r\n                BRA f7d2\r\nf7ac:   CMP #$02\r\n        BNE f7bc\r\n                ; #2 RLE\r\n                JSR f817\r\n                TIA $3700, $0002, $0020\r\n                BRA f7d2\r\nf7bc:   CMP #$03\r\n        BNE f7cf\r\n                ; #3 RLE + XOR\r\n                JSR f817\r\n                JSR f857\r\n                TIA $3700, $0002, $0020\r\n                BRA f7d2\r\nf7cf: ; #4 Loop transfer\r\n        JSR f803\r\n\r\nf7d2:   LDA &lt;$7d\r\n        SBC #$01\r\n        STA &lt;$7d\r\n        LDA &lt;$7e\r\n        SBC #$00\r\n        STA &lt;$7e\r\n        BRA f796\r\n\r\nf7e1:<\/pre>\n<p>The <a href=\"http:\/\/en.wikipedia.org\/wiki\/Run-length_encoding\">RLE<\/a> routine:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">f817:   LDA [$78]\r\n        STA &lt;$83\r\n        LDY #$04\r\n        LDA #$09\r\n        STA &lt;$81\r\n        STZ &lt;$80\r\n        CLX\r\n        LDA #$20\r\n        STA &lt;$82\r\n       \r\nf828:   DEC &lt;$81\r\n        BNE f83a\r\n        PHY\r\n        LDA #$08\r\n        STA &lt;$81\r\n        INC &lt;$80\r\n        LDY &lt;$80\r\n        LDA [$78], Y\r\n        STA &lt;$83\r\n        PLY\r\n\r\nf83a:   ROR &lt;$83 ; the rle counter\r\n        BCS f849\r\n        STZ $3740, X\r\n        INX\r\n        DEC &lt;$82\r\n        BNE f828\r\n        JMP f7f6\r\n\r\nf849:   LDA [$78], Y\r\n        STA $3740, X\r\n        INY\r\n        INX\r\n        DEC &lt;$82\r\n        BNE f828\r\n        JMP f7f6<\/pre>\n<p>The &#8220;XOR&#8221; routine:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">f857:   PHX\r\n        PHY\r\n        LDY #$07\r\n        CLX\r\n       \r\nf85c:   LDA $3740, X\r\n        EOR $3742, X\r\n        STA $3742, X\r\n        LDA $3741, X\r\n        EOR $3743, X\r\n        STA $3743, X\r\n        LDA $3750, X\r\n        EOR $3752, X\r\n        STA $3752, X\r\n        LDA $3751, X\r\n        EOR $3753, X\r\n        STA $3753, X\r\n       \r\n        INX\r\n        INX\r\n       \r\n        DEY\r\n        BNE f85c\r\n       \r\n        PLY\r\n        PLX\r\n        RTS<\/pre>\n<p>The XOR is used here to maximize the number of repetitions in order to make the <a href=\"http:\/\/en.wikipedia.org\/wiki\/Run-length_encoding\">RLE<\/a> more efficient. Well, that&#8217;s how I understood it. I didn&#8217;t make any benchmark to verify if it was really the case. The encoder was written pretty quickly. I reworked the font so that it more or less matches the ASCII set. This simplified the script encoder and the display routine.<br \/>\n<img decoding=\"async\" src=\"http:\/\/blog.blockos.org\/wp-content\/uploads\/2012\/02\/font_big.png\"\/><\/p>\n<p>Back to level names! I got help from a bunch of people to translate them. The last character of each level can be translated to <em>country<\/em>. Unfortunately that was far too big so I switched to <em>&#8220;land&#8221;<\/em>.<\/p>\n<ul>\n<li><img decoding=\"async\" src=\"http:\/\/blog.blockos.org\/wp-content\/uploads\/2012\/02\/lvl1_name.png\"\/> Candy Land<\/li>\n<li><img decoding=\"async\" src=\"http:\/\/blog.blockos.org\/wp-content\/uploads\/2012\/02\/lvl2_name.png\"\/> Toy Land<\/li>\n<li><img decoding=\"async\" src=\"http:\/\/blog.blockos.org\/wp-content\/uploads\/2012\/02\/lvl3_name.png\"\/> Greenery Land<\/li>\n<li><img decoding=\"async\" src=\"http:\/\/blog.blockos.org\/wp-content\/uploads\/2012\/02\/lvl4_name.png\"\/> Ice Land<\/li>\n<li><img decoding=\"async\" src=\"http:\/\/blog.blockos.org\/wp-content\/uploads\/2012\/02\/lvl5_name.png\"\/> Time Land<\/li>\n<li><img decoding=\"async\" src=\"http:\/\/blog.blockos.org\/wp-content\/uploads\/2012\/02\/lvl6_name.png\"\/> Water Land<\/li>\n<li><img decoding=\"async\" src=\"http:\/\/blog.blockos.org\/wp-content\/uploads\/2012\/02\/lvl7_name.png\"\/> Sky Land<\/li>\n<li><img decoding=\"async\" src=\"http:\/\/blog.blockos.org\/wp-content\/uploads\/2012\/02\/lvl8_name.png\"\/> Mirror Land<\/li>\n<li><img decoding=\"async\" src=\"http:\/\/blog.blockos.org\/wp-content\/uploads\/2012\/02\/lvl9_name.png\"\/> Queen Land<\/li>\n<\/ul>\n<p>Here&#8217;s the actual &#8220;font&#8221; made of 16&#215;16 sprites.<br \/>\nThe zigzag lines on the last 16&#215;16 sprite are there to make the compressed data fits into 504 bytes. I must also keep an empty sprite for spacing.<br \/>\n<img decoding=\"async\" src=\"http:\/\/blog.blockos.org\/wp-content\/uploads\/2012\/02\/levels.png\"\/><br \/>\nI had the sprite index list done pretty quickly. But when I started working on the index pointer table, I realized that there were 9 entries. 9? Did I forgot one?<br \/>\nYes, I forgot one. The water land&#8230; And I didn&#8217;t have enough room to put the missing letters (bloody size limit). <\/p>\n<p>After a cautious review, it was decided to rename <em>&#8220;Greenery land&#8221;<\/em> into <em>&#8220;Green land&#8221;<\/em>. As There were already a <em>&#8220;Queen land&#8221;<\/em>, there&#8217;s now enough room for the missing <em>&#8220;Water&#8221;<\/em>.<br \/>\n<img decoding=\"async\" src=\"http:\/\/blog.blockos.org\/wp-content\/uploads\/2012\/02\/levels2.png\"\/><br \/>\nThe only issue remaining was that the sprite index list is 42 bytes long, and mine was 43. As each level name ends with the same sequence I can use the last index as the beginning of the next one (each sequence starts with the length of the index list). So I swapped the sprite for <em>&#8220;nd&#8221;<\/em> with the beginning of the one whose index was equal to a sequence length, namely <em>&#8220;To&#8221;<\/em> which was at index 6. Why 6? First because it&#8217;s the length of the longest index list and also because sprite indexes are always even.<\/p>\n<p>Here&#8217;s the original index list (the fist 2 hexadecimal values are the values of the pointer table):<br \/>\n<code>    8F95 : 05 00 02 04 06 08<br \/>\n    8F9B : 05 0E 02 12 06 08<br \/>\n    8FA1 : 05 18 0A 16 06 08<br \/>\n    8FA7 : 05 14 00 16 06 08<br \/>\n    8FAD : 05 1C 02 20 06 08<br \/>\n    8FB3 : 04 18 1A 06 08<br \/>\n    8FB8 : 04 22 24 06 08<br \/>\n    8FBD : 05 02 10 18 06 08<br \/>\n    8FC3 : 04 26 28 06 08<\/code><\/p>\n<p>The new one:<br \/>\n<code>    8F95 : 05 00 06 04 24 06<br \/>\n    8F9B : 04 02 04 24 06<br \/>\n    8FA0 : 05 08 0A 0C 24 06<br \/>\n    8FA6 : 04 0E 10 24 06<br \/>\n    8FAB : 05 12 14 28 24 06<br \/>\n    8FB1 : 05 20 22 26 24 06<br \/>\n    8FB7 : 04 16 04 24 06<br \/>\n    8FBB : <u><b>06<\/b><\/u> 18 1A 1C 28 24 06<br \/>\n    8FC2 : 05 1E 0A 0C 24 06<\/code><\/p>\n<p>Shortly after beating the evil level names I received the first version of the translated script. Unfortunately the first tests revealed that the current text layout was unreadeable. There were only 2 or 3 words per line. So I had to crawl brack into the rom and change the text box position and size. I was only some couples of values to change.<\/p>\n<p>So the 24th of July 2011, the english patch was released. Nearly 2 years after I started working on it&#8230; Anyway, some days after the release a couple of angry frenchmen complained that we didn&#8217;t release a french patch. In order to silence those annoying creeters I had to modify the font and the script encoder in order to add accents. That was not a big deal. But I let the french translators handle the level names (with some kind of sadistic grin). After some weeks of hard work, they decided to give up and use the english names. The french translation was released in December 2011.<\/p>\n<ul>\n<li>English translation\n<ul>\n<li><a href=\"http:\/\/www.romhacking.net\/translations\/1637\/\">patch<\/a><\/li>\n<li><a href=\"http:\/\/www.blockos.org\/releases\/pcengine\/translation\/Marchen%20Maze%20%5bfinal%5d.zip\">source code<\/a><\/li>\n<\/ul>\n<\/li>\n<li>French translation\n<ul>\n<li><a href=\"http:\/\/www.romhacking.net\/translations\/1703\/\">patch<\/a><\/li>\n<li><a href=\"http:\/\/www.blockos.org\/releases\/pcengine\/translation\/Marchen%20Maze\/fr\/Marchen%20Maze%20%5bfr%5d%5bfinal%5d.zip\">source code<\/a><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Here comes level names. This was the most painful part of the whole translation hacking. First, they are made of sprites. Second, there is a limited number of them. And last but not least, the table that tells which sprites to use for a given level name can&#8217;t be expanded.\u2026 <a class=\"continue-reading-link\" href=\"https:\/\/blog.blockos.org\/?p=367\">Continue reading<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[3,6],"tags":[27,30,19],"_links":{"self":[{"href":"https:\/\/blog.blockos.org\/index.php?rest_route=\/wp\/v2\/posts\/367"}],"collection":[{"href":"https:\/\/blog.blockos.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.blockos.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.blockos.org\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.blockos.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=367"}],"version-history":[{"count":72,"href":"https:\/\/blog.blockos.org\/index.php?rest_route=\/wp\/v2\/posts\/367\/revisions"}],"predecessor-version":[{"id":1018,"href":"https:\/\/blog.blockos.org\/index.php?rest_route=\/wp\/v2\/posts\/367\/revisions\/1018"}],"wp:attachment":[{"href":"https:\/\/blog.blockos.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=367"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.blockos.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=367"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.blockos.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=367"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}