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 {