mirror of
https://github.com/G2-Games/lbee-utils.git
synced 2025-04-19 15:22:53 -05:00
Compare commits
2 commits
43b65cf173
...
f1c23bd3f4
Author | SHA1 | Date | |
---|---|---|---|
f1c23bd3f4 | |||
9b3fcb2480 |
1 changed files with 110 additions and 65 deletions
|
@ -1,10 +1,12 @@
|
|||
use std::{fs::File, io::{Read, Seek}, process::exit};
|
||||
use std::{fs::File, io::{self, BufRead, BufReader, Read, Seek}};
|
||||
use byteorder::{ReadBytesExt, LE};
|
||||
use encoding_rs::SHIFT_JIS;
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use num_traits::{FromPrimitive, ToPrimitive};
|
||||
use safe_transmute::{transmute_many, SingleManyGuard};
|
||||
|
||||
fn main() {
|
||||
let mut script = File::open("LOOPERS_scenario_01").unwrap();
|
||||
let mut script = BufReader::new(File::open("LOOPERS_scenario_01").unwrap());
|
||||
|
||||
while let Ok(byte) = script.read_u8() {
|
||||
if let Some(opcode) = Opcode::from_u8(byte) {
|
||||
|
@ -14,43 +16,90 @@ fn main() {
|
|||
opcode.to_u8().unwrap(),
|
||||
opcode
|
||||
);
|
||||
if opcode == Opcode::TASK {
|
||||
break;
|
||||
}
|
||||
match opcode {
|
||||
Opcode::MESSAGE => {
|
||||
let variables = script.read_u8().unwrap();
|
||||
match variables {
|
||||
1 => continue,
|
||||
4 => {
|
||||
dbg!(script.read_u32::<LE>().unwrap());
|
||||
continue;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
let message = Message {
|
||||
variables,
|
||||
unknown1: Some(script.read_u16::<LE>().unwrap()),
|
||||
unknown2: Some(script.read_u16::<LE>().unwrap()),
|
||||
index: Some(script.read_u16::<LE>().unwrap()),
|
||||
messages: Some((0..3).map(|_| ScriptString::read(&mut script)).collect()),
|
||||
};
|
||||
message.messages.unwrap().iter().for_each(|m| println!("{}", m.to_string()));
|
||||
println!("-----");
|
||||
},
|
||||
Opcode::JUMP => {
|
||||
dbg!(script.read_u32::<LE>().unwrap());
|
||||
println!("------");
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
parse_opcode(opcode, &mut script).unwrap();
|
||||
} else {
|
||||
println!(
|
||||
"{:X?}: {:#04X?} (¡{:?}!)",
|
||||
script.stream_position().unwrap() - 1,
|
||||
byte,
|
||||
Opcode::UNKNOWN,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_opcode<R: Read + BufRead>(opcode: Opcode, mut input: R) -> Result<(), io::Error> {
|
||||
match opcode {
|
||||
Opcode::MESSAGE => {
|
||||
let variables = input.read_u8().unwrap();
|
||||
match variables {
|
||||
// Empty message!?
|
||||
1 => return Ok(()),
|
||||
3 => (),
|
||||
// Unknown data
|
||||
4 => {
|
||||
let mut buf = vec![0; 4];
|
||||
input.read_exact(&mut buf).unwrap();
|
||||
//println!("{:0X?}", buf);
|
||||
return Ok(());
|
||||
}
|
||||
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(clippy::upper_case_acronyms)]
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
#[derive(FromPrimitive, ToPrimitive)]
|
||||
enum Opcode {
|
||||
EQU,
|
||||
|
@ -195,23 +244,11 @@ enum Opcode {
|
|||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct Message {
|
||||
variables: u8,
|
||||
unknown1: Option<u16>,
|
||||
unknown2: Option<u16>,
|
||||
index: Option<u16>,
|
||||
unknown1: u16,
|
||||
unknown2: u16,
|
||||
index: u16,
|
||||
|
||||
messages: Option<Vec<ScriptString>>,
|
||||
}
|
||||
|
||||
impl Default for Message {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
variables: 1,
|
||||
unknown1: None,
|
||||
unknown2: None,
|
||||
index: None,
|
||||
messages: None
|
||||
}
|
||||
}
|
||||
strings: Vec<ScriptString>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
|
@ -230,38 +267,46 @@ enum StringFormat {
|
|||
}
|
||||
|
||||
impl ScriptString {
|
||||
fn read<R: Read + ReadBytesExt>(input: &mut R) -> Self {
|
||||
let length = input.read_i16::<LE>().unwrap();
|
||||
fn read<R: Read + BufRead>(input: &mut R) -> Result<Self, io::Error> {
|
||||
let length = input.read_i16::<LE>()?;
|
||||
|
||||
// Catch very long strings, these should be investigated (they're probably broken)
|
||||
assert!(length < 2_000);
|
||||
|
||||
let (mut buffer, format) = if length < 0 {
|
||||
// If the length is negative, then the length is the exact length in
|
||||
// bytes??
|
||||
(vec![0u8; length.abs() as usize + 1], StringFormat::UTF8)
|
||||
// bytes of the absolute value?!?
|
||||
(vec![0u8; length.unsigned_abs() as usize], StringFormat::UTF8)
|
||||
} else {
|
||||
// Otherwise double the length
|
||||
(vec![0u8; (length as usize + 1) * 2], StringFormat::UTF16)
|
||||
(vec![0u8; (length as usize) * 2], StringFormat::UTF16)
|
||||
};
|
||||
|
||||
input.read_exact(&mut buffer).unwrap();
|
||||
// Read the string into the buffer
|
||||
input.read_exact(&mut buffer)?;
|
||||
|
||||
Self {
|
||||
// Ensure the string is null terminated
|
||||
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,
|
||||
format,
|
||||
buffer,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for ScriptString {
|
||||
fn to_string(&self) -> String {
|
||||
match self.format {
|
||||
StringFormat::UTF8 => String::from_utf8_lossy(&self.buffer).to_string(),
|
||||
StringFormat::UTF16 => {
|
||||
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(),
|
||||
StringFormat::UTF8 | StringFormat::ASCII => String::from_utf8_lossy(&self.buffer).to_string(),
|
||||
StringFormat::UTF16 => String::from_utf16_lossy(transmute_many::<u16, SingleManyGuard>(&self.buffer).unwrap()),
|
||||
StringFormat::ShiftJIS => SHIFT_JIS.decode(&self.buffer).0.to_string(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue