Faxanadu by Vagla 2/12/05-2/13/05 Initial release First thing's first. This game has the worst pointer maze you'll ever find. This becomes more apparent in the TSA/SCROLL/DOORS section, where there are literally pointless pointers (hah), but anyway, just be aware that there are literally pointers to pointers to pointers to pointers in this game. It's quite insane, and usually unnecessary. Things I still have to do with this document include better proofreading (ie going back over all my addresses and making sure I didn't make any typos when reading the pointers and converting them to addresses), more work on text sets and door destination tables, maybe sprite TSA, maybe stats, and maybe palettes. If anyone has any need for particular data on this game that isn't already covered, feel free to ask on Acmlm's board and I'll likely see it eventually. Also, note that the majority of these addresses are untested. I haven't even played through the majority of Faxanadu. I have no idea what most of it is like. So, it's possible that some of this data is inaccurate, since I don't even know what to expect in most of the game (nor do I have the saved states or passwords necessary to get to all of the areas so that I can actually test this stuff out). I'm fairly certain, though, that the majority of it is accurate, and anything which I think could potentially be wrong (besides typos, which I have been fixing as I come across them) has been noted as such (particularly the door desintation tables). If you find a mistake, post about it on Acmlm's and, again, I'll probably come across it eventually. ***LEVEL DATA*** Level data is broken up into what I call chunks. I'm pretty sure that it's done this way because of tilesets. Thus, different chunks, as far as I know, all have different tilesets (and TSA, scroll, doors, sprites, and what-have-you). For level data, there is a pointer to the beginning of each chunk, and then at the beginning of each chunk are pointers for each screen in that chunk. All other data that's broken up into chunks in this game works in just the same way. So here's the basic level data stuff, then. $00010-00015 - Pointers to the 3 chunks' screen pointers in bank 0. $00016-00027 - Screen pointers for chunk 0. $00028-00438 - Level data for chunk 0. $00439-004DE - Screen pointers for chunk 1. $004DF-038AB - Level data for chunk 1. $038AC-038C7 - Screen pointers for chunk 2. $038C8-03F8? - Level data for chunk 2. (I didn't want to manually decompress the last screen to figure out where it ends, so that's why there's a ?. The level data most likely ends on $03F88, but it could also end up to a few bytes before it, too) $04010-04013 - Pointers to the 2 chunks' screen pointers in bank 1. $04014-04093 - Screen pointers for chunk 3. $04094-06892 - Level data for chunk 3. $06893-068E2 - Screen pointers for chunk 4. $068E3-07E?? - Level data for chunk 4. (It likely ends in the vicinity of $07EFF, probably a little before it) $08010-08015 - Pointers to the 3 chunks' screen pointers in bank 2. $08016-08055 - Screen pointers for chunk 5. $08056-09A50 - Level data for chunk 5. $09A51-09A64 - Screen pointers for chunk 6. $09A65-09EA3 - Level data for chunk 6. $09EA4-09ECB - Screen pointers for chunk 7. $09ECC-0AA?? - Level data for chunk 7. (The end of the level data is likely around $0AAF0, but note that I'm quite sure that $0AAF0 itself is not the end of the data) ***Compression*** The game's compression is surprisingly simple, though still quite unique. The game uses sets of 2 bits to determine if it will copy a block from elsewhere on the screen or place a unique block. Bits 00 will copy the block immediately to the left of the current block (so if the block that was just placed was 1F and the next block's command bits were 00, it would place another 1F), after which it would move on to the next block. Bits 01 do the same, except they copy the block above the current block instead of to the left. Bits 10 also do essentially the same thing, though they copy the block that's both above and to the left of the current block. That means that any block xx can be a copy of one of three blocks using these three commands, as illustrated below: 10 01 00 xx So what if you want a unique block? In that case, you use bits 11. Bits 11 tell the game that the next 8 bits give the ID of a unique block. Thus, it takes a total of 10 bits to create a single unique block, whereas it's only 2 bits to copy a block. Okay, so let's decompress the game's first screen, which is screen 00 in chunk 0. This is what it's supposed to look like (barring I didn't make any typos; I found one a few days ago, which I hope was the only one): 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 34 34 01 01 01 01 01 01 01 01 01 01 01 34 0F 56 33 33 01 01 01 01 01 01 01 01 01 01 01 33 45 59 10 0E 5A 01 01 01 01 01 01 01 34 01 01 10 3F 50 69 0E 54 01 01 01 01 01 01 01 33 01 59 50 13 44 69 0E 54 01 01 01 0C 2D 2F 0C 2C 01 53 12 13 1C 69 0E 54 0D 0D 0D 1A 2D 2D 2D 2C 16 53 12 13 12 69 0E 54 07 07 07 07 07 07 06 06 06 53 12 13 4F 69 0E 54 65 65 65 65 65 65 05 05 05 5E 12 13 4E 69 0E 54 66 66 66 66 66 66 66 66 66 66 66 66 66 68 0E 54 04 04 04 04 04 04 04 67 29 03 2A 08 29 03 0E 5F 04 04 04 04 04 04 04 3A 02 02 02 02 02 02 02 02 This is defined by this data: C040000000000000334100000CD30FD5B33100000CCF45D6710C3B5A4003344C433FD4369754400333759D4313 D115430CCB72FC332C753C49C715C3431A41C595C495C1C00C1815D3D5D9400C1435E5D395D98000003685C100 0367CA703CAB08CA70375F40033AC08000 So, I'll go ahead and walk you through a few parts of this. The first part is C040. C040's binary is 11000000 01000000. The 11 means that the next 8 bits define a unique block for the first block of the screen. Those bits are 00000001. So, the first block is $01. The next 6 bits are all 00, so they copy the block that's directly to their left. Thus, they'll all become $01's. C040, therefore, decompresses to . Following that are 6 $00's, all of which have the binary 00000000 and thus simply copy the $01 block 24 times. Then comes 3341, which has the binary 00110011 01000001. The 00 copies the $01 again, the 11 signals a unique block, which has the binary 00110100 ($34), the next 00 copies the $34 block, and then the 01 copies the block directly above it, which is $01. Then come some more 00's, and then 0CD30F. The binary for this is 00001100 11010011 00001111. The two 00's copy the previous $01 twice, the 11 signals a unique block with bits 00110100 ($34), and the next 11 signals another unique block with bits 00001111 ($0F). The next compression set is a little interesting, though, in that it's 5 bytes, yet it only defines 4 blocks. The one shortcoming of this compression format is that it's bloated by one byte if all 4 blocks in the set of 4 are unique blocks, since it not only needs the 4 sets of 8 bits for the 4 blocks, but it also needs the 4 11's that tell it to use unique blocks. Anyway, I think that's enough of a tutorial on how to handle this format. ***TSA, Scroll, and Doors*** $0C010-0C011 - A pointer to the pointer table that points to the chunk's pointers for various things. (It's essentially useless, and I don't even know why it's there) $0C012-0C021 - Pointers to all 8 chunks' TSA/Scroll/Door pointers. $0C022-0C035 - Chunk 0's pointers for all of the following data. The order (which is the same for all 8 chunks) is as follows: >$0C022 - A pointer to chunk 0's Attributes pointer. (Yes, it's fairly useless, but it's there) >$0C024 - A pointer to chunk 0's Block Properties data >$0C026 - A pointer to chunk 0's Scroll data >$0C028 - A pointer to chunk 0's Door location/destination data >$0C02A - A pointer to chunk 0's Door destination table >$0C02C - A pointer to chunk 0's Attributes data >$0C02E-0C035 - 4 pointers to chunk 0's 4 groups of TSA data $0C036-0C0B5 - Chunk 0's attributes. They have an interesting format. Each byte defines attributes for one block, but it does so in a 2x2 block area onscreen. Each byte is broken up into 4 sets of 2 bits, each set defining which block in the 2x2 area uses which palette. Basically, it's possible to have a single block ID use 4 different palettes on different locations of the screen. It's hard to explain, so feel free to experiment. Experimenting with $0C037 is probably easiest, since it's the byte for the sky's attributes. $0C0B6-0C135 - Chunk 0's top left TSA tiles. $0C136-0C1B5 - Chunk 0's top right TSA tiles. $0C1B6-0C235 - Chunk 0's bottom left TSA tiles. $0C236-0C2B5 - Chunk 0's bottom right TSA tiles. $0C2B6-0C335 - Chunk 0's block properties. (00 = air, 01 = solid, 02 = ladder, 04 = foreground, ...) $0C336-0C359 - Chunk 0's scrolling (4 bytes per screen, defining which screen each edge scrolls to. Order is left, right, top, bottom) $0C35A-0C382 - Chunk 0's doors (4 bytes per door. First byte is door location screen ID, second byte is door location yx coordinates, third byte is door destination (the byte tells where in the following door destination table to grab the byte that says which screen to go to) but note that I am not very certain about how this third byte works (ie I might be wrong, I haven't studied it enough), and the fourth byte is the destination location yx coordinates. $0C383-0C42D - Chunk 0's door destination table, I think. I could be completely wrong, but it seems to me that this is what's used to figure out where a door goes. I'll need to fiddle with it some more, since I didn't give it much attention. $0C423-0C436 - Chunk 1's pointers. Uses the same format as Chunk 0's pointers at $0C022. $0C437-0C4BF - Chunk 1's attributes. $0C4C0-0C548 - Chunk 1's top left TSA tiles. $0C549-0C5D1 - Chunk 1's top right TSA tiles. $0C5D2-0C65A - Chunk 1's bottom left TSA tiles. $0C65B-0C6E3 - Chunk 1's bottom right TSA tiles. $0C6E4-0C76C - Chunk 1's block properties. $0C76D-0C86C - Chunk 1's scrolling. $0C86D-0C891 - Chunk 1's doors. $0C892-0C919 - Chunk 1's door destination table. $0C91A-0C92D - Chunk 2's pointers. $0C92E-0C9B5 - Chunk 2's attributes. $0C9B6-0CA3D - Chunk 2's top left TSA tiles. $0CA3E-0CAC5 - Chunk 2's top right TSA tiles. $0CAC6-0CB4D - Chunk 2's bottom left TSA tiles. $0CB4E-0CBD5 - Chunk 2's bottom right TSA tiles. $0CBD6-0CC5D - Chunk 2's block properties. $0CC5E-0CDA9 - Chunk 2's scrolling. $0CDAA-0CDEE - Chunk 2's doors. $0CDEF-0CE8A - Chunk 2's door destination table. $0CE8B-0CE9E - Chunk 5's pointers. $0CE9F-0CEFC - Chunk 5's attributes. $0CEFD-0CF5A - Chunk 5's top left TSA tiles. $0CF5B-0CFB8 - Chunk 5's top right TSA tiles. $0CFB9-0D016 - Chunk 5's bottom left TSA tiles. $0D017-0D074 - Chunk 5's bottom right TSA tiles. $0D075-0D0D2 - Chunk 5's block properties. $0D0D3-0D172 - Chunk 5's scrolling. $0D173-0D19B - Chunk 5's doors. $0D19C-0D1BB - Chunk 5's door destination table. $0D1BC-0D1CF - Chunk 4's pointers. $0D1D0-0D2CF - Chunk 4's attributes. $0D2D0-0D3CF - Chunk 4's top left TSA tiles. $0D3D0-0D4CF - Chunk 4's top right TSA tiles. $0D4D0-0D5CF - Chunk 4's bottom left TSA tiles. $0D5D0-0D6CF - Chunk 4's bottom right TSA tiles. $0D6D0-0D7CF - Chunk 4's block properties. $0D7D0-0D7F7 - Chunk 4's scrolling. $0D7F8-0D7F8 - Chunk 4's doors. $0D7F9-0D838 - Chunk 4's door destination table. $0D839-0D84C - Chunk 3's pointers. $0D84D-0D8BC - Chunk 3's attributes. $0D8BD-0D92C - Chunk 3's top left TSA tiles. $0D92D-0D99C - Chunk 3's top right TSA tiles. $0D99D-0DA0C - Chunk 3's bottom left TSA tiles. $0DA0D-0DA7C - Chunk 3's bottom right TSA tiles. $0DA7D-0DAFC - Chunk 3's block properties. $0DAFD-0DB34 - Chunk 3's scrolling. $0DB35-0DBE1 - Chunk 3's doors. $0DBE2-0DC8D - Chunk 3's door destination table. $0DC8E-0DCA1 - Chunk 6's pointers. $0DCA2-0DD04 - Chunk 6's attributes. $0DD05-0DD67 - Chunk 6's top left TSA tiles. $0DD68-0DDCA - Chunk 6's top right TSA tiles. $0DDCB-0DE2D - Chunk 6's bottom left TSA tiles. $0DE2E-0DE90 - Chunk 6's bottom right TSA tiles. $0DE91-0DEF3 - Chunk 6's block properties. $0DEF4-0DF73 - Chunk 6's scrolling. $0DF74-0DFB4 - Chunk 6's doors. $0DFB5-0E058 - Chunk 6's door destination table. $0E059-0E06C - Chunk 7's pointers. $0E06D-0E0AC - Chunk 7's attributes. $0E0AD-0E0EC - Chunk 7's top left TSA tiles. $0E0ED-0E12C - Chunk 7's top right TSA tiles. $0E12D-0E16C - Chunk 7's bottom left TSA tiles. $0E16D-0E1AC - Chunk 7's bottom right TSA tiles. $0E1AD-0E1EC - Chunk 7's block properties. $0E1ED-0E23C - Chunk 7's scrolling. $0E23D-0E245 - Chunk 7's doors. $0E246-0E2?? - Chunk 7's door destination table. Again, make note that the door destination table is pretty much unknown because I haven't spent the time to look at it. I'll likely look into it later. ***PALETTES*** $2C010-2C1FF - Palettes. $326D8-32717 - Palettes? ***SPRITES*** $2C220-2C22F - Pointers to all 8 chunks' sprite pointers. $2C230-2C241 - Chunk 0's sprite pointers. There is one pointer for each screen, each pointing to where that screen's sprite data begins. $2C242-2C26F - Chunk 0's sprite data. Sprites are defined in sets of 2 bytes. The first byte is a sprite's ID, and the second byte is a sprite's yx location onscreen. You can put as many enemies on a screen as you wish. FF signals the end of the sprite data, after which point comes the sprite text data. Sprite text data basically says which text set a sprite will use if you talk to it. If you want to have both sprites that can be talked to and enemies on the same screen, it's a good idea to put the talk-toable sprites first, since the first byte of the sprite text data corresponds with the first sprite, the second with the second sprite, etc. Sprite text data is terminated with another FF, so it takes 2 FF's to completely terminate the sprite data for a screen. Thus, if you have the talk-toable sprites first, you can define what text sets they use and then terminate the data so you don't have to waste any space on text set bytes for the enemies. If there is no sprite text data for a screen, then you simply use 2 FF's to terminate the sprite $2C270-2C2EF - Chunk 1's sprite pointers. $2C2F0-2C491 - Chunk 1's sprite data. $2C492-2C537 - Chunk 2's sprite pointers. $2C538-2C759 - Chunk 2's sprite data. $2C75A-2C7A9 - Chunk 5's sprite pointers. $2C7AA-2C8B1 - Chunk 5's sprite data. $2C8B2-2C8F1 - Chunk 6's sprite pointers. $2C8F2-2C9CB - Chunk 6's sprite data. $2C9CC-2C9F3 - Chunk 7's sprite pointers. $2C9F4-2CA85 - Chunk 7's sprite data. $2CA86-2CB11 - Chunk 4's sprite pointers. $2CB12-2CC9C - Chunk 4's sprite data. $2CC9D-2CCB8 - Chunk 3's sprite pointers. $2CCB9-2CCED - Chunk 3's sprite data. ***TEXT*** $31B4D-31DBC - Various item names. $34310-373C9 - The game's primary text. FC is a pause, FD is a space, FE is a line break, and FF terminates the text block. Sephiroth3 is to be credited for the following text information: $31F7B-32012 - Low bytes of pointers for text sets $32013-320AA - High bytes of pointers for text sets $320AB-326D7 - Text sets. They basically use a scripting language to achieve all the text stuff in the game, which kinda sucks because it makes it tougher to edit, but it also is a good thing in that it allows for a lot of stuff to be done without having to worry about hardcoded stuff. I'll go ahead and paste much of what Sephiroth3 noted about text set commands (I assume it's an incomplete list), as well as add a few notes of my own: 00 - Makes text boxes close. 01 - Displays text. 05 - Checks if you have some amount of money, and if you don't, it prints "This is not enough Golds." 07 - Gives you an item. The format is the same as for 12. 08 - Causes buying. See 11 (selling) 0D - Jumps to the following address if you have any money at all. 0F - Asks whether to buy or sell, and jumps if buy was chosen. 11 - Causes selling. The next word is the address of an FF-terminated table, with item numbers and prices. 12 - Checks one of the flags in 42C if followed by 80, 81, 82, 83, 92, 8A, 93 or 94. Otherwise, it searches for the 5 lower bits in 3BD + 3 higher bits, or in one of 5 tables at 39D, 3A1, 3A5, 3A9 or 3AD, where 3C2-3C6 contains the counts. If the flag is set, or the value was found, it jumps. - The first byte possibly decides the character picture to show at the left - 01, 02, 03 all display text boxes. Perhaps in different ways? - Non-command bytes (such as the 26 at $320B1) are text IDs specifying which text block to use. 26, for example, uses text block $27 (the 40th text block in the text data at $34310). There are no pointers for text blocks; rather, it just uses the FF values that separate text blocks to identify the beginning of new blocks. Adding or removing FF's will offset all of the following text. Note that this text set stuff hasn't been looked into all that much. Some of this information may be inaccurate (particularly when 'possibly' and 'perhaps' are used in descriptions), but it should be a good start, at least.