changeset 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
files examples/eclrenderer.rs src/th06/ecl.rs src/th06/ecl_vm.rs src/th06/enemy.rs src/th06/mod.rs
diffstat 5 files changed, 445 insertions(+), 210 deletions(-) [+]
line wrap: on
line diff
--- a/examples/eclrenderer.rs	Fri Aug 09 04:25:43 2019 +0200
+++ b/examples/eclrenderer.rs	Sat Aug 10 14:41:30 2019 +0200
@@ -13,6 +13,7 @@
 use touhou::th06::anm0::Anm0;
 use touhou::th06::anm0_vm::{Sprite, Vertex as FakeVertex};
 use touhou::th06::ecl::Ecl;
+use touhou::th06::ecl_vm::EclRunner;
 use touhou::th06::enemy::{Enemy, Game, Position};
 use touhou::util::math::{perspective, setup_camera};
 use touhou::util::prng::Prng;
@@ -127,8 +128,8 @@
     let game = Rc::new(RefCell::new(game));
 
     // And the enemy object.
-    let mut enemy = Enemy::new(Position::new(0., 0.), 500, 0, 640, Rc::downgrade(&anm0), Rc::downgrade(&game));
-    enemy.set_anim(0);
+    let enemy = Enemy::new(Position::new(0., 0.), 500, 0, 640, Rc::downgrade(&anm0), Rc::downgrade(&game));
+    let mut ecl_runner = EclRunner::new(&ecl, enemy.clone(), 0);
 
     assert_eq!(std::mem::size_of::<Vertex>(), std::mem::size_of::<FakeVertex>());
     let vertices: [Vertex; 4] = unsafe { std::mem::uninitialized() };
@@ -174,6 +175,7 @@
                 .as_slice_mut()
                 .unwrap();
 
+            ecl_runner.run_frame();
             let mut game = game.borrow_mut();
             game.run_frame();
             let sprites = game.get_sprites();
--- a/src/th06/ecl.rs	Fri Aug 09 04:25:43 2019 +0200
+++ b/src/th06/ecl.rs	Sat Aug 10 14:41:30 2019 +0200
@@ -11,7 +11,7 @@
 #[derive(Debug, Clone)]
 pub struct CallSub {
     /// Time at which this instruction will be called.
-    pub time: u32,
+    pub time: i32,
 
     /// TODO
     pub rank_mask: u16,
@@ -149,19 +149,19 @@
 declare_sub_instructions!{
     0 => fn Noop(),
     1 => fn Destroy(unused: u32),
-    2 => fn RelativeJump(frame: u32, ip: i32),
-    3 => fn RelativeJumpEx(frame: u32, ip: i32, variable_id: i32),
+    2 => fn RelativeJump(frame: i32, ip: i32),
+    3 => fn RelativeJumpEx(frame: i32, ip: i32, variable_id: i32),
     4 => fn SetInt(var: i32, value: i32),
     5 => fn SetFloat(var: i32, value: f32),
     6 => fn SetRandomInt(var: i32, max: i32),
     8 => fn SetRandomFloat(var: i32, max: f32),
-    9 => fn SetRandomFloat2(var: i32, amplitude: f32, min: f32),
+    9 => fn SetRandomFloatMin(var: i32, amplitude: f32, min: f32),
     10 => fn StoreX(var: i32),
     13 => fn AddInt(var: i32, a: i32, b: i32),
     14 => fn SubstractInt(var: i32, a: i32, b: i32),
     15 => fn MultiplyInt(var: i32, a: i32, b: i32),
     16 => fn DivideInt(var: i32, a: i32, b: i32),
-    17 => fn Modulo(var: i32, a: i32, b: i32),
+    17 => fn ModuloInt(var: i32, a: i32, b: i32),
     18 => fn Increment(var: i32),
     20 => fn AddFloat(var: i32, a: f32, b: f32),
     21 => fn SubstractFloat(var: i32, a: f32, b: f32),
@@ -287,9 +287,9 @@
         let mut i = &input[offset..];
         let mut instructions = Vec::new();
         loop {
-            let (i2, time) = le_u32(i)?;
+            let (i2, time) = le_i32(i)?;
             let (i2, opcode) = le_u16(i2)?;
-            if time == 0xffffffff || opcode == 0xffff {
+            if time == -1 || opcode == 0xffff {
                 break;
             }
 
--- a/src/th06/ecl_vm.rs	Fri Aug 09 04:25:43 2019 +0200
+++ b/src/th06/ecl_vm.rs	Sat Aug 10 14:41:30 2019 +0200
@@ -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 @@
             }
             // 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!()
+        }
+    }
+}
--- a/src/th06/enemy.rs	Fri Aug 09 04:25:43 2019 +0200
+++ b/src/th06/enemy.rs	Sat Aug 10 14:41:30 2019 +0200
@@ -11,8 +11,8 @@
 /// The 2D position of an object in the game.
 #[derive(Debug, Clone, Copy, Default)]
 pub struct Position {
-    x: f32,
-    y: f32,
+    pub(crate) x: f32,
+    pub(crate) y: f32,
 }
 
 /// An offset which can be added to a Position.
