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