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