# HG changeset patch # User Emmanuel Gil Peyrot # Date 1564867815 -7200 # Node ID 7d92730bf543dccaccbab7c93fe89d0460d29635 # Parent 7bde50132735bda9187ad03937eb45baaa939d53 Add a PRNG and use it for anm0 instruction 16. diff --git a/examples/anmrenderer.rs b/examples/anmrenderer.rs --- 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::(), std::mem::size_of::()); let mut vertices: [Vertex; 4] = unsafe { std::mem::uninitialized() }; diff --git a/src/th06/anm0_vm.rs b/src/th06/anm0_vm.rs --- 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>, + prng: Weak>, 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>, sprite_index_offset: u32) -> AnmRunner { + pub fn new(anm: &Anm0, script_id: u8, sprite: Rc>, prng: Weak>, 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()); diff --git a/src/util/mod.rs b/src/util/mod.rs --- 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; diff --git a/src/util/prng.rs b/src/util/prng.rs 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) + } +}