Esempio n. 1
0
    def write_to_rom(self, rom):
        for ips_desc_filename in [s for s in os.listdir(get_ips_directory(rom.type)) if s.lower().endswith(".yml")]:
            patch_name = ips_desc_filename[:-4]
            with open(os.path.join(get_ips_directory(rom.type), ips_desc_filename)) as ips_desc_file:
                ips_desc = yml_load(ips_desc_file)
                if (ips_desc["Title"] in self.patches) and (self.patches[ips_desc["Title"]].lower() == "enabled"):
                    # First, check that we can apply this
                    ranges = map(lambda y: tuple(map(lambda z: int(z, 0), y[1:-1].split(','))), ips_desc["Ranges"])
                    for range in ranges:
                        if not rom.is_unallocated(range):
                            raise CoilSnakeError(
                                "Can't apply patch \"{}\" because range ({:#x},{:#x}) is not unallocated".format(
                                    ips_desc["Title"], range[0], range[1]))

                    # Now apply the patch
                    ips = IpsPatch()
                    offset = 0
                    if "Header" in ips_desc:
                        offset = ips_desc["Header"]
                    ips.load(get_ips_filename(rom.type, patch_name), offset)
                    ips.apply(rom)

                    # Mark the used ranges as used
                    for range in ranges:
                        rom.mark_allocated(range)
Esempio n. 2
0
    def write_to_rom(self, rom):
        for ips_desc_filename in [s for s in os.listdir(get_ips_directory(rom.type)) if s.lower().endswith(".yml")]:
            patch_name = ips_desc_filename[:-4]
            with open(os.path.join(get_ips_directory(rom.type), ips_desc_filename)) as ips_desc_file:
                ips_desc = yml_load(ips_desc_file)
                if "Hidden" in ips_desc and ips_desc["Hidden"]:
                    continue
                elif (ips_desc["Title"] in self.patches) and (self.patches[ips_desc["Title"]].lower() == "enabled"):
                    # First, check that we can apply this
                    ranges = map(lambda y: tuple(map(lambda z: int(z, 0), y[1:-1].split(','))), ips_desc["Ranges"])
                    for range in ranges:
                        if not rom.is_unallocated(range):
                            raise CoilSnakeError(
                                "Can't apply patch \"{}\" because range ({:#x},{:#x}) is not unallocated".format(
                                    ips_desc["Title"], range[0], range[1]))

                    # Now apply the patch
                    ips = IpsPatch()
                    offset = 0
                    if "Header" in ips_desc:
                        offset = ips_desc["Header"]
                    ips.load(get_ips_filename(rom.type, patch_name), offset)
                    ips.apply(rom)

                    # Mark the used ranges as used
                    for range in ranges:
                        rom.mark_allocated(range)
Esempio n. 3
0
class EbpPatch(object):
    def __init__(self):

        self.patch = IpsPatch()
        self.metadata = None

    @property
    def last_offset_used(self):

        return self.patch.last_offset_used

    def load(self, filename):

        try:
            self.patch.load(filename)

            with open(filename, 'rb') as ebp:
                # Skip to the end of the IPS part of the patch
                ebp.seek(5 + sum([
                    3 + 2 + i[1][1] + (2 if i[0] == 'RLE' else 0)
                    for i in self.patch.instructions
                ]) + 3)

                # Load the metadata, if available
                try:
                    self.metadata = json.loads(ebp.read().decode("utf-8"))
                except ValueError:
                    self.metadata = None
        except:
            raise CoilSnakeError("Not a valid EBP file: " + filename)

    def apply(self, rom):

        self.patch.apply(rom)

    def is_applied(self, rom):

        return self.patch.is_applied(rom)

    def create(self,
               clean_rom,
               modified_rom,
               author="",
               title="",
               description=""):

        self.patch.create(clean_rom, modified_rom)
        self.metadata = {
            "patcher": "EBPatcher",  # Used for compatibility with EBPatcher
            "author": author,
            "title": title,
            "description": description
        }
