Mercurial > touhou
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 |
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 | 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 |