Improve automatic fork selection when resource fork is invalid

This commit is contained in:
dgelessus 2019-08-26 02:24:01 +02:00
parent d7fb67fac1
commit acdbbc89b2
2 changed files with 20 additions and 16 deletions

View File

@ -140,6 +140,10 @@ Version 1.2.0 (next version)
* Currently, only the three standard System 7.0 compression formats (``'dcmp'`` IDs 0, 1, 2) are supported. Attempting to access a resource compressed in an unsupported format results in a ``DecompressError``.
* To access the raw resource data as stored in the file, without automatic decompression, use the ``res.data_raw`` attribute (for the Python API), or the ``--no-decompress`` option (for the command-line interface). This can be used to read the resource data in its compressed form, even if the compression format is not supported.
* Improved automatic data/resource fork selection for files whose resource fork contains invalid data.
* This fixes reading certain system files with resource data in their data fork (such as HIToolbox.rsrc in HIToolbox.framework, or .dfont fonts) on recent macOS versions (at least macOS 10.14, possibly earlier). Although these files have no resource fork, recent macOS versions will successfully open the resource fork and return garbage data for it. This behavior is now detected and handled by using the data fork instead.
* Fixed reading from non-seekable streams not working for some resource files.
* Removed the ``allow_seek`` parameter of ``ResourceFork.__init__`` and the ``--read-mode`` command line option. They are no longer necessary, and were already practically useless before due to non-seekable stream reading being broken.

View File

@ -203,10 +203,14 @@ class ResourceFile(collections.abc.Mapping):
"""Open the file at the given path as a ResourceFile.
If rsrcfork is not None, it is treated as boolean and controls whether the data or resource fork of the file should be opened. (On systems other than macOS, opening resource forks will not work of course, since they don't exist.)
If rsrcfork is None, guess whether the data or resource fork should be opened. If the resource fork exists and is not empty, it is opened, otherwise the data fork is opened instead.
If rsrcfork is None, the resource fork is opened if it exists and contains valid resource data, otherwise the data fork is opened instead.
"""
f: typing.io.BinaryIO
if "close" in kwargs:
raise TypeError("ResourceFile.open does not support the 'close' keyword argument")
kwargs["close"] = True
if rsrcfork is None:
# Determine whether the file has a usable resource fork.
try:
@ -214,29 +218,25 @@ class ResourceFile(collections.abc.Mapping):
f = open(os.path.join(filename, "..namedfork", "rsrc"), "rb")
except (FileNotFoundError, NotADirectoryError):
# If the resource fork doesn't exist, fall back to the data fork.
f = open(filename, "rb")
return cls(open(filename, "rb"), **kwargs)
else:
# Resource fork exists, check if it actually contains valid resource data.
# This check is necessary because opening ..namedfork/rsrc on files that don't actually have a resource fork can sometimes succeed, but the resulting stream will either be empty, or (as of macOS 10.14, and possibly earlier) contain garbage data.
try:
# Resource fork exists, check if it actually contains anything.
if f.read(1):
# Resource fork contains data, seek back to start before using it.
f.seek(0)
else:
# Resource fork contains no data, fall back to the data fork.
f.close()
f = open(filename, "rb")
return cls(f, **kwargs)
except InvalidResourceFileError:
# Resource fork is empty or invalid, fall back to the data fork.
f.close()
return cls(open(filename, "rb"), **kwargs)
except BaseException:
f.close()
raise
elif rsrcfork:
# Force use of the resource fork.
f = open(os.path.join(filename, "..namedfork", "rsrc"), "rb")
return cls(open(os.path.join(filename, "..namedfork", "rsrc"), "rb"), **kwargs)
else:
# Force use of the data fork.
f = open(filename, "rb")
# Use the selected fork to build a ResourceFile.
return cls(f, close=True, **kwargs)
return cls(open(filename, "rb"), **kwargs)
def __init__(self, stream: typing.io.BinaryIO, *, close: bool=False):
"""Create a ResourceFile wrapping the given byte stream.