1
0
Fork 0
mirror of https://github.com/doukutsu-rs/doukutsu-rs synced 2024-09-30 05:59:46 +00:00

tsc: overflow and broken varint reader fix

This commit is contained in:
Alula 2020-09-06 17:55:07 +02:00
parent fa342c7568
commit 51b9184a85
No known key found for this signature in database
GPG key ID: 3E00485503A1D8BA

View file

@ -384,9 +384,9 @@ impl TextScriptVM {
state.textscript_vm.reset(); state.textscript_vm.reset();
} }
} }
TextScriptExecutionState::Msg(event, ip, remaining, timer) => { TextScriptExecutionState::Msg(event, ip, remaining, counter) => {
if timer > 0 { if counter > 0 {
state.textscript_vm.state = TextScriptExecutionState::Msg(event, ip, remaining, timer - 1); state.textscript_vm.state = TextScriptExecutionState::Msg(event, ip, remaining, counter - 1);
break; break;
} }
@ -394,7 +394,7 @@ impl TextScriptVM {
let mut cursor = Cursor::new(bytecode); let mut cursor = Cursor::new(bytecode);
cursor.seek(SeekFrom::Start(ip as u64))?; cursor.seek(SeekFrom::Start(ip as u64))?;
let (consumed, chr) = read_cur_wtf8(&mut cursor, remaining); let chr = std::char::from_u32(read_cur_varint(&mut cursor)? as u32).unwrap_or('\u{fffd}');
match chr { match chr {
'\n' if state.textscript_vm.current_line == TextScriptLine::Line1 => { '\n' if state.textscript_vm.current_line == TextScriptLine::Line1 => {
@ -421,11 +421,11 @@ impl TextScriptVM {
_ => {} _ => {}
} }
if (remaining - consumed) > 0 { if remaining > 1 {
let ticks = if state.key_state.jump() || state.key_state.fire() { 1 } else { 4 }; let ticks = if state.key_state.jump() || state.key_state.fire() { 1 } else { 4 };
state.textscript_vm.state = TextScriptExecutionState::Msg(event, ip + consumed, remaining - consumed, ticks); state.textscript_vm.state = TextScriptExecutionState::Msg(event, cursor.position() as u32, remaining - 1, ticks);
} else { } else {
state.textscript_vm.state = TextScriptExecutionState::Running(event, ip + consumed); state.textscript_vm.state = TextScriptExecutionState::Running(event, cursor.position() as u32);
} }
} else { } else {
state.textscript_vm.reset(); state.textscript_vm.reset();
@ -500,12 +500,16 @@ impl TextScriptVM {
} }
OpCode::_UNI => {} OpCode::_UNI => {}
OpCode::_STR => { OpCode::_STR => {
let len = read_cur_varint(&mut cursor)? as u32; let mut len = read_cur_varint(&mut cursor)? as u32;
if state.textscript_vm.flags.render() { if state.textscript_vm.flags.render() {
exec_state = TextScriptExecutionState::Msg(event, cursor.position() as u32, len, 4); exec_state = TextScriptExecutionState::Msg(event, cursor.position() as u32, len, 4);
} else { } else {
while len > 0 {
len -= 1;
let _ = read_cur_varint(&mut cursor)?;
}
// simply skip the text if we aren't in message mode. // simply skip the text if we aren't in message mode.
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32 + len); exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
} }
} }
OpCode::_END | OpCode::END => { OpCode::_END | OpCode::END => {
@ -671,6 +675,7 @@ impl TextScriptVM {
state.textscript_vm.suspend = true; state.textscript_vm.suspend = true;
state.next_scene = Some(Box::new(new_scene)); state.next_scene = Some(Box::new(new_scene));
log::info!("Transitioning to stage {}, with script #{:04}", map_id, event_num);
exec_state = TextScriptExecutionState::Running(event_num, 0); exec_state = TextScriptExecutionState::Running(event_num, 0);
} }
OpCode::MOV => { OpCode::MOV => {
@ -973,7 +978,7 @@ impl TextScript {
/// Compiles a decrypted text script data into internal bytecode. /// Compiles a decrypted text script data into internal bytecode.
pub fn compile(data: &[u8], strict: bool) -> GameResult<TextScript> { pub fn compile(data: &[u8], strict: bool) -> GameResult<TextScript> {
log::debug!("data: {}", String::from_utf8_lossy(data)); log::info!("data: {}", String::from_utf8_lossy(data));
let mut event_map = HashMap::new(); let mut event_map = HashMap::new();
let mut iter = data.iter().copied().peekable(); let mut iter = data.iter().copied().peekable();
@ -1057,20 +1062,41 @@ impl TextScript {
} }
fn put_string(buffer: &mut Vec<u8>, out: &mut Vec<u8>) { fn put_string(buffer: &mut Vec<u8>, out: &mut Vec<u8>) {
let mut cursor: Cursor<&Vec<u8>> = Cursor::new(buffer);
let mut tmp_buf = Vec::new();
let mut remaining = buffer.len() as u32;
let mut chars = 0;
while remaining > 0 {
let (consumed, chr) = read_cur_wtf8(&mut cursor, remaining);
remaining -= consumed;
chars += 1;
TextScript::put_varint(chr as i32, &mut tmp_buf);
}
buffer.clear();
TextScript::put_varint(OpCode::_STR as i32, out); TextScript::put_varint(OpCode::_STR as i32, out);
TextScript::put_varint(buffer.len() as i32, out); TextScript::put_varint(chars, out);
out.append(buffer); out.append(&mut tmp_buf);
} }
fn put_varint(val: i32, out: &mut Vec<u8>) { fn put_varint(val: i32, out: &mut Vec<u8>) {
let mut x = ((val as u32) >> 31) ^ ((val as u32) << 1); let mut x = ((val as u32) >> 31) ^ ((val as u32) << 1);
while x > 0x80 { loop {
out.push((x & 0x7f) as u8 | 0x80); let mut n = (x & 0x7f) as u8;
x >>= 7; x >>= 7;
if x != 0 {
n |= 0x80;
} }
out.push(x as u8); out.push(n);
if x == 0 { break; }
}
} }
fn read_varint<I: Iterator<Item=u8>>(iter: &mut I) -> GameResult<i32> { fn read_varint<I: Iterator<Item=u8>>(iter: &mut I) -> GameResult<i32> {
@ -1080,9 +1106,7 @@ impl TextScript {
let n = iter.next().ok_or_else(|| ParseError(str!("Script unexpectedly ended.")))?; let n = iter.next().ok_or_else(|| ParseError(str!("Script unexpectedly ended.")))?;
result |= (n as u32 & 0x7f) << (o * 7); result |= (n as u32 & 0x7f) << (o * 7);
if n & 0x80 == 0 { if n & 0x80 == 0 { break; }
break;
}
} }
Ok(((result << 31) ^ (result >> 1)) as i32) Ok(((result << 31) ^ (result >> 1)) as i32)
@ -1217,10 +1241,14 @@ impl TextScript {
#[test] #[test]
fn test_varint() { fn test_varint() {
for &n in [1_i32, 23, 456, 7890, 12345, -1, -23, -456].iter() { for n in -4000..=4000 {
let mut out = Vec::new(); let mut out = Vec::new();
TextScript::put_varint(n, &mut out); TextScript::put_varint(n, &mut out);
let result = TextScript::read_varint(&mut out.iter().copied()).unwrap(); let result = TextScript::read_varint(&mut out.iter().copied()).unwrap();
assert_eq!(result, n); assert_eq!(result, n);
let mut cur = Cursor::new(&out);
let result = read_cur_varint(&mut cur).unwrap();
assert_eq!(result, n);
} }
} }