def do_extract_tim(file_path): header = 2048 # 0x800 print("Extracting {}".format(file_path)) # Open TIM file tim_file = open(file_path, "rb").read() # 4bpp and 8bpp TIM file if tim_file[0:4] == b'\x01\x00\x00\x00': tim_tag = b'\x10\x00\x00\x00' tim_bpp = b'' tim_clut_size = b'' if tim_file[20:22] == b'\x10\x00': # 4bpp tim_clut_size = ulong_to_bytes((bytes_to_uint(tim_file[24:26]) * 32 + 12)) tim_bpp = b'\x08\x00\x00\x00' elif tim_file[20:22] == b'\x00\x01': # 8bpp tim_clut_size = ulong_to_bytes((bytes_to_uint(tim_file[24:26]) * 512 + 12)) tim_bpp = b'\x09\x00\x00\x00' tim_fb_pal_x = tim_file[12:14] tim_fb_pal_y = tim_file[16:18] tim_colors = tim_file[20:22] tim_clut_num = tim_file[24:26] cluts = tim_file[256:256 + (bytes_to_uint(tim_clut_size) - 12)] tim_img_size = ulong_to_bytes((bytes_to_uint(tim_file[36:40]) * bytes_to_uint(tim_file[40:44]) * 2 + 12)) tim_fb_img_x = tim_file[28:30] tim_fb_img_y = tim_file[32:34] tim_width = tim_file[36:38] tim_height = tim_file[40:42] tim_pixel_data: bytes = tim_file[header:] # Reversing nibble order should fix MML PC TIM files # tim_pixel_data = bytes(((x << 4 & 0xF0) + (x >> 4)) for x in tim_pixel_data) ord_pixel_data = do_ord_pixel_data(tim_colors, tim_img_size, tim_width, tim_height, tim_pixel_data, encode=False) # Write the decoded TIM to file output_file = open(file_path.split(".")[0] + "_EXT.TIM", "wb") output_file.write( tim_tag + tim_bpp + tim_clut_size + tim_fb_pal_x + tim_fb_pal_y + tim_colors + tim_clut_num + cluts + tim_img_size + tim_fb_img_x + tim_fb_img_y + tim_width + tim_height + bytearray(ord_pixel_data) ) output_file.close() elif tim_file[0:4] == b'\x09\x00\x00\x00': print("CLUT only TIM file") elif tim_file[0:4] == b'\x0A\x00\x00\x00': print("CLUT Patch inside TIM file")
def do_extract_font(file_path): print("Extracting {}".format(file_path)) # Open FONT file font_file = open(file_path, "rb").read() # Create Header of TIM tim_tag = b'\x10\x00\x00\x00' tim_bpp = b'\x08\x00\x00\x00' tim_clut_size = b'\x2c\x00\x00\x00' tim_fb_pal_x = b'\x00\x00' # Not known but needed tim_fb_pal_y = b'\x00\x00' # Not known but needed tim_colors = b'\x10\x00' tim_clut_num = b'\x01\x00' # Palette = #000, #FFF, #BBB, #888 clut = b'\x00\x00\xFF\xFF\xF7\xDE\xEF\xBD\x00\x00\x00\x00\x00\x00\x00\x00' \ b'\x00\x00\xFF\xFF\xF7\xDE\xEF\xBD\x00\x00\x00\x00\x00\x00\x00\x00' tim_img_size = ulong_to_bytes((img_width // 4) * img_height * 2 + 12) tim_fb_img_x = b'\x00\x00' # Not known but needed tim_fb_img_y = b'\x00\x00' # Not known but needed tim_width = b'\x40\x00' tim_height = b'\x00\x02' tim_pixel_data = font_file[2048:] decoded_pixel_data_top = [0] * ((bytes_to_uint(tim_img_size) - 12) // 2) decoded_pixel_data_bottom = [0] * ((bytes_to_uint(tim_img_size) - 12) // 2) offset = 0 for y in range(0, img_height // 2, block_height): for x in range(0, img_width, block_width): for by in range(0, block_height): for bx in range(0, block_width, 2): px = tim_pixel_data[offset] a = px & 0x03 b = px & 0x30 c = (px >> 2) & 0x03 d = (px >> 2) & 0x30 decoded_pixel_data_top[((y + by) * (img_width // 2)) + ((x + bx) // 2)] = b + a decoded_pixel_data_bottom[((y + by) * (img_width // 2)) + ((x + bx) // 2)] = d + c offset += 1 output_file = open(file_path.split(".")[0] + ".TIM", "wb") output_file.write(tim_tag + tim_bpp + tim_clut_size + tim_fb_pal_x + tim_fb_pal_y + tim_colors + tim_clut_num + clut + tim_img_size + tim_fb_img_x + tim_fb_img_y + tim_width + tim_height + bytearray(decoded_pixel_data_top) + bytearray(decoded_pixel_data_bottom)) output_file.close() print("\nFound font. Extraction complete.")
def do_insert_msg(original_msg, text_file): print("\nInserting {}".format(original_msg)) # Open TXT file text = open(text_file, "r").read() # Open MSG file msg_file = open(original_msg, "rb+") # Read the actual file size msg_file.seek(4) file_size = bytes_to_uint(msg_file.read(4)) # Read pointer table size msg_file.seek(header) ptr_tbl_size = bytes_to_uint(msg_file.read(2)) ptr_table: list = [ptr_tbl_size] print("Pointer table size is {} blocks ({} bytes)".format( ptr_tbl_size // 2, ptr_tbl_size)) i = 0 current_block = 1 encoded_block: list = [] while i < len(text): # Skip the [Blocks] text but separate them for recalculating pointers c = text[i] if c == '[': # Find the end of the textual Block info and start of next block_end = text.find(']', i + 1) next_block_start = text.find('[', i + 1) # If there is no more blocks after, read until the end of the file if next_block_start == -1: text_block = text[block_end + 2:-2] # If there is another block after, read until the beginning of the next block else: text_block = text[ block_end + 2:next_block_start - 2] # Remove the \n\n at beginning and end of block # Encode text block to list of bytes encoded_text_block = do_encode_text_block(text_block) encoded_block.append(encoded_text_block) if current_block != ptr_tbl_size // 2: # Append the size of the encoded block to the pointer table ptr_table.append(ptr_table[-1] + len(encoded_text_block)) current_block += 1 i += 1 # Convert pointer table int to sequence of bytes ptr_table_bytes: bytes = b'' for items in ptr_table: if not items == file_size: ptr_table_bytes += (uint_to_bytes(items)) # Convert list of lists of encoded blocks to bytes encoded_block_bytes = bytes( [val for sublist in encoded_block for val in sublist]) print("Encoded text data size is {} bytes".format( len(encoded_block_bytes))) # Get size of file msg_file_size = msg_file.seek(0, os.SEEK_END) # Get size of new file new_file_size = header + len(ptr_table_bytes) + len(encoded_block_bytes) # Check if the new size is within the limits. Else throw and exception if new_file_size > msg_file_size: print( "ERROR: New MSG file is {} bytes. Size limit is {} bytes.".format( new_file_size, msg_file_size)) exit() else: # Write new data to the MSG file msg_file.seek(header) msg_file.write(ptr_table_bytes + encoded_block_bytes) # Get offset after writing pointer table and encoded text current_offset = msg_file.tell() # Write the new file size to the header msg_file.seek(4) msg_file.write(ulong_to_bytes(current_offset - header)) msg_file.seek(current_offset) msg_file.seek(current_offset) # Fill the rest of the file with 00 while current_offset < msg_file_size: msg_file.write(b'\x00') current_offset += 1 msg_file.close()