rpgm_asset_decrypter_lib/lib.rs
1/*!
2# rpgm-asset-decrypter-lib
3
4A library for decrypting/encrypting RPG Maker MV/MZ audio and image assets.
5
6Used in [rpgm-asset-decrypter-rs](https://github.com/savannstm/rpgm-asset-decrypter-rs) CLI tool.
7
8## Installation
9
10`cargo add rpgm-asset-decrypter-lib`
11
12## Usage
13
14Decrypt:
15
16```no_run
17use rpgm_asset_decrypter_lib::Decrypter;
18use std::fs::{read, write};
19
20let mut decrypter = Decrypter::new();
21let file = "./picture.rpgmvp";
22let buf = read(file).unwrap();
23
24// For images, decrypter automatically determines the key.
25// For audio, read `encryptionKey` property from `System.json` and pass it to `Decrypter` constructor.
26let decrypted = decrypter.decrypt(&buf);
27write("./decrypted-pitcure.png", decrypted).unwrap();
28```
29
30Encrypt:
31
32```no_run
33use rpgm_asset_decrypter_lib::{Decrypter, DEFAULT_KEY};
34use std::fs::{read, write};
35
36// When encrypting, decrypter requires a key.
37// It can be read from `encryptionKey` property in `System.json`.
38let mut decrypter = Decrypter::new();
39
40// The library provides default key, which most games use by default.
41// It might not work for every game, so if you get bad output, grab the right one from `System.json`.
42decrypter.set_key_from_str(DEFAULT_KEY).unwrap();
43let file = "./picture.png";
44let buf = read(file).unwrap();
45
46let encrypted = decrypter.encrypt(&buf).unwrap();
47write("./decrypted-pitcure.rpgmvp", encrypted).unwrap();
48```
49
50## License
51
52Project is licensed under WTFPL.
53*/
54
55use thiserror::Error;
56
57const HEADER_LENGTH: usize = 16;
58const KEY_BYTES_LENGTH: usize = 16;
59const PNG_HEADER: [u8; HEADER_LENGTH] = [
60 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
61 0x49, 0x48, 0x44, 0x52,
62];
63const HEADER: [u8; HEADER_LENGTH] = [
64 0x52, 0x50, 0x47, 0x4d, 0x56, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00,
65 0x00, 0x00, 0x00, 0x00,
66];
67const HEX_CHARS: &[u8; 16] = b"0123456789abcdef";
68pub const KEY_LENGTH: usize = 32;
69pub const DEFAULT_KEY: &str = "d41d8cd98f00b204e9800998ecf8427e";
70
71#[derive(Debug, Error)]
72#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
73pub enum Error {
74 #[error(
75 "Key must be set using any of `set_key*` methods before calling `encrypt` function."
76 )]
77 KeyNotSet,
78 #[error("Key must have a fixed length of 32 characters.")]
79 InvalidKeyLength,
80}
81
82#[derive(Default)]
83pub struct Decrypter {
84 key_hex: [u8; KEY_LENGTH],
85 key_bytes: [u8; KEY_BYTES_LENGTH],
86 key_set: bool,
87}
88
89impl Decrypter {
90 /// Creates a new Decrypter instance.
91 ///
92 /// Decrypter requires a key, which you can set from `set_key_from_str()` and `set_key_from_image()` functions.
93 /// You can get the key string from `encryptionKey` field in `System.json` file to set from string, or from any `rpgmvp`/`png_` image.
94 ///
95 /// `decrypt()` function will try to determine the key from input image files, so you don't need to manually set key for it.
96 pub fn new() -> Self {
97 Self::default()
98 }
99
100 #[inline]
101 fn set_key_bytes(&mut self) {
102 for (j, i) in (0..self.key_hex.len()).step_by(2).enumerate() {
103 let u8_hex = [self.key_hex[i], self.key_hex[i + 1]];
104 let u8_hex_str = unsafe { std::str::from_utf8_unchecked(&u8_hex) };
105 self.key_bytes[j] = u8::from_str_radix(u8_hex_str, 16).unwrap();
106 }
107 }
108
109 #[inline]
110 fn process_buffer(&self, buffer: &mut [u8]) {
111 for (i, item) in buffer.iter_mut().enumerate().take(HEADER_LENGTH) {
112 *item ^= self.key_bytes[i];
113 }
114 }
115
116 /// Returns the decrypter's key, or `None` if it's not set.
117 #[inline]
118 pub fn key(&self) -> Option<&str> {
119 if !self.key_set {
120 return None;
121 }
122
123 Some(unsafe { std::str::from_utf8_unchecked(&self.key_hex) })
124 }
125
126 /// Sets the decrypter's key to provided `&str`.
127 /// If key's length is not 32 bytes, the function fails and returns [`Error`].
128 #[inline]
129 pub fn set_key_from_str(&mut self, key: &str) -> Result<(), Error> {
130 if key.len() != KEY_LENGTH {
131 return Err(Error::InvalidKeyLength);
132 }
133
134 self.key_hex =
135 unsafe { *(key.as_bytes().as_ptr() as *const [u8; KEY_LENGTH]) };
136 self.set_key_bytes();
137
138 self.key_set = true;
139 Ok(())
140 }
141
142 /// Sets the key of decrypter from encrypted `file_content` image data.
143 ///
144 /// # Arguments
145 ///
146 /// - `file_content` - The data of RPG Maker file
147 #[inline]
148 pub fn set_key_from_image(&mut self, file_content: &[u8]) {
149 let header = &file_content[HEADER_LENGTH..HEADER_LENGTH * 2];
150 let mut key_hex = [0; KEY_LENGTH];
151
152 for i in (0..HEADER_LENGTH).step_by(2) {
153 let value = PNG_HEADER[i] ^ header[i];
154
155 let high = HEX_CHARS[(value >> 4) as usize];
156 let low = HEX_CHARS[(value & 0x0F) as usize];
157
158 key_hex[i] = high;
159 key_hex[i + 1] = low;
160 }
161
162 let key_string = unsafe { std::str::from_utf8_unchecked(&key_hex) };
163 let _ = self.set_key_from_str(key_string);
164 }
165
166 /// Decrypts RPG Maker file content.
167 /// Auto-determines the key from the input file.
168 ///
169 /// # Arguments
170 ///
171 /// - `file_content` - The data of RPG Maker file.
172 ///
173 /// # Returns
174 ///
175 /// - `Vec<u8>` containing decrypted data.
176 #[inline]
177 pub fn decrypt(&mut self, file_content: &[u8]) -> Vec<u8> {
178 if !self.key_set {
179 self.set_key_from_image(file_content);
180 }
181
182 let mut result = file_content[HEADER_LENGTH..].to_vec();
183 self.process_buffer(&mut result);
184 result
185 }
186
187 /// Encrypts file content.
188 ///
189 /// This function requires decrypter to have a key, which you can fetch from `System.json` file
190 /// or by calling `set_key_from_image()` with the data from encrypted image file.
191 ///
192 /// # Arguments
193 ///
194 /// - `file_content` - The data of `.png`, `.ogg` or `.m4a` file.
195 ///
196 /// # Returns
197 ///
198 /// - `Vec<u8>` containing encrypted data, or `KeyError` if key is not set.
199 #[inline]
200 pub fn encrypt(&self, file_content: &[u8]) -> Result<Vec<u8>, Error> {
201 if !self.key_set {
202 return Err(Error::KeyNotSet);
203 }
204
205 let mut data = file_content.to_vec();
206 self.process_buffer(&mut data);
207
208 let mut output_data = Vec::with_capacity(HEADER.len() + data.len());
209 output_data.extend(HEADER);
210 output_data.extend(data);
211 Ok(output_data)
212 }
213}