Mercurial > touhou
diff src/th06/ecl_vm.rs @ 660:31fc0d881105
Make ecl_vm compile, and use it in eclrenderer (doesn’t render yet).
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Sat, 10 Aug 2019 14:41:30 +0200 |
parents | 53786d834444 |
children | 598f3125cbac |
line wrap: on
line diff
--- a/src/th06/ecl_vm.rs +++ b/src/th06/ecl_vm.rs @@ -1,29 +1,159 @@ //! ECL runner. -use crate::th06::anm0::{ - Script, - Anm0, - Call, - Instruction, -}; -use crate::th06::interpolator::{Interpolator1, Interpolator2, Interpolator3, Formula}; -use crate::util::math::Mat4; +use crate::th06::ecl::{Ecl, SubInstruction}; +use crate::th06::enemy::Enemy; use crate::util::prng::Prng; use std::cell::RefCell; -use std::rc::{Rc, Weak}; +use std::rc::Rc; + +type Variables = ([i32; 4], [f32; 4], [i32; 4]); + +/// Interpreter for enemy scripts. +#[derive(Default)] +pub struct EclRunner { + enemy: Rc<RefCell<Enemy>>, + ecl: Option<Ecl>, + sub: u8, + running: bool, + /// XXX + pub frame: i32, + ip: i32, + variables: Variables, + comparison_reg: i8, + stack: Vec<Variables>, +} + +impl EclRunner { + /// Create a new ECL runner. + pub fn new(ecl: &Ecl, enemy: Rc<RefCell<Enemy>>, sub: u8) -> EclRunner { + EclRunner { + enemy, + // XXX: no clone. + ecl: Some(ecl.clone()), + sub, + ..Default::default() + } + } + + /// Advance the ECL of a single frame. + pub fn run_frame(&mut self) { + while self.running { + let ecl = self.ecl.clone().unwrap(); + let sub = &ecl.subs[self.sub as usize]; + let call = match sub.instructions.get(self.ip as usize) { + Some(call) => call, + None => { + self.running = false; + break; + } + }; + + if call.time > self.frame { + break; + } + self.ip += 1; + + let rank = self.enemy.borrow().get_rank(); + if call.rank_mask & (0x100 << rank) == 0 { + continue; + } + + if call.time == self.frame { + self.run_instruction(call.instr.clone()); + } + } + self.frame += 1; + } - fn run_instruction(&mut self, instruction: Instruction) { - let mut sprite = self.sprite.borrow_mut(); + fn get_i32(&self, var: i32) -> i32 { + let enemy = self.enemy.borrow(); + match var { + -10001 => self.variables.0[0], + -10002 => self.variables.0[1], + -10003 => self.variables.0[2], + -10004 => self.variables.0[3], + -10005 => self.variables.1[0] as i32, + -10006 => self.variables.1[1] as i32, + -10007 => self.variables.1[2] as i32, + -10008 => self.variables.1[3] as i32, + -10009 => self.variables.2[0], + -10010 => self.variables.2[1], + -10011 => self.variables.2[2], + -10012 => self.variables.2[3], + -10013 => enemy.get_rank(), + -10014 => enemy.get_difficulty(), + -10015 => enemy.pos.x as i32, + -10016 => enemy.pos.y as i32, + -10017 => enemy.z as i32, + -10018 => unimplemented!(), + -10019 => unimplemented!(), + -10020 => unreachable!(), + -10021 => unimplemented!(), + -10022 => enemy.frame as i32, + -10023 => unreachable!(), + -10024 => enemy.life as i32, + -10025 => unimplemented!(), + _ => var + } + } + + fn get_f32(&self, var: f32) -> f32 { + let enemy = self.enemy.borrow(); + match var { + -10001.0 => self.variables.0[0] as f32, + -10002.0 => self.variables.0[1] as f32, + -10003.0 => self.variables.0[2] as f32, + -10004.0 => self.variables.0[3] as f32, + -10005.0 => self.variables.1[0], + -10006.0 => self.variables.1[1], + -10007.0 => self.variables.1[2], + -10008.0 => self.variables.1[3], + -10009.0 => self.variables.2[0] as f32, + -10010.0 => self.variables.2[1] as f32, + -10011.0 => self.variables.2[2] as f32, + -10012.0 => self.variables.2[3] as f32, + -10013.0 => enemy.get_rank() as f32, + -10014.0 => enemy.get_difficulty() as f32, + -10015.0 => enemy.pos.x, + -10016.0 => enemy.pos.y, + -10017.0 => enemy.z, + -10018.0 => unimplemented!(), + -10019.0 => unimplemented!(), + -10020.0 => unreachable!(), + -10021.0 => unimplemented!(), + -10022.0 => enemy.frame as f32, + -10023.0 => unreachable!(), + -10024.0 => enemy.life as f32, + -10025.0 => unimplemented!(), + _ => var + } + } + + fn set_i32(&mut self, var: i32, value: i32) { + unimplemented!() + } + + fn set_f32(&mut self, var: f32, value: f32) { + unimplemented!() + } + + fn get_prng(&mut self) -> Rc<RefCell<Prng>> { + let enemy = self.enemy.borrow(); + enemy.prng.upgrade().unwrap() + } + + fn run_instruction(&mut self, instruction: SubInstruction) { match instruction { - Instruction::Noop() { + SubInstruction::Noop() => { // really } // 1 - Instruction::Stop() { - self._enemy.removed = true; + SubInstruction::Destroy(_unused) => { + let mut enemy = self.enemy.borrow_mut(); + enemy.removed = true; } - // 2 - Instruction::RelativeJump(frame, ip) { + // 2 + SubInstruction::RelativeJump(frame, ip) => { self.frame = frame; // ip = ip + flag in th06 self.ip = ip; @@ -31,232 +161,306 @@ use std::rc::{Rc, Weak}; } // 3 // GHIDRA SAYS THERE IS A COMPARISON_REG BUFFER BUT THERE IS NOT!!! - // - // MOV ECX,dword ptr [EBP + 0x8] jumptable 00407544 case 31 + // + // MOV ECX,dword ptr [EBP + 0x8] jumptable 00407544 case 31 // CMP dword ptr [0x9d4 + ECX],0x0 // JLE LAB_00407abb // aka ECX = enemy pointer // ECX->9d4 (aka enemy_pointer_copy->comparison_reg) == 0 // only the pointer is copied, not the value, thus we are safe - Instruction::RelativeJumpEx(frame, ip, var_id) { + SubInstruction::RelativeJumpEx(frame, ip, var_id) => { // TODO: counter_value is a field of "enemy" in th06, to check - counter_value = self._getval(var_id) - 1 - if counter_value > 0 { - Instruction::RelativeJump(frame, ip); - } + let counter_value = self.get_i32(var_id) - 1; + if counter_value > 0 { + SubInstruction::RelativeJump(frame, ip); + } } - - //4, 5 - Instruction::SetVariable(var_id, value) { - self._setval(var_id, value); + // 4 + SubInstruction::SetInt(var_id, value) => { + self.set_i32(var_id, value); + } + // 5 + SubInstruction::SetFloat(var_id, value) => { + self.set_f32(var_id as f32, value); } // 6 - Instruction::SetRandomInt(var_id, maxval) { - self._setval(var_id, self._game.prng.rand_32()%self._getval(maxval)); + SubInstruction::SetRandomInt(var_id, maxval) => { + let random = self.get_prng().borrow_mut().get_u32() as i32; + self.set_i32(var_id, random % self.get_i32(maxval)); } // 7 - Instruction::SetRandomIntMin(var_id, maxval, minval) { - self._setval(var_id, (self._game.prng.rand_32()%self._getval(maxval))+self._getval(minval)); + /* + SubInstruction::SetRandomIntMin(var_id, maxval, minval) => { + self.set_i32(var_id, (self.get_prng().borrow_mut().get_u32() % self.get_i32(maxval)) + self.get_i32(minval)); } + */ // 8 - Instruction::SetRandomFloat(var_id, maxval) { - self._setval(var_id, self._getval(maxval) * self._game.prng.rand_double()) + SubInstruction::SetRandomFloat(var_id, maxval) => { + let random = self.get_prng().borrow_mut().get_f64() as f32; + self.set_f32(var_id as f32, self.get_f32(maxval) * random) } // 9 - Instruction::SetRandomFloatMin(var_id, maxval, minval) { - self._setval(var_id, (self._getval(maxval) * self._game.prng.rand_double())+self._getval(minval)) + SubInstruction::SetRandomFloatMin(var_id, maxval, minval) => { + let random = self.get_prng().borrow_mut().get_f64() as f32; + self.set_f32(var_id as f32, self.get_f32(maxval) * random + self.get_f32(minval)) } // 10 - Instruction::StoreX(var_id) { - self._setval(var_id, self._enemy.x); + SubInstruction::StoreX(var_id) => { + let x = { + let enemy = self.enemy.borrow(); + enemy.pos.x + }; + // TODO: is this really an i32? + self.set_i32(var_id, x as i32); } // 11 - Instruction::StoreY(var_id) { - self._setval(var_id, self._enemy.y); + /* + SubInstruction::StoreY(var_id) => { + let enemy = self.enemy.borrow(); + self.set_i32(var_id, enemy.pos.y); } + */ // 12 - Instruction::StoreZ(var_id) { - self._setval(var_id, self._enemy.z); + /* + SubInstruction::StoreZ(var_id) => { + let enemy = self.enemy.borrow(); + self.set_i32(var_id, enemy.z); } + */ // 13(int), 20(float), same impl in th06 - Instruction::Add(var_id, a, b) { - self._setval(var_id, self._getval(a) + self._getval(b)); + SubInstruction::AddInt(var_id, a, b) => { + self.set_i32(var_id, self.get_i32(a) + self.get_i32(b)); + } + SubInstruction::AddFloat(var_id, a, b) => { + self.set_f32(var_id as f32, self.get_f32(a) + self.get_f32(b)); } // 14(int), 21(float), same impl in th06 - Instruction::Substract(var_id, a, b) { - self._setval(var_id, self._getval(a) - self._getval(b)); + SubInstruction::SubstractInt(var_id, a, b) => { + self.set_i32(var_id, self.get_i32(a) - self.get_i32(b)); + } + SubInstruction::SubstractFloat(var_id, a, b) => { + self.set_f32(var_id as f32, self.get_f32(a) - self.get_f32(b)); } // 15(int), 22(unused) - Instruction::Multiply(var_id, a, b) { - self._setval(var_id, self._getval(a) * self._getval(b)); + SubInstruction::MultiplyInt(var_id, a, b) => { + self.set_i32(var_id, self.get_i32(a) * self.get_i32(b)); } + /* + SubInstruction::MultiplyFloat(var_id, a, b) => { + self.set_f32(var_id as f32, self.get_f32(a) * self.get_f32(b)); + } + */ // 16(int), 23(unused) - Instruction::Divide(var_id, a, b) { - self._setval(var_id, self._getval(a) / self._getval(b)); + SubInstruction::DivideInt(var_id, a, b) => { + self.set_i32(var_id, self.get_i32(a) / self.get_i32(b)); } + /* + SubInstruction::Divide(var_id, a, b) => { + self.set_f32(var_id as f32, self.get_f32(a) / self.get_f32(b)); + } + */ // 17(int) 24(unused) - Instruction::Divide(var_id, a, b) { - self._setval(var_id, self._getval(a) % self._getval(b)); + SubInstruction::ModuloInt(var_id, a, b) => { + self.set_i32(var_id, self.get_i32(a) % self.get_i32(b)); } + /* + SubInstruction::ModuloFloat(var_id, a, b) => { + self.set_f32(var_id as f32, self.get_f32(a) % self.get_f32(b)); + } + */ // 18 // setval used by pytouhou, but not in game(???) - Instruction::Increment(var_id) { - var_id = self._getval(var_id) + 1 + SubInstruction::Increment(var_id) => { + self.set_i32(var_id, self.get_i32(var_id) + 1); } // 19 - Instruction::Decrement(var_id) { - var_id = self._getval(var_id) - 1 + /* + SubInstruction::Decrement(var_id) => { + self.set_i32(var_id, self.get_i32(var_id) - 1); } + */ //25 - Instruction::GetDirection(var_id, x1, y1, x2, y2) { + SubInstruction::GetDirection(var_id, x1, y1, x2, y2) => { //__ctrandisp2 in ghidra, let's assume from pytouhou it's atan2 - self._setval(var_id, atan2(self._getval(y2) - self._getval(y1), self._getval(x2) - self._getval(x1))); + self.set_f32(var_id as f32, (self.get_f32(y2) - self.get_f32(y1)).atan2(self.get_f32(x2) - self.get_f32(x1))); } // 26 - Instruction::FloatToUnitCircle(var_id) { - // TODO: atan2(var_id, ??) is used by th06, maybe ?? is pi? + SubInstruction::FloatToUnitCircle(var_id) => { + // TODO: atan2(var_id, ??) is used by th06, maybe ?? is pi? // we suck at trigonometry so let's use pytouhou for now - self._setval(var_id, (self._getval(var_id) + pi) % (2*pi) - pi); + self.set_f32(var_id as f32, (self.get_f32(var_id as f32) + std::f32::consts::PI) % (2. * std::f32::consts::PI) - std::f32::consts::PI); } // 27(int), 28(float) - Instruction::Compare(a, b) { - a = self._getval(a); - b = self._getval(b); + SubInstruction::CompareInts(a, b) => { + let a = self.get_i32(a); + let b = self.get_i32(b); if a < b { - self.comparison_reg = -1 + self.comparison_reg = -1; } else if a == b { - self.comparison_reg = 0 + self.comparison_reg = 0; } else { - self.comparison_reg = 1 + self.comparison_reg = 1; } } - // 29 - Instruction::RelativeJumpIfLowerThan(frame, ip) { - if self.comparison_reg == -1 { - Instruction::RelativeJump(); + SubInstruction::CompareFloats(a, b) => { + let a = self.get_f32(a); + let b = self.get_f32(b); + if a < b { + self.comparison_reg = -1; + } + else if a == b { + self.comparison_reg = 0; + } + else { + self.comparison_reg = 1; } } - // 30 - Instruction::RelativeJumpIfLowerOrEqual(frame, ip) { - if self.comparison_reg != 1 { - Instruction::RelativeJump(); + // 29 + SubInstruction::RelativeJumpIfLowerThan(frame, ip) => { + if self.comparison_reg == -1 { + SubInstruction::RelativeJump(frame, ip); } } - // 31 - Instruction::RelativeJumpIfEqual(frame, ip) { - if self.comparison_reg == 0 { - Instruction::RelativeJump(); + // 30 + SubInstruction::RelativeJumpIfLowerOrEqual(frame, ip) => { + if self.comparison_reg != 1 { + SubInstruction::RelativeJump(frame, ip); } } - // 32 - Instruction::RelativeJumpIfGreaterThan(frame, ip) { + // 31 + SubInstruction::RelativeJumpIfEqual(frame, ip) => { + if self.comparison_reg == 0 { + SubInstruction::RelativeJump(frame, ip); + } + } + // 32 + SubInstruction::RelativeJumpIfGreaterThan(frame, ip) => { if self.comparison_reg == 1 { - Instruction::RelativeJump(); + SubInstruction::RelativeJump(frame, ip); } } // 33 - Instruction::RelativeJumpIfGreaterOrEqual(frame, ip) { - if self.comparison_reg != -1 - Instruction::RelativeJump(); + SubInstruction::RelativeJumpIfGreaterOrEqual(frame, ip) => { + if self.comparison_reg != -1 { + SubInstruction::RelativeJump(frame, ip); + } } // 34 - Instruction::RelativeJumpIfNotEqual(frame, ip) { - if self.comparison_reg != 0 - Instruction::RelativeJump(); + SubInstruction::RelativeJumpIfNotEqual(frame, ip) => { + if self.comparison_reg != 0 { + SubInstruction::RelativeJump(frame, ip); + } } // 35 - Instruction::Call(sub, param1, param2) { - // does insane stuff with the stack, not implemented + SubInstruction::Call(sub, param1, param2) => { + // does insane stuff with the stack, not implemented + unimplemented!() } - + // 36 - Instruction::Ret(frame, ip) { - // does insane stuff with the stack, not implemented + SubInstruction::Return() => { + // does insane stuff with the stack, not implemented + unimplemented!() } // 37 - Instruction::CallIfSuperior(sub, param1, param2, a, b) { - if(self._getval(b) <= self._getval(a)) { - Instruction::Call(sub, param1, param2); - } - } - // 38 - Instruction::CallIfSuperiorOrEqual(sub, param1, param2, a, b) { - if(self._getval(b) <= self._getval(a)) { - Instruction::Call(sub, param1, param2); + /* + SubInstruction::CallIfSuperior(sub, param1, param2, a, b) => { + if self.get_i32(b) <= self.get_i32(a) { + SubInstruction::Call(sub, param1, param2); } } - // 39 - Instruction::CallIfEqual(sub, param1, param2, a, b) { - if(self._getval(b) == self._getval(a)) { - Instruction::Call(sub, param1, param2); + */ + // 38 + /* + SubInstruction::CallIfSuperiorOrEqual(sub, param1, param2, a, b) => { + if self.get_i32(b) <= self.get_i32(a) { + SubInstruction::Call(sub, param1, param2); } } - // 40 - Instruction::CallIfEqual(sub, param1, param2, a, b) { - if(self._getval(b) == self._getval(a)) { - Instruction::Call(sub, param1, param2); + */ + // 39 + SubInstruction::CallIfEqual(sub, param1, param2, a, b) => { + if self.get_i32(b) == self.get_i32(a) { + SubInstruction::Call(sub, param1, param2); + } + } + // 40 + /* + SubInstruction::CallIfEqual(sub, param1, param2, a, b) => { + if self.get_i32(b) == self.get_i32(a) { + SubInstruction::Call(sub, param1, param2); } } - //41 - Instruction::CallIfInferior(sub, param1, param2, a, b) { - if(self._getval(a) < self._getval(b)) { - Instruction::Call(sub, param1, param2); + */ + //41 + /* + SubInstruction::CallIfInferior(sub, param1, param2, a, b) => { + if self.get_i32(a) < self.get_i32(b) { + SubInstruction::Call(sub, param1, param2); } } - //42 - Instruction::CallIfInferiorOrEqual(sub, param1, param2, a, b) { - if(self._getval(a) <= self._getval(b)) { - Instruction::Call(sub, param1, param2); + */ + //42 + /* + SubInstruction::CallIfInferiorOrEqual(sub, param1, param2, a, b) => { + if self.get_i32(a) <= self.get_i32(b) { + SubInstruction::Call(sub, param1, param2); } } - // 43 - Instruction::SetPos(x, y, z) { - self._enemy.set_pos(self._getval(x), self._getval(y), self._getval(z)); + */ + // 43 + SubInstruction::SetPosition(x, y, z) => { + let mut enemy = self.enemy.borrow_mut(); + enemy.set_pos(self.get_f32(x), self.get_f32(y), self.get_f32(z)); } - // 44 - Instruction::SetPosInterlacing(x, y, z) { + // 44 + /* + SubInstruction::SetPositionInterlacing(x, y, z) => { //TODO: almost the same as setpos, except with 3 different values and sets the - //interlacing, should double check - self._enemy.set_pos(self._getval(x), self._getval(y), self._getval(z)); - } - // 45 - Instruction::SetAngleSpeed(angle, speed) { - self._enemy.update_mode = 0; - self._enemy.angle, self._enemy.speed = self._getval(angle), self._getval(speed); + //interlacing, should double check + let mut enemy = self.enemy.borrow_mut(); + enemy.set_pos(self.get_f32(x), self.get_f32(y), self.get_f32(z)); } - // 46 - Instruction::SetRotationSpeed(speed) { - self._enemy.update_mode = 0 - self._enemy.rotation_speed = self._getval(speed) + */ + // 45 + SubInstruction::SetAngleAndSpeed(angle, speed) => { + let mut enemy = self.enemy.borrow_mut(); + enemy.update_mode = 0; + enemy.angle = self.get_f32(angle); + enemy.speed = self.get_f32(speed); } - // 47 - Instruction::SetSpeed(speed) { - self._enemy.update_mode = 0 - self._enemy.speed = self._getval(speed) + // 46 + SubInstruction::SetRotationSpeed(speed) => { + let mut enemy = self.enemy.borrow_mut(); + enemy.update_mode = 0; + enemy.rotation_speed = self.get_f32(speed); } - // 48 - Instruction::SetAcceleration(acceleration) { - self._enemy.update_mode = 0 - self._enemy.acceleration = self._getval(acceleration) + // 47 + SubInstruction::SetSpeed(speed) => { + let mut enemy = self.enemy.borrow_mut(); + enemy.update_mode = 0; + enemy.speed = self.get_f32(speed); + } + // 48 + SubInstruction::SetAcceleration(acceleration) => { + let mut enemy = self.enemy.borrow_mut(); + enemy.update_mode = 0; + enemy.acceleration = self.get_f32(acceleration); } // 49 - Instruction::SetRandomAngle(min_angle, max_angle) { - angle = self._game.prng.rand_double() * (max_angle - min_angle) + min_angle - self._enemy.angle = angle + SubInstruction::SetRandomAngle(min_angle, max_angle) => { + let angle = self.get_prng().borrow_mut().get_f64() as f32 * (max_angle - min_angle) + min_angle; + let mut enemy = self.enemy.borrow_mut(); + enemy.angle = angle; } // 83 -> star items >>> life items - - - - - - - - - + _ => unimplemented!() + } + } +}