Esempio n. 4
0
def patch_rom(clean_rom_filename, patched_rom_filename, patch_filename, headered, progress_bar=None):
    if not os.path.isfile(clean_rom_filename):
        raise RuntimeError("Clean Rom \"" + clean_rom_filename + "\" is not a file.")
    if not os.path.isfile(patch_filename):
        raise RuntimeError("Patch \"" + patch_filename + "\" is not a file.")

    if clean_rom_filename != patched_rom_filename:
        copyfile(clean_rom_filename, patched_rom_filename)

    log.info("Patching ROM {} with patch {}".format(patched_rom_filename, patch_filename))
    patching_start_time = time.time()

    if patch_filename.endswith(".ips"):
        output_rom = Rom()
        output_rom.from_file(clean_rom_filename)
        patch = IpsPatch()
    elif patch_filename.endswith(".ebp"):
        output_rom = EbRom()
        output_rom.from_file(clean_rom_filename)
        patch = EbpPatch()
    else:
        raise CoilSnakeError("Unknown patch format.")

    # Load the patch and expand the ROM as needed
    add_header = headered and not isinstance(patch, EbpPatch)
    extra = int(add_header)*0x200  # 0x200 if a header will be added, 0 otherwise
    patch.load(patch_filename)
    if isinstance(patch, EbpPatch):
        log.info("Patch: {title} by {author}".format(**patch.metadata))
    if patch.last_offset_used > len(output_rom) + extra:
        if patch.last_offset_used < 0x400000 + extra:
            output_rom.expand(0x400000)
        elif patch.last_offset_used < 0x600000 + extra:
            output_rom.expand(0x600000)
        else:
            output_rom.expand(patch.last_offset_used)

    # If the user specified the patch was made for a headered ROM, add a header
    # to the ROM
    if add_header:
        output_rom.add_header()

    # Apply the patch and write out the patched ROM
    patch.apply(output_rom)
    if add_header:
        # Remove the header that was added, so that we're always dealing with
        # unheadered ROMs in the end
        output_rom.data = output_rom.data[0x200:]
        output_rom.size -= 0x200
    output_rom.to_file(patched_rom_filename)

    log.info("Patched to {} in {:.2f}s".format(patched_rom_filename, time.time() - patching_start_time))
Esempio n. 5
0
class EbpPatch(object):

    def __init__(self):

        self.patch = IpsPatch()
        self.metadata = None

    @property
    def last_offset_used(self):

        return self.patch.last_offset_used

    def load(self, filename):

        try:
            self.patch.load(filename)

            with open(filename, 'rb') as ebp:
                # Skip to the end of the IPS part of the patch
                ebp.seek(
                    5 + sum([3+2+i[1][1]+(2 if i[0] == 'RLE' else 0) for i in self.patch.instructions]) + 3
                )

                # Load the metadata, if available
                try:
                    self.metadata = json.loads(ebp.read().decode("utf-8"))
                except ValueError:
                    self.metadata = None
        except:
            raise CoilSnakeError("Not a valid EBP file: " + filename)

    def apply(self, rom):

        self.patch.apply(rom)

    def is_applied(self, rom):

        return self.patch.is_applied(rom)

    def create(self, clean_rom, modified_rom, author="", title="", description=""):

        self.patch.create(clean_rom, modified_rom)
        self.metadata = {
            "patcher": "EBPatcher",  # Used for compatibility with EBPatcher
            "author": author,
            "title": title,
            "description": description
        }
Esempio n. 6
0
def create_patch(clean_rom,
                 hacked_rom,
                 patch_path,
                 author,
                 description,
                 title,
                 progress_bar=None):
    """Starts creating the patch in its own thread."""

    creating_patch_start_time = time.time()
    # Prepare the metadata.
    metadata = json.dumps({
        "patcher": "EBPatcher",
        "author": author,
        "title": title,
        "description": description
    })

    # Try to create the patch; if it fails, display an error message.
    try:
        if patch_path.endswith(".ebp"):
            log.info("Creating EBP patch by " + author +
                     " with description \"" + description + "\" called " +
                     title + "...")
            patch = EbpPatch()
            patch.create(clean_rom, hacked_rom, patch_path, metadata)
        elif patch_path.endswith(".ips"):
            log.info("Creating IPS patch...")
            patch = IpsPatch()
            patch.create(clean_rom, hacked_rom, patch_path)
        else:
            raise CoilSnakeError("Unknown patch format.")
    except OSError as e:
        log.info("There was an error creating the patch: " + e)
        return

    # Display a success message.
    patch_name = ""
    if patch_path.rfind("/") != -1:
        patch_name = patch_path[patch_path.rfind("/") + 1:len(patch_path)]
    else:
        patch_name = patch_path[patch_path.rfind("\\") + 1:len(patch_path)]
    log.info("The patch {} was successfully created in {:.2f}s.".format(
        patch_name,
        time.time() - creating_patch_start_time))
