Mercurial > touhou
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 781:5b43c42fa680 | 782:a30ce01b9154 |
|---|---|
| 1 use pyo3::exceptions::{PyIOError, PyKeyError}; | 1 use pyo3::exceptions::{PyIOError, PyKeyError}; |
| 2 use pyo3::prelude::*; | 2 use pyo3::prelude::*; |
| 3 use pyo3::types::{PyBytes, PyTuple}; | 3 use pyo3::types::{PyBytes, PyTuple}; |
| 4 use touhou_formats::th06::pbg3; | 4 use touhou_formats::th06::pbg3; |
| 5 use touhou_formats::th06::std as stage; | 5 use touhou_formats::th06::std as stage; |
| 6 use std::collections::HashMap; | 6 use touhou_formats::th06::msg; |
| 7 use std::collections::{BTreeMap, HashMap}; | |
| 7 use std::fs::File; | 8 use std::fs::File; |
| 8 use std::io::BufReader; | 9 use std::io::BufReader; |
| 9 use std::path::PathBuf; | 10 use std::path::PathBuf; |
| 10 use std::sync::{Arc, Mutex}; | 11 use std::sync::{Arc, Mutex}; |
| 11 | 12 |
| 70 }; | 71 }; |
| 71 Ok((call.time, opcode, args)) | 72 Ok((call.time, opcode, args)) |
| 72 } | 73 } |
| 73 | 74 |
| 74 self.inner.script.iter().map(|call| call_to_python(py, call).unwrap()).collect() | 75 self.inner.script.iter().map(|call| call_to_python(py, call).unwrap()).collect() |
| 76 } | |
| 77 } | |
| 78 | |
| 79 #[pyclass(module = "libtouhou")] | |
| 80 struct PyMsg { | |
| 81 inner: msg::Msg, | |
| 82 } | |
| 83 | |
| 84 #[pymethods] | |
| 85 impl PyMsg { | |
| 86 #[getter] | |
| 87 fn msgs(&self, py: Python) -> BTreeMap<u8, Vec<(u16, u8, Py<PyTuple>)>> { | |
| 88 fn call_to_python(py: Python, call: &msg::Call) -> PyResult<(u16, u8, Py<PyTuple>)> { | |
| 89 let (opcode, args) = match &call.instr { | |
| 90 msg::Instruction::Unk1() => (0, ().into_pyobject(py)?), | |
| 91 msg::Instruction::Enter(side, effect) => (1, (side, effect).into_pyobject(py)?), | |
| 92 msg::Instruction::ChangeFace(side, index) => (2, (side, index).into_pyobject(py)?), | |
| 93 msg::Instruction::DisplayText(side, index, text) => (3, (side, index, text).into_pyobject(py)?), | |
| 94 msg::Instruction::Pause(duration) => (4, (duration,).into_pyobject(py)?), | |
| 95 msg::Instruction::Animate(side, effect) => (5, (side, effect).into_pyobject(py)?), | |
| 96 msg::Instruction::SpawnEnemySprite() => (6, ().into_pyobject(py)?), | |
| 97 msg::Instruction::ChangeMusic(track) => (7, (track,).into_pyobject(py)?), | |
| 98 msg::Instruction::DisplayDescription(side, index, text) => (8, (side, index, text).into_pyobject(py)?), | |
| 99 msg::Instruction::ShowScores(unk1) => (8, (unk1,).into_pyobject(py)?), | |
| 100 msg::Instruction::Freeze() => (10, ().into_pyobject(py)?), | |
| 101 msg::Instruction::NextStage() => (11, ().into_pyobject(py)?), | |
| 102 msg::Instruction::Unk2() => (12, ().into_pyobject(py)?), | |
| 103 msg::Instruction::SetAllowSkip(boolean) => (13, (boolean,).into_pyobject(py)?), | |
| 104 msg::Instruction::Unk3() => (14, ().into_pyobject(py)?), | |
| 105 }; | |
| 106 Ok((call.time, opcode, args.unbind())) | |
| 107 } | |
| 108 self.inner.scripts.iter().map(|(index, script)| ( | |
| 109 *index, | |
| 110 script.into_iter().map(|call| call_to_python(py, call).unwrap()).collect(), | |
| 111 )).collect() | |
| 75 } | 112 } |
| 76 } | 113 } |
| 77 | 114 |
| 78 /// A loader for Touhou files. | 115 /// A loader for Touhou files. |
| 79 #[pyclass(module = "libtouhou", subclass)] | 116 #[pyclass(module = "libtouhou", subclass)] |
| 144 Err(PyKeyError::new_err(format!("Unknown file {name:?}"))) | 181 Err(PyKeyError::new_err(format!("Unknown file {name:?}"))) |
| 145 } | 182 } |
| 146 } | 183 } |
| 147 | 184 |
| 148 /// Return the given file as an io.BytesIO object. | 185 /// Return the given file as an io.BytesIO object. |
| 149 fn get_file(&self, py: Python, name: String) -> PyResult<Py<PyAny>> { | 186 fn get_file(&self, py: Python, name: &str) -> PyResult<Py<PyAny>> { |
| 150 let vec = self.get_file_internal(&name)?; | 187 let vec = self.get_file_internal(name)?; |
| 151 let bytes = PyBytes::new(py, &vec); | 188 let bytes = PyBytes::new(py, &vec); |
| 152 let io = py.import("io")?; | 189 let io = py.import("io")?; |
| 153 let bytesio_class = io.dict().get_item("BytesIO")?.unwrap(); | 190 let bytesio_class = io.dict().get_item("BytesIO")?.unwrap(); |
| 154 let file = bytesio_class.call1((bytes,))?; | 191 let file = bytesio_class.call1((bytes,))?; |
| 155 Ok(file.unbind()) | 192 Ok(file.unbind()) |
| 156 } | 193 } |
| 157 | 194 |
| 158 fn get_stage(&self, py: Python, name: String) -> PyResult<Py<PyStage>> { | 195 fn get_stage(&self, py: Python, name: &str) -> PyResult<Py<PyStage>> { |
| 159 let vec = self.get_file_internal(&name)?; | 196 let vec = self.get_file_internal(name)?; |
| 160 let (_, inner) = stage::Stage::from_slice(&vec).unwrap(); | 197 let (_, inner) = stage::Stage::from_slice(&vec).unwrap(); |
| 161 Ok(Py::new(py, PyStage { inner })?) | 198 Ok(Py::new(py, PyStage { inner })?) |
| 199 } | |
| 200 | |
| 201 fn get_msg(&self, py: Python, name: &str) -> PyResult<Py<PyMsg>> { | |
| 202 let vec = self.get_file_internal(name)?; | |
| 203 let (_, inner) = msg::Msg::from_slice(&vec).unwrap(); | |
| 204 Ok(Py::new(py, PyMsg { inner })?) | |
| 162 } | 205 } |
| 163 } | 206 } |
| 164 | 207 |
| 165 #[pymodule] | 208 #[pymodule] |
| 166 mod libtouhou { | 209 mod libtouhou { |
