Actions

SCHG How-to

Difference between revisions of "Port Sonic 2's Level Art Loader to Sonic 1"

From Sonic Retro

(Preparation)
m (Text replacement - "\[\[Category:SCHG How-tos.*" to "")
 
(2 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
{{GuideBy|Clownacy}}
 
{{GuideBy|Clownacy}}
  
Fancy giving your loading screens a speed boost? Sonic 1's Nemesis-compressed art adds quite the chunk to the loading times, and you'll find yourself staring at that blue oval more than you would Sonic 2's blue screen. Porting Sonic 2's level art loading process cuts down on a significant amount of waiting time. And how do we do that?
+
Fancy giving your loading screens a speed boost? Sonic 1's Nemesis-compressed art adds quite the chunk to the loading times, and you'll find yourself staring at that blue oval more than you would Sonic 2's blue screen. Porting Sonic 2's level art loading process cuts down on a significant amount of waiting time. So how do we do that?
  
Sonic 1's level art is loaded as part of the Zone-specific PLCs, forcing it to be Nemesis-compressed. Because of this, we'll need to remove this feature and then introduce Sonic 2's equivalent, which is separate, and, by default, uses a faster compression.
+
Sonic 1's level art is loaded as part of the Zone-specific PLCs, forcing it to be Nemesis-compressed. Because of this, we'll need to remove this feature and then introduce Sonic 2's equivalent, which is separate, and by default uses a faster compression.
  
Notes for hivebrain user were added by Shadow05
+
Grab yourself a compression tool. Some examples are [[Sonic_Hacking_Utilities#Compressors.2FDecompressors|KENS]] and {{LinkRetro|topic=32235&view=findpost&p=767170|title=FW-KENSC}}. Get familiar with them. If you use Windows, you could also use the {{LinkRetro|topic=24573&view=findpost&p=783395|title=KENSSharp}} or {{LinkRetro|topic=36252|title=FW-KENSC}} shell extensions. Also, you'll want to have [[SCHG_How-to:Add_Spin_Dash_to_Sonic_1/Part_3#Converting_Sonic.27s_art_buffer_to_a_DMA_queue|ported QueueDMATransfer]].<br/>
  
=GitHub=
+
==Hivebrain==
==Preparation==
+
===Recompression===
Grab yourself a KENS compressor. You can find some [[Sonic_Hacking_Utilities#Compressors.2FDecompressors|here (KENS)]] and {{LinkRetro|topic=32235&view=findpost&p=767170|title=here (KENSC)}}. Get familiar with them. Also, you'll want to have [[SCHG_How-to:Add_Spin_Dash_to_Sonic_1/Part_3#Converting_Sonic.27s_art_buffer_to_a_DMA_queue|ported QueueDMATransfer]].
 
 
 
==Recompression==
 
 
First, we'll want to recompress our art from Nemesis to Kosinski, Sonic 2's standard level art compression.
 
First, we'll want to recompress our art from Nemesis to Kosinski, Sonic 2's standard level art compression.
  
Inside your 'artnem' folder, take the following files and place them in a separate folder. However, keep a copy of '8x8 - GHZ1.bin' in artnem, and rename it to "8x8 - Title.bin"
+
Inside your 'artnem' folder, make a copy of '8x8ghz1.bin', and rename it to "8x8title.bin". We're doing this because GHZ's tiles are split into two, and the title screen only uses the first half, but Sonic 2's art loader doesn't support this.
 
 
*8x8 - GHZ1.bin
 
  
*8x8 - GHZ2.bin
+
After that, take the following files and place them in a separate folder:
  
*8x8 - LZ.bin
+
*8x8ghz1.bin
 +
*8x8ghz2.bin
 +
*8x8lz.bin
 +
*8x8mz.bin
 +
*8x8sbz.bin
 +
*8x8slz.bin
 +
*8x8syz.bin
  
*8x8 - MZ.bin
+
Inside the new folder, decompress all of these files, then delete them, leaving only the uncompressed files.
  
*8x8 - SBZ.bin
+
Now we need to combine GHZ's files into one, so it will work with Sonic 2's art loader. Open both '8x8ghz1' and '8x8ghz2' in a hex editor, and copy '8x8ghz2' to the end of '8x8ghz1'. After that, save the result as "8x8ghz", and delete '8x8ghz1' and '8x8ghz2'.
  
*8x8 - SLZ.bin
+
Now compress all of the files in the Kosinski format and move them to the 'artkos' folder. Make sure the files still use the '.bin' extension - rename them if they're not.
  
*8x8 - SYZ.bin
+
===Changing the pointers===
 
+
Inside '''sonic1.asm''', change this...
Inside the new folder, decompress all of these files, then delete the Nemesis originals. Open both '8x8 - GHZ1.bin' and '8x8 - GHZ2.bin' in a hex editor, and append the latter to the end of the former, save the result as "8x8 - GHZ.bin". Delete '8x8 - GHZ1.bin' and '8x8 - GHZ2.bin'. Now compress all of the files in Kosinski and move them to the 'artkos' folder.
 
 
 
==Changing the pointers==
 
Inside sonic.asm, change this...
 
 
<syntaxhighlight lang="asm">
 
<syntaxhighlight lang="asm">
Nem_GHZ_1st: incbin "artnem\8x8 - GH1.bin" ; GHZ primary patterns
+
Nem_GHZ_1st: incbin "artnem\8x8ghz1.bin" ; GHZ primary patterns
 
even
 
even
Nem_GHZ_2nd: incbin "artnem\8x8 - GHZ2.bin" ; GHZ secondary patterns
+
Nem_GHZ_2nd: incbin "artnem\8x8ghz2.bin" ; GHZ secondary patterns
 
even</syntaxhighlight>
 
even</syntaxhighlight>
  
 
...to this
 
...to this
<syntaxhighlight lang="asm">Nem_Title: incbin "artnem\8x8 - Title.bin" ; Title patterns
+
<syntaxhighlight lang="asm">Nem_Title: incbin "artnem\8x8title.bin" ; Title patterns
 
even
 
even
Kos_GHZ: incbin "artkos\8x8 - GHZ.bin" ; GHZ primary patterns
+
Kos_GHZ: incbin "artkos\8x8ghz.bin" ; GHZ primary patterns
 
even</syntaxhighlight>
 
even</syntaxhighlight>
  
Follow this theme with the rest of the level art inbins: change the "Nem_" prefix to "Kos_", and change "artnem" to "artkos".
+
Follow this pattern with the rest of the level art incbins: change the "Nem_" prefix to "Kos_", and change "artnem" to "artkos".
  
 
Now find this...
 
Now find this...
Line 58: Line 55:
 
This is simply correcting the title screen's pointer. Like in Sonic 2, the title art is still Nemesis.
 
This is simply correcting the title screen's pointer. Like in Sonic 2, the title art is still Nemesis.
  
Inside the "_inc" folder, find and open '''Pattern Load Cues.asm''' and '''LevelHeaders.asm'''.
+
Inside the "_inc" folder, find and open '''Pattern Load Cues.asm''' and '''Main level load blocks.asm'''.
 
 
Hivebrain users '''LevelHeaders.asm''' is called '''Main level load blocks.asm'''
 
  
In '''LevelHeaders.asm''', you can see entries for the level art, but, like the music entries, they are unused. As seen above, the level art is actually chosen by the pattern load cues. Sonic 2 would go on to actually use these entries, and, after the completion of this guide, your hack will too. But first, let's correct them to match our new labels.
+
In '''Main level load blocks.asm''', you can see entries for the level art, but, like the music entries, they are unused. As seen above, the level art is actually chosen by the pattern load cues. Sonic 2 would go on to actually use these entries, and, after the completion of this guide, your hack will too. But first, let's correct them to match our new labels.
  
 
*Nem_GHZ_2nd → Kos_GHZ
 
*Nem_GHZ_2nd → Kos_GHZ
 
 
*Nem_LZ → Kos_LZ
 
*Nem_LZ → Kos_LZ
 
 
*Nem_MZ → Kos_MZ
 
*Nem_MZ → Kos_MZ
 
 
*Nem_SLZ → Kos_SLZ
 
*Nem_SLZ → Kos_SLZ
 
 
*Nem_SYZ → Kos_SYZ
 
*Nem_SYZ → Kos_SYZ
 +
*Nem_SBZ → Kos_SBZ
  
*Nem_SBZ → Kos_SBZ
+
Go to '''Pattern Load Cues.asm''' and delete the entries for the level art. GHZ and the ending have two, the other levels have only the one.
  
Go to '''Pattern Load Cues.asm''' and comment out the entries for the level art. GHZ and the ending have two, the other levels have only the one.
+
===Porting the level art loading process===
  
==Porting the level art loading process==
+
Back in '''sonic1.asm''', above MainLoadBlockLoad, paste this:
Above LevelDataLoad, paste this:
 
  
<syntaxhighlight lang="asm">LoadZoneTiles:
+
<syntaxhighlight lang="asm">
 +
LoadZoneTiles:
 
moveq #0,d0 ; Clear d0
 
moveq #0,d0 ; Clear d0
move.b (v_zone).w,d0 ; Load number of current zone to d0
+
move.b ($FFFFFE10).w,d0 ; Load number of current zone to d0
 
lsl.w #4,d0 ; Multiply by $10, converting the zone ID into an offset
 
lsl.w #4,d0 ; Multiply by $10, converting the zone ID into an offset
lea (LevelHeaders).l,a2 ; Load LevelHeaders's address into a2
+
lea (MainLoadBlocks).l,a2 ; Load LevelHeaders's address into a2
 
lea (a2,d0.w),a2 ; Offset LevelHeaders by the zone-offset, and load the resultant address to a2
 
lea (a2,d0.w),a2 ; Offset LevelHeaders by the zone-offset, and load the resultant address to a2
 
move.l (a2)+,d0 ; Move the first longword of data that a2 points to to d0, this contains the zone's first PLC ID and its art's address.
 
move.l (a2)+,d0 ; Move the first longword of data that a2 points to to d0, this contains the zone's first PLC ID and its art's address.
Line 112: Line 104:
 
jsr (QueueDMATransfer).l ; Use d1, d2, and d3 to locate the decompressed art and ready for transfer to VRAM
 
jsr (QueueDMATransfer).l ; Use d1, d2, and d3 to locate the decompressed art and ready for transfer to VRAM
 
move.w d7,-(sp) ; Store d7 in the Stack
 
move.w d7,-(sp) ; Store d7 in the Stack
move.b #$C,(v_vbla_routine).w
+
move.b #$C,($FFFFF62A).w
bsr.w WaitForVBla
+
bsr.w DelayProgram
bsr.w RunPLC
+
bsr.w RunPLC_RAM
 
move.w (sp)+,d7 ; Restore d7 from the Stack
 
move.w (sp)+,d7 ; Restore d7 from the Stack
 
move.w #$800,d3 ; Force the DMA transfer length to be $1000/2 (the first cycle is dynamic because the art's DMA'd backwards)
 
move.w #$800,d3 ; Force the DMA transfer length to be $1000/2 (the first cycle is dynamic because the art's DMA'd backwards)
Line 120: Line 112:
  
 
rts
 
rts
; End of function LoadZoneTiles</syntaxhighlight>
+
; End of function LoadZoneTiles
 +
</syntaxhighlight>
 +
 
 +
This is Sonic 2's LoadZoneTiles, modified to match Sonic 1's Main level load blocks block size ($10 over Sonic 2's $12).
 +
 
 +
Now to add some branches to this code.
 +
 
 +
Go to loc_3946 and, above this:
 +
<syntaxhighlight lang="asm"> bsr.w Main level load blocks ; load block mappings and palettes</syntaxhighlight>
 +
 
 +
Add this:
 +
<syntaxhighlight lang="asm"> bsr.w LoadZoneTiles ; load level art</syntaxhighlight>
 +
 
 +
Do the same at End_LoadData.
 +
 
 +
And with that, we're done, a short guide for shorter loading times! Save and build.
 +
 
 +
==GitHub==
 +
===Recompression===
 +
First, we'll want to recompress our art from Nemesis to Kosinski, Sonic 2's standard level art compression.
 +
 
 +
Inside your 'artnem' folder, make a copy of '8x8 - GHZ1.bin', and rename it to "8x8 - Title.bin". We're doing this because GHZ's tiles are split into two, and the title screen only uses the first half, but Sonic 2's art loader doesn't support this.
 +
 
 +
After that, take the following files and place them in a separate folder:
 +
 
 +
*8x8 - GHZ1.bin
 +
*8x8 - GHZ2.bin
 +
*8x8 - LZ.bin
 +
*8x8 - MZ.bin
 +
*8x8 - SBZ.bin
 +
*8x8 - SLZ.bin
 +
*8x8 - SYZ.bin
 +
 
 +
Inside the new folder, decompress all of these files, then delete them, leaving only the uncompressed files.
 +
 
 +
Now we need to combine GHZ's files into one, so it will work with Sonic 2's art loader. Open both '8x8 - GHZ1' and '8x8 - GHZ2' in a hex editor, and copy '8x8 - GHZ2' to the end of '8x8 - GHZ1'. After that, save the result as "8x8 - GHZ", and delete '8x8 - GHZ1' and '8x8 - GHZ2'.
  
For hivebrain users it's called MainLoadBlockLoad and this will be your code
+
Now compress all of the files in the Kosinski format and move them to the 'artkos' folder. Make sure the files still use the '.bin' extension - rename them if they're not.
  
 +
===Changing the pointers===
 +
Inside '''sonic.asm''', change this...
 
<syntaxhighlight lang="asm">
 
<syntaxhighlight lang="asm">
LoadZoneTiles:
+
Nem_GHZ_1st: incbin "artnem\8x8 - GHZ1.bin" ; GHZ primary patterns
 +
even
 +
Nem_GHZ_2nd: incbin "artnem\8x8 - GHZ2.bin" ; GHZ secondary patterns
 +
even</syntaxhighlight>
 +
 
 +
...to this
 +
<syntaxhighlight lang="asm">Nem_Title: incbin "artnem\8x8 - Title.bin" ; Title patterns
 +
even
 +
Kos_GHZ: incbin "artkos\8x8 - GHZ.bin" ; GHZ primary patterns
 +
even</syntaxhighlight>
 +
 
 +
Follow this pattern with the rest of the level art incbins: change the "Nem_" prefix to "Kos_", and change "artnem" to "artkos".
 +
 
 +
Now find this...
 +
<syntaxhighlight lang="asm"> lea (Nem_GHZ_1st).l,a0 ; load GHZ patterns
 +
bsr.w NemDec</syntaxhighlight>
 +
 
 +
...and change it to this:
 +
<syntaxhighlight lang="asm"> lea (Nem_Title).l,a0 ; load Title patterns
 +
bsr.w NemDec</syntaxhighlight>
 +
 
 +
This is simply correcting the title screen's pointer. Like in Sonic 2, the title art is still Nemesis.
 +
 
 +
Inside the "_inc" folder, find and open '''Pattern Load Cues.asm''' and '''LevelHeaders.asm'''.
 +
 
 +
In '''LevelHeaders.asm''', you can see entries for the level art, but, like the music entries, they are unused. As seen above, the level art is actually chosen by the pattern load cues. Sonic 2 would go on to actually use these entries, and, after the completion of this guide, your hack will too. But first, let's correct them to match our new labels.
 +
 
 +
*Nem_GHZ_2nd → Kos_GHZ
 +
 
 +
*Nem_LZ → Kos_LZ
 +
 
 +
*Nem_MZ → Kos_MZ
 +
 
 +
*Nem_SLZ → Kos_SLZ
 +
 
 +
*Nem_SYZ → Kos_SYZ
 +
 
 +
*Nem_SBZ → Kos_SBZ
 +
 
 +
Go to '''Pattern Load Cues.asm''' and comment out the entries for the level art. GHZ and the ending have two, the other levels have only the one.
 +
 
 +
===Porting the level art loading process===
 +
Back in '''sonic.asm''', above LevelDataLoad, paste this:
 +
 
 +
<syntaxhighlight lang="asm">LoadZoneTiles:
 
moveq #0,d0 ; Clear d0
 
moveq #0,d0 ; Clear d0
move.b ($FFFFFE10).w,d0 ; Load number of current zone to d0
+
move.b (v_zone).w,d0 ; Load number of current zone to d0
 
lsl.w #4,d0 ; Multiply by $10, converting the zone ID into an offset
 
lsl.w #4,d0 ; Multiply by $10, converting the zone ID into an offset
lea (MainLoadBlocks).l,a2 ; Load LevelHeaders's address into a2
+
lea (LevelHeaders).l,a2 ; Load LevelHeaders's address into a2
 
lea (a2,d0.w),a2 ; Offset LevelHeaders by the zone-offset, and load the resultant address to a2
 
lea (a2,d0.w),a2 ; Offset LevelHeaders by the zone-offset, and load the resultant address to a2
 
move.l (a2)+,d0 ; Move the first longword of data that a2 points to to d0, this contains the zone's first PLC ID and its art's address.
 
move.l (a2)+,d0 ; Move the first longword of data that a2 points to to d0, this contains the zone's first PLC ID and its art's address.
Line 156: Line 229:
 
jsr (QueueDMATransfer).l ; Use d1, d2, and d3 to locate the decompressed art and ready for transfer to VRAM
 
jsr (QueueDMATransfer).l ; Use d1, d2, and d3 to locate the decompressed art and ready for transfer to VRAM
 
move.w d7,-(sp) ; Store d7 in the Stack
 
move.w d7,-(sp) ; Store d7 in the Stack
move.b #$C,($FFFFF62A).w
+
move.b #$C,(v_vbla_routine).w
bsr.w DelayProgram
+
bsr.w WaitForVBla
bsr.w RunPLC_RAM
+
bsr.w RunPLC
 
move.w (sp)+,d7 ; Restore d7 from the Stack
 
move.w (sp)+,d7 ; Restore d7 from the Stack
 
move.w #$800,d3 ; Force the DMA transfer length to be $1000/2 (the first cycle is dynamic because the art's DMA'd backwards)
 
move.w #$800,d3 ; Force the DMA transfer length to be $1000/2 (the first cycle is dynamic because the art's DMA'd backwards)
Line 164: Line 237:
  
 
rts
 
rts
; End of function LoadZoneTiles
+
; End of function LoadZoneTiles</syntaxhighlight>
</syntaxhighlight>
 
  
 
This is Sonic 2's LoadZoneTiles, modified to match Sonic 1's LevelHeaders block size ($10 over Sonic 2's $12).
 
This is Sonic 2's LoadZoneTiles, modified to match Sonic 1's LevelHeaders block size ($10 over Sonic 2's $12).
Line 171: Line 243:
 
Now to add some branches to this code.
 
Now to add some branches to this code.
  
Go to Level_SkipTtlCard {loc_3946 for hivebrain users} and, above this:
+
Go to Level_SkipTtlCard and, above this:
 
<syntaxhighlight lang="asm"> bsr.w LevelDataLoad ; load block mappings and palettes</syntaxhighlight>
 
<syntaxhighlight lang="asm"> bsr.w LevelDataLoad ; load block mappings and palettes</syntaxhighlight>
  
Line 187: Line 259:
 
If you wish to stick with Kosinski, then simply replacing KosDec will do. If, however, you want to switch to Comper...
 
If you wish to stick with Kosinski, then simply replacing KosDec will do. If, however, you want to switch to Comper...
  
Use a KENSC compressor to recompress your art to Comper, then add CompDec to your disassembly. Lastly, go to LoadZoneTiles and replace this:
+
Use a FW-KENSC compressor to recompress your art to Comper, then add CompDec to your disassembly. Lastly, go to LoadZoneTiles and replace this:
 
<syntaxhighlight lang="asm"> bsr.w  KosDec</syntaxhighlight>
 
<syntaxhighlight lang="asm"> bsr.w  KosDec</syntaxhighlight>
  
Line 196: Line 268:
  
 
{{S1Howtos}}
 
{{S1Howtos}}
[[Category:SCHG How-tos|{{PAGENAME}}]]
+
|{{PAGENAME}}]]

Latest revision as of 10:52, 25 August 2018

(Original guide by Clownacy)

Fancy giving your loading screens a speed boost? Sonic 1's Nemesis-compressed art adds quite the chunk to the loading times, and you'll find yourself staring at that blue oval more than you would Sonic 2's blue screen. Porting Sonic 2's level art loading process cuts down on a significant amount of waiting time. So how do we do that?

Sonic 1's level art is loaded as part of the Zone-specific PLCs, forcing it to be Nemesis-compressed. Because of this, we'll need to remove this feature and then introduce Sonic 2's equivalent, which is separate, and by default uses a faster compression.

Grab yourself a compression tool. Some examples are KENS and
Sonic Retro
FW-KENSC
. Get familiar with them. If you use Windows, you could also use the
Sonic Retro
KENSSharp
or
Sonic Retro
FW-KENSC
shell extensions. Also, you'll want to have ported QueueDMATransfer.

Hivebrain

Recompression

First, we'll want to recompress our art from Nemesis to Kosinski, Sonic 2's standard level art compression.

Inside your 'artnem' folder, make a copy of '8x8ghz1.bin', and rename it to "8x8title.bin". We're doing this because GHZ's tiles are split into two, and the title screen only uses the first half, but Sonic 2's art loader doesn't support this.

After that, take the following files and place them in a separate folder:

  • 8x8ghz1.bin
  • 8x8ghz2.bin
  • 8x8lz.bin
  • 8x8mz.bin
  • 8x8sbz.bin
  • 8x8slz.bin
  • 8x8syz.bin

Inside the new folder, decompress all of these files, then delete them, leaving only the uncompressed files.

Now we need to combine GHZ's files into one, so it will work with Sonic 2's art loader. Open both '8x8ghz1' and '8x8ghz2' in a hex editor, and copy '8x8ghz2' to the end of '8x8ghz1'. After that, save the result as "8x8ghz", and delete '8x8ghz1' and '8x8ghz2'.

Now compress all of the files in the Kosinski format and move them to the 'artkos' folder. Make sure the files still use the '.bin' extension - rename them if they're not.

Changing the pointers

Inside sonic1.asm, change this...

Nem_GHZ_1st:	incbin	"artnem\8x8ghz1.bin"	; GHZ primary patterns
		even
Nem_GHZ_2nd:	incbin	"artnem\8x8ghz2.bin"	; GHZ secondary patterns
		even

...to this

Nem_Title:	incbin	"artnem\8x8title.bin"	; Title patterns
		even
Kos_GHZ:	incbin	"artkos\8x8ghz.bin"	; GHZ primary patterns
		even

Follow this pattern with the rest of the level art incbins: change the "Nem_" prefix to "Kos_", and change "artnem" to "artkos".

Now find this...

		lea	(Nem_GHZ_1st).l,a0 ; load GHZ patterns
		bsr.w	NemDec

...and change it to this:

		lea	(Nem_Title).l,a0 ; load Title patterns
		bsr.w	NemDec

This is simply correcting the title screen's pointer. Like in Sonic 2, the title art is still Nemesis.

Inside the "_inc" folder, find and open Pattern Load Cues.asm and Main level load blocks.asm.

In Main level load blocks.asm, you can see entries for the level art, but, like the music entries, they are unused. As seen above, the level art is actually chosen by the pattern load cues. Sonic 2 would go on to actually use these entries, and, after the completion of this guide, your hack will too. But first, let's correct them to match our new labels.

  • Nem_GHZ_2nd → Kos_GHZ
  • Nem_LZ → Kos_LZ
  • Nem_MZ → Kos_MZ
  • Nem_SLZ → Kos_SLZ
  • Nem_SYZ → Kos_SYZ
  • Nem_SBZ → Kos_SBZ

Go to Pattern Load Cues.asm and delete the entries for the level art. GHZ and the ending have two, the other levels have only the one.

Porting the level art loading process

Back in sonic1.asm, above MainLoadBlockLoad, paste this:

LoadZoneTiles:
		moveq	#0,d0			; Clear d0
		move.b	($FFFFFE10).w,d0		; Load number of current zone to d0
		lsl.w	#4,d0			; Multiply by $10, converting the zone ID into an offset
		lea	(MainLoadBlocks).l,a2	; Load LevelHeaders's address into a2
		lea	(a2,d0.w),a2		; Offset LevelHeaders by the zone-offset, and load the resultant address to a2
		move.l	(a2)+,d0		; Move the first longword of data that a2 points to to d0, this contains the zone's first PLC ID and its art's address.
						; The auto increment is pointless as a2 is overwritten later, and nothing reads from a2 before then
		andi.l	#$FFFFFF,d0    		; Filter out the first byte, which contains the first PLC ID, leaving the address of the zone's art in d0
		movea.l	d0,a0			; Load the address of the zone's art into a0 (source)
		lea	($FF0000).l,a1		; Load v_256x256/StartOfRAM (in this context, an art buffer) into a1 (destination)
		bsr.w	KosDec			; Decompress a0 to a1 (Kosinski compression)

		move.w	a1,d3			; Move a word of a1 to d3, note that a1 doesn't exactly contain the address of v_256x256/StartOfRAM anymore, after KosDec, a1 now contains v_256x256/StartOfRAM + the size of the file decompressed to it, d3 now contains the length of the file that was decompressed
		move.w	d3,d7			; Move d3 to d7, for use in seperate calculations

		andi.w	#$FFF,d3		; Remove the high nibble of the high byte of the length of decompressed file, this nibble is how many $1000 bytes the decompressed art is
		lsr.w	#1,d3			; Half the value of 'length of decompressed file', d3 becomes the 'DMA transfer length'

		rol.w	#4,d7			; Rotate (left) length of decompressed file by one nibble
		andi.w	#$F,d7			; Only keep the low nibble of low byte (the same one filtered out of d3 above), this nibble is how many $1000 bytes the decompressed art is

@loop:		move.w	d7,d2			; Move d7 to d2, note that the ahead dbf removes 1 byte from d7 each time it loops, meaning that the following calculations will have different results each time
		lsl.w	#7,d2
		lsl.w	#5,d2			; Shift (left) d2 by $C, making it high nibble of the high byte, d2 is now the size of the decompressed file rounded down to the nearest $1000 bytes, d2 becomes the 'destination address'

		move.l	#$FFFFFF,d1		; Fill d1 with $FF
		move.w	d2,d1			; Move d2 to d1, overwriting the last word of $FF's with d2, this turns d1 into 'StartOfRAM'+'However many $1000 bytes the decompressed art is', d1 becomes the 'source address'

		jsr	(QueueDMATransfer).l	; Use d1, d2, and d3 to locate the decompressed art and ready for transfer to VRAM
		move.w	d7,-(sp)		; Store d7 in the Stack
		move.b	#$C,($FFFFF62A).w
		bsr.w	DelayProgram
		bsr.w	RunPLC_RAM
		move.w	(sp)+,d7		; Restore d7 from the Stack
		move.w	#$800,d3		; Force the DMA transfer length to be $1000/2 (the first cycle is dynamic because the art's DMA'd backwards)
		dbf	d7,@loop		; Loop for each $1000 bytes the decompressed art is

		rts
; End of function LoadZoneTiles

This is Sonic 2's LoadZoneTiles, modified to match Sonic 1's Main level load blocks block size ($10 over Sonic 2's $12).

Now to add some branches to this code.

Go to loc_3946 and, above this:

		bsr.w	Main level load blocks ; load block mappings and palettes

Add this:

		bsr.w	LoadZoneTiles	; load level art

Do the same at End_LoadData.

And with that, we're done, a short guide for shorter loading times! Save and build.

GitHub

Recompression

First, we'll want to recompress our art from Nemesis to Kosinski, Sonic 2's standard level art compression.

Inside your 'artnem' folder, make a copy of '8x8 - GHZ1.bin', and rename it to "8x8 - Title.bin". We're doing this because GHZ's tiles are split into two, and the title screen only uses the first half, but Sonic 2's art loader doesn't support this.

After that, take the following files and place them in a separate folder:

  • 8x8 - GHZ1.bin
  • 8x8 - GHZ2.bin
  • 8x8 - LZ.bin
  • 8x8 - MZ.bin
  • 8x8 - SBZ.bin
  • 8x8 - SLZ.bin
  • 8x8 - SYZ.bin

Inside the new folder, decompress all of these files, then delete them, leaving only the uncompressed files.

Now we need to combine GHZ's files into one, so it will work with Sonic 2's art loader. Open both '8x8 - GHZ1' and '8x8 - GHZ2' in a hex editor, and copy '8x8 - GHZ2' to the end of '8x8 - GHZ1'. After that, save the result as "8x8 - GHZ", and delete '8x8 - GHZ1' and '8x8 - GHZ2'.

Now compress all of the files in the Kosinski format and move them to the 'artkos' folder. Make sure the files still use the '.bin' extension - rename them if they're not.

Changing the pointers

Inside sonic.asm, change this...

Nem_GHZ_1st:	incbin	"artnem\8x8 - GHZ1.bin"	; GHZ primary patterns
		even
Nem_GHZ_2nd:	incbin	"artnem\8x8 - GHZ2.bin"	; GHZ secondary patterns
		even

...to this

Nem_Title:	incbin	"artnem\8x8 - Title.bin"	; Title patterns
		even
Kos_GHZ:	incbin	"artkos\8x8 - GHZ.bin"	; GHZ primary patterns
		even

Follow this pattern with the rest of the level art incbins: change the "Nem_" prefix to "Kos_", and change "artnem" to "artkos".

Now find this...

		lea	(Nem_GHZ_1st).l,a0 ; load GHZ patterns
		bsr.w	NemDec

...and change it to this:

		lea	(Nem_Title).l,a0 ; load Title patterns
		bsr.w	NemDec

This is simply correcting the title screen's pointer. Like in Sonic 2, the title art is still Nemesis.

Inside the "_inc" folder, find and open Pattern Load Cues.asm and LevelHeaders.asm.

In LevelHeaders.asm, you can see entries for the level art, but, like the music entries, they are unused. As seen above, the level art is actually chosen by the pattern load cues. Sonic 2 would go on to actually use these entries, and, after the completion of this guide, your hack will too. But first, let's correct them to match our new labels.

  • Nem_GHZ_2nd → Kos_GHZ
  • Nem_LZ → Kos_LZ
  • Nem_MZ → Kos_MZ
  • Nem_SLZ → Kos_SLZ
  • Nem_SYZ → Kos_SYZ
  • Nem_SBZ → Kos_SBZ

Go to Pattern Load Cues.asm and comment out the entries for the level art. GHZ and the ending have two, the other levels have only the one.

Porting the level art loading process

Back in sonic.asm, above LevelDataLoad, paste this:

LoadZoneTiles:
		moveq	#0,d0			; Clear d0
		move.b	(v_zone).w,d0		; Load number of current zone to d0
		lsl.w	#4,d0			; Multiply by $10, converting the zone ID into an offset
		lea	(LevelHeaders).l,a2	; Load LevelHeaders's address into a2
		lea	(a2,d0.w),a2		; Offset LevelHeaders by the zone-offset, and load the resultant address to a2
		move.l	(a2)+,d0		; Move the first longword of data that a2 points to to d0, this contains the zone's first PLC ID and its art's address.
						; The auto increment is pointless as a2 is overwritten later, and nothing reads from a2 before then
		andi.l	#$FFFFFF,d0    		; Filter out the first byte, which contains the first PLC ID, leaving the address of the zone's art in d0
		movea.l	d0,a0			; Load the address of the zone's art into a0 (source)
		lea	($FF0000).l,a1		; Load v_256x256/StartOfRAM (in this context, an art buffer) into a1 (destination)
		bsr.w	KosDec			; Decompress a0 to a1 (Kosinski compression)

		move.w	a1,d3			; Move a word of a1 to d3, note that a1 doesn't exactly contain the address of v_256x256/StartOfRAM anymore, after KosDec, a1 now contains v_256x256/StartOfRAM + the size of the file decompressed to it, d3 now contains the length of the file that was decompressed
		move.w	d3,d7			; Move d3 to d7, for use in seperate calculations

		andi.w	#$FFF,d3		; Remove the high nibble of the high byte of the length of decompressed file, this nibble is how many $1000 bytes the decompressed art is
		lsr.w	#1,d3			; Half the value of 'length of decompressed file', d3 becomes the 'DMA transfer length'

		rol.w	#4,d7			; Rotate (left) length of decompressed file by one nibble
		andi.w	#$F,d7			; Only keep the low nibble of low byte (the same one filtered out of d3 above), this nibble is how many $1000 bytes the decompressed art is

@loop:		move.w	d7,d2			; Move d7 to d2, note that the ahead dbf removes 1 byte from d7 each time it loops, meaning that the following calculations will have different results each time
		lsl.w	#7,d2
		lsl.w	#5,d2			; Shift (left) d2 by $C, making it high nibble of the high byte, d2 is now the size of the decompressed file rounded down to the nearest $1000 bytes, d2 becomes the 'destination address'

		move.l	#$FFFFFF,d1		; Fill d1 with $FF
		move.w	d2,d1			; Move d2 to d1, overwriting the last word of $FF's with d2, this turns d1 into 'StartOfRAM'+'However many $1000 bytes the decompressed art is', d1 becomes the 'source address'

		jsr	(QueueDMATransfer).l	; Use d1, d2, and d3 to locate the decompressed art and ready for transfer to VRAM
		move.w	d7,-(sp)		; Store d7 in the Stack
		move.b	#$C,(v_vbla_routine).w
		bsr.w	WaitForVBla
		bsr.w	RunPLC
		move.w	(sp)+,d7		; Restore d7 from the Stack
		move.w	#$800,d3		; Force the DMA transfer length to be $1000/2 (the first cycle is dynamic because the art's DMA'd backwards)
		dbf	d7,@loop		; Loop for each $1000 bytes the decompressed art is

		rts
; End of function LoadZoneTiles

This is Sonic 2's LoadZoneTiles, modified to match Sonic 1's LevelHeaders block size ($10 over Sonic 2's $12).

Now to add some branches to this code.

Go to Level_SkipTtlCard and, above this:

		bsr.w	LevelDataLoad ; load block mappings and palettes

Add this:

		bsr.w	LoadZoneTiles	; load level art

Do the same at End_LoadData.

And with that, we're done, a short guide for shorter loading times! Save and build.

(Optional) Improvements

As mentioned before, this can be taken even further:

Go grab the
Sonic Retro
improved KosDec and NemDec
, along with
Sonic Retro
CompDec
. By using the improved KosDec, or switching to Comper compression, you can achieve even faster load times.

If you wish to stick with Kosinski, then simply replacing KosDec will do. If, however, you want to switch to Comper...

Use a FW-KENSC compressor to recompress your art to Comper, then add CompDec to your disassembly. Lastly, go to LoadZoneTiles and replace this:

		bsr.w   KosDec

with this

		bsr.w   CompDec

That's all. Enjoy your faster loading times!

SCHG How-To Guide: Sonic the Hedgehog (16-bit)
Fixing Bugs
Fix Demo Playback | Fix a Race Condition with Pattern Load Cues | Fix the SEGA Sound | Display the Press Start Button Text | Fix the Level Select Menu | Fix the Hidden Points Bug | Fix Accidental Deletion of Scattered Rings | Fix Ring Timers | Fix the Walk-Jump Bug | Correct Drowning Bugs | Fix the Death Boundary Bug | Fix the Camera Follow Bug | Fix Song Restoration Bugs | Fix the HUD Blinking | Fix the Level Select Graphics Bug | Fix a remember sprite related bug
Changing Design Choices
Change Spike Behavior | Collide with Water After Being Hurt | Fix Special Stage Jumping Physics | Improve the Fade In\Fade Out Progression Routines | Fix Scattered Rings' Underwater Physics | Remove the Speed Cap | Port the REV01 Background Effects | Port Sonic 2's Level Art Loader | Retain Rings Between Acts | Add Sonic 2 (Simon Wai Prototype) Level Select | Improve ObjectMove Subroutines | Port Sonic 2 Level Select
Adding Features
Add Spin Dash ( Part 1 / Part 2 / Part 3 / Part 4 ) | Add Eggman Monitor | Add Super Sonic | Add the Air Roll
Sound Features
Expand the Sound Index | Play Different Songs Per Act | Port Sonic 2 Final Sound Driver | Port Sonic 3's Sound Driver | Port Flamewing's Sonic 3 & Knuckles Sound Driver | Change The SEGA Sound
Extending the Game
Load Chunks From ROM | Add Extra Characters | Make an Alternative Title Screen | Use Dynamic Tilesets | Make GHZ Load Alternate Art | Make Ending Load Alternate Art | Add a New Zone | Set Up the Goggle Monitor | Add New Moves | Add a Dynamic Collision System | Dynamic Special Stage Walls System | Extend Sprite Mappings and Art Limit | Enigma Credits | Use Dynamic Palettes
Miscellaneous
Convert the Hivebrain 2005 Disassembly to ASM68K
Split Disassembly Guides
Set Up a Split Disassembly | Basic Level Editing | Basic Art Editing | Basic ASM Editing (Spin Dash)

|Port Sonic 2's Level Art Loader to Sonic 1]]