.github/workflows | ||
src/rsrcfork | ||
tests | ||
.editorconfig | ||
.gitignore | ||
LICENSE | ||
MANIFEST.in | ||
pyproject.toml | ||
README.md | ||
setup.cfg | ||
setup.py | ||
tox.ini |
rsrcfork
A pure Python, cross-platform library/tool for reading Macintosh resource data,
as stored in resource forks and .rsrc
files.
Resource forks were an important part of the Classic Mac OS, where they provided a standard way to store structured file data, metadata and application resources. This usage continued into Mac OS X (now called macOS) for backward compatibility, but over time resource forks became less commonly used in favor of simple data fork-only formats, application bundles, and extended attributes.
As of OS X 10.8 and the deprecation of the Carbon API, macOS no longer provides any officially supported APIs for using and manipulating resource data. Despite this, parts of macOS still support and use resource forks, for example to store custom file and folder icons set by the user.
Features
-
Pure Python, cross-platform - no native Mac APIs are used.
-
Provides both a Python API and a command-line tool.
-
Resource data can be read from either the resource fork or the data fork.
- On Mac systems, the correct fork is selected automatically when reading a file.
This allows reading both regular resource forks and resource data stored in data forks (as with
.rsrc
and similar files). - On non-Mac systems, resource forks are not available, so the data fork is always used.
- On Mac systems, the correct fork is selected automatically when reading a file.
This allows reading both regular resource forks and resource data stored in data forks (as with
-
Compressed resources (supported by System 7 through Mac OS 9) are automatically decompressed.
- Only the standard System 7.0 resource compression methods are supported. Resources that use non-standard decompressors cannot be decompressed.
-
Object
repr
s are REPL-friendly: all relevant information is displayed, and long data is truncated to avoid filling up the screen by accident.
Requirements
Python 3.6 or later. No other libraries are required.
Installation
rsrcfork
is available on PyPI and can be installed using pip
:
$ python3 -m pip install rsrcfork
Alternatively you can download the source code manually, and run this command in the source code directory to install it:
$ python3 -m pip install .
Examples
Simple example
>>> import rsrcfork
>>> rf = rsrcfork.open("/Users/Shared/Test.textClipping")
>>> rf
<rsrcfork.ResourceFile at 0x1046e6048, attributes ResourceFileAttrs.0, containing 4 resource types: [b'utxt', b'utf8', b'TEXT', b'drag']>
>>> rf[b"TEXT"]
<Resource map for type b'TEXT', containing one resource: <Resource type b'TEXT', id 256, name None, attributes ResourceAttrs.0, data b'Here is some text'>>
Automatic selection of data/resource fork
>>> import rsrcfork
>>> datarf = rsrcfork.open("/System/Library/Fonts/Monaco.dfont") # Resources in data fork
>>> datarf._stream
<_io.BufferedReader name='/System/Library/Fonts/Monaco.dfont'>
>>> resourcerf = rsrcfork.open("/Users/Shared/Test.textClipping") # Resources in resource fork
>>> resourcerf._stream
<_io.BufferedReader name='/Users/Shared/Test.textClipping/..namedfork/rsrc'>
Command-line interface
$ rsrcfork list /Users/Shared/Test.textClipping
4 resource types:
'TEXT': 1 resources:
(256): 17 bytes
'drag': 1 resources:
(128): 64 bytes
'utf8': 1 resources:
(256): 17 bytes
'utxt': 1 resources:
(256): 34 bytes
$ rsrcfork read /Users/Shared/Test.textClipping "'TEXT' (256)"
Resource 'TEXT' (256): 17 bytes:
00000000 48 65 72 65 20 69 73 20 73 6f 6d 65 20 74 65 78 |Here is some tex|
00000010 74 |t|
00000011
Limitations
This library only understands the resource file's general structure, i. e. the type codes, IDs, attributes, and data of the resources stored in the file. The data of individual resources is provided in raw bytes form and is not processed further - the format of this data is specific to each resource type.
Definitions of common resource types can be found inside Carbon and related frameworks in Apple's macOS SDKs as .r
files,
a format roughly similar to C struct definitions,
which is used by the Rez
and DeRez
command-line tools to de/compile resource data.
There doesn't seem to be an exact specification of this format,
and most documentation on it is only available inside old manuals for MPW (Macintosh Programmer's Workshop) or similar development tools for old Mac systems.
Some macOS text editors, such as BBEdit/TextWrangler and TextMate support syntax highlighting for .r
files.
Writing resource data is not supported at all.
Further info on resource files
For technical info and documentation about resource files and resources, see the "resource forks" section of the mac_file_format_docs repo.
Changelog
Version 1.8.1 (next version)
- Added
open
andopen_raw
methods toResource
objects, for stream-based access to resource data. - Fixed reading of compressed resource headers with the header length field incorrectly set to 0 (because real Mac OS apparently accepts this).
Version 1.8.0
- Removed the old (non-subcommand-based) CLI syntax.
- Added filtering support to the
list
subcommand. - Added a
resource-info
subcommand to display technical information about resources (more detailed than what is displayed bylist
andread
). - Added a
raw-compress-info
subcommand to display technical header information about standalone compressed resource data. - Made the library PEP 561-compliant by adding a py.typed file.
- Fixed an incorrect
AssertionError
when using the--no-decompress
command-line options.
Version 1.7.0
-
Added a
raw-decompress
subcommand to decompress compressed resource data stored in a standalone file rather than as a resource. -
Optimized lazy loading of
Resource
objects. Previously, resource data would be read from disk whenever aResource
object was looked up, even if the data itself is never used. Now the resource data is only loaded once thedata
(ordata_raw
) attribute is accessed.- The same optimization applies to the
name
attribute, although this is unlikely to make a difference in practice. - As a result, it is no longer possible to construct
Resource
objects without a resource file. This was previously possible, but had no practical use.
- The same optimization applies to the
-
Fixed a small error in the
'dcmp' (0)
decompression implementation.
Version 1.6.0
-
Added a new subcommand-based command-line syntax to the
rsrcfork
tool, similar to other CLI tools such asgit
ordiskutil
.- This subcommand-based syntax is meant to replace the old CLI options, as the subcommand structure is easier to understand and more extensible in the future.
- Currently there are three subcommands:
list
to list resources in a file,read
to read/display resource data, andread-header
to read a resource file's header data. These subcommands can be used to perform all operations that were also available with the old CLI syntax. - The old CLI syntax is still supported for now, but it will be removed soon.
- The new syntax no longer supports reading CLI arguments from a file (using
@args_file.txt
), abbreviating long options (e. g.--no-d
instead of--no-decompress
), or the short option-f
instead of--fork
. If you have a need for any of these features, please open an issue.
Version 1.5.0
-
Added stream-based decompression methods to the
rsrcfork.compress
module.- The internal decompressor implementations have been refactored to use streams.
- This allows for incremental decompression of compressed resource data.
In practice this has no noticeable effect yet,
because the main
rsrcfork
API doesn't support incremental reading of resource data.
-
Fixed the command line tool always displaying an incorrect error "Cannot specify an explicit fork when reading from stdin" when using
-
(stdin) as the input file.
Version 1.4.0
-
Added
length
andlength_raw
attributes toResource
. These attributes are equivalent to thelen
ofdata
anddata_raw
respectively, but may be faster to access.- Currently, the only optimized case is
length
for compressed resources, but more optimizations may be added in the future.
- Currently, the only optimized case is
-
Added a
compressed_info
attribute toResource
that provides access to the header information of compressed resources. -
Improved handling of compressed resources when listing resource files with the command line tool.
- Metadata of compressed resources is now displayed even if no decompressor implementation is available (as long as the compressed data header can be parsed).
- Performance has been improved - the data no longer needs to be fully decompressed to get its length, this information is now read from the header.
- The
'dcmp'
ID used to decompress each resource is displayed.
-
Fixed an incorrect
options.packages
insetup.cfg
, which made the library unusable except when installing from source using--editable
. -
Fixed
ResourceFile.__enter__
returningNone
, which made it impossible to useResourceFile
properly in awith
statement. -
Fixed various minor errors reported by type checking with
mypy
.
Version 1.3.0.post1
- Fixed an incorrect
options.packages
insetup.cfg
, which made the library unusable except when installing from source using--editable
.
Version 1.2.0.post1
- Fixed an incorrect
options.packages
insetup.cfg
, which made the library unusable except when installing from source using--editable
.
Version 1.3.0
- Added a
--group
command line option to group resources in list format by type (the default), ID, or with no grouping. - Added a
dump-text
output format to the command line tool. This format is identical todump
, but instead of a hex dump, it outputs the resource data as text. The data is decoded as MacRoman and classic Mac newlines (\r
) are translated. This is useful for examining resources that contain mostly plain text. - Changed the command line tool to sort resources by type and ID,
and added a
--no-sort
option to disable sorting and output resources in file order (which was the previous behavior). - Renamed the
rsrcfork.Resource
attributesresource_type
andresource_id
totype
andid
, respectively. The old names have been deprecated and will be removed in the future, but are still supported for now. - Changed
--format=dump
output to matchhexdump -C
's format - spacing has been adjusted, and multiple subsequent identical lines are collapsed into a single*
.
Version 1.2.0
-
Added support for compressed resources.
- Compressed resource data is automatically decompressed, both in the Python API and on the command line.
- This is technically a breaking change, since in previous versions the compressed resource data was returned directly. However, this change will not affect end users negatively, unless one has already implemented custom handling for compressed resources.
- 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 aDecompressError
. - 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.
-
Replaced the
rsrcfork
parameter ofrsrcfork.open
/ResourceFork.open
with a newfork
parameter.fork
accepts string values (like the command line--fork
option) rather thanrsrcfork
's hard to understandNone
/True
/False
.- The old
rsrcfork
parameter has been deprecated and will be removed in the future, but for now it still works as before.
- The old
-
Added an explanatory message when a resource filter on the command line doesn't match any resources in the resource file. Previously there would either be no output or a confusing error, depending on the selected
--format
. -
Changed resource type codes and names to be displayed in MacRoman instead of escaping all non-ASCII characters.
-
Cleaned up the resource descriptions in listings and dumps to improve readability. Previously they included some redundant or unnecessary information - for example, each resource with no attributes set would be explicitly marked as "no attributes".
-
Unified the formats of resource descriptions in listings and dumps, which were previously slightly different from each other.
-
Improved error messages when attempting to read multiple resources using
--format=hex
or--format=raw
. -
Fixed reading from non-seekable streams not working for some resource files.
-
Removed the
allow_seek
parameter ofResourceFork.__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.
Version 1.1.3.post1
- Fixed a formatting error in the README.rst to allow upload to PyPI.
Version 1.1.3
Note: This version is not available on PyPI, see version 1.1.3.post1 changelog for details.
- Added a setuptools entry point for the command-line interface.
This allows calling it using just
rsrcfork
instead ofpython3 -m rsrcfork
. - Changed the default value of
ResourceFork.__init__
'sclose
keyword argument fromTrue
toFalse
. This matches the behavior of classes likezipfile.ZipFile
andtarfile.TarFile
. - Fixed
ResourceFork.open
andResourceFork.__init__
not closing their streams in some cases. - Refactored the single
rsrcfork.py
file into a package. This is an internal change and should have no effect on how thersrcfork
module is used.
Version 1.1.2
- Added support for the resource file attributes "Resources Locked" and "Printer Driver MultiFinder Compatible" from ResEdit.
- Added more dummy constants for resource attributes with unknown meaning, so that resource files containing such attributes can be loaded without errors.
Version 1.1.1
- Fixed overflow issue with empty resource files or empty resource type entries
- Changed
_hexdump
to behave more likehexdump -C
Version 1.1.0
- Added a command-line interface - run
python3 -m rsrcfork --help
for more info
Version 1.0.0
- Initial version