Cross-platform Windows resource-definition script (.rc) to resource file (.res) compiler
0BSD License
A cross-platform Windows resource-definition script (.rc) to resource file (.res) compiler. It has been merged into the Zig compiler (#17069, #17412), but it is also maintained as a separate standalone tool.
rc.exe
works and how compatible resinator
is with its implementation.
resinator
can successfully compile every .rc
file in the Windows-classic-samples repo byte-for-byte identically to the Windows RC compiler when using the includes from MSVC/the Windows SDK. This is tested via win32-samples-rc-tests.resinator
has zero external dependencies at runtime (it embeds Aro for preprocessing) and can be used to cross-compile from any system out-of-the-box (if Windows system include paths are not found, a complete set of MinGW include files are extracted on-demand).A Windows resource-definition file (.rc
) is made up of both C/C++ preprocessor commands and resource definitions.
.rc
file is then compiled into a .res
file.res
file can then be linked into an executable by a linkerresinator
is similar to llvm-rc
and GNU's windres
, in that it aims to be a cross-platform alternative to the Windows rc.exe
tool.
However, unlike llvm-rc
and windres
(see this section), resinator
aims to get as close to 1:1 compatibility with the Windows rc.exe
implementation as possible. That is, the ideal would be:
.res
output of resinator
should match the .res
output of the Windows rc.exe
in as many cases as possible (if not exactly, then functionally). However, resinator
will not support all valid .rc
files (i.e. #pragma code_page
support will be limited to particular code pages).resinator
should fail to compile .rc
files that the Windows rc.exe
tool fails to compile.In practice, though, 1:1 compatibility is not actually desirable, as there are quirks/bugs in the rc.exe
implementation that resinator
attempts to handle better.
rc.exe
(the Win32 RC compiler)resinator
is a drop-in replacement for rc.exe
with some improvements, some known exceptions, and some caveats.
resinator
has better error messagesresinator
handles edge cases better and avoids miscompilationsresinator
avoids some miscompilations of the Win32 RC compiler and generally handles edge cases better (e.g. resource data size overflow). See this exhaustive list of intentional differences between resinator
and rc.exe
.
Also, resinator
will emit helpful warnings/errors for many of these differences:
resinator
does not support UTF-16 encoded scriptsThis is a limitation of the preprocessor that is used by resinator
(see this issue).
resinator
will auto-detect system include paths by default.rc
files can #include
files from MSVC/Windows SDKs (most commonly, windows.h
). The paths for these files are typically provided via the INCLUDE
environment variable or the /i
command line option. resinator
supports the same options, but it will also try to auto-detect system include paths on Windows, or extract a full set of MinGW includes on-demand (meaning #include "windows.h"
in a .rc
file will work out-of-the-box on Linux/MacOS/etc).
This behavior can be controlled with the /:auto-includes
CLI option.
windres
and llvm-rc
Feature | resinator |
windres |
llvm-rc |
rc.exe |
---|---|---|---|---|
Cross-platform | ✅ | ✅ | ✅ | ❌ |
Identical win32-samples-rc-tests outputs as rc.exe
|
✅ | ❌ | ❌ | ✅ |
Support for UTF-16 encoded .rc files |
❌ (TODO) | ❌ | ❌ | ✅ |
CLI compatibility with rc.exe
|
✅ | ❌ | ✅ | ✅ |
Includes preprocessor | ✅ | ❌ | ❌ | ✅ |
Support for outputting .rc files |
❌ | ✅ | ❌ | ❌ |
Support for outputting COFF object files | ❌ (TODO) | ✅ | ❌ | ❌ |
Here is an example .rc
script that is handled differently by each of windres
, llvm-rc
, and the canonical Windows rc.exe
implementation:
// <id> <resource type> { <data> }
1 "FOO" { "bar" }
rc.exe
compiles this to a .res
file with a resource of ID 1
that has the type "FOO"
(the quotes are part of the user-defined resource type name)windres
compiles this to a .res
file with a resource of ID 1
that has the type FOO
(the "FOO"
is parsed as a quoted string)llvm-rc
errors on this file with: Error: expected int or identifier, got "FOO"
resinator
matches the rc.exe
behavior exactly in this caseThis particular example is mostly inconsequential in terms of real-world .rc
files, but it is indicative of how closely the different implementations conform with the rc.exe
behavior. See win32-samples-rc-tests for a more wide-ranging comparison on real-world .rc
files.
resinator
See the version of Zig used in CI to determine which version of Zig should be used to build resinator
.
git clone https://github.com/squeek502/resinator
cd resinator
zig build
resinator
zig build test
The 'fuzzy' tests are a collection of tests that may be similar to either fuzz testing or property testing. With default settings, the more fuzzing-like tests will run for 1000 iterations. These tests rely on rc.exe
being available on PATH
since each test uses rc.exe
as an oracle to determine if resinator
behavior is expected or not.
zig build test_fuzzy
will run all of the 'fuzzy' tests.
Each 'fuzzy' test can be run individually, too. For example, this will run the fuzzy_numbers
test infinitely:
zig build test_fuzzy_numbers -Diterations=0
rc.exe
to be available on the PATH
To build the fuzzer:
zig build fuzz_winafl
To run the fuzzer, from within the winafl/build64/bin
directory (replace C:\path\to\
with the actual path to the relevant directories):
set PATH_TO_INPUTS_DIR=C:\path\to\resinator\test\inputs
set PATH_TO_OUTPUTS_DIR=C:\path\to\resinator\test\outputs
set PATH_TO_DYNAMORIO_BIN=C:\path\to\DynamoRIO-Windows\bin64
set PATH_TO_RESINATOR_FUZZER=C:\path\to\resinator\zig-out\bin\fuzz_winafl.exe
afl-fuzz.exe -i "%PATH_TO_INPUTS_DIR%" -o "%PATH_TO_OUTPUTS_DIR%" -D "%PATH_TO_DYNAMORIO_BIN%" -t 20000 -- -coverage_module fuzz_winafl.exe -target_module fuzz_winafl.exe -target_method fuzzMain -fuzz_iterations 5000 -nargs 2 -- "%PATH_TO_RESINATOR_FUZZER%" @@
Currently, Linux fuzz testing only tests the resinator
implementation for crashes/bugs, and does not check for correctness against rc.exe
.
afl-clang-lto
to be installed.zig build fuzz_rc
To run the fuzzer:
afl-fuzz -i test/inputs -o test/outputs -- ./zig-out/bin/fuzz_rc
Ideally, this fuzzer would compare the outputs against rc.exe
but wine
was not able to perfectly emulate rc.exe
last I tried (need to investigate this more, I was using a rather old version of wine).