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
201 lines
7.8 KiB
C
201 lines
7.8 KiB
C
/* TDKPIN logical-palette creation and PAL resource loading. */
|
|
|
|
#include "tdkpin_palette.h"
|
|
|
|
#include "tdkpin_strings.h"
|
|
|
|
enum {
|
|
DGROUP_PALETTE_HANDLE = 0x444e,
|
|
DGROUP_PALETTE_RGB = 0x4450,
|
|
DGROUP_PAL_RESOURCE_TYPE = 0x045a,
|
|
DGROUP_DAT_RESOURCE_TYPE = 0x045e,
|
|
DGROUP_DISPLAY_DRIVER = 0x0462,
|
|
PALETTE_COLOR_COUNT = 256,
|
|
PALETTE_RGB_BYTES = 768,
|
|
LOGPALETTE_VERSION_0300 = 0x0300,
|
|
PALETTE_ENTRY_NOCOLLAPSE = 0x04,
|
|
CREATE_DIBITMAP_INITIALIZE = 0x00000004,
|
|
};
|
|
|
|
/* Code2 1008:11bd and 131e, represented with their exact signed bounds. */
|
|
static const BorlandInt32Range g_uint16_range = {0, 0xffff};
|
|
static const BorlandInt32Range g_palette_byte_range = {0, 0x02ff};
|
|
|
|
/*
|
|
* 1008:0023 — create and briefly realize a 256-entry PC_NOCOLLAPSE palette in
|
|
* the supplied window DC. Allocation failure is the sole guarded palette path;
|
|
* all GDI results are otherwise consumed exactly as returned.
|
|
*/
|
|
void tdkpin_realize_nocollapse_palette(HWND16 window)
|
|
{
|
|
Win16FarPtr logical_palette = borland_get_mem(sizeof(LOGPALETTE256));
|
|
if (logical_palette == 0) {
|
|
return;
|
|
}
|
|
win16_write_u16(logical_palette, 0, LOGPALETTE_VERSION_0300);
|
|
win16_write_u16(logical_palette, 2, PALETTE_COLOR_COUNT);
|
|
for (uint16_t index = 0; index < PALETTE_COLOR_COUNT; index++) {
|
|
Win16FarPtr entry = win16_far_add_offset(
|
|
logical_palette,
|
|
(uint16_t)(4 + index * sizeof(PALETTEENTRY16)));
|
|
win16_write_u8(entry, 0);
|
|
win16_write_u8(win16_far_add_offset(entry, 1), 0);
|
|
win16_write_u8(win16_far_add_offset(entry, 2), 0);
|
|
win16_write_u8(
|
|
win16_far_add_offset(entry, 3), PALETTE_ENTRY_NOCOLLAPSE);
|
|
}
|
|
|
|
HDC16 dc = GetDC16(window);
|
|
HPALETTE16 palette = CreatePalette16(logical_palette);
|
|
HPALETTE16 previous = SelectPalette16(dc, palette, 0);
|
|
(void)RealizePalette16(dc);
|
|
(void)SelectPalette16(dc, previous, 1);
|
|
(void)DeleteObject16(palette);
|
|
(void)ReleaseDC16(window, dc);
|
|
borland_free_mem(logical_palette, sizeof(LOGPALETTE256));
|
|
}
|
|
|
|
/*
|
|
* 1008:11c5 — materialize a 256-entry LOGPALETTE from the packed DGROUP RGB
|
|
* table, create the GDI palette, then range-check and free the temporary block.
|
|
*/
|
|
void tdkpin_create_palette_from_rgb_table(void)
|
|
{
|
|
const uint32_t allocation_bytes = sizeof(LOGPALETTE256);
|
|
Win16FarPtr logical_palette = borland_get_mem((uint16_t)allocation_bytes);
|
|
win16_write_u16(logical_palette, 0, LOGPALETTE_VERSION_0300);
|
|
win16_write_u16(logical_palette, 2, PALETTE_COLOR_COUNT);
|
|
for (uint16_t index = 0; index < PALETTE_COLOR_COUNT; index++) {
|
|
Win16FarPtr source = win16_far_add_offset(
|
|
win16_dgroup_pointer(DGROUP_PALETTE_RGB),
|
|
(uint16_t)(index * 3));
|
|
Win16FarPtr entry = win16_far_add_offset(
|
|
logical_palette,
|
|
(uint16_t)(4 + index * sizeof(PALETTEENTRY16)));
|
|
win16_write_u8(entry, win16_read_u8(source));
|
|
win16_write_u8(
|
|
win16_far_add_offset(entry, 1),
|
|
win16_read_u8(win16_far_add_offset(source, 1)));
|
|
win16_write_u8(
|
|
win16_far_add_offset(entry, 2),
|
|
win16_read_u8(win16_far_add_offset(source, 2)));
|
|
win16_write_u8(win16_far_add_offset(entry, 3), 0);
|
|
}
|
|
HPALETTE16 palette = CreatePalette16(logical_palette);
|
|
win16_write_u16(
|
|
win16_dgroup_pointer(DGROUP_PALETTE_HANDLE), 0, palette);
|
|
borland_check_int32_range((int32_t)allocation_bytes, &g_uint16_range);
|
|
borland_free_mem(logical_palette, (uint16_t)allocation_bytes);
|
|
}
|
|
|
|
/*
|
|
* 1008:1326 — load a custom "pal" resource and copy exactly 768 bytes. Both
|
|
* source and destination index expressions pass through the compiler-emitted
|
|
* 0..767 range descriptor on every iteration. The loaded resource is unlocked
|
|
* but deliberately not FreeResource'd here, matching the binary.
|
|
*/
|
|
void tdkpin_load_palette_resource(
|
|
Win16FarPtr destination,
|
|
Win16FarPtr resource_name,
|
|
HINSTANCE16 instance)
|
|
{
|
|
HRSRC16 found = FindResource16(
|
|
instance,
|
|
resource_name,
|
|
win16_dgroup_pointer(DGROUP_PAL_RESOURCE_TYPE));
|
|
HGLOBAL16 resource = LoadResource16(instance, found);
|
|
Win16FarPtr source = LockResource16(resource);
|
|
for (uint16_t index = 0; index < PALETTE_RGB_BYTES; index++) {
|
|
borland_check_int32_range(index, &g_palette_byte_range);
|
|
uint8_t value = win16_read_u8(win16_far_add_offset(source, index));
|
|
borland_check_int32_range(index, &g_palette_byte_range);
|
|
win16_write_u8(win16_far_add_offset(destination, index), value);
|
|
}
|
|
(void)GlobalUnlock16(resource);
|
|
}
|
|
|
|
/*
|
|
* 1008:13a8 — project all 256 packed RGB triplets to BITMAPINFO RGBQUADs.
|
|
* The compiler's dead ((1<<bitCount)-1)*4+8 calculation is retained for its
|
|
* exact 16/32-bit wrap semantics even though no later instruction reads it.
|
|
*/
|
|
void tdkpin_fill_bitmap_info_palette(Win16FarPtr bitmap_info)
|
|
{
|
|
uint16_t bit_count = win16_read_u16(win16_far_add_offset(
|
|
bitmap_info, offsetof(BITMAPINFOHEADER16, bit_count)));
|
|
uint8_t shift = (uint8_t)bit_count & 0x1f;
|
|
uint16_t color_count = (uint16_t)(UINT32_C(1) << shift);
|
|
uint32_t unused_allocation_tail =
|
|
((uint32_t)color_count - 1) * sizeof(RGBQUAD16) + 8;
|
|
(void)unused_allocation_tail;
|
|
|
|
for (uint16_t index = 0; index < PALETTE_COLOR_COUNT; index++) {
|
|
Win16FarPtr source = win16_far_add_offset(
|
|
win16_dgroup_pointer(DGROUP_PALETTE_RGB),
|
|
(uint16_t)(index * 3));
|
|
Win16FarPtr color = win16_far_add_offset(
|
|
bitmap_info,
|
|
(uint16_t)(offsetof(BITMAPINFO256, colors) +
|
|
index * sizeof(RGBQUAD16)));
|
|
win16_write_u8(color, win16_read_u8(win16_far_add_offset(source, 2)));
|
|
win16_write_u8(
|
|
win16_far_add_offset(color, 1),
|
|
win16_read_u8(win16_far_add_offset(source, 1)));
|
|
win16_write_u8(
|
|
win16_far_add_offset(color, 2),
|
|
win16_read_u8(source));
|
|
win16_write_u8(win16_far_add_offset(color, 3), 0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 1008:149d — load one custom DAT DIB, create a BITMAPINFO with the global
|
|
* palette, realize that palette in a Display DC, and create a CBM_INIT bitmap.
|
|
* The allocated BITMAPINFO block is not released by the target and remains an
|
|
* intentional ownership leak in this reconstruction.
|
|
*/
|
|
HBITMAP16 tdkpin_load_bitmap_resource(
|
|
Win16FarPtr resource_name,
|
|
HINSTANCE16 instance)
|
|
{
|
|
HRSRC16 found = FindResource16(
|
|
instance,
|
|
resource_name,
|
|
win16_dgroup_pointer(DGROUP_DAT_RESOURCE_TYPE));
|
|
HGLOBAL16 resource = LoadResource16(instance, found);
|
|
Win16FarPtr resource_bytes = LockResource16(resource);
|
|
|
|
BITMAPINFOHEADER16 header;
|
|
Win16FarPtr stack_header = win16_stack_pointer(&header);
|
|
borland_far_copy(stack_header, resource_bytes, sizeof(header));
|
|
Win16FarPtr bits = win16_far_add_offset(resource_bytes, sizeof(header));
|
|
|
|
uint8_t shift = (uint8_t)header.bit_count & 0x1f;
|
|
uint16_t color_bytes = (uint16_t)(UINT32_C(1) << shift);
|
|
color_bytes = (uint16_t)(color_bytes << 2);
|
|
uint16_t bitmap_info_bytes = (uint16_t)(sizeof(header) + color_bytes);
|
|
Win16FarPtr bitmap_info = borland_get_mem(bitmap_info_bytes);
|
|
borland_far_copy(bitmap_info, stack_header, sizeof(header));
|
|
tdkpin_fill_bitmap_info_palette(bitmap_info);
|
|
|
|
HDC16 dc = CreateDC16(
|
|
win16_dgroup_pointer(DGROUP_DISPLAY_DRIVER), 0, 0, 0);
|
|
HPALETTE16 palette = win16_read_u16(
|
|
win16_dgroup_pointer(DGROUP_PALETTE_HANDLE));
|
|
HPALETTE16 previous = SelectPalette16(dc, palette, 0);
|
|
(void)UnrealizeObject16(palette);
|
|
(void)RealizePalette16(dc);
|
|
HBITMAP16 bitmap = CreateDIBitmap16(
|
|
dc,
|
|
stack_header,
|
|
CREATE_DIBITMAP_INITIALIZE,
|
|
bits,
|
|
bitmap_info,
|
|
0);
|
|
(void)SelectPalette16(dc, previous, 0);
|
|
(void)DeleteDC16(dc);
|
|
(void)GlobalUnlock16(resource);
|
|
(void)FreeResource16(resource);
|
|
return bitmap;
|
|
}
|