Ejemplo n.º 1
0
 def enabled(self):
     if Image is None:
         logger.warning(
             f"{__name__}: Missing dependency \"Pillow\". Screenshots disabled"
         )
         return False
     return True
Ejemplo n.º 2
0
    def setregister(self, register, value):
        if not self.latch_enabled:
            logger.info(
                "RTC: Set register, but nothing is latched! 0x%0.4x, 0x%0.2x" %
                (register, value))

        t = time.time() - self.timezero
        if register == 0x08:
            # TODO: What happens, when these value are larger than allowed?
            self.timezero -= int(t % 60) - value
        elif register == 0x09:
            self.timezero -= int(t / 60 % 60) - value
        elif register == 0x0A:
            self.timezero -= int(t / 3600 % 24) - value
        elif register == 0x0B:
            self.timezero -= int(t / 3600 / 24) - value
        elif register == 0x0C:
            day_high = value & 0b1
            halt = (value & 0b1000000) >> 6
            day_carry = (value & 0b10000000) >> 7

            self.halt = halt
            if self.halt == 0:
                pass  # TODO: Start the timer
            else:
                logger.warning("Stopping RTC is not implemented!")

            self.timezero -= int(t / 3600 / 24) - (day_high << 8)
            self.day_carry = day_carry
        else:
            logger.warning("Invalid RTC register: %0.4x %0.2x" %
                           (register, value))
Ejemplo n.º 3
0
    def setitem(self, address, value):
        if 0x0000 <= address < 0x2000:
            self.rambank_enabled = (value & 0b00001111) == 0b1010
        elif 0x2000 <= address < 0x4000:
            value &= 0b00011111
            # The register cannot contain zero (0b00000) and will be initialized as 0b00001
            # Attempting to write 0b00000 will write 0b00001 instead.
            if value == 0:
                value = 1
            self.bank_select_register1 = value
        elif 0x4000 <= address < 0x6000:
            self.bank_select_register2 = value & 0b11
        elif 0x6000 <= address < 0x8000:
            self.memorymodel = value & 0b1
        elif 0xA000 <= address < 0xC000:
            if self.rambanks is None:
                logger.warning(
                    "Game tries to set value 0x%0.2x at RAM address 0x%0.4x, but RAM "
                    "banks are not initialized. Initializing %d RAM banks as "
                    "precaution" % (value, address, self.external_ram_count))
                self.init_rambanks(self.external_ram_count)

            if self.rambank_enabled:
                self.rambank_selected = self.bank_select_register2 if self.memorymodel == 1 else 0
                self.rambanks[self.rambank_selected %
                              self.external_ram_count][address -
                                                       0xA000] = value
        else:
            logger.error("Invalid writing address: %s" % hex(address))
Ejemplo n.º 4
0
 def setitem(self, address, value):
     if 0x0000 <= address < 0x2000:
         # 8-bit register. All bits matter, so only 0b00001010 enables RAM.
         self.rambank_enabled = (value == 0b00001010)
     elif 0x2000 <= address < 0x3000:
         # 8-bit register used for the lower 8 bits of the ROM bank number.
         self.rombank_selected = (self.rombank_selected
                                  & 0b100000000) | value
     elif 0x3000 <= address < 0x4000:
         # 1-bit register used for the most significant bit of the ROM bank number.
         self.rombank_selected = (
             (value & 0x1) << 8) | (self.rombank_selected & 0xFF)
     elif 0x4000 <= address < 0x6000:
         self.rambank_selected = value & 0xF
     elif 0xA000 <= address < 0xC000:
         if self.rambanks is None:
             logger.warning(
                 "Game tries to set value 0x%0.2x at RAM address 0x%0.4x, but RAM "
                 "banks are not initialized. Initializing %d RAM banks as "
                 "precaution" % (value, address, self.external_ram_count))
             self.init_rambanks(self.external_ram_count)
         if self.rambank_enabled:
             self.rambanks[self.rambank_selected %
                           self.external_ram_count][address -
                                                    0xA000] = value
     else:
         logger.error("Unexpected write to 0x%0.4x, value: 0x%0.2x" %
                      (address, value))
Ejemplo n.º 5
0
 def writecommand(self, value):
     if value == 0x00:
         self.latch_enabled = False
     elif value == 0x01:
         if not self.latch_enabled:
             self.latch_rtc()
         self.latch_enabled = True
     else:
         logger.warning("Invalid RTC command: %0.2x" % value)
Ejemplo n.º 6
0
    def load_ram(self, f):
        if not self.rambank_initialized:
            logger.warning("Loading RAM is not supported on {}".format(self.carttype))
            return

        for bank in range(self.external_ram_count):
            for byte in range(8 * 1024):
                self.rambanks[bank][byte] = f.read()

        logger.debug("RAM loaded.")
Ejemplo n.º 7
0
    def save_ram(self, f):
        if not self.rambank_initialized:
            logger.warning("Saving RAM is not supported on {}".format(self.carttype))
            return

        for bank in range(self.external_ram_count):
            for byte in range(8 * 1024):
                f.write(self.rambanks[bank][byte])

        logger.debug("RAM saved.")
