rpgm_common_types/
script.rs

1use flate2::bufread::ZlibDecoder;
2use flate2::bufread::ZlibEncoder;
3use flate2::Compression;
4use ruby_marshal::ArrayValue;
5use ruby_marshal::FromValue;
6use ruby_marshal::FromValueContext;
7use ruby_marshal::FromValueError;
8use ruby_marshal::IntoValue;
9use ruby_marshal::IntoValueError;
10use ruby_marshal::StringValue;
11use ruby_marshal::Value;
12use ruby_marshal::ValueArena;
13use ruby_marshal::ValueHandle;
14use std::io::Read;
15
16const ARRAY_LEN: usize = 3;
17
18/// A list of compressed scripts
19#[derive(Debug)]
20pub struct CompressedScriptList {
21    /// Scripts
22    pub scripts: Vec<CompressedScript>,
23}
24
25impl<'a> FromValue<'a> for CompressedScriptList {
26    fn from_value(ctx: &FromValueContext<'a>, value: &Value) -> Result<Self, FromValueError> {
27        Ok(Self {
28            scripts: FromValue::from_value(ctx, value)?,
29        })
30    }
31}
32impl IntoValue for CompressedScriptList {
33    fn into_value(self, arena: &mut ValueArena) -> Result<ValueHandle, IntoValueError> {
34        self.scripts.into_value(arena)
35    }
36}
37
38/// A list of scripts
39#[derive(Debug)]
40pub struct ScriptList {
41    /// Scripts
42    pub scripts: Vec<Script>,
43}
44
45impl<'a> FromValue<'a> for ScriptList {
46    fn from_value(ctx: &FromValueContext<'a>, value: &Value) -> Result<Self, FromValueError> {
47        Ok(Self {
48            scripts: FromValue::from_value(ctx, value)?,
49        })
50    }
51}
52impl IntoValue for ScriptList {
53    fn into_value(self, arena: &mut ValueArena) -> Result<ValueHandle, IntoValueError> {
54        self.scripts.into_value(arena)
55    }
56}
57
58/// Invalid script ruby data
59#[derive(Debug)]
60pub enum ScriptFromValueError {
61    /// The array len was invalid.
62    InvalidArrayLen { len: usize },
63
64    /// The name was invalid
65    InvalidName { error: std::str::Utf8Error },
66}
67
68impl std::fmt::Display for ScriptFromValueError {
69    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70        match self {
71            Self::InvalidArrayLen { len } => {
72                write!(f, "invalid script array len of {len}, expected {ARRAY_LEN}")
73            }
74            Self::InvalidName { .. } => {
75                write!(f, "the script name is invalid")
76            }
77        }
78    }
79}
80
81impl std::error::Error for ScriptFromValueError {
82    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
83        match self {
84            Self::InvalidName { error } => Some(error),
85            _ => None,
86        }
87    }
88}
89
90impl From<ScriptFromValueError> for FromValueError {
91    fn from(error: ScriptFromValueError) -> Self {
92        FromValueError::Other {
93            error: error.into(),
94        }
95    }
96}
97
98/// A compressed script
99#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
100pub struct CompressedScript {
101    /// A randomly-generated script id.
102    ///
103    /// This should always be positive.
104    pub id: i32,
105
106    /// The name of the script.
107    pub name: String,
108
109    /// The zlib-compressed script data.
110    pub data: Vec<u8>,
111}
112
113impl<'a> FromValue<'a> for CompressedScript {
114    fn from_value(ctx: &FromValueContext<'a>, value: &Value) -> Result<Self, FromValueError> {
115        let script: &ArrayValue = FromValue::from_value(ctx, value)?;
116        let script = script.value();
117
118        let array_len = script.len();
119        if array_len != ARRAY_LEN {
120            return Err(ScriptFromValueError::InvalidArrayLen { len: array_len }.into());
121        }
122
123        let id: i32 = ctx.from_value(script[0])?;
124
125        let name: &StringValue = ctx.from_value(script[1])?;
126        let name = std::str::from_utf8(name.value())
127            .map_err(|error| ScriptFromValueError::InvalidName { error })?;
128
129        let data: &StringValue = ctx.from_value(script[2])?;
130        let data = data.value();
131
132        Ok(Self {
133            id,
134            name: name.into(),
135            data: data.into(),
136        })
137    }
138}
139
140impl IntoValue for CompressedScript {
141    fn into_value(self, arena: &mut ValueArena) -> Result<ValueHandle, IntoValueError> {
142        let id = self.id.into_value(arena)?;
143        let name = arena.create_string(self.name.into()).into_raw();
144        let data = arena.create_string(self.data).into_raw();
145
146        let array = arena.create_array(vec![id, name, data]);
147
148        Ok(array.into())
149    }
150}
151
152/// A decompressed script
153#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
154pub struct Script {
155    /// A randomly-generated script id.
156    ///
157    /// This should always be positive.
158    pub id: i32,
159
160    /// The name of the script.
161    pub name: String,
162
163    /// The script data.
164    pub data: String,
165}
166
167impl<'a> FromValue<'a> for Script {
168    fn from_value(ctx: &FromValueContext<'a>, value: &Value) -> Result<Self, FromValueError> {
169        let script: CompressedScript = FromValue::from_value(ctx, value)?;
170
171        let mut decoder = ZlibDecoder::new(&*script.data);
172        let mut data = String::new();
173        decoder
174            .read_to_string(&mut data)
175            .map_err(FromValueError::new_other)?;
176
177        Ok(Self {
178            id: script.id,
179            name: script.name,
180            data,
181        })
182    }
183}
184
185impl IntoValue for Script {
186    fn into_value(self, arena: &mut ValueArena) -> Result<ValueHandle, IntoValueError> {
187        let id = self.id.into_value(arena)?;
188        let name = arena.create_string(self.name.into()).into_raw();
189
190        let compression = Compression::default();
191        let mut encoder = ZlibEncoder::new(self.data.as_bytes(), compression);
192        let mut data = Vec::new();
193        encoder
194            .read_to_end(&mut data)
195            .map_err(IntoValueError::new_other)?;
196
197        let data = arena.create_string(data).into_raw();
198
199        let array = arena.create_array(vec![id, name, data]);
200
201        Ok(array.into())
202    }
203}