use super::MscsbFile;
use super::super::{Cmd, Command};
use byteorder::{LittleEndian, BigEndian, WriteBytesExt};
impl MscsbFile {
pub fn write(&self, f: &mut Vec<u8>) {
macro_rules! write {
($e:expr) => {
WriteImpl::write($e, f, false);
}
}
let max_str_len = self.get_max_string_size();
let mut script_data: Vec<u8> = vec![];
let script_offsets = self.generate_script_data(&mut script_data);
write!(&b"\xB2\xAC\xBC\xBA\xE6\x90\x32\x01\xFD\x02\x00\x00\x00\x00\x00\x00"[..]);
write!(0u32);
write!(script_data.len() as u32);
write!(self.entrypoint);
write!(self.scripts.len() as u32);
write!(0x16u32);
write!(max_str_len);
write!(self.strings.len() as u32);
write!(vec![0u8; 8]);
write!(&script_data[..]);
write!(vec![0u8; (0x10 - (f.len() % 0x10)) & 0xF]);
write!(script_offsets);
write!(vec![0u8; (0x10 - (f.len() % 0x10)) & 0xF]);
for string in self.strings.iter() {
write!(string.as_bytes());
write!(0u8);
write!(vec![0u8; max_str_len as usize - (1 + string.as_bytes().len())]);
}
}
fn generate_script_data(&self, f: &mut Vec<u8>) -> Vec<u32> {
macro_rules! write {
($e:expr) => {
WriteImpl::write($e, f, true);
}
}
let mut script_offsets = vec![];
write!(vec![0u8; 0x10]);
for script in self.scripts.iter() {
script_offsets.push(f.len() as u32);
for command in script.commands.iter() {
write!(command);
}
}
script_offsets
}
fn get_max_string_size(&self) -> u32 {
self.strings
.iter()
.map(|s| (s.len() + 0x10) & 0xF)
.max()
.unwrap() as u32
}
}
impl WriteImpl for &Command {
fn write(self, f: &mut Vec<u8>, endian: bool) {
macro_rules! write {
($e:expr) => {
WriteImpl::write($e, f, endian);
}
}
write!(self.cmd.value() & (if self.push_bit {0x80u8} else {0x0u8}));
match self.cmd {
Cmd::Begin { arg_count, var_count } => {
write!(arg_count);
write!(var_count);
},
Cmd::Jump { loc } => {
write!(loc);
},
Cmd::Jump5 { loc } => {
write!(loc);
},
Cmd::PushInt { val } => {
write!(val);
},
Cmd::PushVar { var_type, var_num } => {
write!(var_type);
write!(var_num);
},
Cmd::PushShort { val } => {
write!(val);
},
Cmd::IncI { var_type, var_num } => {
write!(var_type);
write!(var_num);
},
Cmd::DecI { var_type, var_num } => {
write!(var_type);
write!(var_num);
},
Cmd::SetVar { var_type, var_num } => {
write!(var_type);
write!(var_num);
},
Cmd::AddVarBy { var_type, var_num } => {
write!(var_type);
write!(var_num);
},
Cmd::SubVarBy { var_type, var_num } => {
write!(var_type);
write!(var_num);
},
Cmd::MultVarBy { var_type, var_num } => {
write!(var_type);
write!(var_num);
},
Cmd::DivVarBy { var_type, var_num } => {
write!(var_type);
write!(var_num);
},
Cmd::ModVarBy { var_type, var_num } => {
write!(var_type);
write!(var_num);
},
Cmd::AndVarBy { var_type, var_num } => {
write!(var_type);
write!(var_num);
},
Cmd::OrVarBy { var_type, var_num } => {
write!(var_type);
write!(var_num);
},
Cmd::XorVarBy { var_type, var_num } => {
write!(var_type);
write!(var_num);
},
Cmd::PrintF { arg_count } => {
write!(arg_count);
},
Cmd::Sys { arg_count, sys_num } => {
write!(arg_count);
write!(sys_num);
},
Cmd::Try { loc } => {
write!(loc);
},
Cmd::CallFunc { arg_count } => {
write!(arg_count);
},
Cmd::CallFunc2 { arg_count } => {
write!(arg_count);
},
Cmd::CallFunc3 { arg_count } => {
write!(arg_count);
},
Cmd::If { loc } => {
write!(loc);
},
Cmd::IfNot { loc } => {
write!(loc);
},
Cmd::Else { loc } => {
write!(loc);
},
Cmd::IntToFloat { stack_pos } => {
write!(stack_pos);
},
Cmd::FloatToInt { stack_pos } => {
write!(stack_pos);
},
Cmd::IncF { var_type, var_num } => {
write!(var_type);
write!(var_num);
},
Cmd::DecF { var_type, var_num } => {
write!(var_type);
write!(var_num);
},
Cmd::VarSetF { var_type, var_num } => {
write!(var_type);
write!(var_num);
},
Cmd::AddVarByF { var_type, var_num } => {
write!(var_type);
write!(var_num);
},
Cmd::SubVarByF { var_type, var_num } => {
write!(var_type);
write!(var_num);
},
Cmd::MultVarByF { var_type, var_num } => {
write!(var_type);
write!(var_num);
},
Cmd::DivVarByF { var_type, var_num } => {
write!(var_type);
write!(var_num);
},
_ => {}
}
}
}
impl Cmd {
#[allow(unreachable_patterns)]
pub fn value(&self) -> u8 {
match self {
Cmd::Nop => 0,
Cmd::Unk1 => 1,
Cmd::Begin { arg_count: _, var_count: _ } => 2,
Cmd::End => 3,
Cmd::Jump { loc: _ } => 4,
Cmd::Jump5 { loc: _ } => 5,
Cmd::Return6 => 6,
Cmd::Return7 => 7,
Cmd::Return8 => 8,
Cmd::Return9 => 9,
Cmd::PushInt { val: _ } => 0xA,
Cmd::PushVar { var_type: _, var_num: _ } => 0xB,
Cmd::ErrorC => 0xC,
Cmd::PushShort { val: _ } => 0xD,
Cmd::AddI => 0xE,
Cmd::SubI => 0xF,
Cmd::MultI => 0x10,
Cmd::DivI => 0x11,
Cmd::ModI => 0x12,
Cmd::NegI => 0x13,
Cmd::IncI { var_type: _, var_num: _ } => 0x14,
Cmd::DecI { var_type: _, var_num: _ } => 0x15,
Cmd::AndI => 0x16,
Cmd::OrI => 0x17,
Cmd::NotI => 0x18,
Cmd::XorI => 0x19,
Cmd::ShiftL => 0x1A,
Cmd::ShiftR => 0x1B,
Cmd::SetVar { var_type: _, var_num: _ } => 0x1C,
Cmd::AddVarBy { var_type: _, var_num: _ } => 0x1D,
Cmd::SubVarBy { var_type: _, var_num: _ } => 0x1E,
Cmd::MultVarBy { var_type: _, var_num: _ } => 0x1F,
Cmd::DivVarBy { var_type: _, var_num: _ } => 0x20,
Cmd::ModVarBy { var_type: _, var_num: _ } => 0x21,
Cmd::AndVarBy { var_type: _, var_num: _ } => 0x22,
Cmd::OrVarBy { var_type: _, var_num: _ } => 0x23,
Cmd::XorVarBy { var_type: _, var_num: _ } => 0x24,
Cmd::Equals => 0x25,
Cmd::NotEquals => 0x26,
Cmd::LessThan => 0x27,
Cmd::LessOrEqual => 0x28,
Cmd::Greater => 0x29,
Cmd::GreaterOrEqual => 0x2A,
Cmd::Not => 0x2B,
Cmd::PrintF { arg_count: _ } => 0x2C,
Cmd::Sys { arg_count: _, sys_num: _ } => 0x2D,
Cmd::Try { loc: _ } => 0x2E,
Cmd::CallFunc { arg_count: _ } => 0x2F,
Cmd::CallFunc2 { arg_count: _ } => 0x2F,
Cmd::CallFunc3 { arg_count: _ } => 0x2F,
Cmd::Push => 0x32,
Cmd::Pop => 0x33,
Cmd::If { loc: _ } => 0x34,
Cmd::IfNot { loc: _ } => 0x35,
Cmd::Else { loc: _ } => 0x36,
Cmd::Error37 => 0x37,
Cmd::IntToFloat { stack_pos: _ } => 0x38,
Cmd::FloatToInt { stack_pos: _ } => 0x39,
Cmd::AddF => 0x3A,
Cmd::SubF => 0x3B,
Cmd::MultF => 0x3C,
Cmd::DivF => 0x3D,
Cmd::NegF => 0x3E,
Cmd::IncF { var_type: _, var_num: _ } => 0x3F,
Cmd::DecF { var_type: _, var_num: _ } => 0x40,
Cmd::VarSetF { var_type: _, var_num: _ } => 0x41,
Cmd::AddVarByF { var_type: _, var_num: _ } => 0x42,
Cmd::SubVarByF { var_type: _, var_num: _ } => 0x43,
Cmd::MultVarByF { var_type: _, var_num: _ } => 0x44,
Cmd::DivVarByF { var_type: _, var_num: _ } => 0x45,
Cmd::EqualsF => 0x46,
Cmd::NotEqualsF => 0x47,
Cmd::LessThanF => 0x48,
Cmd::LessOrEqualF => 0x49,
Cmd::GreaterF => 0x4A,
Cmd::GreaterOrEqualF => 0x4B,
Cmd::Error4C => 0x4C,
Cmd::Exit => 0x4D,
_ => panic!("Cannot cast Cmd {:?} to u8", self),
}
}
}
trait WriteImpl {
fn write(self, f: &mut Vec<u8>, endian: bool);
}
impl WriteImpl for u32 {
fn write(self, f: &mut Vec<u8>, endian: bool) {
if endian {
f.write_u32::<BigEndian>(self).unwrap();
} else {
f.write_u32::<LittleEndian>(self).unwrap();
}
}
}
impl WriteImpl for u16 {
fn write(self, f: &mut Vec<u8>, endian: bool) {
if endian {
f.write_u16::<BigEndian>(self).unwrap();
} else {
f.write_u16::<LittleEndian>(self).unwrap();
}
}
}
impl WriteImpl for u8 {
fn write(self, f: &mut Vec<u8>, _endian: bool) {
f.push(self);
}
}
impl WriteImpl for &[u8] {
fn write(self, f: &mut Vec<u8>, _endian: bool) {
f.extend_from_slice(self);
}
}
impl WriteImpl for &str {
fn write(self, f: &mut Vec<u8>, _endian: bool) {
f.extend_from_slice(self.as_bytes());
}
}
impl<T> WriteImpl for Vec<T> where T: WriteImpl + Copy, {
fn write(self, f: &mut Vec<u8>, endian: bool) {
for i in self {
WriteImpl::write(i, f, endian);
}
}
}
impl<T> WriteImpl for &mut Iterator<Item=T> where T: WriteImpl, {
fn write(self, f: &mut Vec<u8>, endian: bool) {
loop {
match self.next() {
Some(b) => WriteImpl::write(b, f, endian),
None => break
}
}
}
}
impl<T> WriteImpl for std::slice::Iter<'_, T> where T: WriteImpl + Clone, {
fn write(self, f: &mut Vec<u8>, endian: bool) {
for i in self {
WriteImpl::write(i.clone(), f, endian);
}
}
}
impl<T, T2> WriteImpl for (T, T2)
where T: WriteImpl, T2: WriteImpl {
fn write(self, f: &mut Vec<u8>, endian: bool) {
WriteImpl::write(self.0, f, endian);
WriteImpl::write(self.1, f, endian);
}
}