diff python/src/lib.rs @ 782:a30ce01b9154

formats: Rewrite msg parsing in Rust
author Link Mauve <linkmauve@linkmauve.fr>
date Thu, 20 Nov 2025 19:02:19 +0100
parents 5b43c42fa680
children ec1e06402a97
line wrap: on
line diff
--- a/python/src/lib.rs
+++ b/python/src/lib.rs
@@ -3,7 +3,8 @@
 use pyo3::types::{PyBytes, PyTuple};
 use touhou_formats::th06::pbg3;
 use touhou_formats::th06::std as stage;
-use std::collections::HashMap;
+use touhou_formats::th06::msg;
+use std::collections::{BTreeMap, HashMap};
 use std::fs::File;
 use std::io::BufReader;
 use std::path::PathBuf;
@@ -75,6 +76,42 @@
     }
 }
 
+#[pyclass(module = "libtouhou")]
+struct PyMsg {
+    inner: msg::Msg,
+}
+
+#[pymethods]
+impl PyMsg {
+    #[getter]
+    fn msgs(&self, py: Python) -> BTreeMap<u8, Vec<(u16, u8, Py<PyTuple>)>> {
+        fn call_to_python(py: Python, call: &msg::Call) -> PyResult<(u16, u8, Py<PyTuple>)> {
+            let (opcode, args) = match &call.instr {
+                msg::Instruction::Unk1() => (0, ().into_pyobject(py)?),
+                msg::Instruction::Enter(side, effect) => (1, (side, effect).into_pyobject(py)?),
+                msg::Instruction::ChangeFace(side, index) => (2, (side, index).into_pyobject(py)?),
+                msg::Instruction::DisplayText(side, index, text) => (3, (side, index, text).into_pyobject(py)?),
+                msg::Instruction::Pause(duration) => (4, (duration,).into_pyobject(py)?),
+                msg::Instruction::Animate(side, effect) => (5, (side, effect).into_pyobject(py)?),
+                msg::Instruction::SpawnEnemySprite() => (6, ().into_pyobject(py)?),
+                msg::Instruction::ChangeMusic(track) => (7, (track,).into_pyobject(py)?),
+                msg::Instruction::DisplayDescription(side, index, text) => (8, (side, index, text).into_pyobject(py)?),
+                msg::Instruction::ShowScores(unk1) => (8, (unk1,).into_pyobject(py)?),
+                msg::Instruction::Freeze() => (10, ().into_pyobject(py)?),
+                msg::Instruction::NextStage() => (11, ().into_pyobject(py)?),
+                msg::Instruction::Unk2() => (12, ().into_pyobject(py)?),
+                msg::Instruction::SetAllowSkip(boolean) => (13, (boolean,).into_pyobject(py)?),
+                msg::Instruction::Unk3() => (14, ().into_pyobject(py)?),
+            };
+            Ok((call.time, opcode, args.unbind()))
+        }
+        self.inner.scripts.iter().map(|(index, script)| (
+            *index,
+            script.into_iter().map(|call| call_to_python(py, call).unwrap()).collect(),
+        )).collect()
+    }
+}
+
 /// A loader for Touhou files.
 #[pyclass(module = "libtouhou", subclass)]
 #[derive(Default)]
@@ -146,8 +183,8 @@
     }
 
     /// Return the given file as an io.BytesIO object.
-    fn get_file(&self, py: Python, name: String) -> PyResult<Py<PyAny>> {
-        let vec = self.get_file_internal(&name)?;
+    fn get_file(&self, py: Python, name: &str) -> PyResult<Py<PyAny>> {
+        let vec = self.get_file_internal(name)?;
         let bytes = PyBytes::new(py, &vec);
         let io = py.import("io")?;
         let bytesio_class = io.dict().get_item("BytesIO")?.unwrap();
@@ -155,11 +192,17 @@
         Ok(file.unbind())
     }
 
-    fn get_stage(&self, py: Python, name: String) -> PyResult<Py<PyStage>> {
-        let vec = self.get_file_internal(&name)?;
+    fn get_stage(&self, py: Python, name: &str) -> PyResult<Py<PyStage>> {
+        let vec = self.get_file_internal(name)?;
         let (_, inner) = stage::Stage::from_slice(&vec).unwrap();
         Ok(Py::new(py, PyStage { inner })?)
     }
+
+    fn get_msg(&self, py: Python, name: &str) -> PyResult<Py<PyMsg>> {
+        let vec = self.get_file_internal(name)?;
+        let (_, inner) = msg::Msg::from_slice(&vec).unwrap();
+        Ok(Py::new(py, PyMsg { inner })?)
+    }
 }
 
 #[pymodule]