Files
tdkpin/original/reconstructed/tdkpin_mechanism_render.c
T
ddidderr 8b99e9607c feat(reconstruction): complete binary-backed C recovery
Replace the partial mechanics transcriptions with a separate, readable C11
reconstruction of the complete Win16 image while preserving the original raw
Ghidra export as immutable evidence. Cover all ordinary and overlapping entry
points, Borland runtime behavior, Win16 imports, segmented data, callbacks,
resources, indirect control flow, physics, rendering, persistence, and
startup/shutdown lifecycles.

Add deterministic extraction and audit tooling plus address-linked ledgers for
functions, imports, DGROUP ranges and objects, relocations, resources, and
callbacks. The final gate records zero raw, partial, restored, unknown,
blocked, or unclassified required units. Keep the semantic-fidelity boundary
explicit: the portable C is not claimed to reproduce a byte-identical Borland
NE build.

Add strict focused harnesses for every reconstructed C unit, exact resource
round-trip checks, and a 16-bit Borland Real48 reference probe. No Rust source
or Cargo metadata is changed in this phase.

Test Plan:
- `bash original/tools/test_reconstructed_c.sh` -- passed
- `bash original/tools/probe_real48_reference.sh` -- passed bit-for-bit
- `python3 original/tools/audit_reconstruction.py --require-complete` -- passed
- `git diff --cached --check` -- passed
- `git diff HEAD -- '*.rs' Cargo.toml Cargo.lock` -- empty
2026-08-23 16:41:17 +02:00

133 lines
4.6 KiB
C

/* Five-target rotation and 91x90 panel compositor from TDKPIN Code1. */
#include "tdkpin_mechanism_render.h"
#include "tdkpin_sound.h"
typedef struct {
INT16 x;
INT16 y;
} TargetPoint;
static const TargetPoint g_target_positions[6][5] = {
{{9, 41}, {23, 11}, {56, 14}, {63, 47}, {35, 63}},
{{11, 45}, {18, 13}, {52, 11}, {63, 42}, {39, 64}},
{{13, 50}, {15, 17}, {46, 9}, {64, 37}, {44, 62}},
{{17, 55}, {11, 23}, {39, 7}, {64, 30}, {50, 59}},
{{21, 60}, {9, 28}, {35, 7}, {62, 25}, {54, 56}},
{{8, 35}, {26, 8}, {59, 18}, {59, 50}, {27, 61}},
};
static Win16FarPtr caller_receiver(uint16_t caller_bp)
{
uint16_t offset = win16_read_stack_u16((uint16_t)(caller_bp + 6));
uint16_t selector = win16_read_stack_u16((uint16_t)(caller_bp + 8));
return win16_make_far_pointer(selector, offset);
}
static void rotate_target_state(uint16_t caller_bp, Win16FarPtr receiver)
{
static const uint16_t record_words[5] = {
0x3371, 0x33c4, 0x3417, 0x346a, 0x34bd};
uint16_t first_word = win16_read_u16(
win16_dgroup_pointer(record_words[0]));
for (uint16_t index = 0; index < 4; index++) {
win16_write_u16(
win16_dgroup_pointer(record_words[index]),
0,
win16_read_u16(win16_dgroup_pointer(record_words[index + 1])));
}
win16_write_u16(
win16_dgroup_pointer(record_words[4]), 0, first_word);
uint16_t row = (uint16_t)(
0x0476 + (uint16_t)g_current_player * 0x14u);
uint8_t first_byte = win16_read_u8(
win16_far_add_offset(receiver, row));
for (uint16_t index = 0; index < 4; index++) {
win16_write_u8(
win16_far_add_offset(receiver, (uint16_t)(row + index)),
win16_read_u8(win16_far_add_offset(
receiver, (uint16_t)(row + index + 1))));
}
win16_write_u8(
win16_far_add_offset(receiver, (uint16_t)(row + 4)), first_byte);
for (uint16_t item = 15; item <= 19; item++) {
tdkpin_refresh_player_item(caller_bp, item);
}
}
/* 1000:6f27 -- render and optionally rotate the five-target panel. */
void tdkpin_rotate_and_draw_targets(uint16_t caller_bp)
{
Win16FarPtr receiver = caller_receiver(caller_bp);
HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4));
HDC16 dc = GetDC16(window);
HDC16 source_dc = CreateCompatibleDC16(dc);
HDC16 panel_dc = CreateCompatibleDC16(dc);
HBITMAP16 panel_bitmap = CreateCompatibleBitmap16(dc, 91, 90);
HGDIOBJ16 old_source = SelectObject16(
source_dc, win16_read_u16(win16_dgroup_pointer(0x0859)));
HGDIOBJ16 old_panel = SelectObject16(panel_dc, panel_bitmap);
int32_t state = (int32_t)win16_read_u32(win16_dgroup_pointer(0x0855));
if (state < 6) {
INT16 source_x = (INT16)(uint16_t)(
((uint32_t)state - 1u) * 91u);
(void)BitBlt16(
panel_dc, 0, 0, 91, 90,
source_dc, source_x, 0, 0x00cc0020u);
} else {
(void)SelectObject16(
source_dc, win16_read_u16(win16_dgroup_pointer(0x085d)));
(void)BitBlt16(
panel_dc, 0, 0, 91, 90,
source_dc, 79, 33, 0x00cc0020u);
(void)SelectObject16(
source_dc, win16_read_u16(win16_dgroup_pointer(0x0859)));
rotate_target_state(caller_bp, receiver);
}
if (state == 1) {
tdkpin_play_sound_if_not_tilted(11);
}
if (state >= 1 && state <= 6) {
static const uint16_t target_words[5] = {
0x3371, 0x33c4, 0x3417, 0x346a, 0x34bd};
const TargetPoint *positions = g_target_positions[state - 1];
for (uint16_t index = 0; index < 5; index++) {
if (win16_read_u16(
win16_dgroup_pointer(target_words[index])) != 0) {
(void)BitBlt16(
panel_dc,
positions[index].x,
positions[index].y,
17,
17,
source_dc,
455,
17,
0x008800c6u);
(void)BitBlt16(
panel_dc,
positions[index].x,
positions[index].y,
17,
17,
source_dc,
455,
0,
0x00ee0086u);
}
}
}
(void)BitBlt16(
dc, 79, 33, 91, 90, panel_dc, 0, 0, 0x00cc0020u);
(void)SelectObject16(source_dc, old_source);
(void)SelectObject16(panel_dc, old_panel);
(void)DeleteObject16(panel_bitmap);
(void)DeleteDC16(panel_dc);
(void)DeleteDC16(source_dc);
(void)ReleaseDC16(window, dc);
}