Mercurial > touhou
annotate formats/src/th06/t6rp.rs @ 795:2d60a14f4816 default tip
python: Rewrite the main entrypoint in Rust
This lets us progressively replace Python modules with Rust ones.
Currently missing features include:
- Saving replays
- Networking code for cooperative mode
- Reading a configuration file for options
- Maybe more.
But the base game is working, so yay!
| author | Link Mauve <linkmauve@linkmauve.fr> |
|---|---|
| date | Tue, 02 Jun 2026 19:06:16 +0200 |
| parents | 8c2ef2d503c9 |
| children |
| 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 |
|
795
2d60a14f4816
python: Rewrite the main entrypoint in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
794
diff
changeset
|
41 /// XXX |
|
2d60a14f4816
python: Rewrite the main entrypoint in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
794
diff
changeset
|
42 // TODO: Make non-pub. |
|
2d60a14f4816
python: Rewrite the main entrypoint in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
794
diff
changeset
|
43 pub unknown: u32, |
|
794
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
44 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
45 /// The list of keys pressed during this level. |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
46 pub keys: Vec<(u32, u16, u16)>, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
47 } |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
48 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
49 /// The unencrypted part of a replay. |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
50 #[derive(Debug, Clone)] |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
51 pub struct Header { |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
52 /// The version, only 1.02 is supported so far. |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
53 pub version: u16, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
54 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
55 /// 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
|
56 pub character: u8, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
57 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
58 /// The rank picked by the player, 0 = Easy, 4 = Extra. |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
59 pub rank: u8, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
60 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
61 /// The checksum of the encrypted part of this replay. |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
62 pub checksum: u32, |
|
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 unknown1: 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 unknown2: u8, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
67 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
68 /// The encryption key for the rest of this replay. |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
69 pub key: u8, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
70 } |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
71 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
72 /// The encrypted part of a replay. |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
73 #[derive(Debug, Clone)] |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
74 pub struct T6rp { |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
75 unknown3: u8, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
76 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
77 /// The date at which this replay got recorded. |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
78 pub date: [u8; 9], |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
79 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
80 /// The name of the player. |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
81 pub name: [u8; 9], |
|
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 unknown4: u16, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
84 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
85 /// The score at the end of this replay. |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
86 pub score: 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 unknown5: u32, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
89 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
90 /// How much slowdown has been experienced while recording this replay. |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
91 pub slowdown: f32, |
|
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 unknown6: u32, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
94 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
95 /// 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
|
96 pub levels: [Option<Level>; 7], |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
97 } |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
98 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
99 // 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
|
100 // newtype for the decrypted data to have verify() and parse(). |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
101 impl T6rp { |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
102 /// 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
|
103 pub fn parse_header(input: &[u8]) -> IResult<&[u8], Header> { |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
104 let (encrypted, (_, version, character, rank, checksum, unknown1, unknown2, key)) = ( |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
105 tag(&b"T6RP"[..]), |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
106 le_u16, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
107 le_u8, |
|
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_u32, |
|
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 le_u8, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
112 le_u8, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
113 ) |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
114 .parse(input)?; |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
115 Ok(( |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
116 encrypted, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
117 Header { |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
118 version, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
119 character, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
120 rank, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
121 checksum, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
122 unknown1, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
123 unknown2, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
124 key, |
|
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 } |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
128 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
129 /// 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
|
130 pub fn decrypt(key: u8, encrypted: &[u8]) -> Vec<u8> { |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
131 let mut decrypted = Vec::with_capacity(encrypted.len()); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
132 for (i, c) in encrypted.iter().enumerate() { |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
133 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
|
134 } |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
135 decrypted |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
136 } |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
137 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
138 /// Verify decrypted data against the header’s checksum. |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
139 pub fn verify(key: u8, checksum: u32, data: &[u8]) -> bool { |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
140 let real_sum = data |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
141 .iter() |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
142 .map(|c| *c as u32) |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
143 .sum::<u32>() |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
144 .wrapping_add(0x3f000318) |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
145 .wrapping_add(key as u32); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
146 checksum == real_sum |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
147 } |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
148 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
149 /// Parse a slice of bytes into a `T6rp` struct. |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
150 pub fn from_slice(input: &[u8]) -> IResult<&[u8], T6rp> { |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
151 let ( |
|
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 (unknown3, date, name, unknown4, score, unknown5, slowdown, unknown6, stage_offsets), |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
154 ) = ( |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
155 le_u8, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
156 count(le_u8, 9), |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
157 count(le_u8, 9), |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
158 le_u16, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
159 le_u32, |
|
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 le_f32, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
162 le_u32, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
163 count(le_u32, 7), |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
164 ) |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
165 .parse(input)?; |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
166 let date = date.try_into().unwrap(); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
167 let name = name.try_into().unwrap(); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
168 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
169 let mut levels = [const { None }; 7]; |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
170 for (index, offset) in stage_offsets.into_iter().enumerate() { |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
171 if offset == 0 { |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
172 continue; |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
173 } |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
174 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
175 let data = &input[offset as usize - 15..]; |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
176 let ( |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
177 mut data, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
178 (score, random_seed, point_items, power, lives, bombs, difficulty, unknown), |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
179 ) = (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
|
180 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
181 let mut keys = Vec::new(); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
182 loop { |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
183 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
|
184 data = data2; |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
185 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
186 if time == 9999999 { |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
187 break; |
|
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 keys.push((time, keystate, unknown)); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
190 } |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
191 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
192 levels[index] = Some(Level { |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
193 score, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
194 random_seed, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
195 point_items, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
196 power, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
197 lives, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
198 bombs, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
199 difficulty, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
200 unknown, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
201 keys, |
|
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 } |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
204 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
205 Ok(( |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
206 b"", |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
207 T6rp { |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
208 unknown3, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
209 date, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
210 name, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
211 unknown4, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
212 score, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
213 unknown5, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
214 slowdown, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
215 unknown6, |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
216 levels, |
|
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 } |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
221 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
222 #[cfg(test)] |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
223 mod tests { |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
224 use super::*; |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
225 use std::fs::File; |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
226 use std::io::{self, Read}; |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
227 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
228 #[test] |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
229 fn t6rp() { |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
230 let file = File::open("EoSD/CM.bak/demo00.rpy").unwrap(); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
231 let mut file = io::BufReader::new(file); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
232 let mut buf = Vec::new(); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
233 file.read_to_end(&mut buf).unwrap(); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
234 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
235 let (encrypted, header) = T6rp::parse_header(&buf).unwrap(); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
236 assert_eq!(header.version, 0x0102); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
237 assert_eq!(header.character, 0); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
238 assert_eq!(header.rank, 3); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
239 assert_eq!(header.checksum, 0x3f03188d); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
240 assert_eq!(header.key, 0x9d); |
|
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 let data = T6rp::decrypt(header.key, &encrypted); |
|
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 assert!(T6rp::verify(header.key, header.checksum, &data)); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
245 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
246 let (_, t6rp) = T6rp::from_slice(&data).unwrap(); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
247 assert_eq!(t6rp.date, *b"08/21/02\0"); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
248 assert_eq!(t6rp.name, *b"A \0"); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
249 assert_eq!(t6rp.score, 7837690); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
250 assert_eq!(t6rp.slowdown, 0.62111616); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
251 |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
252 let level4 = t6rp.levels[4 - 1].as_ref().unwrap(); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
253 assert_eq!(level4.score, 7837690); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
254 assert_eq!(level4.random_seed, 0xbf81); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
255 assert_eq!(level4.point_items, 0); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
256 assert_eq!(level4.power, 128); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
257 assert_eq!(level4.lives, 2); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
258 assert_eq!(level4.bombs, 3); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
259 assert_eq!(level4.difficulty, 16); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
260 assert_eq!(level4.keys.len(), 322); |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
261 } |
|
8c2ef2d503c9
formats: Implement T6RP in Rust
Link Mauve <linkmauve@linkmauve.fr>
parents:
diff
changeset
|
262 } |
