def death_bank(direction, sprite): len = 0x3F60 image = rom_inject.compile_death_image(direction, sprite) return bytes( itertools.chain.from_iterable( common.convert_to_4bpp(image, (0, 0), (0, 16 * i, 128, 16 * (i + 1)), None) for i in range(16)))[:len]
def file_select(sprite): file_select_sprites = Image.new("P", (128, 24), 0) for image_name, src, dest in [ ("file_select_head", None, (0, 0)), ("file_select_head1", None, (24, 0)), ("file_select_head2", None, (48, 0)), ("file_select_visor", None, (72, 0)), ("file_select_visor1", None, (88, 0)), ("file_select_visor2", None, (104, 0)), ("file_select_visor3", None, (72, 8)), ("file_select_visor4", None, (88, 8)), ("file_select_cursor_array", (0, 24, 8, 32), (112, 16)), ("file_select_cursor_array", (0, 16, 8, 24), (112, 8)), ("file_select_cursor_array", (0, 8, 8, 16), (120, 0)), ("file_select_cursor_array", (8, 8, 16, 16), (120, 8)), ("file_select_cursor_array", (8, 16, 16, 24), (120, 16)), ("file_select_piping", (0, 0, 24, 8), (72, 16)), # Top ("file_select_piping", (16, 8, 24, 24), (104, 8)), # Side ("file_select_piping", (0, 16, 8, 24), (96, 16)), # Corner ]: source_image = sprite.images[image_name] source_image = source_image.crop(src) if src else source_image file_select_sprites.paste(source_image, dest) cursor_array = sprite.images["file_select_cursor_array"] file_select_missile = cursor_array.crop((0, 0, 8, 8)) file_select_missile_head = cursor_array.crop((8, 24, 16, 32)) data = bytearray() # Due to how convert_to_4bpp package the data we must first get a whole sheet row, then extract the third row (first 0x200 bytes) data.extend( common.convert_to_4bpp(file_select_sprites, (0, 0), (0, 0, 128, 16), None)) data.extend( common.convert_to_4bpp(file_select_sprites, (0, 0), (0, 16, 128, 32), None)[:0x200]) data.extend( common.convert_to_4bpp(file_select_missile, (0, 0), (0, 0, 8, 8), None)) data.extend( common.convert_to_4bpp(file_select_missile_head, (0, 0), (0, 0, 8, 8), None)) return data
def get_binary_sprite_sheet(self): top_half_of_rows = bytearray() bottom_half_of_rows = bytearray() # 28 rows, 8 columns for image_name in [f"{row}{column}" for row in itertools.chain(ascii_uppercase, ["AA","AB"]) for column in range(8)]: # AB7 holds the palette block so use null_block instead image_name = image_name if image_name != "AB7" else "null_block" raw_image = common.convert_to_4bpp(self.images[image_name],(0,0),(0,0,16,16),None) top_half_of_rows += bytes(raw_image[:0x40]) bottom_half_of_rows += bytes(raw_image[0x40:]) return bytes(b for row_offset in range(0,len(top_half_of_rows),0x200) \ for b in top_half_of_rows[row_offset:row_offset+0x200]+bottom_half_of_rows[row_offset:row_offset+0x200])
def inject_into_ROM(self, rom): #should work for the combo rom, VT rando, and the (J) rom. Not sure about the (U) rom...maybe? #the sheet needs to be placed directly into address $108000-$10F000 for i,row in enumerate(itertools.chain(ascii_uppercase, ["AA","AB"])): #over all 28 rows of the sheet for column in range(8): #over all 8 columns image_name = f"{row}{column}" if image_name == "AB7": #AB7 is special, because the palette block sits there in the PNG, so this can't be actually used image_name = "null_block" raw_image_data = common.convert_to_4bpp(self.images[image_name], (0,0), (0,0,16,16), None) rom.bulk_write_to_snes_address(0x108000+0x400*i+0x40*column,raw_image_data[:0x40],0x40) rom.bulk_write_to_snes_address(0x108200+0x400*i+0x40*column,raw_image_data[0x40:],0x40) #the palettes need to be placed directly into address $1BD308-$1BD380, not including the transparency or gloves colors converted_palette = common.convert_to_555(self.master_palette) for i in range(4): rom.write_to_snes_address(0x1BD308+0x1E*i,converted_palette[0x10*i+1:0x10*i+0x10],0x0F*"2") #the glove colors are placed into $1BEDF5-$1BEDF8 for i in range(2): rom.write_to_snes_address(0x1BEDF5+0x02*i,converted_palette[0x10+0x10*i],2) return rom
def inject_into_ROM(self, spiffy_dict, rom): #should work for the combo rom, VT rando #should work for the (J) & (U) ROMs but won't automatically include the extra code needed to manage gloves, etc #this'll check VT rando Tournament Flag tournament_flag = rom.read(0x180213, 2) == 1 #this'll check combo Tournament Flag if rom.type() == "EXHIROM" and not tournament_flag: config = rom.read_from_snes_address(0x80FF52, 2) fieldvals = {} fieldvals["gamemode"] = ["singleworld", "multiworld"] fieldvals["z3logic"] = ["normal", "hard"] fieldvals["m3logic"] = ["normal", "hard"] field = {} field["race"] = ((config & (1 << 15)) >> 15) > 0 # 1 bit field["keysanity"] = ((config & (0b11 << 13)) >> 13) > 0 # 2 bits field["gamemode"] = ((config & (1 << 12)) >> 12) # 1 bit field["z3logic"] = ((config & (0b11 << 10)) >> 10) # 2 bits field["m3logic"] = ((config & (0b11 << 8)) >> 8) # 2 bits field["version"] = {} field["version"]["major"] = ( (config & (0b1111 << 4)) >> 4) # 4 bits field["version"]["minor"] = ( (config & (0b1111 << 0)) >> 0) # 4 bits field["gamemode"] = fieldvals["gamemode"][field["gamemode"]] field["z3logic"] = fieldvals["z3logic"][field["z3logic"]] field["m3logic"] = fieldvals["m3logic"][field["m3logic"]] tournament_flag = field["race"] if not tournament_flag: #the sheet needs to be placed directly into address $108000-$10F000 for i, row in enumerate( itertools.chain( ascii_uppercase, ["AA", "AB"])): #over all 28 rows of the sheet for column in range(8): #over all 8 columns image_name = f"{row}{column}" if image_name == "AB7": #AB7 is special, because the palette block sits there in the PNG, so this can't be actually used image_name = "null_block" raw_image_data = common.convert_to_4bpp( self.images[image_name], (0, 0), (0, 0, 16, 16), None) rom.bulk_write_to_snes_address( 0x108000 + 0x400 * i + 0x40 * column, raw_image_data[:0x40], 0x40) rom.bulk_write_to_snes_address( 0x108200 + 0x400 * i + 0x40 * column, raw_image_data[0x40:], 0x40) #the palettes need to be placed directly into address $1BD308-$1BD380, not including the transparency or gloves colors converted_palette = common.convert_to_555(self.master_palette) for i in range(4): rom.write_to_snes_address( 0x1BD308 + 0x1E * i, converted_palette[0x10 * i + 1:0x10 * i + 0x10], 0x0F * "2") #the glove colors are placed into $1BEDF5-$1BEDF8 for i in range(2): rom.write_to_snes_address(0x1BEDF5 + 0x02 * i, converted_palette[0x10 + 0x10 * i], 2) if (hex(rom.read_from_snes_address(0x238000, 2)) == "0x3702") and (hex( rom.read_from_snes_address(0x23801E, 2)) == "0x3702"): # print("v32-compatible credits") contiguous = digits + ascii_uppercase + "'" letters = { "hi": { " ": "0x9F", '.': "0xA0", '/': "0xA2", ':': "0xA3", '_': "0xA6" }, "lo": { " ": "0x9F", '.': "0xC0", '/': "0xC2", ':': "0xC3", '_': "0xC6" } } for i, ltr in enumerate(itertools.chain(contiguous)): letters["hi"][ltr] = hex(i + 83).upper().replace( "0X", "0x") letters["lo"][ltr] = hex(i + 83 + 38).upper().replace( "0X", "0x") msg = { "hi": { "ascii": "", "rom": { "hex": [], "dec": [] } }, "lo": { "ascii": "", "rom": { "hex": [], "dec": [] } } } author = "" author_short = "" if "author.name" in self.metadata: author = self.metadata["author.name"] if "author.name-short" in self.metadata: author_short = self.metadata["author.name-short"] char_class = "a-zA-Z0-9\'\.\/\:\_ " pattern = r'^([' + char_class + ']+)$' antipattern = r'([^' + char_class + '])' linelen = 32 if len(author) <= linelen: matches = re.match(pattern, author) if matches: author = matches.groups(0)[0] else: author = re.sub(antipattern, "", author) if len(author_short) <= linelen: matches = re.match(pattern, author_short) if matches: author_short = matches.groups(0)[0] else: author_short = re.sub(antipattern, "", author_short) if len(author_short) > len(author): author = author_short author = author.upper() lpad = int((linelen - len(author)) / 2) - 2 author = author.rjust(lpad + len(author)).ljust(linelen) for i, ltr in enumerate(itertools.chain(author)): msg["hi"]["ascii"] += ltr msg["lo"]["ascii"] += ltr msg["hi"]["rom"]["hex"].append(letters["hi"][ltr]) msg["lo"]["rom"]["hex"].append(letters["lo"][ltr]) msg["hi"]["rom"]["dec"].append(int(letters["hi"][ltr], 16)) msg["lo"]["rom"]["dec"].append(int(letters["lo"][ltr], 16)) # print(msg) rom.bulk_write_to_snes_address(0x238002, msg["hi"]["rom"]["dec"], 0x20) rom.bulk_write_to_snes_address(0x238020, msg["lo"]["rom"]["dec"], 0x20) else: # FIXME: English raise AssertionError(f"Cannot inject into a Race/Tournament ROM!") return rom