Services and Tools

NRC Resource Compiler

nrc is the repo's resource compiler. It turns per-target res/ directories into generated C sources or external packed .res files, preserving localized message overrides and producing the ResPack entry points consumed at runtime by core/respack.c.

Relevant Source Files

Role In The Build Graph

nrc is built as a standalone command app under tools/nrc. The tools super-build in tools/CMakeLists.txt compiles sewer, osbs, core, and then the nrc executable in a separate Debug-only tools tree.

When a target uses NRC_EMBEDDED or NRC_PACKED, nap_generate_tools() first makes sure a local nrc binary exists under ${CMAKE_BINARY_DIR}/tools/build. Later, nap_resource_packs(...) invokes that executable once for each immediate resource-pack directory under the target's res/ folder.

Resource Directory Contract

The build system treats each immediate subdirectory of a target-local res/ folder as one resource pack. Real examples in this checkout include res_guihello, res_products, and res_htjson.

Localized overrides live one level deeper. For example, demo/products/res/res_products/ja_JP and demo/die/res/res_die/es_es are locale directories under a pack. Outside the packs themselves, the same top-level res/ folder may also hold app icons, license.txt, and pack.txt, which CMake treats specially for packaging and installation.

What Counts As A Resource

The compiler recognizes three runtime categories:

  • Message files: .msg files become text resources.
  • Image files: .png, .jpg, .gif, and .bmp are tagged as image resources.
  • Generic files: every other extension is still accepted and treated as a generic file resource.

So the resource compiler is permissive by default. It does not require a whitelist beyond the handful of extensions that deserve image-specific typing.

Message File Syntax And Localization Rules

msgparser.c accepts UTF-8 text with an optional BOM, skips whitespace, supports both // and /* ... */ comments, and parses one message per logical line. Each entry begins with an uppercase-or-underscore identifier, optionally followed by more uppercase letters, underscores, or digits, and the rest of the line becomes the message text.

Localization is anchored on the global pack. Global .msg files define the canonical set of message IDs. A localized .msg file can override those IDs, but it does not create new ones. If a localized file references an ID that does not exist globally, nrc emits a warning; if a global message has no localized counterpart, it emits a warning for that omission too.

Embedded Versus Packed Output

Mode CLI Option Generated Files Runtime Entry Point
Embedded -dc <pack>.h and <pack>.c respack_embedded("<pack>")
Packed -dp <pack>.h, <pack>.c, and <pack>.res respack_packed("<pack>", locale)

In both modes the header exports one ResId per resource plus a function named <pack>_respack(const char_t *locale). Resource IDs are not enums; they are generated strings with the form N23R3C75::<pack>::<index>.

What The Generator Actually Emits

Embedded mode writes the full payload into C source: messages become escaped string literals and binary resources become byte arrays. For each locale, the generated <pack>_respack(...) function chooses a localized override when available and otherwise falls back to the global resource.

Packed mode writes the same logical data into a binary .res file. The companion C file becomes a thin stub that only defines the ResId values and forwards the pack load to respack_packed(...).

Runtime Loading Contract

The runtime half lives in src/core/respack.c. Packed resources are loaded from a path relative to the executable: res/<pack>.res on Windows and Linux, and ../resources/<pack>.res for macOS bundles.

Once loaded, the runtime keeps the binary pack buffer alive and exposes resources through typed APIs. Text resources are returned with respack_text(...), generic files with respack_file(...), and typed object resources such as images are materialized lazily through respack_object(...). That is why the image-handling path in draw2d/image.c goes through the object API rather than the raw file accessor.

Up-To-Date Checks And Exit Codes

nrclib.c only regenerates a pack when the source directory is newer than the generated .c and .h files. The destination argument must name a .c file in an existing directory; nrc infers the matching header and packed file names from it.

  • 0: regeneration happened successfully.
  • 1: the outputs were already up to date.
  • -1: generation completed with warnings.
  • -2: generation failed with errors.
  • -3: command-line usage error.
  • -4: asserts were triggered.

The CMake wrapper in nap_resource_packs(...) treats 0 and 1 as normal success, reports -1 as warnings, and treats anything else as an error.

Reading Order

Start with nrc.c for the command-line surface, then read nrclib.c for validation and up-to-date checks, then resgen.c for the real format and code-generation logic. Follow that with msgparser.c if localized text syntax matters, and finish with prj/NAppTarget.cmake plus src/core/respack.c to connect build-time generation to runtime loading.