use crate::utils::cal_board_numbers;
use crate::videos::base_video::{BaseVideo, NewBaseVideo};
use crate::videos::byte_reader::ByteReader;
use crate::videos::types::{ErrReadVideoReason, Event, MouseEvent, VideoActionStateRecorder};
#[cfg(any(feature = "py", feature = "rs"))]
use crate::videos::NewSomeVideo;
use crate::videos::NewSomeVideo2;
pub struct RmvVideo {
pub file_name: String,
pub data: BaseVideo<Vec<Vec<i32>>>,
}
#[cfg(any(feature = "py", feature = "rs"))]
impl NewSomeVideo<&str> for RmvVideo {
fn new(file_name: &str) -> Self {
RmvVideo {
file_name: file_name.to_string(),
data: BaseVideo::<Vec<Vec<i32>>>::new(file_name),
}
}
}
impl NewSomeVideo2<Vec<u8>, &str> for RmvVideo {
fn new(raw_data: Vec<u8>, file_name: &str) -> Self {
RmvVideo {
file_name: file_name.to_string(),
data: BaseVideo::<Vec<Vec<i32>>>::new(raw_data),
}
}
}
impl RmvVideo {
pub fn parse(&mut self) -> Result<(), ErrReadVideoReason> {
match self.data.get_char() {
Ok('*') => {}
Ok(_) => return Err(ErrReadVideoReason::FileIsNotRmv),
Err(_) => return Err(ErrReadVideoReason::FileIsEmpty),
};
match self.data.get_char() {
Ok('r') => {}
_ => return Err(ErrReadVideoReason::FileIsNotRmv),
};
match self.data.get_char() {
Ok('m') => {}
_ => return Err(ErrReadVideoReason::FileIsNotRmv),
};
match self.data.get_char() {
Ok('v') => {}
_ => return Err(ErrReadVideoReason::FileIsNotRmv),
};
let format_version = match self.data.get_u16() {
Ok(format_version) => format_version,
_ => return Err(ErrReadVideoReason::FileIsNotRmv),
};
if format_version == 0 || format_version > 2 {
return Err(ErrReadVideoReason::FileIsNotRmv);
}
let clone_id = if format_version >= 2 {
self.data.get_u8()?
} else { 0 };
let _major_version_of_clone = if format_version >= 2 {
self.data.get_u8()?
} else { 0 };
self.data.offset += 4;
let result_string_size = if format_version == 1 {
self.data.get_u16()?
} else { 0 };
let version_info_size = self.data.get_u16()?;
self.data.offset += 4;
let preflags_size = self.data.get_u16()?;
let properties_size = self.data.get_u16()?;
let _extension_properties_size = if format_version >= 2 {
self.data.get_u16()?
} else { 0 };
self.data.offset += 6;
if format_version == 1 {
self.data.offset += 1;
self.data.static_params.bbbv = 0;
if result_string_size > 35 {
self.data.offset += (result_string_size - 32) as usize;
let mut bbbv: String = "".to_string();
for _ in 0..3 {
let v = self.data.get_char()?;
if v as u8 >= 48 && v as u8 <= 57 {
bbbv.push(v);
}
}
self.data.static_params.bbbv = match bbbv.parse() {
Ok(v) => v,
Err(_) => return Err(ErrReadVideoReason::InvalidParams),
};
self.data.offset += 26;
} else if result_string_size >= 3 {
self.data.offset += result_string_size as usize - 3;
}
self.data.offset += 2;
}
self.data.offset += version_info_size as usize;
let num_player_info = self.data.get_u16()?;
let mut token = vec![];
let mut player_identifier_buffer = vec![];
let mut unique_identifier_buffer = vec![];
let mut country_buffer = vec![];
if num_player_info > 0 {
let name_length = self.data.get_u8()?;
player_identifier_buffer = self.data.get_buffer(name_length)?;
}
if num_player_info > 1 {
let nick_length = self.data.get_u8()?;
unique_identifier_buffer = self.data.get_buffer(nick_length)?;
}
if num_player_info > 2 {
let country_length = self.data.get_u8()?;
country_buffer = self.data.get_buffer(country_length)?;
}
if num_player_info > 3 {
let token_length = self.data.get_u8()?;
token = self.data.get_buffer(token_length as usize)?;
}
let timestamp_boardgen: u32 = self.data.get_u32()?.into();
self.data.width = self.data.get_u8()?.into();
self.data.height = self.data.get_u8()?.into();
self.data.mine_num = self.data.get_u16()?.into();
self.data.board = vec![vec![0; self.data.width]; self.data.height];
for _ in 0..self.data.mine_num {
let c = self.data.get_u8()? as usize;
let d = self.data.get_u8()? as usize;
if c >= self.data.width || d >= self.data.height {
return Err(ErrReadVideoReason::InvalidMinePosition);
}
self.data.board[d][c] = -1;
}
cal_board_numbers(&mut self.data.board);
let mut preflags_items = vec![];
if preflags_size > 0 {
let num_pre_flags = self.data.get_u16()?;
for _ in 0..num_pre_flags {
let c = self.data.get_u8()? as u16;
let d = self.data.get_u8()? as u16;
preflags_items.push((c, d));
}
}
let preflags_items = preflags_items;
let mut square_size = 16u8; let mut utf8 = true; self.data.offset += 1;
self.data.nf = if self.data.get_u8()? == 1 {
true
} else {
false
};
self.data.mode = self.data.get_u8()? as u16;
self.data.level = self.data.get_u8()? + 3;
let mut properties_read = 4;
if format_version >= 2 {
let bbbv_low = self.data.get_u8()? as usize;
let bbbv_high = self.data.get_u8()? as usize;
self.data.static_params.bbbv = bbbv_low + (bbbv_high << 8);
square_size = self.data.get_u8()?;
properties_read += 3;
} else {
utf8 = false;
if properties_size > 4 {
utf8 = if self.data.get_u8()? == 1 {
true
} else {
false
};
properties_read += 1;
}
}
let square_size = square_size;
let utf8 = utf8;
self.data.cell_pixel_size = square_size;
self.data.offset += (properties_size - properties_read) as usize;
let mut clone_name = None;
if format_version >= 2 {
let num_extension_properties = self.data.get_u16()?;
for _ii in 0..num_extension_properties {
let key_size = self.data.get_u8()?;
let key = self.data.get_utf8_string(key_size as usize)?;
let value_size = self.data.get_u8()?;
let value = self.data.get_buffer(value_size as usize)?;
if key == "clone_name" {
clone_name = Some(String::from_utf8(value));
}
}
}
let clone_name = match clone_name {
None => None,
Some(Ok(s)) => Some(s),
Some(Err(_)) => {
return Err(ErrReadVideoReason::Utf8Error);
}
};
if utf8 {
let utf8_errfunc = |_e| ErrReadVideoReason::Utf8Error;
self.data.player_identifier = String::from_utf8(player_identifier_buffer).map_err(utf8_errfunc)?;
self.data.unique_identifier = String::from_utf8(unique_identifier_buffer).map_err(utf8_errfunc)?;
self.data.country = String::from_utf8(country_buffer).map_err(utf8_errfunc)?;
let _ = String::from_utf8(token.clone()).map_err(utf8_errfunc)?;
}
else {
self.data.player_identifier = <BaseVideo<Vec<Vec<i32>>>>::get_unknown_cp_encoding_string_from_buf(player_identifier_buffer)?;
self.data.unique_identifier = <BaseVideo<Vec<Vec<i32>>>>::get_unknown_cp_encoding_string_from_buf(unique_identifier_buffer)?;
self.data.country = <BaseVideo<Vec<Vec<i32>>>>::get_unknown_cp_encoding_string_from_buf(country_buffer)?;
}
let mut first_op_flag = true;
let xoffset = if format_version == 1 {12} else {0};
let yoffset = if format_version == 1 {56} else {0};
let (mut x, mut y, mut time) = (0u16, 0u16, 0u32);
for (c, d) in preflags_items {
self.data
.video_action_state_recorder
.push(VideoActionStateRecorder {
event: Some(Event::Mouse(MouseEvent {
mouse: "pf".to_string(),
x: c * self.data.cell_pixel_size as u16,
y: d * self.data.cell_pixel_size as u16,
})),
..VideoActionStateRecorder::default()
});
}
loop {
let c = self.data.get_u8()?;
if c == 0 {
if format_version >= 2 {
return Err(ErrReadVideoReason::InvalidVideoEvent);
}
self.data.offset += 4;
} else if c <= 7 || (format_version >= 2 && c == 28 && !first_op_flag) {
if c == 28 {
time += self.data.get_u8()? as u32;
let mv = self.data.get_u8()?;
x = x.wrapping_add(((mv >> 4) & 7u8) as u16);
x = x.wrapping_sub(((mv >> 4) & 8u8) as u16);
y = y.wrapping_add((mv & 7u8) as u16);
y = y.wrapping_sub((mv & 8u8) as u16);
} else {
time = self.data.get_u32()? >> 8;
x = (self.data.get_u16()?).wrapping_sub(xoffset);
y = (self.data.get_u16()?).wrapping_sub(yoffset);
}
if c >= 1 {
if x >= self.data.width as u16 * self.data.cell_pixel_size as u16
|| y >= self.data.height as u16 * self.data.cell_pixel_size as u16 {
x = self.data.width as u16 * self.data.cell_pixel_size as u16;
y = self.data.height as u16 * self.data.cell_pixel_size as u16;
}
if first_op_flag {
first_op_flag = false;
self.data
.video_action_state_recorder
.push(VideoActionStateRecorder {
time: time as f64 / 1000.0,
event: Some(Event::Mouse(MouseEvent {
mouse: "lc".to_string(),
x,
y,
})),
..VideoActionStateRecorder::default()
});
}
self.data
.video_action_state_recorder
.push(VideoActionStateRecorder {
time: time as f64 / 1000.0,
event: Some(Event::Mouse(MouseEvent {
mouse: match c {
1 => "mv".to_string(),
2 => "lc".to_string(),
3 => "lr".to_string(),
4 => "rc".to_string(),
5 => "rr".to_string(),
6 => "mc".to_string(),
7 => "mr".to_string(),
28 => "mv".to_string(),
_ => return Err(ErrReadVideoReason::InvalidVideoEvent),
},
x,
y,
})),
..VideoActionStateRecorder::default()
});
}
} else if c == 8 {
return Err(ErrReadVideoReason::InvalidParams);
} else if c <= 14 || (c >= 18 && c <= 27) {
self.data.offset += 2;
} else if c <= 17 {
self.data.is_completed = c == 16;
break;
} else {
return Err(ErrReadVideoReason::InvalidParams);
}
}
self.data
.set_rtime(self.data.video_action_state_recorder.last().unwrap().time)
.unwrap();
self.data.start_time = timestamp_boardgen as u64 * 1000000;
self.data.end_time =
self.data.start_time + (self.data.get_rtime_ms().unwrap() as u64) * 1000;
self.data.software = if format_version == 1 {
"Viennasweeper".to_string()
} else {
match clone_id {
0 => match clone_name {
Some(s) => s,
None => {
return Err(ErrReadVideoReason::InvalidParams)
},
},
1 => "Viennasweeper".to_string(),
_ => "Unknown".to_string(),
}
};
self.data.is_official = self.data.is_completed;
self.data.is_fair = self.data.is_completed;
self.data.can_analyse = true;
return Ok(());
}
}