def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: string_id = STRING_ID_US overlay_size = OVERLAY13_INITAL_SIZE_US if config.game_region == GAME_REGION_EU: string_id = STRING_ID_EU overlay_size = OVERLAY13_INITAL_SIZE_EU if config.game_region == GAME_REGION_JP: string_id = STRING_ID_JP overlay_size = OVERLAY13_INITAL_SIZE_JP # Change dialogue for lang in config.string_index_data.languages: filename = 'MESSAGE/' + lang.filename bin_before = rom.getFileByName(filename) strings = StrHandler.deserialize(bin_before) strings.strings[string_id - 1] = get_locales().translate( MESSAGE, lang.locale.replace('-', '_')) bin_after = StrHandler.serialize(strings) rom.setFileByName(filename, bin_after) table = loadOverlayTable(rom.arm9OverlayTable, lambda x, y: bytes()) ov = table[13] ov.ramSize = overlay_size + OVERLAY13_ADD_SIZE rom.arm9OverlayTable = saveOverlayTable(table) ov13 = rom.files[ov.fileID] rom.files[ov.fileID] = ov13[:overlay_size] try: apply() except RuntimeError as ex: raise ex
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: if not self.is_applied(rom, config): if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: bar_list = BAR_LIST_US if config.game_region == GAME_REGION_EU: bar_list = BAR_LIST_EU if config.game_region == GAME_REGION_JP: bar_list = BAR_LIST_JP data = rom.loadArm9Overlays([19])[19].data header = bytearray([0xFF] * (4 + 2 * NB_ITEMS)) write_u32(header, u32(4 + 2 * NB_ITEMS), 0) list_data: List[bytes] = [] for x in range(bar_list, bar_list + BAR_LIST_SIZE, BAR_LIST_ENTRY_SIZE): item_id = read_u16(data, x) cdata = bytes(data[x + 2:x + BAR_LIST_ENTRY_SIZE]) if cdata in list_data: index = list_data.index(cdata) else: index = len(list_data) list_data.append(cdata) write_u16(header, u16_checked(index), 4 + 2 * item_id) file_data = header + b''.join(list_data) if ITEM_LIST_PATH not in rom.filenames: create_file_in_rom(rom, ITEM_LIST_PATH, file_data) else: rom.setFileByName(ITEM_LIST_PATH, file_data) try: apply() except RuntimeError as ex: raise ex
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data): if not self.is_applied(rom, config): path_len = len(LIST_PATH%0)+1 if path_len%4!=0: path_len += 4-(path_len%4) if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: table = ITEM_LISTS_TABLE_US if config.game_region == GAME_REGION_EU: table = ITEM_LISTS_TABLE_EU ranges = [] for i in range(ITEM_LISTS_NB): start = read_uintle(rom.arm9, table+i*4, 4)-ARM9_START end = start size = 0 while size<ITEM_LISTS_SIZE: val = read_uintle(rom.arm9, end, 2) if val>=30000: size += (val-30000)*2 else: size += 2 end += 2 data = rom.arm9[start:end] if end%4!=0: end += 4-(end%4) path = LIST_PATH%i if path not in rom.filenames: create_file_in_rom(rom, path, data) else: rom.setFileByName(path, data) rom.arm9 = rom.arm9[:start]+bytes([0xCC]*(end-start))+rom.arm9[end:] ranges.append([start, end]) ranges.sort() i = 0 while i<len(ranges)-1: if ranges[i][1]==ranges[i+1][0]: ranges[i][1] = ranges[i+1][1] del ranges[i+1] i-=1 i+=1 buffer = bytearray(4*ITEM_LISTS_NB) for i in range(ITEM_LISTS_NB): path = LIST_PATH%i while ranges[0][1]-ranges[0][0]<path_len: del ranges[0] if len(ranges)==0: raise RuntimeError("Don't have enough space to put filenames! ") rom.arm9 = rom.arm9[:ranges[0][0]]+path.encode(encoding="ascii")+bytes(path_len-len(path))+rom.arm9[ranges[0][0]+path_len:] write_uintle(buffer, ARM9_START+ranges[0][0], i*4, 4) ranges[0][0] += path_len rom.arm9 = rom.arm9[:table]+buffer+rom.arm9[table+len(buffer):] try: apply() except RuntimeError as ex: raise ex
def is_applied(self, rom: NintendoDSRom, config: Pmd2Data) -> bool: if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: return read_u32( rom.loadArm9Overlays([29])[29].data, OFFSET_US) != ORIGINAL_INSTRUCTION if config.game_region == GAME_REGION_EU: return read_u32( rom.loadArm9Overlays([29])[29].data, OFFSET_EU) != ORIGINAL_INSTRUCTION raise NotImplementedError()
def is_applied(self, rom: NintendoDSRom, config: Pmd2Data) -> bool: if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: return read_u32( rom.loadArm9Overlays([11])[11].data, PATCH_CHECK_ADDR_APPLIED_US ) != PATCH_CHECK_INSTR_APPLIED if config.game_region == GAME_REGION_EU: return read_u32( rom.loadArm9Overlays([11])[11].data, PATCH_CHECK_ADDR_APPLIED_EU ) != PATCH_CHECK_INSTR_APPLIED raise NotImplementedError()
def draw_object(img: Image.Image, draw, obj: SsaObject, rom: NintendoDSRom): """Draws the sprite for an object""" if obj.object.name == 'NULL': if draw_invisible_actors_objects: # Draw invisible object hitboxes w = obj.hitbox_w * BPC_TILE_DIM h = obj.hitbox_h * BPC_TILE_DIM tlx = obj.pos.x_absolute - int(w / 2) tly = obj.pos.y_absolute - int(h / 2) draw.rectangle(( tlx, tly, tlx + w, tly + h, ), COLOR_OBJECTS, (0, 0, 0)) return try: sprite = FileType.WAN.deserialize( rom.getFileByName(f'GROUND/{obj.object.name}.wan') ) ani_group = sprite.get_animations_for_group(sprite.anim_groups[0]) except (ValueError, TypeError) as e: warnings.warn(f"Failed to render a sprite, replaced with placeholder ({obj}): {e}") if not draw_invisible_actors_objects: return return triangle(draw, obj.pos.x_absolute, obj.pos.y_absolute, COLOR_OBJECTS, obj.pos.direction.id) frame_id = obj.pos.direction.id - 1 if obj.pos.direction.id > 0 else 0 if frame_id > len(ani_group) - 1: frame_id = 0 mfg_id = ani_group[frame_id].frames[0].frame_id sprite_img, (cx, cy) = sprite.render_frame_group(sprite.frame_groups[mfg_id]) render_x = obj.pos.x_absolute - cx render_y = obj.pos.y_absolute - cy img.paste(sprite_img, (render_x, render_y), sprite_img)
def draw_maps_main(): global monster_bin_pack_file, monster_md os.makedirs(output_dir, exist_ok=True) rom = NintendoDSRom.fromFile(os.path.join(base_dir, 'skyworkcopy_edit.nds')) monster_bin_pack_file = FileType.BIN_PACK.deserialize( rom.getFileByName('MONSTER/monster.bin')) monster_md = FileType.MD.deserialize( rom.getFileByName('BALANCE/monster.md')) script_info = load_script_files(get_rom_folder(rom, SCRIPT_DIR)) map_bg_entry_level_list = FileType.BG_LIST_DAT.deserialize( rom.getFileByName('MAP_BG/bg_list.dat')).level for script_map in script_info['maps'].values(): # Map BGs are NOT *actually* mapped 1:1 to scripts. They are loaded via Opcode. However it turns out, using the BPL name # is an easy way to map them. map_bg_entry = next(x for x in map_bg_entry_level_list if x.bpl_name == script_map['name']) if script_map['enter_sse'] is not None: process( rom, map_bg_entry, script_map['name'], SCRIPT_DIR + '/' + script_map['name'] + '/' + script_map['enter_sse']) for ssa, _ in script_map['ssas']: process(rom, map_bg_entry, script_map['name'], SCRIPT_DIR + '/' + script_map['name'] + '/' + ssa) for sss in script_map['subscripts'].keys(): process(rom, map_bg_entry, script_map['name'], SCRIPT_DIR + '/' + script_map['name'] + '/' + sss)
def draw_object(img: Image.Image, draw, obj: SsaObject, rom: NintendoDSRom): """Draws the sprite for an object""" if obj.object.name == 'NULL': return triangle(draw, obj.pos.x_absolute, obj.pos.y_absolute, COLOR_ACTOR, obj.pos.direction.id) try: sprite = FileType.WAN.deserialize( rom.getFileByName(f'GROUND/{obj.object.name}.wan')) except ValueError as e: warnings.warn( f"Failed to render a sprite, replaced with placeholder ({obj}): {e}" ) return triangle(draw, obj.pos.x_absolute, obj.pos.y_absolute, COLOR_ACTOR, obj.pos.direction.id) ani_group = sprite.get_animations_for_group(sprite.anim_groups[0]) frame_id = obj.pos.direction.id - 1 if obj.pos.direction.id > 0 else 0 if frame_id > len(ani_group) - 1: frame_id = 0 mfg_id = ani_group[frame_id].frames[0].frame_id sprite_img, (cx, cy) = sprite.render_frame_group(sprite.frame_groups[mfg_id]) render_x = obj.pos.x_absolute - cx render_y = obj.pos.y_absolute - cy img.paste(sprite_img, (render_x, render_y), sprite_img)
def main(): output_dir = os.path.join(os.path.dirname(__file__), 'dbg_output') base_dir = os.path.join(os.path.dirname(__file__), '..', '..', '..', '..', '..') os.makedirs(output_dir, exist_ok=True) rom = NintendoDSRom.fromFile(os.path.join(base_dir, 'skyworkcopy.nds')) file_name = 'SCRIPT/COMMON/unionall.ssb' # Files that don't work right now: print(file_name) bin_before = rom.getFileByName(file_name) ssb = SsbHandler.deserialize(bin_before) target_point = 0x11BF for i, ops in enumerate(ssb.routine_ops): print(f">>> Routine {i}:") for op in ops: offset = target_point - op.offset offset_two = target_point - int.from_bytes(op.offset.to_bytes( 2, byteorder='little', signed=False), byteorder='little', signed=True) for param in op.params: if param == offset or param == offset_two: print( f"{op.offset:10x}: ({op.op_code.id:3}) {op.op_code.name:45} - {', '.join(hex(x) for x in op.params)}" )
def main_test(): import os from skytemple_files.common.types.file_types import FileType from ndspy.rom import NintendoDSRom from skytemple_files.common.util import get_ppmdu_config_for_rom # Testing. base_dir = os.path.join(os.path.dirname(__file__), '..', '..', '..', '..') rom = NintendoDSRom.fromFile(os.path.join(base_dir, 'skyworkcopy_us.nds')) config = get_ppmdu_config_for_rom(rom) out_dir = '/tmp/monster_graphs' os.makedirs(out_dir, exist_ok=True) monster_md = FileType.MD.deserialize(rom.getFileByName('BALANCE/monster.md')) level_bin = FileType.BIN_PACK.deserialize(rom.getFileByName('BALANCE/m_level.bin')) waza_p = FileType.WAZA_P.deserialize(rom.getFileByName('BALANCE/waza_p.bin')) move_string_block = config.string_index_data.string_blocks['Move Names'] monster_name_block = config.string_index_data.string_blocks['Pokemon Names'] strings = FileType.STR.deserialize(rom.getFileByName('MESSAGE/text_e.str')) move_strings = strings.strings[move_string_block.begin:move_string_block.end] monster_strings = strings.strings[monster_name_block.begin:monster_name_block.end] level_bin = level_bin # The level_bin has no entry for monster 0. for monster, lbinentry_bin, waza_entry in zip(monster_md.entries[1:], level_bin, waza_p.learnsets[1:]): level_bin_entry = FileType.LEVEL_BIN_ENTRY.deserialize( FileType.COMMON_AT.deserialize(FileType.SIR0.deserialize(lbinentry_bin).content).decompress() ) graph_provider = LevelUpGraphProvider(monster, level_bin_entry, waza_entry, move_strings) g = graph_provider.provide( f'{monster_strings[monster.md_index]}', dark=True ) g.render_to_file(os.path.join(out_dir, f'{monster.md_index}.svg')) g.render_to_png(os.path.join(out_dir, f'{monster.md_index}.png'), dpi=92)
def main(): rom_path = sys.argv[1] rom = NintendoDSRom.fromFile(rom_path) config = get_ppmdu_config_for_rom(rom) arm9 = rom.arm9 original_arm9 = arm9[0:len(arm9)] extra_dungeon_data = ExtraDungeonDataList.read(arm9, config) guest_pokemon_data = GuestPokemonList.read(arm9, config) ExtraDungeonDataList.write(extra_dungeon_data, arm9, config) GuestPokemonList.write(guest_pokemon_data, arm9, config) extra_dungeon_data2 = ExtraDungeonDataList.read(arm9, config) guest_pokemon_data2 = GuestPokemonList.read(arm9, config) assert extra_dungeon_data == extra_dungeon_data2 assert guest_pokemon_data == guest_pokemon_data2 guest_pokemon_data.append( GuestPokemon(0, 64, 0, [1, 2, 3, 4], 901, 50, 255, 100, 102, 77, 88, 0, 0)) GuestPokemonList.write(guest_pokemon_data, arm9, config) guest_pokemon_data2 = GuestPokemonList.read(arm9, config) assert guest_pokemon_data == guest_pokemon_data2 guest_pokemon_data2.pop(-1) GuestPokemonList.write(guest_pokemon_data2, arm9, config) assert original_arm9 == arm9
def run_main(rom_path, export_dir, actor_mapping_path=None, opt_draw_invisible_actors_objects=True): global monster_bin_pack_file, monster_md, draw_invisible_actors_objects, ground_dungeon_tilesets draw_invisible_actors_objects = opt_draw_invisible_actors_objects print("Loading ROM and core files...") os.makedirs(export_dir, exist_ok=True) rom = NintendoDSRom.fromFile(rom_path) config = get_ppmdu_config_for_rom(rom) scriptdata = config.script_data if actor_mapping_path: with open(actor_mapping_path, 'r') as f: actor_mapping = json.load(f) for name, entid in actor_mapping.items(): scriptdata.level_entities__by_name[name].entid = entid monster_bin_pack_file = FileType.BIN_PACK.deserialize(rom.getFileByName('MONSTER/monster.bin')) monster_md = FileType.MD.deserialize(rom.getFileByName('BALANCE/monster.md')) map_bg_dir = os.path.join(export_dir, 'MAP_BG') dungeon_map_bg_dir = os.path.join(export_dir, 'MAP_BG_DUNGEON_TILESET') print("-- DRAWING BACKGROUNDS --") draw_map_bgs(rom, map_bg_dir) print("-- DRAWING REST ROOM AND BOSS ROOM BACKGROUNDS --") draw_dungeon_map_bgs(rom, dungeon_map_bg_dir, config) print("-- DRAWING MAP ENTITIES --") draw_maps(rom, os.path.join(export_dir, 'MAP'), scriptdata)
def _outer_wrapper(wrapped_function): import inspect import pytest from ndspy.rom import NintendoDSRom from unittest import SkipTest from parameterized import parameterized rom = None if 'SKYTEMPLE_TEST_ROM' in os.environ and os.environ[ 'SKYTEMPLE_TEST_ROM'] != '': rom = NintendoDSRom.fromFile(os.environ['SKYTEMPLE_TEST_ROM']) if rom: def dataset_name_func(testcase_func, _, param): return f'{testcase_func.__name__}/{param.args[0]}' files = [(x, rom.getFileByName(x)) for x in get_files_from_rom_with_extension(rom, file_ext) if x.startswith(path)] if len(files) < 1: def no_files(*args, **kwargs): raise SkipTest("No matching files were found in the ROM.") return pytest.mark.romtest(no_files) else: spec = inspect.getfullargspec(wrapped_function) if "pmd2_data" in spec.args or "pmd2_data" in spec.kwonlyargs: pmd2_data = get_ppmdu_config_for_rom(rom) def pmd2datawrapper(*args, **kwargs): return wrapped_function(*args, **kwargs, pmd2_data=pmd2_data) pmd2datawrapper.__name__ = wrapped_function.__name__ parameterized.expand(files, name_func=dataset_name_func)( pytest.mark.romtest(pmd2datawrapper)) else: parameterized.expand(files, name_func=dataset_name_func)( pytest.mark.romtest(wrapped_function)) # since expands now adds the tests to our locals, we need to pass them back... # this isn't hacky at all wdym??????ßßß frame_locals = inspect.currentframe( ).f_back.f_locals # type: ignore for local_name, local in inspect.currentframe().f_locals.items( ): # type: ignore if local_name.startswith('test_'): frame_locals[local_name] = local else: def no_tests(*args, **kwargs): raise SkipTest("No ROM file provided or ROM not found.") return pytest.mark.romtest(no_tests)
async def main(executor): output_dir = os.path.join(os.path.dirname(__file__), 'dbg_output') base_dir = os.path.join(os.path.dirname(__file__), '..', '..', '..', '..', '..') os.makedirs(output_dir, exist_ok=True) rom = NintendoDSRom.fromFile( os.path.join(base_dir, 'skyworkcopy_us_unpatched.nds')) script_info = load_script_files(get_rom_folder(rom, SCRIPT_DIR)) # total, opening. decompiling, parsing, compiling, serializing times: List[Tuple[float, float, float, float, float, float]] = [] static_data = Pmd2XmlReader.load_default(for_version='EoS_NA') awaitables = [] for i, file_name in enumerate(get_files_from_rom_with_extension( rom, 'ssb')): # TODO: Those scripts fail for JP. if file_name in [ 'SCRIPT/D42P21A/enter23.ssb', 'SCRIPT/D73P11A/us0303.ssb', 'SCRIPT/D73P11A/us0305.ssb', 'SCRIPT/D73P11A/us2003.ssb', 'SCRIPT/D73P11A/us2005.ssb', 'SCRIPT/D73P11A/us2103.ssb', 'SCRIPT/D73P11A/us2105.ssb', 'SCRIPT/D73P11A/us2203.ssb', 'SCRIPT/D73P11A/us2205.ssb', 'SCRIPT/D73P11A/us2303.ssb', 'SCRIPT/D73P11A/us2305.ssb' ]: continue # Run multiple in parallel with asyncio executors. awaitables.append( loop.run_in_executor(executor, process_single, file_name, times, static_data, output_dir, rom)) pending = awaitables while len(pending) > 0: done, pending = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED) # to raise exceptions of tasks back to main loop: for fut in done: try: fut.result() except Exception: loop.stop() with poison_lock: poison_container[0] = True raise times_structured = list(zip(*times)) print_table_row("", "TOTAL", "OPENING", "DECOMPILING", "PARSING", "COMPILING", "SERIALIZING") print_table_row(*(["==========="] * 7)) print_table_row("TOTAL:", *[round(sum(t), 2) for t in times_structured]) print_table_row("AVG:", *[round(sum(t) / len(t), 2) for t in times_structured]) print_table_row("MAX:", *[round(max(t), 2) for t in times_structured]) print_table_row("MIN:", *[round(min(t), 2) for t in times_structured])
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: type_table = TYPE_TABLE_US gummi_iq_table = GUMMI_IQ_TABLE_US gummi_belly_table = GUMMI_BELLY_TABLE_US if config.game_region == GAME_REGION_EU: type_table = TYPE_TABLE_EU gummi_iq_table = GUMMI_IQ_TABLE_EU gummi_belly_table = GUMMI_BELLY_TABLE_EU if config.game_region == GAME_REGION_JP: type_table = TYPE_TABLE_JP gummi_iq_table = GUMMI_IQ_TABLE_JP gummi_belly_table = GUMMI_BELLY_TABLE_JP bincfg = config.binaries['overlay/overlay_0010.bin'] data = bytearray(get_binary_from_rom_ppmdu(rom, bincfg)) data[type_table:type_table + TABLE_LEN] = bytearray(NEW_TYPES) set_binary_in_rom_ppmdu(rom, bincfg, bytes(data)) # Change Fairy's type name for filename in get_files_from_rom_with_extension(rom, 'str'): bin_before = rom.getFileByName(filename) strings = StrHandler.deserialize(bin_before) block = config.string_index_data.string_blocks['Type Names'] strings.strings[block.begin + 18] = TYPE_LIST[filename] bin_after = StrHandler.serialize(strings) rom.setFileByName(filename, bin_after) bincfg = config.binaries['arm9.bin'] data = bytearray(get_binary_from_rom_ppmdu(rom, bincfg)) data[gummi_iq_table:gummi_iq_table + TABLE_LEN] = bytearray(NEW_IQ_GUMMI) data[gummi_belly_table:gummi_belly_table + TABLE_LEN] = bytearray(NEW_BELLY_GUMMI) set_binary_in_rom_ppmdu(rom, bincfg, bytes(data)) try: apply() except RuntimeError as ex: raise ex
def main(rom_file, directory): rom = NintendoDSRom.fromFile(rom_file) for file_name in get_files_from_rom_with_extension(rom, 'ssb'): if os.path.exists(os.path.join(directory, file_name)): print(file_name) with open(os.path.join(directory, file_name), 'rb') as f: rom.setFileByName(file_name, f.read()) rom.saveToFile(rom_file)
def draw_map_bgs(rom: NintendoDSRom, map_bg_dir): global map_bgs, map_bg_durations os.makedirs(map_bg_dir, exist_ok=True) bin = rom.getFileByName('MAP_BG/bg_list.dat') bg_list = FileType.BG_LIST_DAT.deserialize(bin) count = len(bg_list.level) for i, l in enumerate(bg_list.level): try: bma = l.get_bma(rom) print(f"{i + 1}/{count} - {l.bpl_name}") bpas = l.get_bpas(rom) non_none_bpas = [b for b in bpas if b is not None] bpc = l.get_bpc(rom) bpl = l.get_bpl(rom) # Saving animated map! bpa_duration = -1 pal_ani_duration = -1 if len(non_none_bpas) > 0: bpa_duration = round( 1000 / 60 * non_none_bpas[0].frame_info[0].duration_per_frame) if bpl.has_palette_animation: pal_ani_duration = round(1000 / 60 * max(spec.duration_per_frame for spec in bpl.animation_specs)) duration = max(bpa_duration, pal_ani_duration) if duration == -1: # Default for only one frame, doesn't really matter duration = 1000 frames = bma.to_pil(bpc, bpl, bpas, include_collision=False, include_unknown_data_block=False) frames[0].save(os.path.join(map_bg_dir, l.bpl_name + '.gif'), save_all=True, append_images=frames[1:], duration=duration, loop=0, optimize=False) frames[0].save(os.path.join(map_bg_dir, l.bpl_name + '.png')) map_bgs[l.bpl_name] = frames map_bg_durations[l.bpl_name] = duration except (NotImplementedError, SystemError) as ex: print(f"error for {l.bma_name}: {repr(ex)}", file=sys.stderr) print(''.join( traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__)), file=sys.stderr)
def load(self): """Load the ROM into memory and initialize all modules""" self._rom = NintendoDSRom.fromFile(self.filename) self._loaded_modules = {} for name, module in Modules.all().items(): if name == 'rom': self._rom_module = module(self) else: self._loaded_modules[name] = module(self) self._sprite_renderer = SpriteProvider(self) self._string_provider = StringProvider(self)
def set_binary_in_rom_ppmdu(rom: NintendoDSRom, binary: 'Pmd2Binary', data: bytes): """Sets the correct binary in the rom, using the binary block specifications.""" parts = binary.filepath.split('/') if parts[0] == 'arm9.bin': rom.arm9 = bytes(data) return if parts[0] == 'arm7.bin': rom.arm7 = bytes(data) return if parts[0] == 'overlay': if len(parts) > 1: r = re.compile(r'overlay_(\d+).bin', re.IGNORECASE) match = r.match(parts[1]) if match is not None: ov_id = int(match.group(1)) overlays = rom.loadArm9Overlays([ov_id]) if len(overlays) > 0: rom.files[overlays[ov_id].fileID] = data return raise ValueError(f"Binary {binary.filepath} not found.")
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: string_id = STRING_ID_US if config.game_region == GAME_REGION_EU: string_id = STRING_ID_EU if config.game_region == GAME_REGION_JP: string_id = STRING_ID_JP # Change dialogue for lang in config.string_index_data.languages: filename = 'MESSAGE/' + lang.filename bin_before = rom.getFileByName(filename) strings = StrHandler.deserialize(bin_before) strings.strings[string_id - 1] = get_locales().translate(MESSAGE, lang.locale.replace('-', '_')) bin_after = StrHandler.serialize(strings) rom.setFileByName(filename, bin_after) try: apply() except RuntimeError as ex: raise ex
def main(): output_dir = os.path.join(os.path.dirname(__file__), 'dbg_output', 'graphs') base_dir = os.path.join(os.path.dirname(__file__), '..', '..', '..', '..', '..') rom = NintendoDSRom.fromFile(os.path.join(base_dir, 'skyworkcopy.nds')) total_count_labels_before = 0 total_count_labels_after = 0 for file_name in get_files_from_rom_with_extension(rom, 'ssb'): print(file_name) bin_before = rom.getFileByName(file_name) ssb = SsbHandler.deserialize(bin_before) routine_ops = ssb.get_filled_routine_ops() resolver = OpsLabelJumpToResolver(routine_ops) routine_ops = list(resolver) grapher = SsbGraphMinimizer(routine_ops) total_count_labels_before += grapher.count_labels() draw_graphs(grapher, file_name, output_dir, 'before_optimize') grapher.optimize_paths() draw_graphs(grapher, file_name, output_dir, 'after_optimize') #grapher._graphs = [ grapher._graphs[86] ] grapher.build_branches() draw_graphs(grapher, file_name, output_dir, 'after_branch_before_group') grapher.group_branches() grapher.invert_branches() draw_graphs(grapher, file_name, output_dir, 'after_branch') grapher.build_and_group_switch_cases() grapher.group_switch_cases() grapher.build_switch_fallthroughs() draw_graphs(grapher, file_name, output_dir, 'after_switch') grapher.build_loops() draw_graphs(grapher, file_name, output_dir, 'after_loops') grapher.remove_label_markers() draw_graphs(grapher, file_name, output_dir, 'done') total_count_labels_after += grapher.count_labels() print("Total number labels before: " + str(total_count_labels_before)) print("Total number labels after: " + str(total_count_labels_after))
def load(self): """Load the ROM into memory and initialize all modules""" self._rom = NintendoDSRom.fromFile(self.filename) self._loaded_modules = {} for name, module in Modules.all().items(): logger.debug(f"Loading module {name} for ROM...") if name == 'rom': self._rom_module = module(self) else: self._loaded_modules[name] = module(self) self._sprite_renderer = SpriteProvider(self) self._string_provider = StringProvider(self) self._icon_banner = IconBanner(self._rom)
def create_mapping(): from ndspy.rom import NintendoDSRom rom = NintendoDSRom.fromFile( '/home/marco/dev/skytemple/skytemple/skyworkcopy_us_unpatched.nds') from skytemple_files.common.util import get_ppmdu_config_for_rom static_data = get_ppmdu_config_for_rom(rom) from skytemple_files.patch.patches import Patcher patcher = Patcher(rom, static_data) patcher.apply('ActorAndLevelLoader') from skytemple_files.common.types.file_types import FileType from skytemple_files.data.md.model import Md md: Md = FileType.MD.deserialize(rom.getFileByName('BALANCE/monster.md')) from skytemple_files.list.actor.model import ActorListBin actor_list: ActorListBin = FileType.SIR0.unwrap_obj( FileType.SIR0.deserialize(rom.getFileByName('BALANCE/actor_list.bin')), ActorListBin) from skytemple_files.hardcoded.fixed_floor import HardcodedFixedFloorTables from skytemple_files.common.util import get_binary_from_rom_ppmdu boss_list = HardcodedFixedFloorTables.get_monster_spawn_list( get_binary_from_rom_ppmdu( rom, static_data.binaries['overlay/overlay_0029.bin']), static_data) actor_list_pokedex_number_mapping = [] for e in actor_list.list: monster = md.entries[e.entid] actor_list_pokedex_number_mapping.append( monster.national_pokedex_number) boss_list_pokedex_number_mapping = [] for boss in boss_list: try: monster = md.entries[boss.md_idx] boss_list_pokedex_number_mapping.append( monster.national_pokedex_number) except IndexError: boss_list_pokedex_number_mapping.append(0) mapping = {} for idx, a in enumerate(actor_list_pokedex_number_mapping): if a == 0: continue indices = [ i for i, x in enumerate(boss_list_pokedex_number_mapping) if x == a ] if len(indices) > 0: mapping[idx] = indices print(mapping)
def main(): os.makedirs(output_dir, exist_ok=True) rom = NintendoDSRom.fromFile(os.path.join(base_dir, 'skyworkcopy.nds')) script_info = load_script_files(get_rom_folder(rom, SCRIPT_DIR)) for script_map in script_info['maps'].values(): if script_map['enter_sse'] is not None: process(rom, SCRIPT_DIR + '/' + script_map['name'] + '/', script_map['enter_sse'], script_map['enter_ssbs']) for ssa, ssb in script_map['ssas']: process(rom, SCRIPT_DIR + '/' + script_map['name'] + '/', ssa, [ssb]) for sss, ssb in script_map['subscripts'].items(): process(rom, SCRIPT_DIR + '/' + script_map['name'] + '/', sss, ssb)
def main(): rom_path = sys.argv[1] rom = NintendoDSRom.fromFile(rom_path) config = get_ppmdu_config_for_rom(rom) block_compressed = config.binaries['arm9.bin'].symbols['CompressedIqGroupsSkills'] arm9 = rom.arm9 groups = IqGroupsSkills.read_uncompressed(arm9, config) IqGroupsSkills.write_compressed(arm9, groups, config) assert arm9[block_compressed.begin:block_compressed.end] == COMPRESSED_GROUPS groups2 = IqGroupsSkills.read_compressed(arm9, config) for i in range(len(groups)): assert sorted(groups[i]) == sorted(groups2[i])
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: if not self.is_applied(rom, config): if OBJECT_TABLE_PATH not in rom.filenames: if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: start_ov11 = START_OV11_US start_table = START_TABLE_US table_entries = TABLE_ENTRIES_US if config.game_region == GAME_REGION_EU: start_ov11 = START_OV11_EU start_table = START_TABLE_EU table_entries = TABLE_ENTRIES_EU if config.game_region == GAME_REGION_JP: start_ov11 = START_OV11_JP start_table = START_TABLE_JP table_entries = TABLE_ENTRIES_JP data = rom.loadArm9Overlays([11])[11].data table = [] for i in range(table_entries): offset = start_table - start_ov11 + i * 0xC array = bytearray(0x10) attr = read_u32(data, offset) write_u32(array, attr, 0) attr2 = read_u8(data, offset + 8) write_u8(array, attr2, 4) addr: int = read_u32(data, offset + 4) if addr != 0: addr -= start_ov11 count = 0 while data[addr + count] != 0: if count >= 10: raise ValueError("Invalid string length (more than 10 characters)") array[5 + count] = data[addr + count] count += 1 if sum(array) == 0: print("Found blank entry, stopping at", i) break table.append(array) file_data = b''.join(table) create_file_in_rom(rom, OBJECT_TABLE_PATH, file_data) try: apply() except RuntimeError as ex: raise ex
def get_binary_from_rom_ppmdu(rom: NintendoDSRom, binary: 'Pmd2Binary'): """Returns the correct binary from the rom, using the binary block specifications.""" parts = binary.filepath.split('/') if parts[0] == 'arm9.bin': return rom.arm9 + rom.arm9PostData if parts[0] == 'arm7.bin': return rom.arm7 if parts[0] == 'overlay': if len(parts) > 1: r = re.compile(r'overlay_(\d+).bin', re.IGNORECASE) match = r.match(parts[1]) if match is not None: ov_id = int(match.group(1)) overlays = rom.loadArm9Overlays([ov_id]) if len(overlays) > 0: return overlays[ov_id].data raise ValueError(f"Binary {binary.filepath} not found.")
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: START_ACCURACY = self.get_parameter("StartGraphicPos") START_POWER = START_ACCURACY + 12 MAX_POWER = f"[M:B{START_POWER}]" + (f"[M:B{START_POWER + 10}]") * 9 + f"[M:B{START_POWER + 9}]" MAX_ACCU = f"[M:B{START_POWER}]" + (f"[M:B{START_ACCURACY + 11}]") * 10 + f"[M:B{START_POWER}]" DESC_CHANGES = {8: MAX_POWER, 60: MAX_POWER, 75: MAX_POWER, 100: MAX_POWER, 153: MAX_POWER, 156: MAX_POWER, 205: MAX_POWER, 206: MAX_POWER, 348: MAX_POWER, 477: MAX_POWER, 61: MAX_ACCU, 340: MAX_ACCU, 535: MAX_ACCU} bin_before = rom.getFileByName("FONT/markfont.dat") model = GraphicFontHandler.deserialize(bin_before) entries = [] for x in range(model.get_nb_entries()): entries.append(model.get_entry(x)) while len(entries) < max(START_ACCURACY + 12, START_POWER + 11): entries.append(None) for x in range(START_ACCURACY, START_ACCURACY + 12): img = Image.open(os.path.join(SRC_DIR, "accu_%02d.png" % (x - START_ACCURACY)), 'r') entries[x] = img for x in range(START_POWER, START_POWER + 11): img = Image.open(os.path.join(SRC_DIR, "pow_%02d.png" % (x - START_POWER)), 'r') entries[x] = img model.set_entries(entries) bin_after = GraphicFontHandler.serialize(model) rom.setFileByName("FONT/markfont.dat", bin_after) # Change some move descriptions for filename in get_files_from_rom_with_extension(rom, 'str'): bin_before = rom.getFileByName(filename) strings = StrHandler.deserialize(bin_before) block = config.string_index_data.string_blocks['Move Descriptions'] for k, v in DESC_CHANGES.items(): strings.strings[block.begin + k] = strings.strings[block.begin + k].replace(OLD_STAT, v) bin_after = StrHandler.serialize(strings) rom.setFileByName(filename, bin_after) try: apply() except RuntimeError as ex: raise ex
def main(): os.makedirs(output_dir, exist_ok=True) rom = NintendoDSRom.fromFile(os.path.join(base_dir, 'skyworkcopy.nds')) script_info = load_script_files(get_rom_folder(rom, SCRIPT_DIR)) for script_map in script_info['maps'].values(): # Map BGs are NOT *actually* mapped 1:1 to scripts. They are loaded via Opcode. However it turns out, using the BPL name # is an easy way to map them. if script_map['enter_sse'] is not None: process( rom, script_map['name'], SCRIPT_DIR + '/' + script_map['name'] + '/' + script_map['enter_sse']) for ssa, _ in script_map['ssas']: process(rom, script_map['name'], SCRIPT_DIR + '/' + script_map['name'] + '/' + ssa) for sss in script_map['subscripts'].keys(): process(rom, script_map['name'], SCRIPT_DIR + '/' + script_map['name'] + '/' + sss)
def main(): output_dir = os.path.join(os.path.dirname(__file__), 'dbg_output') base_dir = os.path.join(os.path.dirname(__file__), '..', '..', '..', '..', '..') os.makedirs(output_dir, exist_ok=True) rom = NintendoDSRom.fromFile(os.path.join(base_dir, 'skyworkcopy.nds')) for file_name in get_files_from_rom_with_extension(rom, 'ssb'): if 'm03a1310' not in file_name: continue print(file_name) out_file_name = os.path.join(output_dir, file_name.replace('/', '_') + '.txt') bin_before = rom.getFileByName(file_name) ssb = SsbHandler.deserialize(bin_before) with open(out_file_name, 'w') as f: f.write(export_ssb_as_txt(ssb))