Esempio n. 7
0
def patch_rom(clean_rom_filename, patched_rom_filename, patch_filename, headered, progress_bar=None):
    if not os.path.isfile(clean_rom_filename):
        raise RuntimeError("Clean Rom \"" + clean_rom_filename + "\" is not a file.")
    if not os.path.isfile(patch_filename):
        raise RuntimeError("Patch \"" + patch_filename + "\" is not a file.")

    if clean_rom_filename != patched_rom_filename:
        copyfile(clean_rom_filename, patched_rom_filename)

    log.info("Patching ROM {} with patch {}".format(patched_rom_filename, patch_filename))
    patching_start_time = time.time()

    if patch_filename.endswith(".ips"):
        output_rom = Rom()
        output_rom.from_file(clean_rom_filename)
        patch = IpsPatch()
    elif patch_filename.endswith(".ebp"):
        output_rom = EbRom()
        output_rom.from_file(clean_rom_filename)
        patch = EbpPatch()
    else:
        raise CoilSnakeError("Unknown patch format.")

    # Load the patch and expand the ROM as needed
    add_header = headered and not isinstance(patch, EbpPatch)
    extra = int(add_header)*0x200  # 0x200 if a header will be added, 0 otherwise
    patch.load(patch_filename)
    if isinstance(patch, EbpPatch):
        log.info("Patch: {title} by {author}".format(**patch.metadata))
    if patch.last_offset_used > len(output_rom) + extra:
        if patch.last_offset_used < 0x400000 + extra:
            output_rom.expand(0x400000)
        elif patch.last_offset_used < 0x600000 + extra:
            output_rom.expand(0x600000)
        else:
            output_rom.expand(patch.last_offset_used)

    # If the user specified the patch was made for a headered ROM, add a header
    # to the ROM
    if add_header:
        output_rom.add_header()

    # Apply the patch and write out the patched ROM
    patch.apply(output_rom)
    if add_header:
        # Remove the header that was added, so that we're always dealing with
        # unheadered ROMs in the end
        output_rom.data = output_rom.data[0x200:]
        output_rom.size -= 0x200
    output_rom.to_file(patched_rom_filename)

    log.info("Patched to {} in {:.2f}s".format(patched_rom_filename, time.time() - patching_start_time))
Esempio n. 8
0
class EbpPatch(object):

    def __init__(self):

        self.patch = IpsPatch()
        self.metadata = None

    @property
    def last_offset_used(self):

        return self.patch.last_offset_used
    
    def load(self, filename):

        try:
            self.patch.load(filename)

            with open(filename, 'rb') as ebp:
                # Skip to the end of the IPS part of the patch
                ebp.seek(
                    5 + sum([3+2+i[1][1]+(2 if i[0] == 'RLE' else 0) for i in self.patch.instructions]) + 3
                )

                # Load the metadata, if available
                try:
                    self.metadata = json.loads(ebp.read().decode("utf-8"))
                except ValueError:
                    self.metadata = None
        except:
            raise CoilSnakeError("Not a valid EBP file: " + filename)

    def apply(self, rom):

        self.patch.apply(rom)

    def is_applied(self, rom):

        return self.patch.is_applied(rom)

    def create(self, clean_rom, hacked_rom, patch_path, metadata):

        self.patch.create(clean_rom, hacked_rom, patch_path)
        
        with open(patch_path, "ab") as pfile:
            pfile.write(bytes(metadata, 'utf8'))
            pfile.close()
