1use bytes::{Buf, BufMut, BytesMut};
4use ordered_hash_map::OrderedHashMap;
5
6use std::collections::HashSet;
7use std::convert::TryFrom;
8use std::io::{BufRead, Cursor, Read};
9
10use crate::parser_builder::ParserBuilder;
11use crate::reader::{ReplayBufReadExt, ReplayReadError, ReplayReadExt, ReplayResult};
12use crate::replay::*;
13use crate::version::{Command, CommandId, Version};
14
15#[derive(Debug)]
17pub struct ParserOptions<V: Version> {
18 pub commands: HashSet<V::CommandId>,
20 pub limit: Option<usize>,
22 pub save_commands: bool,
24 pub stop_on_desync: bool,
26}
27
28impl<V: Version> Clone for ParserOptions<V> {
30 fn clone(&self) -> Self {
31 Self {
32 commands: self.commands.clone(),
33 limit: self.limit.clone(),
34 save_commands: self.save_commands.clone(),
35 stop_on_desync: self.stop_on_desync.clone(),
36 }
37 }
38}
39
40#[derive(Debug)]
56pub struct Parser<V: Version> {
57 options: ParserOptions<V>,
58}
59
60impl<V: Version> Parser<V> {
61 pub fn new() -> Parser<V> {
65 ParserBuilder::new().commands_default().build()
66 }
67
68 pub fn with_options(options: ParserOptions<V>) -> Parser<V> {
70 Parser { options }
71 }
72
73 pub fn new_stream(&self) -> StreamParser<V> {
75 StreamParser::with_options(self.options.clone())
76 }
77
78 pub fn parse(&self, reader: &mut (impl Read + BufRead)) -> ReplayResult<Replay<V>> {
80 self.parse_with_callback(reader, V::Command::process_command)
81 }
82
83 pub fn parse_with_callback(
108 &self,
109 reader: &mut (impl Read + BufRead),
110 callback: impl Fn(&mut SimData, &V::Command) -> ReplayResult<()>,
111 ) -> ReplayResult<Replay<V>> {
112 let mut buf = Vec::new();
113 Ok(Replay {
114 header: parse_header_with_buf(reader, &mut buf)?,
115 body: parse_body_with_callback(reader, &self.options, callback, &mut buf)?,
116 })
117 }
118
119 pub fn parse_header(&self, reader: &mut (impl Read + BufRead)) -> ReplayResult<ReplayHeader> {
121 Ok(parse_header(reader)?)
122 }
123
124 pub fn parse_body(&self, reader: &mut impl Read) -> ReplayResult<ReplayBody<V>> {
127 self.parse_body_with_callback(reader, V::Command::process_command)
128 }
129
130 pub fn parse_body_with_callback(
136 &self,
137 reader: &mut impl Read,
138 callback: impl Fn(&mut SimData, &V::Command) -> ReplayResult<()>,
139 ) -> ReplayResult<ReplayBody<V>> {
140 parse_body_with_callback(reader, &self.options, callback, &mut vec![])
141 }
142}
143
144#[derive(Debug)]
168pub struct StreamParser<V: Version> {
169 options: ParserOptions<V>,
170 buffer: BytesMut,
172 reuse_buf: Vec<u8>,
174 header: Option<ReplayHeader>,
175 body: Option<ReplayBody<V>>,
176}
177
178impl<V: Version> StreamParser<V> {
179 pub fn new() -> StreamParser<V> {
182 ParserBuilder::new().commands_default().build_stream()
183 }
184
185 pub fn with_options(options: ParserOptions<V>) -> StreamParser<V> {
187 StreamParser {
188 options,
189 buffer: BytesMut::new(),
190 reuse_buf: Vec::new(),
191 header: None,
192 body: None,
193 }
194 }
195
196 pub fn header(&self) -> Option<&ReplayHeader> {
198 self.header.as_ref()
199 }
200
201 pub fn body(&self) -> Option<&ReplayBody<V>> {
203 self.body.as_ref()
204 }
205
206 pub fn feed(&mut self, data: &[u8]) {
220 self.buffer.extend_from_slice(data)
221 }
222
223 pub fn feed_reader(&mut self, reader: &mut impl Read, max: usize) -> std::io::Result<usize> {
243 self.buffer.reserve(max);
245 let uninit_slice = self.buffer.chunk_mut();
246
247 unsafe {
248 let buf = std::slice::from_raw_parts_mut(uninit_slice.as_mut_ptr(), max);
249 buf.fill(0);
252
253 let n = reader.read(buf)?;
254
255 self.buffer.advance_mut(n);
256 Ok(n)
257 }
258 }
259
260 pub fn parse(&mut self) -> ReplayResult<()> {
268 if self.header.is_none() {
269 self.parse_header()
270 } else {
271 self.parse_body()
272 }
273 }
274
275 pub fn parse_header(&mut self) -> ReplayResult<()> {
283 let mut cur = Cursor::new(&self.buffer[..]);
284 let result = parse_header_with_buf(&mut cur, &mut self.reuse_buf);
285
286 let result = match result {
287 Err(ReplayReadError::IO(_)) => {
288 return Err(result.unwrap_err());
292 }
293 Ok(header) => {
294 self.header.replace(header);
295 Ok(())
296 }
297 err => Err(err.unwrap_err()),
298 };
299
300 let n = cur.position() as usize;
301 self.buffer.advance(n);
302
303 return result;
304 }
305
306 pub fn parse_body(&mut self) -> ReplayResult<()> {
314 self.parse_body_with_callback(V::Command::process_command)
315 }
316
317 pub fn parse_body_with_callback(
320 &mut self,
321 callback: impl Fn(&mut SimData, &V::Command) -> ReplayResult<()>,
322 ) -> ReplayResult<()> {
323 self.body.get_or_insert_with(|| ReplayBody {
324 commands: Vec::new(),
325 sim: SimData::new(),
326 });
327
328 while self.options.limit.is_none()
329 || self.body.as_ref().unwrap().commands.len() < self.options.limit.unwrap()
330 {
331 if !self.has_frame()? {
333 return Err(ReplayReadError::IO(std::io::Error::new(
334 std::io::ErrorKind::UnexpectedEof,
335 "partial frame",
336 )));
337 }
338 match self.parse_command() {
339 Ok(Some(cmd)) => {
340 let result = callback(&mut self.body.as_mut().unwrap().sim, &cmd);
341
342 if let Err(ReplayReadError::Desynced(_)) = result {
344 if self.options.stop_on_desync {
345 result?;
346 }
347 } else {
348 result?;
349 }
350
351 if self.options.save_commands {
352 self.body.as_mut().unwrap().commands.push(cmd);
353 }
354
355 if self.buffer.is_empty() {
356 return Ok(());
357 }
358 }
359 Ok(None) => {}
360 err => return Err(err.unwrap_err()),
361 }
362 }
363
364 loop {
366 self.parse_command_frame().map(|_| ())?;
367 if self.buffer.is_empty() {
368 return Ok(());
369 }
370 }
371 }
372
373 pub fn has_frame(&self) -> ReplayResult<bool> {
395 crate::scfa::bytes::has_frame(&self.buffer[..])
397 }
398
399 pub fn parse_command(&mut self) -> ReplayResult<Option<V::Command>> {
427 let mut cur = Cursor::new(&self.buffer[..]);
429 let result = parse_command(&mut cur, &self.options, &mut self.reuse_buf);
430
431 if let Err(ReplayReadError::IO(_)) = &result {
432 return result;
436 } else {
437 let n = cur.position() as usize;
438 self.buffer.advance(n);
439
440 return result;
441 }
442 }
443
444 pub fn parse_command_frame(&mut self) -> ReplayResult<ReplayCommandFrame<V>> {
450 let mut cur = Cursor::new(&self.buffer[..]);
451 let result = parse_command_frame(&mut cur);
452
453 if let Err(ReplayReadError::IO(_)) = &result {
454 return result;
455 } else {
456 let n = cur.position() as usize;
457 self.buffer.advance(n);
458
459 return result;
460 }
461 }
462
463 pub fn can_finalize(&self) -> bool {
466 self.buffer.is_empty()
467 }
468
469 pub fn finalize(&mut self) -> ReplayResult<StreamReplay<V>> {
474 match self.buffer.is_empty() {
475 true => Ok(self.force_finalize()),
476 false => Err(ReplayReadError::IO(std::io::Error::new(
477 std::io::ErrorKind::UnexpectedEof,
478 "replay finalized before all data was consumed",
479 ))),
480 }
481 }
482
483 pub fn force_finalize(&mut self) -> StreamReplay<V> {
485 let result = StreamReplay {
486 header: self.header.take(),
487 body: self.body.take(),
488 };
489 self.buffer.clear();
490
491 result
492 }
493
494 pub fn reset(&mut self) {
500 self.header.take();
501 self.body.take();
502 self.buffer.clear();
503 }
504}
505
506#[derive(Debug)]
508pub struct StreamReplay<V: Version> {
509 pub header: Option<ReplayHeader>,
510 pub body: Option<ReplayBody<V>>,
511}
512
513impl<V: Version> StreamReplay<V> {
514 pub fn unwrap(self) -> Replay<V> {
516 Replay {
517 header: self.header.unwrap(),
518 body: self.body.unwrap(),
519 }
520 }
521}
522
523pub fn parse_body_ticks<V: Version>(reader: &mut impl Read) -> ReplayResult<u32> {
526 let mut ticks = 0;
527 let mut buf = vec![0u8; 4096];
528
529 loop {
531 unsafe {
532 ticks += match parse_body_ticks_command::<V>(reader, buf.as_mut_slice()) {
533 Err(ReplayReadError::IO(err))
534 if err.kind() == std::io::ErrorKind::UnexpectedEof =>
535 {
536 return Ok(ticks);
537 }
538 other => other?,
539 }
540 }
541 }
542}
543
544#[inline]
546unsafe fn parse_body_ticks_command<V: Version>(
547 reader: &mut impl Read,
548 buf: &mut [u8],
549) -> ReplayResult<u32> {
550 debug_assert!(buf.len() >= 4);
551
552 let (command_id, size) = parse_command_frame_header::<V>(reader)?;
554
555 if command_id != V::CommandId::advance() {
556 reader.skip_buf(size as usize - 3, buf)?;
557 return Ok(0);
558 }
559 if size != 7 {
560 reader.skip_buf(size as usize - 3, buf)?;
561 return Err(ReplayReadError::Malformed("invalid command size"));
562 }
563 reader.read_exact(buf.get_unchecked_mut(0..4))?;
565 let ticks = buf.get_unchecked(0..4).read_u32_le().unwrap();
566
567 Ok(ticks)
568}
569
570pub fn parse_header(reader: &mut (impl Read + BufRead)) -> ReplayResult<ReplayHeader> {
572 let mut buf = Vec::new();
573 parse_header_with_buf(reader, &mut buf)
574}
575
576fn parse_header_with_buf(
577 reader: &mut (impl Read + BufRead),
578 buf: &mut Vec<u8>,
579) -> ReplayResult<ReplayHeader> {
580 let scfa_version = reader.read_string_with_capacity(28)?;
582 reader.read_string()?;
585 let version_and_mapname = reader.read_string()?;
586 let mut version_and_mapname = version_and_mapname.splitn(2, "\r\n");
587 let replay_version = match version_and_mapname.next() {
588 Some(s) => s.to_string(),
589 None => return Err(ReplayReadError::Malformed("missing replay version")),
590 };
591 let map_file = match version_and_mapname.next() {
592 Some(s) => s.to_string(),
593 None => return Err(ReplayReadError::Malformed("missing map name")),
594 };
595 reader.read_string()?;
597
598 let mods_size = reader.read_u32_le()? as usize;
599 reader.read_exact_to_vec(mods_size, buf)?;
600 let mods = (&buf[..mods_size]).read_lua_object()?;
601
602 let scenario_size = reader.read_u32_le()? as usize;
603 reader.read_exact_to_vec(scenario_size, buf)?;
604 let scenario = (&buf[..scenario_size]).read_lua_object()?;
605 let num_sources = reader.read_u8()? as usize;
606
607 let mut players = OrderedHashMap::new();
608 for _ in 0..num_sources {
609 let name = reader.read_string()?;
610 let player_id = reader.read_i32_le()?;
611 players.insert(name, player_id);
612 }
613
614 let cheats_enabled = reader.read_bool()?;
615 let army_count = reader.read_u8()? as usize;
616
617 let mut armies = OrderedHashMap::new();
618 for _ in 0..army_count {
619 let player_data_size = reader.read_u32_le()? as usize;
620 reader.read_exact_to_vec(player_data_size, buf)?;
621 let player_data = (&buf[..player_data_size]).read_lua_object()?;
622 let player_source = reader.read_u8()?;
623 armies.insert(player_source, player_data);
624
625 if player_source != 255 {
626 reader.skip(1)?;
627 }
628 }
629
630 let seed = reader.read_u32_le()?;
631
632 Ok(ReplayHeader {
633 scfa_version,
634 replay_version,
635 map_file,
636 mods,
637 scenario,
638 players,
639 cheats_enabled,
640 armies,
641 seed,
642 })
643}
644
645fn parse_body_with_callback<V: Version>(
647 reader: &mut impl Read,
648 options: &ParserOptions<V>,
649 callback: impl Fn(&mut SimData, &V::Command) -> ReplayResult<()>,
650 buf: &mut Vec<u8>,
651) -> ReplayResult<ReplayBody<V>> {
652 let mut commands = Vec::new();
653 let mut sim = SimData::new();
654
655 while options.limit.is_none() || commands.len() < options.limit.unwrap() {
656 match parse_command(reader, options, buf) {
657 Err(ReplayReadError::IO(ref e)) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
658 break
659 }
660 Err(e) => return Err(e),
661 Ok(opt) => {
662 if let Some(cmd) = opt {
663 let result = callback(&mut sim, &cmd);
664
665 if let Err(ReplayReadError::Desynced(_)) = result {
667 if options.stop_on_desync {
668 result?;
669 }
670 } else {
671 result?;
672 }
673
674 if options.save_commands {
675 commands.push(cmd);
676 }
677 }
678 }
679 }
680 }
681
682 Ok(ReplayBody { commands, sim })
683}
684
685fn parse_command_frame<V: Version>(
686 reader: &mut (impl Read + BufRead),
687) -> ReplayResult<ReplayCommandFrame<V>> {
688 let (command_id, size) = parse_command_frame_header::<V>(reader)?;
689 let data = reader.vec_read_exact((size - 3) as usize)?;
690
691 Ok(ReplayCommandFrame {
692 command_id,
693 size,
694 data,
695 })
696}
697
698fn parse_command_frame_header<V: Version>(
699 reader: &mut impl Read,
700) -> ReplayResult<(V::CommandId, u16)> {
701 let command_id = reader.read_u8()?;
702 let command_id = match V::CommandId::try_from(command_id) {
703 Err(_) => return Err(ReplayReadError::Malformed("invalid command")),
704 Ok(c) => c,
705 };
706
707 let size = reader.read_u16_le()?;
708 if size < 3 {
709 return Err(ReplayReadError::Malformed("invalid command size"));
710 }
711
712 Ok((command_id, size))
713}
714
715pub fn parse_command<V: Version>(
730 reader: &mut impl Read,
731 ctx: &ParserOptions<V>,
732 buf: &mut Vec<u8>,
733) -> ReplayResult<Option<V::Command>> {
734 let (command_id, size) = parse_command_frame_header::<V>(reader)?;
735 let len = size as usize - 3;
736
737 reader.read_exact_to_vec(len, buf)?;
739
740 if !ctx.commands.contains(&command_id) {
741 return Ok(None);
742 }
743
744 Ok(Some(V::Command::parse_command_data(
745 command_id.as_u8(),
746 &buf[..len],
747 )?))
748}
749
750#[cfg(test)]
751mod tests {
752 use super::*;
753 use pretty_assertions::assert_eq;
754
755 use crate::lua::LuaObject;
756 use crate::scfa::replay::*;
757 use crate::scfa::SCFA;
758 use std::io::Write;
759
760 #[test]
761 fn parse_issue_command() {
762 let mut data: &[u8] = &[
763 0x0C, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
765 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0x00, 0x60, 0x28,
767 0x44, 0x00, 0x70, 0x95, 0x41, 0x00, 0x40, 0xA9, 0x43, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
769 0x75, 0x72, 0x62, 0x30, 0x31, 0x30, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
771 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x01, 0x01, 0x04, 0x00, 0x01,
773 ];
774 let ctx = ParserOptions::<SCFA> {
775 commands: [ReplayCommandId::try_from(replay_command::ISSUE_COMMAND).unwrap()]
776 .iter()
777 .cloned()
778 .collect(),
779 limit: None,
780 save_commands: true,
781 stop_on_desync: true,
782 };
783
784 let issue_command = parse_command(&mut data, &ctx, &mut vec![])
785 .unwrap()
786 .unwrap();
787
788 let command = GameCommand {
790 entity_ids: vec![0],
791 id: 0,
792 coordinated_attack_cmd_id: -1,
793 type_: game_command::BUILD_MOBILE,
794 arg2: -1,
795 target: Target::Position(Position {
796 x: 0.0,
797 y: 0.0,
798 z: 0.0,
799 }),
800 arg3: 0,
801 formation: None,
802 blueprint: "urb0101".to_string(),
803 arg4: 0,
804 arg5: 1,
805 arg6: 1,
806 upgrades: LuaObject::Nil,
807 clear_queue: None,
808 };
809 match issue_command {
810 ReplayCommand::IssueCommand(issue_command) => {
811 assert_eq!(command.entity_ids, issue_command.entity_ids);
812 assert_eq!(command.id, issue_command.id);
813 assert_eq!(
814 command.coordinated_attack_cmd_id,
815 issue_command.coordinated_attack_cmd_id
816 );
817 assert_eq!(command.type_, issue_command.type_);
818 assert_eq!(command.arg2, issue_command.arg2);
819 assert_eq!(command.arg3, issue_command.arg3);
820 assert_eq!(command.blueprint, issue_command.blueprint);
821 assert_eq!(command.arg4, issue_command.arg4);
822 assert_eq!(command.arg5, issue_command.arg5);
823 assert_eq!(command.arg6, issue_command.arg6);
824 assert_eq!(command.upgrades, issue_command.upgrades);
825 assert_eq!(command.clear_queue, issue_command.clear_queue);
826 }
827 _ => panic!("Wrong command type produced"),
828 }
829 }
830
831 #[test]
832 fn parse_lua_sim_callback() {
833 let mut data: &[u8] = &[
834 0x16, 0x1A, 0x00, 0x43, 0x6C, 0x65, 0x61, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74,
835 0x73, 0x00, 0x04, 0x05, 0x01, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x40, 0x00,
836 ];
837 let ctx = ParserOptions::<SCFA> {
838 commands: [ReplayCommandId::try_from(replay_command::LUA_SIM_CALLBACK).unwrap()]
839 .iter()
840 .cloned()
841 .collect(),
842 limit: None,
843 save_commands: true,
844 stop_on_desync: true,
845 };
846
847 let command = parse_command(&mut data, &ctx, &mut vec![])
848 .unwrap()
849 .unwrap();
850
851 match command {
852 ReplayCommand::LuaSimCallback {
853 func,
854 args,
855 selection,
856 } => {
857 assert_eq!(func, "ClearTargets");
858 assert!(args.as_hashmap().unwrap().is_empty());
859 assert_eq!(selection, vec![0x4000E0]);
860 }
861 _ => panic!("Wrong command type produced"),
862 }
863 }
864
865 #[test]
866 fn test_parser_init() {
867 let _ = Parser::<SCFA>::new();
868 }
869
870 #[test]
871 fn test_stream_init() {
872 let stream = StreamParser::<SCFA>::new();
873
874 assert_eq!(stream.header().is_none(), true);
875 assert_eq!(stream.body().is_none(), true);
876 }
877
878 #[test]
879 fn test_stream_parse_command() {
880 let mut stream = StreamParser::<SCFA>::new();
881
882 assert_eq!(stream.has_frame().unwrap(), false);
883
884 stream.feed(&[0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00]);
886
887 assert_eq!(stream.has_frame().unwrap(), true);
888 assert_eq!(
889 stream.parse_command().unwrap(),
890 Some(ReplayCommand::Advance { ticks: 1 })
891 );
892 assert_eq!(stream.has_frame().unwrap(), false);
893 }
894
895 #[test]
896 fn test_stream_feed_reader() {
897 let mut stream = StreamParser::<SCFA>::new();
898 let mut data: &[u8] = &[0x0, 0x07];
899
900 assert_eq!(stream.feed_reader(&mut data, 10).unwrap(), 2);
902
903 let mut cur = Cursor::new(vec![0x00, 0x01, 0x00]);
905 assert_eq!(stream.feed_reader(&mut cur, 1).unwrap(), 1);
906 assert_eq!(stream.feed_reader(&mut cur, 10).unwrap(), 2);
907
908 cur.write(&[0x00, 0x00]).unwrap();
910 cur.set_position(cur.position() - 2);
911 assert_eq!(stream.feed_reader(&mut cur, 10).unwrap(), 2);
912
913 assert_eq!(
914 stream.parse_command().unwrap(),
915 Some(ReplayCommand::Advance { ticks: 1 })
916 );
917 }
918}