66#include "libsm64.h"
77#include "utils.h"
88
9- #define SM64COMPRESS_VERSION "0.1.1a "
9+ #define SM64COMPRESS_VERSION "0.2a "
1010
1111#define MAX_REFS 64
1212
1313typedef struct
1414{
1515 unsigned int level ; // original level script offset where referenced
1616 unsigned int offset ; // offset within level script where referenced
17- unsigned char type ; // command type: 0x1A, 0x18, or 0xFF for ASM
17+ unsigned char type ; // command type: 0x1A, 0x18, or 0xFF, 0xFE, 0xFD for ASM
1818} block_ref ;
1919
2020typedef struct
2121{
22- unsigned int old ; // starting offset in original ROM
23- unsigned int old_end ; // ending offset in original ROM
24- unsigned int new ; // starting offset in new ROM
25- unsigned int new_end ; // ending offset in new ROM
26- block_ref refs [MAX_REFS ]; // references to this block
27- int ref_count ; // number of references
22+ unsigned int old ; // starting offset in original ROM
23+ unsigned int old_end ; // ending offset in original ROM
24+ unsigned int new ; // starting offset in new ROM
25+ unsigned int new_end ; // ending offset in new ROM
26+ block_ref refs [MAX_REFS ]; // references to this block
27+ int ref_count ; // number of references
28+ char compressible ; // if block is not currenlty, but potentially compressible
2829 enum {
2930 BLOCK_LEVEL ,
3031 BLOCK_MIO0 ,
3132 BLOCK_RAW
3233 } type ;
33- char compressible ; // whether or not block is compressible
3434} block ;
3535
3636typedef struct
@@ -49,7 +49,7 @@ static const compress_config default_config =
4949{
5050 NULL , // input filename
5151 NULL , // output filename
52- 16 , // MIO0 alignment
52+ 16 , // block alignment
5353 0 , // compress all MIO0 blocks
5454 0 , // dump
5555 0 , // f3d
@@ -58,20 +58,21 @@ static const compress_config default_config =
5858
5959static void print_usage (void )
6060{
61- ERROR ("Usage: sm64compress [-c ] [-v] FILE [OUT_FILE]\n"
61+ ERROR ("Usage: sm64compress [-a ALIGNMENT] [-c] [-d] [-f] [-g ] [-v] FILE [OUT_FILE]\n"
6262 "\n"
63- "sm64compress v" SM64COMPRESS_VERSION ": Super Mario 64 ROM compressor\n"
63+ "sm64compress v" SM64COMPRESS_VERSION ": Super Mario 64 ROM compressor and fixer \n"
6464 "\n"
6565 "Optional arguments:\n"
66- " -a ALIGNMENT byte boundary to align MIO0 blocks (default: %d)\n"
67- " -c compress all blocks using MIO0\n"
66+ " -a ALIGNMENT byte boundary to align blocks (default: %d)\n"
67+ " -c compress all 0x17 blocks using MIO0 (experimental)\n"
68+ " -d dump blocks to 'dump' directory\n"
6869 " -f fix F3D combine blending parameters\n"
6970 " -g fix geo layout display list layers\n"
7071 " -v verbose progress output\n"
7172 "\n"
7273 "File arguments:\n"
7374 " FILE input ROM file\n"
74- " OUT_FILE output shrunk ROM file (default: replaces input extension with .out.z64)\n" ,
75+ " OUT_FILE output compressed ROM file (default: replaces input extension with .out.z64)\n" ,
7576 default_config .alignment );
7677 exit (1 );
7778}
@@ -342,7 +343,6 @@ static int sm64_compress_mio0(const compress_config *config,
342343#define SEGMENT2_ROM_OFFSET 0x800000
343344#define SEGMENT2_ROM_END 0x81BB64
344345 block block_table [MAX_BLOCKS ];
345- unsigned char * src ;
346346 unsigned char * tmp_raw = NULL ;
347347 unsigned char * tmp_cmp = NULL ;
348348 int block_count = 0 ;
@@ -395,23 +395,38 @@ static int sm64_compress_mio0(const compress_config *config,
395395 blk -> new = blk -> old ;
396396 blk -> new_end = blk -> old_end ;
397397 } else {
398+ unsigned char * src ;
398399 int src_len ;
400+ int block_len = blk -> old_end - blk -> old ;
399401 // implement fixes
400402 // TODO: this is liberally applied to all data
401403 // TODO: this assumes fake MIO0 headers
402404 if (config -> fix_f3d ) {
403- fix_f3d (& in_buf [blk -> old ], blk -> old_end - blk -> old );
405+ fix_f3d (& in_buf [blk -> old ], block_len );
404406 }
405407 if (config -> fix_geo ) {
406- fix_geo (& in_buf [blk -> old ], blk -> old_end - blk -> old );
408+ fix_geo (& in_buf [blk -> old ], block_len );
407409 }
408410 if (config -> compress && blk -> type == BLOCK_MIO0 ) {
409- // TODO: this decompression step may be unnecesary if it is a fake header
411+ // decompress to remove fake header and recompress
410412 int raw_len = mio0_decode (& in_buf [blk -> old ], tmp_raw , NULL );
411413 int cmp_len = mio0_encode (tmp_raw , raw_len , tmp_cmp );
412414 src = tmp_cmp ;
413415 src_len = cmp_len ;
414- INFO ("Compressed %08X %06X=%06X => %06X\n" , blk -> old , blk -> old_end - blk -> old , raw_len , cmp_len );
416+ INFO ("Compressed %08X %06X=%06X => %06X\n" , blk -> old , block_len , raw_len , cmp_len );
417+ } else if (config -> compress && blk -> compressible ) {
418+ // decompress to remove fake header and recompress
419+ int cmp_len = mio0_encode (& in_buf [blk -> old ], block_len , tmp_cmp );
420+ src = tmp_cmp ;
421+ src_len = cmp_len ;
422+ INFO ("Compressed %08X %06X => %06X\n" , blk -> old , block_len , cmp_len );
423+ for (int r = 0 ; r < blk -> ref_count ; r ++ ) {
424+ if (blk -> refs [r ].type == 0x17 ) {
425+ blk -> refs [r ].type = 0x18 ;
426+ } else {
427+ ERROR ("Block %08X ref %X:%X type = %02X\n" , blk -> old , blk -> refs [r ].level , blk -> refs [r ].offset , blk -> refs [r ].type );
428+ }
429+ }
415430 } else {
416431 src = & in_buf [blk -> old ];
417432 src_len = blk -> old_end - blk -> old ;
@@ -443,15 +458,15 @@ static int sm64_compress_mio0(const compress_config *config,
443458 addr_high ++ ;
444459 }
445460 write_u16_be (& out_buf [offset + 0x2 ], addr_high );
446- write_u16_be (& out_buf [offset + 0xe ], addr_low );
461+ write_u16_be (& out_buf [offset + 0xE ], addr_low );
447462
448463 addr_low = blk -> new_end & 0xFFFF ;
449464 addr_high = (blk -> new_end >> 16 ) & 0xFFFF ;
450465 if (addr_low & 0x8000 ) {
451466 addr_high ++ ;
452467 }
453468 write_u16_be (& out_buf [offset + 0x6 ], addr_high );
454- write_u16_be (& out_buf [offset + 0xa ], addr_low );
469+ write_u16_be (& out_buf [offset + 0xA ], addr_low );
455470 INFO ("Updated ASM @ %08X: %08X %08X %08X %08X\n" , offset ,
456471 read_u32_be (& out_buf [offset + 0 ]), read_u32_be (& out_buf [offset + 4 ]),
457472 read_u32_be (& out_buf [offset + 8 ]), read_u32_be (& out_buf [offset + 0xC ]));
@@ -480,7 +495,7 @@ static int sm64_compress_mio0(const compress_config *config,
480495 write_u16_be (& out_buf [0xD4788 + 0x2 ], addr_low );
481496 INFO ("Updated ASM @ %08X: %08X %08X\n" , 0xD4784 ,
482497 read_u32_be (& out_buf [0xD4784 + 0 ]), read_u32_be (& out_buf [0xD4784 + 4 ]));
483- } else if (blk -> refs [r ].type == 0xFD ) { // sequence bank
498+ } else if (blk -> refs [r ].type == 0xFD ) { // some other data
484499 unsigned addr_low = blk -> new & 0xFFFF ;
485500 unsigned addr_high = (blk -> new >> 16 ) & 0xFFFF ;
486501 INFO ("Updating ASM @ %08X: %08X %08X %08X %08X %08X\n" , 0x101BB0 ,
@@ -510,9 +525,10 @@ static int sm64_compress_mio0(const compress_config *config,
510525 } else {
511526 block * level = & block_table [level_idx ];
512527 unsigned offset = level -> new + blk -> refs [r ].offset ;
513- INFO ("Updating @ %08X:%08X %08X-%08X to %08X-%08X\n" , level -> old , level -> new ,
514- read_u32_be (& out_buf [offset + 4 ]), read_u32_be (& out_buf [offset + 8 ]),
515- blk -> new , blk -> new_end );
528+ INFO ("Updating @ %08X:%08X %02X %08X-%08X to %02X %08X-%08X\n" , level -> old , level -> new ,
529+ out_buf [offset ], read_u32_be (& out_buf [offset + 4 ]), read_u32_be (& out_buf [offset + 8 ]),
530+ blk -> refs [r ].type , blk -> new , blk -> new_end );
531+ out_buf [offset ] = blk -> refs [r ].type ;
516532 write_u32_be (& out_buf [offset + 4 ], blk -> new );
517533 write_u32_be (& out_buf [offset + 8 ], blk -> new_end );
518534 }
@@ -527,17 +543,16 @@ static int sm64_compress_mio0(const compress_config *config,
527543 out_buf [0xd48b7 ] = 0x5C ;
528544 }
529545
546+ #define DUMP_DIR "dump"
530547 if (config -> dump ) {
531- make_dir ("dump" );
548+ make_dir (DUMP_DIR );
532549 for (int i = 0 ; i < block_count ; i ++ ) {
550+ char fname [FILENAME_MAX ];
533551 block * blk = & block_table [i ];
534- if (blk -> type == BLOCK_LEVEL ) {
535- char fname [512 ];
536- sprintf (fname , "dump/%07X.%07X.old.bin" , blk -> old , blk -> old );
537- (void )write_file (fname , & in_buf [blk -> old ], blk -> old_end - blk -> old );
538- sprintf (fname , "dump/%07X.%07X.new.bin" , blk -> old , blk -> new );
539- (void )write_file (fname , & out_buf [blk -> new ], blk -> new_end - blk -> new );
540- }
552+ sprintf (fname , "%s/%07X.%07X.old.bin" , DUMP_DIR , blk -> old , blk -> old );
553+ (void )write_file (fname , & in_buf [blk -> old ], blk -> old_end - blk -> old );
554+ sprintf (fname , "%s/%07X.%07X.new.bin" , DUMP_DIR , blk -> old , blk -> new );
555+ (void )write_file (fname , & out_buf [blk -> new ], blk -> new_end - blk -> new );
541556 }
542557 }
543558
@@ -587,7 +602,7 @@ int main(int argc, char *argv[])
587602 // copy first 8MB from input to output
588603 memcpy (out_buf , in_buf , 8 * MB );
589604
590- // compact the SM64 MIO0 files and adjust pointers
605+ // compact the SM64 blocks and adjust pointers
591606 out_size = sm64_compress_mio0 (& config , in_buf , in_size , out_buf );
592607
593608 // update N64 header CRC
@@ -600,5 +615,7 @@ int main(int argc, char *argv[])
600615 exit (1 );
601616 }
602617
618+ INFO ("Size: %dMB -> %dMB\n" , (int )in_size /(1 * MB ), (int )out_size /(1 * MB ));
619+
603620 return 0 ;
604621}
0 commit comments