Esempio n. 9
0
class EbpPatch(object):
    def __init__(self):

        self.patch = IpsPatch()
        self.metadata = None

    @property
    def last_offset_used(self):

        return self.patch.last_offset_used

    def load(self, filename):

        try:
            self.patch.load(filename)

            with open(filename, 'rb') as ebp:
                # Skip to the end of the IPS part of the patch
                ebp.seek(5 + sum([
                    3 + 2 + i[1][1] + (2 if i[0] == 'RLE' else 0)
                    for i in self.patch.instructions
                ]) + 3)

                # Load the metadata, if available
                try:
                    self.metadata = json.loads(ebp.read().decode("utf-8"))
                except ValueError:
                    self.metadata = None
        except:
            raise CoilSnakeError("Not a valid EBP file: " + filename)

    def apply(self, rom):

        self.patch.apply(rom)

    def is_applied(self, rom):

        return self.patch.is_applied(rom)

    def create(self, clean_rom, hacked_rom, patch_path, metadata):

        self.patch.create(clean_rom, hacked_rom, patch_path)

        with open(patch_path, "ab") as pfile:
            pfile.write(bytes(metadata, 'utf8'))
            pfile.close()
Esempio n. 10
0
    def _clean(self):
        """If this is a clean version of one of the variants of the
        EarthBound ROM, patch it so that it becomes a clean version of
        the reference ROM.
        """

        # Truncate the ROM if it is has been expanded
        if len(self) > 0x300000:
            # Change the ExHiROM bytes in case this is an ExHiROM
            self[0x00ffd5] = 0x31
            self[0x00ffd7] = 0x0c

            # Truncate the data
            self.size = 0x300000
            self.data = self.data[:0x300000]

        # Ensure the ROM isn't too small
        elif len(self) < 0x300000:
            raise CoilSnakeError("Not a valid clean EarthBound ROM.")

        # Check if this ROM is already a reference ROM
        hash = self._calc_hash()
        if hash == self.REFERENCE_MD5:
            return

        # Try to fix the ROM with a patch if it is one of the known alternatives
        try:
            patch_filename = self.ALT_MD5[hash]
        except KeyError:
            pass  # Unknown variant
        else:
            patch = IpsPatch()
            patch.load(
                os.path.join(ASSET_PATH, "rom-fixes", "Earthbound",
                             patch_filename))
            patch.apply(self)
            self._setup_rom_post_load()
            return

        # As a last attempt, try to set the last byte to 0x0, since LunarIPS
        # likes to add 0xff at the end
        self[-1] = 0x0
        if self._calc_hash() == self.REFERENCE_MD5:
            return

        raise CoilSnakeError("Not a valid clean EarthBound ROM.")
Esempio n. 11
0
def apply_relocation_patch(rom):
    ips = IpsPatch()
    ips.load(get_ips_filename(rom.type, "swirl_relocate"), 0x200)
    ips.apply(rom)
Esempio n. 12
0
 def get_patch(self, mode, rom_type):
     ips = IpsPatch()
     ips.load(
         get_ips_filename(rom_type, '{}_{}'.format(self.patch_name_prefix,
                                                   mode)), 0)
     return ips
Esempio n. 13
0
    def __init__(self):

        self.patch = IpsPatch()
        self.metadata = None
Esempio n. 14
0
    def __init__(self):

        self.patch = IpsPatch()
        self.metadata = None
Esempio n. 15
0
def apply_relocation_patch(rom):
    ips = IpsPatch()
    ips.load(get_ips_filename(rom.type, "swirl_relocate"), 0x200)
    ips.apply(rom)
Esempio n. 16
0
def test_swirl_relocated(rom):
    ips = IpsPatch()
    ips.load(get_ips_filename(rom.type, "swirl_relocate"), 0x200)
    return ips.is_applied(rom)
Esempio n. 17
0
def test_swirl_relocated(rom):
    ips = IpsPatch()
    ips.load(get_ips_filename(rom.type, "swirl_relocate"), 0x200)
    return ips.is_applied(rom)