annotate pytouhou/formats/t6rp.py @ 316:f0be7ea62330

Fix a bug with ECL instruction 96, and fix overall ECL handling. The issue with instruction 96 was about death callbacks, being executed on the caller of instruction 96 instead of the dying enemies. This was introduced by changeset 5930b33a0370. Additionnaly, ECL processes are now an attribute of the Enemy, and death/timeout conditions are checked right after the ECL frame, even if the ECL script has already ended, just like in the original game.
author Thibaut Girka <thib@sitedethib.com>
date Thu, 29 Mar 2012 21:18:35 +0200
parents 5492472963b0
children 56523a16db1d
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
187
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
1 # -*- encoding: utf-8 -*-
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
2 ##
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
3 ## Copyright (C) 2011 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
4 ##
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
5 ## This program is free software; you can redistribute it and/or modify
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
6 ## it under the terms of the GNU General Public License as published
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
7 ## by the Free Software Foundation; version 3 only.
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
8 ##
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
9 ## This program is distributed in the hope that it will be useful,
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
10 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
11 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
12 ## GNU General Public License for more details.
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
13 ##
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
14
204
88361534c77e Add some documentation (argh, so much left to document!)
Thibaut Girka <thib@sitedethib.com>
parents: 193
diff changeset
15 """Touhou 6 Replay (T6RP) files handling.
88361534c77e Add some documentation (argh, so much left to document!)
Thibaut Girka <thib@sitedethib.com>
parents: 193
diff changeset
16
88361534c77e Add some documentation (argh, so much left to document!)
Thibaut Girka <thib@sitedethib.com>
parents: 193
diff changeset
17 This module provides classes for handling the Touhou 6 Replay file format.
88361534c77e Add some documentation (argh, so much left to document!)
Thibaut Girka <thib@sitedethib.com>
parents: 193
diff changeset
18 The T6RP file format is an encrypted format describing different aspects of
88361534c77e Add some documentation (argh, so much left to document!)
Thibaut Girka <thib@sitedethib.com>
parents: 193
diff changeset
19 a game of EoSD. Since the EoSD engine is entirely deterministic, a small
88361534c77e Add some documentation (argh, so much left to document!)
Thibaut Girka <thib@sitedethib.com>
parents: 193
diff changeset
20 replay file is sufficient to unfold a full game.
88361534c77e Add some documentation (argh, so much left to document!)
Thibaut Girka <thib@sitedethib.com>
parents: 193
diff changeset
21 """
88361534c77e Add some documentation (argh, so much left to document!)
Thibaut Girka <thib@sitedethib.com>
parents: 193
diff changeset
22
187
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
23 from struct import unpack
188
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
24 from io import BytesIO
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
25
305
5492472963b0 Minor cleanup
Thibaut Girka <thib@sitedethib.com>
parents: 204
diff changeset
26 from pytouhou.utils.helpers import read_string, get_logger
187
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
27
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
28 logger = get_logger(__name__)
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
29
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
30
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
31 class Level(object):
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
32 def __init__(self):
188
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
33 self.score = 0
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
34 self.random_seed = 0
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
35
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
36 self.power = 0
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
37 self.lives = 2
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
38 self.bombs = 3
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
39 self.difficulty = 16
187
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
40 self.keys = []
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
41
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
42
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
43 class T6RP(object):
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
44 def __init__(self):
188
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
45 self.version = 0x102
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
46 self.character = 0
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
47 self.rank = 0
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
48 self.key = 0
189
ba3297ab3bde Fix handling for replay files not beginning with stage 1
Thibaut Girka <thib@sitedethib.com>
parents: 188
diff changeset
49 self.levels = [None] * 7
187
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
50
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
51
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
52 @classmethod
188
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
53 def read(cls, file, decrypt=True, verify=True):
204
88361534c77e Add some documentation (argh, so much left to document!)
Thibaut Girka <thib@sitedethib.com>
parents: 193
diff changeset
54 """Read a T6RP file.
88361534c77e Add some documentation (argh, so much left to document!)
Thibaut Girka <thib@sitedethib.com>
parents: 193
diff changeset
55
88361534c77e Add some documentation (argh, so much left to document!)
Thibaut Girka <thib@sitedethib.com>
parents: 193
diff changeset
56 Raise an exception if the file is invalid.
88361534c77e Add some documentation (argh, so much left to document!)
Thibaut Girka <thib@sitedethib.com>
parents: 193
diff changeset
57 Return a T6RP instance otherwise.
88361534c77e Add some documentation (argh, so much left to document!)
Thibaut Girka <thib@sitedethib.com>
parents: 193
diff changeset
58
88361534c77e Add some documentation (argh, so much left to document!)
Thibaut Girka <thib@sitedethib.com>
parents: 193
diff changeset
59 Keyword arguments:
88361534c77e Add some documentation (argh, so much left to document!)
Thibaut Girka <thib@sitedethib.com>
parents: 193
diff changeset
60 decrypt -- whether or not to decrypt the file (default True)
88361534c77e Add some documentation (argh, so much left to document!)
Thibaut Girka <thib@sitedethib.com>
parents: 193
diff changeset
61 verify -- whether or not to verify the file's checksum (default True)
88361534c77e Add some documentation (argh, so much left to document!)
Thibaut Girka <thib@sitedethib.com>
parents: 193
diff changeset
62 """
88361534c77e Add some documentation (argh, so much left to document!)
Thibaut Girka <thib@sitedethib.com>
parents: 193
diff changeset
63
187
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
64 if file.read(4) != b'T6RP':
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
65 raise Exception
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
66
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
67 replay = cls()
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
68
188
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
69 replay.version, replay.character, replay.rank = unpack('<HBB', file.read(4))
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
70 checksum, replay.unknown1, replay.unknown2, replay.key = unpack('<IBBB', file.read(7))
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
71
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
72 # Decrypt data
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
73 if decrypt:
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
74 decrypted_file = BytesIO()
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
75 file.seek(0)
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
76 decrypted_file.write(file.read(15))
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
77 decrypted_file.write(b''.join(chr((ord(c) - replay.key - 7*i) & 0xff) for i, c in enumerate(file.read())))
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
78 file = decrypted_file
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
79 file.seek(15)
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
80
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
81 # Verify checksum
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
82 if verify:
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
83 data = file.read()
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
84 file.seek(15)
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
85 if checksum != (sum(ord(c) for c in data) + 0x3f000318 + replay.key) & 0xffffffff:
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
86 raise Exception #TODO
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
87
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
88 replay.unknown3 = unpack('<B', file.read(1))
193
9f58e2a6e950 Fix particles, fix "random" item popping, change update order to match the original game's more closely.
Thibaut Girka <thib@sitedethib.com>
parents: 189
diff changeset
89 replay.date = file.read(9) #read_string(file, 9, 'ascii')
9f58e2a6e950 Fix particles, fix "random" item popping, change update order to match the original game's more closely.
Thibaut Girka <thib@sitedethib.com>
parents: 189
diff changeset
90 replay.name = file.read(9) #read_string(file, 9, 'ascii').rstrip()
188
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
91 replay.unknown4, replay.score, replay.unknown5, replay.slowdown, replay.unknown6 = unpack('<HIIfI', file.read(18))
187
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
92
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
93 stages_offsets = unpack('<7I', file.read(28))
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
94
189
ba3297ab3bde Fix handling for replay files not beginning with stage 1
Thibaut Girka <thib@sitedethib.com>
parents: 188
diff changeset
95 for i, offset in enumerate(stages_offsets):
187
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
96 if offset == 0:
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
97 continue
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
98
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
99 level = Level()
189
ba3297ab3bde Fix handling for replay files not beginning with stage 1
Thibaut Girka <thib@sitedethib.com>
parents: 188
diff changeset
100 replay.levels[i] = level
187
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
101
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
102 file.seek(offset)
188
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
103 (level.score, level.random_seed, level.unknown1, level.power,
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
104 level.lives, level.bombs, level.difficulty, level.unknown2) = unpack('<IHHBbbBI', file.read(16))
187
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
105
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
106 while True:
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
107 time, keys, unknown = unpack('<IHH', file.read(8))
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
108
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
109 if time == 9999999:
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
110 break
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
111
188
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
112 level.keys.append((time, keys, unknown))
187
46793ccfedca Implement replays.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
diff changeset
113
188
008f90ebfdc0 Fix replay handling and add support for encrypted replays
Thibaut Girka <thib@sitedethib.com>
parents: 187
diff changeset
114 return replay