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}