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

159 lines
No EOL
4.9 KiB
Markdown

# 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](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:
```cpp
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:
```cpp
#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:
```cpp
#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:
```cpp
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:
```cpp
#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.