Skip to content

Commit 97a67c7

Browse files
committed
util: Replace file collection lambdas with file entry objects.
1 parent 4dbab82 commit 97a67c7

File tree

3 files changed

+103
-48
lines changed

3 files changed

+103
-48
lines changed

openage/cabextract/cab.py

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2015-2023 the openage authors. See copying.md for legal info.
1+
# Copyright 2015-2024 the openage authors. See copying.md for legal info.
22

33
"""
44
Provides CABFile, an extractor for the MSCAB format.
@@ -18,7 +18,7 @@
1818
from ..util.filelike.readonly import PosSavingReadOnlyFileLikeObject
1919
from ..util.filelike.stream import StreamFragment
2020
from ..util.files import read_guaranteed, read_nullterminated_string
21-
from ..util.fslike.filecollection import FileCollection
21+
from ..util.fslike.filecollection import FileCollection, FileEntry
2222
from ..util.math import INF
2323
from ..util.strings import try_decode
2424
from ..util.struct import NamedStruct, Flags
@@ -216,6 +216,28 @@ def verify_checksum(self) -> Union[None, NoReturn]:
216216
raise ValueError("checksum error in MSCAB data block")
217217

218218

219+
class CABEntry(FileEntry):
220+
"""
221+
Entry in a CAB file.
222+
"""
223+
224+
def __init__(self, fileobj: CFFile):
225+
self.fileobj = fileobj
226+
227+
def open_r(self):
228+
return StreamFragment(
229+
self.fileobj.folder.plain_stream,
230+
self.fileobj.pos,
231+
self.fileobj.size
232+
)
233+
234+
def size(self) -> int:
235+
return self.fileobj.size
236+
237+
def mtime(self) -> float:
238+
return self.fileobj.timestamp
239+
240+
219241
class CABFile(FileCollection):
220242
"""
221243
The actual file system-like CAB object.
@@ -275,20 +297,9 @@ def __init__(self, cab: FileLikeObject, offset: int = 0):
275297
"CABFile has multiple entries with the same path: " +
276298
b'/'.join(fileobj.path).decode())
277299

278-
def open_r(fileobj=fileobj):
279-
""" Returns a opened ('rb') file-like object for fileobj. """
280-
return StreamFragment(
281-
fileobj.folder.plain_stream,
282-
fileobj.pos,
283-
fileobj.size
284-
)
285-
286-
self.add_fileentry(fileobj.path, (
287-
open_r,
288-
None,
289-
lambda fileobj=fileobj: fileobj.size,
290-
lambda fileobj=fileobj: fileobj.timestamp
291-
))
300+
file_entry = CABEntry(fileobj)
301+
302+
self.add_fileentry(fileobj.path, file_entry)
292303

293304
def __repr__(self):
294305
return "CABFile"

openage/convert/value_object/read/media/drs.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2013-2022 the openage authors. See copying.md for legal info.
1+
# Copyright 2013-2024 the openage authors. See copying.md for legal info.
22

33
"""
44
Code for reading Genie .DRS archives.
@@ -12,7 +12,7 @@
1212

1313
from .....log import spam, dbg
1414
from .....util.filelike.stream import StreamFragment
15-
from .....util.fslike.filecollection import FileCollection
15+
from .....util.fslike.filecollection import FileCollection, FileEntry
1616
from .....util.strings import decode_until_null
1717
from .....util.struct import NamedStruct
1818

@@ -87,6 +87,23 @@ class DRSFileInfo(NamedStruct):
8787
file_size = "i"
8888

8989

90+
class DRSEntry(FileEntry):
91+
"""
92+
Entry in a DRS archive.
93+
"""
94+
95+
def __init__(self, fileobj: GuardedFile, offset: int, size: int):
96+
self.fileobj = fileobj
97+
self.offset = offset
98+
self.entry_size = size
99+
100+
def open_r(self):
101+
return StreamFragment(self.fileobj, self.offset, self.entry_size)
102+
103+
def size(self) -> int:
104+
return self.entry_size
105+
106+
90107
class DRS(FileCollection):
91108
"""
92109
represents a file archive in DRS format.
@@ -133,14 +150,9 @@ def __init__(self, fileobj: GuardedFile, game_version: GameVersion):
133150
self.tables.append(table_header)
134151

135152
for filename, offset, size in self.read_tables():
136-
def open_r(offset=offset, size=size):
137-
""" Returns a opened ('rb') file-like object for fileobj. """
138-
return StreamFragment(self.fileobj, offset, size)
139-
140-
self.add_fileentry(
141-
[filename.encode()],
142-
(open_r, None, lambda size=size: size, None)
143-
)
153+
file_entry = DRSEntry(self.fileobj, offset, size)
154+
155+
self.add_fileentry([filename.encode()], file_entry)
144156