Ejemplo n.º 8
0
    def __init__(self, *args):
        super().__init__(*args)

        if not self.enabled():
            return

        if not self.pyboy_argv.get("loadstate"):
            logger.warning(
                "To replay input consistently later, it is recommended to load a state at boot. This will be"
                "embedded into the .replay file.")

        logger.info("Recording event inputs")
        self.recorded_input = []
Ejemplo n.º 9
0
    def set_emulation_speed(self, target_speed):
        """
        Set the target emulation speed. It might loose accuracy of keeping the exact speed, when using a high
        `target_speed`.

        The speed is defined as a multiple of real-time. I.e `target_speed=2` is double speed.

        A `target_speed` of `0` means unlimited. I.e. fastest possible execution.

        Args:
            target_speed (int): Target emulation speed as multiplier of real-time.
        """
        if target_speed > 5:
            logger.warning("The emulation speed might not be accurate when speed-target is higher than 5")
        self.target_emulationspeed = target_speed
Ejemplo n.º 10
0
def main():
    argv = parser.parse_args()
    if argv.no_logger:
        logger.disabled = True
    else:
        addconsolehandler()

    if argv.record_input and not argv.loadstate:
        logger.warning(
            "To replay input consistently later, it is required to load a state at boot. This will be"
            "embedded into the .replay file.")

    # Start PyBoy and run loop
    pyboy = PyBoy(
        argv.ROM,
        window_type=argv.window,
        window_scale=argv.scale,
        bootrom_file=argv.bootrom,
        autopause=argv.autopause,
        debugging=argv.debug,
        profiling=argv.profiling,
        record_input=argv.record_input is not None,
        disable_input=argv.no_input,
        enable_rewind=argv.rewind,
    )

    if argv.loadstate is not None:
        if argv.loadstate != '':
            # Use filepath given
            with open(argv.loadstate, 'rb') as f:
                pyboy.load_state(f)
        else:
            # Guess filepath from ROM path
            with open(argv.ROM + ".state", 'rb') as f:
                pyboy.load_state(f)

    while not pyboy.tick():
        pass

    pyboy.stop()

    if argv.profiling:
        print("\n".join(profiling_printer(pyboy._get_cpu_hitrate())))

    if argv.record_input:
        save_replay(argv.ROM, argv.loadstate, argv.record_input,
                    pyboy._get_recorded_input())
Ejemplo n.º 11
0
 def setitem(self, address, value):
     if 0x2000 <= address < 0x4000:
         if value == 0:
             value = 1
         self.rombank_selected = (value & 0b1)
         logger.debug("Switching bank 0x%0.4x, 0x%0.2x" % (address, value))
     elif 0xA000 <= address < 0xC000:
         if self.rambanks is None:
             from . import EXTERNAL_RAM_TABLE
             logger.warning(
                 "Game tries to set value 0x%0.2x at RAM address 0x%0.4x, but "
                 "RAM banks are not initialized. Initializing %d RAM banks as "
                 "precaution" % (value, address, EXTERNAL_RAM_TABLE[0x02]))
             self.init_rambanks(EXTERNAL_RAM_TABLE[0x02])
         self.rambanks[self.rambank_selected][address - 0xA000] = value
     else:
         logger.warning("Unexpected write to 0x%0.4x, value: 0x%0.2x" %
                        (address, value))
Ejemplo n.º 12
0
    def getregister(self, register):
        if not self.latch_enabled:
            logger.info("RTC: Get register, but nothing is latched! 0x%0.2x" % register)

        if register == 0x08:
            return self.sec_latch
        elif register == 0x09:
            return self.min_latch
        elif register == 0x0A:
            return self.hour_latch
        elif register == 0x0B:
            return self.day_latch_low
        elif register == 0x0C:
            day_high = self.day_latch_high & 0b1
            halt = self.halt << 6
            day_carry = self.day_carry << 7
            return day_high + halt + day_carry
        else:
            logger.warning("Invalid RTC register: %0.4x" % (register))
Ejemplo n.º 13
0
    def set_lives_left(self, amount):
        """
        Set the amount lives to any number between 0 and 99.

        This should only be called when the game has started.

        Args:
            amount (int): The wanted number of lives
        """
        if not self.game_has_started:
            logger.warning("Please call set_lives_left after starting the game")

        if 0 <= amount <= 99:
            tens = amount // 10
            ones = amount % 10
            self.pyboy.set_memory_value(ADDR_LIVES_LEFT, (tens << 4) | ones)
            self.pyboy.set_memory_value(ADDR_LIVES_LEFT_DISPLAY, tens)
            self.pyboy.set_memory_value(ADDR_LIVES_LEFT_DISPLAY + 1, ones)
        else:
            logger.error(f"{amount} is out of bounds. Only values between 0 and 99 allowed.")
