1#![allow(clippy::cognitive_complexity)]
4
5use crate::ai::AIInfo;
6use crate::bitmap::Bitmap;
7use crate::header::SCXHeader;
8use crate::map::Map;
9use crate::player::*;
10use crate::triggers::TriggerSystem;
11use crate::types::*;
12use crate::victory::*;
13use crate::{Error, Result, VersionBundle};
14use byteorder::{ReadBytesExt, WriteBytesExt, LE};
15use flate2::{read::DeflateDecoder, write::DeflateEncoder, Compression};
16use genie_support::{
17 f32_eq, read_opt_u32, read_str, write_opt_str, write_str, StringKey, UnitTypeID,
18};
19use std::convert::{TryFrom, TryInto};
20use std::io::{self, Read, Write};
21
22#[derive(Debug, Clone, Default)]
31pub struct ScenarioObject {
32 pub position: (f32, f32, f32),
34 pub id: i32,
36 pub object_type: UnitTypeID,
38 pub state: u8,
40 pub angle: f32,
42 pub frame: i16,
44 pub garrisoned_in: Option<i32>,
47}
48
49impl ScenarioObject {
50 pub fn read_from(mut input: impl Read, version: SCXVersion) -> Result<Self> {
51 let position = (
52 input.read_f32::<LE>()?,
53 input.read_f32::<LE>()?,
54 input.read_f32::<LE>()?,
55 );
56 let id = input.read_i32::<LE>()?;
57 let object_type = input.read_u16::<LE>()?.into();
58 let state = input.read_u8()?;
59 let angle = input.read_f32::<LE>()?;
60 let frame = if version < SCXVersion(*b"1.15") {
61 -1
62 } else {
63 input.read_i16::<LE>()?
64 };
65 let garrisoned_in = if version < SCXVersion(*b"1.13") {
66 None
67 } else {
68 Some(input.read_i32::<LE>()?)
69 }
70 .and_then(|id| match id {
71 -1 => None,
72 id => Some(id),
73 })
74 .and_then(|id| match id {
75 0 if version > SCXVersion(*b"1.12") => None,
77 id => Some(id),
78 });
79
80 Ok(Self {
81 position,
82 id,
83 object_type,
84 state,
85 angle,
86 frame,
87 garrisoned_in,
88 })
89 }
90
91 pub fn write_to(&self, mut output: impl Write, version: SCXVersion) -> Result<()> {
92 output.write_f32::<LE>(self.position.0)?;
93 output.write_f32::<LE>(self.position.1)?;
94 output.write_f32::<LE>(self.position.2)?;
95 output.write_i32::<LE>(self.id)?;
96 output.write_u16::<LE>(self.object_type.into())?;
97 output.write_u8(self.state)?;
98 output.write_f32::<LE>(self.angle)?;
99 if version > SCXVersion(*b"1.14") {
100 output.write_i16::<LE>(self.frame)?;
101 }
102 if version > SCXVersion(*b"1.12") {
103 match self.garrisoned_in {
104 Some(id) => output.write_i32::<LE>(id)?,
105 None => output.write_i32::<LE>(-1)?,
106 }
107 }
108 Ok(())
109 }
110}
111
112#[derive(Debug, Default, Clone)]
113pub(crate) struct RGEScen {
114 pub(crate) version: f32,
116 player_names: Vec<Option<String>>,
118 player_string_table: Vec<Option<StringKey>>,
120 player_base_properties: Vec<PlayerBaseProperties>,
121 victory_conquest: bool,
122 pub(crate) name: String,
124 description_string_table: Option<StringKey>,
125 hints_string_table: Option<StringKey>,
126 win_message_string_table: Option<StringKey>,
127 loss_message_string_table: Option<StringKey>,
128 history_string_table: Option<StringKey>,
129 scout_string_table: Option<StringKey>,
130 description: Option<String>,
131 hints: Option<String>,
132 win_message: Option<String>,
133 loss_message: Option<String>,
134 history: Option<String>,
135 scout: Option<String>,
136 pregame_cinematic: Option<String>,
137 victory_cinematic: Option<String>,
138 loss_cinematic: Option<String>,
139 mission_bmp: Option<String>,
140 player_build_lists: Vec<Option<String>>,
141 player_city_plans: Vec<Option<String>>,
142 player_ai_rules: Vec<Option<String>>,
143 player_files: Vec<PlayerFiles>,
144 ai_rules_types: Vec<i8>,
145}
146
147impl RGEScen {
148 pub fn read_from(mut input: impl Read) -> Result<Self> {
149 let version = input.read_f32::<LE>()?;
150 log::debug!("RGEScen version {}", version);
151 let mut player_names = vec![None; 16];
152 if version > 1.13 {
153 for name in player_names.iter_mut() {
154 *name = read_str(&mut input, 256)?;
155 }
156 }
157
158 let mut player_string_table = vec![None; 16];
159 if version > 1.16 {
160 for string_id in player_string_table.iter_mut() {
161 *string_id = read_opt_u32(&mut input)?;
162 }
163 }
164
165 let mut player_base_properties = vec![PlayerBaseProperties::default(); 16];
166 if version > 1.13 {
167 for properties in player_base_properties.iter_mut() {
168 properties.active = input.read_i32::<LE>()?;
169 properties.player_type = input.read_i32::<LE>()?;
170 properties.civilization = input.read_i32::<LE>()?;
171 properties.posture = input.read_i32::<LE>()?;
172 }
173 }
174
175 let victory_conquest = if version >= 1.07 {
176 input.read_u8()? != 0
177 } else {
178 true
179 };
180
181 dbg!(victory_conquest);
182
183 {
184 let _timeline_count = input.read_i16::<LE>()?;
185 let _timeline_available = input.read_i16::<LE>()?;
186 let _old_time = input.read_f32::<LE>()?;
187 dbg!(_timeline_count, _timeline_available, _old_time);
188 assert_eq!(_timeline_count, 0, "Unexpected RGE_Timeline");
189 }
191
192 if version >= 1.28 {
193 let _civ_lock = &mut [0; 16];
194 input.read_u32_into::<LE>(_civ_lock)?;
195 }
196
197 let name_length = input.read_i16::<LE>()? as usize;
198 let name = read_str(&mut input, name_length)?.unwrap_or_default();
200
201 let (
202 description_string_table,
203 hints_string_table,
204 win_message_string_table,
205 loss_message_string_table,
206 history_string_table,
207 ) = if version >= 1.16 {
208 (
209 read_opt_u32(&mut input)?,
210 read_opt_u32(&mut input)?,
211 read_opt_u32(&mut input)?,
212 read_opt_u32(&mut input)?,
213 read_opt_u32(&mut input)?,
214 )
215 } else {
216 Default::default()
217 };
218
219 let scout_string_table = if version >= 1.22 {
220 read_opt_u32(&mut input)?
221 } else {
222 Default::default()
223 };
224
225 let description_length = input.read_i16::<LE>()? as usize;
226 let description = read_str(&mut input, description_length)?;
227
228 let (hints, win_message, loss_message, history) = if version >= 1.11 {
229 let hints_length = input.read_i16::<LE>()? as usize;
230 let hints = read_str(&mut input, hints_length)?;
231 let win_message_length = input.read_i16::<LE>()? as usize;
232 let win_message = read_str(&mut input, win_message_length)?;
233 let loss_message_length = input.read_i16::<LE>()? as usize;
234 let loss_message = read_str(&mut input, loss_message_length)?;
235 let history_length = input.read_i16::<LE>()? as usize;
236 let history = read_str(&mut input, history_length)?;
237 (hints, win_message, loss_message, history)
238 } else {
239 (None, None, None, None)
240 };
241
242 let scout = if version >= 1.22 {
243 let scout_length = input.read_i16::<LE>()? as usize;
244 read_str(&mut input, scout_length)?
245 } else {
246 None
247 };
248
249 if version < 1.03 {
250 }
252
253 let len = input.read_i16::<LE>()? as usize;
254 let pregame_cinematic = read_str(&mut input, len)?;
255 let len = input.read_i16::<LE>()? as usize;
256 let victory_cinematic = read_str(&mut input, len)?;
257 let len = input.read_i16::<LE>()? as usize;
258 let loss_cinematic = read_str(&mut input, len)?;
259
260 let mission_bmp = if version >= 1.09 {
261 let len = input.read_i16::<LE>()? as usize;
262 read_str(&mut input, len)?
263 } else {
264 None
265 };
266
267 let _mission_picture = if version >= 1.10 {
268 Bitmap::read_from(&mut input)?
269 } else {
270 None
271 };
272
273 let mut player_build_lists = vec![None; 16];
274 for build_list in player_build_lists.iter_mut() {
275 let len = input.read_u16::<LE>()? as usize;
276 *build_list = read_str(&mut input, len)?;
277 }
278
279 let mut player_city_plans = vec![None; 16];
280 for city_plan in player_city_plans.iter_mut() {
281 let len = input.read_u16::<LE>()? as usize;
282 *city_plan = read_str(&mut input, len)?;
283 }
284
285 let mut player_ai_rules = vec![None; 16];
286 if version >= 1.08 {
287 for ai_rules in player_ai_rules.iter_mut() {
288 let len = input.read_u16::<LE>()? as usize;
289 *ai_rules = read_str(&mut input, len)?;
290 }
291 }
292
293 let mut player_files = vec![PlayerFiles::default(); 16];
294 for files in player_files.iter_mut() {
295 let build_list_length = input.read_i32::<LE>()? as usize;
296 let city_plan_length = input.read_i32::<LE>()? as usize;
297 let ai_rules_length = if version >= 1.08 {
298 input.read_i32::<LE>()? as usize
299 } else {
300 0
301 };
302
303 files.build_list = read_str(&mut input, build_list_length)?;
304 files.city_plan = read_str(&mut input, city_plan_length)?;
305 files.ai_rules = read_str(&mut input, ai_rules_length)?;
306 }
307
308 let mut ai_rules_types = vec![0; 16];
309 if version >= 1.20 {
310 for rule_type in ai_rules_types.iter_mut() {
311 *rule_type = input.read_i8()?;
312 }
313 }
314
315 if version >= 1.02 {
316 let sep = input.read_i32::<LE>()?;
317 debug_assert_eq!(sep, -99);
318 }
319
320 Ok(RGEScen {
321 version,
322 player_names,
323 player_string_table,
324 player_base_properties,
325 victory_conquest,
326 name,
327 description_string_table,
328 hints_string_table,
329 win_message_string_table,
330 loss_message_string_table,
331 history_string_table,
332 scout_string_table,
333 description,
334 hints,
335 win_message,
336 loss_message,
337 history,
338 scout,
339 pregame_cinematic,
340 victory_cinematic,
341 loss_cinematic,
342 mission_bmp,
343 player_build_lists,
344 player_city_plans,
345 player_ai_rules,
346 player_files,
347 ai_rules_types,
348 })
349 }
350
351 pub fn write_to(&self, mut output: impl Write, version: f32) -> Result<()> {
352 output.write_f32::<LE>(version)?;
353
354 if version > 1.13 {
355 assert_eq!(self.player_names.len(), 16);
356 for name in &self.player_names {
357 let mut padded_bytes = Vec::with_capacity(256);
358 if let Some(ref name) = name {
359 let name_bytes = name.as_bytes();
360 padded_bytes.write_all(name_bytes)?;
361 }
362 padded_bytes.extend(vec![0; 256 - padded_bytes.len()]);
363 output.write_all(&padded_bytes)?;
364 }
365 }
366
367 if version > 1.16 {
368 assert_eq!(self.player_string_table.len(), 16);
369 for id in &self.player_string_table {
370 write_opt_string_key(&mut output, id)?;
371 }
372 }
373
374 if version > 1.13 {
375 assert_eq!(self.player_base_properties.len(), 16);
376 for props in &self.player_base_properties {
377 output.write_i32::<LE>(props.active)?;
378 output.write_i32::<LE>(props.player_type)?;
379 output.write_i32::<LE>(props.civilization)?;
380 output.write_i32::<LE>(props.posture)?;
381 }
382 }
383
384 if version >= 1.07 {
385 output.write_u8(if self.victory_conquest { 1 } else { 0 })?;
386 }
387
388 output.write_i16::<LE>(0)?;
390 output.write_i16::<LE>(0)?;
391 output.write_f32::<LE>(-1.0)?;
392
393 if version >= 1.28 {
394 for _ in 0..16 {
396 output.write_u32::<LE>(0)?;
397 }
398 }
399
400 write_str(&mut output, &self.name)?;
401
402 if version >= 1.16 {
403 write_opt_string_key(&mut output, &self.description_string_table)?;
404 write_opt_string_key(&mut output, &self.hints_string_table)?;
405 write_opt_string_key(&mut output, &self.win_message_string_table)?;
406 write_opt_string_key(&mut output, &self.loss_message_string_table)?;
407 write_opt_string_key(&mut output, &self.history_string_table)?;
408 }
409 if version >= 1.22 {
410 write_opt_string_key(&mut output, &self.scout_string_table)?;
411 }
412
413 write_opt_str(&mut output, &self.description)?;
414 if version >= 1.11 {
415 write_opt_str(&mut output, &self.hints)?;
416 write_opt_str(&mut output, &self.win_message)?;
417 write_opt_str(&mut output, &self.loss_message)?;
418 write_opt_str(&mut output, &self.history)?;
419 }
420 if version >= 1.22 {
421 write_opt_str(&mut output, &self.scout)?;
422 }
423
424 write_opt_str(&mut output, &self.pregame_cinematic)?;
425 write_opt_str(&mut output, &self.victory_cinematic)?;
426 write_opt_str(&mut output, &self.loss_cinematic)?;
427 if version >= 1.09 {
428 write_opt_str(&mut output, &None)?;
430 }
431
432 if version >= 1.10 {
433 output.write_u32::<LE>(0)?;
435 output.write_u32::<LE>(0)?;
436 output.write_u32::<LE>(0)?;
437 output.write_u16::<LE>(1)?;
438 }
439
440 assert_eq!(self.player_build_lists.len(), 16);
441 for build_list in &self.player_build_lists {
442 write_opt_str(&mut output, build_list)?;
443 }
444
445 assert_eq!(self.player_city_plans.len(), 16);
446 for city_plan in &self.player_city_plans {
447 write_opt_str(&mut output, city_plan)?;
448 }
449
450 if version >= 1.08 {
451 assert_eq!(self.player_ai_rules.len(), 16);
452 for ai_rules in &self.player_ai_rules {
453 write_opt_str(&mut output, ai_rules)?;
454 }
455 }
456
457 assert_eq!(self.player_files.len(), 16);
458 for files in &self.player_files {
459 if let Some(build_list) = &files.build_list {
460 output.write_u32::<LE>(build_list.len() as u32)?;
461 } else {
462 output.write_u32::<LE>(0)?;
463 }
464 if let Some(city_plan) = &files.city_plan {
465 output.write_u32::<LE>(city_plan.len() as u32)?;
466 } else {
467 output.write_u32::<LE>(0)?;
468 }
469 if version >= 1.08 {
470 if let Some(ai_rules) = &files.ai_rules {
471 output.write_u32::<LE>(ai_rules.len() as u32)?;
472 } else {
473 output.write_u32::<LE>(0)?;
474 }
475 }
476 if let Some(build_list) = &files.build_list {
477 output.write_all(build_list.as_bytes())?;
478 }
479 if let Some(city_plan) = &files.city_plan {
480 output.write_all(city_plan.as_bytes())?;
481 }
482 if version >= 1.08 {
483 if let Some(ai_rules) = &files.ai_rules {
484 output.write_all(ai_rules.as_bytes())?;
485 }
486 }
487 }
488
489 if version >= 1.20 {
490 assert_eq!(self.ai_rules_types.len(), 16);
491 for ai_rules_type in &self.ai_rules_types {
492 output.write_i8(*ai_rules_type)?;
493 }
494 }
495
496 output.write_i32::<LE>(-99)?;
497
498 Ok(())
499 }
500}
501
502#[derive(Debug, Default, Clone)]
507pub struct TribeScen {
508 pub(crate) base: RGEScen,
514 player_start_resources: Vec<PlayerStartResources>,
516 victory: VictoryInfo,
518 victory_all_flag: bool,
520 mp_victory_type: i32,
522 victory_score: i32,
524 victory_time: i32,
526 diplomacy: Vec<Vec<DiplomaticStance>>,
528 legacy_victory_info: Vec<Vec<LegacyVictoryInfo>>,
529 allied_victory: Vec<i32>,
531 teams_locked: bool,
532 can_change_teams: bool,
533 random_start_locations: bool,
534 max_teams: u8,
535 num_disabled_techs: Vec<i32>,
539 disabled_techs: Vec<Vec<i32>>,
541 num_disabled_units: Vec<i32>,
545 disabled_units: Vec<Vec<i32>>,
547 num_disabled_buildings: Vec<i32>,
551 disabled_buildings: Vec<Vec<i32>>,
553 combat_mode: i32,
558 naval_mode: i32,
563 all_techs: bool,
565 player_start_ages: Vec<StartingAge>,
567 view: (i32, i32),
569 map_type: Option<i32>,
571 base_priorities: Vec<i8>,
572 water_definition: Option<String>,
574 color_mood: Option<String>,
576 collide_and_correct: bool,
580 villager_force_drop: bool,
584}
585
586impl TribeScen {
587 #[deprecated = "Use TribeScen::read_from instead"]
588 #[doc(hidden)]
589 pub fn from(input: impl Read) -> Result<Self> {
590 Self::read_from(input)
591 }
592
593 pub fn read_from(mut input: impl Read) -> Result<Self> {
595 let mut base = RGEScen::read_from(&mut input)?;
596 let version = base.version;
597
598 let mut player_start_resources = vec![PlayerStartResources::default(); 16];
599
600 if version <= 1.13 {
602 for name in base.player_names.iter_mut() {
603 *name = read_str(&mut input, 256)?;
604 }
605
606 for i in 0..16 {
607 let properties = &mut base.player_base_properties[i];
608 properties.active = input.read_i32::<LE>()?;
609 let resources = PlayerStartResources::read_from(&mut input, version)?;
610 properties.player_type = input.read_i32::<LE>()?;
611 properties.civilization = input.read_i32::<LE>()?;
612 properties.posture = input.read_i32::<LE>()?;
613 player_start_resources[i] = resources;
614 }
615 } else {
616 for resources in player_start_resources.iter_mut() {
617 *resources = PlayerStartResources::read_from(&mut input, version)?;
618 }
619 }
620
621 if version >= 1.02 {
622 let sep = input.read_i32::<LE>()?;
623 debug_assert_eq!(sep, -99);
624 }
625
626 let victory = VictoryInfo::read_from(&mut input)?;
627 let victory_all_flag = input.read_i32::<LE>()? != 0;
628
629 let mp_victory_type = if version >= 1.13 {
630 input.read_i32::<LE>()?
631 } else {
632 4
633 };
634 let victory_score = if version >= 1.13 {
635 input.read_i32::<LE>()?
636 } else {
637 900
638 };
639 let victory_time = if version >= 1.13 {
640 input.read_i32::<LE>()?
641 } else {
642 9000
643 };
644
645 log::debug!(
646 "Victory values: {} {} {}",
647 mp_victory_type,
648 victory_score,
649 victory_time
650 );
651
652 let mut diplomacy = vec![vec![DiplomaticStance::Neutral; 16]; 16];
653 for player_diplomacy in diplomacy.iter_mut() {
654 for stance in player_diplomacy.iter_mut() {
655 *stance = DiplomaticStance::try_from(input.read_i32::<LE>()?)?;
656 }
657 }
658
659 let mut legacy_victory_info = vec![vec![LegacyVictoryInfo::default(); 12]; 16];
660 for list in legacy_victory_info.iter_mut() {
661 for victory_info in list.iter_mut() {
662 *victory_info = LegacyVictoryInfo::read_from(&mut input)?;
663 }
664 }
665
666 if version >= 1.02 {
667 let sep = input.read_i32::<LE>()?;
668 debug_assert_eq!(sep, -99);
669 }
670
671 let mut allied_victory = vec![0i32; 16];
672 for setting in allied_victory.iter_mut() {
673 *setting = input.read_i32::<LE>()?;
674 }
675
676 let (teams_locked, can_change_teams, random_start_locations, max_teams) = if version >= 1.24
677 {
678 (
679 input.read_i8()? != 0,
680 input.read_i8()? != 0,
681 input.read_i8()? != 0,
682 input.read_u8()?,
683 )
684 } else if f32_eq!(version, 1.23) {
685 (input.read_i32::<LE>()? != 0, true, true, 4)
686 } else {
687 (false, true, true, 4)
688 };
689
690 let mut num_disabled_techs = vec![0; 16];
691 let mut disabled_techs = vec![vec![]; 16];
692 let mut num_disabled_units = vec![0; 16];
693 let mut disabled_units = vec![vec![]; 16];
694 let mut num_disabled_buildings = vec![0; 16];
695 let mut disabled_buildings = vec![vec![]; 16];
696
697 if version >= 1.28 {
698 input.read_i32_into::<LE>(&mut num_disabled_techs)?;
700 for (player_disabled_techs, &num) in
701 disabled_techs.iter_mut().zip(num_disabled_techs.iter())
702 {
703 *player_disabled_techs = vec![0; num as usize];
704 input.read_i32_into::<LE>(player_disabled_techs)?;
705 }
706
707 input.read_i32_into::<LE>(&mut num_disabled_units)?;
708 for (player_disabled_units, &num) in
709 disabled_units.iter_mut().zip(num_disabled_units.iter())
710 {
711 *player_disabled_units = vec![0; num as usize];
712 input.read_i32_into::<LE>(player_disabled_units)?;
713 }
714
715 input.read_i32_into::<LE>(&mut num_disabled_buildings)?;
716 for (player_disabled_buildings, &num) in disabled_buildings
717 .iter_mut()
718 .zip(num_disabled_buildings.iter())
719 {
720 *player_disabled_buildings = vec![0; num as usize];
721 input.read_i32_into::<LE>(player_disabled_buildings)?;
722 }
723 } else if version >= 1.18 {
724 input.read_i32_into::<LE>(&mut num_disabled_techs)?;
726 for player_disabled_techs in disabled_techs.iter_mut() {
727 *player_disabled_techs = vec![0; 30];
728 input.read_i32_into::<LE>(player_disabled_techs)?;
729 }
730
731 input.read_i32_into::<LE>(&mut num_disabled_units)?;
732 for player_disabled_units in disabled_units.iter_mut() {
733 *player_disabled_units = vec![0; 30];
734 input.read_i32_into::<LE>(player_disabled_units)?;
735 }
736
737 input.read_i32_into::<LE>(&mut num_disabled_buildings)?;
738 let max_disabled_buildings = if version >= 1.25 { 30 } else { 20 };
739 for player_disabled_buildings in disabled_buildings.iter_mut() {
740 *player_disabled_buildings = vec![0; max_disabled_buildings];
741 input.read_i32_into::<LE>(player_disabled_buildings)?;
742 }
743 } else if version > 1.03 {
744 for i in 0..16 {
746 let player_disabled_techs = &mut disabled_techs[i];
747 *player_disabled_techs = vec![0; 20];
748 input.read_i32_into::<LE>(player_disabled_techs)?;
749 num_disabled_techs[i] = player_disabled_techs
751 .iter()
752 .position(|val| *val <= 0)
753 .map(|index| (index as i32) + 1)
754 .unwrap_or(0);
755 }
756 } else {
757 }
759
760 let combat_mode = if version > 1.04 {
761 input.read_i32::<LE>()?
762 } else {
763 0
764 };
765 let (naval_mode, all_techs) = if version >= 1.12 {
766 (input.read_i32::<LE>()?, input.read_i32::<LE>()? != 0)
767 } else {
768 (0, false)
769 };
770
771 let mut player_start_ages = vec![StartingAge::Default; 16];
772 if version > 1.05 {
773 for start_age in player_start_ages.iter_mut() {
774 *start_age = StartingAge::try_from(input.read_i32::<LE>()?, version)?;
775 }
776 }
777
778 log::debug!("starting ages: {:?}", player_start_ages);
779
780 if version >= 1.02 {
781 let sep = input.read_i32::<LE>()?;
782 debug_assert_eq!(sep, -99);
783 }
784
785 let view = if version >= 1.19 {
786 (input.read_i32::<LE>()?, input.read_i32::<LE>()?)
787 } else {
788 (-1, -1)
789 };
790
791 let map_type = if version >= 1.21 {
792 match input.read_i32::<LE>()? {
793 -2 | -1 => None,
795 id => Some(
796 id.try_into()
797 .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?,
798 ),
799 }
800 } else {
801 None
802 };
803
804 let mut base_priorities = vec![0; 16];
805 if version >= 1.24 {
806 input.read_i8_into(&mut base_priorities)?;
807 }
808
809 let mut water_definition = None;
810 let mut color_mood = None;
811 let mut collide_and_correct = false;
812 let mut villager_force_drop = false;
813
814 if version >= 1.35 {
815 let _trigger_count = input.read_u32::<LE>()?;
818 }
819 if version >= 1.30 {
820 let _str_signature = input.read_u16::<LE>()?;
821 water_definition = {
822 let len = input.read_u16::<LE>()?;
823 read_str(&mut input, len as usize)?
824 };
825 }
826
827 if version >= 1.32 {
828 let _str_signature = input.read_u16::<LE>()?;
829 color_mood = {
830 let len = input.read_u16::<LE>()?;
831 read_str(&mut input, len as usize)?
832 };
833 }
834 if version >= 1.36 {
835 collide_and_correct = input.read_u8()? != 0;
836 }
837 if version >= 1.37 {
838 villager_force_drop = input.read_u8()? != 0;
839 }
840
841 Ok(TribeScen {
842 base,
843 player_start_resources,
844 victory,
845 victory_all_flag,
846 mp_victory_type,
847 victory_score,
848 victory_time,
849 diplomacy,
850 legacy_victory_info,
851 allied_victory,
852 teams_locked,
853 can_change_teams,
854 random_start_locations,
855 max_teams,
856 num_disabled_techs,
857 disabled_techs,
858 num_disabled_units,
859 disabled_units,
860 num_disabled_buildings,
861 disabled_buildings,
862 combat_mode,
863 naval_mode,
864 all_techs,
865 player_start_ages,
866 view,
867 map_type,
868 base_priorities,
869 water_definition,
870 color_mood,
871 collide_and_correct,
872 villager_force_drop,
873 })
874 }
875
876 pub fn write_to(&self, mut output: impl Write, version: f32, num_triggers: u32) -> Result<()> {
878 self.base.write_to(&mut output, version)?;
879
880 if version <= 1.13 {
881 assert_eq!(self.base.player_names.len(), 16);
882 for name in &self.base.player_names {
883 let mut padded_bytes = Vec::with_capacity(256);
884 if let Some(ref name) = name {
885 let name_bytes = name.as_bytes();
886 padded_bytes.write_all(name_bytes)?;
887 }
888 padded_bytes.extend(vec![0; 256 - padded_bytes.len()]);
889 output.write_all(&padded_bytes)?;
890 }
891
892 assert_eq!(self.base.player_base_properties.len(), 16);
893 assert_eq!(self.player_start_resources.len(), 16);
894 for i in 0..16 {
895 let properties = &self.base.player_base_properties[i];
896 let resources = &self.player_start_resources[i];
897 output.write_i32::<LE>(properties.active)?;
898 resources.write_to(&mut output, version)?;
899 output.write_i32::<LE>(properties.player_type)?;
900 output.write_i32::<LE>(properties.civilization)?;
901 output.write_i32::<LE>(properties.posture)?;
902 }
903 } else {
904 assert_eq!(self.player_start_resources.len(), 16);
905 for start_resources in &self.player_start_resources {
906 start_resources.write_to(&mut output, version)?;
907 }
908 }
909
910 if version >= 1.02 {
911 output.write_i32::<LE>(-99)?;
912 }
913
914 self.victory.write_to(&mut output)?;
915 output.write_i32::<LE>(if self.victory_all_flag { 1 } else { 0 })?;
916
917 if version >= 1.13 {
918 output.write_i32::<LE>(self.mp_victory_type)?;
919 output.write_i32::<LE>(self.victory_score)?;
920 output.write_i32::<LE>(self.victory_time)?;
921 }
922
923 assert_eq!(self.diplomacy.len(), 16);
924 for player_diplomacy in &self.diplomacy {
925 assert_eq!(player_diplomacy.len(), 16);
926 for stance in player_diplomacy {
927 output.write_i32::<LE>((*stance).into())?;
928 }
929 }
930
931 assert_eq!(self.legacy_victory_info.len(), 16);
932 for list in &self.legacy_victory_info {
933 for entry in list {
934 entry.write_to(&mut output)?;
935 }
936 }
937
938 if version >= 1.02 {
939 output.write_i32::<LE>(-99)?;
940 }
941
942 for value in &self.allied_victory {
943 output.write_i32::<LE>(*value)?;
944 }
945
946 if version >= 1.24 {
947 output.write_i8(if self.teams_locked { 1 } else { 0 })?;
948 output.write_i8(if self.can_change_teams { 1 } else { 0 })?;
949 output.write_i8(if self.random_start_locations { 1 } else { 0 })?;
950 output.write_u8(self.max_teams)?;
951 } else if f32_eq!(version, 1.23) {
952 output.write_i32::<LE>(if self.teams_locked { 1 } else { 0 })?;
953 }
954
955 if version >= 1.28 {
956 for num in &self.num_disabled_techs {
957 output.write_i32::<LE>(*num)?;
958 }
959 for (player_disabled_techs, &num) in self
960 .disabled_techs
961 .iter()
962 .zip(self.num_disabled_techs.iter())
963 {
964 for i in 0..num as usize {
965 output.write_i32::<LE>(*player_disabled_techs.get(i).unwrap_or(&-1))?;
966 }
967 }
968
969 for num in &self.num_disabled_units {
970 output.write_i32::<LE>(*num)?;
971 }
972 for (player_disabled_units, &num) in self
973 .disabled_units
974 .iter()
975 .zip(self.num_disabled_units.iter())
976 {
977 for i in 0..num as usize {
978 output.write_i32::<LE>(*player_disabled_units.get(i).unwrap_or(&-1))?;
979 }
980 }
981
982 for num in &self.num_disabled_buildings {
983 output.write_i32::<LE>(*num)?;
984 }
985 for (player_disabled_buildings, &num) in self
986 .disabled_buildings
987 .iter()
988 .zip(self.num_disabled_buildings.iter())
989 {
990 for i in 0..num as usize {
991 output.write_i32::<LE>(*player_disabled_buildings.get(i).unwrap_or(&-1))?;
992 }
993 }
994 } else if version >= 1.18 {
995 let max_disabled_buildings = if version >= 1.25 { 30 } else { 20 };
996 let most = *self.num_disabled_buildings.iter().max().unwrap_or(&0);
997 if most > max_disabled_buildings {
998 return Err(Error::TooManyDisabledBuildingsError(
999 most,
1000 max_disabled_buildings,
1001 ));
1002 }
1003
1004 for num in &self.num_disabled_techs {
1005 output.write_i32::<LE>(*num)?;
1006 }
1007 for player_disabled_techs in &self.disabled_techs {
1008 for i in 0..30 {
1009 output.write_i32::<LE>(*player_disabled_techs.get(i).unwrap_or(&-1))?;
1010 }
1011 }
1012
1013 for num in &self.num_disabled_units {
1014 output.write_i32::<LE>(*num)?;
1015 }
1016 for player_disabled_units in &self.disabled_units {
1017 for i in 0..30 {
1018 output.write_i32::<LE>(*player_disabled_units.get(i).unwrap_or(&-1))?;
1019 }
1020 }
1021
1022 for num in &self.num_disabled_buildings {
1023 output.write_i32::<LE>(*num)?;
1024 }
1025 for player_disabled_buildings in &self.disabled_buildings {
1026 for i in 0..max_disabled_buildings as usize {
1027 output.write_i32::<LE>(*player_disabled_buildings.get(i).unwrap_or(&-1))?;
1028 }
1029 }
1030 } else if version > 1.03 {
1031 let most = *self.num_disabled_techs.iter().max().unwrap_or(&0);
1032 if most > 20 {
1033 return Err(Error::TooManyDisabledTechsError(most));
1034 }
1035 if self.num_disabled_units.iter().any(|&n| n > 0) {
1036 return Err(Error::CannotDisableUnitsError);
1037 }
1038 if self.num_disabled_buildings.iter().any(|&n| n > 0) {
1039 return Err(Error::CannotDisableBuildingsError);
1040 }
1041
1042 for player_disabled_techs in &self.disabled_techs {
1044 for i in 0..20 {
1045 output.write_i32::<LE>(*player_disabled_techs.get(i).unwrap_or(&-1))?;
1046 }
1047 }
1048 } else {
1049 if self.num_disabled_techs.iter().any(|&n| n > 0) {
1051 return Err(Error::CannotDisableTechsError);
1052 }
1053 if self.num_disabled_units.iter().any(|&n| n > 0) {
1054 return Err(Error::CannotDisableUnitsError);
1055 }
1056 if self.num_disabled_buildings.iter().any(|&n| n > 0) {
1057 return Err(Error::CannotDisableBuildingsError);
1058 }
1059 }
1060
1061 if version > 1.04 {
1062 output.write_i32::<LE>(0)?;
1063 }
1064 if version >= 1.12 {
1065 output.write_i32::<LE>(0)?;
1066 output.write_i32::<LE>(if self.all_techs { 1 } else { 0 })?;
1067 }
1068
1069 if version > 1.05 {
1070 for start_age in &self.player_start_ages {
1071 output.write_i32::<LE>(start_age.to_i32(version))?;
1072 }
1073 }
1074
1075 if version >= 1.02 {
1076 output.write_i32::<LE>(-99)?;
1077 }
1078
1079 if version >= 1.19 {
1080 output.write_i32::<LE>(self.view.0)?;
1081 output.write_i32::<LE>(self.view.1)?;
1082 }
1083
1084 if version >= 1.21 {
1085 output.write_i32::<LE>(self.map_type.unwrap_or(-1))?;
1086 }
1087
1088 if version >= 1.24 {
1089 assert_eq!(self.base_priorities.len(), 16);
1090 for priority in &self.base_priorities {
1091 output.write_i8(*priority)?;
1092 }
1093 }
1094
1095 if version >= 1.28 {
1096 output.write_u32::<LE>(num_triggers)?;
1097 output.write_u16::<LE>(0)?;
1098 write_opt_str(&mut output, &self.water_definition)?;
1099 }
1100
1101 if version >= 1.36 {
1102 output.write_u8(0)?;
1103 output.write_u8(0)?;
1104 write_opt_str(&mut output, &self.color_mood)?;
1105 output.write_u8(if self.collide_and_correct { 1 } else { 0 })?;
1106 }
1107 if version >= 1.37 {
1108 output.write_u8(if self.villager_force_drop { 1 } else { 0 })?;
1109 }
1110
1111 Ok(())
1112 }
1113
1114 pub fn version(&self) -> f32 {
1115 self.base.version
1116 }
1117
1118 pub fn description(&self) -> Option<&str> {
1119 self.base.description.as_ref().map(|s| &**s)
1121 }
1122}
1123
1124#[derive(Debug, Clone)]
1125pub struct SCXFormat {
1126 pub(crate) version: SCXVersion,
1128 pub(crate) header: SCXHeader,
1130 pub(crate) next_object_id: i32,
1132 pub(crate) tribe_scen: TribeScen,
1134 pub(crate) map: Map,
1136 world_players: Vec<WorldPlayerData>,
1138 pub(crate) player_objects: Vec<Vec<ScenarioObject>>,
1140 scenario_players: Vec<ScenarioPlayerData>,
1142 pub(crate) triggers: Option<TriggerSystem>,
1144 ai_info: Option<AIInfo>,
1146}
1147
1148impl SCXFormat {
1149 pub fn version(&self) -> VersionBundle {
1151 VersionBundle {
1152 format: self.version,
1153 header: self.header.version,
1154 data: self.tribe_scen.version(),
1155 triggers: self.triggers.as_ref().map(|triggers| triggers.version()),
1156 map: self.map.version(),
1157 ..VersionBundle::aoc()
1158 }
1159 }
1160
1161 fn load_inner(version: SCXVersion, player_version: f32, mut input: impl Read) -> Result<Self> {
1162 let header = SCXHeader::read_from(&mut input, version)?;
1163
1164 let mut input = DeflateDecoder::new(&mut input);
1165 let next_object_id = input.read_i32::<LE>()?;
1166
1167 let tribe_scen = TribeScen::read_from(&mut input)?;
1168
1169 let map = Map::read_from(&mut input)?;
1170
1171 let num_players = input.read_u32::<LE>()?;
1172 log::debug!("number of players: {}", num_players);
1173 let mut world_players = Vec::with_capacity(num_players as usize);
1174 for _ in 1..num_players {
1175 world_players.push(WorldPlayerData::read_from(&mut input, player_version)?);
1176 }
1177
1178 fn read_scenario_players(
1179 mut input: impl Read,
1180 player_version: f32,
1181 ) -> Result<Vec<ScenarioPlayerData>> {
1182 let num = input.read_u32::<LE>()?;
1183 let mut players = Vec::with_capacity(num as usize);
1184 log::debug!("number of scenario players: {}", num);
1185 for _ in 1..num {
1186 players.push(ScenarioPlayerData::read_from(&mut input, player_version)?);
1187 }
1188 Ok(players)
1189 }
1190
1191 fn read_player_objects(
1192 mut input: impl Read,
1193 num_players: u32,
1194 version: SCXVersion,
1195 ) -> Result<Vec<Vec<ScenarioObject>>> {
1196 let mut player_objects = Vec::with_capacity(num_players as usize);
1197 for _ in 0..num_players {
1198 let num_objects = input.read_u32::<LE>()?;
1199 let mut objects = Vec::with_capacity(num_objects as usize);
1200 log::debug!("number of objects: {}", num_objects);
1201 for _ in 0..num_objects {
1202 objects.push(ScenarioObject::read_from(&mut input, version)?);
1203 }
1204 player_objects.push(objects);
1205 }
1206 Ok(player_objects)
1207 }
1208
1209 let (scenario_players, player_objects) = if version >= SCXVersion(*b"1.36") {
1211 let players = read_scenario_players(&mut input, player_version)?;
1212 let objects = read_player_objects(&mut input, num_players, version)?;
1213 (players, objects)
1214 } else {
1215 let objects = read_player_objects(&mut input, num_players, version)?;
1216 let players = read_scenario_players(&mut input, player_version)?;
1217 (players, objects)
1218 };
1219
1220 let triggers = if version < SCXVersion(*b"1.14") {
1221 None
1222 } else {
1223 Some(TriggerSystem::read_from(&mut input)?)
1224 };
1225
1226 let ai_info = if version > SCXVersion(*b"1.17") && version < SCXVersion(*b"2.00") {
1227 AIInfo::read_from(&mut input)?
1228 } else {
1229 None
1230 };
1231
1232 Ok(SCXFormat {
1233 version,
1234 header,
1235 next_object_id,
1236 tribe_scen,
1237 map,
1238 world_players,
1239 player_objects,
1240 scenario_players,
1241 triggers,
1242 ai_info,
1243 })
1244 }
1245
1246 pub fn load_scenario(mut input: impl Read) -> Result<Self> {
1247 let mut format_version = [0; 4];
1248 input.read_exact(&mut format_version)?;
1249 let format_version = SCXVersion(format_version);
1250 if let Some(player_version) = format_version.to_player_version() {
1251 Self::load_inner(format_version, player_version, input)
1252 } else {
1253 Err(Error::UnsupportedFormatVersionError(format_version))
1254 }
1255 }
1256
1257 fn write_player_objects(
1258 &self,
1259 mut output: impl Write,
1260 format_version: SCXVersion,
1261 ) -> Result<()> {
1262 for objects in &self.player_objects {
1263 output.write_i32::<LE>(objects.len() as i32)?;
1264 for object in objects {
1265 object.write_to(&mut output, format_version)?;
1266 }
1267 }
1268 Ok(())
1269 }
1270
1271 fn write_scenario_players(
1272 &self,
1273 mut output: impl Write,
1274 player_version: f32,
1275 victory_version: f32,
1276 ) -> Result<()> {
1277 output.write_i32::<LE>(self.scenario_players.len() as i32 + 1)?;
1278 for player in &self.scenario_players {
1279 player.write_to(&mut output, player_version, victory_version)?;
1280 }
1281 Ok(())
1282 }
1283
1284 pub fn write_to(&self, mut output: impl Write, version: &VersionBundle) -> Result<()> {
1285 let player_version = match version.format.to_player_version() {
1286 Some(v) => v,
1287 None => return Err(Error::UnsupportedFormatVersionError(version.format)),
1288 };
1289
1290 output.write_all(version.format.as_bytes())?;
1291 self.header
1292 .write_to(&mut output, version.format, version.header)?;
1293
1294 let mut output = DeflateEncoder::new(output, Compression::default());
1295 output.write_i32::<LE>(self.next_object_id)?;
1296
1297 let num_triggers = self
1298 .triggers
1299 .as_ref()
1300 .map(|trigger_system| trigger_system.num_triggers())
1301 .unwrap_or(0);
1302 self.tribe_scen
1303 .write_to(&mut output, version.data, num_triggers)?;
1304 self.map.write_to(&mut output, version.map)?;
1305
1306 output.write_i32::<LE>(self.player_objects.len() as i32)?;
1307 for player in &self.world_players {
1308 player.write_to(&mut output, player_version)?;
1309 }
1310
1311 if version.format >= SCXVersion(*b"1.36") {
1312 self.write_scenario_players(&mut output, player_version, version.victory)?;
1313 self.write_player_objects(&mut output, version.format)?;
1314 } else {
1315 self.write_player_objects(&mut output, version.format)?;
1316 self.write_scenario_players(&mut output, player_version, version.victory)?;
1317 }
1318
1319 if version.format > SCXVersion(*b"1.13") {
1320 let def = TriggerSystem::default();
1321 let triggers = match self.triggers {
1322 Some(ref tr) => tr,
1323 None => &def,
1324 };
1325 triggers.write_to(&mut output, version.triggers.unwrap_or(1.6))?;
1326 }
1327
1328 if version.format > SCXVersion(*b"1.17") && version.format < SCXVersion(*b"2.00") {
1329 let def = AIInfo::default();
1330 let ai_info = match self.ai_info {
1331 Some(ref ai) => ai,
1332 None => &def,
1333 };
1334 ai_info.write_to(&mut output)?;
1335 }
1336
1337 output.finish()?;
1338
1339 Ok(())
1340 }
1341
1342 pub fn mod_name(&self) -> Option<&str> {
1346 self.tribe_scen.base.player_names[9]
1347 .as_ref()
1348 .map(|string| string.as_str())
1349 }
1350
1351 #[cfg(test)]
1355 pub fn hash(&self) -> u64 {
1356 use std::{
1357 collections::hash_map::DefaultHasher,
1358 hash::{Hash, Hasher},
1359 };
1360 let mut hasher = DefaultHasher::new();
1361 format!("{:#?}", self).hash(&mut hasher);
1362 hasher.finish()
1363 }
1364}
1365
1366fn write_opt_string_key(mut output: impl Write, opt_key: &Option<StringKey>) -> Result<()> {
1367 output.write_u32::<LE>(if let Some(key) = opt_key {
1368 key.try_into()
1369 .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?
1370 } else {
1371 0xFFFF_FFFF
1372 })?;
1373 Ok(())
1374}
1375
1376#[cfg(test)]
1377mod tests {
1378 use super::SCXFormat;
1379 use crate::{Result, VersionBundle};
1380 use std::fs::File;
1381 use std::io::{Cursor, ErrorKind, Read};
1382
1383 fn save_and_load(format: &SCXFormat, as_version: VersionBundle) -> Result<SCXFormat> {
1384 let mut out = vec![];
1385 format.write_to(&mut out, &as_version)?;
1386
1387 let mut f = Cursor::new(out);
1388 let scx = SCXFormat::load_scenario(&mut f)?;
1389 assert_consumed(f);
1390 Ok(scx)
1391 }
1392
1393 fn assert_consumed(mut input: impl Read) {
1394 let byte = &mut [0];
1395 match input.read_exact(byte) {
1396 Err(err) if err.kind() == ErrorKind::UnexpectedEof => (),
1397 Err(err) => panic!("{}", err),
1398 Ok(_) => {
1399 let mut trailing_data = vec![byte[0]];
1400 input.read_to_end(&mut trailing_data).unwrap();
1401 panic!("data left in buffer ({}): {:?}", trailing_data.len(), {
1402 trailing_data.truncate(32);
1403 trailing_data
1404 });
1405 }
1406 }
1407 }
1408
1409 #[test]
1411 fn oldest_aoe1_scn_on_aoeheaven() {
1412 let mut f = File::open("test/scenarios/ The Destruction of Rome.scn").unwrap();
1413 let format = SCXFormat::load_scenario(&mut f).expect("failed to read");
1414 assert_consumed(f);
1415 let mut out = vec![];
1416 format
1417 .write_to(&mut out, &format.version())
1418 .expect("failed to write");
1419 }
1420
1421 #[test]
1422 fn aoe1_beta_scn_reserialize() {
1423 let mut f = File::open("test/scenarios/Dawn of a New Age.scn").unwrap();
1424 let format = SCXFormat::load_scenario(&mut f).expect("failed to read");
1425 assert_consumed(f);
1426 let format2 = save_and_load(&format, format.version()).expect("save-and-load failed");
1427
1428 assert_eq!(
1429 format.hash(),
1430 format2.hash(),
1431 "should produce exactly the same scenario"
1432 );
1433 }
1434
1435 #[test]
1436 fn aoe1_beta_scn_to_aoc() {
1437 let mut f = File::open("test/scenarios/Dawn of a New Age.scn").unwrap();
1438 let format = SCXFormat::load_scenario(&mut f).expect("failed to read");
1439 assert_consumed(f);
1440 let format2 = save_and_load(&format, VersionBundle::aoc()).expect("save-and-load failed");
1441
1442 assert_eq!(
1443 format2.version(),
1444 VersionBundle::aoc(),
1445 "should have converted to AoC versions"
1446 );
1447 }
1448
1449 #[test]
1451 fn aoe1_trial_scn() {
1452 let mut f = File::open("test/scenarios/Bronze Age Art of War.scn").unwrap();
1453 let format = SCXFormat::load_scenario(&mut f).expect("failed to read");
1454 assert_consumed(f);
1455 let mut out = vec![];
1456 format
1457 .write_to(&mut out, &format.version())
1458 .expect("failed to write");
1459 }
1460
1461 #[test]
1463 fn aoe1_ppc_trial_scn() {
1464 let mut f = File::open("test/scenarios/CEASAR.scn").unwrap();
1465 let format = SCXFormat::load_scenario(&mut f).expect("failed to read");
1466 assert_consumed(f);
1467 let mut out = vec![];
1468 format
1469 .write_to(&mut out, &format.version())
1470 .expect("failed to write");
1471 }
1472
1473 #[test]
1475 fn aoe1_scn() {
1476 let mut f = File::open("test/scenarios/A New Emporer.scn").unwrap();
1477 let format = SCXFormat::load_scenario(&mut f).expect("failed to read");
1478 assert_consumed(f);
1479 let mut out = vec![];
1480 format
1481 .write_to(&mut out, &format.version())
1482 .expect("failed to write");
1483 }
1484
1485 #[test]
1487 fn aoe1_ror_scx() {
1488 let mut f = File::open("test/scenarios/Jeremiah Johnson (Update).scx").unwrap();
1489 let format = SCXFormat::load_scenario(&mut f).expect("failed to read");
1490 assert_consumed(f);
1491 let mut out = vec![];
1492 format
1493 .write_to(&mut out, &format.version())
1494 .expect("failed to write");
1495 }
1496
1497 #[test]
1498 fn aoe1_ror_to_aoc() -> Result<()> {
1499 let mut f = File::open("test/scenarios/El advenimiento de los hunos_.scx")?;
1500 let format = SCXFormat::load_scenario(&mut f)?;
1501 assert_consumed(f);
1502 let format2 = save_and_load(&format, VersionBundle::aoc())?;
1503
1504 assert_eq!(
1505 format2.version(),
1506 VersionBundle::aoc(),
1507 "should have converted to AoC versions"
1508 );
1509
1510 Ok(())
1511 }
1512
1513 #[test]
1515 fn oldest_aok_scn_on_aokheaven() {
1516 let mut f = File::open("test/scenarios/CAMELOT.SCN").unwrap();
1517 let format = SCXFormat::load_scenario(&mut f).expect("failed to read");
1518 assert_consumed(f);
1519 let mut out = vec![];
1520 format
1521 .write_to(&mut out, &format.version())
1522 .expect("failed to write");
1523 }
1524
1525 #[test]
1526 fn aoc_scx() {
1527 let mut f = File::open("test/scenarios/Age of Heroes b1-3-5.scx").unwrap();
1528 let format = SCXFormat::load_scenario(&mut f).expect("failed to read");
1529 assert_consumed(f);
1530 let mut out = vec![];
1531 format
1532 .write_to(&mut out, &format.version())
1533 .expect("failed to write");
1534 }
1535
1536 #[test]
1537 fn hd_aoe2scenario() {
1538 let mut f = File::open("test/scenarios/Year_of_the_Pig.aoe2scenario").unwrap();
1539 let format = SCXFormat::load_scenario(&mut f).expect("failed to read");
1540 assert_consumed(f);
1541 let format2 = save_and_load(&format, format.version()).expect("save-and-load failed");
1542
1543 assert_eq!(
1544 format.hash(),
1545 format2.hash(),
1546 "should produce exactly the same scenario"
1547 );
1548 }
1549
1550 #[test]
1551 fn hd_scx2() {
1552 let mut f = File::open("test/scenarios/real_world_amazon.scx").unwrap();
1553 let format = SCXFormat::load_scenario(&mut f).expect("failed to read");
1554 assert_consumed(f);
1555 let mut out = vec![];
1556 format
1557 .write_to(&mut out, &format.version())
1558 .expect("failed to write");
1559 }
1560
1561 #[test]
1566 #[ignore]
1567 fn aoe_de_scn() {
1568 let mut f = File::open("test/scenarios/Corlis.aoescn").unwrap();
1569 let format = SCXFormat::load_scenario(&mut f).expect("failed to read");
1570 assert_consumed(f);
1571 let mut out = vec![];
1572 format
1573 .write_to(&mut out, &format.version())
1574 .expect("failed to write");
1575 }
1576
1577 #[test]
1581 fn aoe_de2_1_36() {
1582 let mut f = File::open("test/scenarios/Hotkey Trainer Buildings.aoe2scenario").unwrap();
1583 let format = SCXFormat::load_scenario(&mut f).expect("failed to read");
1584 assert_consumed(f);
1585 let format2 = save_and_load(&format, format.version()).expect("save-and-load failed");
1586 assert_eq!(
1587 format.hash(),
1588 format2.hash(),
1589 "should produce exactly the same scenario"
1590 );
1591 }
1592
1593 #[test]
1596 fn aoe_de2_1_37() {
1597 let mut f = File::open("test/scenarios/layertest.aoe2scenario").unwrap();
1598 let format = SCXFormat::load_scenario(&mut f).expect("failed to read");
1599 assert_consumed(f);
1600 let format2 = save_and_load(&format, format.version()).expect("save-and-load failed");
1601 assert_eq!(
1602 format.hash(),
1603 format2.hash(),
1604 "should produce exactly the same scenario"
1605 );
1606 }
1607}