The aim of this article is to explain how locations work in the game to allow a person with some knowledge to be able to understand and edit them. In the current lecture, we will call “location” to every visitable place in the game. This includes the world maps, over worlds, underworlds and rooms in the game. There are 512 locations in Final Fantasy V, indexed by a value, which starts at 0x0000 and ends at 0x01FF. Location descriptors The locations information is stored at CE/9C00 to CE/CFFF. Every location record consists in 26 Bytes and there are 512 entries. In the current lecture, we will call “location descriptors” to these records.
00 01 id 02 Location name 03 <Unknown> 04 Graphic maths 05 Tile Properties 06 07 <Unknown> 08 Tile Blocks 09 0A 0B Graphics 0C 0D 0E 0F Tilemaps 10 11 12 13 14 <Unknown> 15 <Unknown> 16 Palette 17 18 <Unknown> 19 Music
The subroutine at C0/5AF6 manages location descriptors. Location descriptors overview
The 'ids' are incremental values, which set the id of the location entry. It begins at 00 00 in the location 0 and ends in FF 01 at location 511.
You can visit the following page for a list of all the map IDs.
The location name is a pointer to the table Location names offsets stored at D0/7000-D0/71FF. The names are stored at E7/0000 in RPGe's and at D0/7200-D0/77FF in the original Japanese game.
<Unknown> Byte 03
The higher bit is a flag, which marks if the animated tiles in the VRAM still updating the animation or stops. The six lower bits of this byte, multiplied by three, is an offset in the table in C0/5BB8. The pointed value in that table will be written in the SNES register $2131 (Color Math Designation) to setup any transparency effect.
This byte is an index in the Tile Properties Offsets table, multiplying it value by two. The table is located at the address:
CF/C540 to CF/C56D Tile properties offsets [23 registers of 2 bytes each]
Adding the offset value to CF/C540, you get the beginning of the data chunk:
CF/C56E Tile properties [LZSS] [2 bytes * 256 after decompressed]
The result of decompressing the chunk to 00/1186 is a table of 256 records of two bytes each. Every record is a property of the "tile block" (see below) with the same index and, as long as we know, they are related with walls and tiles "over" and “under” the player characters.
Byte 00 Byte 01 ------------------------------------ x x x x x x x x | x x x x U D L R
Here, a null value means "wall" and a one means "no wall". Subroutine C0/163F uses the other bits, but this code is still undocumented, so these properties are mostly unknown.
<Unknown> bytes 06 to 07
A tile block is a graphic of 16x16 pixels, drawn using the SNES 8x8 pixels tiles and palettes stored in the VRAM. Every tile block contains, then, four 8x8 pixels tiles, each of them with some properties. Every location have a set of 256 tile blocks. The Tile Blocks byte is an index used at C0/6B6E. Multiplied by two is an offset to table CF/0000.
CF/0000 to CF/0037 Tile blocks offset
The offset, added to bank CF, is the Tile Blocks of this location.
CF/0038 to CF/C53F Tile blocks [Compressed Type02 Unheaded]
Every tile blocks table contains 0x0800 bytes. The bytes are stored as four sub-tables of 0x0200 bytes each. Each sub-table contains the info of one of the 8x8 pixels tiles that composes the block. The tables are a sequence of 0x100 registers of two bytes each, following a SNES VRAM tile standard:
[--------Byte0--------] [--------Byte1--------] I7 I6 I5 I4 I3 I2 I1 I0 | V0 H0 R0 P2 P1 P0 I9 I8 I = VRAM Background tile id. V = Vertical flip. H = Horizontal flip. R = Tile priority. P = Tile palette.
The bytes 09, 0A and 0B of the descriptor, are four 6 bits width indexes to an offsets table. These indexes multiplied by 4 are offsets to read from that table. The table is located at DC/2D84 and contains 4 bytes offsets. These offsets to table DC/2E24 are pointers to three tables of 256 tiles (4bpp 8x8). Each pointer is a mark to perform a read of 0x2000 bytes and store them in the VRAM. DC/2D84 World Map and Locations [2bpp] background offsets (4 bytes to add to DC/2E24) DC/2E24 [4bpp] World Map and Locations 8x8 Background graphics
The last 6 bits of the byte 0B are an index to an offsets table of 2 bytes per record, located at DC/0000. These offsets to table DC/0024 point a table of 256 tiles (2bpp 8x8). The pointer is a mark to perform a read of 0x1000 bytes and store them in the VRAM: DC/0000 World Map and Locations 8x8 Background/Foreground tiles offsets (2 bytes) DC/0024 [2bpp] World Map and Locations 8x8 Background/Foreground tiles
Descriptor byte 09 stored at $1115 used in $C0/591A First load to VRAM [4bpp 8x8] Descriptor byte .. ...... .. ..... used in $C0/596A Second load to VRAM [4bpp 8x8] Descriptor byte 0A stored at $1116 used in $C0/59D7 Third load to VRAM [4bpp 8x8] Descriptor byte 0B stored at $1117 used in $C0/5A45 2bpp load to VRAM [2bpp 8x8]
The data is stored at VRAM with some DMA operations:
Bytes:2000 (inc) VRAM: 0000 -> 1000 // Graphics load 4bpp Bytes:2000 (inc) VRAM: 1000 -> 2000 // Graphics load 4bpp Bytes:2000 (inc) VRAM: 2000 -> 3000 // Graphics load 4bpp Bytes:1000 (inc) VRAM: 4000 -> 4400 // Graphics load 2bpp
After the graphics are set to VRAM the last tiles of the 256x256 tables of 8x8 tiles are in constant update to produce some animations effects, i.e. moving water or fire. Usually the updated tiles are the ones from E0 to FF of every set. The mechanics of that behavior are currently undocumented.
The bytes 0C to 0F contain three indexes of 10 bits-per-index to the tilemaps of the three background layers.
bit_7 bit_6 bit_5 bit_4 bit_3 bit_2 bit_1 bit_0 ----------------------------------------------- 0C Bg0_7 Bg0_6 Bg0_5 Bg0_4 Bg0_3 Bg0_2 Bg0_1 Bg0_0 0D Bg1_5 Bg1_4 Bg1_3 Bg1_2 Bg1_1 Bg1_0 Bg0_9 Bg0_8 0E Bg2_3 Bg2_2 Bg2_1 Bg2_0 Bg1_9 Bg1_8 Bg1_7 Bg1_6 0F ????? ????? Bg2_9 Bg2_8 Bg2_7 Bg2_6 Bg2_5 Bg2_4
The ASM subtract one for every one of these three indexes. If one index result is under zero, the location does not have tiles in that layer. Otherwise, multiplying the by two results in as offset to the table CB/0000.
CB/0000 to CB/028F Tilemaps offsets [2bytes * 0x0148]
That table contains a 2 bytes offset to every possible tilemap.
CB/0290 to CD/FDFF Tile maps [Compressed LZSS] [64 * 64 bytes (after decompressing)]
That means tilemaps are stored in the banks CB, CC and CD but the offset to access them is only 2 bytes long. To be able to deduce the bank where the tilemap to read is stored, the initial bank is set to CB and is performed a sequential read over the Tilemaps offsets from zero to the actual index. When one offset is lesser than the previous, the algorithm begins to read the in next bank (that means the tilemaps order is the same of their indexes) After calculating the address of the tilemap, it is decompressed:
Bakcground 00 -> [LZSS] Tilemap decompressed to 7F:0000 Bakcground 01 -> [LZSS] Tilemap decompressed to 7F:1000 Bakcground 02 -> [LZSS] Tilemap decompressed to 7F:2000
<Unknown> bytes 10 to 14
Descriptor byte 10, stored at $111C used in C0/63D4 subroutine: 00:0B7A = ((00:0AD9 * 2 ^ (C0:6461,((00:1120 >> 06) & 0x03) - 1)) - 00:111C) & 0x3F Descriptor byte 11, stored at $111D used in C0/63D4 subroutine: 00:0B79 = ((00:0AD8 * 2 ^ (C0:6461,((00:1120 >> 04) & 0x03) - 1)) - 00:111D) & 0x3F Descriptor byte 12, stored at $111E used in C0/63D4 subroutine: 00:0B78 = ((00:0AD9 * 2 ^ (C0:6461,((00:1120 >> 02) & 0x03) - 1)) - 00:111E) & 0x3F Descriptor byte 13, stored at $111F used in C0/63D4 subroutine: 00:0B77 = ((00:0AD8 * 2 ^ (C0:6461,((00:1120 >> 00) & 0x03) - 1)) - 00:111F) & 0x3F) Descriptor byte 14, stored at $1120 used in C0/63D4 subroutine.
<Unknown> byte 15
Descriptor byte 15, stored at $1121 used in C0/580E, C0/6D35, C0/6D51
This byte is the index to the background palette. Indexes are from 0x00 to 0x2B. Multiply this index by 0x0100 results in an offset to the background palettes tables, stored at C3/BB00.
C3/BB00 Background palettes [15-bit RGB, 2 bytes per color]
Every palette has 0x80 registers of 2 bytes each. The lecture of the location writes all the 0x0100 bytes to 00:0C00.
<Unknown> bytes 17 to 18
This byte is the index to the music to play when the PC reach the location.