Beispiel #1
0
    def get_rom_template_data(self, rom_path):

        # open rom file
        romfp = open(rom_path, "rb")

        # get card specific data from template.txt
        serial = gamecard.ncsd_serial(romfp)
        sha1 = gamecard.ncch_sha1sum(romfp)
        template_data = titles.get_template(serial, sha1)
        if not template_data:
            return None
        else:
            return template_data
Beispiel #2
0
    def write_rom(self,
                  rom,
                  silent=False,
                  progress=None,
                  use_header_bin=False,
                  verbose=False):
        """Write rom to sdcard.

        Roms are stored at the position marked in the position headers (starting
        at 0x2000000).

        This code first looks for a free block with enough space to hold the
        specified rom, then continues to write the data to that location.
        After successful writing the savegame slot for this game is filled with
        zero.
        The last thing to do is to find the game in template.txt and write the
        data from that file to offset 0x1400 inside the rom on sdcard.

        Keyword Arguments:
        rom -- path to rom file"""

        self.fail_on_non_sky3ds()

        # follow symlink
        rom = os.path.realpath(rom)

        # get rom size and calculate block count
        rom_size = os.path.getsize(rom)
        rom_blocks = int(rom_size / 0x200)

        # get free blocks on sd-card and search for a block big enough for the rom
        start_block = 0
        for free_block in self.free_blocks[::-1]:
            if free_block[1] >= rom_blocks:
                start_block = free_block[0]
                break

        if start_block == 0:
            raise Exception("Not enough free continous blocks")

        self.diskfp.seek(0)
        position_header_length = 0x100

        # find free slot for game (card format is limited to 31 games)
        free_slot = -1
        for i in range(0, int(position_header_length / 0x8) - 1):
            position = struct.unpack("ii", self.diskfp.read(0x8))
            if position == (-1, -1):
                free_slot = i
                break

        if free_slot == -1:
            raise Exception(
                "No free slot found. There can be a maximum of %d games on one card."
                % int(position_header_length / 0x8))

        # seek to start of rom on sd-card
        self.diskfp.seek(start_block * 0x200)

        # open rom file
        romfp = open(rom, "rb")

        # get card specific data from template.txt
        serial = gamecard.ncsd_serial(romfp)
        sha1 = gamecard.ncch_sha1sum(romfp)

        template_data = titles.get_template(serial, sha1)
        if template_data:
            generated_template = False
            card_data = bytearray.fromhex(template_data['card_data'])

        else:
            generated_template = True
            logging.warning(
                "Automagically creating sky3ds header (this will fail, lol)")
            card_data = bytearray()

            # card crypto + card id + eeprom id(?)
            romfp.seek(0x1244)
            card_data += romfp.read(0x4)
            romfp.seek(0x1240)
            card_data += romfp.read(0x4)
            romfp.seek(0x1248)
            card_data += romfp.read(0x4)

            # CRC16 of NCCH header?
            romfp.seek(0x1000)
            crc16 = titles.crc16(bytearray(romfp.read(0x200)))
            card_data += bytearray(struct.pack("H", crc16)[::-1])
            card_data += bytearray(
                struct.pack("H",
                            (crc16 << 16 | crc16 ^ 0xffff) & 0xFFFF)[::-1])

            # CTRIMAGE + zero-padding
            card_data += bytearray("CTRIMAGE", "ascii")
            card_data += bytearray([0x00] * 0x8)

            # ?!?!?!?
            card_data += bytearray([0x00] * 0x10)

            # zero-padding
            card_data += bytearray([0x00] * 0x10)

            # unique id
            romfp.seek(0x1200)
            card_data += romfp.read(0x40)

            # name
            romfp.seek(0x1150)
            card_data += romfp.read(0x10)
            card_data += bytearray([0x00] * 0xf0)

            # zero-padding
            card_data += bytearray([0x00] * 0x80)

        if use_header_bin:
            header_bin = os.path.join(data_dir, 'header.bin')
            if os.path.exists(header_bin):
                logging.info(
                    "Injecting headers from header.bin instead of template.txt!"
                )
                try:
                    header_bin_fp = open(header_bin, "rb")
                    rom_header = bytearray(header_bin_fp.read(0x44))
                    header_bin_fp.close()
                    for byte in range(0x40):
                        card_data[0x40 + byte] = rom_header[byte]
                except:
                    raise Exception(
                        "Error: Can't inject headers from header.bin")

        elif rom[-4:] == ".3dz":
            romfp.seek(0x1200)
            rom_header = romfp.read(0x44)
            if rom_header[0x00:0x10] != bytearray([0xff] * 0x10):
                logging.info(
                    "Injecting headers from 3dz file instead of template.txt!")
                for byte in range(0x40):
                    card_data[0x40 + byte] = rom_header[byte]
                for byte in range(0x4):
                    card_data[0x4 + byte] = rom_header[0x40 + byte]

        # recalculate checksum for sky3ds header (important after injection from 3dz or header.bin)
        crc16 = titles.crc16(card_data[:-2])
        card_data[-2] = (crc16 & 0xFF00) >> 8
        card_data[-1] = (crc16 & 0x00FF)

        if len(card_data) != 0x200:
            raise Exception("Invalid template data")

        if verbose:
            template = "\nUsed template:\n"
            template += "** : %s\n" % card_data[0x80:0x90].decode("ascii")
            template += "SHA1: %s\n" % gamecard.ncch_sha1sum(romfp).upper()
            for i in range(0, 0x20):
                line = ""
                for j in range(0, 0x10):
                    line += ("%.2x " % card_data[i * 0x10 + j]).upper()
                template += line + "\n"
            template += "\n"
            logging.info(template)

        # write rom (with fancy progressbar!)
        romfp.seek(0)
        try:
            if not silent and not progress:
                progress = ProgressBar(
                    widgets=[Percentage(),
                             Bar(), FileTransferSpeed()],
                    maxval=rom_size).start()
        except:
            pass
        written = 0
        while written < rom_size:
            chunk = romfp.read(1024 * 1024 * 8)

            self.diskfp.write(chunk)
            os.fsync(self.diskfp)

            written = written + len(chunk)
            try:
                if not silent:
                    progress.update(written)
            except:
                pass
        try:
            if not silent:
                progress.finish()
        except:
            pass

        # seek to slot header and write position + block-count of rom
        self.diskfp.seek(free_slot * 0x8)
        self.diskfp.write(struct.pack("ii", start_block, rom_blocks))

        # add savegame slot
        self.diskfp.seek(0x100000 * (1 + len(self.rom_list)))
        self.diskfp.write(bytearray([0xff] * 0x100000))

        self.diskfp.seek(start_block * 0x200 + 0x1400)
        self.diskfp.write(card_data)

        # cleanup
        romfp.close()
        os.fsync(self.diskfp)

        self.update_rom_list()