@@ -60,6 +60,8 @@
     enemies: Vec<Rc<RefCell<Enemy>>>,
     anmrunners: Vec<Rc<RefCell<AnmRunner>>>,
     prng: Rc<RefCell<Prng>>,
+    rank: i32,
+    difficulty: i32,
 }
 
 impl Game {
@@ -69,6 +71,8 @@
             enemies: Vec::new(),
             anmrunners: Vec::new(),
             prng,
+            rank: 0,
+            difficulty: 0,
         }
     }
 
@@ -109,52 +113,53 @@
 #[derive(Default)]
 pub struct Enemy {
     // Common to all elements in game.
-    pos: Position,
-    removed: bool,
-    anmrunner: Weak<RefCell<AnmRunner>>,
+    pub(crate) pos: Position,
+    pub(crate) removed: bool,
+    pub(crate) anmrunner: Weak<RefCell<AnmRunner>>,
 
     // Specific to enemy.
     // Floats.
-    z: f32,
-    angle: f32,
-    speed: f32,
-    rotation_speed: f32,
-    acceleration: f32,
+    pub(crate) z: f32,
+    pub(crate) angle: f32,
+    pub(crate) speed: f32,
+    pub(crate) rotation_speed: f32,
+    pub(crate) acceleration: f32,
 
     // Ints.
-    type_: u32,
-    bonus_dropped: u32,
-    die_score: u32,
-    frame: u32,
-    life: u32,
-    death_flags: u32,
-    current_laser_id: u32,
-    low_life_trigger: Option<u32>,
-    timeout: Option<u32>,
-    remaining_lives: u32,
-    bullet_launch_interval: u32,
-    bullet_launch_timer: u32,
-    death_anim: u32,
-    direction: u32,
-    update_mode: u32,
+    pub(crate) type_: u32,
+    pub(crate) bonus_dropped: u32,
+    pub(crate) die_score: u32,
+    /// XXX
+    pub frame: u32,
+    pub(crate) life: u32,
+    pub(crate) death_flags: u32,
+    pub(crate) current_laser_id: u32,
+    pub(crate) low_life_trigger: Option<u32>,
+    pub(crate) timeout: Option<u32>,
+    pub(crate) remaining_lives: u32,
+    pub(crate) bullet_launch_interval: u32,
+    pub(crate) bullet_launch_timer: u32,
+    pub(crate) death_anim: u32,
+    pub(crate) direction: u32,
+    pub(crate) update_mode: u32,
 
     // Bools.
-    visible: bool,
-    was_visible: bool,
-    touchable: bool,
-    collidable: bool,
-    damageable: bool,
-    boss: bool,
-    automatic_orientation: bool,
-    delay_attack: bool,
+    pub(crate) visible: bool,
+    pub(crate) was_visible: bool,
+    pub(crate) touchable: bool,
+    pub(crate) collidable: bool,
+    pub(crate) damageable: bool,
+    pub(crate) boss: bool,
+    pub(crate) automatic_orientation: bool,
+    pub(crate) delay_attack: bool,
 
     // Tuples.
-    difficulty_coeffs: (f32, f32, u32, u32, u32, u32),
-    extended_bullet_attributes: Option<(u32, u32, u32, u32, f32, f32, f32, f32)>,
-    bullet_attributes: Option<(i16, i16, u32, u32, u32, f32, f32, f32, f32, u32)>,
-    bullet_launch_offset: Offset,
-    movement_dependant_sprites: Option<(f32, f32, f32, f32)>,
-    screen_box: Option<(f32, f32, f32, f32)>,
+    pub(crate) difficulty_coeffs: (f32, f32, u32, u32, u32, u32),
+    pub(crate) extended_bullet_attributes: Option<(u32, u32, u32, u32, f32, f32, f32, f32)>,
+    pub(crate) bullet_attributes: Option<(i16, i16, u32, u32, u32, f32, f32, f32, f32, u32)>,
+    pub(crate) bullet_launch_offset: Offset,
+    pub(crate) movement_dependant_sprites: Option<(f32, f32, f32, f32)>,
+    pub(crate) screen_box: Option<(f32, f32, f32, f32)>,
 
     // Callbacks.
     death_callback: Option<Callback>,
@@ -170,21 +175,22 @@
     options: Vec<Element>,
 
     // Interpolators.
-    interpolator: Option<Interpolator2<f32>>,
-    speed_interpolator: Option<Interpolator1<f32>>,
+    pub(crate) interpolator: Option<Interpolator2<f32>>,
+    pub(crate) speed_interpolator: Option<Interpolator1<f32>>,
 
     // Misc stuff, do we need them?
-    anm0: Weak<RefCell<Anm0>>,
+    pub(crate) anm0: Weak<RefCell<Anm0>>,
     process: Rc<RefCell<Process>>,
-    game: Weak<RefCell<Game>>,
-    prng: Weak<RefCell<Prng>>,
-    hitbox_half_size: [f32; 2],
+    pub(crate) game: Weak<RefCell<Game>>,
+    pub(crate) prng: Weak<RefCell<Prng>>,
+    pub(crate) hitbox_half_size: [f32; 2],
 }
 
 impl Enemy {
     /// Create a new enemy.
-    pub fn new(pos: Position, life: i32, bonus_dropped: u32, die_score: u32, anm0: Weak<RefCell<Anm0>>, game: Weak<RefCell<Game>>) -> Enemy {
-        Enemy {
+    pub fn new(pos: Position, life: i32, bonus_dropped: u32, die_score: u32, anm0: Weak<RefCell<Anm0>>, game: Weak<RefCell<Game>>) -> Rc<RefCell<Enemy>> {
+        let game_rc = game.upgrade().unwrap();
+        let enemy = Enemy {
             pos,
             anm0,
             game,
@@ -197,7 +203,10 @@
             damageable: true,
             difficulty_coeffs: (-0.5, 0.5, 0, 0, 0, 0),
             ..Default::default()
-        }
+        };
+        let enemy = Rc::new(RefCell::new(enemy));
+        game_rc.borrow_mut().enemies.push(enemy.clone());
+        enemy
     }
 
     /// Sets the animation to the one indexed by index in the current anm0.
@@ -211,10 +220,29 @@
         (*game.borrow_mut()).anmrunners.push(anmrunner);
     }
 
+    /// Sets the current position of the enemy.
+    pub fn set_pos(&mut self, x: f32, y: f32, z: f32) {
+        self.pos.x = x;
+        self.pos.y = y;
+        self.z = z;
+    }
+
     /// Sets the hitbox around the enemy.
     pub fn set_hitbox(&mut self, width: f32, height: f32) {
         self.hitbox_half_size = [width, height];
     }
+
+    pub(crate) fn get_rank(&self) -> i32 {
+        let game = self.game.upgrade().unwrap();
+        let game = game.borrow();
+        game.rank
+    }
+
+    pub(crate) fn get_difficulty(&self) -> i32 {
+        let game = self.game.upgrade().unwrap();
+        let game = game.borrow();
+        game.difficulty
+    }
 }
 
 #[cfg(test)]
--- a/src/th06/mod.rs	Fri Aug 09 04:25:43 2019 +0200
+++ b/src/th06/mod.rs	Sat Aug 10 14:41:30 2019 +0200
@@ -4,5 +4,6 @@
 pub mod anm0;
 pub mod anm0_vm;
 pub mod ecl;
+pub mod ecl_vm;
 pub mod enemy;
 pub mod interpolator;