mirror of
https://github.com/G2-Games/lbee-utils.git
synced 2025-04-18 23:02:56 -05:00
Added preliminary script parsing
This commit is contained in:
parent
3c4d7a89ec
commit
3437b6c7a9
5 changed files with 354 additions and 1 deletions
|
@ -3,7 +3,7 @@ resolver = "2"
|
|||
members = [
|
||||
"cz",
|
||||
"pak_explorer",
|
||||
"luca_pak", "utils",
|
||||
"luca_pak", "utils", "luca_script",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
|
|
12
luca_script/Cargo.toml
Normal file
12
luca_script/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "luca_script"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors.workspace = true
|
||||
|
||||
[dependencies]
|
||||
byteorder-lite = "0.1.0"
|
||||
encoding_rs = "0.8.35"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
107
luca_script/src/LBEE_opcodes
Normal file
107
luca_script/src/LBEE_opcodes
Normal file
|
@ -0,0 +1,107 @@
|
|||
EQU
|
||||
EQUN
|
||||
EQUV
|
||||
ADD
|
||||
SUB
|
||||
MUL
|
||||
DIV
|
||||
MOD
|
||||
AND
|
||||
OR
|
||||
RANDOM
|
||||
VARSTR
|
||||
SET
|
||||
FLAGCLR
|
||||
GOTO
|
||||
ONGOTO
|
||||
GOSUB
|
||||
IFY
|
||||
IFN
|
||||
RETURN
|
||||
JUMP
|
||||
FARCALL
|
||||
FARRETURN
|
||||
JUMPPOINT
|
||||
END
|
||||
VARSTR_SET
|
||||
TALKNAME_SET
|
||||
ARFLAGSET
|
||||
COLORBG_SET
|
||||
SPLINE_SET
|
||||
SHAKELIST_SET
|
||||
MESSAGE
|
||||
MESSAGE_CLEAR
|
||||
SELECT
|
||||
CLOSE_WINDOW
|
||||
LOG
|
||||
LOG_PAUSE
|
||||
LOG_END
|
||||
VOICE
|
||||
WAIT_COUNT
|
||||
WAIT_TIME
|
||||
FFSTOP
|
||||
INIT
|
||||
STOP
|
||||
IMAGELOAD
|
||||
IMAGEUPADTE
|
||||
ARC
|
||||
MOVE
|
||||
MOVE2
|
||||
ROT
|
||||
PEND
|
||||
FADE
|
||||
SCALE
|
||||
SHAKE
|
||||
SHAKELIST
|
||||
BASE
|
||||
MCMOVE
|
||||
MCARC
|
||||
MCROT
|
||||
MCSHAKE
|
||||
MCFADE
|
||||
WAIT
|
||||
DRAW
|
||||
WIPE
|
||||
FRAMEON
|
||||
FRAMEOFF
|
||||
FW
|
||||
SCISSOR
|
||||
DELAY
|
||||
RASTER
|
||||
TONE
|
||||
SCALECOSSIN
|
||||
BMODE
|
||||
SIZE
|
||||
SPLINE
|
||||
DISP
|
||||
MASK
|
||||
SG_QUAKE
|
||||
BGM
|
||||
BGM_WAITSTART
|
||||
BGM_WAITFADE
|
||||
SE
|
||||
SE_STOP
|
||||
SE_WAIT
|
||||
VOLUME
|
||||
MOVIE
|
||||
SETCGFLAG
|
||||
EX
|
||||
TROPHY
|
||||
SETBGMFLAG
|
||||
TASK
|
||||
BTFUNC
|
||||
BATTLE
|
||||
KOEP
|
||||
BT_ACCESSORY_SELECT
|
||||
UNDO_CLEAR
|
||||
PTFUNC
|
||||
PT
|
||||
GMFUNC
|
||||
GM
|
||||
DEL_CALLSTACK
|
||||
FULLQUAKE_ZOOM
|
||||
LBFUNC
|
||||
LBBG
|
||||
HAIKEI_SET
|
||||
SAYAVOICETEXT
|
||||
UNKNOWN
|
181
luca_script/src/main.rs
Normal file
181
luca_script/src/main.rs
Normal file
|
@ -0,0 +1,181 @@
|
|||
mod utils;
|
||||
|
||||
use std::{cell::LazyCell, fs::{self, File}, io::Read, path::PathBuf};
|
||||
|
||||
use byteorder_lite::{ReadBytesExt, LE};
|
||||
use utils::Encoding;
|
||||
|
||||
const OPCODES: LazyCell<Vec<String>> = LazyCell::new(|| fs::read_to_string("LBEE_opcodes")
|
||||
.unwrap()
|
||||
.split("\n")
|
||||
.map(|s| s.to_owned())
|
||||
.collect()
|
||||
);
|
||||
|
||||
fn main() {
|
||||
let file_path = PathBuf::from("SEEN0513");
|
||||
|
||||
let mut script = File::open(&file_path).unwrap();
|
||||
|
||||
println!("Start parsing script");
|
||||
let script = parse_script(
|
||||
&mut script,
|
||||
file_path.file_name().unwrap().to_str().unwrap()
|
||||
);
|
||||
println!("Parsing finished");
|
||||
|
||||
for c in script.opcodes {
|
||||
let ascii_string = String::from_utf8_lossy(&c.param_bytes);
|
||||
//println!("{:>4}: '{:>11}' — {}", c.index, c.string, ascii_string);
|
||||
SpecificOpcode::decode(&c.string, c.param_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_script<S: Read>(script_stream: &mut S, name: &str) -> Script {
|
||||
let mut opcodes = Vec::new();
|
||||
let mut _offset = 0;
|
||||
let mut i = 0;
|
||||
let mut pos = 0;
|
||||
loop {
|
||||
// Read all base info
|
||||
let (length, number, flag) = (
|
||||
script_stream.read_u16::<LE>().unwrap() as usize,
|
||||
script_stream.read_u8().unwrap(),
|
||||
script_stream.read_u8().unwrap()
|
||||
);
|
||||
let string = OPCODES[number as usize].clone();
|
||||
|
||||
_offset += 4;
|
||||
|
||||
let raw_len = length - 4;
|
||||
let mut raw_bytes = vec![0u8; raw_len];
|
||||
script_stream.read_exact(&mut raw_bytes).unwrap();
|
||||
_offset += raw_len;
|
||||
|
||||
// Read extra align byte if alignment needed
|
||||
let align = if length % 2 != 0 {
|
||||
_offset += 1;
|
||||
Some(script_stream.read_u8().unwrap())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut fixed_param = None;
|
||||
let param_bytes = match flag {
|
||||
0 => raw_bytes.clone(),
|
||||
f if f < 2 => {
|
||||
fixed_param = Some(vec![
|
||||
u16::from_le_bytes(raw_bytes[..2].try_into().unwrap()),
|
||||
]);
|
||||
raw_bytes[2..].to_vec()
|
||||
}
|
||||
_ => {
|
||||
fixed_param = Some(vec![
|
||||
u16::from_le_bytes(raw_bytes[..2].try_into().unwrap()),
|
||||
u16::from_le_bytes(raw_bytes[2..4].try_into().unwrap()),
|
||||
]);
|
||||
raw_bytes[4..].to_vec()
|
||||
}
|
||||
};
|
||||
|
||||
opcodes.push(Opcode {
|
||||
index: i,
|
||||
position: pos,
|
||||
length,
|
||||
number,
|
||||
string: string.clone(),
|
||||
flag,
|
||||
raw_bytes,
|
||||
align,
|
||||
fixed_param,
|
||||
param_bytes
|
||||
});
|
||||
|
||||
// Break if END opcode reached
|
||||
if &string == "END" {
|
||||
break;
|
||||
}
|
||||
|
||||
pos += (length + 1) & !1;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Script {
|
||||
name: name.to_string(),
|
||||
code_count: opcodes.len(),
|
||||
opcodes,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Script {
|
||||
name: String,
|
||||
opcodes: Vec<Opcode>,
|
||||
code_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Opcode {
|
||||
index: usize,
|
||||
position: usize,
|
||||
length: usize,
|
||||
number: u8,
|
||||
string: String,
|
||||
|
||||
flag: u8,
|
||||
raw_bytes: Vec<u8>,
|
||||
align: Option<u8>,
|
||||
fixed_param: Option<Vec<u16>>,
|
||||
param_bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum SpecificOpcode {
|
||||
Message {
|
||||
voice_id: u16,
|
||||
messages: Vec<String>,
|
||||
end: Vec<u8>,
|
||||
},
|
||||
Select,
|
||||
Battle,
|
||||
Task,
|
||||
SayAVoiceText,
|
||||
VarStrSet,
|
||||
GoTo,
|
||||
GoSub,
|
||||
Jump,
|
||||
FarCall,
|
||||
IFN,
|
||||
IFY,
|
||||
Random,
|
||||
ImageLoad,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl SpecificOpcode {
|
||||
pub fn decode(opcode_str: &str, param_bytes: Vec<u8>) -> Self {
|
||||
match opcode_str {
|
||||
"MESSAGE" => Self::message(param_bytes),
|
||||
_ => Self::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
fn message(param_bytes: Vec<u8>) -> Self {
|
||||
let voice_id = u16::from_le_bytes(param_bytes[0..2].try_into().unwrap());
|
||||
|
||||
let mut messages = Vec::new();
|
||||
let mut offset = 2;
|
||||
for _ in 0..2 {
|
||||
let (o, string) = utils::get_string(¶m_bytes, offset, Encoding::UTF16, None).unwrap();
|
||||
messages.push(string);
|
||||
offset = o;
|
||||
}
|
||||
dbg!(&messages);
|
||||
|
||||
Self::Message {
|
||||
voice_id,
|
||||
messages,
|
||||
end: param_bytes[offset..].to_vec()
|
||||
}
|
||||
}
|
||||
}
|
53
luca_script/src/utils.rs
Normal file
53
luca_script/src/utils.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
use std::error::Error;
|
||||
|
||||
use encoding_rs::*;
|
||||
|
||||
pub enum Encoding {
|
||||
UTF8,
|
||||
UTF16,
|
||||
ShiftJIS,
|
||||
}
|
||||
|
||||
pub fn get_string(
|
||||
bytes: &[u8],
|
||||
offset: usize,
|
||||
format: Encoding,
|
||||
len: Option<usize>
|
||||
) -> Result<(usize, String), Box<dyn Error>> {
|
||||
let slice = &bytes[offset..];
|
||||
|
||||
// Find the end of the string
|
||||
let mut end = 0;
|
||||
let mut char_width = 1;
|
||||
if let Some(l) = len {
|
||||
end = l;
|
||||
} else {
|
||||
match format {
|
||||
Encoding::UTF8 | Encoding::ShiftJIS => {
|
||||
while (end < slice.len()) && (slice[end] != 0) {
|
||||
end += 1
|
||||
}
|
||||
},
|
||||
Encoding::UTF16 => {
|
||||
char_width = 2;
|
||||
while (end + 1 < slice.len()) && !((slice[end] == 0) && (slice[end + 1] == 0)) {
|
||||
end += 2
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
let string = match format {
|
||||
Encoding::UTF8 => String::from_utf8(slice[..end].to_vec())?,
|
||||
Encoding::UTF16 => {
|
||||
String::from_utf16(
|
||||
&slice[..end].chunks_exact(2)
|
||||
.map(|e| u16::from_le_bytes(e.try_into().unwrap()))
|
||||
.collect::<Vec<u16>>()
|
||||
)?
|
||||
}
|
||||
Encoding::ShiftJIS => SHIFT_JIS.decode(&slice[..end]).0.to_string(),
|
||||
};
|
||||
|
||||
Ok((offset + end + char_width, string))
|
||||
}
|
Loading…
Reference in a new issue