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 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 = File::open("LOOPERS_scenario_01").unwrap();
|
let mut script = BufReader::new(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) {
|
||||||
|
@ -14,43 +16,90 @@ fn main() {
|
||||||
opcode.to_u8().unwrap(),
|
opcode.to_u8().unwrap(),
|
||||||
opcode
|
opcode
|
||||||
);
|
);
|
||||||
if opcode == Opcode::TASK {
|
|
||||||
break;
|
parse_opcode(opcode, &mut script).unwrap();
|
||||||
}
|
} else {
|
||||||
match opcode {
|
println!(
|
||||||
Opcode::MESSAGE => {
|
"{:X?}: {:#04X?} (¡{:?}!)",
|
||||||
let variables = script.read_u8().unwrap();
|
script.stream_position().unwrap() - 1,
|
||||||
match variables {
|
byte,
|
||||||
1 => continue,
|
Opcode::UNKNOWN,
|
||||||
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!("------");
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(non_camel_case_types)]
|
||||||
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||||
#[derive(FromPrimitive, ToPrimitive)]
|
#[derive(FromPrimitive, ToPrimitive)]
|
||||||
enum Opcode {
|
enum Opcode {
|
||||||
EQU,
|
EQU,
|
||||||
|
@ -195,23 +244,11 @@ enum Opcode {
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
struct Message {
|
struct Message {
|
||||||
variables: u8,
|
variables: u8,
|
||||||
unknown1: Option<u16>,
|
unknown1: u16,
|
||||||
unknown2: Option<u16>,
|
unknown2: u16,
|
||||||
index: Option<u16>,
|
index: u16,
|
||||||
|
|
||||||
messages: Option<Vec<ScriptString>>,
|
strings: 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)]
|
||||||
|
@ -230,38 +267,46 @@ enum StringFormat {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScriptString {
|
impl ScriptString {
|
||||||
fn read<R: Read + ReadBytesExt>(input: &mut R) -> Self {
|
fn read<R: Read + BufRead>(input: &mut R) -> Result<Self, io::Error> {
|
||||||
let length = input.read_i16::<LE>().unwrap();
|
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 {
|
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??
|
// bytes of the absolute value?!?
|
||||||
(vec![0u8; length.abs() as usize + 1], StringFormat::UTF8)
|
(vec![0u8; length.unsigned_abs() as usize], StringFormat::UTF8)
|
||||||
} else {
|
} else {
|
||||||
// Otherwise double the length
|
// 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,
|
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 => String::from_utf8_lossy(&self.buffer).to_string(),
|
StringFormat::UTF8 | StringFormat::ASCII => String::from_utf8_lossy(&self.buffer).to_string(),
|
||||||
StringFormat::UTF16 => {
|
StringFormat::UTF16 => String::from_utf16_lossy(transmute_many::<u16, SingleManyGuard>(&self.buffer).unwrap()),
|
||||||
String::from_utf16_lossy(
|
StringFormat::ShiftJIS => SHIFT_JIS.decode(&self.buffer).0.to_string(),
|
||||||
&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!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue