Digging up the data on palette and graphics can be quite difficult when the ROM has been edited by Lunar Magic before, especially, if the level of interest uses custom palettes, Super GFX bypass and so on.
So I went through the ROM in a lot of trial and error comparing ROMs and looking at the differences.
Palettes:
When LM installs custom palettes, it makes a hijack at $00A5C0 changing it from JSL $05BE89 to JSL $0EF570.
The new routine installed at $0EF570 is $57 bytes long and is presumingly to load the palettes for levels that use custom. I included a disassembly (tool made, didn't check) below.
Now for the question of where the palette actually is. $0EF600 is the Palette pointer table (3 bytes per level)
With the possible values being:
000000 = no custom palette for level
FFFFFF = no custom palette for any level (ASM hijack not installed)
xxxxxx = SNES address of palette
If there is a proper address stored in the table, it will point to the palette which is $0202 bytes long. The first two bytes are the back area color, the remaining are the 16x16 palette simple in order from 0,0 - 0,1 - 0,2 ... F,F. Stored as SNES palette format (-bbb bbgg gggr rrrr) in little endian. The colors are only 5 btit.
So, if the palette pointer is 000000 or FFFFFF, you have to fetch it from the level header, which honestly is just a LOT more trouble.
In case you aren't familiar with it, here is all the data.
As a short summery, the level data pointers are at $05E000, 3 bytes per level.
The first 5 bytes pointed to by $05E000 contain the level header, which, amongst other things, contains which Back Area, BG, FG and sprite palettes are selected:
From there on, you can put together the palette using LOTS of different tables. These tables all use the same SNES little endian 5 bit color format as above. The first column (assuming a 16x16 palette) is black (0000), the second is white (7FDD). As for the remaining colors:
So as you can see, it's a pain in the ass compared to when the level uses cusotm palette.
Graphics:
Note, for a LM umodified ROM, I suggest you just look at Alcaro's documentation
Now for what LM does:
There is a pointer (3 bytes, little endian) at $0FF7FF, which points to a table with #$20 bytes for each level. The format is as follows:
With each value being 2 bytes long (little endian). The first 12 bit are the GFX/ExGFX number used for the slot in question ranging from 000-FFF.
As you can see, this table handles all kinds of stuff, in fact in contains information from these dialogs: (just look at them in Lunar Magic if you don't know what the codes mean).
What's most important is that the higher 4 bit of the above values, as these contain additional information on whether certain settings are enabled and such. the following table only contains information on the high 4 bit of the above values in the table.
So, you can use this table to check if the super GFX bypass is enabled, if yes, simple fetch the GFX value used from the table itself. If not, you have to get the index of the GFX sets used from the level header (as explained in the palette data above).
To get the GFX number coresponding to the index you got from the level header, you use these addresses. They are tables of 4 byte per index with each byte simply being the GFX number to be used in the slot (sprite in order of SP1-SP4 and FG/BG in order of FG1,FG2,BG1,FG3 (presumingly, didn't test this one)
$00A8C3 + (#$04 * sprite index)
$00A92B + (#$04 * fgbg index)
Now that you have the GFX number, the last thing to figure out is where the graphics themselves are that corespond to that GFX number.
One thing you could do, if you only need it in ASM, is put the GFX number as 16 bit into A and JSL to $0FF900. A routine added by LM for loading and decompressing graphics. Thing is, I don't know where exactly it decompresses them to. Some puffer in RAM probably, if someone knows, please tell me.
By deassembling the routine, you can also figure out the tables for the GFX locations:
The last remaining question is where GFX32 (player graphics) and GFX33 (animated tiles).
$00B8D8 is the pointer for the Mario graphics and $00B88B for the animated tiles. Both pointers are 16 bit. The bank byte for both comes from $00B890.
Oh yeah btw, the graphics are compressed using LC_LZ2 format. Because the site always takes forever to load, here is the format copied from SMWiki
Lastly, here are two disassmbled routines, for no real reason other than because I have them:
Anime statistic on MyAnimeList:
400 animes completed ✓
6000 episodes completed ✓
100 Days completed ✓
... what even am I doing with my life?
So I went through the ROM in a lot of trial and error comparing ROMs and looking at the differences.
Palettes:
When LM installs custom palettes, it makes a hijack at $00A5C0 changing it from JSL $05BE89 to JSL $0EF570.
The new routine installed at $0EF570 is $57 bytes long and is presumingly to load the palettes for levels that use custom. I included a disassembly (tool made, didn't check) below.
Now for the question of where the palette actually is. $0EF600 is the Palette pointer table (3 bytes per level)
With the possible values being:
000000 = no custom palette for level
FFFFFF = no custom palette for any level (ASM hijack not installed)
xxxxxx = SNES address of palette
If there is a proper address stored in the table, it will point to the palette which is $0202 bytes long. The first two bytes are the back area color, the remaining are the 16x16 palette simple in order from 0,0 - 0,1 - 0,2 ... F,F. Stored as SNES palette format (-bbb bbgg gggr rrrr) in little endian. The colors are only 5 btit.
So, if the palette pointer is 000000 or FFFFFF, you have to fetch it from the level header, which honestly is just a LOT more trouble.
In case you aren't familiar with it, here is all the data.
As a short summery, the level data pointers are at $05E000, 3 bytes per level.
The first 5 bytes pointed to by $05E000 contain the level header, which, amongst other things, contains which Back Area, BG, FG and sprite palettes are selected:
Code
|First | |Second| |Third | |Fourth| |Last | BBBLLLLL CCCOOOOO 3MMMSSSS TTPPPFFF IIVVZZZZ BBB =BG palette LLLLL =Length of level (amount of screens) CCC =BG color OOOOO =Level mode 3 =Layer 3 Priority MMM =Music SSSS =Sprite set TT =Time PPP =Sprite palette FFF =FG palette II =Item memory VV =Vertical scroll ZZZZ =Tile set
From there on, you can put together the palette using LOTS of different tables. These tables all use the same SNES little endian 5 bit color format as above. The first column (assuming a 16x16 palette) is black (0000), the second is white (7FDD). As for the remaining colors:
Code
Back Area Color x = $00B0A0 + (2 * x) BG Palette x = $00B0B0 + (#$18 * x). (Palette 0,2 to 0,7 and 1,2 to 1,7) FG Palette x = $00B190 + (#$18 * x). (Palette 2,2 to 2,7 and 3,2 to 3,7) Sprite Palette x = $00B318 + (#$18 * x). (Palette E,2 to E,7 and F,2 to F,7) Palette row 4-D colors 2-7: = $00B250 + (#$0C * (row - 4)) - palette 6,4 ($00B26E-$00B26F) will be overwritten by animation (not in boss rooms) - palette 8,6 and 8,7 ($00B288-$00B28B) will be overwritten with the first 2 colors of the player Player Palette (8,6 - 8,F) = $00B2C8 + (#$14 * player) - player: Mario(0), Luigi(1), FireMario(2), FireLuigi(3) - small, big and cape all share the same palette Layer 3 palette (row 0-1 colors 8-F) = $00B170 + (#$10 * row) - shared by all levels Berry Palette (row 2-4/9-B color 9-F) = $00B674 + (#$0E * (row - 2)) - palette row 2/9, 3/A and 4/B are the same Animated color 6,4 = $00B60C + (#$02 * frame) - 16 bytes, 8 colors in total - also used on OW for yellow map spot
So as you can see, it's a pain in the ass compared to when the level uses cusotm palette.
Graphics:
Note, for a LM umodified ROM, I suggest you just look at Alcaro's documentation
Now for what LM does:
There is a pointer (3 bytes, little endian) at $0FF7FF, which points to a table with #$20 bytes for each level. The format is as follows:
Code
AN2,LT3,BG3,BG2,FG3,BG1,FG2,FG1,SP4,SP3,SP2,SP1,LG4,LG3,LG2,LG1
With each value being 2 bytes long (little endian). The first 12 bit are the GFX/ExGFX number used for the slot in question ranging from 000-FFF.
As you can see, this table handles all kinds of stuff, in fact in contains information from these dialogs: (just look at them in Lunar Magic if you don't know what the codes mean).
What's most important is that the higher 4 bit of the above values, as these contain additional information on whether certain settings are enabled and such. the following table only contains information on the high 4 bit of the above values in the table.
Code
AN2 = abc- LG1 = vvvv LG2 = hhhh LG3 = yyyy LG4 = Y-fe SP1 = SCxx a = SuperGFX enabled (red poison muchroom), b = Layer 3 GFX enabled (green poison mushroom), c = Layer 3 Tilemap enabled (green poison mushroom), (fish icon for below:) vvvv = Vert. Scroll hhhh = Horz. Scroll yyyyY = Y Init xx = X Init C = CGADSUB S = Layer 3 to subscreen f = fix layer 3 scroll e = Advanced l3 bypass settings enabled
So, you can use this table to check if the super GFX bypass is enabled, if yes, simple fetch the GFX value used from the table itself. If not, you have to get the index of the GFX sets used from the level header (as explained in the palette data above).
To get the GFX number coresponding to the index you got from the level header, you use these addresses. They are tables of 4 byte per index with each byte simply being the GFX number to be used in the slot (sprite in order of SP1-SP4 and FG/BG in order of FG1,FG2,BG1,FG3 (presumingly, didn't test this one)
$00A8C3 + (#$04 * sprite index)
$00A92B + (#$04 * fgbg index)
Now that you have the GFX number, the last thing to figure out is where the graphics themselves are that corespond to that GFX number.
One thing you could do, if you only need it in ASM, is put the GFX number as 16 bit into A and JSL to $0FF900. A routine added by LM for loading and decompressing graphics. Thing is, I don't know where exactly it decompresses them to. Some puffer in RAM probably, if someone knows, please tell me.
By deassembling the routine, you can also figure out the tables for the GFX locations:
Code
00-31: Bank byte: $00B9F6 High byte: $00B9C4 Low byte: $00B992 each indexed by GFX number 80-FF: $0FF600 (3 byte per entry long table) indexed by (GFX number - $80) * 3 000000 means not inserted 100-FFF: pointer for table start at $0FF937 (3 byte per entry long table) indexed by (GFX number - $100) * 3 000000 means not inserted
The last remaining question is where GFX32 (player graphics) and GFX33 (animated tiles).
$00B8D8 is the pointer for the Mario graphics and $00B88B for the animated tiles. Both pointers are 16 bit. The bank byte for both comes from $00B890.
Oh yeah btw, the graphics are compressed using LC_LZ2 format. Because the site always takes forever to load, here is the format copied from SMWiki
Originally posted by SMWiki
The compressed data consists of "chunks", each with a header:
If the header byte is $FF, the end of the compressed data has been reached, and decompression will be aborted.
Here is a list of commands bits which the LC_LZ2 decompression function can use during the decompression:
Code
header bits 76543210 CCCLLLLL CCC: Command bits LLLLL: Length
If the header byte is $FF, the end of the compressed data has been reached, and decompression will be aborted.
Here is a list of commands bits which the LC_LZ2 decompression function can use during the decompression:
Code
000 "Direct Copy" Followed by (L+1) bytes of data 001 "Byte Fill" Followed by one byte to be repeated (L+1) times 010 "Word Fill" Followed by two bytes. Output first byte, then second, then first, then second, etc. until (L+1) bytes has been outputted 011 "Increasing Fill" Followed by one byte to be repeated (L+1) times, but the byte is increased by 1 after each write 100 "Repeat" Followed by two bytes (ABCD byte order) containing address (in the output buffer) to copy (L+1) bytes from 101 (Unused command) 110 (Unused command) 111 "Long length" This command has got a two-byte header: 111CCCLL LLLLLLLL CCC: Real command LLLLLLLLLL: Length
Lastly, here are two disassmbled routines, for no real reason other than because I have them:
Code org $0EF570 PresuminglyPaletteLoadingRoutine: REP #$30 LDA $FE BEQ .arLabel0C DEC A JSR $F583 STZ $FE .arLabel0C SEP #$30 JSL $05BE8A RTL SEP #$10 PHB : PHK : PLB REP #$10 STA $00 ASL A CLC ADC $00 TAY LDA $F600,y STA $04 INY LDA $F600,y BNE .arLabel2E PLB : RTS .arLabel2E PLB STA $05 SEP #$10 LDY #$00 STA $08 LDA [$04],y STA $0701,y INC $04 INC $04 LDA #$0100 CLC ADC $04 STA $07 .arLabel48 LDA [$04],y STA $0703,y LDA [$07],y STA $0803,y INY : INY BNE .arLabel48 RTS | Code ;input A = 16bit GFX number org $0FF900 LoadAndDecompressGFX: PHX PHY PHP REP #$30 CMP #$0100 BCS label_0FF92B CMP #$0080 BCS label_0FF944 CMP #$007F BEQ label_0FF96B TAX SEP #$30 LDA $00B992,X STA $8A LDA $00B9C4,X STA $8B LDA $00B9F6,X STA $8C BRA label_0FF95A label_0FF92B: SEC SBC #$0100 STA $8A ASL A CLC ADC $8A TAX LDA $128008,X ;changes! STA $8A LDA $128009,X STA $8B BRA label_0FF95A label_0FF944: AND #$007F STA $8A ASL A CLC ADC $8A TAX LDA $0FF600,X STA $8A LDA $0FF601,X STA $8B label_0FF95A: SEP #$30 PHK PER $0005 PHB PHY JML $00BA47 REP #$30 LDA #$0100 label_0FF96B: PLP PLY PLX RTL |
Anime statistic on MyAnimeList:
400 animes completed ✓
6000 episodes completed ✓
100 Days completed ✓
... what even am I doing with my life?