Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/squeek502/resinator
Cross-platform Windows resource-definition script (.rc) to resource file (.res) compiler
https://github.com/squeek502/resinator
rc win32 windows-resource-file zig
Last synced: 3 days ago
JSON representation
Cross-platform Windows resource-definition script (.rc) to resource file (.res) compiler
- Host: GitHub
- URL: https://github.com/squeek502/resinator
- Owner: squeek502
- License: 0bsd
- Created: 2022-10-23T21:00:47.000Z (about 2 years ago)
- Default Branch: master
- Last Pushed: 2024-12-14T04:08:22.000Z (20 days ago)
- Last Synced: 2024-12-23T22:09:52.256Z (10 days ago)
- Topics: rc, win32, windows-resource-file, zig
- Language: Zig
- Homepage:
- Size: 1.41 MB
- Stars: 109
- Watchers: 3
- Forks: 3
- Open Issues: 12
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
![](https://www.ryanliptak.com/images/resinator-dynamic-32.svg) resinator
=========A cross-platform Windows resource-definition script (.rc) to resource file (.res) compiler. It has been merged into the Zig compiler ([#17069](https://github.com/ziglang/zig/pull/17069), [#17412](https://github.com/ziglang/zig/pull/17412)), but it is also maintained as a separate standalone tool.
- This is a fully from-scratch and [clean-room](https://en.wikipedia.org/wiki/Clean_room_design) implementation, using [fuzz testing](#testing-resinator) as the primary method of determining how `rc.exe` works and how compatible `resinator` is with its implementation.
+ See [this talk](https://www.youtube.com/watch?v=RZczLb_uI9E) for a deeper dive into this
- `resinator` can successfully compile every `.rc` file in the [Windows-classic-samples repo](https://github.com/microsoft/Windows-classic-samples) 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](https://github.com/squeek502/win32-samples-rc-tests).
- `resinator` has zero external dependencies at runtime (it embeds [Aro](https://github.com/Vexu/arocc) 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](https://www.mingw-w64.org/) include files are extracted on-demand).
- [Documentation](https://squeek502.github.io/resinator/) (really just a dumping ground for documentation-adjacent stuff; to-be-improved-upon)## Overview
A Windows resource-definition file (`.rc`) is made up of both C/C++ preprocessor commands and resource definitions.
- The preprocessor commands are evaluated first via [Aro](https://github.com/Vexu/arocc)
- The preprocessed `.rc` file is then compiled into a `.res` file
- The `.res` file can then be linked into an executable by a linker`resinator` 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](https://learn.microsoft.com/en-us/windows/win32/menurc/using-rc-the-rc-command-line-).
However, unlike `llvm-rc` and `windres` (see [this section](#an-example-of-the-differences-between-the-implementations)), `resinator` aims to get as close to 1:1 compatibility with the Windows `rc.exe` implementation as possible. That is, the ideal would be:
- The `.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.
## Comparison to `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 messages
![](https://github.com/squeek502/resinator/assets/2389051/1d2a6dd1-dbcb-4df8-88b1-ab1b84ea36eb)
#### `resinator` handles edge cases better and avoids miscompilations
`resinator` 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`](https://squeek502.github.io/resinator/divergences.html).
Also, `resinator` will emit helpful warnings/errors for many of these differences:
![](https://github.com/squeek502/resinator/assets/2389051/85150176-a06f-47a4-9e80-ed15961a1d72)
#### `resinator` does not support UTF-16 encoded scripts
This is a limitation of the preprocessor that is used by `resinator` (see [this issue](https://github.com/squeek502/resinator/issues/5)).
#### `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.
## Comparison to `windres` and `llvm-rc`
| Feature | `resinator` | `windres` | `llvm-rc` | `rc.exe` |
| --- | --- | --- | --- | --- |
| Cross-platform | ✅ | ✅ | ✅ | ❌ |
| Identical [`win32-samples-rc-tests`](https://github.com/squeek502/win32-samples-rc-tests) outputs as `rc.exe` | ✅ | ❌ | ❌ | ✅ |
| Support for UTF-16 encoded `.rc` files | ❌ [(TODO)](https://github.com/squeek502/resinator/issues/5) | ❌ | ❌ | ✅ |
| CLI compatibility with `rc.exe` | ✅ | ❌ | ✅ | ✅ |
| Includes preprocessor | ✅ | ❌ | ❌ | ✅ |
| Support for outputting `.rc` files | ❌ | ✅ | ❌ | ❌ |
| Support for outputting COFF object files | ❌ [(TODO)](https://github.com/squeek502/resinator/issues/7) | ✅ | ❌ | ❌ |#### An example of the differences between the implementations
Here is an example `.rc` script that is handled differently by each of `windres`, `llvm-rc`, and the canonical Windows `rc.exe` implementation:
```c
// { }
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](https://github.com/squeek502/win32-samples-rc-tests) for a more wide-ranging comparison on real-world `.rc` files.
## Compiling `resinator`
See [the version of Zig used in CI](https://github.com/squeek502/resinator/blob/master/.github/workflows/ci.yml#L23) to determine which version of Zig should be used to build `resinator`.
```
git clone https://github.com/squeek502/resinator
cd resinator
zig build
```## Testing `resinator`
```
zig build test
```### 'Fuzzy' tests
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
```### Fuzz testing
#### On Windows
- Requires [winafl](https://github.com/googleprojectzero/winafl) to be installed/built.
- Requires `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%" @@
```#### On Linux
Currently, Linux fuzz testing only tests the `resinator` implementation for crashes/bugs, and does not check for correctness against `rc.exe`.
- Requires [afl++](https://github.com/AFLplusplus/AFLplusplus) with `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).