Mercurial > touhou
comparison python/src/lib.rs @ 781:5b43c42fa680
Stop exposing PBG3 to Python
| author | Link Mauve <linkmauve@linkmauve.fr> |
|---|---|
| date | Sun, 09 Nov 2025 19:50:35 +0100 |
| parents | ee09657d3789 |
| children | a30ce01b9154 |
comparison
equal
deleted
inserted
replaced
| 780:1ada4036ab88 | 781:5b43c42fa680 |
|---|---|
| 5 use touhou_formats::th06::std as stage; | 5 use touhou_formats::th06::std as stage; |
| 6 use std::collections::HashMap; | 6 use std::collections::HashMap; |
| 7 use std::fs::File; | 7 use std::fs::File; |
| 8 use std::io::BufReader; | 8 use std::io::BufReader; |
| 9 use std::path::PathBuf; | 9 use std::path::PathBuf; |
| 10 use std::sync::{Arc, Mutex}; | |
| 10 | 11 |
| 11 #[cfg(feature = "glide")] | 12 #[cfg(feature = "glide")] |
| 12 mod glide; | 13 mod glide; |
| 13 | 14 |
| 14 #[pyclass(module = "libtouhou")] | 15 #[pyclass(module = "libtouhou")] |
| 72 | 73 |
| 73 self.inner.script.iter().map(|call| call_to_python(py, call).unwrap()).collect() | 74 self.inner.script.iter().map(|call| call_to_python(py, call).unwrap()).collect() |
| 74 } | 75 } |
| 75 } | 76 } |
| 76 | 77 |
| 77 #[pyclass(module = "libtouhou")] | |
| 78 struct PBG3 { | |
| 79 filename: PathBuf, | |
| 80 inner: pbg3::PBG3<BufReader<File>>, | |
| 81 } | |
| 82 | |
| 83 impl PBG3 { | |
| 84 fn get_file_internal(&mut self, name: &str) -> Vec<u8> { | |
| 85 self.inner.get_file(name, true).unwrap() | |
| 86 } | |
| 87 } | |
| 88 | |
| 89 #[pymethods] | |
| 90 impl PBG3 { | |
| 91 #[staticmethod] | |
| 92 fn from_filename(filename: PathBuf) -> PyResult<PBG3> { | |
| 93 let inner = pbg3::from_path_buffered(&filename)?; | |
| 94 Ok(PBG3 { | |
| 95 filename, | |
| 96 inner | |
| 97 }) | |
| 98 } | |
| 99 | |
| 100 fn __repr__(&self) -> String { | |
| 101 format!("PBG3({})", self.filename.to_str().unwrap()) | |
| 102 } | |
| 103 | |
| 104 #[getter] | |
| 105 fn file_list(&self) -> Vec<String> { | |
| 106 self.inner.list_files().cloned().collect() | |
| 107 } | |
| 108 | |
| 109 fn list_files(&self) -> Vec<String> { | |
| 110 self.inner.list_files().cloned().collect() | |
| 111 } | |
| 112 | |
| 113 fn get_file(&mut self, py: Python, name: &str) -> Py<PyBytes> { | |
| 114 let data = self.get_file_internal(name); | |
| 115 PyBytes::new(py, &data).into() | |
| 116 } | |
| 117 } | |
| 118 | |
| 119 /// A loader for Touhou files. | 78 /// A loader for Touhou files. |
| 120 #[pyclass(module = "libtouhou", get_all, subclass)] | 79 #[pyclass(module = "libtouhou", subclass)] |
| 121 #[derive(Default)] | 80 #[derive(Default)] |
| 122 struct Loader { | 81 struct Loader { |
| 123 /// The file names to the possible executable. | 82 /// The file names to the possible executable. |
| 83 #[pyo3(get)] | |
| 124 exe_files: Vec<PathBuf>, | 84 exe_files: Vec<PathBuf>, |
| 125 | 85 |
| 126 /// The path to the game directory. | 86 /// The path to the game directory. |
| 87 #[pyo3(get)] | |
| 127 game_dir: Option<PathBuf>, | 88 game_dir: Option<PathBuf>, |
| 128 | 89 |
| 129 /// A map from inner filenames to the archive containing them. | 90 /// A map from inner filenames to the archive containing them. |
| 130 known_files: HashMap<String, Py<PBG3>>, | 91 known_files: HashMap<String, Arc<Mutex<pbg3::PBG3<BufReader<File>>>>>, |
| 131 } | 92 } |
| 132 | 93 |
| 133 #[pymethods] | 94 #[pymethods] |
| 134 impl Loader { | 95 impl Loader { |
| 135 /// Create a new Loader for the given game_dir. | 96 /// Create a new Loader for the given game_dir. |
| 144 | 105 |
| 145 /// Scan the game_dir for archives. | 106 /// Scan the game_dir for archives. |
| 146 /// | 107 /// |
| 147 /// paths_lists is a list of ':'-separated glob patterns, the first matching file will be used | 108 /// paths_lists is a list of ':'-separated glob patterns, the first matching file will be used |
| 148 /// and the other ones ignored. | 109 /// and the other ones ignored. |
| 149 fn scan_archives(&mut self, py: Python, paths_lists: Vec<String>) -> PyResult<()> { | 110 fn scan_archives(&mut self, paths_lists: Vec<String>) -> PyResult<()> { |
| 150 for paths in paths_lists.iter() { | 111 for paths in paths_lists.iter() { |
| 151 let found_paths: Vec<_> = paths.split(':').map(|path| { | 112 let found_paths: Vec<_> = paths.split(':').map(|path| { |
| 152 glob::glob(if let Some(game_dir) = self.game_dir.as_ref() { | 113 glob::glob(if let Some(game_dir) = self.game_dir.as_ref() { |
| 153 game_dir.join(path) | 114 game_dir.join(path) |
| 154 } else { | 115 } else { |
| 160 } | 121 } |
| 161 let path = &found_paths[0]; | 122 let path = &found_paths[0]; |
| 162 if let Some(extension) = path.extension() && extension == "exe" { | 123 if let Some(extension) = path.extension() && extension == "exe" { |
| 163 self.exe_files.extend(found_paths); | 124 self.exe_files.extend(found_paths); |
| 164 } else { | 125 } else { |
| 165 let pbg3 = PBG3::from_filename(path.to_owned())?; | 126 let pbg3 = pbg3::from_path_buffered(path)?; |
| 166 let filenames = pbg3.list_files(); | 127 let filenames: Vec<_> = pbg3.list_files().cloned().collect(); |
| 167 let pbg3 = Py::new(py, pbg3)?; | 128 let pbg3 = Arc::new(Mutex::new(pbg3)); |
| 168 for name in filenames { | 129 for name in filenames { |
| 169 self.known_files.insert(name.clone(), Py::clone_ref(&pbg3, py)); | 130 self.known_files.insert(name.clone(), Arc::clone(&pbg3)); |
| 170 } | 131 } |
| 171 } | 132 } |
| 172 } | 133 } |
| 173 Ok(()) | 134 Ok(()) |
| 174 } | 135 } |
| 175 | 136 |
| 176 /// Return the given file as an io.BytesIO object. | 137 /// Return the given file as a Vec<u8>. |
| 177 fn get_file(&self, py: Python, name: String) -> PyResult<Py<PyAny>> { | 138 fn get_file_internal(&self, name: &str) -> PyResult<Vec<u8>> { |
| 178 if let Some(archive) = self.known_files.get(&name) { | 139 if let Some(archive) = self.known_files.get(name) { |
| 179 let mut archive = archive.borrow_mut(py); | 140 let mut archive = archive.lock().unwrap(); |
| 180 let bytes = archive.get_file(py, &name); | 141 let bytes = archive.get_file(name, true)?; |
| 181 let io = py.import("io")?; | 142 Ok(bytes) |
| 182 let bytesio_class = io.dict().get_item("BytesIO")?.unwrap(); | |
| 183 let file = bytesio_class.call1((bytes,))?; | |
| 184 Ok(file.unbind()) | |
| 185 } else { | 143 } else { |
| 186 Err(PyKeyError::new_err(format!("Unknown file {name:?}"))) | 144 Err(PyKeyError::new_err(format!("Unknown file {name:?}"))) |
| 187 } | 145 } |
| 188 } | 146 } |
| 189 | 147 |
| 148 /// Return the given file as an io.BytesIO object. | |
| 149 fn get_file(&self, py: Python, name: String) -> PyResult<Py<PyAny>> { | |
| 150 let vec = self.get_file_internal(&name)?; | |
| 151 let bytes = PyBytes::new(py, &vec); | |
| 152 let io = py.import("io")?; | |
| 153 let bytesio_class = io.dict().get_item("BytesIO")?.unwrap(); | |
| 154 let file = bytesio_class.call1((bytes,))?; | |
| 155 Ok(file.unbind()) | |
| 156 } | |
| 157 | |
| 190 fn get_stage(&self, py: Python, name: String) -> PyResult<Py<PyStage>> { | 158 fn get_stage(&self, py: Python, name: String) -> PyResult<Py<PyStage>> { |
| 191 let archive = self.known_files.get(&name).unwrap(); | 159 let vec = self.get_file_internal(&name)?; |
| 192 let mut archive = archive.borrow_mut(py); | 160 let (_, inner) = stage::Stage::from_slice(&vec).unwrap(); |
| 193 let bytes = archive.get_file_internal(&name); | |
| 194 let (_, inner) = stage::Stage::from_slice(&bytes).unwrap(); | |
| 195 Ok(Py::new(py, PyStage { inner })?) | 161 Ok(Py::new(py, PyStage { inner })?) |
| 196 } | 162 } |
| 197 } | 163 } |
| 198 | 164 |
| 199 #[pymodule] | 165 #[pymodule] |
