15 config: Vec<GameCfg>, |
15 config: Vec<GameCfg>, |
16 messages: Vec<String>, |
16 messages: Vec<String>, |
17 } |
17 } |
18 |
18 |
19 impl Demo { |
19 impl Demo { |
20 fn save(self, filename: String) -> io::Result<()> { |
|
21 let text = format!("{}", demo_to_haskell(self)); |
|
22 let mut file = fs::File::open(filename)?; |
|
23 file.write(text.as_bytes())?; |
|
24 Ok(()) |
|
25 } |
|
26 |
|
27 fn load(filename: String) -> io::Result<Self> { |
|
28 let mut file = fs::File::open(filename)?; |
|
29 let mut bytes = vec![]; |
|
30 file.read_to_end(&mut bytes)?; |
|
31 match super::haskell::parse(&bytes[..]) { |
|
32 Ok((_, value)) => haskell_to_demo(value).ok_or(io::Error::new( |
|
33 io::ErrorKind::InvalidData, |
|
34 "Invalid demo structure", |
|
35 )), |
|
36 Err(_) => Err(io::Error::new( |
|
37 io::ErrorKind::InvalidData, |
|
38 "Unable to parse file", |
|
39 )), |
|
40 } |
|
41 } |
|
42 |
|
43 fn load_hwd(filename: String) -> io::Result<Self> { |
20 fn load_hwd(filename: String) -> io::Result<Self> { |
44 let file = fs::File::open(filename)?; |
21 let file = fs::File::open(filename)?; |
45 let mut reader = io::BufReader::new(file); |
22 let mut reader = io::BufReader::new(file); |
46 |
23 |
47 #[inline] |
24 #[inline] |
271 messages, |
248 messages, |
272 }) |
249 }) |
273 } |
250 } |
274 } |
251 } |
275 |
252 |
276 fn demo_to_haskell(mut demo: Demo) -> HaskellValue { |
253 fn replay_to_haskell(mut replay: Replay) -> HaskellValue { |
277 use HaskellValue as Hs; |
254 use HaskellValue as Hs; |
278 |
255 |
279 let mut teams = Vec::with_capacity(demo.teams.len()); |
256 let mut teams = Vec::with_capacity(replay.teams.len()); |
280 for team in demo.teams { |
257 for team in replay.teams { |
281 let mut fields = HashMap::<String, HaskellValue>::new(); |
258 let mut fields = HashMap::<String, HaskellValue>::new(); |
282 |
259 |
283 fields.insert("teamowner".to_string(), Hs::String(team.owner)); |
260 fields.insert("teamowner".to_string(), Hs::String(team.owner)); |
284 fields.insert("teamname".to_string(), Hs::String(team.name)); |
261 fields.insert("teamname".to_string(), Hs::String(team.name)); |
285 fields.insert("teamcolor".to_string(), Hs::Number(team.color)); |
262 fields.insert("teamcolor".to_string(), Hs::Number(team.color)); |
314 Hs::String(name.to_string()), |
291 Hs::String(name.to_string()), |
315 Hs::String(value), |
292 Hs::String(value), |
316 ])); |
293 ])); |
317 }; |
294 }; |
318 |
295 |
319 for config_item in &demo.config { |
296 let config = replay.config; |
320 match config_item { |
297 |
321 GameCfg::FeatureSize(size) => save_map_config("FEATURE_SIZE", size.to_string()), |
298 save_map_config("FEATURE_SIZE", config.feature_size.to_string()); |
322 GameCfg::MapType(map_type) => save_map_config("MAP", map_type.clone()), |
299 save_map_config("MAP", config.map_type); |
323 GameCfg::MapGenerator(generator) => save_map_config("MAPGEN", generator.to_string()), |
300 save_map_config("MAPGEN", config.map_generator.to_string()); |
324 GameCfg::MazeSize(size) => save_map_config("MAZE_SIZE", size.to_string()), |
301 save_map_config("MAZE_SIZE", config.maze_size.to_string()); |
325 GameCfg::Seed(seed) => save_map_config("SEED", seed.clone()), |
302 save_map_config("SEED", config.seed); |
326 GameCfg::Template(template) => save_map_config("TEMPLATE", template.to_string()), |
303 save_map_config("TEMPLATE", config.template.to_string()); |
327 GameCfg::DrawnMap(map) => save_map_config("DRAWNMAP", map.clone()), |
304 if let Some(drawn_map) = config.drawn_map { |
328 _ => (), |
305 save_map_config("DRAWNMAP", drawn_map); |
329 } |
|
330 } |
306 } |
331 |
307 |
332 let mut save_game_config = |name: &str, mut value: Vec<String>| { |
308 let mut save_game_config = |name: &str, mut value: Vec<String>| { |
333 map_config.push(Hs::Tuple(vec![ |
309 map_config.push(Hs::Tuple(vec![ |
334 Hs::String(name.to_string()), |
310 Hs::String(name.to_string()), |
335 Hs::List(value.drain(..).map(Hs::String).collect()), |
311 Hs::List(value.drain(..).map(Hs::String).collect()), |
336 ])); |
312 ])); |
337 }; |
313 }; |
338 |
314 |
339 for config_item in &demo.config { |
315 match config.ammo { |
340 match config_item { |
316 Ammo { |
341 GameCfg::Ammo(name, Some(ammo)) => { |
317 name, |
342 save_game_config("AMMO", vec![name.clone(), ammo.clone()]) |
318 settings: Some(settings), |
343 } |
319 } => save_game_config("AMMO", vec![name, settings.clone()]), |
344 GameCfg::Ammo(name, None) => save_game_config("AMMO", vec![name.clone()]), |
320 Ammo { name, .. } => save_game_config("AMMO", vec![name.clone()]), |
345 GameCfg::Scheme(name, scheme) => { |
321 } |
346 let mut values = vec![name.clone()]; |
322 |
347 values.extend_from_slice(&scheme); |
323 match config.scheme { |
348 save_game_config("SCHEME", values); |
324 Scheme { name, settings } => { |
349 } |
325 let mut values = vec![name]; |
350 GameCfg::Script(script) => save_game_config("SCRIPT", vec![script.clone()]), |
326 values.extend_from_slice(&settings); |
351 GameCfg::Theme(theme) => save_game_config("THEME", vec![theme.clone()]), |
327 save_game_config("SCHEME", values); |
352 _ => (), |
328 } |
353 } |
329 } |
354 } |
330 |
|
331 save_game_config("SCRIPT", vec![config.script]); |
|
332 save_game_config("THEME", vec![config.theme]); |
355 |
333 |
356 Hs::Tuple(vec![ |
334 Hs::Tuple(vec![ |
357 Hs::List(teams), |
335 Hs::List(teams), |
358 Hs::List(map_config), |
336 Hs::List(map_config), |
359 Hs::List(game_config), |
337 Hs::List(game_config), |
360 Hs::List(demo.messages.drain(..).map(Hs::String).collect()), |
338 Hs::List(replay.message_log.drain(..).map(Hs::String).collect()), |
361 ]) |
339 ]) |
362 } |
340 } |
363 |
341 |
364 fn haskell_to_demo(value: HaskellValue) -> Option<Demo> { |
342 fn haskell_to_replay(value: HaskellValue) -> Option<Replay> { |
365 use HaskellValue::*; |
343 use HaskellValue::*; |
|
344 let mut config = RoomConfig::new(); |
366 let mut lists = value.into_tuple()?; |
345 let mut lists = value.into_tuple()?; |
367 let mut lists_iter = lists.drain(..); |
346 let mut lists_iter = lists.drain(..); |
368 |
347 |
369 let teams_list = lists_iter.next()?.into_list()?; |
348 let teams_list = lists_iter.next()?.into_list()?; |
370 let map_config = lists_iter.next()?.into_list()?; |
349 let map_config = lists_iter.next()?.into_list()?; |
407 } |
386 } |
408 } |
387 } |
409 teams.push(team_info) |
388 teams.push(team_info) |
410 } |
389 } |
411 |
390 |
412 let mut config = Vec::with_capacity(map_config.len() + game_config.len()); |
|
413 |
|
414 for item in map_config { |
391 for item in map_config { |
415 let mut tuple = item.into_tuple()?; |
392 let mut tuple = item.into_tuple()?; |
416 let mut tuple_iter = tuple.drain(..); |
393 let mut tuple_iter = tuple.drain(..); |
417 let name = tuple_iter.next()?.into_string()?; |
394 let name = tuple_iter.next()?.into_string()?; |
418 let value = tuple_iter.next()?.into_string()?; |
395 let value = tuple_iter.next()?.into_string()?; |
419 |
396 |
420 let config_item = match &name[..] { |
397 match &name[..] { |
421 "FEATURE_SIZE" => GameCfg::FeatureSize(u32::from_str(&value).ok()?), |
398 "FEATURE_SIZE" => config.feature_size = u32::from_str(&value).ok()?, |
422 "MAP" => GameCfg::MapType(value), |
399 "MAP" => config.map_type = value, |
423 "MAPGEN" => GameCfg::MapGenerator(u32::from_str(&value).ok()?), |
400 "MAPGEN" => config.map_generator = u32::from_str(&value).ok()?, |
424 "MAZE_SIZE" => GameCfg::MazeSize(u32::from_str(&value).ok()?), |
401 "MAZE_SIZE" => config.maze_size = u32::from_str(&value).ok()?, |
425 "SEED" => GameCfg::Seed(value), |
402 "SEED" => config.seed = value, |
426 "TEMPLATE" => GameCfg::Template(u32::from_str(&value).ok()?), |
403 "TEMPLATE" => config.template = u32::from_str(&value).ok()?, |
427 "DRAWNMAP" => GameCfg::DrawnMap(value), |
404 "DRAWNMAP" => config.drawn_map = Some(value), |
428 _ => None?, |
405 _ => {} |
429 }; |
406 }; |
430 config.push(config_item); |
|
431 } |
407 } |
432 |
408 |
433 for item in game_config { |
409 for item in game_config { |
434 let mut tuple = item.into_tuple()?; |
410 let mut tuple = item.into_tuple()?; |
435 let mut tuple_iter = tuple.drain(..); |
411 let mut tuple_iter = tuple.drain(..); |
436 let name = tuple_iter.next()?.into_string()?; |
412 let name = tuple_iter.next()?.into_string()?; |
437 let mut value = tuple_iter.next()?.into_list()?; |
413 let mut value = tuple_iter.next()?.into_list()?; |
438 let mut value_iter = value.drain(..); |
414 let mut value_iter = value.drain(..); |
439 |
415 |
440 let config_item = match &name[..] { |
416 let config_item = match &name[..] { |
441 "AMMO" => GameCfg::Ammo( |
417 "AMMO" => { |
442 value_iter.next()?.into_string()?, |
418 config.ammo = Ammo { |
443 value_iter.next().and_then(|v| v.into_string()), |
419 name: value_iter.next()?.into_string()?, |
444 ), |
420 settings: value_iter.next().and_then(|v| v.into_string()), |
445 "SCHEME" => GameCfg::Scheme( |
421 } |
446 value_iter.next()?.into_string()?, |
422 } |
447 value_iter.filter_map(|v| v.into_string()).collect(), |
423 "SCHEME" => { |
448 ), |
424 config.scheme = Scheme { |
449 "SCRIPT" => GameCfg::Script(value_iter.next()?.into_string()?), |
425 name: value_iter.next()?.into_string()?, |
450 "THEME" => GameCfg::Theme(value_iter.next()?.into_string()?), |
426 settings: value_iter.filter_map(|v| v.into_string()).collect(), |
|
427 } |
|
428 } |
|
429 "SCRIPT" => config.script = value_iter.next()?.into_string()?, |
|
430 "THEME" => config.theme = value_iter.next()?.into_string()?, |
451 _ => None?, |
431 _ => None?, |
452 }; |
432 }; |
453 config.push(config_item); |
|
454 } |
433 } |
455 |
434 |
456 let mut messages = Vec::with_capacity(engine_messages.len()); |
435 let mut messages = Vec::with_capacity(engine_messages.len()); |
457 |
436 |
458 for message in engine_messages { |
437 for message in engine_messages { |
459 messages.push(message.into_string()?); |
438 messages.push(message.into_string()?); |
460 } |
439 } |
461 |
440 |
462 Some(Demo { |
441 Some(Replay { |
|
442 config, |
463 teams, |
443 teams, |
464 config, |
444 message_log: messages, |
465 messages, |
|
466 }) |
445 }) |
467 } |
446 } |
|
447 |
|
448 impl Replay { |
|
449 pub fn save(self, filename: String) -> io::Result<()> { |
|
450 let text = format!("{}", replay_to_haskell(self)); |
|
451 let mut file = fs::File::open(filename)?; |
|
452 file.write(text.as_bytes())?; |
|
453 Ok(()) |
|
454 } |
|
455 |
|
456 pub fn load(filename: &str) -> io::Result<Self> { |
|
457 let mut file = fs::File::open(filename)?; |
|
458 let mut bytes = vec![]; |
|
459 file.read_to_end(&mut bytes)?; |
|
460 match super::haskell::parse(&bytes[..]) { |
|
461 Ok((_, value)) => haskell_to_replay(value).ok_or(io::Error::new( |
|
462 io::ErrorKind::InvalidData, |
|
463 "Invalid replay structure", |
|
464 )), |
|
465 Err(_) => Err(io::Error::new( |
|
466 io::ErrorKind::InvalidData, |
|
467 "Unable to parse file", |
|
468 )), |
|
469 } |
|
470 } |
|
471 } |