rpgmvx_ace_types/
script.rs

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