Beispiel #3
0
    def write_rom(self, rom, silent=False, progress=None, use_header_bin=False, verbose=False):
        """Write rom to sdcard.

        Roms are stored at the position marked in the position headers (starting
        at 0x2000000).

        This code first looks for a free block with enough space to hold the
        specified rom, then continues to write the data to that location.
        After successful writing the savegame slot for this game is filled with
        zero.
        The last thing to do is to find the game in template.txt and write the
        data from that file to offset 0x1400 inside the rom on sdcard.

        Keyword Arguments:
        rom -- path to rom file"""

        self.fail_on_non_sky3ds()

        # follow symlink
        rom = os.path.realpath(rom)

        # get rom size and calculate block count
        rom_size = os.path.getsize(rom)
        rom_blocks = int(rom_size / 0x200)

        # get free blocks on sd-card and search for a block big enough for the rom
        start_block = 0
        for free_block in self.free_blocks[::-1]:
            if free_block[1] >= rom_blocks:
                start_block = free_block[0]
                break

        if start_block == 0:
            raise Exception("Not enough free continous blocks")

        self.diskfp.seek(0)
        position_header_length = 0x100

        # find free slot for game (card format is limited to 31 games)
        free_slot = -1
        for i in range(0, int(position_header_length / 0x8) - 1):
            position = struct.unpack("ii", self.diskfp.read(0x8))
            if position == (-1, -1):
                free_slot = i
                break

        if free_slot == -1:
            raise Exception("No free slot found. There can be a maximum of %d games on one card." % int(position_header_length / 0x8))

        # seek to start of rom on sd-card
        self.diskfp.seek(start_block * 0x200)

        # open rom file
        romfp = open(rom, "rb")

        # get card specific data from template.txt
        serial = gamecard.ncsd_serial(romfp)
        sha1 = gamecard.ncch_sha1sum(romfp)

        template_data = titles.get_template(serial, sha1)
        if template_data:
            generated_template = False
            card_data = bytearray.fromhex(template_data['card_data'])

        else:
            generated_template = True
            logging.warning("Automagically creating sky3ds header (this will fail, lol)")
            card_data = bytearray()

            # card crypto + card id + eeprom id(?)
            romfp.seek(0x1244)
            card_data += romfp.read(0x4)
            romfp.seek(0x1240)
            card_data += romfp.read(0x4)
            romfp.seek(0x1248)
            card_data += romfp.read(0x4)

            # CRC16 of NCCH header?
            romfp.seek(0x1000)
            crc16 = titles.crc16(bytearray(romfp.read(0x200)))
            card_data += bytearray(struct.pack("H", crc16)[::-1])
            card_data += bytearray(struct.pack("H", (crc16 << 16 | crc16 ^ 0xffff) & 0xFFFF)[::-1])

            # CTRIMAGE + zero-padding
            card_data += bytearray("CTRIMAGE", "ascii")
            card_data += bytearray([0x00]*0x8)

            # ?!?!?!?
            card_data += bytearray([0x00] * 0x10)

            # zero-padding
            card_data += bytearray([0x00] * 0x10)

            # unique id
            romfp.seek(0x1200)
            card_data += romfp.read(0x40)

            # name
            romfp.seek(0x1150)
            card_data += romfp.read(0x10)
            card_data += bytearray([0x00] * 0xf0)

            # zero-padding
            card_data += bytearray([0x00] * 0x80)

        if use_header_bin:
            header_bin = os.path.join(data_dir,'header.bin')
            if os.path.exists(header_bin):
                logging.info("Injecting headers from header.bin instead of template.txt!")
                try:
                    header_bin_fp = open(header_bin, "rb")
                    rom_header = bytearray(header_bin_fp.read(0x44))
                    header_bin_fp.close()
                    for byte in range(0x40):
                        card_data[0x40+byte] = rom_header[byte]
                except:
                    raise Exception("Error: Can't inject headers from header.bin")

        elif rom[-4:] == ".3dz":
            romfp.seek(0x1200)
            rom_header = romfp.read(0x44)
            if rom_header[0x00:0x10] != bytearray([0xff]*0x10):
                logging.info("Injecting headers from 3dz file instead of template.txt!")
                for byte in range(0x40):
                    card_data[0x40+byte] = rom_header[byte]
                for byte in range(0x4):
                    card_data[0x4+byte] = rom_header[0x40+byte]

        # recalculate checksum for sky3ds header (important after injection from 3dz or header.bin)
        crc16 = titles.crc16(card_data[:-2])
        card_data[-2] = (crc16 & 0xFF00) >> 8
        card_data[-1] = (crc16 & 0x00FF)

        if len(card_data) != 0x200:
            raise Exception("Invalid template data")

        if verbose:
            template  = "\nUsed template:\n"
            template += "** : %s\n" % card_data[0x80:0x90].decode("ascii")
            template += "SHA1: %s\n" % gamecard.ncch_sha1sum(romfp).upper()
            for i in range(0, 0x20):
                line = ""
                for j in range(0, 0x10):
                    line += ("%.2x " % card_data[i*0x10+j]).upper()
                template += line + "\n"
            template += "\n"
            logging.info(template)

        # write rom (with fancy progressbar!)
        romfp.seek(0)
        try:
            if not silent and not progress:
                progress = ProgressBar(widgets=[Percentage(), Bar(), FileTransferSpeed()], maxval=rom_size).start()
        except:
            pass
        written = 0
        while written < rom_size:
            chunk = romfp.read(1024*1024*8)

            self.diskfp.write(chunk)
            os.fsync(self.diskfp)

            written = written + len(chunk)
            try:
                if not silent:
                   progress.update(written)
            except:
                pass
        try:
            if not silent:
                progress.finish()
        except:
            pass

        # seek to slot header and write position + block-count of rom
        self.diskfp.seek(free_slot * 0x8)
        self.diskfp.write(struct.pack("ii", start_block, rom_blocks))

        # add savegame slot
        self.diskfp.seek(0x100000 * (1 + len(self.rom_list)))
        self.diskfp.write(bytearray([0xff]*0x100000))

        self.diskfp.seek(start_block * 0x200 + 0x1400)
        self.diskfp.write(card_data)

        # cleanup
        romfp.close()
        os.fsync(self.diskfp)

        self.update_rom_list()