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())
def test_record_replay(): pyboy = PyBoy(tetris_rom, window_type="headless", bootrom_file=utils.boot_rom, record_input=True) pyboy.tick() pyboy.send_input(windowevent.PRESS_ARROW_DOWN) pyboy.tick() pyboy.send_input(windowevent.PRESS_ARROW_UP) pyboy.tick() pyboy.tick() pyboy.send_input(windowevent.PRESS_ARROW_DOWN) pyboy.tick() pyboy.send_input(windowevent.PRESS_ARROW_UP) pyboy.tick() events = pyboy._get_recorded_input() assert len( events ) == 4, "We assumed only 4 frames were recorded, as frames without events are skipped." frame_no, keys, frame_data = events[0] assert frame_no == 1, "We inserted the key on the second frame" assert keys[ 0] == windowevent.PRESS_ARROW_DOWN, "Check we have the right keypress" assert sum( base64.b64decode(frame_data) ) / 0xFF == 144 * 160 * 3, "Frame does not contain 160x144 of RGB data" pyboy.stop(save=False) test_file = 'test.replay' main.save_replay(tetris_rom, None, test_file, events) with open(test_file, 'rb') as f: m = hashlib.sha256() m.update(f.read()) digest = m.digest() os.remove(test_file) assert digest == b'\xd1\xe2\x13B\xf0$\xaa\xaa\xe2\xf2\xf3Iz\x9aj\x98\xc8^\xc4J:\x08\x1d\xf4n}\x80\x08o\x03)\xda', \ "The replay did not result in the expected output"
def replay(ROM, replay, window='headless', verify=True, record_gif=None, gif_destination=None, enable_rewind=False, overwrite=False): with open(replay, 'rb') as f: recorded_input, b64_romhash, b64_state = json.loads( zlib.decompress(f.read()).decode('ascii')) verify_rom_hash(ROM, b64_romhash) state_data = io.BytesIO(base64.b64decode( b64_state.encode('utf8'))) if b64_state is not None else None pyboy = PyBoy(ROM, window_type=window, bootrom_file=utils.boot_rom, disable_input=True, hide_window=False, enable_rewind=enable_rewind, record_input=(RESET_REPLAYS and window in ['SDL2', 'headless', 'OpenGL'])) pyboy.set_emulation_speed(0) if state_data is not None: pyboy.load_state(state_data) # Filters out the blacklisted events recorded_input = list( map( lambda event_tuple: (event_tuple[0], list(filter(lambda x: x not in event_filter, event_tuple[1])), event_tuple[2]), recorded_input)) frame_count = 0 next_event = recorded_input.pop(0) recording = False while recorded_input != []: if record_gif is not None and (frame_count in record_gif): pyboy.send_input(windowevent.SCREEN_RECORDING_TOGGLE) recording ^= True if next_event[0] == frame_count: for e in next_event[1]: pyboy.send_input(e) if verify and not overwrite: verify_screen_image_np( pyboy, base64.b64decode(next_event[2].encode('utf8'))) next_event = recorded_input.pop(0) frame_count += 1 # if frame_count % 30 == 0: # print(frame_count) # breakpoint() pyboy.tick() # If end-frame in record_gif is high than frame counter # if recording: # pyboy.send_input(windowevent.SCREEN_RECORDING_TOGGLE) # recording ^= True if gif_destination: move_gif(pyboy.get_cartridge_title(), gif_destination) if overwrite: with open(replay, 'wb') as f: f.write( zlib.compress( json.dumps((pyboy._get_recorded_input(), b64_romhash, b64_state)).encode())) pyboy.stop(save=False)