Crusader_Decomp/docs/remorse-cpp-compatibility-header-draft.md
2026-04-05 18:27:09 +02:00

4.9 KiB

Remorse C++ Compatibility Header Draft

Purpose

This note defines the minimum compatibility/support layer that future hand-maintained Remorse C++ skeletons should target.

It is not a final header and it is not a compiler lock-in decision. It is a draft contract so early class-emission work does not silently drift into host-only modern C++ assumptions.

This note should stay paired with docs/remorse-rebuild-abi-notes.md.

Why This Layer Is Needed

Current rebuild notes already make three constraints clear:

  • Track A original-style executable reconstruction remains in scope
  • the executable is shaped by segmented pointers and far-call conventions
  • host compilation success is not enough if layout, slot order, or call shape drift

That means the first C++ skeletons should compile against a compatibility layer that makes these constraints explicit even before the exact compiler/toolchain decision is closed.

Minimum Header Categories

1. Exact-width integer aliases

Needed because:

  • field maps are being recovered by offset
  • slot/value and save/load structures use exact-width arithmetic
  • sign extension versus zero extension matters repeatedly in the current notes

Current draft surface:

using u8 = std::uint8_t;
using s8 = std::int8_t;
using u16 = std::uint16_t;
using s16 = std::int16_t;
using u32 = std::uint32_t;
using s32 = std::int32_t;

2. Packing controls

Needed because:

  • object and save-state layouts are currently tracked by exact offsets
  • future vtable-bearing structs must not silently pick host-default padding

Current draft surface:

#define CR_PACK_PUSH_1
#define CR_PACK_POP
#define CR_PACKED

The final spelling can change per compiler. The important part is that early source slices already depend on named packing controls rather than ad hoc pragmas in every file.

3. Calling-convention markers

Needed because:

  • later method and helper promotion will need to distinguish ordinary helpers from call-shape-sensitive surfaces
  • Track A may need explicit far/near or compiler-family-specific conventions

Current draft surface:

#define CR_CDECL
#define CR_STDCALL
#define CR_THISCALL
#define CR_FARCALL

CR_FARCALL is intentionally placeholder-level today. The current value is in making far-call-sensitive surfaces visible in source, not in pretending the final compiler spelling is already known.

4. Segmented pointer helper types

Needed because:

  • several notes still preserve segment:offset provenance directly
  • owner-loaded VM source pairs and some callback/resource lanes should not be flattened too early

Current draft surface:

struct FarPtr16 {
    u16 offset;
    u16 segment;
};

template <typename T>
struct FarPtr {
    u16 offset;
    u16 segment;
};

Current rule:

  • use these first as evidence-preserving placeholders, not as proof that final emitted code must literally use this exact host representation everywhere.

5. Vtable and slot-order helpers

Needed because:

  • current family notes still treat slot order as evidence, not cosmetics
  • later MCP or hand-authored promotion should preserve raw order even when names are provisional

Current draft surface:

#define CR_VSLOT(index)

This can stay documentation-only at first if needed. The point is to keep raw slot numbering visible in provisional class headers.

First Skeleton Rules

When the first class family is emitted to source, the compatibility header should enforce these rules:

  1. every field uses exact-width aliases
  2. every packed layout is explicit
  3. every unresolved far/segment-sensitive pointer uses a named placeholder type
  4. every provisional virtual surface keeps raw slot order visible
  5. Track A-only assumptions are marked instead of being silently baked into Track B-style cleanup

Families Most Likely To Need This Immediately

  1. EntityDispatchEntry
  2. SpriteNode
  3. EntityVmRuntime
  4. EntityVmOwnerResource
  5. EntityVmContext

Entity will also need it, but its family split is large enough that it should probably not be the first source-emission pilot.

What This Draft Deliberately Avoids

  • picking one final compiler family now
  • pretending near/far semantics are already solved
  • turning unresolved imported-runtime calls into polished modern interfaces
  • flattening segment:offset provenance into generic host pointers too early

Proposed Next Step

Once the first pilot family is chosen, convert this note into a real minimal header with:

  • exact-width aliases
  • packing markers
  • calling-convention placeholders
  • one segmented-pointer placeholder type
  • one comment block explaining Track A versus Track B expectations

Bottom Line

The compatibility header should exist before the first C++ skeleton is emitted, not after.

That keeps early source work honest and preserves the option of an original-style rebuild instead of quietly drifting into a pure host-port codebase.