3434# that people want to expand on this goofy project and worry about
3535# compatibility, we define a constant for the file format that is written into
3636# the output file.
37- FILE_FORMAT = 3
37+ FILE_FORMAT = 4
3838
3939# Number of tiles (w, h) for fullscreen and thumbnail sizes.
4040FULLSCREEN_TILES = (32 , 28 )
@@ -493,7 +493,7 @@ def dump_debug_file(frame_dir, audio_dir, args):
493493 run (args .debug , check = True , args = ffmpeg_args )
494494
495495
496- def png_to_sega_frame (in_path , out_file , expected_tiles ):
496+ def png_to_sega_frame (in_path , out_file , expected_tiles , dedup , pal = 0 ):
497497 img = Image .open (in_path )
498498 pixels = img .getdata ()
499499 width , height = img .size
@@ -513,6 +513,7 @@ def png_to_sega_frame(in_path, out_file, expected_tiles):
513513
514514 # Each tile is 8x8 pixels, 4 bit palette index per pixel.
515515 binary_tiles = b''
516+ tile_map = []
516517
517518 # The image dimensions should each be a multiple of 8 already.
518519 assert width % 8 == 0 and height % 8 == 0
@@ -522,6 +523,8 @@ def png_to_sega_frame(in_path, out_file, expected_tiles):
522523 # We should have a fullscreen image.
523524 assert (tiles_width , tiles_height ) == expected_tiles
524525
526+ binary_tile_to_index = {}
527+
525528 for tile_y in range (tiles_height ):
526529 for tile_x in range (tiles_width ):
527530 tile = []
@@ -539,15 +542,33 @@ def png_to_sega_frame(in_path, out_file, expected_tiles):
539542
540543 tile .append (palette_index )
541544
542- binary_tiles += pack_tile (tile )
545+ binary_tile = pack_tile (tile )
546+ index = binary_tile_to_index .get (binary_tile , None )
547+ if dedup == False :
548+ index = None
549+
550+ if index is None :
551+ index = len (binary_tile_to_index )
552+ binary_tile_to_index [binary_tile ] = index
553+ binary_tiles += binary_tile
554+
555+ tile_map .append (index | (pal << 13 ))
543556
544- # SegaVideoFrameHeader contains the palette only
557+ # Output the palette
545558 out_file .write (pack_palette (palette ))
559+
560+ if dedup == True :
561+ # Pack the tile map
562+ out_file .write (pack_tile_map (tile_map ))
563+ # The number of tiles to follow
564+ out_file .write (len (binary_tile_to_index ).to_bytes (4 , 'big' ))
565+
546566 # Actual tile data follows
547567 out_file .write (binary_tiles )
548568
549- # A palette is always 32 bytes
550- return 32 + len (binary_tiles )
569+ # A palette is always 32 bytes, the tile map is 1792 bytes, the number of
570+ # tiles is encoded in 4 bytes.
571+ return 32 + 1792 + 4 + len (binary_tiles )
551572
552573
553574def sega_color_map (value ):
@@ -587,6 +608,13 @@ def pack_tile(palette_indexes):
587608 return packed
588609
589610
611+ def pack_tile_map (tile_indexes ):
612+ packed = b''
613+ for idx in tile_indexes :
614+ packed += idx .to_bytes (2 , 'big' )
615+ return packed
616+
617+
590618def pack_palette (palette ):
591619 packed = b''
592620 assert (len (palette ) <= 16 )
@@ -654,7 +682,8 @@ def write_chunk(f, state):
654682 chunk_frame_data_len = 0
655683 for i in range (chunk_frame_count ):
656684 input_path = state .frame_paths [state .frame_path_index ]
657- bytes_written = png_to_sega_frame (input_path , f , FULLSCREEN_TILES )
685+ bytes_written = png_to_sega_frame (input_path , f , FULLSCREEN_TILES ,
686+ dedup = True , pal = (1 if (i % 2 ) else 0 ))
658687 chunk_frame_data_len += bytes_written
659688 state .frame_count -= 1
660689 state .frame_path_index += 1
@@ -854,7 +883,7 @@ def generate_thumbnail(args, fullcolor_dir, thumb_dir):
854883
855884 # Then convert to Sega format.
856885 with open (sega_frame_out , 'wb' ) as f :
857- png_to_sega_frame (thumb_out , f , THUMBNAIL_TILES )
886+ png_to_sega_frame (thumb_out , f , THUMBNAIL_TILES , dedup = False )
858887
859888 print ('Thumbnail generated from frame #{}.' .format (thumb_index + 1 ))
860889
0 commit comments