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
This commit is contained in:
@@ -0,0 +1,373 @@
|
||||
#include "../tdkpin_multimedia.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
Win16FarPtr g_multimedia_procedures[11];
|
||||
HINSTANCE16 g_multimedia_module;
|
||||
uint8_t g_multimedia_initialized;
|
||||
uint8_t g_multimedia_pending;
|
||||
uint16_t g_multimedia_version;
|
||||
uint8_t g_multimedia_auto_subclassed;
|
||||
uint8_t g_multimedia_argument_2;
|
||||
uint8_t g_multimedia_argument_1;
|
||||
uint8_t g_multimedia_legacy_windows;
|
||||
char g_multimedia_library_path[21] = "CTL3D.DLL";
|
||||
Win16FarPtr g_object_windows_application;
|
||||
HINSTANCE16 g_win16_previous_instance;
|
||||
HINSTANCE16 g_win16_instance;
|
||||
uint16_t g_win16_show_command;
|
||||
Win16FarPtr g_win16_message_box;
|
||||
Win16FarPtr g_object_windows_dispatch_proc;
|
||||
uint16_t g_object_windows_thunk_code_selector;
|
||||
Win16FarPtr g_object_windows_free_thunk;
|
||||
Win16FarPtr g_object_windows_message_hook;
|
||||
|
||||
static HINSTANCE16 g_freed_module;
|
||||
static Win16FarPtr g_called_procedure;
|
||||
static uint16_t g_called_argument;
|
||||
static unsigned g_sequence;
|
||||
static bool g_check_sequence;
|
||||
static Win16FarPtr g_destroyed_application;
|
||||
static HINSTANCE16 g_load_result;
|
||||
static uint16_t g_register_result;
|
||||
static uint16_t g_version_result;
|
||||
static uint16_t g_enabled_result;
|
||||
static uint16_t g_auto_result;
|
||||
static int g_search_success_attempt;
|
||||
static unsigned g_search_calls;
|
||||
static char g_search_paths[5][256];
|
||||
static unsigned g_load_calls;
|
||||
static unsigned g_proc_calls;
|
||||
static UINT16 g_error_modes[4];
|
||||
static unsigned g_error_mode_calls;
|
||||
static uint8_t g_dgroup[0x5000];
|
||||
|
||||
static void write_far_string(Win16FarPtr destination, const char *source)
|
||||
{
|
||||
size_t length = strlen(source);
|
||||
for (size_t index = 0; index <= length; index++) {
|
||||
win16_write_u8(
|
||||
win16_far_add_offset(destination, (uint16_t)index),
|
||||
(uint8_t)source[index]);
|
||||
}
|
||||
}
|
||||
|
||||
static void read_far_string(
|
||||
Win16FarPtr source, char *destination, size_t capacity)
|
||||
{
|
||||
size_t index = 0;
|
||||
while (index + 1 < capacity) {
|
||||
uint8_t byte = win16_read_u8(
|
||||
win16_far_add_offset(source, (uint16_t)index));
|
||||
destination[index++] = (char)byte;
|
||||
if (byte == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
destination[capacity - 1] = 0;
|
||||
}
|
||||
|
||||
BOOL16 FreeLibrary16(HINSTANCE16 module)
|
||||
{
|
||||
if (g_check_sequence) {
|
||||
assert(++g_sequence == 2);
|
||||
}
|
||||
g_freed_module = module;
|
||||
return 1;
|
||||
}
|
||||
|
||||
Win16Handle GetCurrentTask16(void)
|
||||
{
|
||||
return 0x1234;
|
||||
}
|
||||
|
||||
uint16_t win16_call_pascal_u16(Win16FarPtr procedure, uint16_t argument)
|
||||
{
|
||||
if (g_check_sequence) {
|
||||
assert(++g_sequence == 1);
|
||||
}
|
||||
g_called_procedure = procedure;
|
||||
g_called_argument = argument;
|
||||
if (procedure == g_multimedia_procedures[6]) {
|
||||
return g_register_result;
|
||||
}
|
||||
if (procedure == g_multimedia_procedures[0]) {
|
||||
assert(argument == g_win16_instance);
|
||||
return g_auto_result;
|
||||
}
|
||||
return 0xabcd;
|
||||
}
|
||||
|
||||
uint16_t win16_call_pascal_u16_no_args(Win16FarPtr procedure)
|
||||
{
|
||||
if (procedure == g_multimedia_procedures[5]) {
|
||||
return g_version_result;
|
||||
}
|
||||
assert(procedure == g_multimedia_procedures[3]);
|
||||
return g_enabled_result;
|
||||
}
|
||||
|
||||
void object_windows_application_destruct(
|
||||
Win16FarPtr object, uint16_t vmt)
|
||||
{
|
||||
assert(++g_sequence == 3);
|
||||
assert(vmt == 0);
|
||||
g_destroyed_application = object;
|
||||
}
|
||||
|
||||
void borland_finish_object_destructor(
|
||||
BorlandObjectCallFrame *frame, uint16_t vmt_field_offset)
|
||||
{
|
||||
assert(++g_sequence == 4);
|
||||
assert(frame->object == g_destroyed_application);
|
||||
assert(frame->vmt_argument == 0x0200 && vmt_field_offset == 0);
|
||||
}
|
||||
|
||||
UINT16 SetErrorMode16(UINT16 mode)
|
||||
{
|
||||
g_error_modes[g_error_mode_calls++] = mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
HINSTANCE16 LoadLibrary16(const char *library_name)
|
||||
{
|
||||
assert(strcmp(library_name, g_multimedia_library_path) == 0);
|
||||
g_load_calls++;
|
||||
return g_load_result;
|
||||
}
|
||||
|
||||
Win16FarPtr GetProcAddress16(
|
||||
HINSTANCE16 module, const char *procedure_name)
|
||||
{
|
||||
static const char *const names[11] = {
|
||||
"CTL3DAUTOSUBCLASS", "CTL3DCOLORCHANGE", "CTL3DCTLCOLOREX",
|
||||
"CTL3DENABLED", "CTL3DDLGFRAMEPAINT", "CTL3DGETVER",
|
||||
"CTL3DREGISTER", "CTL3DSUBCLASSDLG", "CTL3DSUBCLASSDLGEX",
|
||||
"CTL3DSUBCLASSCTL", "CTL3DUNREGISTER",
|
||||
};
|
||||
assert(module == g_load_result && g_proc_calls < 11);
|
||||
unsigned index = 0;
|
||||
while (index < 11 && strcmp(procedure_name, names[index]) != 0) {
|
||||
index++;
|
||||
}
|
||||
assert(index < 11);
|
||||
g_proc_calls++;
|
||||
return win16_make_far_pointer(
|
||||
0x9000, (uint16_t)(0x100 + index));
|
||||
}
|
||||
|
||||
Win16FarPtr multimedia_search_file(
|
||||
Win16FarPtr search_path, Win16FarPtr file_name, Win16FarPtr result)
|
||||
{
|
||||
char file_name_text[32];
|
||||
read_far_string(file_name, file_name_text, sizeof(file_name_text));
|
||||
assert(strcmp(file_name_text, g_multimedia_library_path) == 0);
|
||||
assert(g_search_calls < 5);
|
||||
read_far_string(
|
||||
search_path, g_search_paths[g_search_calls],
|
||||
sizeof(g_search_paths[g_search_calls]));
|
||||
bool found = (int)g_search_calls == g_search_success_attempt;
|
||||
g_search_calls++;
|
||||
win16_write_u8(result, found ? 'X' : 0);
|
||||
win16_write_u8(win16_far_add_offset(result, 1), 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
UINT16 GetWindowsDirectoryFar16(Win16FarPtr buffer, UINT16 size)
|
||||
{
|
||||
assert(size == 0xff);
|
||||
write_far_string(buffer, "WIN");
|
||||
return 3;
|
||||
}
|
||||
|
||||
UINT16 GetSystemDirectoryFar16(Win16FarPtr buffer, UINT16 size)
|
||||
{
|
||||
assert(size == 0xff);
|
||||
write_far_string(buffer, "SYS");
|
||||
return 3;
|
||||
}
|
||||
|
||||
HINSTANCE16 GetModuleHandle16(Win16FarPtr module_name)
|
||||
{
|
||||
assert(module_name == win16_make_far_pointer(0x7000, 0x1234));
|
||||
return 0x5555;
|
||||
}
|
||||
|
||||
UINT16 GetModuleFileNameFar16(
|
||||
HINSTANCE16 module, Win16FarPtr buffer, UINT16 size)
|
||||
{
|
||||
assert(module == 0x5555 && size == 0x100);
|
||||
write_far_string(buffer, "C:\\APP\\GAME.EXE");
|
||||
return 15;
|
||||
}
|
||||
|
||||
uint16_t multimedia_split_path(
|
||||
Win16FarPtr extension,
|
||||
Win16FarPtr file_name,
|
||||
Win16FarPtr directory,
|
||||
Win16FarPtr input)
|
||||
{
|
||||
char input_text[32];
|
||||
read_far_string(input, input_text, sizeof(input_text));
|
||||
assert(strcmp(input_text, "C:\\APP\\GAME.EXE") == 0);
|
||||
assert(extension != 0 && file_name != 0 && directory == input);
|
||||
write_far_string(directory, "MOD");
|
||||
return 7;
|
||||
}
|
||||
|
||||
Win16FarPtr multimedia_environment_value(Win16FarPtr name)
|
||||
{
|
||||
char name_text[8];
|
||||
read_far_string(name, name_text, sizeof(name_text));
|
||||
assert(strcmp(name_text, "PATH") == 0);
|
||||
return win16_make_far_pointer(0x7000, 0x4700);
|
||||
}
|
||||
|
||||
static void fill_procedures(void)
|
||||
{
|
||||
for (unsigned index = 0; index < 11; index++) {
|
||||
g_multimedia_procedures[index] =
|
||||
win16_make_far_pointer(0x8000, (uint16_t)(0x100 + index));
|
||||
}
|
||||
}
|
||||
|
||||
static void reset_loader(void)
|
||||
{
|
||||
for (unsigned index = 0; index < 11; index++) {
|
||||
g_multimedia_procedures[index] = 0;
|
||||
}
|
||||
g_multimedia_initialized = 0;
|
||||
g_multimedia_module = 0;
|
||||
g_multimedia_version = 0;
|
||||
g_multimedia_auto_subclassed = 0;
|
||||
g_multimedia_argument_1 = 0;
|
||||
g_multimedia_argument_2 = 0;
|
||||
g_multimedia_legacy_windows = 0;
|
||||
g_load_result = 64;
|
||||
g_register_result = 1;
|
||||
g_version_result = 9;
|
||||
g_enabled_result = 1;
|
||||
g_auto_result = 1;
|
||||
g_search_success_attempt = 0;
|
||||
g_search_calls = 0;
|
||||
g_load_calls = 0;
|
||||
g_proc_calls = 0;
|
||||
g_error_mode_calls = 0;
|
||||
g_freed_module = 0;
|
||||
write_far_string(
|
||||
win16_make_far_pointer(0x7000, 0x478d),
|
||||
g_multimedia_library_path);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uint8_t stack_anchor = 0;
|
||||
uintptr_t stack_base = (uintptr_t)&stack_anchor - 0x8000;
|
||||
win16_reset_segment_bindings();
|
||||
win16_bind_segment(0x7000, g_dgroup, sizeof(g_dgroup), true);
|
||||
win16_set_dgroup_selector(0x7000);
|
||||
win16_bind_segment(0x7200, (void *)stack_base, 0x10000, true);
|
||||
write_far_string(win16_make_far_pointer(0x7000, 0x046a), "PATH");
|
||||
write_far_string(win16_make_far_pointer(0x7000, 0x4700), "ENV");
|
||||
write_far_string(
|
||||
win16_make_far_pointer(0x7000, 0x478d),
|
||||
g_multimedia_library_path);
|
||||
fill_procedures();
|
||||
g_multimedia_module = 31;
|
||||
g_multimedia_initialized = 1;
|
||||
g_freed_module = 0;
|
||||
multimedia_unload();
|
||||
assert(g_freed_module == 0 && g_multimedia_module == 31);
|
||||
assert(!g_multimedia_initialized);
|
||||
for (unsigned index = 0; index < 11; index++) {
|
||||
assert(g_multimedia_procedures[index] == 0);
|
||||
}
|
||||
|
||||
fill_procedures();
|
||||
g_multimedia_module = 64;
|
||||
g_multimedia_initialized = 1;
|
||||
Win16FarPtr terminator = g_multimedia_procedures[10];
|
||||
g_called_procedure = 0;
|
||||
multimedia_finalize_current_task();
|
||||
assert(g_called_procedure == terminator && g_called_argument == 0x1234);
|
||||
assert(g_freed_module == 64 && !g_multimedia_initialized);
|
||||
|
||||
fill_procedures();
|
||||
g_multimedia_initialized = 0;
|
||||
g_called_procedure = 0;
|
||||
multimedia_finalize_current_task();
|
||||
assert(g_called_procedure == 0 && g_multimedia_procedures[0] != 0);
|
||||
|
||||
fill_procedures();
|
||||
g_multimedia_module = 64;
|
||||
g_multimedia_initialized = 1;
|
||||
g_sequence = 0;
|
||||
g_check_sequence = true;
|
||||
Win16FarPtr application = win16_make_far_pointer(0x7000, 2);
|
||||
multimedia_application_destruct(application, 0x0200);
|
||||
g_check_sequence = false;
|
||||
assert(g_sequence == 4 && g_destroyed_application == application);
|
||||
reset_loader();
|
||||
g_win16_instance = 0x4444;
|
||||
g_multimedia_pending = 0x7f;
|
||||
assert(multimedia_initialize_export(1, 2, 1, 0x1234, 0x7000) == 1);
|
||||
assert(g_multimedia_pending == 0);
|
||||
assert(g_multimedia_argument_1 == 1 && g_multimedia_argument_2 == 2);
|
||||
assert(g_multimedia_auto_subclassed == 1);
|
||||
assert(g_proc_calls == 11 && g_error_mode_calls == 2);
|
||||
assert(g_error_modes[0] == 0x8000 && g_error_modes[1] == 0);
|
||||
|
||||
assert(multimedia_initialize(9, 8, 0, 0x1234, 0x7000) == 1);
|
||||
assert(g_load_calls == 1); /* Already initialized: no second load. */
|
||||
|
||||
reset_loader();
|
||||
g_load_result = 31;
|
||||
assert(multimedia_initialize(1, 2, 1, 0x1234, 0x7000) == 0);
|
||||
assert(g_proc_calls == 0 && g_multimedia_module == 31);
|
||||
|
||||
reset_loader();
|
||||
g_register_result = 0;
|
||||
assert(multimedia_initialize(1, 2, 1, 0x1234, 0x7000) == 0);
|
||||
assert(g_freed_module == 64 && g_multimedia_procedures[0] == 0);
|
||||
|
||||
reset_loader();
|
||||
g_version_result = 8;
|
||||
assert(multimedia_initialize(1, 2, 1, 0x1234, 0x7000) == 0);
|
||||
assert(g_freed_module == 64);
|
||||
|
||||
reset_loader();
|
||||
g_multimedia_legacy_windows = 1;
|
||||
g_search_success_attempt = -1;
|
||||
assert(multimedia_initialize(1, 2, 1, 0x1234, 0x7000) == 0);
|
||||
assert(g_load_calls == 0 && g_multimedia_module == 0);
|
||||
|
||||
reset_loader();
|
||||
g_multimedia_legacy_windows = 1;
|
||||
assert(multimedia_initialize(3, 4, 1, 0x1234, 0x7000) == 1);
|
||||
assert(g_proc_calls == 10 && g_multimedia_procedures[8] == 0);
|
||||
assert(g_multimedia_auto_subclassed == 0);
|
||||
assert(g_error_mode_calls == 0);
|
||||
|
||||
for (int attempt = 0; attempt < 5; attempt++) {
|
||||
g_search_success_attempt = attempt;
|
||||
g_search_calls = 0;
|
||||
assert(multimedia_locate_library(
|
||||
win16_make_far_pointer(0x7000, 0x1234),
|
||||
win16_make_far_pointer(0x7000, 0x478d)));
|
||||
assert(g_search_calls == (unsigned)attempt + 1);
|
||||
}
|
||||
assert(strcmp(g_search_paths[0], "") == 0);
|
||||
assert(strcmp(g_search_paths[1], "WIN") == 0);
|
||||
assert(strcmp(g_search_paths[2], "SYS") == 0);
|
||||
assert(strcmp(g_search_paths[3], "MOD") == 0);
|
||||
assert(strcmp(g_search_paths[4], "ENV") == 0);
|
||||
g_search_success_attempt = -1;
|
||||
g_search_calls = 0;
|
||||
assert(!multimedia_locate_library(
|
||||
win16_make_far_pointer(0x7000, 0x1234),
|
||||
win16_make_far_pointer(0x7000, 0x478d)));
|
||||
assert(g_search_calls == 5);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user