changeset 646:7d92730bf543

Add a PRNG and use it for anm0 instruction 16.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Sat, 03 Aug 2019 23:30:15 +0200
parents 7bde50132735
children 1520b559cacc
files examples/anmrenderer.rs src/th06/anm0_vm.rs src/util/mod.rs src/util/prng.rs
diffstat 4 files changed, 57 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/examples/anmrenderer.rs
+++ b/examples/anmrenderer.rs
@@ -13,6 +13,7 @@ use luminance_glfw::surface::{GlfwSurfac
 use touhou::th06::anm0::Anm0;
 use touhou::th06::anm0_vm::{AnmRunner, Sprite, Vertex as FakeVertex};
 use touhou::util::math::{perspective, setup_camera};
+use touhou::util::prng::Prng;
 use std::cell::RefCell;
 use std::fs::File;
 use std::io::{BufReader, Read};
@@ -110,8 +111,11 @@ fn main() {
     // Create the sprite.
     let sprite = Rc::new(RefCell::new(Sprite::new(0., 0.)));
 
+    // TODO: seed this PRNG with a valid seed.
+    let prng = Rc::new(RefCell::new(Prng::new(0)));
+
     // Create the AnmRunner from the ANM and the sprite.
-    let mut anm_runner = AnmRunner::new(&anm0, script, sprite.clone(), 0);
+    let mut anm_runner = AnmRunner::new(&anm0, script, sprite.clone(), Rc::downgrade(&prng), 0);
 
     assert_eq!(std::mem::size_of::<Vertex>(), std::mem::size_of::<FakeVertex>());
     let mut vertices: [Vertex; 4] = unsafe { std::mem::uninitialized() };
--- a/src/th06/anm0_vm.rs
+++ b/src/th06/anm0_vm.rs
@@ -8,8 +8,9 @@ use crate::th06::anm0::{
 };
 use crate::th06::interpolator::{Interpolator1, Interpolator2, Interpolator3, Formula};
 use crate::util::math::Mat4;
+use crate::util::prng::Prng;
 use std::cell::RefCell;
-use std::rc::Rc;
+use std::rc::{Rc, Weak};
 
 /// TODO
 #[repr(C)]
@@ -204,6 +205,7 @@ impl Sprite {
 pub struct AnmRunner {
     anm: Anm0,
     sprite: Rc<RefCell<Sprite>>,
+    prng: Weak<RefCell<Prng>>,
     running: bool,
     sprite_index_offset: u32,
     script: Script,
@@ -216,10 +218,11 @@ pub struct AnmRunner {
 
 impl AnmRunner {
     /// Create a new `AnmRunner`.
-    pub fn new(anm: &Anm0, script_id: u8, sprite: Rc<RefCell<Sprite>>, sprite_index_offset: u32) -> AnmRunner {
+    pub fn new(anm: &Anm0, script_id: u8, sprite: Rc<RefCell<Sprite>>, prng: Weak<RefCell<Prng>>, sprite_index_offset: u32) -> AnmRunner {
         let mut runner = AnmRunner {
             anm: anm.clone(),
             sprite: sprite,
+            prng,
             running: true,
             waiting: false,
 
@@ -347,8 +350,13 @@ impl AnmRunner {
             Instruction::KeepStill() => {
                 self.running = false;
             }
-            Instruction::LoadRandomSprite(min_index, amplitude) => {
-                let sprite_index = min_index; // XXX: + randrange(amplitude);
+            Instruction::LoadRandomSprite(min_index, mut amplitude) => {
+                if amplitude > 0 {
+                    let prng = self.prng.upgrade().unwrap();
+                    let rand = prng.borrow_mut().get_u16();
+                    amplitude = (rand as u32) % amplitude;
+                }
+                let sprite_index = min_index + amplitude;
 
                 // TODO: refactor that with Instruction::LoadSprite.
                 sprite.anm = Some(self.anm.clone());
--- a/src/util/mod.rs
+++ b/src/util/mod.rs
@@ -3,6 +3,7 @@
 pub mod bitstream;
 pub mod lzss;
 pub mod math;
+pub mod prng;
 
 #[cfg(test)]
 use std::io;
new file mode 100644
--- /dev/null
+++ b/src/util/prng.rs
@@ -0,0 +1,39 @@
+//! Random number generator extracted from EoSD.
+
+/// Pseudo-random number generator from EoSD.
+#[derive(Debug)]
+pub struct Prng {
+    seed: u16,
+}
+
+impl Prng {
+    /// Create a new pseudo-random number generator from this seed.
+    pub fn new(seed: u16) -> Prng {
+        Prng {
+            seed,
+        }
+    }
+
+    /// Generates a pseudo-random u16.
+    ///
+    /// RE’d from 102h.exe@0x41e780
+    pub fn get_u16(&mut self) -> u16 {
+        let x = ((self.seed ^ 0x9630) - 0x6553) & 0xffff;
+        self.seed = (((x & 0xc000) >> 14) | (x << 2)) & 0xffff;
+        self.seed
+    }
+
+    /// Combines two u16 into a single u32.
+    ///
+    /// RE’d from 102h.exe@0x41e7f0
+    pub fn get_u32(&mut self) -> u32 {
+        ((self.get_u16() as u32) << 16) | self.get_u16() as u32
+    }
+
+    /// Transforms an u32 into a f64.
+    ///
+    /// RE’d from 102h.exe@0x41e820
+    pub fn get_f64(&mut self) -> f64 {
+        self.get_u32() as f64 / (0x100000000u64 as f64)
+    }
+}