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
191 lines
6.2 KiB
C
191 lines
6.2 KiB
C
/* Dynamic Win16 multimedia helper DLL lifecycle from Code3. */
|
|
|
|
#include "tdkpin_multimedia.h"
|
|
|
|
extern Win16FarPtr g_multimedia_procedures[11]; /* 1028:4750..477b */
|
|
extern HINSTANCE16 g_multimedia_module; /* 1028:4781 unaligned */
|
|
extern uint8_t g_multimedia_initialized; /* 1028:4784 */
|
|
|
|
static const char *const multimedia_procedure_names[11] = {
|
|
"CTL3DAUTOSUBCLASS",
|
|
"CTL3DCOLORCHANGE",
|
|
"CTL3DCTLCOLOREX",
|
|
"CTL3DENABLED",
|
|
"CTL3DDLGFRAMEPAINT",
|
|
"CTL3DGETVER",
|
|
"CTL3DREGISTER",
|
|
"CTL3DSUBCLASSDLG",
|
|
"CTL3DSUBCLASSDLGEX",
|
|
"CTL3DSUBCLASSCTL",
|
|
"CTL3DUNREGISTER",
|
|
};
|
|
|
|
/*
|
|
* 1010:00a2 — test whether the CTL3D file can be found in the exact original
|
|
* search order. The 80-byte result is only a success indicator; LoadLibrary
|
|
* later receives the unchanged global library name/path buffer.
|
|
*/
|
|
bool multimedia_locate_library(
|
|
Win16FarPtr module, Win16FarPtr library_path)
|
|
{
|
|
uint8_t search_path_bytes[256];
|
|
uint8_t result_bytes[80];
|
|
uint8_t extension_bytes[5];
|
|
uint8_t file_name_bytes[9];
|
|
Win16FarPtr search_path = win16_stack_pointer(search_path_bytes);
|
|
Win16FarPtr result = win16_stack_pointer(result_bytes);
|
|
Win16FarPtr extension = win16_stack_pointer(extension_bytes);
|
|
Win16FarPtr file_name = win16_stack_pointer(file_name_bytes);
|
|
|
|
win16_write_u8(search_path, 0);
|
|
(void)multimedia_search_file(search_path, library_path, result);
|
|
if (win16_read_u8(result) != 0) {
|
|
return true;
|
|
}
|
|
|
|
(void)GetWindowsDirectoryFar16(search_path, 0xff);
|
|
(void)multimedia_search_file(search_path, library_path, result);
|
|
if (win16_read_u8(result) != 0) {
|
|
return true;
|
|
}
|
|
|
|
(void)GetSystemDirectoryFar16(search_path, 0xff);
|
|
(void)multimedia_search_file(search_path, library_path, result);
|
|
if (win16_read_u8(result) != 0) {
|
|
return true;
|
|
}
|
|
|
|
HINSTANCE16 module_handle = GetModuleHandle16(module);
|
|
(void)GetModuleFileNameFar16(module_handle, search_path, 0x100);
|
|
(void)multimedia_split_path(
|
|
extension, file_name, search_path, search_path);
|
|
(void)multimedia_search_file(search_path, library_path, result);
|
|
if (win16_read_u8(result) != 0) {
|
|
return true;
|
|
}
|
|
|
|
Win16FarPtr environment_path = multimedia_environment_value(
|
|
win16_dgroup_pointer(0x046a));
|
|
(void)multimedia_search_file(environment_path, library_path, result);
|
|
return win16_read_u8(result) != 0;
|
|
}
|
|
|
|
/* 1010:0002 — unload the helper and clear every dynamically resolved slot. */
|
|
void multimedia_unload(void)
|
|
{
|
|
if (g_multimedia_module >= 0x20) {
|
|
(void)FreeLibrary16(g_multimedia_module);
|
|
}
|
|
g_multimedia_initialized = 0;
|
|
for (unsigned index = 0; index < 11; index++) {
|
|
g_multimedia_procedures[index] = 0;
|
|
}
|
|
}
|
|
|
|
/* 1010:0079 — notify the helper for this task, then perform common unload. */
|
|
void multimedia_finalize_current_task(void)
|
|
{
|
|
Win16FarPtr terminate_task = g_multimedia_procedures[10];
|
|
if (g_multimedia_initialized && terminate_task != 0) {
|
|
(void)win16_call_pascal_u16(terminate_task, GetCurrentTask16());
|
|
multimedia_unload();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 1010:03e0 — multimedia-aware TApplication destructor. Finalize the current
|
|
* helper task before the application/root destructors and owned-allocation
|
|
* cleanup. The hidden VMT zero passed to TApplication prevents double free.
|
|
*/
|
|
void multimedia_application_destruct(
|
|
Win16FarPtr object, uint16_t vmt)
|
|
{
|
|
multimedia_finalize_current_task();
|
|
object_windows_application_destruct(object, 0);
|
|
BorlandObjectCallFrame frame = {object, vmt};
|
|
borland_finish_object_destructor(&frame, 0);
|
|
}
|
|
|
|
/*
|
|
* 1010:03af — exported loader wrapper. Each byte argument occupies a 16-bit
|
|
* Pascal stack slot; only AL is returned. The target always clears 1028:4783
|
|
* after the underlying 1010:01c2 call, regardless of its result.
|
|
*/
|
|
uint8_t multimedia_initialize_export(
|
|
uint8_t argument_1,
|
|
uint8_t argument_2,
|
|
uint8_t argument_3,
|
|
uint16_t argument_4,
|
|
uint16_t argument_5)
|
|
{
|
|
uint8_t result = multimedia_initialize(
|
|
argument_1, argument_2, argument_3, argument_4, argument_5);
|
|
g_multimedia_pending = 0;
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* 1010:01c2 — load and validate CTL3D.DLL/CTL3DV2.DLL.
|
|
* Procedure slots retain their exact binary order at 1028:4750..477b.
|
|
*/
|
|
uint8_t multimedia_initialize(
|
|
uint8_t argument_1,
|
|
uint8_t argument_2,
|
|
uint8_t auto_subclass,
|
|
uint16_t module_offset,
|
|
uint16_t module_selector)
|
|
{
|
|
if (g_multimedia_initialized) {
|
|
return 1;
|
|
}
|
|
|
|
if (!g_multimedia_legacy_windows) {
|
|
(void)SetErrorMode16(0x8000);
|
|
g_multimedia_module = LoadLibrary16(g_multimedia_library_path);
|
|
(void)SetErrorMode16(0);
|
|
} else {
|
|
bool located = multimedia_locate_library(
|
|
win16_make_far_pointer(module_selector, module_offset),
|
|
win16_dgroup_pointer(0x478d));
|
|
g_multimedia_module = located
|
|
? LoadLibrary16(g_multimedia_library_path)
|
|
: 0;
|
|
}
|
|
|
|
if (g_multimedia_module < 0x20) {
|
|
return 0;
|
|
}
|
|
for (unsigned index = 0; index < 11; index++) {
|
|
if (index == 8 && g_multimedia_legacy_windows) {
|
|
g_multimedia_procedures[index] = 0;
|
|
} else {
|
|
g_multimedia_procedures[index] = GetProcAddress16(
|
|
g_multimedia_module, multimedia_procedure_names[index]);
|
|
}
|
|
}
|
|
|
|
g_multimedia_initialized = win16_call_pascal_u16(
|
|
g_multimedia_procedures[6], GetCurrentTask16()) != 0;
|
|
if (g_multimedia_initialized) {
|
|
g_multimedia_version =
|
|
win16_call_pascal_u16_no_args(g_multimedia_procedures[5]);
|
|
bool enabled = win16_call_pascal_u16_no_args(
|
|
g_multimedia_procedures[3]) != 0;
|
|
g_multimedia_initialized = enabled && g_multimedia_version >= 9;
|
|
}
|
|
if (!g_multimedia_initialized) {
|
|
multimedia_unload();
|
|
return 0;
|
|
}
|
|
|
|
g_multimedia_argument_2 = argument_2;
|
|
g_multimedia_argument_1 = argument_1;
|
|
if (!g_multimedia_legacy_windows && auto_subclass != 0) {
|
|
g_multimedia_auto_subclassed = win16_call_pascal_u16(
|
|
g_multimedia_procedures[0], g_win16_instance) != 0;
|
|
} else {
|
|
g_multimedia_auto_subclassed = 0;
|
|
}
|
|
return 1;
|
|
}
|