def decrypt_amiromtype1_stream(stream, data): # If we open a stream starting with AMIROMTYPE1, we assume that the file # has been opened via Archive and stored in the database with the # decrypted checksum. # FIXME: Better to implement this properly with archive filters... path = stream.archive_path archive = Archive(path) key_path = archive.join(archive.dirname(path), "rom.key") key_archive = Archive(key_path) try: f2 = key_archive.open(key_path) except Exception: raise Exception("Did not find rom.key to decrypt ROM with") print("Using key file", key_path) key_data = f2.read() f2.close() out_data = [] result = {} f = BytesIO(data[len("AMIROMTYPE1"):] + stream.read()) sha1_obj = hashlib.sha1() while True: data = f.read(len(key_data)) if not data: break dec = [] for i in range(len(data)): dec.append(data[i] ^ key_data[i]) dec_data = bytes(dec) # if file is not None: # file.write(dec_data) out_data.append(dec_data) # if sha1 is not None: sha1_obj.update(dec_data) result["data"] = b"".join(out_data) result["sha1"] = sha1_obj.hexdigest() # print(result) ROMManager.patch_rom(result) # if file is not None: # file.write(result["data"]) # return result return BytesIO(result["data"])
def write_stream(stream, path, sha1=None): dst = path dst_partial = os.path.join(os.path.dirname(dst), "~" + os.path.basename(dst) + ".tmp") sha1_obj = hashlib.sha1() written_size = 0 with open(dst_partial, "wb") as f: while True: data = stream.read(CHUNK_SIZE) if data.startswith(b"AMIROMTYPE1"): # FIXME: Better to implement this properly with archive # filters... stream = decrypt_amiromtype1_stream(stream, data) data = stream.read(CHUNK_SIZE) if not data: break f.write(data) sha1_obj.update(data) written_size += len(data) written_sha1 = sha1_obj.hexdigest() # If SHA-1 did not match, see if the written file matches one of the ROMs # than ROMManager can transform into the correct one. if (sha1 is not None and written_sha1 != sha1 and (written_sha1, sha1) in ROMManager.rom_transformations): print(f"Patching ROM {written_sha1} -> {sha1}") rom = {"sha1": written_sha1, "data": b""} with open(dst_partial, "rb") as f: rom["data"] = f.read() ROMManager.patch_rom(rom) with open(dst_partial, "wb") as f: f.write(rom["data"]) written_sha1 = rom["sha1"] if sha1 is not None: if written_sha1 != sha1: raise Exception( f"Written SHA-1 ({written_sha1}) does not match expected SHA-1 ({sha1})" ) os.rename(dst_partial, dst) return written_sha1, written_size
def prepare_roms(self): print("LaunchHandler.prepare_roms") current_task.set_progress(gettext("Preparing kickstart ROMs...")) amiga_model = self.config.get("amiga_model", "A500") model_config = Amiga.get_model_config(amiga_model) roms = [("kickstart_file", model_config["kickstarts"])] if self.config["kickstart_ext_file"] or model_config["ext_roms"]: # not all Amigas have extended ROMs roms.append(("kickstart_ext_file", model_config["ext_roms"])) if amiga_model.lower() == "cd32/fmv": roms.append(("fvm_rom", [CD32_FMV_ROM])) if self.config["graphics_card"].lower().startswith("picasso-iv"): roms.append(("graphics_card_rom", [PICASSO_IV_74_ROM])) if self.config["accelerator"].lower() == "cyberstorm-ppc": roms.append(("accelerator_rom", ["cyberstormppc.rom"])) if self.config["freezer_cartridge"] == "action-replay-2": # Ideally, we would want to recognize ROMs based on zeroing the # first four bytes, but right now we simply recognize a common # additional version. freezer_cartridge_rom isn't a real option, # we just want to copy the rom file and let FS-UAE find it roms.append( ( "[freezer_cartridge]", [ ACTION_REPLAY_MK_II_2_14_ROM.sha1, ACTION_REPLAY_MK_II_2_14_MOD_ROM.sha1, ], ) ) elif self.config["freezer_cartridge"] == "action-replay-3": roms.append( ( "[freezer_cartridge]", [ ACTION_REPLAY_MK_III_3_17_ROM.sha1, ACTION_REPLAY_MK_III_3_17_MOD_ROM.sha1, ], ) ) use_temp_kickstarts_dir = False for config_key, default_roms in roms: print("[ROM]", config_key, default_roms) src = self.config[config_key] print("[ROM]", src) if not src: for sha1 in default_roms: print("[ROM] Trying", sha1) if is_sha1(sha1): rom_src = self.fsgs.file.find_by_sha1(sha1) if rom_src: src = rom_src print("[ROM] Found", rom_src) break else: # roms_dir = FSGSDirectories.get_kickstarts_dir() # src = os.path.join(roms_dir, sha1) # if os.path.exists(src): # break # loop up file in roms dir instead src = sha1 elif src == "internal": continue elif src: src = Paths.expand_path(src) if not src: raise TaskFailure( gettext( "Did not find required Kickstart or " "ROM for {}. Wanted one of these files: {}".format( config_key, repr(default_roms) ) ) ) dest = os.path.join(self.temp_dir, os.path.basename(src)) def lookup_rom_from_src(src): parts = src.split(":", 1) if len(parts) == 2 and len(parts[0]) > 1: # src has a scheme (not a Windows drive letter). Assume # we can find this file. return src archive = Archive(src) if archive.exists(src): return src dirs = [self.fsgs.amiga.get_kickstarts_dir()] for dir_ in dirs: path = os.path.join(dir_, src) print("[ROM] Checking", repr(path)) archive = Archive(path) if archive.exists(path): return path return None org_src = src src = lookup_rom_from_src(src) if not src and org_src == "cyberstormppc.rom": src = lookup_rom_from_src( "ralphschmidt-cyberstorm-ppc-4471.rom" ) if not src: for ( dir_ ) in FSGSDirectories.get_amiga_forever_directories(): path = os.path.join( dir_, "Shared", "rom", "ralphschmidt-cyberstorm-ppc-4471.rom", ) if os.path.exists(path): src = path print("[ROM] Found", path) break else: print("[ROM] Trying", path) stream = None # FIXME: prepare_roms should be rewritten, it's kind of crap. # Rom patching and decryption should be handled differently. Should # use file database filters, and decryption via rom.key should only # be supported when using uncompressed files directly on disk. if not src or not os.path.exists(src): try: stream = self.fsgs.file.open(src) if stream is None: raise FileNotFoundError(src) except FileNotFoundError: raise TaskFailure( gettext( "Cannot find required ROM " "file: {name}".format(name=repr(org_src)) ) ) with open(dest, "wb") as f: if stream: print("[ROM] From stream => {}".format(dest)) rom = {} rom["data"] = stream.read() rom["sha1"] = hashlib.sha1(rom["data"]).hexdigest() ROMManager.patch_rom(rom) f.write(rom["data"]) else: archive = Archive(src) ROMManager.decrypt_archive_rom(archive, src, file=f) if use_temp_kickstarts_dir: self.config[config_key] = os.path.basename(src) else: self.config[config_key] = dest if use_temp_kickstarts_dir: self.config["kickstarts_dir"] = self.temp_dir