Language…
11 users online: codfish1002, eltiolavara9, GiraffeKiller, Ice Man, koffe190, LazyRuns, RicardoDeMelo, sinseiga, SirGabe, yoshi9429, YuriGamer - Guests: 254 - Bots: 376
Users: 64,795 (2,375 active)
Latest user: mathew

Graphics and Palette data in LM edited ROMs

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:

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: #lm{sgfxby}#lm{l3gfxby}#lm{props} (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:
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?