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#[derive(Debug)]
33pub enum MapFromValueError {
34 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#[derive(Debug)]
52pub enum MapIntoValueError {
53 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#[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 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}