annotate formats/src/th06/t6rp.rs @ 794:8c2ef2d503c9 default tip

formats: Implement T6RP in Rust This has been tested against demo00.rpy shipped in CM.DAT.
author Link Mauve <linkmauve@linkmauve.fr>
date Tue, 02 Jun 2026 16:39:21 +0200
parents
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
794
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
1 //! Touhou 6 Replay (T6RP) format support.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
2 //!
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
3 //! This module provides classes for handling the Touhou 6 Replay file format.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
4 //! The T6RP file format is an encrypted format describing different aspects of
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
5 //! a game of EoSD. Since the EoSD engine is entirely deterministic, a small
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
6 //! replay file is sufficient to unfold a full game.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
7
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
8 use std::convert::TryInto;
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
9
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
10 use nom::{
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
11 bytes::complete::tag,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
12 multi::count,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
13 number::complete::{le_f32, le_i8, le_u16, le_u32, le_u8},
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
14 IResult, Parser,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
15 };
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
16
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
17 /// A single level of a replay.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
18 #[derive(Debug, Clone, Default)]
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
19 pub struct Level {
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
20 /// The score before starting the level.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
21 pub score: u32,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
22
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
23 /// The seed initializing the PRNG.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
24 pub random_seed: u16,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
25
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
26 /// The amount of point items collected before starting this level.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
27 pub point_items: u16,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
28
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
29 /// The power level at the beginning of the level.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
30 pub power: u8,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
31
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
32 /// How many lives are remaining before starting this level.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
33 pub lives: i8,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
34
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
35 /// How many bombs are remaining before starting this level.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
36 pub bombs: i8,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
37
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
38 /// The hidden difficulty at the beginning of this level.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
39 pub difficulty: u8,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
40
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
41 unknown: u32,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
42
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
43 /// The list of keys pressed during this level.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
44 pub keys: Vec<(u32, u16, u16)>,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
45 }
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
46
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
47 /// The unencrypted part of a replay.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
48 #[derive(Debug, Clone)]
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
49 pub struct Header {
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
50 /// The version, only 1.02 is supported so far.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
51 pub version: u16,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
52
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
53 /// The character picked by the player, 0 = Reimu A, 3 = Marisa B.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
54 pub character: u8,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
55
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
56 /// The rank picked by the player, 0 = Easy, 4 = Extra.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
57 pub rank: u8,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
58
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
59 /// The checksum of the encrypted part of this replay.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
60 pub checksum: u32,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
61
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
62 unknown1: u8,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
63
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
64 unknown2: u8,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
65
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
66 /// The encryption key for the rest of this replay.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
67 pub key: u8,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
68 }
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
69
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
70 /// The encrypted part of a replay.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
71 #[derive(Debug, Clone)]
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
72 pub struct T6rp {
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
73 unknown3: u8,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
74
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
75 /// The date at which this replay got recorded.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
76 pub date: [u8; 9],
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
77
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
78 /// The name of the player.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
79 pub name: [u8; 9],
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
80
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
81 unknown4: u16,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
82
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
83 /// The score at the end of this replay.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
84 pub score: u32,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
85
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
86 unknown5: u32,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
87
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
88 /// How much slowdown has been experienced while recording this replay.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
89 pub slowdown: f32,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
90
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
91 unknown6: u32,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
92
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
93 /// Each of the stages that got recorded, only 0..=6 or 7 can be set.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
94 pub levels: [Option<Level>; 7],
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
95 }
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
96
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
97 // TODO: Add a newtype for `encrypted`, to provide the decrypt() method on it and then another
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
98 // newtype for the decrypted data to have verify() and parse().
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
99 impl T6rp {
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
100 /// Parse a slice of bytes into a `Header` struct, which is the unencrypted part of T6RP.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
101 pub fn parse_header(input: &[u8]) -> IResult<&[u8], Header> {
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
102 let (encrypted, (_, version, character, rank, checksum, unknown1, unknown2, key)) = (
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
103 tag(&b"T6RP"[..]),
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
104 le_u16,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
105 le_u8,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
106 le_u8,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
107 le_u32,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
108 le_u8,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
109 le_u8,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
110 le_u8,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
111 )
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
112 .parse(input)?;
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
113 Ok((
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
114 encrypted,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
115 Header {
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
116 version,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
117 character,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
118 rank,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
119 checksum,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
120 unknown1,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
121 unknown2,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
122 key,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
123 },
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
124 ))
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
125 }
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
126
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
127 /// Decrypt the contents of the replay with the header’s key.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
128 pub fn decrypt(key: u8, encrypted: &[u8]) -> Vec<u8> {
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
129 let mut decrypted = Vec::with_capacity(encrypted.len());
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
130 for (i, c) in encrypted.iter().enumerate() {
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
131 decrypted.push(c.wrapping_sub(key).wrapping_sub((7 * i) as u8));
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
132 }
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
133 decrypted
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
134 }
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
135
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
136 /// Verify decrypted data against the header’s checksum.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
137 pub fn verify(key: u8, checksum: u32, data: &[u8]) -> bool {
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
138 let real_sum = data
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
139 .iter()
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
140 .map(|c| *c as u32)
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
141 .sum::<u32>()
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
142 .wrapping_add(0x3f000318)
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
143 .wrapping_add(key as u32);
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
144 checksum == real_sum
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
145 }
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
146
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
147 /// Parse a slice of bytes into a `T6rp` struct.
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
148 pub fn from_slice(input: &[u8]) -> IResult<&[u8], T6rp> {
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
149 let (
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
150 _,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
151 (unknown3, date, name, unknown4, score, unknown5, slowdown, unknown6, stage_offsets),
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
152 ) = (
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
153 le_u8,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
154 count(le_u8, 9),
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
155 count(le_u8, 9),
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
156 le_u16,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
157 le_u32,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
158 le_u32,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
159 le_f32,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
160 le_u32,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
161 count(le_u32, 7),
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
162 )
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
163 .parse(input)?;
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
164 let date = date.try_into().unwrap();
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
165 let name = name.try_into().unwrap();
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
166
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
167 let mut levels = [const { None }; 7];
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
168 for (index, offset) in stage_offsets.into_iter().enumerate() {
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
169 if offset == 0 {
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
170 continue;
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
171 }
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
172
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
173 let data = &input[offset as usize - 15..];
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
174 let (
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
175 mut data,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
176 (score, random_seed, point_items, power, lives, bombs, difficulty, unknown),
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
177 ) = (le_u32, le_u16, le_u16, le_u8, le_i8, le_i8, le_u8, le_u32).parse(data)?;
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
178
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
179 let mut keys = Vec::new();
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
180 loop {
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
181 let (data2, (time, keystate, unknown)) = (le_u32, le_u16, le_u16).parse(data)?;
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
182 data = data2;
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
183
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
184 if time == 9999999 {
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
185 break;
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
186 }
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
187 keys.push((time, keystate, unknown));
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
188 }
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
189
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
190 levels[index] = Some(Level {
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
191 score,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
192 random_seed,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
193 point_items,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
194 power,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
195 lives,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
196 bombs,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
197 difficulty,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
198 unknown,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
199 keys,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
200 });
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
201 }
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
202
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
203 Ok((
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
204 b"",
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
205 T6rp {
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
206 unknown3,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
207 date,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
208 name,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
209 unknown4,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
210 score,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
211 unknown5,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
212 slowdown,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
213 unknown6,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
214 levels,
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
215 },
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
216 ))
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
217 }
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
218 }
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
219
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
220 #[cfg(test)]
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
221 mod tests {
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
222 use super::*;
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
223 use std::fs::File;
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
224 use std::io::{self, Read};
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
225
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
226 #[test]
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
227 fn t6rp() {
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
228 let file = File::open("EoSD/CM.bak/demo00.rpy").unwrap();
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
229 let mut file = io::BufReader::new(file);
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
230 let mut buf = Vec::new();
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
231 file.read_to_end(&mut buf).unwrap();
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
232
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
233 let (encrypted, header) = T6rp::parse_header(&buf).unwrap();
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
234 assert_eq!(header.version, 0x0102);
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
235 assert_eq!(header.character, 0);
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
236 assert_eq!(header.rank, 3);
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
237 assert_eq!(header.checksum, 0x3f03188d);
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
238 assert_eq!(header.key, 0x9d);
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
239
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
240 let data = T6rp::decrypt(header.key, &encrypted);
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
241
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
242 assert!(T6rp::verify(header.key, header.checksum, &data));
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
243
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
244 let (_, t6rp) = T6rp::from_slice(&data).unwrap();
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
245 assert_eq!(t6rp.date, *b"08/21/02\0");
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
246 assert_eq!(t6rp.name, *b"A \0");
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
247 assert_eq!(t6rp.score, 7837690);
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
248 assert_eq!(t6rp.slowdown, 0.62111616);
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
249
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
250 let level4 = t6rp.levels[4 - 1].as_ref().unwrap();
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
251 assert_eq!(level4.score, 7837690);
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
252 assert_eq!(level4.random_seed, 0xbf81);
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
253 assert_eq!(level4.point_items, 0);
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
254 assert_eq!(level4.power, 128);
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
255 assert_eq!(level4.lives, 2);
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
256 assert_eq!(level4.bombs, 3);
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
257 assert_eq!(level4.difficulty, 16);
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
258 assert_eq!(level4.keys.len(), 322);
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
259 }
8c2ef2d503c9 formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff changeset
260 }