def is_applied(self, rom: NintendoDSRom, config: Pmd2Data) -> bool: overlay29 = get_binary_from_rom_ppmdu( rom, config.binaries['overlay/overlay_0029.bin']) overlay31 = get_binary_from_rom_ppmdu( rom, config.binaries['overlay/overlay_0031.bin']) if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: x = 0 while x < totaloverlay29checks: if overlay29[CHECK_US[x]:CHECK_US[x] + 4] != BYTES_US[x]: return True x += 1 if overlay31[CHECK_US[6]:CHECK_US[6] + 4] != BYTES_US[6]: return True return False if config.game_region == GAME_REGION_EU: x = 0 while x < totaloverlay29checks: if overlay29[CHECK_EU[x]:CHECK_EU[x] + 4] != BYTES_EU[x]: return True x += 1 if overlay31[CHECK_EU[6]:CHECK_EU[6] + 4] != BYTES_EU[6]: return True return False raise NotImplementedError()
def run(self, status: Status): if not self.config['improvements']['download_portraits']: return status.done() status.step("Apply 'ActorAndLevelLoader' patch...") patcher = Patcher(self.rom, self.static_data) if not patcher.is_applied('ActorAndLevelLoader'): patcher.apply('ActorAndLevelLoader') overlay13 = get_binary_from_rom_ppmdu( self.rom, self.static_data.binaries['overlay/overlay_0013.bin']) actor_list: ActorListBin = FileType.SIR0.unwrap_obj( FileType.SIR0.deserialize( self.rom.getFileByName('BALANCE/actor_list.bin')), ActorListBin) starters = HardcodedPersonalityTestStarters.get_partner_md_ids( overlay13, self.static_data) partners = HardcodedPersonalityTestStarters.get_player_md_ids( overlay13, self.static_data) md = FileType.MD.deserialize( self.rom.getFileByName('BALANCE/monster.md')) with urllib.request.urlopen( "http://sprites.pmdcollab.org/resources/pokemons.json") as url: config = json.loads(url.read().decode()) kao = FileType.KAO.deserialize( self.rom.getFileByName('FONT/kaomado.kao')) if fun.is_fun_allowed(): status.step("Downloading portraits...") fun.replace_portraits(self.rom, self.static_data) return status.done() status.step("Downloading portraits for NPCs...") for actor in actor_list.list: if actor.entid > 0: self._import_portrait(kao, config, actor.entid, md.entries[actor.entid]) status.step("Downloading portraits for starters...") for starter in starters: self._import_portrait(kao, config, starter, md.entries[starter]) status.step("Downloading portraits for partners...") for partner in partners: self._import_portrait(kao, config, partner, md.entries[partner]) self.rom.setFileByName('FONT/kaomado.kao', FileType.KAO.serialize(kao)) def add_rows(): if Global.main_builder: o = Global.main_builder.get_object('store_debug_portraits') o.clear() for row in self._debugs: o.append(row) self.frontend.idle_add(add_rows) status.done()
def run(self, status: Status): if not self.config['starters_npcs']['starters']: return status.done() status.step("Randomizing Partner Starters...") overlay13 = get_binary_from_rom_ppmdu( self.rom, self.static_data.binaries['overlay/overlay_0013.bin']) pokemon_string_data = self.static_data.string_index_data.string_blocks[ "Pokemon Names"] langs = list(get_all_string_files(self.rom, self.static_data)) orig_partner_ids = HardcodedPersonalityTestStarters.get_partner_md_ids( overlay13, self.static_data) new_partner_ids = [ self._random_gender(choice(get_allowed_md_ids(self.config))) for _ in range(0, len(orig_partner_ids)) ] HardcodedPersonalityTestStarters.set_partner_md_ids( new_partner_ids, overlay13, self.static_data) status.step("Randomizing Player Starters...") # The player options are put into two-pairs for each nature, first male then female. orig_player_ids = HardcodedPersonalityTestStarters.get_player_md_ids( overlay13, self.static_data) new_player_ids = [] k = 0 # Index of text for "Will be..." for i in range(0, len(orig_player_ids)): new_id = choice(get_allowed_md_ids(self.config)) if k % 3 == 0: k += 1 # todo: refactor, this isn't really efficient. for lang, string_file in langs: string_file.strings[0x67C + k] = replace_strings( string_file.strings[0x67C + k], { self._get_name(string_file, orig_player_ids[i], pokemon_string_data): self._get_name(string_file, new_id, pokemon_string_data) }) if i % 2 == 1 and new_id + NUM_ENTITIES <= 1154: new_id += NUM_ENTITIES new_player_ids.append(new_id) k += 1 HardcodedPersonalityTestStarters.set_player_md_ids( new_player_ids, overlay13, self.static_data) status.step("Cloning missing starter portraits...") kao = FileType.KAO.deserialize( self.rom.getFileByName('FONT/kaomado.kao')) for new in new_player_ids + new_partner_ids: new_base = new % 600 clone_missing_portraits(kao, new_base - 1) set_binary_in_rom_ppmdu( self.rom, self.static_data.binaries['overlay/overlay_0013.bin'], overlay13) for lang, string_file in langs: self.rom.setFileByName(f'MESSAGE/{lang.filename}', FileType.STR.serialize(string_file)) self.rom.setFileByName('FONT/kaomado.kao', FileType.KAO.serialize(kao)) status.done()
def is_applied(self, rom: NintendoDSRom, config: Pmd2Data) -> bool: overlay13 = get_binary_from_rom_ppmdu( rom, config.binaries['overlay/overlay_0013.bin']) if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US or config.game_region == GAME_REGION_EU: return overlay13[OFFSET:OFFSET + 4] != ORIGINAL_BYTESEQ raise NotImplementedError()
def run(self, status: Status): if not self.config['starters_npcs']['npcs']: return status.done() status.step("Apply 'ActorAndLevelLoader' patch...") patcher = Patcher(self.rom, self.static_data) if not patcher.is_applied('ActorAndLevelLoader'): patcher.apply('ActorAndLevelLoader') status.step("Updating special recruitment table...") actor_list: ActorListBin = FileType.SIR0.unwrap_obj( FileType.SIR0.deserialize( self.rom.getFileByName('BALANCE/actor_list.bin')), ActorListBin) binary = get_binary_from_rom_ppmdu( self.rom, self.static_data.binaries['overlay/overlay_0011.bin']) sp_list = HardcodedRecruitmentTables.get_monster_species_list( binary, self.static_data) for i, actor in enumerate(actor_list.list): if i in ACTOR_TO_RECRUIT_MAPPING: for bi in ACTOR_TO_RECRUIT_MAPPING[i]: sp_list[bi] = actor.entid HardcodedRecruitmentTables.set_monster_species_list( sp_list, binary, self.static_data) set_binary_in_rom_ppmdu( self.rom, self.static_data.binaries['overlay/overlay_0011.bin'], binary) status.done()
def __init__(self, config: RandomizerConfig, rom: NintendoDSRom, static_data: Pmd2Data, seed: str): super().__init__(config, rom, static_data, seed) self.dungeons = HardcodedDungeons.get_dungeon_list( get_binary_from_rom_ppmdu(self.rom, self.static_data.binaries['arm9.bin']), self.static_data)
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 is_applied(self, rom: NintendoDSRom, config: Pmd2Data) -> bool: if config.game_version == GAME_VERSION_EOS: ORIGINAL_BYTES = bytes([0xD8, 0x0D, 0x00, 0xEB]) OFFSETS = { GAME_REGION_US: 0x605F4, GAME_REGION_EU: 0x60898, } offset = OFFSETS.get(config.game_region) if offset is not None: overlay29 = get_binary_from_rom_ppmdu( rom, config.binaries["overlay/overlay_0029.bin"]) return overlay29[offset:offset + len(ORIGINAL_BYTES)] != ORIGINAL_BYTES raise NotImplementedError()
def _artist_credits(self): credit_map: Dict[str, ArtistCredits] = {} credits = "" overlay13 = get_binary_from_rom_ppmdu(self.rom, self.static_data.binaries['overlay/overlay_0013.bin']) actor_list: ActorListBin = FileType.SIR0.unwrap_obj( FileType.SIR0.deserialize(self.rom.getFileByName('BALANCE/actor_list.bin')), ActorListBin ) starters = HardcodedPersonalityTestStarters.get_partner_md_ids(overlay13, self.static_data) partners = HardcodedPersonalityTestStarters.get_player_md_ids(overlay13, self.static_data) md = FileType.MD.deserialize(self.rom.getFileByName('BALANCE/monster.md')) with urllib.request.urlopen("http://sprites.pmdcollab.org/resources/pokemons.json") as url: config = json.loads(url.read().decode()) with urllib.request.urlopen("http://sprites.pmdcollab.org/resources/credits.json") as url: credits_config = json.loads(url.read().decode()) for starter in starters: self._process_portrait(credit_map, config, credits_config, starter, md.entries[starter], None) for partner in partners: self._process_portrait(credit_map, config, credits_config, partner, md.entries[partner], None) for actor in actor_list.list: if actor.entid > 0: self._process_portrait(credit_map, config, credits_config, actor.entid, md.entries[actor.entid], actor) credit_map = {k: credit_map[k] for k in sorted(credit_map)} for entry in credit_map.values(): setface = "" if entry.actor: setface = f"message_SetFaceEmpty({SsbConstant.create_for(entry.actor).name}, FACE_HAPPY, FACE_POS_TOP_L_FACEINW);" others = "" if entry.other_artists: others = list(set(entry.other_artists)) main = '' others_short = others[:3] if len(others_short) != len(others): main += ' + more' others = f"More Authors: [CS:A]{', '.join(others_short) + main}[CR]" credits += f""" case menu("{entry.name}"): {setface} message_Talk("Last Author: [CS:A]{escape(entry.main_artist)}[CR]\\n{escape(others)}\\nsprites.pmdcollab.org/portrait.html?id={escape(entry.id_str)}"); jump @l_artists; """ return credits
def extract(self, entry_len: int, string_offs_per_entry: List[int], write_subheader=True): """Performs the extraction. Raises a RuntimeError on error.""" try: binary = get_binary_from_rom_ppmdu(self._rom, self._binary) data = self._wrap_sir0(binary, binary[self._block.begin:self._block.end], entry_len, string_offs_per_entry, write_subheader) if self._out_path not in self._rom.filenames: create_file_in_rom(self._rom, self._out_path, data) else: self._rom.setFileByName(self._out_path, data) except BaseException as ex: raise RuntimeError("Error during extraction for patch.") from ex
def run(self, status: Status): if not self.config['starters_npcs']['npcs']: return status.done() status.step("Apply 'ActorAndLevelLoader' patch...") patcher = Patcher(self.rom, self.static_data) if not patcher.is_applied('ActorAndLevelLoader'): patcher.apply('ActorAndLevelLoader') status.step("Updating bosses...") actor_list: ActorListBin = FileType.SIR0.unwrap_obj( FileType.SIR0.deserialize( self.rom.getFileByName('BALANCE/actor_list.bin')), ActorListBin) binary = get_binary_from_rom_ppmdu( self.rom, self.static_data.binaries['overlay/overlay_0029.bin']) boss_list = HardcodedFixedFloorTables.get_monster_spawn_list( binary, self.static_data) for i, actor in enumerate(actor_list.list): if i in ACTOR_TO_BOSS_MAPPING: for bi in ACTOR_TO_BOSS_MAPPING[i]: boss_list[bi].md_idx = actor.entid for extra_id in EXTRA_FF_MONSTER_RANDOMIZE: boss_list[extra_id].md_idx = choice( get_allowed_md_ids(self.config, False)) HardcodedFixedFloorTables.set_monster_spawn_list( binary, boss_list, self.static_data) set_binary_in_rom_ppmdu( self.rom, self.static_data.binaries['overlay/overlay_0029.bin'], binary) status.done()
def get_binary(self, binary: Union[Pmd2Binary, BinaryName, str]) -> bytes: if not isinstance(binary, Pmd2Binary): binary = self.get_rom_module().get_static_data().binaries[str( binary)] return get_binary_from_rom_ppmdu(self._rom, binary) # type: ignore
from skytemple_files.dungeon_data.fixed_bin.handler import FixedBinHandler from skytemple_files.dungeon_data.fixed_bin.model import TileRule, TileRuleType, EntityRule from skytemple_files.hardcoded.fixed_floor import HardcodedFixedFloorTables, EntitySpawnEntry, ItemSpawn, \ MonsterSpawn, TileSpawn 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('/home/marco/dev/skytemple/skytemple/skyworkcopy.nds') static_data = get_ppmdu_config_for_rom(rom) fixed_bin = rom.getFileByName('BALANCE/fixed.bin') fixed = FixedBinHandler.deserialize(fixed_bin, static_data=static_data) ov29 = get_binary_from_rom_ppmdu(rom, static_data.binaries['overlay/overlay_0029.bin']) ov10 = get_binary_from_rom_ppmdu(rom, static_data.binaries['overlay/overlay_0010.bin']) entity_table = HardcodedFixedFloorTables.get_entity_spawn_table(ov29, static_data) item_table = HardcodedFixedFloorTables.get_item_spawn_list(ov29, static_data) tile_table = HardcodedFixedFloorTables.get_tile_spawn_list(ov29, static_data) monster_table = HardcodedFixedFloorTables.get_monster_spawn_list(ov29, static_data) monster_stats_table = HardcodedFixedFloorTables.get_monster_spawn_stats_table(ov10, static_data) T = TileRule WH = TileRuleType.WALL_HALLWAY FR = TileRuleType.FLOOR_ROOM WZ = TileRuleType.WARP_ZONE SR = TileRuleType.SECONDARY_ROOM FH = TileRuleType.FLOOR_HALLWAY K1 = TileRuleType.FL_WA_ROOM_FLAG_0C
from skytemple_files.graphics.dma.dma_drawer import DmaDrawer from skytemple_files.graphics.dma.model import Dma, DmaType from skytemple_files.graphics.dpc.model import Dpc from skytemple_files.graphics.dpci.model import Dpci from skytemple_files.graphics.dpl.model import Dpl from skytemple_files.graphics.dpla.model import Dpla from skytemple_files.hardcoded.fixed_floor import HardcodedFixedFloorTables 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(output_dir, 'test_fixed_floor.nds')) static_data = get_ppmdu_config_for_rom(rom) ov29 = get_binary_from_rom_ppmdu(rom, static_data.binaries['overlay/overlay_0029.bin']) fixed = FileType.FIXED_BIN.deserialize(rom.getFileByName('BALANCE/fixed.bin'), static_data) dungeon_bin = FileType.DUNGEON_BIN.deserialize(rom.getFileByName('DUNGEON/dungeon.bin'), static_data) mappa = FileType.MAPPA_BIN.deserialize(rom.getFileByName('BALANCE/mappa_s.bin')) monster_bin = FileType.BIN_PACK.deserialize(rom.getFileByName('MONSTER/monster.bin')) monster_md = FileType.MD.deserialize(rom.getFileByName('BALANCE/monster.md')) entity_table = HardcodedFixedFloorTables.get_entity_spawn_table(ov29, static_data) item_table = HardcodedFixedFloorTables.get_item_spawn_list(ov29, static_data) monster_table = HardcodedFixedFloorTables.get_monster_spawn_list(ov29, static_data) def draw_monster_sprite(img: Image.Image, x: int, y: int, monster_id: int, direction: Pmd2ScriptDirection): sprite_index = monster_md.entries[monster_id].sprite_index if sprite_index >= len(monster_bin): return False
# You should have received a copy of the GNU General Public License # along with SkyTemple. If not, see <https://www.gnu.org/licenses/>. # mypy: ignore-errors import os from ndspy.rom import NintendoDSRom from skytemple_files.common.util import get_ppmdu_config_for_rom, get_binary_from_rom_ppmdu from skytemple_files.hardcoded.main_menu_music import HardcodedMainMenuMusic base_dir = os.path.join(os.path.dirname(__file__), '..', '..', '..', '..') rom_us = NintendoDSRom.fromFile(os.path.join(base_dir, 'skyworkcopy_us.nds')) rom_eu = NintendoDSRom.fromFile(os.path.join(base_dir, 'skyworkcopy.nds')) ppmdu_us = get_ppmdu_config_for_rom(rom_us) ppmdu_eu = get_ppmdu_config_for_rom(rom_eu) ov00_us = get_binary_from_rom_ppmdu( rom_us, ppmdu_us.binaries['overlay/overlay_0000.bin']) ov00_eu = get_binary_from_rom_ppmdu( rom_eu, ppmdu_us.binaries['overlay/overlay_0000.bin']) ov09_us = get_binary_from_rom_ppmdu( rom_us, ppmdu_us.binaries['overlay/overlay_0009.bin']) ov09_eu = get_binary_from_rom_ppmdu( rom_eu, ppmdu_us.binaries['overlay/overlay_0009.bin']) print(HardcodedMainMenuMusic.get_main_menu_music(ov00_us, ppmdu_us, ov09_us)) print(HardcodedMainMenuMusic.get_main_menu_music(ov00_eu, ppmdu_eu, ov09_eu)) HardcodedMainMenuMusic.set_main_menu_music(123, ov00_us, ppmdu_us, ov09_us) HardcodedMainMenuMusic.set_main_menu_music(4, ov00_eu, ppmdu_eu, ov09_eu) print(HardcodedMainMenuMusic.get_main_menu_music(ov00_us, ppmdu_us, ov09_us)) print(HardcodedMainMenuMusic.get_main_menu_music(ov00_eu, ppmdu_eu, ov09_eu))
from ndspy.rom import NintendoDSRom from skytemple_files.common.types.file_types import FileType from skytemple_files.common.util import get_ppmdu_config_for_rom, get_binary_from_rom_ppmdu from skytemple_files.dungeon_data.mappa_bin.validator.exception import DungeonTotalFloorCountInvalidError from skytemple_files.dungeon_data.mappa_bin.validator.validator import DungeonValidator from skytemple_files.hardcoded.dungeons import HardcodedDungeons rom = NintendoDSRom.fromFile('../../../../../4261 - Pokemon Mystery Dungeon Explorers of Sky (U)(Xenophobia).nds') config = get_ppmdu_config_for_rom(rom) mappa_bin = rom.getFileByName('BALANCE/mappa_s.bin') mappa = FileType.MAPPA_BIN.deserialize(mappa_bin) dungeons = HardcodedDungeons.get_dungeon_list( get_binary_from_rom_ppmdu(rom, config.binaries['arm9.bin']), config ) for i, dungeon in enumerate(dungeons): print(i, dungeon) print("") validator = DungeonValidator(mappa.floor_lists) validator.validate(dungeons) for e in validator.errors: if not isinstance(e, DungeonTotalFloorCountInvalidError): print(repr(e)) print("") print(validator.invalid_dungeons)
# # SkyTemple is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # SkyTemple is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with SkyTemple. If not, see <https://www.gnu.org/licenses/>. # mypy: ignore-errors import os from ndspy.rom import NintendoDSRom from skytemple_files.common.util import get_ppmdu_config_for_rom, get_binary_from_rom_ppmdu from skytemple_files.hardcoded.ground_dungeon_tilesets import HardcodedGroundDungeonTilesets base_dir = os.path.join(os.path.dirname(__file__), '..', '..', '..', '..') rom = NintendoDSRom.fromFile(os.path.join(base_dir, 'skyworkcopy.nds')) ppmdu = get_ppmdu_config_for_rom(rom) l = HardcodedGroundDungeonTilesets.get_ground_dungeon_tilesets( get_binary_from_rom_ppmdu(rom, ppmdu.binaries['overlay/overlay_0011.bin']), ppmdu) for i, d in enumerate(l): print(i, d)
# You should have received a copy of the GNU General Public License # along with SkyTemple. If not, see <https://www.gnu.org/licenses/>. # mypy: ignore-errors import os from ndspy.rom import NintendoDSRom from skytemple_files.common.util import get_ppmdu_config_for_rom, get_binary_from_rom_ppmdu from skytemple_files.hardcoded.dungeon_misc import HardcodedDungeonMisc base_dir = os.path.join(os.path.dirname(__file__), '..', '..', '..', '..') rom_us = NintendoDSRom.fromFile(os.path.join(base_dir, 'skyworkcopy_us.nds')) rom_eu = NintendoDSRom.fromFile(os.path.join(base_dir, 'skyworkcopy.nds')) ppmdu_us = get_ppmdu_config_for_rom(rom_us) ppmdu_eu = get_ppmdu_config_for_rom(rom_eu) arm9_us = get_binary_from_rom_ppmdu(rom_us, ppmdu_us.binaries['arm9.bin']) arm9_eu = get_binary_from_rom_ppmdu(rom_eu, ppmdu_us.binaries['arm9.bin']) ov10_us = get_binary_from_rom_ppmdu( rom_us, ppmdu_us.binaries['overlay/overlay_0010.bin']) ov10_eu = get_binary_from_rom_ppmdu( rom_eu, ppmdu_us.binaries['overlay/overlay_0010.bin']) ov29_us = get_binary_from_rom_ppmdu( rom_us, ppmdu_us.binaries['overlay/overlay_0029.bin']) ov29_eu = get_binary_from_rom_ppmdu( rom_eu, ppmdu_us.binaries['overlay/overlay_0029.bin']) def test(getter, setter, expected_value, ov_us, ov_eu): assert getter(ov_us, ppmdu_us) == expected_value assert getter(ov_eu, ppmdu_eu) == expected_value setter(123, ov_us, ppmdu_us)
def draw_dungeon_map_bgs(rom, dungeon_map_bg_dir, config): os.makedirs(dungeon_map_bg_dir, exist_ok=True) dungeon_bin = FileType.DUNGEON_BIN.deserialize(rom.getFileByName('DUNGEON/dungeon.bin'), config) ground_dungeon_tilesets = HardcodedGroundDungeonTilesets.get_ground_dungeon_tilesets( get_binary_from_rom_ppmdu(rom, config.binaries['overlay/overlay_0011.bin']), config ) dungeons = HardcodedDungeons.get_dungeon_list( get_binary_from_rom_ppmdu(rom, config.binaries['arm9.bin']), config ) mappa = FileType.MAPPA_BIN.deserialize(rom.getFileByName('BALANCE/mappa_s.bin')) levels_by_id = config.script_data.level_list__by_id bg_list_bin = rom.getFileByName('MAP_BG/bg_list.dat') bg_list = FileType.BG_LIST_DAT.deserialize(bg_list_bin) for i, entry in enumerate(ground_dungeon_tilesets): if entry.ground_level >= 0xFFFF: continue level = levels_by_id[entry.ground_level] print(f"{i + 1}/{len(ground_dungeon_tilesets)-1} - {level.name}") print(entry) mappa_idx = dungeons[entry.dungeon_id].mappa_index start_offset = dungeons[entry.dungeon_id].start_after length = dungeons[entry.dungeon_id].number_floors if entry.dungeon_id == 71: print("DEEP CONCEALED RUINS SKIPPED") continue if entry.unk2 == 1: tileset_id = mappa.floor_lists[mappa_idx][start_offset].layout.tileset_id elif entry.unk2 == 100: tileset_id = mappa.floor_lists[mappa_idx][start_offset + length - 1].layout.tileset_id else: raise ValueError("Unknown unk2") if tileset_id == 170: tileset_id = 1 dma: Dma = dungeon_bin.get(f'dungeon{tileset_id}.dma') dpl: Dpl = dungeon_bin.get(f'dungeon{tileset_id}.dpl') dpla: Dpla = dungeon_bin.get(f'dungeon{tileset_id}.dpla') dpci: Dpci = dungeon_bin.get(f'dungeon{tileset_id}.dpci') dpc: Dpc = dungeon_bin.get(f'dungeon{tileset_id}.dpc') bma: Bma = bg_list.level[level.mapid].get_bma(rom) duration = round(1000 / 60 * max(16, min(dpla.durations_per_frame_for_colors))) drawer = DmaDrawer(dma) rules = drawer.rules_from_bma(bma) mappings = drawer.get_mappings_for_rules(rules, treat_outside_as_wall=True, variation_index=0) frames = drawer.draw(mappings, dpci, dpc, dpl, dpla) frames[0].save( os.path.join(dungeon_map_bg_dir, level.name + '.gif'), save_all=True, append_images=frames[1:], duration=duration, loop=0, optimize=False ) frames[0].save( os.path.join(dungeon_map_bg_dir, level.name + '.png') )
def apply(self, patch: Union[Pmd2Patch, Pmd2SimplePatch], binaries: Dict[str, Pmd2Binary], patch_file_dir: str, stub_path: str, game_id: str): try: with tempfile.TemporaryDirectory() as tmp: shutil.copytree(patch_file_dir, tmp, dirs_exist_ok=True) # Build ASM file to run asm_entrypoint = '' # First read in stub with open(os.path.join(tmp, stub_path)) as f: asm_entrypoint += f.read() + '\n' if isinstance(patch, Pmd2SimplePatch): for replace in patch.string_replacements: fn = os.path.join(tmp, replace.filename) game = None for game_candidate in replace.games: if game_candidate.game_id == game_id: game = game_candidate if game is not None: with open(os.path.join(tmp, fn), 'r') as f: new_content = replace.regexp.sub( game.replace, f.read()) with open(os.path.join(tmp, fn), 'w') as f: f.write(new_content) # If it's a simple patch just output and re-import all binaries. for binary_name, binary in binaries.items(): binary_path = os.path.join(tmp, binary_name.split('/')[-1]) # Write binary to tmp dir with open(binary_path, 'wb') as f: try: f.write( get_binary_from_rom_ppmdu( self.rom, binary)) except ValueError as err: if binary_name.split( '/')[-1] == 'overlay_0036.bin': continue # We ignore if End's extra overlay is missing. raise err # For simple patches we also output the overlay table as y9.bin: binary_path = os.path.join(tmp, Y9_BIN) # Write binary to tmp dir with open(binary_path, 'wb') as f: f.write(self.rom.arm9OverlayTable) # Then include other includes for include in patch.includes: asm_entrypoint += f'.include "{os.path.join(tmp, include.filename)}"\n' # Build binary blocks if isinstance(patch, Pmd2Patch): for open_bin in patch.open_bins: binary = binaries[open_bin.filepath] binary_path = os.path.join( tmp, open_bin.filepath.split('/')[-1]) os.makedirs(os.path.dirname(binary_path), exist_ok=True) # Write binary to tmp dir with open(binary_path, 'wb') as f: f.write(get_binary_from_rom_ppmdu( self.rom, binary)) asm_entrypoint += f'.open "{binary_path}", 0x{binary.loadaddress:0x}\n' for include in open_bin.includes: asm_entrypoint += f'.include "{os.path.join(tmp, include.filename)}"\n' asm_entrypoint += '.close\n' # Write final asm file with open_utf8(os.path.join(tmp, ASM_ENTRYPOINT_FN), 'w') as f: f.write(asm_entrypoint) # Run armips original_cwd = os.getcwd() os.chdir(tmp) try: prefix = "" # Under Windows, try to load from SkyTemple _resources dir first. if sys.platform.startswith('win') and os.path.exists( os.path.join(get_resources_dir(), 'armips.exe')): prefix = os.path.join(get_resources_dir(), '') result = subprocess.Popen( [f'{prefix}armips', ASM_ENTRYPOINT_FN], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) retcode = result.wait() except FileNotFoundError as ex: raise ArmipsNotInstalledError( "ARMIPS could not be found. Make sure, that " "'armips' is inside your system's PATH.") from ex finally: # Restore cwd os.chdir(original_cwd) if retcode != 0: raise PatchError( "ARMIPS reported an error while applying the patch.", str(result.stdout.read(), 'utf-8'), str(result.stderr.read(), 'utf-8') if result.stderr else '') # Load the binaries back into the ROM opened_binaries = {} if isinstance(patch, Pmd2SimplePatch): # Read in all binaries again opened_binaries = binaries # Also read in arm9OverlayTable binary_path = os.path.join(tmp, Y9_BIN) with open(binary_path, 'rb') as f: self.rom.arm9OverlayTable = f.read() else: # Read opened binaries again for open_bin in patch.open_bins: opened_binaries[open_bin.filepath] = binaries[ open_bin.filepath] for binary_name, binary in opened_binaries.items(): binary_path = os.path.join(tmp, binary_name.split('/')[-1]) with open(binary_path, 'rb') as f: try: set_binary_in_rom_ppmdu(self.rom, binary, f.read()) except ValueError as err: if binary_name.split( '/')[-1] == 'overlay_0036.bin': continue # We ignore if End's extra overlay is missing. raise err except (PatchError, ArmipsNotInstalledError): raise except BaseException as ex: raise RuntimeError(f"Error while applying the patch: {ex}") from ex
# the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # SkyTemple is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with SkyTemple. If not, see <https://www.gnu.org/licenses/>. import os from ndspy.rom import NintendoDSRom from skytemple_files.common.util import get_ppmdu_config_for_rom, get_binary_from_rom_ppmdu from skytemple_files.hardcoded.rank_up_table import HardcodedRankUpTable base_dir = os.path.join(os.path.dirname(__file__), '..', '..', '..', '..') rom_us = NintendoDSRom.fromFile(os.path.join(base_dir, 'skyworkcopy_us.nds')) ppmdu_us = get_ppmdu_config_for_rom(rom_us) arm9_us = get_binary_from_rom_ppmdu(rom_us, ppmdu_us.binaries['arm9.bin']) rank_up = HardcodedRankUpTable.get_rank_up_table(arm9_us, ppmdu_us) for i, rank in enumerate(rank_up): print(i, rank) # Try setting and see if still same. HardcodedRankUpTable.set_rank_up_table(rank_up, arm9_us, ppmdu_us) assert rank_up == HardcodedRankUpTable.get_rank_up_table(arm9_us, ppmdu_us)
# # You should have received a copy of the GNU General Public License # along with SkyTemple. If not, see <https://www.gnu.org/licenses/>. # mypy: ignore-errors import os from ndspy.rom import NintendoDSRom from skytemple_files.common.util import get_ppmdu_config_for_rom, get_binary_from_rom_ppmdu from skytemple_files.hardcoded.fixed_floor import HardcodedFixedFloorTables base_dir = os.path.join(os.path.dirname(__file__), '..', '..', '..', '..') rom = NintendoDSRom.fromFile(os.path.join(base_dir, 'skyworkcopy.nds')) ppmdu = get_ppmdu_config_for_rom(rom) ov29 = get_binary_from_rom_ppmdu(rom, ppmdu.binaries['overlay/overlay_0029.bin']) ov10 = get_binary_from_rom_ppmdu(rom, ppmdu.binaries['overlay/overlay_0010.bin']) def print_and_test(binary, getter, setter): values = getter(binary, ppmdu) for i, val in enumerate(values): print(i, val) # Try setting and see if still same. setter(binary, values, ppmdu) assert values == getter(binary, ppmdu)
# GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with SkyTemple. If not, see <https://www.gnu.org/licenses/>. # mypy: ignore-errors import os from ndspy.rom import NintendoDSRom from skytemple_files.common.util import get_ppmdu_config_for_rom, get_binary_from_rom_ppmdu from skytemple_files.hardcoded.recruitment_tables import HardcodedRecruitmentTables base_dir = os.path.join(os.path.dirname(__file__), '..', '..', '..', '..') rom_us = NintendoDSRom.fromFile(os.path.join(base_dir, 'skyworkcopy_us.nds')) ppmdu_us = get_ppmdu_config_for_rom(rom_us) ov11_us = get_binary_from_rom_ppmdu(rom_us, ppmdu_us.binaries['overlay/overlay_0011.bin']) species = HardcodedRecruitmentTables.get_monster_species_list(ov11_us, ppmdu_us) level = HardcodedRecruitmentTables.get_monster_levels_list(ov11_us, ppmdu_us) location = HardcodedRecruitmentTables.get_monster_locations_list(ov11_us, ppmdu_us) for i, (e_species, e_level, e_location) in enumerate(zip(species, level, location)): print(i, e_species, e_level, e_location) # Try setting and see if still same. HardcodedRecruitmentTables.set_monster_species_list(species, ov11_us, ppmdu_us) assert species == HardcodedRecruitmentTables.get_monster_species_list(ov11_us, ppmdu_us) HardcodedRecruitmentTables.set_monster_levels_list(level, ov11_us, ppmdu_us) assert level == HardcodedRecruitmentTables.get_monster_levels_list(ov11_us, ppmdu_us)
def apply(self, patch: Union[Pmd2Patch, Pmd2SimplePatch], binaries: Dict[str, Pmd2Binary], patch_file_dir: str, stub_path: str, game_id: str, parameter_values: Dict[str, Union[int, str]]): with tempfile.TemporaryDirectory() as tmp: try: shutil.copytree(patch_file_dir, tmp, symlinks=True, dirs_exist_ok=True) set_rw_permission_folder(tmp) # Build ASM file to run asm_entrypoint = '' # First read in stub with open(os.path.join(tmp, stub_path)) as fi: asm_entrypoint += fi.read() + '\n' if isinstance(patch, Pmd2SimplePatch): for replace in patch.string_replacements: fn = os.path.join(tmp, replace.filename) game = None for game_candidate in replace.games: if game_candidate.game_id == game_id: game = game_candidate if game is not None: with open_utf8(os.path.join(tmp, fn), 'r') as fi: new_content = replace.regexp.sub( game.replace, fi.read()) with open_utf8(os.path.join(tmp, fn), 'w') as fi: fi.write(new_content) # If it's a simple patch just output and re-import all binaries. for binary_name, binary in binaries.items(): binary_path = os.path.join(tmp, binary_name.split('/')[-1]) # Write binary to tmp dir with open(binary_path, 'wb') as fib: try: fib.write( get_binary_from_rom_ppmdu( self.rom, binary)) except ValueError as err: if binary_name.split( '/')[-1] == 'overlay_0036.bin': continue # We ignore if End's extra overlay is missing. raise err # For simple patches we also output the overlay table as y9.bin: binary_path = os.path.join(tmp, Y9_BIN) # Write binary to tmp dir with open(binary_path, 'wb') as fib: fib.write(self.rom.arm9OverlayTable) # Then include other includes for include in patch.includes: asm_entrypoint += f'.include "{os.path.join(tmp, include.filename)}"\n' # Build binary blocks if isinstance(patch, Pmd2Patch): for open_bin in patch.open_bins: binary = binaries[open_bin.filepath] binary_path = os.path.join( tmp, open_bin.filepath.split('/')[-1]) os.makedirs(os.path.dirname(binary_path), exist_ok=True) # Write binary to tmp dir with open(binary_path, 'wb') as fib: fib.write( get_binary_from_rom_ppmdu(self.rom, binary)) asm_entrypoint += f'.open "{binary_path}", 0x{binary.loadaddress:0x}\n' for include in open_bin.includes: asm_entrypoint += f'.include "{os.path.join(tmp, include.filename)}"\n' asm_entrypoint += '.close\n' # Write final asm file with open_utf8(os.path.join(tmp, ASM_ENTRYPOINT_FN), 'w') as fi: fi.write(asm_entrypoint) # Build parameters for equ parameters = [] for param_name, param_value in parameter_values.items(): parameters += [ '-equ', param_name, f'"{param_value}"' if isinstance( param_value, str) else str(param_value) ] # Run armips try: prefix = "" # Under Windows, try to load from SkyTemple _resources dir first. if sys.platform.startswith('win') and os.path.exists( os.path.join(get_resources_dir(), 'armips.exe')): prefix = os.path.join(get_resources_dir(), '') exec_name = os.getenv('SKYTEMPLE_ARMIPS_EXEC', f'{prefix}armips') cmd_line = [exec_name, ASM_ENTRYPOINT_FN] + parameters if os.getenv('SKYTEMPLE_DEBUG_ARMIPS_OUTPUT', False): print("ARMIPS CMDLINE:") print(cmd_line) result = subprocess.Popen(cmd_line, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=tmp) retcode = result.wait() except FileNotFoundError as ex: raise make_user_err( ArmipsNotInstalledError, _("ARMIPS could not be found. Make sure, that " "'armips' is inside your system's PATH.")) from ex if os.getenv('SKYTEMPLE_DEBUG_ARMIPS_OUTPUT', False): print("ARMIPS OUTPUT:") if result is not None: print(str(result.stdout.read(), 'utf-8')) # type: ignore print( str(result.stderr.read(), 'utf-8') if result.stderr else '') # type: ignore if retcode != 0: raise make_user_err( PatchError, _("ARMIPS reported an error while applying the patch." ), str(result.stdout.read(), 'utf-8'), str(result.stderr.read(), 'utf-8') # type: ignore if result.stderr else '') # type: ignore # Load the binaries back into the ROM opened_binaries = {} if isinstance(patch, Pmd2SimplePatch): # Read in all binaries again opened_binaries = binaries # Also read in arm9OverlayTable binary_path = os.path.join(tmp, Y9_BIN) with open(binary_path, 'rb') as fib: self.rom.arm9OverlayTable = fib.read() else: # Read opened binaries again for open_bin in patch.open_bins: opened_binaries[open_bin.filepath] = binaries[ open_bin.filepath] for binary_name, binary in opened_binaries.items(): binary_path = os.path.join(tmp, binary_name.split('/')[-1]) with open(binary_path, 'rb') as fib: try: set_binary_in_rom_ppmdu(self.rom, binary, fib.read()) except ValueError as err: if binary_name.split( '/')[-1] == 'overlay_0036.bin': continue # We ignore if End's extra overlay is missing. raise err except (PatchError, ArmipsNotInstalledError): raise except BaseException as ex: raise RuntimeError(f( _("Error while applying the patch: {ex}"))) from ex
os.makedirs(out_dir, exist_ok=True) in_rom = NintendoDSRom.fromFile( os.path.join(base_dir, 'skyworkcopy_us.nds')) # Load PPMDU config, but remove all data about Patches and LooseBinFiles. config = get_ppmdu_config_for_rom(in_rom) config.asm_patches_constants.patches = {} config.asm_patches_constants.loose_bin_files = {} patcher = Patcher(in_rom, config, skip_core_patches=True) # Load the package patcher.add_pkg(package) assert not patcher.is_applied('ExamplePatch') patcher.apply('ExamplePatch') with open(os.path.join(out_dir, 'example_patch_ov11.bin'), 'wb') as f: f.write( get_binary_from_rom_ppmdu( in_rom, config.binaries['overlay/overlay_0011.bin'])) assert patcher.is_applied('ExamplePatch') in_rom.saveToFile(os.path.join(out_dir, 'patched.nds')) # Check if really patched out_rom = NintendoDSRom.fromFile(os.path.join(out_dir, 'patched.nds')) new_patcher = Patcher(out_rom, get_ppmdu_config_for_rom(out_rom)) new_patcher.add_pkg(package) assert new_patcher.is_applied('ExamplePatch')