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
443 lines
15 KiB
C
443 lines
15 KiB
C
/* ObjectWindows TApplication construction/destruction used by TDKPIN. */
|
|
|
|
#include "tdkpin_application.h"
|
|
|
|
const char object_windows_error_format[] = "Error code = %d. Continue?";
|
|
const char object_windows_error_caption[] = "Application Error";
|
|
|
|
enum {
|
|
APPLICATION_STATUS_OFFSET = 2,
|
|
APPLICATION_NAME_OFFSET = 4,
|
|
APPLICATION_FIELD_08_OFFSET = 8,
|
|
APPLICATION_FIELD_0A_OFFSET = 10,
|
|
APPLICATION_FIELD_0C_OFFSET = 12,
|
|
APPLICATION_FIELD_0E_OFFSET = 14,
|
|
APPLICATION_FIELD_10_OFFSET = 16,
|
|
APPLICATION_INIT_APPLICATION_SLOT = 0x10,
|
|
APPLICATION_INIT_INSTANCE_SLOT = 0x14,
|
|
APPLICATION_PREPROCESS_MESSAGE_SLOT = 0x24,
|
|
APPLICATION_DIALOG_MESSAGE_SLOT = 0x28,
|
|
APPLICATION_ACCELERATOR_SLOT = 0x2c,
|
|
APPLICATION_MDI_ACCELERATOR_SLOT = 0x30,
|
|
APPLICATION_RUN_SLOT = 0x20,
|
|
APPLICATION_ERROR_SLOT = 0x40,
|
|
APPLICATION_SETUP_SLOT = 0x18,
|
|
APPLICATION_CREATE_MAIN_WINDOW_SLOT = 0x34,
|
|
APPLICATION_VALIDATE_WINDOW_SLOT = 0x3c,
|
|
APPLICATION_ERROR_SLOT_FOR_WINDOW = 0x40,
|
|
APPLICATION_IDLE_SLOT = 0x0c,
|
|
APPLICATION_ACCELERATOR_OFFSET = 0x0c,
|
|
APPLICATION_ACTIVE_DIALOG_OFFSET = 0x0e,
|
|
APPLICATION_MAIN_WINDOW_OFFSET = 8,
|
|
OBJECT_WINDOWS_BOUND_DISPATCH_OFFSET = 0x0133,
|
|
OBJECT_WINDOWS_OBJECT_HANDLE_OFFSET = 4,
|
|
OBJECT_WINDOWS_MDI_CLIENT_SLOT = 0x30,
|
|
OBJECT_WINDOWS_CAN_CLOSE_SLOT = 0x3c,
|
|
OBJECT_WINDOWS_CREATE_SLOT = 0x20,
|
|
OBJECT_WINDOWS_EXEC_DIALOG_SLOT = 0x4c,
|
|
WIN16_WM_QUIT = 0x0012,
|
|
WIN16_PM_REMOVE = 1,
|
|
WIN16_MB_YESNO_ICONHAND = 0x0014,
|
|
WIN16_IDNO = 7,
|
|
};
|
|
|
|
static void call_application_vmt_slot(
|
|
Win16FarPtr application, uint16_t slot)
|
|
{
|
|
uint16_t vmt = win16_read_u16(application);
|
|
Win16FarPtr procedure =
|
|
win16_read_far_pointer(win16_dgroup_pointer(vmt), slot);
|
|
win16_call_object_method(procedure, application);
|
|
}
|
|
|
|
/*
|
|
* 1018:1a61 — construct ObjectWindows' application root.
|
|
*
|
|
* The process-global application pointer is published before virtual
|
|
* initialization. InitApplication (+0x10) runs only for the first Win16
|
|
* instance; InitInstance (+0x14) runs only while the status word remains zero.
|
|
*/
|
|
Win16FarPtr object_windows_application_construct(
|
|
Win16FarPtr object, uint16_t vmt, Win16FarPtr name)
|
|
{
|
|
BorlandObjectCallFrame frame = {object, vmt};
|
|
if (!borland_enter_object_constructor(&frame, 0)) {
|
|
return frame.object;
|
|
}
|
|
|
|
frame.object = borland_default_object_constructor(frame.object, 0);
|
|
win16_write_u16(frame.object, APPLICATION_NAME_OFFSET,
|
|
win16_far_offset(name));
|
|
win16_write_u16(frame.object, APPLICATION_NAME_OFFSET + 2,
|
|
win16_far_selector(name));
|
|
g_object_windows_application = frame.object;
|
|
win16_write_u16(frame.object, APPLICATION_FIELD_0C_OFFSET, 0);
|
|
win16_write_u16(frame.object, APPLICATION_STATUS_OFFSET, 0);
|
|
win16_write_u16(frame.object, APPLICATION_FIELD_08_OFFSET, 0);
|
|
win16_write_u16(frame.object, APPLICATION_FIELD_0A_OFFSET, 0);
|
|
win16_write_u16(frame.object, APPLICATION_FIELD_0E_OFFSET, 0);
|
|
win16_write_u16(frame.object, APPLICATION_FIELD_10_OFFSET, 0);
|
|
|
|
g_object_windows_dispatch_proc = MakeProcInstance16(
|
|
win16_relocated_code_pointer(
|
|
4, OBJECT_WINDOWS_BOUND_DISPATCH_OFFSET),
|
|
g_win16_instance);
|
|
borland_initialize_heap_reserve();
|
|
|
|
if (g_win16_previous_instance == 0) {
|
|
call_application_vmt_slot(
|
|
frame.object, APPLICATION_INIT_APPLICATION_SLOT);
|
|
}
|
|
if (win16_read_u16(win16_far_add_offset(
|
|
frame.object, APPLICATION_STATUS_OFFSET)) == 0) {
|
|
call_application_vmt_slot(
|
|
frame.object, APPLICATION_INIT_INSTANCE_SLOT);
|
|
}
|
|
return frame.object;
|
|
}
|
|
|
|
/* 1018:1b08 — release the shared proc instance, then destroy/free the object. */
|
|
void object_windows_application_destruct(Win16FarPtr object, uint16_t vmt)
|
|
{
|
|
FreeProcInstance16(g_object_windows_dispatch_proc);
|
|
borland_default_object_destructor(object, 0);
|
|
BorlandObjectCallFrame frame = {object, vmt};
|
|
borland_finish_object_destructor(&frame, 0);
|
|
}
|
|
|
|
/* 1018:1b35 — default IdleAction: no background work was performed. */
|
|
bool object_windows_application_idle_default(Win16FarPtr application)
|
|
{
|
|
(void)application;
|
|
return false;
|
|
}
|
|
|
|
/* 1018:1b4b — default first-instance InitApplication hook is intentionally empty. */
|
|
void object_windows_application_init_application_default(
|
|
Win16FarPtr application)
|
|
{
|
|
(void)application;
|
|
}
|
|
|
|
/*
|
|
* 1018:1bdf — publish the currently active dialog at object +0x0e/+0x10.
|
|
* The modal-dialog path saves and restores this exact far pointer, and the
|
|
* message loop uses it as the IsDialogMessage16 target.
|
|
*/
|
|
void object_windows_application_set_active_dialog(
|
|
Win16FarPtr application, Win16FarPtr dialog)
|
|
{
|
|
win16_write_u16(application, APPLICATION_FIELD_0E_OFFSET,
|
|
win16_far_offset(dialog));
|
|
win16_write_u16(application, APPLICATION_FIELD_10_OFFSET,
|
|
win16_far_selector(dialog));
|
|
}
|
|
|
|
static Win16FarPtr application_vmt_procedure(
|
|
Win16FarPtr application, uint16_t slot)
|
|
{
|
|
uint16_t vmt = win16_read_u16(application);
|
|
return win16_read_far_pointer(win16_dgroup_pointer(vmt), slot);
|
|
}
|
|
|
|
/* 1018:0e9e — show one ObjectWindows object only after it owns an HWND. */
|
|
void object_windows_show(Win16FarPtr object, INT16 command)
|
|
{
|
|
HWND16 window = win16_read_u16(win16_far_add_offset(
|
|
object, OBJECT_WINDOWS_OBJECT_HANDLE_OFFSET));
|
|
if (window != 0) {
|
|
(void)ShowWindow16(window, command);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 1018:1b57 — run setup, create/publish MainWindow, then show it. A null
|
|
* creator result stores ObjectWindows status -5 instead.
|
|
*/
|
|
void object_windows_application_initialize(Win16FarPtr application)
|
|
{
|
|
win16_call_object_method(
|
|
application_vmt_procedure(application, APPLICATION_SETUP_SLOT),
|
|
application);
|
|
Win16FarPtr main_window = win16_call_object_far_method(
|
|
application_vmt_procedure(
|
|
application, APPLICATION_CREATE_MAIN_WINDOW_SLOT),
|
|
application);
|
|
win16_write_u16(application, APPLICATION_MAIN_WINDOW_OFFSET,
|
|
win16_far_offset(main_window));
|
|
win16_write_u16(application, APPLICATION_MAIN_WINDOW_OFFSET + 2,
|
|
win16_far_selector(main_window));
|
|
if (main_window == 0) {
|
|
win16_write_u16(application, APPLICATION_STATUS_OFFSET, 0xfffb);
|
|
return;
|
|
}
|
|
object_windows_show(main_window, (INT16)g_win16_show_command);
|
|
}
|
|
|
|
/* 1018:1bb0 — enter the message loop, or report a preexisting status error. */
|
|
void object_windows_application_run(Win16FarPtr application)
|
|
{
|
|
uint16_t status = win16_read_u16(win16_far_add_offset(
|
|
application, APPLICATION_STATUS_OFFSET));
|
|
if (status == 0) {
|
|
win16_call_object_method(
|
|
application_vmt_procedure(application, APPLICATION_RUN_SLOT),
|
|
application);
|
|
return;
|
|
}
|
|
win16_call_object_u16_method(
|
|
application_vmt_procedure(application, APPLICATION_ERROR_SLOT),
|
|
application,
|
|
status);
|
|
}
|
|
|
|
/*
|
|
* 1018:1bfc — ObjectWindows application message loop.
|
|
* Only WM_QUIT terminates the loop. With an empty queue, a false IdleAction
|
|
* result causes WaitMessage; either idle outcome then repeats the loop.
|
|
*/
|
|
void object_windows_application_message_loop(Win16FarPtr application)
|
|
{
|
|
MSG16 message;
|
|
bool quit = false;
|
|
do {
|
|
if (PeekMessage16(&message, 0, 0, 0, WIN16_PM_REMOVE) != 0) {
|
|
if (message.message == WIN16_WM_QUIT) {
|
|
quit = true;
|
|
} else {
|
|
Win16FarPtr filter = application_vmt_procedure(
|
|
application, APPLICATION_PREPROCESS_MESSAGE_SLOT);
|
|
if (!win16_call_application_message_filter(
|
|
filter, application, &message)) {
|
|
(void)TranslateMessage16(&message);
|
|
(void)DispatchMessage16(&message);
|
|
}
|
|
}
|
|
} else {
|
|
Win16FarPtr idle = application_vmt_procedure(
|
|
application, APPLICATION_IDLE_SLOT);
|
|
if (!win16_call_object_bool_method(idle, application)) {
|
|
(void)WaitMessage16();
|
|
}
|
|
}
|
|
} while (!quit);
|
|
win16_write_u16(application, APPLICATION_STATUS_OFFSET, message.wparam);
|
|
}
|
|
|
|
/* 1018:1c7f — dialog, MDI, then ordinary accelerator preprocessing. */
|
|
bool object_windows_application_preprocess_message(
|
|
Win16FarPtr application, MSG16 *message)
|
|
{
|
|
static const uint16_t slots[] = {
|
|
APPLICATION_DIALOG_MESSAGE_SLOT,
|
|
APPLICATION_MDI_ACCELERATOR_SLOT,
|
|
APPLICATION_ACCELERATOR_SLOT,
|
|
};
|
|
for (size_t index = 0; index < sizeof(slots) / sizeof(slots[0]); index++) {
|
|
Win16FarPtr filter =
|
|
application_vmt_procedure(application, slots[index]);
|
|
if (win16_call_application_message_filter(
|
|
filter, application, message)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* 1018:1cd6 — route through the active dialog's HWND when present.
|
|
* NEG AX; SBB AL,AL; NEG AL normalizes only AL, deliberately preserving AH
|
|
* from the negated Win16 BOOL return.
|
|
*/
|
|
uint16_t object_windows_application_process_dialog_message(
|
|
Win16FarPtr application, MSG16 *message)
|
|
{
|
|
Win16FarPtr dialog =
|
|
win16_read_far_pointer(application, APPLICATION_ACTIVE_DIALOG_OFFSET);
|
|
if (dialog == 0) {
|
|
return 0;
|
|
}
|
|
HWND16 window = win16_read_u16(win16_far_add_offset(
|
|
dialog, OBJECT_WINDOWS_OBJECT_HANDLE_OFFSET));
|
|
if (window == 0) {
|
|
return 0;
|
|
}
|
|
uint16_t api_result = (uint16_t)IsDialogMessage16(window, message);
|
|
uint16_t negated = (uint16_t)(0u - api_result);
|
|
return (uint16_t)((negated & 0xff00u) | (api_result != 0 ? 1u : 0u));
|
|
}
|
|
|
|
/* 1018:1d22 — process the application's ordinary accelerator table. */
|
|
bool object_windows_application_process_accelerator(
|
|
Win16FarPtr application, MSG16 *message)
|
|
{
|
|
HACCEL16 accelerator = win16_read_u16(win16_far_add_offset(
|
|
application, APPLICATION_ACCELERATOR_OFFSET));
|
|
if (accelerator == 0) {
|
|
return false;
|
|
}
|
|
Win16FarPtr main_window = win16_read_far_pointer(
|
|
application, APPLICATION_MAIN_WINDOW_OFFSET);
|
|
HWND16 window = win16_read_u16(win16_far_add_offset(
|
|
main_window, OBJECT_WINDOWS_OBJECT_HANDLE_OFFSET));
|
|
return TranslateAccelerator16(window, accelerator, message) != 0;
|
|
}
|
|
|
|
/* 1018:1d64 — ask the main window for its MDI client, then translate there. */
|
|
bool object_windows_application_process_mdi_accelerator(
|
|
Win16FarPtr application, MSG16 *message)
|
|
{
|
|
Win16FarPtr main_window = win16_read_far_pointer(
|
|
application, APPLICATION_MAIN_WINDOW_OFFSET);
|
|
uint16_t main_vmt = win16_read_u16(main_window);
|
|
Win16FarPtr getter = win16_read_far_pointer(
|
|
win16_dgroup_pointer(main_vmt), OBJECT_WINDOWS_MDI_CLIENT_SLOT);
|
|
Win16FarPtr client =
|
|
win16_call_object_far_method(getter, main_window);
|
|
if (client == 0) {
|
|
return false;
|
|
}
|
|
HWND16 window = win16_read_u16(win16_far_add_offset(
|
|
client, OBJECT_WINDOWS_OBJECT_HANDLE_OFFSET));
|
|
return TranslateMDISysAccel16(window, message) != 0;
|
|
}
|
|
|
|
/* 1018:1f56 — delegate application shutdown approval to MainWindow.CanClose. */
|
|
bool object_windows_application_can_close(Win16FarPtr application)
|
|
{
|
|
Win16FarPtr main_window = win16_read_far_pointer(
|
|
application, APPLICATION_MAIN_WINDOW_OFFSET);
|
|
uint16_t vmt = win16_read_u16(main_window);
|
|
Win16FarPtr can_close = win16_read_far_pointer(
|
|
win16_dgroup_pointer(vmt), OBJECT_WINDOWS_CAN_CLOSE_SLOT);
|
|
return win16_call_object_bool_method(can_close, main_window);
|
|
}
|
|
|
|
/* 1018:10e9 — default TWindow VMT +0x30: this is not an MDI frame. */
|
|
Win16FarPtr object_windows_window_get_mdi_client_default(
|
|
Win16FarPtr window)
|
|
{
|
|
(void)window;
|
|
return 0;
|
|
}
|
|
|
|
static void report_application_error(
|
|
Win16FarPtr application, uint16_t error)
|
|
{
|
|
win16_call_object_u16_method(
|
|
application_vmt_procedure(
|
|
application, APPLICATION_ERROR_SLOT_FOR_WINDOW),
|
|
application,
|
|
error);
|
|
}
|
|
|
|
/*
|
|
* 1018:1db1 — validate a newly constructed window against heap-reserve and
|
|
* object-status failures. Every failure reports through VMT +0x40 and invokes
|
|
* the candidate's virtual destructor with dispose mode 1. OOM (-2) also
|
|
* reprovisions the Borland emergency reserve afterward.
|
|
*/
|
|
Win16FarPtr object_windows_application_validate_window(
|
|
Win16FarPtr application, Win16FarPtr window)
|
|
{
|
|
if (window == 0) {
|
|
return 0;
|
|
}
|
|
if (borland_heap_reserve_is_unallocated()) {
|
|
report_application_error(application, 0xfffe);
|
|
borland_dispose_object(window);
|
|
borland_ensure_heap_reserve();
|
|
return 0;
|
|
}
|
|
|
|
uint16_t status = win16_read_u16(win16_far_add_offset(window, 2));
|
|
if (status != 0) {
|
|
report_application_error(application, status);
|
|
borland_dispose_object(window);
|
|
return 0;
|
|
}
|
|
return window;
|
|
}
|
|
|
|
/* 1018:1e39 — validate and create a modeless window, disposing failures. */
|
|
Win16FarPtr object_windows_application_make_window(
|
|
Win16FarPtr application, Win16FarPtr window)
|
|
{
|
|
if (window == 0) {
|
|
return 0;
|
|
}
|
|
Win16FarPtr validated = win16_call_application_window_method(
|
|
application_vmt_procedure(
|
|
application, APPLICATION_VALIDATE_WINDOW_SLOT),
|
|
application,
|
|
window);
|
|
if (validated == 0) {
|
|
return 0;
|
|
}
|
|
|
|
uint16_t vmt = win16_read_u16(window);
|
|
Win16FarPtr create = win16_read_far_pointer(
|
|
win16_dgroup_pointer(vmt), OBJECT_WINDOWS_CREATE_SLOT);
|
|
if (win16_call_object_bool_method(create, window)) {
|
|
return window;
|
|
}
|
|
report_application_error(
|
|
application, win16_read_u16(win16_far_add_offset(window, 2)));
|
|
borland_dispose_object(window);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* 1018:1eb0 — validate, execute, and always dispose a modal dialog. Negative
|
|
* execution results are reported and translated to the fixed fallback 2.
|
|
*/
|
|
int16_t object_windows_application_exec_dialog(
|
|
Win16FarPtr application, Win16FarPtr dialog)
|
|
{
|
|
int16_t result = 2;
|
|
Win16FarPtr validated = win16_call_application_window_method(
|
|
application_vmt_procedure(
|
|
application, APPLICATION_VALIDATE_WINDOW_SLOT),
|
|
application,
|
|
dialog);
|
|
if (validated == 0) {
|
|
return result;
|
|
}
|
|
|
|
uint16_t vmt = win16_read_u16(dialog);
|
|
Win16FarPtr execute = win16_read_far_pointer(
|
|
win16_dgroup_pointer(vmt), OBJECT_WINDOWS_EXEC_DIALOG_SLOT);
|
|
int16_t execute_result =
|
|
win16_call_object_int_method(execute, dialog);
|
|
if (execute_result < 0) {
|
|
report_application_error(application, (uint16_t)execute_result);
|
|
} else {
|
|
result = execute_result;
|
|
}
|
|
borland_dispose_object(dialog);
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* 1018:1f14 — format and display one ObjectWindows application error.
|
|
* The original passes a far pointer to the caller's uint16 error word as the
|
|
* wvsprintf argument list. Choosing IDNO (7) exits with that full AX word.
|
|
*/
|
|
void object_windows_application_error(
|
|
Win16FarPtr application, uint16_t error)
|
|
{
|
|
(void)application;
|
|
char text[32];
|
|
uint16_t arguments[1] = {error};
|
|
(void)wvsprintf16(text, object_windows_error_format, arguments);
|
|
INT16 response = win16_call_message_box(
|
|
g_win16_message_box,
|
|
0,
|
|
text,
|
|
object_windows_error_caption,
|
|
WIN16_MB_YESNO_ICONHAND);
|
|
if (response == WIN16_IDNO) {
|
|
borland_runtime_exit(error);
|
|
}
|
|
}
|