Files
tdkpin/original/reconstructed/tdkpin_collision_events.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

286 lines
10 KiB
C

/* Bitmask-driven collision event dispatcher from TDKPIN Code1. */
#include "tdkpin_collision_events.h"
#include "tdkpin_arithmetic.h"
#include "tdkpin_random.h"
#include "tdkpin_sound.h"
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 Win16FarPtr record(uint16_t id)
{
return win16_dgroup_pointer((uint16_t)(0x095b + id * 0x53u));
}
static uint8_t record_active(uint16_t id)
{
return win16_read_u8(win16_far_add_offset(record(id), 0x34));
}
static void set_record_active(uint16_t id, uint8_t active)
{
win16_write_u8(win16_far_add_offset(record(id), 0x34), active);
}
static uint16_t record_link(uint16_t id)
{
return win16_read_u16(win16_far_add_offset(record(id), 0x43));
}
static uint16_t player_row_offset(uint16_t item)
{
return (uint16_t)(
0x0467 + (uint16_t)g_current_player * 0x14u + item);
}
static void activate_record_groups(
uint16_t caller_bp, uint16_t first, uint16_t groups)
{
for (uint16_t group = 0; group < groups; group++) {
uint16_t base = (uint16_t)(first + group * 3u);
set_record_active(base, 1);
set_record_active((uint16_t)(base + 1u), 1);
set_record_active((uint16_t)(base + 2u), 1);
tdkpin_refresh_collision_record(caller_bp, base);
}
}
static bool group_heads_all_inactive(uint16_t first, uint16_t groups)
{
for (uint16_t group = 0; group < groups; group++) {
if (record_active((uint16_t)(first + group * 3u)) != 0) {
return false;
}
}
return true;
}
static void restore_record_bounds_on_dc(
uint16_t id, HDC16 dc)
{
Win16FarPtr bounds = win16_far_add_offset(record(id), 0x4b);
INT16 left = (INT16)win16_read_u16(bounds);
INT16 top = (INT16)win16_read_u16(win16_far_add_offset(bounds, 2));
INT16 right = (INT16)win16_read_u16(win16_far_add_offset(bounds, 4));
INT16 bottom = (INT16)win16_read_u16(win16_far_add_offset(bounds, 6));
INT16 width = (INT16)(uint16_t)(
(uint16_t)right - (uint16_t)left + 1u);
INT16 height = (INT16)(uint16_t)(
(uint16_t)bottom - (uint16_t)top + 1u);
tdkpin_restore_background_region_a(height, width, top, left, dc);
tdkpin_present_background_region(height, width, top, left, dc);
}
static void publish_three_record_completion(
uint16_t caller_bp, Win16FarPtr receiver)
{
win16_write_u8(win16_far_add_offset(receiver, 0x0bdc), 1);
static const uint16_t ids[3] = {153, 6, 154};
for (uint16_t index = 0; index < 3; index++) {
set_record_active(ids[index], 1);
}
HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4));
HDC16 dc = GetDC16(window);
for (uint16_t index = 0; index < 3; index++) {
restore_record_bounds_on_dc(ids[index], dc);
}
(void)ReleaseDC16(window, dc);
(void)caller_bp;
}
static void handle_sprite_award(
uint16_t caller_bp, Win16FarPtr receiver)
{
tdkpin_play_sound_if_not_tilted(12);
set_record_active(149, 1);
win16_write_u8(win16_far_add_offset(receiver, (uint16_t)(
0x00f6 + (uint16_t)g_current_player * 0x00afu)), 1);
tdkpin_restore_record_or_item_a(caller_bp, 149);
Win16FarPtr sprite = win16_dgroup_pointer((uint16_t)(
0x09a4 + (uint16_t)g_current_player * 2u));
if (win16_read_u16(win16_dgroup_pointer(0x399a)) != 0) {
win16_write_u16(sprite, 0, 7);
} else {
uint16_t previous = win16_read_u16(sprite);
uint16_t selected;
do {
selected = (uint16_t)(borland_random_below(6) + 1u);
} while (selected == previous);
win16_write_u16(sprite, 0, selected);
}
HWND16 window = win16_read_u16(win16_far_add_offset(receiver, 4));
HDC16 dc = GetDC16(window);
tdkpin_composite_player_sprite(win16_read_u16(sprite), dc);
tdkpin_present_background_region(123, 42, 79, 55, dc);
(void)ReleaseDC16(window, dc);
}
static void handle_ready_upgrade(uint16_t caller_bp)
{
tdkpin_play_sound_if_not_tilted(12);
if (!group_heads_all_inactive(90, 5)) {
return;
}
tdkpin_play_sound_if_not_tilted(17);
Win16FarPtr ready = win16_dgroup_pointer((uint16_t)(
0x0816 + (uint16_t)g_current_player));
uint8_t value = win16_read_u8(ready);
if (value == 6) {
tdkpin_add_score_from_caller_frame(caller_bp, 50000);
} else {
value = value == 1 ? 2 : (uint8_t)(value + 1u);
win16_write_u8(ready, value);
tdkpin_restore_record_or_item_a(
caller_bp, (uint16_t)(value + 999u));
}
activate_record_groups(caller_bp, 90, 5);
}
static void handle_item_bank(
uint16_t caller_bp, Win16FarPtr receiver)
{
tdkpin_play_sound_if_not_tilted(12);
if (!group_heads_all_inactive(109, 4)) {
return;
}
uint16_t slot = 1;
for (uint16_t item = 1; item <= 9; item++) {
if (win16_read_u8(win16_far_add_offset(
receiver, player_row_offset((uint16_t)(item + 5u)))) != 0) {
slot++;
}
}
if (slot < 9) {
tdkpin_play_sound_if_not_tilted(17);
win16_write_u8(
win16_far_add_offset(
receiver, player_row_offset((uint16_t)(slot + 5u))),
1);
tdkpin_restore_record_or_item_a(
caller_bp, (uint16_t)(slot + 1005u));
tdkpin_add_score_from_caller_frame(
caller_bp, (int32_t)slot * 10000);
} else if (slot == 9) {
tdkpin_play_sound_if_not_tilted(17);
win16_write_u8(
win16_far_add_offset(receiver, player_row_offset(14)), 1);
tdkpin_restore_record_or_item_a(caller_bp, 1014);
tdkpin_add_score_from_caller_frame(caller_bp, 24464);
publish_three_record_completion(caller_bp, receiver);
} else {
tdkpin_play_sound_if_not_tilted(7);
if (win16_read_u8(win16_far_add_offset(receiver, 0x0bdc)) == 0) {
win16_write_u8(win16_far_add_offset(receiver, 0x0bdc), 1);
} else {
Win16FarPtr secondary = win16_dgroup_pointer((uint16_t)(
0x098e + (uint16_t)g_current_player * 4u));
win16_write_u32(
secondary, 0, win16_read_u32(secondary) + 100000u);
uint8_t multiplier = win16_read_u8(win16_dgroup_pointer((uint16_t)(
0x09a1 + (uint16_t)g_current_player)));
int32_t shown = borland_multiply_i32(
(int32_t)win16_read_u32(secondary), multiplier);
tdkpin_draw_score_from_caller_frame(
caller_bp,
(uint16_t)shown,
(uint16_t)((uint32_t)shown >> 16));
}
}
activate_record_groups(caller_bp, 109, 4);
}
static void handle_three_record_switch(
uint16_t caller_bp, Win16FarPtr receiver, uint16_t record_id)
{
if (record_id == 150) {
win16_write_u8(win16_far_add_offset(receiver, 0x00f7), 10);
tdkpin_restore_record_or_item_a(caller_bp, 150);
set_record_active(153, 1);
tdkpin_restore_record_or_item_a(caller_bp, 153);
} else if (record_id == 151) {
win16_write_u8(win16_far_add_offset(receiver, 0x00f8), 10);
tdkpin_restore_record_or_item_a(caller_bp, 151);
set_record_active(6, 1);
tdkpin_restore_record_or_item_a(caller_bp, 6);
} else if (record_id == 152) {
win16_write_u8(win16_far_add_offset(receiver, 0x00f9), 10);
tdkpin_restore_record_or_item_a(caller_bp, 152);
set_record_active(154, 1);
tdkpin_restore_record_or_item_a(caller_bp, 154);
}
}
static void handle_timed_record(
uint16_t caller_bp, Win16FarPtr receiver, uint16_t record_id)
{
if (record_id >= 51 && record_id <= 53) {
tdkpin_play_sound_if_not_tilted(6);
win16_write_u8(
win16_far_add_offset(receiver, (uint16_t)(record_id + 0x61u)), 5);
tdkpin_restore_record_or_item_a(caller_bp, record_id);
uint8_t ready = win16_read_u8(win16_dgroup_pointer((uint16_t)(
0x0816 + (uint16_t)g_current_player)));
tdkpin_add_score_from_caller_frame(
caller_bp, (int32_t)((int16_t)ready - 1) * 1000);
} else if (record_id >= 140 && record_id <= 147) {
win16_write_u8(
win16_far_add_offset(receiver, (uint16_t)(record_id + 0x61u)),
20);
tdkpin_restore_record_or_item_a(caller_bp, record_id);
}
}
/* 1000:b476 -- dispatch all collision event bits in their binary order. */
void tdkpin_handle_collision_events(
uint16_t caller_bp, uint16_t event_flags, uint16_t record_id)
{
if (win16_read_u8(win16_dgroup_pointer(0x07c6)) != 0) {
return;
}
Win16FarPtr receiver = caller_receiver(caller_bp);
if ((event_flags & 0x0001u) != 0 &&
record_id >= 129 && record_id <= 133) {
uint16_t item = (uint16_t)(record_id - 0x72u);
win16_write_u8(win16_far_add_offset(
receiver, player_row_offset(item)), 1);
tdkpin_restore_record_or_item_a(
caller_bp, (uint16_t)(item + 1000u));
}
if ((event_flags & 0x0002u) != 0) {
uint16_t linked = record_link(record_id);
set_record_active(linked, 0);
tdkpin_refresh_collision_record(caller_bp, linked);
if ((event_flags & 0x2000u) != 0) {
set_record_active((uint16_t)(linked + 1u), 0);
set_record_active((uint16_t)(linked + 2u), 0);
}
}
if ((event_flags & 0x0400u) != 0 &&
win16_read_u8(win16_dgroup_pointer(0x07c7)) == 0 &&
win16_read_u8(win16_dgroup_pointer(0x07c8)) == 0) {
win16_write_u32(win16_dgroup_pointer(0x0855), 0, 0);
win16_write_u8(win16_dgroup_pointer(0x07c7), 1);
}
if ((event_flags & 0x0004u) != 0) {
handle_sprite_award(caller_bp, receiver);
}
if ((event_flags & 0x0008u) != 0) {
handle_ready_upgrade(caller_bp);
}
if ((event_flags & 0x0010u) != 0) {
handle_item_bank(caller_bp, receiver);
}
if ((event_flags & 0x1000u) != 0) {
handle_three_record_switch(caller_bp, receiver, record_id);
}
if ((event_flags & 0x0040u) != 0) {
handle_timed_record(caller_bp, receiver, record_id);
}
}