Files
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

294 lines
11 KiB
C

/* Complete game-window setup and collision/table construction at 1000:0270. */
#include "tdkpin_game_setup.h"
#include "tdkpin_application.h"
#include "tdkpin_command_line.h"
#include "tdkpin_heap.h"
#include "tdkpin_palette.h"
#include "tdkpin_shortstrings.h"
#include "tdkpin_strings.h"
#include "tdkpin_system_timer.h"
#include "tdkpin_window_placement.h"
#include <stdint.h>
#include "tdkpin_game_setup_collision_data.inc"
typedef struct {
uint16_t word_0;
uint16_t word_2;
uint16_t word_4;
uint16_t word_6;
} SetupWordQuad;
static const SetupWordQuad g_game_setup_word_quads[20] = {
{0x0067, 0x0130, 0x0079, 0x0143},
{0x0077, 0x0118, 0x0089, 0x012b},
{0x0094, 0x0110, 0x00a6, 0x0123},
{0x00b1, 0x0118, 0x00c3, 0x012b},
{0x00c1, 0x012f, 0x00d3, 0x0142},
{0x0000, 0x0014, 0x0000, 0x0014},
{0x0000, 0x0014, 0x0000, 0x0014},
{0x0000, 0x0014, 0x0000, 0x0014},
{0x0000, 0x0014, 0x0000, 0x0014},
{0x0000, 0x0014, 0x0000, 0x0014},
{0x0000, 0x0014, 0x0000, 0x0014},
{0x0000, 0x0014, 0x0000, 0x0014},
{0x0000, 0x0014, 0x0000, 0x0014},
{0x0000, 0x0014, 0x0000, 0x0014},
{0x0057, 0x0044, 0x0067, 0x0054},
{0x0069, 0x0029, 0x007b, 0x0039},
{0x008a, 0x0033, 0x009a, 0x0043},
{0x008a, 0x0053, 0x009a, 0x0063},
{0x006a, 0x005e, 0x007a, 0x006e},
{0x0009, 0x000e, 0x0019, 0x001e},
};
static void write_far(Win16FarPtr base, uint16_t offset, Win16FarPtr value)
{
win16_write_u32(base, offset, value);
}
static void fail_setup(
Win16FarPtr window,
Win16FarPtr text,
HWND16 owner,
bool close_window)
{
Win16FarPtr message_box = win16_read_far_pointer(
win16_dgroup_pointer(0x0666), 0);
(void)win16_call_message_box_far(
message_box,
owner,
text,
win16_dgroup_pointer(0x02aa),
0x2010);
if (close_window && window != 0) {
object_windows_close_if_allowed(window);
}
borland_runtime_exit(0);
}
static void initialize_base_state(Win16FarPtr window)
{
win16_write_u8(win16_dgroup_pointer(0x07cf), 0);
win16_write_u8(win16_dgroup_pointer(0x0874), 0);
win16_write_u8(win16_dgroup_pointer(0x0875), 0);
win16_write_u16(window, 0x41, 0);
win16_write_u16(window, 0x43, 0);
win16_write_u16(window, 0x45, 0);
win16_write_u16(window, 0x47, 0);
if (borland_heap_free_space() < UINT32_C(0x00400000)) {
fail_setup(window, win16_dgroup_pointer(0x0260), 0, true);
}
object_windows_setup_window(window);
HCURSOR16 cursor = LoadCursor16(0, win16_make_far_pointer(0, 0x7f02));
(void)SetCursor16(cursor);
HWND16 handle = win16_read_u16(win16_far_add_offset(window, 4));
tdkpin_realize_nocollapse_palette(handle);
tdkpin_load_palette_resource(
win16_dgroup_pointer(0x4450),
win16_make_far_pointer(0, 999),
win16_read_u16(win16_dgroup_pointer(0x06fe)));
tdkpin_create_palette_from_rgb_table();
win16_write_u8(win16_dgroup_pointer(0x07cc), 1);
win16_write_u16(window, 0x4d, 0);
win16_write_u16(window, 0x4f, 0);
win16_write_u8(win16_far_add_offset(window, 0x51), 0);
win16_write_u8(win16_far_add_offset(window, 0x0bd8), 0);
win16_write_u32(win16_dgroup_pointer(0x0720), 0, GetTickCount16());
win16_write_u16(window, 0x0be3, 0);
win16_write_u8(win16_dgroup_pointer(0x07c7), 0);
win16_write_u8(win16_dgroup_pointer(0x07c8), 0);
win16_write_u16(win16_dgroup_pointer(0x086d), 0, 10);
win16_write_u16(win16_dgroup_pointer(0x086f), 0, 10);
win16_write_u8(win16_dgroup_pointer(0x0873), 0);
win16_write_u8(win16_dgroup_pointer(0x07ca), 0);
win16_write_u8(win16_dgroup_pointer(0x07cb), 0);
win16_write_u8(win16_dgroup_pointer(0x07c9), 1);
win16_write_u32(window, 0x0bca, 0);
win16_write_u8(win16_far_add_offset(window, 0x0bd9), 0);
win16_write_u32(window, 0x0bc2, 325000);
win16_write_u32(window, 0x0bc6, 413000);
win16_write_u32(win16_dgroup_pointer(0x07d3), 0, 325000);
win16_write_u32(win16_dgroup_pointer(0x07d7), 0, 413000);
win16_write_u8(win16_far_add_offset(window, 0x0bdd), 0);
win16_write_u8(win16_far_add_offset(window, 0x0bdf), 0);
win16_write_u8(win16_far_add_offset(window, 0x0bdc), 0);
win16_write_u8(win16_far_add_offset(window, 0x0be0), 0);
win16_write_u8(win16_far_add_offset(window, 0x0be1), 0);
win16_write_u8(win16_far_add_offset(window, 0x0be2), 0);
win16_write_u8(win16_dgroup_pointer(0x07c5), 0);
win16_write_u8(win16_far_add_offset(window, 0x0bde), 1);
win16_write_u8(win16_dgroup_pointer(0x07d0), 0);
win16_write_u8(win16_dgroup_pointer(0x07d1), 0);
win16_write_u8(win16_far_add_offset(window, 0x61), 1);
win16_write_u16(win16_dgroup_pointer(0x086b), 0, 99);
win16_write_u16(win16_dgroup_pointer(0x0869), 0, 0);
win16_write_u16(win16_dgroup_pointer(0x0876), 0, 0);
win16_write_u16(win16_dgroup_pointer(0x0878), 0, 0);
win16_write_u16(win16_dgroup_pointer(0x087a), 0, 0);
win16_write_u16(win16_dgroup_pointer(0x087c), 0, 0);
}
static void initialize_player_defaults(Win16FarPtr window)
{
for (uint16_t item = 1; item <= 20; item++) {
for (uint16_t player = 1; player <= 4; player++) {
win16_write_u8(
win16_far_add_offset(window, (uint16_t)(
0x047b + (player - 1u) * 0x14u + item)),
0);
}
}
for (uint16_t player = 1; player <= 4; player++) {
win16_write_u32(
win16_dgroup_pointer((uint16_t)(0x097e + player * 4u)), 0, 0);
win16_write_u32(
win16_dgroup_pointer((uint16_t)(0x098e + player * 4u)), 0, 0);
win16_write_u8(
win16_dgroup_pointer((uint16_t)(0x09a1 + player)), 1);
win16_write_u16(
win16_dgroup_pointer((uint16_t)(0x09a4 + player * 2u)), 0, 0);
win16_write_u16(
win16_dgroup_pointer((uint16_t)(0x081f + player * 2u)), 0, 0xffff);
win16_write_u8(
win16_dgroup_pointer((uint16_t)(0x0816 + player)), 1);
win16_write_u32(
win16_dgroup_pointer((uint16_t)(0x0835 + player * 4u)), 0, 1);
win16_write_u8(
win16_dgroup_pointer((uint16_t)(0x081a + player)), 1);
}
static const uint32_t thresholds[4] = {
140000u, 650000u, 1300000u, 4000000u};
for (uint16_t index = 0; index < 4; index++) {
win16_write_u32(
win16_dgroup_pointer((uint16_t)(0x0829 + index * 4u)),
0,
thresholds[index]);
}
}
static void initialize_ball_projection(Win16FarPtr window)
{
win16_write_u32(window, 0x0baa, 0);
win16_write_u32(window, 0x0bae, 0);
win16_write_u16(window, 0x0bce, 0);
win16_write_u16(window, 0x0bd0, 0);
win16_write_u16(window, 0x0bd2, 0);
int32_t pixel_x = borland_divide_i32(325500, 1000).quotient;
int32_t pixel_y = borland_divide_i32(413500, 1000).quotient;
win16_write_u32(window, 0x0ba2, (uint32_t)pixel_x);
win16_write_u32(window, 0x0ba6, (uint32_t)pixel_y);
win16_write_u32(window, 0x0bb2, 0);
win16_write_u32(window, 0x0bb6, 0);
win16_write_u8(win16_far_add_offset(window, 0x0bda), 0);
win16_write_u8(win16_far_add_offset(window, 0x0bdb), 0);
win16_write_u32(window, 0x0bd4, 0);
}
static void build_ini_file_name(Win16FarPtr result)
{
uint8_t parameter_bytes[256];
uint8_t module_name_bytes[103];
Win16FarPtr parameter = win16_stack_pointer(parameter_bytes);
Win16FarPtr module_name = win16_stack_pointer(module_name_bytes);
borland_parameter_string(0, parameter);
(void)borland_short_to_c_string(module_name, parameter);
borland_parameter_string(0, parameter);
uint16_t dot = borland_short_position(
win16_relocated_code_pointer(1, 0x026e), parameter);
(void)borland_far_strncpy(result, module_name, dot);
(void)borland_far_strcat(result, win16_dgroup_pointer(0x02c3));
}
static void initialize_profile_and_timer(Win16FarPtr window)
{
uint8_t ini_file_bytes[101];
Win16FarPtr ini_file = win16_stack_pointer(ini_file_bytes);
build_ini_file_name(ini_file);
uint16_t language = GetPrivateProfileInt16(
win16_dgroup_pointer(0x02c7),
win16_dgroup_pointer(0x02d0),
1,
ini_file);
if (language < 1 || language > 5) {
language = 1;
}
win16_write_u16(win16_dgroup_pointer(0x07cd), 0, language);
uint16_t speed_word = GetPrivateProfileInt16(
win16_dgroup_pointer(0x02c7),
win16_dgroup_pointer(0x02d9),
3,
ini_file);
uint8_t speed = (uint8_t)speed_word;
if (speed < 1 || speed > 5) {
speed = 3;
}
win16_write_u8(win16_far_add_offset(window, 0x60), speed);
win16_write_u32(window, 0x5a, 15);
win16_write_u32(window, 0x56, 3800);
static const uint16_t delay_by_speed[5] = {50, 40, 30, 20, 10};
static const uint16_t step_by_speed[5] = {5, 4, 3, 2, 1};
uint16_t delay = delay_by_speed[speed - 1u];
win16_write_u16(
win16_dgroup_pointer(0x0871), 0, step_by_speed[speed - 1u]);
Win16FarPtr timer = tdkpin_system_timer_construct(
0,
0x05c0,
0,
1,
delay,
win16_read_u16(win16_far_add_offset(window, 4)));
write_far(window, 0x49, timer);
if (timer == 0) {
fail_setup(window, win16_dgroup_pointer(0x02df),
win16_read_u16(win16_far_add_offset(window, 4)), false);
}
}
static void initialize_collision_and_quad_tables(void)
{
for (uint16_t index = 0; index < 177; index++) {
tdkpin_initialize_collision_record(&g_game_setup_collision_calls[index]);
}
for (uint16_t index = 0; index < 20; index++) {
const SetupWordQuad *quad = &g_game_setup_word_quads[index];
tdkpin_set_word_quad(
quad->word_6,
quad->word_4,
quad->word_2,
quad->word_0,
(uint16_t)(index + 1u));
}
}
static void initialize_collision_mirrors(Win16FarPtr window)
{
for (uint16_t id = 1; id <= 175; id++) {
Win16FarPtr record = win16_dgroup_pointer((uint16_t)(
0x095b + id * 0x53u));
win16_write_u8(
win16_far_add_offset(window, (uint16_t)(0x03cc + id)),
win16_read_u8(win16_far_add_offset(record, 0x34)));
win16_write_u16(
window,
(uint16_t)(0x0a42 + id * 2u),
win16_read_u16(win16_far_add_offset(record, 0x43)));
win16_write_u8(win16_far_add_offset(window, (uint16_t)(0x61 + id)), 0);
}
}
/* 1000:0270 -- complete TDKPIN game-window setup. */
void tdkpin_setup_game_window(Win16FarPtr window)
{
initialize_base_state(window);
initialize_player_defaults(window);
initialize_ball_projection(window);
initialize_profile_and_timer(window);
initialize_collision_and_quad_tables();
initialize_collision_mirrors(window);
}