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        let mut j = 0;
153
154        for i in 0..HEADER_LENGTH {
155            let value = PNG_HEADER[i] ^ header[i];
156
157            let high = HEX_CHARS[(value >> 4) as usize];
158            let low = HEX_CHARS[(value & 0x0F) as usize];
159
160            key_hex[j] = high;
161            key_hex[j + 1] = low;
162            j += 2;
163        }
164
165        let key_string = unsafe { std::str::from_utf8_unchecked(&key_hex) };
166        let _ = self.set_key_from_str(key_string);
167    }
168
169    /// Decrypts RPG Maker file content.
170    /// Auto-determines the key from the input file.
171    ///
172    /// # Arguments
173    ///
174    /// - `file_content` - The data of RPG Maker file.
175    ///
176    /// # Returns
177    ///
178    /// - `Vec<u8>` containing decrypted data.
179    #[inline]
180    pub fn decrypt(&mut self, file_content: &[u8]) -> Vec<u8> {
181        if !self.key_set {
182            self.set_key_from_image(file_content);
183        }
184
185        let mut result = file_content[HEADER_LENGTH..].to_vec();
186        self.process_buffer(&mut result);
187        result
188    }
189
190    /// Encrypts file content.
191    ///
192    /// This function requires decrypter to have a key, which you can fetch from `System.json` file
193    /// or by calling `set_key_from_image()` with the data from encrypted image file.
194    ///
195    /// # Arguments
196    ///
197    /// - `file_content` - The data of `.png`, `.ogg` or `.m4a` file.
198    ///
199    /// # Returns
200    ///
201    /// - `Vec<u8>` containing encrypted data, or `KeyError` if key is not set.
202    #[inline]
203    pub fn encrypt(&self, file_content: &[u8]) -> Result<Vec<u8>, Error> {
204        if !self.key_set {
205            return Err(Error::KeyNotSet);
206        }
207
208        let mut data = file_content.to_vec();
209        self.process_buffer(&mut data);
210
211        let mut output_data = Vec::with_capacity(HEADER.len() + data.len());
212        output_data.extend(HEADER);
213        output_data.extend(data);
214        Ok(output_data)
215    }
216}