comparison pytouhou/resource/loader.py @ 778:816e1f01d650

Partially replace the Loader with a Rust one
author Link Mauve <linkmauve@linkmauve.fr>
date Sat, 08 Nov 2025 18:26:01 +0100
parents 79c3f782dd41
children ee09657d3789
comparison
equal deleted inserted replaced
777:11249e4b4e03 778:816e1f01d650
10 ## but WITHOUT ANY WARRANTY; without even the implied warranty of 10 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 ## GNU General Public License for more details. 12 ## GNU General Public License for more details.
13 ## 13 ##
14 14
15 import os 15 from libtouhou import Loader as RustLoader
16 from glob import glob
17 from itertools import chain
18 from io import BytesIO
19
20 from pytouhou.formats import WrongFormatError
21 from libtouhou import PBG3
22 from pytouhou.formats.std import Stage 16 from pytouhou.formats.std import Stage
23 from pytouhou.formats.ecl import ECL 17 from pytouhou.formats.ecl import ECL
24 from pytouhou.formats.anm0 import ANM0 18 from pytouhou.formats.anm0 import ANM0
25 from pytouhou.formats.msg import MSG 19 from pytouhou.formats.msg import MSG
26 from pytouhou.formats.sht import SHT 20 from pytouhou.formats.sht import SHT
32 26
33 logger = get_logger(__name__) 27 logger = get_logger(__name__)
34 28
35 29
36 30
37 class Directory: 31 class Loader(RustLoader):
38 def __init__(self, path):
39 self.path = path
40
41
42 def __enter__(self):
43 return self
44
45
46 def __exit__(self, type, value, traceback):
47 return False
48
49
50 @property
51 def file_list(self):
52 return self.list_files()
53
54 def list_files(self):
55 file_list = []
56 for path in os.listdir(self.path):
57 if os.path.isfile(os.path.join(self.path, path)):
58 file_list.append(path)
59 return file_list
60
61
62 def get_file(self, name):
63 return open(os.path.join(self.path, str(name)), 'rb')
64
65
66
67 class ArchiveDescription:
68 _formats = {b'PBG3': PBG3}
69
70 @classmethod
71 def get_from_path(cls, path):
72 if os.path.isdir(path):
73 instance = Directory(path)
74 file_list = instance.list_files()
75 return instance
76 with open(path, 'rb') as file:
77 magic = file.read(4)
78 format_class = cls._formats[magic]
79 return format_class.from_filename(path)
80
81
82
83 class Loader:
84 def __init__(self, game_dir=None): 32 def __init__(self, game_dir=None):
85 self.exe_files = []
86 self.game_dir = game_dir
87 self.known_files = {}
88 self.instanced_anms = {} # Cache for the textures. 33 self.instanced_anms = {} # Cache for the textures.
89 self.loaded_anms = [] # For the double loading warnings. 34 self.loaded_anms = [] # For the double loading warnings.
90
91
92 def scan_archives(self, paths_lists):
93 for paths in paths_lists:
94 def _expand_paths():
95 for path in paths.split(os.path.pathsep):
96 if self.game_dir and not os.path.isabs(path):
97 path = os.path.join(self.game_dir, path)
98 yield glob(path)
99 paths = list(chain(*_expand_paths()))
100 if not paths:
101 raise IOError
102 path = paths[0]
103 if os.path.splitext(path)[1] == '.exe':
104 self.exe_files.extend(paths)
105 else:
106 archive_description = ArchiveDescription.get_from_path(path)
107 for name in archive_description.file_list:
108 self.known_files[name] = archive_description
109
110
111 def get_file(self, name):
112 archive = self.known_files[name]
113 file = archive.get_file(name)
114 if isinstance(file, bytes):
115 return BytesIO(file)
116 return file
117 35
118 36
119 def get_anm(self, name): 37 def get_anm(self, name):
120 if name in self.loaded_anms: 38 if name in self.loaded_anms:
121 logger.warning('ANM0 %s already loaded', name) 39 logger.warning('ANM0 %s already loaded', name)