Files
tdkpin/original/reconstructed/tdkpin_game_assets.c
T
ddidderr c80f5e2136 fix(ui): restore staged loading progress
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
2026-08-23 19:03:09 +02:00

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);
}