Correct raw 1000:531e's Pascal-order BitBlt to destination (202,328), height 13, and width (stage-1)*7 from original DAT994. Render stages 2 through 35 over DAT995, add deterministic loading screenshots, and update the C harness and evidence ledger for the corrected 237x13 source. Test Plan: - bash original/tools/test_reconstructed_c.sh - python3 original/tools/audit_reconstruction.py --require-complete - cargo test --all-targets - cargo clippy --all-targets --all-features -- -D warnings - rumdl check original/C_RECONSTRUCTION_FINAL_AUDIT.md tdkpin-rs/CHANGELOG.md tdkpin-rs/RECONSTRUCTION.md tdkpin-rs/README.md - cargo run --quiet -- --simulate loading --at 0.55 --screenshot /tmp/tdkpin-loading-mid.png - git diff --check
279 lines
9.1 KiB
C
279 lines
9.1 KiB
C
/* TDKPIN asset-loading progress and bitmap ownership paths. */
|
|
|
|
#include "tdkpin_game_assets.h"
|
|
|
|
#include "tdkpin_palette.h"
|
|
#include "tdkpin_window_placement.h"
|
|
|
|
enum {
|
|
FRAME_MEMORY_DC = -0x0c,
|
|
FRAME_PREVIOUS_BITMAP = -0x04,
|
|
FRAME_LOADING_BITMAP = -0x02,
|
|
FRAME_DESTINATION_DC = 0x0e,
|
|
LOADING_DESTINATION_X = 202,
|
|
LOADING_DESTINATION_Y = 328,
|
|
LOADING_HEIGHT = 13,
|
|
WIN16_SRCCOPY = 0x00cc0020,
|
|
DGROUP_OVERLAY_BITMAP = 0x0867,
|
|
WINDOW_HANDLE_OFFSET = 4,
|
|
OVERLAY_DAT_RESOURCE = 993,
|
|
DGROUP_PALETTE_UPDATE_ACTIVE = 0x07c4,
|
|
MESSAGE_WPARAM_OFFSET = 4,
|
|
MESSAGE_RESULT_OFFSET = 10,
|
|
DGROUP_PALETTE_HANDLE = 0x444e,
|
|
DGROUP_BOARD_INDEX = 0x07cd,
|
|
BOARD_DAT_RESOURCE_BASE = 1000,
|
|
WIN16_IDC_WAIT = 0x7f02,
|
|
WIN16_IDC_ARROW = 0x7f00,
|
|
BOARD_WIDTH = 640,
|
|
BOARD_HEIGHT = 460,
|
|
};
|
|
|
|
static Win16FarPtr frame_field(Win16FarPtr caller_bp, int16_t offset)
|
|
{
|
|
return win16_far_add_offset(caller_bp, (uint16_t)offset);
|
|
}
|
|
|
|
/*
|
|
* 1000:531e — draw one numbered asset-loading stage. The target receives the
|
|
* caller's BP, mutates its scratch HDC/HGDIOBJ words, and deletes the compatible
|
|
* DC through DeleteObject16 (not DeleteDC16). This unusual API choice is kept.
|
|
*/
|
|
void tdkpin_draw_loading_stage(Win16FarPtr caller_bp, uint8_t stage)
|
|
{
|
|
HDC16 destination =
|
|
win16_read_u16(frame_field(caller_bp, FRAME_DESTINATION_DC));
|
|
HDC16 memory_dc = CreateCompatibleDC16(destination);
|
|
win16_write_u16(
|
|
frame_field(caller_bp, FRAME_MEMORY_DC), 0, memory_dc);
|
|
HBITMAP16 loading_bitmap =
|
|
win16_read_u16(frame_field(caller_bp, FRAME_LOADING_BITMAP));
|
|
HGDIOBJ16 previous = SelectObject16(memory_dc, loading_bitmap);
|
|
win16_write_u16(
|
|
frame_field(caller_bp, FRAME_PREVIOUS_BITMAP), 0, previous);
|
|
uint16_t wrapped_width = (uint16_t)(((uint16_t)stage - 1) * 7);
|
|
(void)BitBlt16(
|
|
destination,
|
|
LOADING_DESTINATION_X,
|
|
LOADING_DESTINATION_Y,
|
|
(INT16)wrapped_width,
|
|
LOADING_HEIGHT,
|
|
memory_dc,
|
|
0,
|
|
0,
|
|
WIN16_SRCCOPY);
|
|
(void)SelectObject16(memory_dc, previous);
|
|
(void)DeleteObject16(memory_dc);
|
|
}
|
|
|
|
/* 1000:5be5 — finish TWindow setup, load DAT 993, then focus the HWND. */
|
|
void tdkpin_setup_overlay_window(Win16FarPtr window)
|
|
{
|
|
object_windows_setup_window(window);
|
|
HBITMAP16 overlay = tdkpin_load_bitmap_resource(
|
|
win16_make_far_pointer(0, OVERLAY_DAT_RESOURCE), g_win16_instance);
|
|
win16_write_u16(
|
|
win16_dgroup_pointer(DGROUP_OVERLAY_BITMAP), 0, overlay);
|
|
(void)SetFocus16(
|
|
win16_read_u16(win16_far_add_offset(window, WINDOW_HANDLE_OFFSET)));
|
|
}
|
|
|
|
static void invalidate_palette_window(
|
|
Win16FarPtr window, Win16FarPtr message)
|
|
{
|
|
HWND16 handle =
|
|
win16_read_u16(win16_far_add_offset(window, WINDOW_HANDLE_OFFSET));
|
|
win16_write_u8(
|
|
win16_dgroup_pointer(DGROUP_PALETTE_UPDATE_ACTIVE), 1);
|
|
(void)InvalidateRect16(handle, 0, 0);
|
|
(void)UpdateWindow16(handle);
|
|
win16_write_u32(message, MESSAGE_RESULT_OFFSET, 0);
|
|
}
|
|
|
|
/*
|
|
* 1000:58e7 — reset on wParam zero; otherwise begin one non-iconic palette
|
|
* refresh only while the reentrancy byte is clear. Suppressed paths return the
|
|
* target's uninitialized local word.
|
|
*/
|
|
uint16_t tdkpin_palette_change_variant_a(
|
|
Win16FarPtr window, Win16FarPtr message)
|
|
{
|
|
uint16_t wparam =
|
|
win16_read_u16(win16_far_add_offset(message, MESSAGE_WPARAM_OFFSET));
|
|
if (wparam == 0) {
|
|
win16_write_u8(
|
|
win16_dgroup_pointer(DGROUP_PALETTE_UPDATE_ACTIVE), 0);
|
|
return 0;
|
|
}
|
|
if (win16_read_u8(
|
|
win16_dgroup_pointer(DGROUP_PALETTE_UPDATE_ACTIVE)) == 0) {
|
|
HWND16 handle = win16_read_u16(
|
|
win16_far_add_offset(window, WINDOW_HANDLE_OFFSET));
|
|
if (IsIconic16(handle) == 0) {
|
|
invalidate_palette_window(window, message);
|
|
return 0;
|
|
}
|
|
}
|
|
return win16_indeterminate_u16();
|
|
}
|
|
|
|
/* 1000:5956 — zero-wParam refresh variant without the IsIconic test. */
|
|
void tdkpin_palette_change_variant_b(
|
|
Win16FarPtr window, Win16FarPtr message)
|
|
{
|
|
uint16_t wparam =
|
|
win16_read_u16(win16_far_add_offset(message, MESSAGE_WPARAM_OFFSET));
|
|
if (wparam == 0 &&
|
|
win16_read_u8(
|
|
win16_dgroup_pointer(DGROUP_PALETTE_UPDATE_ACTIVE)) == 0) {
|
|
invalidate_palette_window(window, message);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 1000:59a0 — WM_PALETTECHANGED. Ignore notifications originating from this
|
|
* window or its child; otherwise realize the global palette as a background
|
|
* palette, update mapped colors, restore the previous palette, and consume it.
|
|
*/
|
|
static uint16_t wm_palette_changed_impl(
|
|
Win16FarPtr window, Win16FarPtr message)
|
|
{
|
|
HWND16 window_handle =
|
|
win16_read_u16(win16_far_add_offset(window, WINDOW_HANDLE_OFFSET));
|
|
HWND16 changed_window =
|
|
win16_read_u16(win16_far_add_offset(message, MESSAGE_WPARAM_OFFSET));
|
|
if (changed_window == window_handle ||
|
|
GetParent16(changed_window) == window_handle) {
|
|
return 1;
|
|
}
|
|
|
|
HDC16 dc = GetDC16(window_handle);
|
|
HPALETTE16 palette = win16_read_u16(
|
|
win16_dgroup_pointer(DGROUP_PALETTE_HANDLE));
|
|
HPALETTE16 previous = SelectPalette16(dc, palette, 1);
|
|
(void)RealizePalette16(dc);
|
|
(void)UpdateColors16(dc);
|
|
(void)SelectPalette16(dc, previous, 0);
|
|
(void)ReleaseDC16(window_handle, dc);
|
|
win16_write_u32(message, MESSAGE_RESULT_OFFSET, 0);
|
|
return 0;
|
|
}
|
|
|
|
uint16_t tdkpin_wm_palette_changed(
|
|
Win16FarPtr window, Win16FarPtr message)
|
|
{
|
|
return wm_palette_changed_impl(window, message);
|
|
}
|
|
|
|
uint16_t tdkpin_wm_palette_changed_variant_b(
|
|
Win16FarPtr window, Win16FarPtr message)
|
|
{
|
|
return wm_palette_changed_impl(window, message);
|
|
}
|
|
|
|
uint16_t tdkpin_wm_palette_changed_variant_c(
|
|
Win16FarPtr window, Win16FarPtr message)
|
|
{
|
|
return wm_palette_changed_impl(window, message);
|
|
}
|
|
|
|
/*
|
|
* 1000:5a37 — WM_QUERYNEWPALETTE. Return/publish the signed number of palette
|
|
* entries remapped and invalidate the whole window only when that count > 0.
|
|
*/
|
|
static int16_t wm_query_new_palette_impl(
|
|
Win16FarPtr window, Win16FarPtr message)
|
|
{
|
|
HWND16 window_handle =
|
|
win16_read_u16(win16_far_add_offset(window, WINDOW_HANDLE_OFFSET));
|
|
HDC16 dc = GetDC16(window_handle);
|
|
HPALETTE16 palette = win16_read_u16(
|
|
win16_dgroup_pointer(DGROUP_PALETTE_HANDLE));
|
|
HPALETTE16 previous = SelectPalette16(dc, palette, 0);
|
|
int16_t changed = (int16_t)RealizePalette16(dc);
|
|
(void)SelectPalette16(dc, previous, 0);
|
|
(void)ReleaseDC16(window_handle, dc);
|
|
if (changed > 0) {
|
|
(void)InvalidateRect16(window_handle, 0, 0);
|
|
}
|
|
win16_write_u32(message, MESSAGE_RESULT_OFFSET, (uint16_t)changed);
|
|
return changed;
|
|
}
|
|
|
|
int16_t tdkpin_wm_query_new_palette(
|
|
Win16FarPtr window, Win16FarPtr message)
|
|
{
|
|
return wm_query_new_palette_impl(window, message);
|
|
}
|
|
|
|
int16_t tdkpin_wm_query_new_palette_variant_b(
|
|
Win16FarPtr window, Win16FarPtr message)
|
|
{
|
|
return wm_query_new_palette_impl(window, message);
|
|
}
|
|
|
|
int16_t tdkpin_wm_query_new_palette_variant_c(
|
|
Win16FarPtr window, Win16FarPtr message)
|
|
{
|
|
return wm_query_new_palette_impl(window, message);
|
|
}
|
|
|
|
/* 1000:6129 — setup one board window and load DAT 1000+board_index. */
|
|
void tdkpin_setup_board_window(Win16FarPtr window)
|
|
{
|
|
object_windows_setup_window(window);
|
|
HCURSOR16 wait_cursor = LoadCursor16(
|
|
0, win16_make_far_pointer(0, WIN16_IDC_WAIT));
|
|
(void)SetCursor16(wait_cursor);
|
|
uint16_t board_index = win16_read_u16(
|
|
win16_dgroup_pointer(DGROUP_BOARD_INDEX));
|
|
uint16_t resource_id =
|
|
(uint16_t)(board_index + BOARD_DAT_RESOURCE_BASE);
|
|
HBITMAP16 bitmap = tdkpin_load_bitmap_resource(
|
|
win16_make_far_pointer(0, resource_id), g_win16_instance);
|
|
win16_write_u16(
|
|
win16_dgroup_pointer(DGROUP_OVERLAY_BITMAP), 0, bitmap);
|
|
(void)SetFocus16(
|
|
win16_read_u16(win16_far_add_offset(window, WINDOW_HANDLE_OFFSET)));
|
|
}
|
|
|
|
/*
|
|
* 1000:616e — draw the current 640x460 board DAT through a compatible DC,
|
|
* preserving the caller's palette/object selections and restoring Arrow.
|
|
*/
|
|
void tdkpin_draw_board_overlay(
|
|
Win16FarPtr ignored_object,
|
|
uint16_t ignored_1,
|
|
uint16_t ignored_2,
|
|
HDC16 destination)
|
|
{
|
|
(void)ignored_object;
|
|
(void)ignored_1;
|
|
(void)ignored_2;
|
|
HPALETTE16 palette = win16_read_u16(
|
|
win16_dgroup_pointer(DGROUP_PALETTE_HANDLE));
|
|
HPALETTE16 previous_palette = SelectPalette16(destination, palette, 0);
|
|
(void)UnrealizeObject16(palette);
|
|
(void)RealizePalette16(destination);
|
|
HDC16 source = CreateCompatibleDC16(destination);
|
|
HBITMAP16 bitmap = win16_read_u16(
|
|
win16_dgroup_pointer(DGROUP_OVERLAY_BITMAP));
|
|
HGDIOBJ16 previous_bitmap = SelectObject16(source, bitmap);
|
|
(void)BitBlt16(
|
|
destination,
|
|
0,
|
|
0,
|
|
BOARD_WIDTH,
|
|
BOARD_HEIGHT,
|
|
source,
|
|
0,
|
|
0,
|
|
WIN16_SRCCOPY);
|
|
(void)SelectObject16(source, previous_bitmap);
|
|
(void)DeleteDC16(source);
|
|
(void)SelectPalette16(destination, previous_palette, 0);
|
|
HCURSOR16 arrow = LoadCursor16(
|
|
0, win16_make_far_pointer(0, WIN16_IDC_ARROW));
|
|
(void)SetCursor16(arrow);
|
|
}
|