145157
def read_tables(self) -> typing.Generator[tuple[str, str, str], None, None]:
146158
"""

openage/util/fslike/filecollection.py

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
# Copyright 2015-2022 the openage authors. See copying.md for legal info.
1+
# Copyright 2015-2024 the openage authors. See copying.md for legal info.
22

33
"""
44
Provides Filecollection, a utility class for combining multiple file-like
55
objects to a FSLikeObject.
66
"""
7+
from __future__ import annotations
8+
import typing
79

810
from collections import OrderedDict
911
from io import UnsupportedOperation
@@ -12,6 +14,9 @@
1214
from .abstract import FSLikeObject
1315
from .path import Path
1416

17+
if typing.TYPE_CHECKING:
18+
from openage.util.filelike.stream import StreamFragment
19+
1520

1621
class FileCollection(FSLikeObject):
1722
"""
@@ -59,14 +64,12 @@ def get_direntries(self, parts=None, create: bool = False) -> tuple[OrderedDict,
5964

6065
return entries
6166

62-
def add_fileentry(self, parts, fileentry):
67+
def add_fileentry(self, parts, fileentry: FileEntry):
6368
"""
6469
Adds a file entry (and parent directory entries, if needed).
6570
6671
This method should not be called directly; instead, use the
6772
add_file method of Path objects that were obtained from this.
68-
69-
fileentry must be open_r, open_w, size, mtime.
7073
"""
7174
if not parts:
7275
raise IsADirectoryError("FileCollection.root is a directory")
@@ -79,11 +82,9 @@ def add_fileentry(self, parts, fileentry):
7982

8083
entries[0][name] = fileentry
8184

82-
def get_fileentry(self, parts):
85+
def get_fileentry(self, parts) -> FileEntry:
8386
"""
8487
Gets a file entry. Helper method for internal use.
85-
86-
Returns open_r, open_w, size, mtime
8788
"""
8889
if not parts:
8990
raise IsADirectoryError(
@@ -101,45 +102,45 @@ def get_fileentry(self, parts):
101102

102103
return entries[0][name]
103104

104-
def open_r(self, parts) -> None:
105-
open_r, _, _, _ = self.get_fileentry(parts)
105+
def open_r(self, parts: list[bytes]) -> StreamFragment:
106+
entry = self.get_fileentry(parts)
107+
108+
open_r = entry.open_r()
106109

107110
if open_r is None:
108111
raise UnsupportedOperation(
109112
"not readable: " +
110113
b"/".join(parts).decode(errors='replace'))
111114

112-
return open_r()
115+
return open_r
116+
117+
def open_w(self, parts: list[bytes]):
118+
entry = self.get_fileentry(parts)
113119

114-
def open_w(self, parts) -> None:
115-
_, open_w, _, _ = self.get_fileentry(parts)
120+
open_w = entry.open_w()
116121

117122
if open_w is None:
118123
raise UnsupportedOperation(
119124
"not writable: " +
120125
b"/".join(parts).decode(errors='replace'))
121126

127+
return open_w
128+
122129
def list(self, parts):
123130
fileentries, subdirs = self.get_direntries(parts)
124131

125132
yield from subdirs
126133
yield from fileentries
127134

128135
def filesize(self, parts) -> int:
129-
_, _, filesize, _ = self.get_fileentry(parts)
136+
entry = self.get_fileentry(parts)
130137

131-
if filesize is None:
132-
return None
133-
134-
return filesize()
138+
return entry.size()
135139

136140
def mtime(self, parts) -> float:
137-
_, _, _, mtime = self.get_fileentry(parts)
138-
139-
if mtime is None:
140-
return None
141+
entry = self.get_fileentry(parts)
141142

142-
return mtime()
143+
return entry.mtime()
143144

144145
def mkdirs(self, parts) -> None:
145146
self.get_direntries(parts, create=True)
@@ -248,3 +249,34 @@ def add_file_from_path(self, path: Path) -> None:
248249
open_w = None
249250

250251
self.add_file(path.open_r, open_w, path.filesize, path.mtime)
252+
253+
254+
class FileEntry:
255+
"""
256+
Entry in a file collection archive.
257+
"""
258+
# pylint: disable=no-self-use
259+
260+
def open_r(self) -> StreamFragment:
261+
"""
262+
Returns a file-like object for reading.
263+
"""
264+
raise UnsupportedOperation("FileEntry.open_r")
265+
266+
def open_w(self):
267+
"""
268+
Returns a file-like object for writing.
269+
"""
270+
raise UnsupportedOperation("FileEntry.open_w")
271+
272+
def size(self) -> int:
273+
"""
274+
Returns the size of the entr<.
275+
"""
276+
raise UnsupportedOperation("FileEntry.size")
277+
278+
def mtime(self) -> float:
279+
"""
280+
Returns the modification time of the entry.
281+
"""
282+
raise UnsupportedOperation("FileEntry.mtime")

0 commit comments

Comments
 (0)