Ejemplo n.º 14
0
 def setitem(self, address, value):
     if 0x0000 <= address < 0x2000:
         if (value & 0b00001111) == 0b1010:
             self.rambank_enabled = True
         elif value == 0:
             self.rambank_enabled = False
         else:
             # Pan Docs: "Practically any value with 0Ah in the
             # lower 4 bits enables RAM, and any other value
             # disables RAM."
             self.rambank_enabled = False
             logger.warning(
                 "Unexpected command for MBC3: Address: 0x%0.4x, Value: 0x%0.2x"
                 % (address, value))
     elif 0x2000 <= address < 0x4000:
         if value == 0:
             value = 1
         # print "ROM Bank switch:", value & 0b01111111
         self.rombank_selected = value & 0b01111111  # sets 7LSB of ROM bank address
     elif 0x4000 <= address < 0x6000:
         self.rambank_selected = value
     elif 0x6000 <= address < 0x8000:
         if self.rtc_enabled:
             self.rtc.writecommand(value)
         else:
             # NOTE: Pokemon Red/Blue will do this, but it can safely be ignored:
             # https://github.com/pret/pokered/issues/155
             logger.warning(
                 "RTC not present. Game tried to issue RTC command: 0x%0.4x, 0x%0.2x"
                 % (address, value))
     elif 0xA000 <= address < 0xC000:
         if self.rambank_selected <= 0x03:
             self.rambanks[self.rambank_selected][address - 0xA000] = value
         elif 0x08 <= self.rambank_selected <= 0x0C:
             self.rtc.setregister(self.rambank_selected, value)
         else:
             raise logger.error("Invalid RAM bank selected: 0x%0.2x" %
                                self.rambank_selected)
     else:
         raise logger.error("Invalid writing address: 0x%0.4x" % address)
Ejemplo n.º 15
0
 def setitem(self, address, value):
     if 0x0000 <= address < 0x2000:
         if (value & 0b00001111) == 0x0A:
             self.rambank_enabled = True
         else:
             self.rambank_enabled = False
     elif 0x2000 <= address < 0x4000:
         # But (when using the register below to specify the upper ROM Bank bits), the same
         # happens for Bank 20h, 40h, and 60h. Any attempt to address these ROM Banks will
         # select Bank 21h, 41h, and 61h instead.
         if value == 0:
             value = 1
         # sets 5LSB of ROM bank address
         self.rombank_selected = self.rombank_selected & 0b11100000 | value & 0b00011111
     elif 0x4000 <= address < 0x6000:
         # Note: 16Mbit = 2MB. 2MB/(8KB banks) = 128 banks. 128 is addressable with 7 bits
         if self.memorymodel == 0:  # 16/8 mode
             # sets 2MSB of ROM bank address
             self.rombank_selected = self.rombank_selected & 0b00011111 | (
                 address & 0b11) << 5
         # Note: 4Mbit = 0.5MB. 0.5MB/(8KB banks) = 32 banks. 32 is addressably with 5 bits
         elif self.memorymodel == 1:  # 4/32 mode
             self.rambank_selected = value & 0b00000011
         else:
             raise logger.error("Invalid memory model: %s" %
                                self.memorymodel)
     elif 0x6000 <= address < 0x8000:
         self.memorymodel = value & 0x1
     elif 0xA000 <= address < 0xC000:
         if self.rambanks is None:
             from . import EXTERNAL_RAM_TABLE
             logger.warning(
                 "Game tries to set value 0x%0.2x at RAM address 0x%0.4x, but RAM "
                 "banks are not initialized. Initializing %d RAM banks as "
                 "precaution" % (value, address, EXTERNAL_RAM_TABLE[0x02]))
             self.init_rambanks(EXTERNAL_RAM_TABLE[0x02])
         self.rambanks[self.rambank_selected][address - 0xA000] = value
     else:
         raise logger.error("Invalid writing address: %s" % hex(address))
Ejemplo n.º 16
0
    assert argv_load_state_file[
        0] != '-', "Load state file looks like an argument"

argv_record_input_file = None
argv_record_input = "--record-input" in sys.argv
if argv_record_input:
    assert not argv_disable_input
    idx = sys.argv.index("--record-input")
    assert len(sys.argv) > idx + 1
    argv_record_input_file = sys.argv[idx + 1]
    assert argv_record_input_file[
        0] != '-', "Output file looks like an argument"

if argv_record_input and not argv_loadstate:
    logger.warning(
        "To replay input consistently later, it is required to load a state at boot. This will be embedded"
        "into the .replay file.")


def getROM(romdir):
    """Give a list of ROMs to start"""
    found_files = list(
        filter(
            lambda f: f.lower().endswith(".gb") or f.lower().endswith(".gbc"),
            os.listdir(romdir)))
    for i, f in enumerate(found_files):
        print("%s\t%s" % (i + 1, f))
    filename = input("Write the name or number of the ROM file:\n")

    try:
        filename = romdir + found_files[int(filename) - 1]