Skip to content

Repository files navigation

engraver

Edits resources in existing Win32 PE files.


What Is This?

In a POSIX environment, it is common in porting projects for developers to want to embed resources into Windows PE binary files. Examples include updating version information or setting icons.

However, the development environment must have a toolchain such as windres installed.

While there are other libraries for manipulating the PE binary format, they can be cumbersome to use—even when you only want to manipulate resources—because they involve a large API.

engraver is a small library implemented entirely in C99 that specializes solely in editing the resource information of PE binary files after the fact. You can easily integrate it into your project.

The project contains:

  • libengraver: C99 library published as libengraver.a and libengraver.so
  • engraver: small CLI wrapper around the library

You can use with pre-built packages (Debian/Ubuntu deb packages and windows executables) in release page.

Build manually

make clean all test

The build creates:

  • build/libengraver.a
  • build/libengraver.so
  • build/engraver

CLI

engraver <input-pe file> <updates-json file> <output-pe file>

The input and output paths must differ. Resources not mentioned by JSON are preserved.

JSON

The JSON root object may contain strings, version, icons, and raw.

{
  "strings": [{ "id": 1, "language": 1041, "value": "Title" }],
  "version": {
    "language": 1041,
    "codePage": 1200,
    "fixed": {
      "fileVersion": "1.2.3.4",
      "productVersion": "1.2.3.4",
      "fileType": "app"
    },
    "strings": {
      "CompanyName": "Example",
      "FileDescription": "Example App"
    }
  },
  "icons": [{ "id": 1, "language": 1041, "path": "assets/app.ico" }],
  "raw": [{ "type": 24, "id": 1, "language": 1041, "path": "assets/manifest.xml" }]
}

Asset paths are resolved relative to the JSON file directory. v1 accepts numeric id, type, and language values. Icon paths must point to .ico files.

Library Flow

eg_json_document *document = NULL;
eg_resource_update *update = NULL;
eg_pe_file *file = NULL;

eg_load_json_file("updates.json", &document);
eg_json_document_to_update(document, &update);
eg_pe_open_file("input.exe", &file);
eg_pe_write_file(file, update, "output.exe");

eg_pe_close(file);
eg_resource_update_destroy(update);
eg_release_json(document);

The functions above use the default POSIX file I/O implementation. Use the _with_io variants when embedding libengraver in an environment that needs custom file handles or virtual files.

eg_io_ops ops = {
    .context = user_context,
    .open_read = open_read,
    .open_write = open_write,
    .read = read_file,
    .write = write_file,
    .close = close_file
};

eg_load_json_file_with_io(&ops, "updates.json", &document);
eg_json_document_to_update_with_io(&ops, document, &update);
eg_pe_open_file_with_io(&ops, "input.exe", &file);
eg_pe_write_file_with_io(&ops, file, update, "output.exe");

read() reports EOF by returning EG_OK with *out_size == 0. write() must write the full requested size or return an error. close() is called after a successful open, including when a later read or write fails.


Build Packages (Advanced topics)

build_package.sh runs Debian package builds inside distro-specific Podman containers and Win32 package builds with MinGW and Wine. Run prereq.sh first to build target-specific Podman images with the apt build dependencies already installed for Linux packages.

Prerequisites:

sudo apt-get install podman qemu-user-static dpkg-dev binutils mingw-w64 wine zip unzip
npm install -g screw-up

Build examples:

# Prepare prerequisite images
./prereq.sh

# Ubuntu 24.04 / amd64
./build_package.sh --target deb --distro ubuntu --release 24.04 --arch amd64

# Debian bookworm / arm64
./build_package.sh --target deb --distro debian --release bookworm --arch arm64

# Win32 / amd64 and i686
./build_package.sh --target win32

# Full Linux and Win32 matrix
./build_package.sh --target all

Notes:

  • Generated packages are engraver and libengraver-dev.
  • Win32 packages are generated as zip files with engraver.exe, libengraver.dll, import/static libraries, headers, and docs.
  • Supported targets are Debian bookworm (x86_64, i686, arm64, armv7l), Debian trixie (x86_64, i686, arm64, armv7l, riscv64), and Ubuntu 22.04, 24.04, 26.04 (x86_64, arm64).
  • Supported Win32 architectures are amd64 and i686.
  • Arch aliases: x86_64|amd64, i686|i386, armv7l|armv7|armhf, arm64|aarch64.
  • Ubuntu release aliases: 24.04|noble, 22.04|jammy.
  • Debug build: add --debug.
  • Rebuild prerequisite images after dependency or base-image changes: ./prereq.sh --force.
  • Outputs: artifacts/<package>-<version>-<distro>-<release>-<deb-arch>.deb.
  • Win32 outputs: artifacts/engraver-<version>-win32-<arch>.zip.

Batch build for all predefined combos:

./build_package_all.sh

License

Under MIT.

About

Edits resources in existing Win32 PE files ✒️

Topics

Resources

Stars

Watchers

Forks

Releases

Contributors

Languages