Mercurial > touhou
changeset 188:008f90ebfdc0
Fix replay handling and add support for encrypted replays
author | Thibaut Girka <thib@sitedethib.com> |
---|---|
date | Thu, 27 Oct 2011 14:24:07 +0200 |
parents | 46793ccfedca |
children | ba3297ab3bde |
files | eclviewer.py pytouhou/formats/t6rp.py pytouhou/game/game.py pytouhou/game/games.py pytouhou/opengl/gamerunner.py |
diffstat | 5 files changed, 64 insertions(+), 28 deletions(-) [+] |
line wrap: on
line diff
--- a/eclviewer.py +++ b/eclviewer.py @@ -35,11 +35,16 @@ def main(path, stage_num, rank, characte character = replay.character if not replay.levels[stage_num-1]: raise Exception + from pytouhou.utils.random import Random + prng = Random(replay.levels[stage_num-1].random_seed) + else: + prng = None resource_loader = Loader() resource_loader.scan_archives(os.path.join(path, name) for name in ('CM.DAT', 'ST.DAT')) - game = EoSDGame(resource_loader, [PlayerState(character=character)], stage_num, rank, 16) + game = EoSDGame(resource_loader, [PlayerState(character=character)], stage_num, rank, 16, + prng=prng) # Load stage data stage = resource_loader.get_stage('stage%d.std' % stage_num)
--- a/pytouhou/formats/t6rp.py +++ b/pytouhou/formats/t6rp.py @@ -13,6 +13,9 @@ ## from struct import unpack +from io import BytesIO + +from pytouhou.utils.random import Random from pytouhou.utils.helpers import read_string from pytouhou.utils.helpers import get_logger @@ -22,43 +25,70 @@ logger = get_logger(__name__) class Level(object): def __init__(self): + self.score = 0 + self.random_seed = 0 + + self.power = 0 + self.lives = 2 + self.bombs = 3 + self.difficulty = 16 self.keys = [] class T6RP(object): def __init__(self): + self.version = 0x102 + self.character = 0 + self.rank = 0 + self.key = 0 self.levels = [] @classmethod - def read(cls, file): + def read(cls, file, decrypt=True, verify=True): if file.read(4) != b'T6RP': raise Exception - if file.read(2) != b'\x02\x01': - raise Exception replay = cls() - replay.character, replay.rank, checksum, unknown, key, unknown = unpack('<BBHIBB', file.read(10)) + replay.version, replay.character, replay.rank = unpack('<HBB', file.read(4)) + checksum, replay.unknown1, replay.unknown2, replay.key = unpack('<IBBB', file.read(7)) + + # Decrypt data + if decrypt: + decrypted_file = BytesIO() + file.seek(0) + decrypted_file.write(file.read(15)) + decrypted_file.write(b''.join(chr((ord(c) - replay.key - 7*i) & 0xff) for i, c in enumerate(file.read()))) + file = decrypted_file + file.seek(15) + + # Verify checksum + if verify: + data = file.read() + file.seek(15) + if checksum != (sum(ord(c) for c in data) + 0x3f000318 + replay.key) & 0xffffffff: + raise Exception #TODO + + replay.unknown3 = unpack('<B', file.read(1)) replay.date = read_string(file, 9, 'ascii') replay.name = read_string(file, 9, 'ascii').rstrip() - unknown, replay.score, unknown, replay.slowdown, unknown = unpack('<HIIfI', file.read(18)) + replay.unknown4, replay.score, replay.unknown5, replay.slowdown, replay.unknown6 = unpack('<HIIfI', file.read(18)) stages_offsets = unpack('<7I', file.read(28)) replay.levels = [] for offset in stages_offsets: - replay.levels.append(None) - if offset == 0: continue level = Level() - replay.levels[-1] = level + replay.levels.append(level) file.seek(offset) - level.score, level.random_seed, unknown, level.power, level.lives, level.bombs, level.difficulty, unknown = unpack('<IHHBbbBI', file.read(16)) + (level.score, level.random_seed, level.unknown1, level.power, + level.lives, level.bombs, level.difficulty, level.unknown2) = unpack('<IHHBbbBI', file.read(16)) while True: time, keys, unknown = unpack('<IHH', file.read(8)) @@ -66,5 +96,6 @@ class T6RP(object): if time == 9999999: break - level.keys.append((time, keys)) + level.keys.append((time, keys, unknown)) + return replay
--- a/pytouhou/game/game.py +++ b/pytouhou/game/game.py @@ -27,7 +27,7 @@ from pytouhou.game.effect import Particl class Game(object): def __init__(self, resource_loader, player_states, stage, rank, difficulty, - bullet_types, item_types, characters, nb_bullets_max=None): + bullet_types, item_types, characters, prng=None, nb_bullets_max=None): self.resource_loader = resource_loader self.nb_bullets_max = nb_bullets_max @@ -48,7 +48,7 @@ class Game(object): self.difficulty = difficulty self.boss = None self.spellcard = None - self.prng = Random() + self.prng = prng or Random() self.frame = 0 self.enm_anm_wrapper = resource_loader.get_anm_wrapper2(('stg%denm.anm' % stage, @@ -57,6 +57,11 @@ class Game(object): ecl = resource_loader.get_ecl('ecldata%d.ecl' % stage) self.ecl_runner = ECLMainRunner(ecl, self) + #TODO: The game calls it two times. What for? + # See 102h.exe@0x413220 if you think you're brave enough. + self.prng.rand_uint16() + self.prng.rand_uint16() + def drop_bonus(self, x, y, _type, end_pos=None): player = self.players[0] #TODO
--- a/pytouhou/game/games.py +++ b/pytouhou/game/games.py @@ -18,7 +18,7 @@ from pytouhou.game.bullettype import Bul from pytouhou.game.itemtype import ItemType class EoSDGame(Game): - def __init__(self, resource_loader, players, stage, rank, difficulty): + def __init__(self, resource_loader, players, stage, rank, difficulty, **kwargs): etama3 = resource_loader.get_anm_wrapper(('etama3.anm',)) etama4 = resource_loader.get_anm_wrapper(('etama4.anm',)) bullet_types = [BulletType(etama3, 0, 11, 14, 15, 16, hitbox_size=4), @@ -48,5 +48,5 @@ class EoSDGame(Game): Character(player01, 5., 2.5, 2.5, 42.)] Game.__init__(self, resource_loader, players, stage, rank, difficulty, - bullet_types, item_types, characters, nb_bullets_max=640) + bullet_types, item_types, characters, nb_bullets_max=640, **kwargs)
--- a/pytouhou/opengl/gamerunner.py +++ b/pytouhou/opengl/gamerunner.py @@ -30,7 +30,6 @@ class GameRunner(pyglet.window.Window, G self.push_handlers(self.keys) else: self.keys = 0 - self.instruction_pointer = 0 self.replay_level = replay.levels[game.stage-1] self.fps_display = pyglet.clock.ClockDisplay() @@ -113,20 +112,16 @@ class GameRunner(pyglet.window.Window, G keystate |= 128 if self.keys[pyglet.window.key.LCTRL]: keystate |= 256 - self.game.run_iter(keystate) #TODO: self.keys... + self.game.run_iter(keystate) else: - frame, keys = self.replay_level.keys[self.instruction_pointer] + keystate = 0 + for frame, _keystate, unknown in self.replay_level.keys: + if self.game.frame < frame: + break + else: + keystate = _keystate - if frame > self.game.frame: - self.game.run_iter(self.keys) - return - - self.instruction_pointer += 1 - - if frame == self.game.frame: - self.keys = keys - - self.game.run_iter(self.keys) + self.game.run_iter(keystate) def on_draw(self):