rpgmxp_types/
map.rs

1use crate::AudioFile;
2use crate::Event;
3use crate::Table;
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::ObjectValue;
11use ruby_marshal::SymbolValue;
12use ruby_marshal::Value;
13use ruby_marshal::ValueArena;
14use ruby_marshal::ValueHandle;
15use std::collections::HashMap;
16
17const OBJECT_NAME: &[u8] = b"RPG::Map";
18
19const BGM_FIELD: &[u8] = b"@bgm";
20const TILESET_ID_FIELD: &[u8] = b"@tileset_id";
21const EVENTS_FIELD: &[u8] = b"@events";
22const BGS_FIELD: &[u8] = b"@bgs";
23const AUTOPLAY_BGM_FIELD: &[u8] = b"@autoplay_bgm";
24const DATA_FIELD: &[u8] = b"@data";
25const AUTOPLAY_BGS_FIELD: &[u8] = b"@autoplay_bgs";
26const HEIGHT_FIELD: &[u8] = b"@height";
27const ENCOUNTER_STEP_FIELD: &[u8] = b"@encounter_step";
28const WIDTH_FIELD: &[u8] = b"@width";
29const ENCOUNTER_LIST_FIELD: &[u8] = b"@encounter_list";
30
31/// An error that may occur while creating a map from a ruby value.
32#[derive(Debug)]
33pub enum MapFromValueError {
34    /// We currently don't support a non-empty encounter list.
35    NonEmptyEncounterList,
36}
37
38impl std::fmt::Display for MapFromValueError {
39    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
40        match self {
41            Self::NonEmptyEncounterList => {
42                write!(f, "a non-empty map encounter_list was encountered")
43            }
44        }
45    }
46}
47
48impl std::error::Error for MapFromValueError {}
49
50/// An error that may occur when transforming a map into a ruby value
51#[derive(Debug)]
52pub enum MapIntoValueError {
53    /// We currently don't support a non-empty encounter list.
54    NonEmptyEncounterList,
55}
56
57impl std::fmt::Display for MapIntoValueError {
58    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
59        match self {
60            Self::NonEmptyEncounterList => {
61                write!(f, "a non-empty map encounter_list was provided")
62            }
63        }
64    }
65}
66
67impl std::error::Error for MapIntoValueError {}
68
69/// A Map
70#[derive(Debug, serde::Serialize, serde::Deserialize)]
71pub struct Map {
72    pub bgm: AudioFile,
73    pub tileset_id: i32,
74    pub events: HashMap<i32, Event>,
75    pub bgs: AudioFile,
76    pub autoplay_bgm: bool,
77    pub data: Table,
78    pub autoplay_bgs: bool,
79    pub height: i32,
80    pub encounter_step: i32,
81    pub width: i32,
82
83    /// I haven't encountered a non-empty field here.
84    /// If you see one open an issue.
85    pub encounter_list: Vec<()>,
86}
87
88impl<'a> FromValue<'a> for Map {
89    fn from_value(ctx: &FromValueContext<'a>, value: &Value) -> Result<Self, FromValueError> {
90        let object: &ObjectValue = FromValue::from_value(ctx, value)?;
91        let name = object.name();
92        let name: &SymbolValue = ctx.from_value(name.into())?;
93        let name = name.value();
94
95        if name != OBJECT_NAME {
96            return Err(FromValueError::UnexpectedObjectName { name: name.into() });
97        }
98
99        let instance_variables = object.instance_variables();
100
101        let mut bgm_field = None;
102        let mut tileset_id_field = None;
103        let mut events_field = None;
104        let mut bgs_field = None;
105        let mut autoplay_bgm_field = None;
106        let mut data_field = None;
107        let mut autoplay_bgs_field = None;
108        let mut height_field = None;
109        let mut encounter_step_field = None;
110        let mut width_field = None;
111        let mut encounter_list_field = None;
112
113        for (key, value) in instance_variables.iter().copied() {
114            let key: &SymbolValue = ctx.from_value(key.into())?;
115            let key = key.value();
116
117            match key {
118                BGM_FIELD => {
119                    if bgm_field.is_some() {
120                        return Err(FromValueError::DuplicateInstanceVariable { name: key.into() });
121                    }
122
123                    let bgm = ctx.from_value(value)?;
124                    bgm_field = Some(bgm);
125                }
126                TILESET_ID_FIELD => {
127                    if tileset_id_field.is_some() {
128                        return Err(FromValueError::DuplicateInstanceVariable { name: key.into() });
129                    }
130
131                    tileset_id_field = Some(ctx.from_value(value)?);
132                }
133                EVENTS_FIELD => {
134                    if events_field.is_some() {
135                        return Err(FromValueError::DuplicateInstanceVariable { name: key.into() });
136                    }
137
138                    events_field = Some(ctx.from_value(value)?);
139                }
140                BGS_FIELD => {
141                    if bgs_field.is_some() {
142                        return Err(FromValueError::DuplicateInstanceVariable { name: key.into() });
143                    }
144
145                    bgs_field = Some(ctx.from_value(value)?);
146                }
147                AUTOPLAY_BGM_FIELD => {
148                    if autoplay_bgm_field.is_some() {
149                        return Err(FromValueError::DuplicateInstanceVariable { name: key.into() });
150                    }
151
152                    autoplay_bgm_field = Some(ctx.from_value(value)?);
153                }
154                DATA_FIELD => {
155                    if data_field.is_some() {
156                        return Err(FromValueError::DuplicateInstanceVariable { name: key.into() });
157                    }
158
159                    data_field = Some(ctx.from_value(value)?);
160                }
161                AUTOPLAY_BGS_FIELD => {
162                    if autoplay_bgs_field.is_some() {
163                        return Err(FromValueError::DuplicateInstanceVariable { name: key.into() });
164                    }
165
166                    autoplay_bgs_field = Some(ctx.from_value(value)?);
167                }
168                HEIGHT_FIELD => {
169                    if height_field.is_some() {
170                        return Err(FromValueError::DuplicateInstanceVariable { name: key.into() });
171                    }
172
173                    height_field = Some(ctx.from_value(value)?);
174                }
175                ENCOUNTER_STEP_FIELD => {
176                    if encounter_step_field.is_some() {
177                        return Err(FromValueError::DuplicateInstanceVariable { name: key.into() });
178                    }
179
180                    encounter_step_field = Some(ctx.from_value(value)?);
181                }
182                WIDTH_FIELD => {
183                    if width_field.is_some() {
184                        return Err(FromValueError::DuplicateInstanceVariable { name: key.into() });
185                    }
186
187                    width_field = Some(ctx.from_value(value)?);
188                }
189                ENCOUNTER_LIST_FIELD => {
190                    if encounter_list_field.is_some() {
191                        return Err(FromValueError::DuplicateInstanceVariable { name: key.into() });
192                    }
193
194                    let encounter_list: &ArrayValue = ctx.from_value(value)?;
195
196                    if !encounter_list.is_empty() {
197                        return Err(FromValueError::new_other(
198                            MapFromValueError::NonEmptyEncounterList,
199                        ));
200                    }
201
202                    encounter_list_field = Some(Vec::new());
203                }
204                _ => {
205                    return Err(FromValueError::UnknownInstanceVariable { name: key.into() });
206                }
207            }
208        }
209
210        let bgm = bgm_field.ok_or_else(|| FromValueError::MissingInstanceVariable {
211            name: BGM_FIELD.into(),
212        })?;
213        let tileset_id =
214            tileset_id_field.ok_or_else(|| FromValueError::MissingInstanceVariable {
215                name: TILESET_ID_FIELD.into(),
216            })?;
217        let events = events_field.ok_or_else(|| FromValueError::MissingInstanceVariable {
218            name: EVENTS_FIELD.into(),
219        })?;
220        let bgs = bgs_field.ok_or_else(|| FromValueError::MissingInstanceVariable {
221            name: BGS_FIELD.into(),
222        })?;
223        let autoplay_bgm =
224            autoplay_bgm_field.ok_or_else(|| FromValueError::MissingInstanceVariable {
225                name: AUTOPLAY_BGM_FIELD.into(),
226            })?;
227        let data = data_field.ok_or_else(|| FromValueError::MissingInstanceVariable {
228            name: DATA_FIELD.into(),
229        })?;
230        let autoplay_bgs =
231            autoplay_bgs_field.ok_or_else(|| FromValueError::MissingInstanceVariable {
232                name: AUTOPLAY_BGS_FIELD.into(),
233            })?;
234        let height = height_field.ok_or_else(|| FromValueError::MissingInstanceVariable {
235            name: HEIGHT_FIELD.into(),
236        })?;
237        let encounter_step =
238            encounter_step_field.ok_or_else(|| FromValueError::MissingInstanceVariable {
239                name: ENCOUNTER_STEP_FIELD.into(),
240            })?;
241        let width = width_field.ok_or_else(|| FromValueError::MissingInstanceVariable {
242            name: WIDTH_FIELD.into(),
243        })?;
244        let encounter_list =
245            encounter_list_field.ok_or_else(|| FromValueError::MissingInstanceVariable {
246                name: ENCOUNTER_LIST_FIELD.into(),
247            })?;
248
249        Ok(Self {
250            bgm,
251            tileset_id,
252            events,
253            bgs,
254            autoplay_bgm,
255            data,
256            autoplay_bgs,
257            height,
258            encounter_step,
259            width,
260            encounter_list,
261        })
262    }
263}
264
265impl IntoValue for Map {
266    fn into_value(self, arena: &mut ValueArena) -> Result<ValueHandle, IntoValueError> {
267        let object_name = arena.create_symbol(OBJECT_NAME.into());
268
269        let bgm_field_key = arena.create_symbol(BGM_FIELD.into());
270        let tileset_id_field_key = arena.create_symbol(TILESET_ID_FIELD.into());
271        let events_field_key = arena.create_symbol(EVENTS_FIELD.into());
272        let bgs_field_key = arena.create_symbol(BGS_FIELD.into());
273        let autoplay_bgm_field_key = arena.create_symbol(AUTOPLAY_BGM_FIELD.into());
274        let data_field_key = arena.create_symbol(DATA_FIELD.into());
275        let autoplay_bgs_field_key = arena.create_symbol(AUTOPLAY_BGS_FIELD.into());
276        let height_field_key = arena.create_symbol(HEIGHT_FIELD.into());
277        let encounter_step_field_key = arena.create_symbol(ENCOUNTER_STEP_FIELD.into());
278        let width_field_key = arena.create_symbol(WIDTH_FIELD.into());
279        let encounter_list_field_key = arena.create_symbol(ENCOUNTER_LIST_FIELD.into());
280
281        let bgm_field_value = self.bgm.into_value(arena)?;
282        let tileset_id_field_value = self.tileset_id.into_value(arena)?;
283        let events_field_value = self.events.into_value(arena)?;
284        let bgs_field_value = self.bgs.into_value(arena)?;
285        let autoplay_bgm_field_value = self.autoplay_bgm.into_value(arena)?;
286        let data_field_value = self.data.into_value(arena)?;
287        let autoplay_bgs_field_value = self.autoplay_bgs.into_value(arena)?;
288        let height_field_value = self.height.into_value(arena)?;
289        let encounter_step_field_value = self.encounter_step.into_value(arena)?;
290        let width_field_value = self.width.into_value(arena)?;
291        if !self.encounter_list.is_empty() {
292            return Err(IntoValueError::new_other(
293                MapIntoValueError::NonEmptyEncounterList,
294            ));
295        }
296        let encounter_list_field_value = arena.create_array(Vec::new()).into();
297
298        let fields = vec![
299            (bgm_field_key, bgm_field_value),
300            (tileset_id_field_key, tileset_id_field_value),
301            (events_field_key, events_field_value),
302            (bgs_field_key, bgs_field_value),
303            (autoplay_bgm_field_key, autoplay_bgm_field_value),
304            (data_field_key, data_field_value),
305            (autoplay_bgs_field_key, autoplay_bgs_field_value),
306            (height_field_key, height_field_value),
307            (encounter_step_field_key, encounter_step_field_value),
308            (width_field_key, width_field_value),
309            (encounter_list_field_key, encounter_list_field_value),
310        ];
311
312        let object = arena.create_object(object_name, fields);
313
314        Ok(object.into())
315    }
316}