Compare commits

..

No commits in common. "f1c23bd3f4ec3b3d7dbe494d9912a9279beb66ed" and "43b65cf173fc5dca54eb5a0cae60874e5dd345a2" have entirely different histories.

View file

@ -1,12 +1,10 @@
use std::{fs::File, io::{self, BufRead, BufReader, Read, Seek}}; use std::{fs::File, io::{Read, Seek}, process::exit};
use byteorder::{ReadBytesExt, LE}; use byteorder::{ReadBytesExt, LE};
use encoding_rs::SHIFT_JIS;
use num_derive::{FromPrimitive, ToPrimitive}; use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::{FromPrimitive, ToPrimitive}; use num_traits::{FromPrimitive, ToPrimitive};
use safe_transmute::{transmute_many, SingleManyGuard};
fn main() { fn main() {
let mut script = BufReader::new(File::open("LOOPERS_scenario_01").unwrap()); let mut script = File::open("LOOPERS_scenario_01").unwrap();
while let Ok(byte) = script.read_u8() { while let Ok(byte) = script.read_u8() {
if let Some(opcode) = Opcode::from_u8(byte) { if let Some(opcode) = Opcode::from_u8(byte) {
@ -16,90 +14,43 @@ fn main() {
opcode.to_u8().unwrap(), opcode.to_u8().unwrap(),
opcode opcode
); );
if opcode == Opcode::TASK {
parse_opcode(opcode, &mut script).unwrap(); break;
} else { }
println!( match opcode {
"{:X?}: {:#04X?} (¡{:?}!)", Opcode::MESSAGE => {
script.stream_position().unwrap() - 1, let variables = script.read_u8().unwrap();
byte, match variables {
Opcode::UNKNOWN, 1 => continue,
); 4 => {
} dbg!(script.read_u32::<LE>().unwrap());
} continue;
} }
_ => (),
fn parse_opcode<R: Read + BufRead>(opcode: Opcode, mut input: R) -> Result<(), io::Error> { }
match opcode { let message = Message {
Opcode::MESSAGE => { variables,
let variables = input.read_u8().unwrap(); unknown1: Some(script.read_u16::<LE>().unwrap()),
match variables { unknown2: Some(script.read_u16::<LE>().unwrap()),
// Empty message!? index: Some(script.read_u16::<LE>().unwrap()),
1 => return Ok(()), messages: Some((0..3).map(|_| ScriptString::read(&mut script)).collect()),
3 => (), };
// Unknown data message.messages.unwrap().iter().for_each(|m| println!("{}", m.to_string()));
4 => { println!("-----");
let mut buf = vec![0; 4]; },
input.read_exact(&mut buf).unwrap(); Opcode::JUMP => {
//println!("{:0X?}", buf); dbg!(script.read_u32::<LE>().unwrap());
return Ok(()); println!("------");
} }
n => unimplemented!("{n}"), _ => (),
} }
let message = Message {
variables,
unknown1: input.read_u16::<LE>().unwrap(),
unknown2: input.read_u16::<LE>().unwrap(),
index: input.read_u16::<LE>().unwrap(),
strings: (0..3).map(|_| ScriptString::read(&mut input)).collect::<Result<Vec<_>, _>>()?,
};
message.strings.iter().enumerate().for_each(|m| println!("{}: {}, {}", m.0, m.1.length, m.1.to_string()));
println!("-----");
},
Opcode::IMAGELOAD => {
let mode = input.read_u8().unwrap();
println!("Mode: {mode}");
if mode == 0 {
println!("Unknown: {}", input.read_u16::<LE>().unwrap());
} else {
println!("Unknown: {}", input.read_u16::<LE>().unwrap());
println!("Unknown: {}", input.read_u16::<LE>().unwrap());
}
let image_id = input.read_u16::<LE>().unwrap();
println!("Image ID: {image_id}");
println!("-----");
},
/*
Opcode::SELECT => {
let var_id = script.read_u16::<LE>().unwrap();
script.read_u16::<LE>().unwrap();
script.read_u16::<LE>().unwrap();
script.read_u16::<LE>().unwrap();
let msg_str = script.read_u16::<LE>().unwrap();
script.read_u16::<LE>().unwrap();
script.read_u16::<LE>().unwrap();
script.read_u16::<LE>().unwrap();
println!("{var_id} & {msg_str}\n-----");
},
*/
Opcode::JUMP => {
input.read_u16::<LE>().unwrap();
} }
Opcode::VARSTR => {
println!("Unknown: {}", input.read_u32::<LE>()?);
println!("-----");
}
_ => (),
} }
Ok(())
} }
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#[allow(clippy::upper_case_acronyms)]
#[repr(u8)] #[repr(u8)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(FromPrimitive, ToPrimitive)] #[derive(FromPrimitive, ToPrimitive)]
enum Opcode { enum Opcode {
EQU, EQU,
@ -244,11 +195,23 @@ enum Opcode {
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
struct Message { struct Message {
variables: u8, variables: u8,
unknown1: u16, unknown1: Option<u16>,
unknown2: u16, unknown2: Option<u16>,
index: u16, index: Option<u16>,
strings: Vec<ScriptString>, messages: Option<Vec<ScriptString>>,
}
impl Default for Message {
fn default() -> Self {
Self {
variables: 1,
unknown1: None,
unknown2: None,
index: None,
messages: None
}
}
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
@ -267,46 +230,38 @@ enum StringFormat {
} }
impl ScriptString { impl ScriptString {
fn read<R: Read + BufRead>(input: &mut R) -> Result<Self, io::Error> { fn read<R: Read + ReadBytesExt>(input: &mut R) -> Self {
let length = input.read_i16::<LE>()?; let length = input.read_i16::<LE>().unwrap();
// Catch very long strings, these should be investigated (they're probably broken)
assert!(length < 2_000);
let (mut buffer, format) = if length < 0 { let (mut buffer, format) = if length < 0 {
// If the length is negative, then the length is the exact length in // If the length is negative, then the length is the exact length in
// bytes of the absolute value?!? // bytes??
(vec![0u8; length.unsigned_abs() as usize], StringFormat::UTF8) (vec![0u8; length.abs() as usize + 1], StringFormat::UTF8)
} else { } else {
// Otherwise double the length // Otherwise double the length
(vec![0u8; (length as usize) * 2], StringFormat::UTF16) (vec![0u8; (length as usize + 1) * 2], StringFormat::UTF16)
}; };
// Read the string into the buffer input.read_exact(&mut buffer).unwrap();
input.read_exact(&mut buffer)?;
// Ensure the string is null terminated Self {
let string_end = match format {
StringFormat::UTF16 => input.read_u16::<LE>()?,
_ => input.read_u8()? as u16,
};
assert!(!(string_end != 0), "String not null terminated!");
Ok(Self {
length, length,
format, format,
buffer, buffer,
}) }
} }
} }
impl ToString for ScriptString { impl ToString for ScriptString {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self.format { match self.format {
StringFormat::UTF8 | StringFormat::ASCII => String::from_utf8_lossy(&self.buffer).to_string(), StringFormat::UTF8 => String::from_utf8_lossy(&self.buffer).to_string(),
StringFormat::UTF16 => String::from_utf16_lossy(transmute_many::<u16, SingleManyGuard>(&self.buffer).unwrap()), StringFormat::UTF16 => {
StringFormat::ShiftJIS => SHIFT_JIS.decode(&self.buffer).0.to_string(), String::from_utf16_lossy(
&self.buffer
.chunks(2)
.map(|c| u16::from_le_bytes(c.try_into().unwrap())).collect::<Vec<u16>>()).to_owned()
},
StringFormat::ASCII => String::from_utf8_lossy(&self.buffer).to_string(),
_ => unimplemented!(), _ => unimplemented!(),
} }
} }