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

416 lines
15 KiB
C

/* Address-linked high-score persistence from TDKPIN Code1. */
#include "tdkpin_highscores.h"
#include "tdkpin_application.h"
#include "tdkpin_dialog.h"
#include "tdkpin_game_windows.h"
enum {
HIGHSCORE_XOR_KEY = 0x0010,
HIGHSCORE_FILE_HEADER = 0x0014,
HIGHSCORE_FILE_HEADER_BYTES = 16,
HIGHSCORE_FILE_NAME = 0x0312,
HIGHSCORE_LOAD_FILE_NAME = 0x0320,
HIGHSCORE_DEFAULT_NAME = 0x032d,
HIGHSCORE_EDIT_INITIAL_NAME = 0x0336,
HIGHSCORE_EDIT_CAPTION = 0x0349,
HIGHSCORE_EDIT_LABEL = 0x0373,
HIGHSCORE_WINDOW_TITLE = 0x038b,
HIGHSCORE_HEADING = 0x02f4,
HIGHSCORE_SINGLE_DIGIT_PREFIX = 0x0300,
HIGHSCORE_RANK_SUFFIX = 0x0303,
OBJECT_WINDOWS_APPLICATION_POINTER = 0x064e,
CURRENT_PLAYER = 0x07d1,
HIGHSCORE_INSERT_INDEX = 0x07d2,
HIGHSCORE_SCORE_INDEX_ZERO = 0x087a,
HIGHSCORE_NAME_INDEX_ZERO = 0x0890,
PLAYER_SCORE_INDEX_ZERO = 0x097e,
HIGHSCORE_SCORES_BYTES = TDKPIN_HIGHSCORE_COUNT * 4,
HIGHSCORE_NAMES_BYTES =
TDKPIN_HIGHSCORE_COUNT * TDKPIN_HIGHSCORE_NAME_BYTES,
};
static Win16FarPtr highscore_name(uint16_t one_based_index)
{
return win16_dgroup_pointer((uint16_t)(
HIGHSCORE_NAME_INDEX_ZERO +
one_based_index * TDKPIN_HIGHSCORE_NAME_BYTES));
}
static Win16FarPtr highscore_score(uint16_t one_based_index)
{
return win16_dgroup_pointer((uint16_t)(
HIGHSCORE_SCORE_INDEX_ZERO + one_based_index * 4u));
}
static Win16FarPtr highscore_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 application_virtual_method(
Win16FarPtr application, uint16_t slot)
{
uint16_t vmt = win16_read_u16(application);
return win16_read_far_pointer(win16_dgroup_pointer(vmt), slot);
}
static void fill_indeterminate_stack_bytes(
Win16FarPtr destination, uint16_t count)
{
/*
* ENTER does not initialize SS. The portable model exposes one selected
* residue word instead of introducing undefined C reads; alternating its
* low/high bytes preserves the target's word-oriented stale-stack model.
*/
uint16_t residue = win16_indeterminate_u16();
for (uint16_t index = 0; index < count; index++) {
win16_write_u8(
win16_far_add_offset(destination, index),
(uint8_t)(index & 1u ? residue >> 8 : residue));
}
}
/*
* 1000:a8ea -- persist the complete high-score table.
*
* The binary's 260-byte local payload has the 220-byte name table first and
* the 40-byte encrypted score table immediately after it. Each one-based
* global score is XORed with the same static dword key. The file order is
* header, encrypted scores, then names, despite the reverse local ordering.
*/
void tdkpin_save_highscores(uint16_t ignored_static_link)
{
(void)ignored_static_link;
HFILE16 file = LCreate16(win16_dgroup_pointer(HIGHSCORE_FILE_NAME), 0);
if (file == (HFILE16)-1) {
return;
}
uint8_t local_payload[HIGHSCORE_NAMES_BYTES + HIGHSCORE_SCORES_BYTES];
Win16FarPtr names = win16_stack_pointer(local_payload);
Win16FarPtr encrypted_scores =
win16_far_add_offset(names, HIGHSCORE_NAMES_BYTES);
borland_fill_bytes(names, HIGHSCORE_NAMES_BYTES, 0);
uint32_t key = win16_read_u32(
win16_dgroup_pointer(HIGHSCORE_XOR_KEY));
for (uint16_t index = 1; index <= TDKPIN_HIGHSCORE_COUNT; index++) {
uint16_t score_offset = (uint16_t)(
HIGHSCORE_SCORE_INDEX_ZERO + index * 4u);
uint16_t name_offset = (uint16_t)(
HIGHSCORE_NAME_INDEX_ZERO +
index * TDKPIN_HIGHSCORE_NAME_BYTES);
win16_write_u32(
encrypted_scores,
(uint16_t)((index - 1u) * 4u),
win16_read_u32(win16_dgroup_pointer(score_offset)) ^ key);
(void)borland_far_strcpy(
win16_far_add_offset(
names,
(uint16_t)((index - 1u) * TDKPIN_HIGHSCORE_NAME_BYTES)),
win16_dgroup_pointer(name_offset));
}
(void)LWrite16(
file,
win16_dgroup_pointer(HIGHSCORE_FILE_HEADER),
HIGHSCORE_FILE_HEADER_BYTES);
(void)LWrite16(file, encrypted_scores, HIGHSCORE_SCORES_BYTES);
(void)LWrite16(file, names, HIGHSCORE_NAMES_BYTES);
(void)LClose16(file);
}
/*
* 1000:a99c -- load and authenticate the complete high-score table.
*
* The same file is first opened with mode 0 and retried with mode 0x40. A
* 16-byte header is compared through the compiler's ShortString helpers. All
* _lread returns are ignored by the original: a short score read therefore
* leaves stale SS bytes to be decoded, while a short name read leaves the
* untouched tail of the global name table. Invalid or unavailable files
* install descending 1,000,000..100,000 scores and ten copies of "No Name".
*/
void tdkpin_load_highscores(uint16_t ignored_static_link)
{
(void)ignored_static_link;
bool loaded = false;
HFILE16 file = LOpen16(
win16_dgroup_pointer(HIGHSCORE_LOAD_FILE_NAME), 0);
if (file == (HFILE16)-1) {
file = LOpen16(
win16_dgroup_pointer(HIGHSCORE_LOAD_FILE_NAME), 0x40);
}
if (file != (HFILE16)-1) {
uint8_t raw_header[HIGHSCORE_FILE_HEADER_BYTES];
uint8_t encrypted_score_bytes[HIGHSCORE_SCORES_BYTES];
uint8_t file_header_short[256];
uint8_t expected_header_short[256];
Win16FarPtr raw_header_far = win16_stack_pointer(raw_header);
Win16FarPtr encrypted_scores =
win16_stack_pointer(encrypted_score_bytes);
Win16FarPtr file_header = win16_stack_pointer(file_header_short);
Win16FarPtr expected_header =
win16_stack_pointer(expected_header_short);
fill_indeterminate_stack_bytes(
raw_header_far, HIGHSCORE_FILE_HEADER_BYTES);
fill_indeterminate_stack_bytes(
encrypted_scores, HIGHSCORE_SCORES_BYTES);
(void)LRead16(
file, raw_header_far, HIGHSCORE_FILE_HEADER_BYTES);
borland_bytes_to_short(
file_header,
raw_header_far,
HIGHSCORE_FILE_HEADER_BYTES);
borland_bytes_to_short(
expected_header,
win16_dgroup_pointer(HIGHSCORE_FILE_HEADER),
HIGHSCORE_FILE_HEADER_BYTES);
if (borland_short_compare(file_header, expected_header) == 0) {
loaded = true;
(void)LRead16(file, encrypted_scores, HIGHSCORE_SCORES_BYTES);
(void)LRead16(
file,
win16_dgroup_pointer(
HIGHSCORE_NAME_INDEX_ZERO +
TDKPIN_HIGHSCORE_NAME_BYTES),
HIGHSCORE_NAMES_BYTES);
uint32_t key = win16_read_u32(
win16_dgroup_pointer(HIGHSCORE_XOR_KEY));
for (uint16_t index = 1;
index <= TDKPIN_HIGHSCORE_COUNT;
index++) {
uint32_t encrypted = win16_read_u32(
win16_far_add_offset(
encrypted_scores,
(uint16_t)((index - 1u) * 4u)));
win16_write_u32(
win16_dgroup_pointer((uint16_t)(
HIGHSCORE_SCORE_INDEX_ZERO + index * 4u)),
0,
encrypted ^ key);
}
}
(void)LClose16(file);
}
if (!loaded) {
for (uint16_t index = 1;
index <= TDKPIN_HIGHSCORE_COUNT;
index++) {
int32_t score = borland_multiply_i32(
(int32_t)(11u - index), 100000);
win16_write_u32(
win16_dgroup_pointer((uint16_t)(
HIGHSCORE_SCORE_INDEX_ZERO + index * 4u)),
0,
(uint32_t)score);
(void)borland_far_strcpy(
win16_dgroup_pointer((uint16_t)(
HIGHSCORE_NAME_INDEX_ZERO +
index * TDKPIN_HIGHSCORE_NAME_BYTES)),
win16_dgroup_pointer(HIGHSCORE_DEFAULT_NAME));
}
}
}
/*
* 1000:ab3c -- run optional name editing, then create/show the score window.
* The argument is the caller's BP; SS:[BP+6] is the actual window receiver.
*/
void tdkpin_show_highscore_windows(uint16_t caller_bp)
{
Win16FarPtr receiver = highscore_caller_receiver(caller_bp);
uint8_t insertion = win16_read_u8(
win16_dgroup_pointer(HIGHSCORE_INSERT_INDEX));
Win16FarPtr application = win16_read_far_pointer(
win16_dgroup_pointer(OBJECT_WINDOWS_APPLICATION_POINTER), 0);
if (insertion != 0) {
Win16FarPtr name = highscore_name(insertion);
(void)borland_far_strcpy(
name,
win16_dgroup_pointer(HIGHSCORE_EDIT_INITIAL_NAME));
Win16FarPtr dialog = object_windows_text_dialog_construct(
0,
0x01ce,
0x0016,
name,
win16_dgroup_pointer(HIGHSCORE_EDIT_LABEL),
win16_dgroup_pointer(HIGHSCORE_EDIT_CAPTION),
receiver);
(void)win16_call_application_dialog_method(
application_virtual_method(application, 0x38),
application,
dialog);
}
Win16FarPtr child = tdkpin_centered_child_window_construct(
0,
0x010e,
win16_dgroup_pointer(HIGHSCORE_WINDOW_TITLE),
receiver);
child = win16_call_application_window_method(
application_virtual_method(application, 0x34),
application,
child);
win16_write_u32(receiver, 0x41, child);
object_windows_show(
win16_read_far_pointer(receiver, 0x41), 1);
}
static bool score_is_greater(uint32_t left, uint32_t right)
{
int16_t left_high = (int16_t)(left >> 16);
int16_t right_high = (int16_t)(right >> 16);
return left_high > right_high ||
(left_high == right_high &&
(uint16_t)left > (uint16_t)right);
}
/* 1000:abe5 -- insert the active player's score into the descending top ten. */
void tdkpin_insert_current_score(uint16_t caller_bp)
{
uint8_t current_player = win16_read_u8(
win16_dgroup_pointer(CURRENT_PLAYER));
uint32_t new_score = win16_read_u32(
win16_dgroup_pointer((uint16_t)(
PLAYER_SCORE_INDEX_ZERO + (uint16_t)current_player * 4u)));
uint16_t insertion = 1;
while (!score_is_greater(
new_score, win16_read_u32(highscore_score(insertion)))) {
if (insertion == TDKPIN_HIGHSCORE_COUNT) {
return;
}
insertion++;
}
if ((uint16_t)(insertion + 1u) <= TDKPIN_HIGHSCORE_COUNT) {
for (uint16_t destination = TDKPIN_HIGHSCORE_COUNT;
destination != insertion;
destination--) {
win16_write_u32(
highscore_score(destination),
0,
win16_read_u32(highscore_score(
(uint16_t)(destination - 1u))));
borland_far_copy(
highscore_name(destination),
highscore_name((uint16_t)(destination - 1u)),
TDKPIN_HIGHSCORE_NAME_BYTES);
}
}
win16_write_u32(highscore_score(insertion), 0, new_score);
win16_write_u8(
win16_dgroup_pointer(HIGHSCORE_INSERT_INDEX),
(uint8_t)insertion);
tdkpin_show_highscore_windows(caller_bp);
tdkpin_save_highscores(caller_bp);
}
static void text_out_c_string(
HDC16 dc, INT16 x, INT16 y, Win16FarPtr text)
{
(void)TextOut16(
dc, x, y, text, (INT16)borland_far_strlen(text));
}
/*
* 1000:5c13 -- TWindow +4c paint override for the 360x300 high-score panel.
* The receiver and PAINTSTRUCT are ABI-visible but unused; all drawing uses
* the HDC supplied as the fifth word in the ten-byte Pascal argument frame.
*/
void tdkpin_paint_highscores(
Win16FarPtr ignored_window,
PAINTSTRUCT16 *ignored_paint,
HDC16 dc)
{
(void)ignored_window;
(void)ignored_paint;
HPALETTE16 palette = win16_read_u16(
win16_dgroup_pointer(0x444e));
HBITMAP16 background = win16_read_u16(
win16_dgroup_pointer(0x0867));
HPALETTE16 old_palette = SelectPalette16(dc, palette, 0);
(void)UnrealizeObject16(palette);
(void)RealizePalette16(dc);
HDC16 background_dc = CreateCompatibleDC16(dc);
HGDIOBJ16 old_background_bitmap =
SelectObject16(background_dc, background);
HDC16 buffer_dc = CreateCompatibleDC16(dc);
HBITMAP16 buffer_bitmap = CreateCompatibleBitmap16(dc, 360, 300);
HGDIOBJ16 old_buffer_bitmap =
SelectObject16(buffer_dc, buffer_bitmap);
COLORREF16 old_text_color = GetTextColor16(buffer_dc);
UINT16 old_text_alignment = SetTextAlign16(buffer_dc, 6);
INT16 old_background_mode = SetBkMode16(buffer_dc, 1);
(void)BitBlt16(
buffer_dc, 0, 0, 360, 300,
background_dc, 32, 204, 0x00cc0020u);
(void)SetTextColor16(buffer_dc, 0);
uint8_t display_bytes[41];
uint8_t conversion_bytes[41];
uint8_t number_short_bytes[256];
uint8_t name_short_bytes[256];
Win16FarPtr display = win16_stack_pointer(display_bytes);
Win16FarPtr conversion = win16_stack_pointer(conversion_bytes);
Win16FarPtr number_short = win16_stack_pointer(number_short_bytes);
Win16FarPtr name_short = win16_stack_pointer(name_short_bytes);
(void)borland_far_strcpy(
display, win16_dgroup_pointer(HIGHSCORE_HEADING));
text_out_c_string(buffer_dc, 180, 27, display);
for (uint16_t index = 1; index <= TDKPIN_HIGHSCORE_COUNT; index++) {
INT16 y = (INT16)(index * 20u + 40u);
(void)SetTextAlign16(buffer_dc, 0);
(void)borland_far_strcpy(
display,
win16_dgroup_pointer(
index < 10 ? HIGHSCORE_SINGLE_DIGIT_PREFIX : 0x02ff));
borland_format_i32_short_string(255, number_short, 0, index);
(void)borland_short_to_c_string(conversion, number_short);
(void)borland_far_strcat(display, conversion);
(void)borland_far_strcat(
display, win16_dgroup_pointer(HIGHSCORE_RANK_SUFFIX));
borland_bytes_to_short(
name_short, highscore_name(index), TDKPIN_HIGHSCORE_NAME_BYTES);
(void)borland_short_to_c_string(conversion, name_short);
(void)borland_far_strcat(display, conversion);
text_out_c_string(buffer_dc, 30, y, display);
(void)SetTextAlign16(buffer_dc, 2);
borland_format_i32_short_string(
255,
number_short,
0,
(int32_t)win16_read_u32(highscore_score(index)));
(void)borland_short_to_c_string(display, number_short);
text_out_c_string(buffer_dc, 320, y, display);
}
(void)BitBlt16(
dc, 0, 0, 360, 300, buffer_dc, 0, 0, 0x00cc0020u);
(void)SetTextColor16(buffer_dc, old_text_color);
(void)SetTextAlign16(buffer_dc, old_text_alignment);
(void)SetBkMode16(buffer_dc, old_background_mode);
(void)SelectObject16(buffer_dc, old_buffer_bitmap);
(void)DeleteObject16(buffer_bitmap);
(void)DeleteDC16(buffer_dc);
(void)SelectObject16(background_dc, old_background_bitmap);
(void)DeleteDC16(background_dc);
(void)SelectPalette16(dc, old_palette, 0);
}