changeset 781:5b43c42fa680

Stop exposing PBG3 to Python
author Link Mauve <linkmauve@linkmauve.fr>
date Sun, 09 Nov 2025 19:50:35 +0100
parents 1ada4036ab88
children a30ce01b9154
files python/src/lib.rs
diffstat 1 files changed, 28 insertions(+), 62 deletions(-) [+]
line wrap: on
line diff
--- a/python/src/lib.rs
+++ b/python/src/lib.rs
@@ -7,6 +7,7 @@
 use std::fs::File;
 use std::io::BufReader;
 use std::path::PathBuf;
+use std::sync::{Arc, Mutex};
 
 #[cfg(feature = "glide")]
 mod glide;
@@ -74,60 +75,20 @@
     }
 }
 
-#[pyclass(module = "libtouhou")]
-struct PBG3 {
-    filename: PathBuf,
-    inner: pbg3::PBG3<BufReader<File>>,
-}
-
-impl PBG3 {
-    fn get_file_internal(&mut self, name: &str) -> Vec<u8> {
-        self.inner.get_file(name, true).unwrap()
-    }
-}
-
-#[pymethods]
-impl PBG3 {
-    #[staticmethod]
-    fn from_filename(filename: PathBuf) -> PyResult<PBG3> {
-        let inner = pbg3::from_path_buffered(&filename)?;
-        Ok(PBG3 {
-            filename,
-            inner
-        })
-    }
-
-    fn __repr__(&self) -> String {
-        format!("PBG3({})", self.filename.to_str().unwrap())
-    }
-
-    #[getter]
-    fn file_list(&self) -> Vec<String> {
-        self.inner.list_files().cloned().collect()
-    }
-
-    fn list_files(&self) -> Vec<String> {
-        self.inner.list_files().cloned().collect()
-    }
-
-    fn get_file(&mut self, py: Python, name: &str) -> Py<PyBytes> {
-        let data = self.get_file_internal(name);
-        PyBytes::new(py, &data).into()
-    }
-}
-
 /// A loader for Touhou files.
-#[pyclass(module = "libtouhou", get_all, subclass)]
+#[pyclass(module = "libtouhou", subclass)]
 #[derive(Default)]
 struct Loader {
     /// The file names to the possible executable.
+    #[pyo3(get)]
     exe_files: Vec<PathBuf>,
 
     /// The path to the game directory.
+    #[pyo3(get)]
     game_dir: Option<PathBuf>,
 
     /// A map from inner filenames to the archive containing them.
-    known_files: HashMap<String, Py<PBG3>>,
+    known_files: HashMap<String, Arc<Mutex<pbg3::PBG3<BufReader<File>>>>>,
 }
 
 #[pymethods]
@@ -146,7 +107,7 @@
     ///
     /// paths_lists is a list of ':'-separated glob patterns, the first matching file will be used
     /// and the other ones ignored.
-    fn scan_archives(&mut self, py: Python, paths_lists: Vec<String>) -> PyResult<()> {
+    fn scan_archives(&mut self, paths_lists: Vec<String>) -> PyResult<()> {
         for paths in paths_lists.iter() {
             let found_paths: Vec<_> = paths.split(':').map(|path| {
                 glob::glob(if let Some(game_dir) = self.game_dir.as_ref() {
@@ -162,36 +123,41 @@
             if let Some(extension) = path.extension() && extension == "exe" {
                 self.exe_files.extend(found_paths);
             } else {
-                let pbg3 = PBG3::from_filename(path.to_owned())?;
-                let filenames = pbg3.list_files();
-                let pbg3 = Py::new(py, pbg3)?;
+                let pbg3 = pbg3::from_path_buffered(path)?;
+                let filenames: Vec<_> = pbg3.list_files().cloned().collect();
+                let pbg3 = Arc::new(Mutex::new(pbg3));
                 for name in filenames {
-                    self.known_files.insert(name.clone(), Py::clone_ref(&pbg3, py));
+                    self.known_files.insert(name.clone(), Arc::clone(&pbg3));
                 }
             }
         }
         Ok(())
     }
 
-    /// Return the given file as an io.BytesIO object.
-    fn get_file(&self, py: Python, name: String) -> PyResult<Py<PyAny>> {
-        if let Some(archive) = self.known_files.get(&name) {
-            let mut archive = archive.borrow_mut(py);
-            let bytes = archive.get_file(py, &name);
-            let io = py.import("io")?;
-            let bytesio_class = io.dict().get_item("BytesIO")?.unwrap();
-            let file = bytesio_class.call1((bytes,))?;
-            Ok(file.unbind())
+    /// Return the given file as a Vec<u8>.
+    fn get_file_internal(&self, name: &str) -> PyResult<Vec<u8>> {
+        if let Some(archive) = self.known_files.get(name) {
+            let mut archive = archive.lock().unwrap();
+            let bytes = archive.get_file(name, true)?;
+            Ok(bytes)
         } else {
             Err(PyKeyError::new_err(format!("Unknown file {name:?}")))
         }
     }
 
+    /// 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)?;
+        let bytes = PyBytes::new(py, &vec);
+        let io = py.import("io")?;
+        let bytesio_class = io.dict().get_item("BytesIO")?.unwrap();
+        let file = bytesio_class.call1((bytes,))?;
+        Ok(file.unbind())
+    }
+
     fn get_stage(&self, py: Python, name: String) -> PyResult<Py<PyStage>> {
-        let archive = self.known_files.get(&name).unwrap();
-        let mut archive = archive.borrow_mut(py);
-        let bytes = archive.get_file_internal(&name);
-        let (_, inner) = stage::Stage::from_slice(&bytes).unwrap();
+        let vec = self.get_file_internal(&name)?;
+        let (_, inner) = stage::Stage::from_slice(&vec).unwrap();
         Ok(Py::new(py, PyStage { inner })?)
     }
 }