def read_rels_extract(self): # extract rels from 'RELs.arc' print(f"{self.step_count:2} Read RELs from '{self.rels_archive_path}'") self.step_count += 1 found_rel_count = 0 with self.rels_archive_path.open('rb') as file: rarc = libarc.read(file.read()) for depth, file in rarc.files_and_folders: if not isinstance(file, libarc.File): continue if file.name.endswith(".rel"): if not file.name.lower() in settings.SHA1: error( f"unknown REL file: '{file.name}' ({file.name.lower()})" ) fatal_exit() sha1_check(file.name, file.data, settings.SHA1[file.name.lower()]) rel = librel.read(file.data) rel.path = Path(file.name) rel.map = self.MAPS[file.name.lower().replace( ".rel", ".map")] self.RELS[rel.index] = rel found_rel_count += 1 info(f"found {found_rel_count} RELs in '{self.rels_archive_path}'")
def read_rels(self): # loads rels print(f"{self.step_count:2} Read RELs at '{self._rel_path}'") self.step_count += 1 self.MAPS = {} for map_filepath in self.maps_path: self.MAPS[map_filepath.name.lower()] = map_filepath self.RELS = {} for rel_filepath in self.rels_path: with rel_filepath.open('rb') as file: if not rel_filepath.name.lower() in settings.SHA1: error( f"unknown REL file: '{rel_filepath}' ({rel_filepath.name.lower()})" ) fatal_exit() data = bytearray(file.read()) sha1_check(rel_filepath, data, settings.SHA1[rel_filepath.name.lower()]) rel = librel.read(data) rel.path = rel_filepath rel.map = self.MAPS[rel_filepath.name.lower().replace( ".rel", ".map")] self.RELS[rel.index] = rel
def check_sha1(game_path, build_path): #dol_path = game_path.joinpath("main.dol") #if not dol_path.exists(): # raise CheckException(f"File not found: '{dol_path}'") rel_path = game_path.joinpath("rel/Final/Release") if not rel_path.exists(): raise CheckException(f"Path not found: '{rel_path}'") rels_path = get_files_with_ext(rel_path, ".rel") rels_archive_path = game_path.joinpath("RELS.arc") if not rels_archive_path.exists(): raise CheckException(f"File not found: '{rels_archive_path}'") #LOG.debug(f"DOL Path: '{dol_path}'") LOG.debug(f"RELs Path: '{rel_path}' (found {len(rels_path)} RELs)") LOG.debug(f"RELs Archive Path: '{rels_archive_path}'") EXPECTED = {} #with dol_path.open('rb') as file: # data = file.read() # EXPECTED[0] = (str(dol_path), sha1_from_data(data),sha1_from_data(data),) EXPECTED[0] = ( "", "4997D93B9692620C40E90374A0F1DBF0E4889395", "4997D93B9692620C40E90374A0F1DBF0E4889395", ) for rel_filepath in rels_path: with rel_filepath.open('rb') as file: data = bytearray(file.read()) yaz0_data = data if struct.unpack('>I', data[:4])[0] == 0x59617A30: data = yaz0.decompress(io.BytesIO(data)) rel = librel.read(data) EXPECTED[rel.index] = ( str(rel_filepath), sha1_from_data(yaz0_data), sha1_from_data(data), ) with rels_archive_path.open('rb') as file: rarc = libarc.read(file.read()) for depth, file in rarc.files_and_folders: if not isinstance(file, libarc.File): continue if file.name.endswith(".rel"): data = file.data yaz0_data = data if struct.unpack('>I', data[:4])[0] == 0x59617A30: data = yaz0.decompress(io.BytesIO(data)) xxx_path = Path('build').joinpath(file.name) with xxx_path.open('wb') as write_file: write_file.write(data) rel = librel.read(data) EXPECTED[rel.index] = ( file.name, sha1_from_data(yaz0_data), sha1_from_data(data), ) if not build_path.exists(): raise CheckException(f"Path not found: '{build_path}'") build_dol_path = build_path.joinpath("main.dol") if not build_dol_path.exists(): raise CheckException(f"File not found: '{build_dol_path}'") build_rels_path = get_files_with_ext(build_path, ".rel") CURRENT = {} with build_dol_path.open('rb') as file: data = file.read() CURRENT[0] = ( str(build_dol_path), sha1_from_data(data), sha1_from_data(data), ) for rel_filepath in build_rels_path: with rel_filepath.open('rb') as file: data = bytearray(file.read()) yaz0_data = data if struct.unpack('>I', data[:4])[0] == 0x59617A30: data = yaz0.decompress(io.BytesIO(data)) rel = librel.read(data) CURRENT[rel.index] = ( str(rel_filepath), sha1_from_data(yaz0_data), sha1_from_data(data), ) expected_keys = set(EXPECTED.keys()) current_keys = set(CURRENT.keys()) match = expected_keys - current_keys if len(match) > 0: raise CheckException( f"Missing RELs (expected: {len(expected_keys)}, found: {len(current_keys)})" ) errors = 0 for key in expected_keys: if key in current_keys: expected = EXPECTED[key] current = CURRENT[key] if current[2] != expected[2]: errors += 1 LOG.error( f"{current[2]} {expected[2]} {current[0]} ({expected[0]})") if errors > 0: raise CheckException("NO MATCH!") return True
def rel_info( debug, rel_path, dump_header, dump_sections, dump_data, dump_relocation, dump_imp, dump_all, ): if debug: LOG.setLevel(logging.DEBUG) path = Path(rel_path) if not path.exists(): LOG.error(f"File not found: '{path}'") sys.exit(1) with open(path, "rb") as file: buffer = file.read() rel = librel.read(buffer) if dump_all: dump_header = True dump_sections = True dump_data = True dump_relocation = True dump_imp = True if not (dump_header or dump_sections or dump_data or dump_relocation or dump_imp): CONSOLE.print("no dump options specified") if dump_header: CONSOLE.print(f"Header:") CONSOLE.print(f"\tindex: {rel.index}") CONSOLE.print(f"\tversion: {rel.version}") CONSOLE.print(f"\tsection count: {rel.numSections}") CONSOLE.print(f"\tsection offset: 0x{rel.sectionInfoOffset:04X}") CONSOLE.print(f"") CONSOLE.print(f"\tname offset: 0x{rel.nameOffset:04X}") CONSOLE.print(f"\tname size: {rel.nameSize}") CONSOLE.print(f"\talign: 0x{rel.align:02X}") CONSOLE.print(f"\tfix size: 0x{rel.fixSize:04X}") CONSOLE.print(f"\tbss size: 0x{rel.bssSection:X}") CONSOLE.print(f"\tbss align: 0x{rel.bssAlign:02X}") CONSOLE.print(f"\tbss section: 0x{rel.bssSize:04X}") CONSOLE.print(f"\trel offset: 0x{rel.relOffset:04X}") CONSOLE.print(f"\timp offset: 0x{rel.impOffset:04X}") CONSOLE.print(f"\timp size: 0x{rel.impSize:04X}") CONSOLE.print( f"\t_prolog: 0x{rel.prolog:04X} (section: 0x{rel.prologSection:X})" ) CONSOLE.print( f"\t_epilog: 0x{rel.epilog:04X} (section: 0x{rel.epilogSection:X})" ) CONSOLE.print( f"\t_unresolved: 0x{rel.unresolved:04X} (section: 0x{rel.unresolvedSection:X})" ) if dump_sections: CONSOLE.print(f"Sections:") for i, section in enumerate(rel.sections): CONSOLE.print( f"\t#{i:<2} offset: 0x{section.offset:08X}, length: 0x{section.length:04X}, unknown flag: {section.unknown_flag}, executable flag: {section.executable_flag}" ) if dump_imp: CONSOLE.print(f"Imp Table:") imp_buffer = rel.data[rel.impOffset:] for i in range(rel.impSize // 8): imp_offset = 0x8 * i module_id, rel_offset = struct.unpack( ">II", imp_buffer[imp_offset:][:0x8]) CONSOLE.print( f"\t#{i:<2} module: {module_id:>4}, offset: 0x{rel_offset:04X}" ) if dump_relocation: CONSOLE.print(f"Relocations:") imp_buffer = rel.data[rel.impOffset:] for i in range(rel.impSize // 8): imp_offset = 0x8 * i module_id, rel_offset = struct.unpack( ">II", imp_buffer[imp_offset:][:0x8]) CONSOLE.print(f"\t[ module: {module_id:>4} ]") section = None offset = 0 rel_index = 0 while True: relocation = librel.read_relocation(module_id, rel.data[rel_offset:]) rel_offset += 0x8 rel_index += 1 extra = "" if section and path.name in settings.REL_TEMP_LOCATION: base = settings.REL_TEMP_LOCATION[path.name] base += section.offset - rel.sections[1].offset extra = f" | {base:08X} {base+relocation.offset + offset:08X}" CONSOLE.print( f"\t#{rel_index:<3} {librel.RELOCATION_NAMES[relocation.type]:<20} {relocation.section:>4} 0x{relocation.offset+offset:08X} 0x{relocation.addend:08X}{extra}" ) if relocation.type == librel.R_PPC_NONE: continue if relocation.type == librel.R_DOLPHIN_NOP: # NOP is used to simulate long offset values, this is because # the offset field is limited to 2^16-1 values. Thus, any offsets # above 2^16 will be divided into a NOP + original relocation type. offset += relocation.offset continue if relocation.type == librel.R_DOLPHIN_SECTION: section = rel.sections[relocation.section] offset = 0 continue assert section relocation.parent = section relocation.relative_offset = relocation.offset relocation.offset += offset offset = relocation.offset assert relocation.type != librel.R_DOLPHIN_MRKREF if relocation.type == librel.R_DOLPHIN_END: break if dump_data: CONSOLE.print(f"Sections:") for i, section in enumerate(rel.sections): if section.data: CONSOLE.print(f"\t#{i:<2}") hexdata = hexdump.hexdump(section.data, result="return") CONSOLE.print(hexdata)