Grim2D vtable evidence appendix¶
This appendix collects one concrete callsite snippet per vtable offset,
plus the current grim.dll entry signature and address from
analysis/ghidra/derived/grim2d_vtable_map.json.
Ghidra signatures include the implicit this pointer on vtable calls; the
Suggested signature lines omit it and add const qualifiers where evidence
supports them.
0x0 — grim_release @ 0x10005c80¶
- Ghidra signature:
void grim_release(void) - Call sites: 0 (unique funcs: 0)
- Sample calls: crimsonland_main:L21607 (base vtable call)
- First callsite: crimsonland_main (line 21607)
grim.dll body:
0x4 — grim_set_paused @ 0x10005c90¶
- Ghidra signature:
void grim_set_paused(int paused) - Notes: stores the paused flag in a byte-sized global (likely boolean)
- Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
0x8 — grim_get_version @ 0x10005ca0¶
- Ghidra signature:
float grim_get_version(void) - Notes: returns the hard-coded engine version constant
- Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
0xc — grim_save_screenshot @ 0x10005cb0¶
- Confirmed C++ signature:
bool grim_save_screenshot(char *path) - Previous Ghidra name/signature:
int grim_check_device(void) - Notes: exact-matched front-buffer capture; creates an
A8R8G8B8image surface, copies the front buffer, saves it as BMP, and releases the surface - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
IDirect3DSurface8 *surface = 0;
if (grim_d3d_device->CreateImageSurface(
grim_present_width, grim_present_height,
D3DFMT_A8R8G8B8, &surface) < 0)
return false;
if (grim_d3d_device->GetFrontBuffer(surface) < 0 ||
D3DXSaveSurfaceToFileA(path, 0, surface, 0, 0) < 0) {
surface->Release();
return false;
}
surface->Release();
return true;
0x10 — grim_apply_config @ 0x10005d40¶
- Confirmed C++ signature:
bool grim_apply_config(void) - Notes: creates the D3D8 interface and shows the config dialog
- Call sites: 1 (unique funcs: 1)
- Sample calls: crimsonland_main (
FUN_0042c450):L19443 - First callsite: crimsonland_main (
FUN_0042c450) (line 21580)
FUN_00401870(&DAT_0047eea0,(byte *)s____invoking_grim_config_00474aa0);
FUN_00402860(0x47eea0);
cVar1 = (**(code **)(*DAT_0048083c + 0x10))();
DAT_004aaf45 = 1;
if (cVar1 == '\0') {
grim.dll body:
DAT_1005b2c4 = (int *)Direct3DCreate8(0xdc);
DialogBoxParamA(DAT_1005bacc,(LPCSTR)0x74,(HWND)0x0,(DLGPROC)&LAB_10002120,0);
(**(code **)(*DAT_1005b2c4 + 8))(DAT_1005b2c4);
if (grim_config_dialog_canceled == '\0') {
(**(code **)(*in_ECX + 0x20))(0x54,DAT_1005d400);
The recovered method lazily loads icon resource 0x72, probes Direct3D8 and
device capabilities, runs dialog resource 0x74, and releases the temporary
interface. Direct3D creation failure records the native error, shows a Grim
message box, and returns false. An accepted dialog applies config IDs 0x54,
0x2b, 8, 0x34, 8, 0x29, and 0x2a in that order; both duplicated
writes are native. The method matches all 124 instructions and all 25
references under MSVC 6.5 /O2 /GB.
0x14 — grim_init_system @ 0x10005eb0¶
- Confirmed C++ signature:
bool grim_init_system(void) - Notes: initializes D3D + input devices and loads
smallFnt.dat - Call sites: 1 (unique funcs: 1)
- Sample calls: crimsonland_main (
FUN_0042c450):L19504 - First callsite: crimsonland_main (
FUN_0042c450) (line 21641)
FUN_00401870(&DAT_0047eea0,(byte *)s____using_joystick_00474998);
FUN_00401870(&DAT_0047eea0,(byte *)s____initiating_Grim_system_0047497c);
cVar1 = (**(code **)(*DAT_0048083c + 0x14))();
if (cVar1 == '\0') {
FUN_00401870(&DAT_0047eea0,(byte *)s_Critical_failure__00474968);
The recovered grim.dll method records the current directory, initializes D3D,
then conditionally initializes mouse, keyboard, and joystick input. Mouse or
keyboard failure tears down the partially initialized system and returns
false; joystick failure is nonfatal and disables that input path. The success
tail initializes timing, installs crimson.paq, and copies the optional
256-byte load\\smallFnt.dat glyph-width table.
The two config calls expose the original value-wrapper shape: implicit integer
conversion initializes record word 0, while implicit string conversion
initializes record word 3. VC6 constructs those 16-byte by-value arguments
directly on the outgoing stack. The recovered method matches all 93 native
instructions and all 32 references under MSVC 6.5 /O2 /GB.
0x18 — grim_shutdown @ 0x10005ff0¶
- Confirmed C++ signature:
void grim_shutdown(void) - Notes: exact-matched teardown of the optional lookup blob, mouse, keyboard, joystick, Direct3D resources, and window, in that order
- Call sites: 1 (unique funcs: 1)
- Sample calls: crimsonland_main (
FUN_0042c450):L19599 - First callsite: crimsonland_main (
FUN_0042c450) (line 21736)
FUN_0043d110();
FUN_00401870(&DAT_0047eea0,(byte *)s_Shutdown_Grim___00474848);
(**(code **)(*DAT_0048083c + 0x18))();
FUN_00402860(0x47eea0);
(**(code **)*DAT_0048083c)();
grim.dll body:
grim_lookup_blob_load(&grim_empty_string);
grim_mouse_shutdown();
grim_keyboard_shutdown();
grim_joystick_shutdown();
grim_d3d_shutdown();
grim_window_destroy();
0x1c — grim_apply_settings @ 0x10006020¶
- Confirmed C++ signature:
bool grim_apply_settings(void) - Previous Ghidra signature:
void grim_apply_settings(void) - Notes: exact-matched wrapper enters
grim_run_loop(0x10003c00) and returnstrueafter the loop exits; the sole call site ignores the result - Call sites: 1 (unique funcs: 1)
- Sample calls: crimsonland_main (
FUN_0042c450):L19581 - First callsite: crimsonland_main (
FUN_0042c450) (line 21718)
puVar9 = puVar10;
} while ((int)puVar10 < 0x4805c0);
(**(code **)(*DAT_0048083c + 0x1c))();
LVar11 = RegCreateKeyExA((HKEY)0x80000001,s_Software_10tons_entertainment_Cr_00474604,0,(LPSTR)0x0
,0,0xf003f,(LPSECURITY_ATTRIBUTES)0x0,&pHStack_5c4,(LPDWORD)0x0);
grim.dll body:
0x20 — grim_set_config_var @ 0x10006580¶
- Confirmed name:
set_config_var - Confirmed C++ signature:
void __thiscall IGrim2D::set_config_var(unsigned int id, grim_config_value_t value) - Previous Ghidra signature:
void grim_set_config_var(unsigned int id, unsigned int value) - Notes: config/state dispatcher; some IDs map to D3D render/texture stage state, while others update the table or consume pointer/callback fields.
- Call sites: 206 (unique funcs: 35)
- Sample calls: FUN_00401dd0:L754; FUN_00401dd0:L755; FUN_00401dd0:L847; FUN_00402d50:L1438; FUN_00402d50:L1460; demo_trial_overlay_render (
FUN_004047c0):L3147; ui_render_keybind_help:L3373; ui_render_keybind_help:L3377 - First callsite: FUN_00401dd0 (line 754)
(**(code **)(*DAT_0048083c + 0xd0))(&fStack_4c,DAT_00471140,0x40800000,&puStack_44);
(**(code **)(*DAT_0048083c + 0xf0))();
(**(code **)(*DAT_0048083c + 0x20))(0x15,2);
(**(code **)(*DAT_0048083c + 0x20))(0x18,0x3f000000);
(**(code **)(*DAT_0048083c + 0x114))
The vtable entry is exactly 0x10006580. Live Binary Ninja shows five stack
dwords (id plus the four record words) at every path and retn 0x14 on all
returns. A representative caller reserves 16 bytes, initializes only word 0,
pushes ID 0x15, and calls slot +0x20; the callee removes the entire 20-byte
argument block. Other recovered callers initialize all four words. This is
MSVC's ordinary by-value aggregate ABI, not a variadic two-word API.
0x24 — grim_get_config_var @ 0x10006c30¶
- Confirmed name:
get_config_var - Confirmed C++ signature:
grim_config_value_t __thiscall IGrim2D::get_config_var(int id) - ABI-level analyzer signature:
grim_config_value_t *grim_get_config_var(grim_config_value_t *out, int id) - Previous Ghidra signature:
void grim_get_config_var(unsigned int *out, int id) - Notes: grim.dll returns one 16-byte record from
grim_config_valuesfor IDs in0..127; all other IDs return the zero-filledgrim_config_default. - Call sites: 17 (unique funcs: 4)
- Sample calls: config_sync_from_grim (
FUN_0041ec60):L13402; config_sync_from_grim (FUN_0041ec60):L13410; config_sync_from_grim (FUN_0041ec60):L13413; config_sync_from_grim (FUN_0041ec60):L13415; config_sync_from_grim (FUN_0041ec60):L13417; config_sync_from_grim (FUN_0041ec60):L13419; crimsonland_main (FUN_0042c450):L19456; crimsonland_main (FUN_0042c450):L19458 - First callsite: config_sync_from_grim (
FUN_0041ec60) (line 15539)
acStack_4b0[0] = '|';
acStack_4b0[1] = -0x14;
acStack_4b0[2] = 'A';
acStack_4b0[3] = '\0';
puVar2 = (undefined1 *)(**(code **)(*DAT_0048083c + 0x24))();
DAT_0048050c = *puVar2;
puVar3 = (undefined4 *)(**(code **)(*DAT_0048083c + 0x24))(&stack0xfffffb60);
DAT_00480504 = *puVar3;
puVar3 = (undefined4 *)(**(code **)(*DAT_0048083c + 0x24))(&uStack_4a8,0x2a);
DAT_00480508 = *puVar3;
Live Binary Ninja resolves the apparent out argument as MSVC's hidden
structure-return buffer: callers push the ID and a 16-byte destination, the
method returns with retn 8, and callers immediately read through the pointer
left in EAX. A natural by-value return compiles with VC6.5 to all 32 native
instructions and all five masked data references, including the four
base-plus-offset reads from the fallback record.
0x28 — grim_get_error_text @ 0x10006ca0¶
- Ghidra signature:
char * grim_get_error_text(void) - Suggested signature:
const char * grim_get_error_text(void) - Call sites: 1 (unique funcs: 1)
- Sample calls: crimsonland_main (
FUN_0042c450):L19509 - First callsite: crimsonland_main (
FUN_0042c450) (line 21646)
uType = 0;
pcVar6 = s_Crimsonland__00474958;
lpText = (LPCSTR)(**(code **)(*DAT_0048083c + 0x28))();
MessageBoxA((HWND)0x0,lpText,pcVar6,uType);
(**(code **)*DAT_0048083c)();
grim.dll body:
0x2c — grim_clear_color @ 0x10006cb0¶
- Confirmed C++ signature:
void grim_clear_color(float r, float g, float b, float a) - Notes: exact-matched
IDirect3DDevice8::Clearcall guarded by render-disabled and device-ready flags; usesD3DCOLOR_COLORVALUE(r, g, b, a) - Call sites: 5 (unique funcs: 3)
- Sample calls: FUN_00417b80:L9215; terrain_generate_random:L9452; crimsonland_main (
FUN_0042c450):L19534; crimsonland_main (FUN_0042c450):L19538; crimsonland_main (FUN_0042c450):L19547 - First callsite: terrain_generate (
FUN_00417b80) (line 11352)
fStack_98 = 0.24705882;
uStack_9c = 0x417c89;
(**(code **)(*DAT_0048083c + 0x2c))();
iVar3 = iStack_70;
uStack_9c = 0;
grim.dll body:
if (!grim_render_disabled && grim_device_ready) {
grim_d3d_device->Clear(
0, 0, D3DCLEAR_TARGET,
D3DCOLOR_COLORVALUE(r, g, b, a), 0.0f, 0);
}
0x30 — grim_set_render_target @ 0x10006d50¶
- Recovered signature:
bool grim_set_render_target(int target_index) - Notes: called with
-1to restore the backbuffer - Call sites: 6 (unique funcs: 3)
- Sample calls: FUN_00417b80:L9209; FUN_00417b80:L9333; terrain_generate_random:L9446; terrain_generate_random:L9563; fx_queue_render (
FUN_00427920):L17949; fx_queue_render (FUN_00427920):L18035 - First callsite: terrain_generate (
FUN_00417b80) (line 11346)
fStack_88 = DAT_0048f530;
iStack_8c = 0x417c6a;
(**(code **)(*DAT_0048083c + 0x30))();
iStack_8c = 0x3f800000;
uStack_90 = 0x3dc8c8c9;
The recovered grim.dll method caches and restores the backbuffer around texture-backed render targets:
if (target_index < 0) {
// Release the active texture surface and restore the cached backbuffer.
} else {
// Cache GetRenderTarget(), acquire texture mip level zero, then call
// SetRenderTarget().
}
The native failure paths only clear AL, confirming the bool return ABI.
The full recovered method matches all 89 native instructions and all 19
references under MSVC 6.5 /O2 /GB.
0x34 — grim_get_time_ms @ 0x10006e40¶
- Ghidra signature:
int grim_get_time_ms(void) - Notes: returns the global millisecond counter
- Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
0x38 — grim_set_time_ms @ 0x10006e50¶
- Ghidra signature:
void grim_set_time_ms(int ms) - Notes: overwrites the global millisecond counter
- Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
0x3c — grim_get_frame_dt @ 0x10006e60¶
- Ghidra signature:
float grim_get_frame_dt(void) - Notes: clamps the frame delta to
0.1 - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
0x40 — grim_get_fps @ 0x10006e90¶
- Ghidra signature:
float grim_get_fps(void) - Notes: returns the cached FPS estimate
- Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
0x44 — grim_is_key_down @ 0x10007320¶
- Confirmed name:
is_key_down - Confirmed C++ signature:
unsigned char __thiscall IGrim2D::is_key_down(unsigned int key) - Previous Ghidra signature:
int grim_is_key_down(unsigned int key) - Notes: byte-sized 0/1 predicate; the underlying helper aliases keys by their low byte before reading the 256-byte keyboard state.
- Call sites: 6 (unique funcs: 2)
- Sample calls: console_update (
FUN_00401a40):L509; console_update (FUN_00401a40):L511; console_update (FUN_00401a40):L526; console_update (FUN_00401a40):L528; ui_focus_update (FUN_0043d830):L26638; ui_focus_update (FUN_0043d830):L26639 - First callsite: console_update (
FUN_00401a40) (line 509)
(float10)*(int *)((int)param_1 + 0x18));
FUN_00401060();
cVar2 = (**(code **)(*DAT_0048083c + 0x44))(0x1d);
if (cVar2 == '\0') {
cVar2 = (**(code **)(*DAT_0048083c + 0x44))(0x9d);
Live callers load ECX=this, push one key, call vtable slot +0x44, and test
only AL. The callee forwards to grim_keyboard_key_down, ignores this, and
uses retn 4. Modeling both functions with byte returns compiles to all five
native instructions, full prefix, and masked references 1/0/0; declaring the
wrapper as int or native C++ bool adds a widening/normalization sequence.
0x48 — grim_was_key_pressed @ 0x10007390¶
- Confirmed name:
was_key_pressed - Confirmed C++ signature:
bool __thiscall IGrim2D::was_key_pressed(unsigned int key) - Previous Ghidra signature:
int grim_was_key_pressed(unsigned int key) - Notes: press/repeat query with a 0.5-second initial delay and 0.1-second held-repeat interval.
- Call sites: 39 (unique funcs: 16)
- Sample calls: console_update (
FUN_00401a40):L514; console_update (FUN_00401a40):L522; console_update (FUN_00401a40):L531; console_update (FUN_00401a40):L543; console_update (FUN_00401a40):L547; console_update (FUN_00401a40):L551; console_update (FUN_00401a40):L574; console_update (FUN_00401a40):L578 - First callsite: console_update (
FUN_00401a40) (line 514)
if (cVar2 != '\0') goto LAB_00401ac4;
LAB_00401add:
cVar2 = (**(code **)(*DAT_0048083c + 0x48))(200);
if (cVar2 != '\0') {
*(int *)((int)param_1 + 0x14) = *(int *)((int)param_1 + 0x14) + 1;
Live Binary Ninja shows that the full key value is passed to
grim_keyboard_key_down, while all repeat arrays index (unsigned char)key.
On a down key whose timer is zero, the function reloads the timer from the
global 0.5f delay. Subsequent repeats multiply that delay by 0.2f, giving
0.1f; releasing the key resets its timer to zero and first-press latch to 1.
The timers are decremented and clamped elsewhere in the keyboard poll.
The recovered virtual-bool member compiles with MSVC 6.5 to all 31 native
instructions, full prefix, and masked references 10/0/0. Callers supply
ECX=this, push one key, and test only AL, confirming the member ABI and
byte-sized result.
0x4c — grim_flush_input @ 0x10007330¶
- Confirmed name:
flush_input - Confirmed C++ signature:
void __thiscall IGrim2D::flush_input(void) - Ghidra signature:
void grim_flush_input(void) - Notes: clears the 256-byte keyboard state, drains queued DirectInput events, clears the state again, and empties the key-char FIFO.
- Call sites: 12 (unique funcs: 10)
- Sample calls: console_set_open (
FUN_004018b0):L346; quest_mode_update:L4357; tutorial_prompt_dialog (FUN_00408530):L5083; tutorial_prompt_dialog (FUN_00408530):L5104; gameplay_update_and_render:L5879; game_over_screen_update:L7055; quest_failed_screen_update:L7326; quest_results_screen_update (FUN_00410d20):L7702 - First callsite: console_set_open (
FUN_004018b0) (line 346)
*(undefined1 *)((int)this + 0x28) = param_1;
DAT_0047f4d4 = param_1;
(**(code **)(*DAT_0048083c + 0x4c))();
return;
}
Live Binary Ninja identifies a 91-byte function at the vtable's +0x4c slot.
It zeroes grim_keyboard_state[256], initializes an event count to 10, and
calls the keyboard device's GetDeviceData slot with 20-byte events. The call
is repeated while the returned unsigned count is nonzero, with a 101-call
safety cap. The function then zeroes the keyboard state again and clears
grim_key_char_queue_count.
The recovered C++ source compiles with MSVC 6.5 to all 34 native instructions,
with full prefix and masked references 5/0/0. The five references resolve to
the keyboard device, event buffer, keyboard state (twice), and key-char count.
0x50 — grim_get_key_char @ 0x10005c40¶
- Confirmed name:
get_key_char - Confirmed C++ signature:
int __thiscall IGrim2D::get_key_char(void) - Ghidra signature:
int grim_get_key_char(void) - Notes: pops the oldest entry from the internal eight-int key-character FIFO, shifts the remaining entries down, and returns zero when empty.
- Call sites: 1 (unique funcs: 1)
- Sample calls: console_input_poll (
FUN_00401060):L33 - First callsite: console_input_poll (
FUN_00401060) (line 33)
int iVar1;
iVar1 = (**(code **)(*DAT_0048083c + 0x50))();
if (DAT_0047f4d4 != '\0') {
if ((iVar1 != 0) && (DAT_0047ea58 == '\0')) {
Live Binary Ninja shows grim_key_char_queue[8] at 0x1005d3c4 and its count
at 0x1005d3e4. The enqueue path caps the count below 7, leaving the eighth
slot available to the getter's shift. The recovered snapshot-and-do/while
source compiles to all 22 native instructions, full prefix, and references
4/0/0.
0x54 — grim_set_key_char_buffer @ 0x10005c20¶
- Confirmed name:
set_key_char_buffer - Confirmed C++ signature:
void __thiscall IGrim2D::set_key_char_buffer(unsigned char *buffer, int *count, int size) - Ghidra signature:
void grim_set_key_char_buffer(unsigned char *buffer, int *count, int size) - Notes: installs a caller-owned text buffer, its count pointer, and capacity; native code performs no null or size validation here.
- Call sites: 2 (unique funcs: 2)
- Sample calls: crimsonland_main (
FUN_0042c450):L19559; ui_text_input_update (FUN_0043ecf0):L27394 - First callsite: crimsonland_main (
FUN_0042c450) (line 21696)
puVar9 = (undefined4 *)((int)puVar9 + 1);
}
(**(code **)(*DAT_0048083c + 0x54))(&DAT_004aacd8,&DAT_004aaedc,uVar14);
FUN_0041ec60();
puVar13 = &DAT_00490be0;
The three arguments are stored verbatim at 0x10053048, 0x1005304c, and
0x10053050, immediately after the unrelated key-repeat delay. Three ordinary
global assignments compile to all seven native instructions, full prefix, and
references 3/0/0; the member ignores this and removes 12 argument bytes.
0x58 — grim_is_mouse_button_down @ 0x10007410¶
- Confirmed name:
is_mouse_button_down - Confirmed C++ signature:
unsigned char __thiscall IGrim2D::is_mouse_button_down(int button) - Previous Ghidra signature:
int grim_is_mouse_button_down(int button) - Notes: button 0 is observed; cached mode returns the saved byte directly,
otherwise the helper extracts bit 7 from
DIMOUSESTATE2.rgbButtons[button]. - Call sites: 4 (unique funcs: 3)
- Sample calls: gameplay_update_and_render:L6349; input_primary_just_pressed (
FUN_00446030):L31421; input_primary_just_pressed (FUN_00446030):L31439; input_primary_is_down (FUN_004460f0):L31467 - First callsite: gameplay_update_and_render (line 6349)
grim.dll body:
if (grim_input_cached != '\0') {
return CONCAT31((int3)((uint)button >> 8),(&DAT_1005a044)[button]);
}
bVar1 = FUN_1000a590(button);
return CONCAT31(extraout_var,bVar1);
The recovered byte-predicate member and its raw DirectInput helper both compile
exactly with MSVC 6.5. The member matches 11/11 instructions with references
3/0/0; the helper at 0x1000a590 matches 4/4 with references 1/0/0.
0x5c — grim_was_mouse_button_pressed @ 0x10007440¶
- Confirmed name:
was_mouse_button_pressed - Confirmed C++ signature:
bool __thiscall IGrim2D::was_mouse_button_pressed(int button) - Previous Ghidra signature:
int grim_was_mouse_button_pressed(void *this, int button) - Notes: edge-triggered mouse button query; the per-button latch is true only while the button is released.
- Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
cVar1 = (**(code **)(*(int *)this + 0x58))(button);
if ((cVar1 == '\0') || ((&grim_mouse_button_latch)[button] == '\0')) {
uVar4 = 0;
}
else {
uVar4 = 1;
}
uVar2 = (**(code **)(*(int *)this + 0x58))(button);
(&grim_mouse_button_latch)[button] = (char)uVar2 == '\0';
return CONCAT31((int3)((uint)uVar2 >> 8),uVar4);
The source performs the down query twice by design: the first value is ANDed
with the previous release latch to produce the result, and the second is
negated into the next latch state. Cached mode applies the same short-circuit
logic directly to grim_mouse_button_cache. This natural source compiles to
all 51 native instructions, full prefix, and masked references 7/0/0.
0x60 — grim_get_mouse_wheel_delta @ 0x10007560¶
- Provisional name:
get_mouse_wheel_delta(high) - Guess:
float get_mouse_wheel_delta(void) - Notes: positive/negative scroll used to change selection
- Ghidra signature:
float grim_get_mouse_wheel_delta(void) - Call sites: 2 (unique funcs: 1)
- Sample calls: ui_scrollbar_update (
FUN_0043def0):L26948; ui_scrollbar_update (FUN_0043def0):L26952 - First callsite: ui_scrollbar_update (
FUN_0043def0) (line 29084)
(**(code **)(*DAT_0048083c + 0xd0))(&stack0xffffffb0,0x3f800000,fVar1,&local_30);
}
fVar8 = (float10)(**(code **)(*DAT_0048083c + 0x60))();
if ((float10)0.0 < fVar8) {
*param_2 = *param_2 - 1.0;
0x64 — grim_set_mouse_pos @ 0x10007530¶
- Provisional name:
set_mouse_pos(high) - Guess:
void set_mouse_pos(float x, float y) - Ghidra signature:
void grim_set_mouse_pos(float x, float y) - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
0x68 — grim_get_mouse_x @ 0x10007510¶
- Provisional name:
get_mouse_x(high) - Guess:
float get_mouse_x(void) - Ghidra signature:
float grim_get_mouse_x(void) - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
0x6c — grim_get_mouse_y @ 0x10007520¶
- Provisional name:
get_mouse_y(high) - Guess:
float get_mouse_y(void) - Ghidra signature:
float grim_get_mouse_y(void) - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
0x70 — grim_get_mouse_dx @ 0x100074d0¶
- Provisional name:
get_mouse_dx(high) - Guess:
float get_mouse_dx(void) - Ghidra signature:
float grim_get_mouse_dx(void) - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
0x74 — grim_get_mouse_dy @ 0x100074e0¶
- Provisional name:
get_mouse_dy(high) - Guess:
float get_mouse_dy(void) - Ghidra signature:
float grim_get_mouse_dy(void) - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
0x78 — grim_get_mouse_dx_indexed @ 0x100074f0¶
- Provisional name:
get_mouse_dx_indexed(high) - Guess:
float get_mouse_dx_indexed(int index) - Ghidra signature:
float grim_get_mouse_dx_indexed(int index) - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
0x7c — grim_get_mouse_dy_indexed @ 0x10007500¶
- Provisional name:
get_mouse_dy_indexed(high) - Guess:
float get_mouse_dy_indexed(int index) - Ghidra signature:
float grim_get_mouse_dy_indexed(int index) - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
Live Binary Ninja and the VC6.5 matcher confirm the full mouse motion/position
cluster. grim_get_mouse_dx, grim_get_mouse_dy, and grim_get_mouse_y are
two-instruction x87 global getters with references 1/0/0; grim_set_mouse_pos
writes the supplied X/Y bit patterns to both accumulated and cached globals in
9/9 instructions with references 4/0/0. The indexed methods intentionally
ignore their stack argument and dispatch through this slots 0x70 and
0x74; each natural forwarding method matches all 3 instructions and ends in
retn 4.
grim.dll — joystick direction helpers @ 0x10006ea0..0x10006fd9¶
- Confirmed names:
grim_joystick_up_active,grim_joystick_down_active,grim_joystick_left_active, andgrim_joystick_right_active - Confirmed signatures:
int helper(void)
Each helper snapshots the configured deadzone and the corresponding axis
center before reading X (0x98) or Y (0x9c) through the Grim2D interface.
The negative helpers test axis - center < -deadzone; the positive helpers
test axis - center > deadzone, so equality remains inactive. Natural VC6.5
source reproduces all 88 instructions across the four functions, with full
prefixes and aggregate references 12/0/0.
0x80 — grim_is_key_active @ 0x10006fe0¶
- Confirmed name:
grim_is_key_active - Recovered signature:
unsigned char grim_is_key_active(int key) - Ghidra signature:
int grim_is_key_active(int key)(stale width) - Call sites: 6 (unique funcs: 4)
- Sample calls: gameplay_update_and_render:L5929; input_any_key_pressed (
FUN_00446000):L29232; input_primary_just_pressed (FUN_00446030):L29266; input_primary_just_pressed (FUN_00446030):L29282; input_primary_is_down (FUN_004460f0):L29308; input_primary_is_down (FUN_004460f0):L29310 - First callsite: tutorial_timeline_update (
FUN_00408990) (line 5277)
if (DAT_00486fd8 == 1) {
puVar6 = &DAT_00490be0;
while ((((cVar2 = (**(code **)(*DAT_0048083c + 0x80))(puVar6[-1]), cVar2 == '\0' &&
(cVar2 = (**(code **)(*DAT_0048083c + 0x80))(*puVar6), cVar2 == '\0')) &&
((cVar2 = (**(code **)(*DAT_0048083c + 0x80))(puVar6[1]), cVar2 == '\0' &&
The live grim.dll body and its natural VC6.5 WIP recover the complete routing:
key <= 0xff -> raw keyboard query
0x100..0x104 -> mouse buttons 0..4
0x11f..0x12a -> joystick buttons 0..11
0x131..0x134 -> up/down/left/right deadzone helpers
0x13f,0x140,0x141,
0x153,0x154,0x155 -> fabs(axis * 0.001f) > 0.5
0x16d..0x17b -> provider(player 0..2, action 0..4)
all other IDs -> 0
The byte return is visible at direct forwarding exits, which preserve al
without widening. The current source is deliberately recorded as WIP:
174/175 candidate/target instructions, 73.93% normalized match, 2/175 prefix,
and references 7/0/1. Its remaining debt is register allocation and the
placement of the shared axis-threshold tail. The lone conflicting masked
reference is the resulting alignment of the candidate Rz load with the target
X load; all six axis globals are present, and no semantic branch is unresolved.
0x84 — grim_get_config_float @ 0x100071b0¶
- Provisional name:
get_config_float(high) - Guess:
float get_config_float(int id) - Notes: IDs 0x13f..0x155
- Ghidra signature:
float grim_get_config_float(int id) - Suggested signature:
float grim_get_config_float(int id)(special-cases0x15fto returngrim_get_mouse_dx) - Call sites: 6 (unique funcs: 1)
- Sample calls: FUN_00448b50:L30229; FUN_00448b50:L30233; FUN_00448b50:L30237; FUN_00448b50:L30241; FUN_00448b50:L30245; FUN_00448b50:L30249
- First callsite: player_update (
FUN_004136b0) (line 9703)
}
if (iVar10 == 3) {
fVar19 = (float10)(**(code **)(*DAT_0048083c + 0x84))((&DAT_00490c0c)[iVar6 * 0xd8]);
pfVar12 = (float *)(&DAT_00490c08)[iVar6 * 0xd8];
fVar20 = (float10)(**(code **)(*DAT_0048083c + 0x84))();
grim.dll mapping:
if (id == 0x13f) {
return (float)DAT_1005d830 * 0.001;
}
if (id == 0x140) {
return (float)grim_joystick_axis_y * 0.001;
}
if (id == 0x15f) {
fVar3 = (float10)(**(code **)(*in_ECX + 0x70))();
return (float)fVar3;
}
Live Binary Ninja and the VC6.5 matcher confirm the complete router. Six
DIJOYSTATE2 axes (0x13f, 0x140, 0x141, 0x153, 0x154, 0x155)
are scaled by 0.001f; 0x15f/0x160 return direct mouse X/Y delta; and a
three-iteration loop maps 0x163..0x165 and 0x168..0x16a to the indexed
delta compatibility methods. All other IDs return 0.0f. Natural C++ matches
all 88 instructions, full prefix, and references 13/0/0.
0x88 — grim_get_slot_float @ 0x100072c0¶
- Notes: returns a float slot from the engine scratch array
- Ghidra signature:
float grim_get_slot_float(int index) - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
0x8c — grim_get_slot_int @ 0x100072d0¶
- Notes: returns an int slot from the engine scratch array
- Ghidra signature:
int grim_get_slot_int(int index) - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
0x90 — grim_set_slot_float @ 0x100072e0¶
- Notes: writes a float slot in the engine scratch array
- Ghidra signature:
void grim_set_slot_float(int index, float value) - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
0x94 — grim_set_slot_int @ 0x10007300¶
- Notes: writes an int slot in the engine scratch array
- Ghidra signature:
void grim_set_slot_int(int index, int value) - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
Live Binary Ninja names the two backing arrays grim_slot_ints and
grim_slot_floats. Natural unchecked array access reproduces all 14 VC6.5
instructions across the four getters/setters, with full prefixes and aggregate
references 4/0/0; there is no bounds check or conversion beyond the x87
float load/store required by the ABI.
0x98 — grim_get_joystick_x @ 0x10007580¶
- Notes: returns cached joystick axis X
- Ghidra signature:
int grim_get_joystick_x(void) - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
0x9c — grim_get_joystick_y @ 0x10007590¶
- Notes: returns cached joystick axis Y
- Ghidra signature:
int grim_get_joystick_y(void) - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
0xa0 — grim_get_joystick_z @ 0x100075a0¶
- Notes: returns cached joystick axis Z
- Ghidra signature:
int grim_get_joystick_z(void) - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
0xa4 — grim_get_joystick_pov @ 0x100075b0¶
- Ghidra signature:
int grim_get_joystick_pov(int index) - Call sites: 2 (unique funcs: 2)
- Sample calls: input_aim_pov_left_active; input_aim_pov_right_active
- First callsite: input_aim_pov_left_active
0xa8 — grim_is_joystick_button_down @ 0x100075c0¶
- Previous Ghidra signature:
int grim_is_joystick_button_down(int button) - Confirmed signature:
unsigned char grim_is_joystick_button_down(int button) - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
Live Binary Ninja identifies the callee as the 19-byte
grim_joystick_button_down: it masks the argument to its low byte, reads
DIJOYSTATE2.rgbButtons[index], and shifts bit 7 into AL. The natural helper
and public wrapper each match all 5 native instructions with references
1/0/0. The absence of widening in the wrapper confirms a byte return rather
than the earlier provisional bool/int declaration. The X/Y/Z accessors each
match 2/2 instructions, and the unchecked POV array accessor matches 3/3.
0xac — grim_create_texture @ 0x100075d0¶
- Confirmed name:
create_texture - Confirmed C++ signature:
bool create_texture(char *name, int width, int height) - Notes: used for terrain texture
- Previous Ghidra signature:
int grim_create_texture(char *name, int width, int height) - Call sites: 2 (unique funcs: 1)
- Sample calls: init_audio_and_terrain:L21242; init_audio_and_terrain:L21250
- First callsite: init_audio_and_terrain (
FUN_0042a9f0) (line 21221)
if (DAT_004871c8 == '\0') {
lVar5 = __ftol();
cVar3 = (**(code **)(*DAT_0048083c + 0xac))(s_ground_004740c4,(int)lVar5,(int)lVar5);
fVar2 = DAT_004803b8;
if (cVar3 == '\0') {
grim.dll body:
uVar1 = grim_find_free_texture_slot();
uVar2 = (**(code **)(*DAT_10059dbc + 0x50))(DAT_10059dbc,width,height,1,1,DAT_1005a488,0,local_10);
pvVar3 = operator_new(0x18);
pvVar3 = grim_texture_init(pvVar3,unaff_EDI,(char *)name_00);
(&DAT_1005d404)[uVar1] = pvVar3;
The recovered VC6.5 source matches all 81 native instructions (257 bytes;
references 13/0/0). It scans for a free slot, creates a one-level managed
texture, allocates and initializes the 24-byte texture entry, stores it in the
slot table, and returns a byte-sized success value. Allocation failures unwind
the partially constructed entry through the compiler-generated cleanup path.
0xb0 — grim_recreate_texture @ 0x10007790¶
- Confirmed name:
recreate_texture - Confirmed C++ signature:
bool recreate_texture(int handle) - Previous Ghidra signature:
int grim_recreate_texture(int handle) - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
Recovered source shape:
bool IGrim2D_cpp::grim_recreate_texture(int handle)
{
if (grim_texture_slots[handle] == 0) {
return false;
}
IDirect3DTexture8 *replacement;
if (D3DXCreateTexture(
grim_d3d_device,
grim_texture_slots[handle]->width,
grim_texture_slots[handle]->height,
1,
0,
grim_preferred_texture_format,
D3DPOOL_MANAGED,
&replacement) < 0) {
return false;
}
if (d3dx_copy_texture_filtered(
replacement,
grim_texture_slots[handle]->texture,
0,
0,
0x10,
1.0f) < 0) {
replacement->Release();
return false;
}
grim_texture_slots[handle]->texture->Release();
grim_texture_slots[handle]->texture = replacement;
return true;
}
This natural VC6.5 source matches all 57 native instructions (157 bytes;
references 8/0/0). The six-argument copy target is a linked internal D3DX8
resampler rather than a public load API: its body validates copy/filter flags
and consumes the final argument as an x87 scale. The old texture is released
and replaced only after a successful copy; the temporary replacement is
released on failure.
0xb4 — grim_load_texture @ 0x100076e0¶
- Confirmed name:
load_texture - Confirmed C++ signature:
bool load_texture(char *name, char *path) - Notes: name + filename
- Previous Ghidra signature:
int grim_load_texture(char *name, char *path) - Call sites: 3 (unique funcs: 3)
- Sample calls: ui_element_load:L10132; texture_get_or_load (
FUN_0042a670):L18970; texture_get_or_load_alt (FUN_0042a700):L18996 - First callsite: ui_element_load (line 12269)
FUN_00401870(&DAT_0047eea0,(byte *)s_Loading_uiElement__s_004737b4);
}
(**(code **)(*DAT_0048083c + 0xb4))(local_100,param_2);
iVar2 = (**(code **)(*DAT_0048083c + 0xc0))(&stack0xfffffef8);
*(int *)(iStack_8 + 0xe0) = iVar2;
The public method is an exact 7-instruction, 21-byte wrapper around
grim_load_texture_internal (references 1/0/0). Its return remains in AL,
confirming the byte-sized C++ bool ABI.
0xb8 — grim_save_texture @ 0x10007750¶
- Confirmed name:
save_texture - Confirmed C++ signature:
bool __thiscall IGrim2D::save_texture(int handle, char *path) - Previous Ghidra signature:
int grim_validate_texture(int handle) - Notes: live disassembly reads both stack arguments and returns with
retn 0x8. For a populated handle it calls the statically linkedD3DXSaveTextureToFileA(path, 2, texture, NULL); D3DX image format2is TGA. The old one-argument validation interpretation lost the path argument and misidentified the operation. - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
texture = grim_texture_slots[handle];
if (texture == NULL)
return false;
return D3DXSaveTextureToFileA(path, 2, texture->texture, NULL) >= 0;
0xbc — grim_destroy_texture @ 0x10007700¶
- Notes: releases the texture, clears the slot, and adjusts the max handle
- Ghidra signature:
void grim_destroy_texture(int handle) - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
texture = (void *)(&DAT_1005d404)[handle];
if (texture != (void *)0x0) {
grim_texture_release(texture);
operator_delete(texture);
(&DAT_1005d404)[handle] = 0;
}
0xc0 — grim_get_texture_handle @ 0x10007740¶
- Provisional name:
get_texture_handle(high) - Guess:
int get_texture_handle(const char *name) - Notes: returns -1 on missing
- Ghidra signature:
int grim_get_texture_handle(char *name) - Suggested signature:
int grim_get_texture_handle(const char *name) - Call sites: 22 (unique funcs: 8)
- Sample calls: demo_purchase_screen_update (
FUN_0040b740):L6362; demo_purchase_screen_update (FUN_0040b740):L6374; gameplay_reset_state (FUN_00412dc0):L8687; gameplay_reset_state (FUN_00412dc0):L8698; gameplay_reset_state (FUN_00412dc0):L8710; gameplay_reset_state (FUN_00412dc0):L8722; gameplay_reset_state (FUN_00412dc0):L8734; gameplay_reset_state (FUN_00412dc0):L8750 - First callsite: demo_purchase_screen_update (
FUN_0040b740) (line 6749)
pcStack_f4 = s_mockup_00472964;
pcStack_f8 = (char *)0x40bb08;
pcStack_f8 = (char *)(**(code **)(*DAT_0048083c + 0xc0))();
fStack_fc = 3.58732e-43;
iStack_104 = DAT_00480508 / 2 + -0x8c;
0xc4 — grim_bind_texture @ 0x10007830¶
- Confirmed name:
bind_texture - Confirmed C++ signature:
void __thiscall IGrim2D::bind_texture(int handle, int stage) - Notes: negative handles, empty slots, and slots with a null texture pointer
return without touching D3D state. A valid slot supplies its
+4texture field toIDirect3DDevice8::SetTexture(stage, texture), then becomes the current bound handle. Live disassembly calls D3D vtable slot0xf4and returns withretn 0x8. - Ghidra signature:
void grim_bind_texture(int handle, int stage) - Call sites: 66 (unique funcs: 22)
- Sample calls: ui_draw_clock_gauge:L3882; ui_draw_clock_gauge:L3891; ui_render_aim_indicators:L5641; ui_render_aim_indicators:L5663; ui_draw_textured_quad:L9120; terrain_generate (
FUN_00417b80):L9220; terrain_generate (FUN_00417b80):L9265; terrain_generate (FUN_00417b80):L9296 - First callsite: ui_draw_clock_gauge (line 3882)
- Runtime evidence: the checked-in
ui_render_trace.jsonlcontains 44,536grim_bind_textureevents.
iVar2 = 1;
(**(code **)(*DAT_0048083c + 0x20))(0x15,1);
(**(code **)(*DAT_0048083c + 0xc4))(DAT_0048f7c8,0);
iVar1 = 0;
(**(code **)(*DAT_0048083c + 0x100))(0,0,0x3f800000,0x3f800000);
grim.dll body:
if (((-1 < handle) && ((&DAT_1005d404)[handle] != 0)) &&
(iVar1 = *(int *)((&DAT_1005d404)[handle] + 4), iVar1 != 0)) {
(**(code **)(*DAT_10059dbc + 0xf4))(DAT_10059dbc,stage,iVar1);
_DAT_10053060 = handle;
}
The recovered VC6.5 source matches all 20 instructions and all 3 masked
references. A minimal two-field texture-slot view recovers the validated
+4 texture pointer without inventing the rest of the resource layout.
0xc8 — grim_draw_fullscreen_quad @ 0x10007870¶
- Confirmed name:
draw_fullscreen_quad - Confirmed C++ signature:
void __thiscall IGrim2D::draw_fullscreen_quad(int unused) - Ghidra signature (missing unused argument):
void grim_draw_fullscreen_quad(void) - Notes: the sole EXE call pushes zero, and live Binary Ninja shows
retn 0x4, proving one unused stack argument. The method clears rotation, batches one current-texture quad from the origin to the unsigned-at-use backbuffer dimensions, and ends the batch. - Call sites: 1 (unique funcs: 1)
- Sample calls: terrain_render (
FUN_004188a0):L11783 - First callsite: terrain_render (
FUN_004188a0) (line 11783) - Runtime evidence:
evidence_summary.jsonrecords 6,902 entry/exit events and 3,451begin_batch/end_batchpairs.
grim.dll body:
(**(code **)(*in_ECX + 0xfc))(0);
(**(code **)(*in_ECX + 0xe8))();
(**(code **)(*in_ECX + 0x11c))(0,0,(float)DAT_1005c400,(float)DAT_10059dc0);
(**(code **)(*in_ECX + 0xf0))();
The recovered VC6.5 source matches all 32 instructions and both masked
references. Explicit unsigned casts of the signed dimension globals naturally
produce the native qword fild stack schedule; the corrected parameter
accounts for the native stack cleanup without an ABI shim.
0xcc — grim_draw_fullscreen_color @ 0x100079b0¶
- Confirmed name:
draw_fullscreen_color - Confirmed C++ signature:
void __thiscall IGrim2D::draw_fullscreen_color(float r, float g, float b, float a) - Ghidra signature:
void grim_draw_fullscreen_color(float r, float g, float b, float a) - Notes: positive alpha selects untextured D3D8 color/alpha state, forwards the
RGBA scalars to
set_color, clears rotation, and draws one quad from the origin to the unsigned backbuffer dimensions. Non-positive alpha returns before changing state; the normal modulate state is restored after drawing. - Call sites: 2 (unique funcs: 2)
- Sample calls: gameplay_render_world (
FUN_00405960):L3696; FUN_00406af0:L4120 - First callsite: gameplay_render_world (
FUN_00405960) (line 3696) - Runtime evidence:
evidence_summary.jsonrecords 60 calls, of which 30 positive-alpha paths begin and end a batch.
FUN_004295f0();
if (0.0 < DAT_00487264) {
(**(code **)(*DAT_0048083c + 0xcc))(0,0,0,DAT_00487264);
}
return;
grim.dll body:
if (0.0 < a) {
(**(code **)(*DAT_10059dbc + 0xf4))(DAT_10059dbc,0,0);
(**(code **)(*in_ECX + 0x114))(piVar1,uVar2,uVar3,uVar4);
(**(code **)(*in_ECX + 0xfc))(0);
(**(code **)(*in_ECX + 0xe8))();
(**(code **)(*in_ECX + 0x11c))(0,0,(float)DAT_1005c400,(float)DAT_10059dc0);
(**(code **)(*in_ECX + 0xf0))();
}
The recovered VC6.5 source matches all 83 instructions and all 8 masked
references. Declaring the backbuffer dimensions as unsigned naturally produces
the native qword fild conversions; live Binary Ninja confirms the four-float
member ABI and retn 0x10.
0xd0 — grim_draw_rect_filled @ 0x100078e0¶
- Confirmed name:
draw_rect_filled - Confirmed C++ signature:
void __thiscall IGrim2D::draw_rect_filled(float *xy, float w, float h, float *rgba) - Notes: draws UI panel backgrounds with an explicit RGBA pointer. Live Binary
Ninja disassembly loads the fourth stack argument, tests
rgba[3], receivesthisinECX, and returns withretn 0x10. This corrects the prior three-argument Ghidra prototype. - Ghidra signature (missing fourth argument):
void grim_draw_rect_filled(float *xy, float w, float h) - Call sites: 24 (unique funcs: 14)
- Sample calls: FUN_00401dd0:L740; FUN_00401dd0:L752; FUN_00402d50:L1448; demo_trial_overlay_render (
FUN_004047c0):L3096; ui_render_keybind_help:L3369; tutorial_prompt_dialog (FUN_00408530):L5029; demo_purchase_screen_update (FUN_0040b740):L6476; demo_purchase_screen_update (FUN_0040b740):L6480 - First callsite: FUN_00401dd0 (line 740)
fStack_48 = (float)*(int *)(param_1 + 0x18);
fStack_4c = DAT_00471140;
(**(code **)(*DAT_0048083c + 0xd0))(&stack0xffffffd4);
(**(code **)(*DAT_0048083c + 0x114))
(0x3dcccccd,0x3f19999a,0x3f800000,
grim.dll body:
if (0.0 < *(float *)(in_stack_00000010 + 0xc)) {
(**(code **)(*DAT_10059dbc + 0xf4))(DAT_10059dbc,0,0);
(**(code **)(*in_ECX + 0xfc))(0);
(**(code **)(*in_ECX + 0xe8))();
(**(code **)(*in_ECX + 0x11c))(*puVar1,puVar1[1],uVar2,uVar3);
(**(code **)(*in_ECX + 0xf0))();
}
The recovered VC6.5 source matches all 72 instructions and all 6 masked
references. For positive alpha it selects untextured D3D8 color/alpha state,
forwards rgba to set_color_ptr, clears rotation, brackets one quad with
begin_batch/end_batch, and restores the normal modulate state.
0xd4 — grim_draw_rect_outline @ 0x10008f10¶
- Confirmed name:
draw_rect_outline - Confirmed C++ signature:
void __thiscall IGrim2D::draw_rect_outline(float *xy, float w, float h) - Notes: selects untextured D3D8 color/alpha state, clears rotation, and
brackets the edge draw with
begin_batch/end_batch. Unit height or width takes a one-quad fast path. Other rectangles draw top, left, bottom, and right quads; the bottom edge is widened by one pixel before normal modulate state is restored. - Ghidra signature:
void grim_draw_rect_outline(float *xy, float w, float h) - Call sites: 12 (unique funcs: 11)
- Sample calls: FUN_00402d50:L1454; demo_trial_overlay_render (
FUN_004047c0):L3107; ui_render_keybind_help:L3372; tutorial_prompt_dialog (FUN_00408530):L5031; quest_results_screen_update (FUN_00410d20):L7694; ui_menu_item_update:L27177; ui_text_input_update (FUN_0043ecf0):L27413; ui_text_input_update (FUN_0043ecf0):L27448 - First callsite: FUN_00402d50 (line 1454)
- Runtime evidence: the checked-in
ui_render_trace_summary.jsoncontains 3,132grim_draw_rect_outlineevents.
fStack_4c = (float)(DAT_00480504 / 2 + -0x6e);
fStack_48 = (float)(DAT_00480508 / 2 + -0x1e);
(**(code **)(*DAT_0048083c + 0xd4))(&fStack_4c,0x435c0000,0x42700000);
iVar1 = *DAT_0048083c;
iVar2 = (**(code **)(iVar1 + 0x14c))
grim.dll body:
(**(code **)(*in_ECX + 0xfc))(0);
(**(code **)(*in_ECX + 0xe8))();
(**(code **)(*in_ECX + 0x11c))(fRam00000000,fRam00000004,4,0x3f800000);
(**(code **)(*in_ECX + 0x11c))(fRam00000000,fRam00000004,0x3f800000,0);
(**(code **)(*in_ECX + 0x11c))(fRam00000000,fVar3 + fRam00000004,fVar2 + 1.0,0x3f800000);
(**(code **)(*in_ECX + 0x11c))(fVar1 + fRam00000000,fRam00000004,0x3f800000,0);
(**(code **)(*in_ECX + 0xf0))();
The recovered VC6.5 source matches all 125 instructions and all 8 masked
references. Live Binary Ninja confirms the three-argument member ABI,
documented D3D8 calls, comparison branches, four vtable draw_quad calls, and
retn 0xc without source-shaping artifacts.
0xd8 — grim_draw_circle_filled @ 0x10007b90¶
- Confirmed name:
draw_circle_filled - Confirmed C++ signature:
void grim_draw_circle_filled(float x, float y, float radius) - Primitive:
D3DPT_TRIANGLEFAN - Segment count:
int(radius * 0.125f + 12.0f) - Call sites: 1 (unique funcs: 1)
- Sample calls: ui_render_aim_indicators:L5640
- First callsite: ui_render_aim_indicators (line 6027)
DAT_004802a8 = _DAT_00484fc8 + (float)(&DAT_00490900)[DAT_004aaf0c * 0xd8];
DAT_004802ac = _DAT_00484fcc + (float)(&DAT_00490904)[DAT_004aaf0c * 0xd8];
(**(code **)(*DAT_0048083c + 0xd8))(DAT_004802a8,DAT_004802ac,uVar7);
(**(code **)(*DAT_0048083c + 0xc4))(DAT_0048f7e8,0);
(**(code **)(*DAT_0048083c + 0x100))(0x3f000000,0,0x3f000000,0x3f800000);
The recovered source matches all 115 native instructions (432 bytes;
references 32/0/0). It opens and locks the dynamic vertex buffer directly,
emits the center followed by an inclusive 0..segment_count perimeter loop,
unlocks, and draws vertex_count - 2 fan primitives. Each vertex is the native
28-byte transformed/lit layout {x, y, z, rhw, color, u, v}; the current
depth, RHW, color, and UV values are copied into every generated point.
0xdc — grim_draw_circle_outline @ 0x10007d40¶
- Confirmed name:
draw_circle_outline - Confirmed C++ signature:
void grim_draw_circle_outline(float x, float y, float radius) - Primitive:
D3DPT_TRIANGLESTRIP - Segment count:
int(radius * 0.2f + 14.0f) - Outer radius:
radius + 2.0f - Call sites: 1 (unique funcs: 1)
- Sample calls: ui_render_aim_indicators:L5644
- First callsite: ui_render_aim_indicators (line 6031)
(**(code **)(*DAT_0048083c + 0x100))(0x3f000000,0,0x3f000000,0x3f800000);
(**(code **)(*DAT_0048083c + 0x114))(0x3f800000,0x3f800000,0x3f800000,0x3f0ccccd);
(**(code **)(*DAT_0048083c + 0xdc))(DAT_004802a8,DAT_004802ac,uVar7);
(**(code **)(*DAT_0048083c + 0x114))(0x3f800000,0x3f333333,0x3dcccccd,0x3f4ccccd);
DAT_004802a8 = _DAT_00484fc8 + (float)(&DAT_00490900)[DAT_004aaf0c * 0xd8];
The recovered source matches all 120 native instructions (462 bytes;
references 31/0/0). Its inclusive loop emits an inner vertex at radius
with v = 0.0f and an outer vertex at radius + 2.0f with v = 1.0f, then
draws vertex_count - 2 strip primitives. VC6 hoists the repeated outer-radius
expression and retains it with the segment divisor on the x87 stack across the
loop; the natural repeated expression reproduces that behavior exactly.
0xe0 — grim_draw_line @ 0x100080b0¶
- Confirmed name:
draw_line - Confirmed C++ signature:
void grim_draw_line(float *p0, float *p1, float thickness) - Previous Ghidra signature:
void grim_draw_line(void *this, float *p0, float *p1, float thickness) - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
Recovered source shape:
static GrimLineVector half_vector;
half_vector.x = p1[0] - p0[0];
half_vector.y = p1[1] - p0[1];
D3DXVec2Normalize(&half_vector, &half_vector);
half_vector.x = half_vector.y * thickness;
half_vector.y = half_vector.x * thickness;
grim_draw_line_quad(p0, p1, &half_vector.x);
The source matches all 40 native instructions (134 bytes; references
13/0/0), including VC6's guard and atexit registration for the
function-static vector. The linked thunk is D3DXVec2Normalize. The second
component genuinely multiplies the already-scaled first component by
thickness; the scratch preserves that observed behavior instead of silently
turning it into a conventional perpendicular-vector formula.
0xe4 — grim_draw_line_quad @ 0x10008150¶
- Confirmed name:
draw_line_quad - Confirmed C++ signature:
void grim_draw_line_quad(float *p0, float *p1, float *half_vec) - Previous Ghidra signature:
void grim_draw_line_quad(void *this, float *p0, float *p1, float *half_vec) - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
(**(code **)(*(int *)this + 0x138))
(*p0 - *half_vec,p0[1] - half_vec[1],*p0 + *half_vec,p0[1] + half_vec[1],
*p1 + *half_vec,p1[1] + half_vec[1],*p1 - *half_vec,p1[1] - half_vec[1]);
The direct eight-float forwarding source matches all 42 native instructions
(99 bytes; no masked references). It expands the two endpoints into four
corners and calls vtable slot 0x138 (grim_draw_quad_points).
0xe8 — grim_begin_batch @ 0x10007ac0¶
- Confirmed name:
begin_batch - Confirmed C++ signature:
void __thiscall IGrim2D::begin_batch(void) - Notes: marks the batch active, begins a D3D8 scene, locks the dynamic vertex
buffer with flags
0x2800, and resets the low-word vertex count. Lock failure clears the device-ready byte. - Ghidra signature:
void grim_begin_batch(void) - Call sites: 79 (unique funcs: 23)
- Sample calls: ui_draw_clock_gauge:L3887; ui_draw_clock_gauge:L3892; ui_render_aim_indicators:L5683; FUN_00417b80:L9228; FUN_00417b80:L9271; FUN_00417b80:L9299; terrain_generate_random:L9464; terrain_generate_random:L9506
- First callsite: ui_draw_clock_gauge (line 3887)
(**(code **)(*DAT_0048083c + 0x114))(0x3f800000,0x3f800000,0x3f800000,iVar2);
(**(code **)(*DAT_0048083c + 0xfc))(0);
(**(code **)(*DAT_0048083c + 0xe8))();
(**(code **)(*DAT_0048083c + 0x11c))((float)iVar2,(float)iVar1,0x42000000,0x42000000);
(**(code **)(*DAT_0048083c + 0xf0))();
The recovered VC6.5 source matches all 27 instructions and all 9 masked
references. 0x2800 is the documented D3D8 combination
D3DLOCK_DISCARD | D3DLOCK_NOSYSLOCK.
0xec — grim_flush_batch @ 0x100083c0¶
- Confirmed name:
flush_batch - Confirmed C++ signature:
void __thiscall IGrim2D::flush_batch(void) - Notes: unlocks and submits the current vertex buffer, then relocks it for continued batching. Only a successful relock clears the low-word count.
- Ghidra signature:
void grim_flush_batch(void) - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
if ((grim_render_disabled == '\0') && (grim_batch_active != '\0')) {
(**(code **)(*DAT_10059e2c + 0x30))(DAT_10059e2c);
(**(code **)(*DAT_10059dbc + 0x11c))
(DAT_10059dbc,4,0,grim_vertex_count & 0xffff,0,(grim_vertex_count & 0xffff) >> 1);
iVar1 = (**(code **)(*DAT_10059e2c + 0x2c))(DAT_10059e2c,0,0,&DAT_10059e34,0x2800);
if (-1 < iVar1) {
grim_vertex_count = grim_vertex_count & 0xffff0000;
}
}
The recovered VC6.5 source matches all 37 instructions and all 8 masked
references. It uses ordinary D3D8 Unlock, DrawIndexedPrimitive, and Lock
calls; native code submits even when the current count is zero.
0xf0 — grim_end_batch @ 0x10007b20¶
- Confirmed name:
end_batch - Confirmed C++ signature:
void __thiscall IGrim2D::end_batch(void) - Notes: unlocks the vertex buffer, conditionally submits a nonempty triangle list, ends the D3D8 scene, and clears the active flag. If device readiness is lost after unlock, native code returns without clearing the active flag.
- Ghidra signature:
void grim_end_batch(void) - Call sites: 86 (unique funcs: 28)
- Sample calls: FUN_00401dd0:L753; demo_trial_overlay_render (
FUN_004047c0):L3134; ui_draw_clock_gauge:L3889; ui_draw_clock_gauge:L3895; ui_render_aim_indicators:L5702; demo_purchase_screen_update (FUN_0040b740):L6346; ui_draw_textured_quad:L9125; terrain_generate (FUN_00417b80):L9261 - First callsite: FUN_00401dd0 (line 753)
fStack_48 = fStack_48 - 4.0;
(**(code **)(*DAT_0048083c + 0xd0))(&fStack_4c,DAT_00471140,0x40800000,&puStack_44);
(**(code **)(*DAT_0048083c + 0xf0))();
(**(code **)(*DAT_0048083c + 0x20))(0x15,2);
(**(code **)(*DAT_0048083c + 0x20))(0x18,0x3f000000);
The recovered VC6.5 source matches all 36 instructions and all 8 masked references. It derives the D3D primitive count as half of the low-word vertex count and skips only the draw call for an empty batch.
0xf4 — grim_submit_vertex_raw @ 0x10008e30¶
- Confirmed name:
submit_vertex_raw - Confirmed C++ signature:
void submit_vertex_raw(float *vertex) - Notes: copies 7 floats and auto-flushes when the batch is full
- Previous Ghidra signature:
void grim_submit_vertex_raw(float *vertex) - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
if (grim_batch_active == '\0') {
(**(code **)(*in_ECX + 0xe8))();
}
for (iVar1 = 7; iVar1 != 0; iVar1 = iVar1 + -1) {
*pfVar2 = *vertex;
vertex = vertex + 1;
pfVar2 = pfVar2 + 1;
}
grim_vertex_count._0_2_ = (ushort)grim_vertex_count + 1;
if (DAT_1005976c <= (ushort)grim_vertex_count) {
(**(code **)(*in_ECX + 0xec))();
}
The recovered source matches all 35 native instructions (116 bytes;
references 9/0/0). It rejects disabled/unready rendering, lazily begins a
batch, copies one 28-byte transformed/lit vertex, advances the cursor and
low-word count, and flushes at capacity.
0xf8 — grim_submit_quad_raw @ 0x10008eb0¶
- Confirmed name:
submit_quad_raw - Confirmed C++ signature:
void submit_quad_raw(float *verts) - Notes: copies 28 floats (4 vertices) and auto-flushes when the batch is full
- Previous Ghidra signature:
void grim_submit_quad_raw(float *verts) - Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
for (iVar1 = 0x1c; iVar1 != 0; iVar1 = iVar1 + -1) {
*pfVar2 = *verts;
verts = verts + 1;
pfVar2 = pfVar2 + 1;
}
grim_vertex_count._0_2_ = (ushort)grim_vertex_count + 4;
if (DAT_1005976c <= (ushort)grim_vertex_count) {
(**(code **)(*in_ECX + 0xec))();
}
The recovered source matches all 25 native instructions (91 bytes;
references 7/0/0). This lower-level path checks only the render-disable byte,
copies 112 bytes into an already active batch, advances by four vertices, and
flushes at capacity.
0xfc — grim_set_rotation @ 0x10007f30¶
- Confirmed name:
set_rotation - Confirmed C++ signature:
void __thiscall IGrim2D::set_rotation(float radians) - Notes: stores the supplied angle and evaluates sine/cosine at
radians + 0.7853982f. The resulting row-major matrix is{cos, sin, -sin, cos}. Live Binary Ninja shows the one-argument member ABI, x87fcos/fsin, seven scalar global writes, andretn 0x4. - Ghidra signature:
void grim_set_rotation(float radians) - Call sites: 65 (unique funcs: 17)
- Sample calls: FUN_00401dd0:L736; ui_draw_clock_gauge:L3886; ui_draw_clock_gauge:L3893; ui_render_aim_indicators:L5662; demo_purchase_screen_update (
FUN_0040b740):L6325; terrain_render (FUN_004188a0):L9599; terrain_render (FUN_004188a0):L9630; creature_render_type (FUN_00418b60):L9718 - First callsite: FUN_00401dd0 (line 736)
- Runtime evidence: the checked-in
ui_render_trace_summary.jsoncontains 53,352grim_set_rotationevents.
uStack_40 = 0;
puStack_44 = (undefined1 *)0x401e8d;
(**(code **)(*DAT_0048083c + 0xfc))();
puStack_44 = &stack0xffffffdc;
fStack_48 = (float)*(int *)(param_1 + 0x18);
grim.dll precompute:
_DAT_10059e30 = radians;
fVar1 = (float10)fcos((float10)radians + (float10)0.7853982);
grim_rotation_cos = (float)fVar1;
The recovered VC6.5 source matches all 19 instructions and all 11 masked references. Ordinary repeated trig expressions plus row-major assignments produce the native x87 schedule and register allocation without volatile state, copies, or layout-only expressions.
0x100 — grim_set_uv @ 0x10008350¶
- Confirmed name:
set_uv - Confirmed C++ signature:
void __thiscall IGrim2D::set_uv(float u0, float v0, float u1, float v1) - Notes: expands opposite UV corners into the four clockwise pairs consumed by
draw calls. The vtable slot and
retn 0x10establish the member ABI even though the body does not otherwise readthis. - Ghidra signature:
void grim_set_uv(float u0, float v0, float u1, float v1) - Call sites: 59 (unique funcs: 23)
- Sample calls: demo_trial_overlay_render (
FUN_004047c0):L3126; ui_draw_clock_gauge:L3884; ui_render_aim_indicators:L5635; ui_render_aim_indicators:L5642; ui_render_aim_indicators:L5664; demo_purchase_screen_update (FUN_0040b740):L6331; ui_draw_textured_quad:L9121; terrain_generate (FUN_00417b80):L9206 - First callsite: demo_trial_overlay_render (
FUN_004047c0) (line 3126)
uStack_18c = 0;
uStack_190 = 0x4048bd;
(**(code **)(*DAT_0048083c + 0x100))();
uStack_190 = 0x4239999a;
fStack_198 = param_1[1] + 22.0;
grim.dll UV assignment:
The recovered VC6.5 source matches all 17 instructions and all 8 masked references. It consists solely of ordinary field assignments to the same four-element UV array recovered independently in the exact quad renderers.
0x104 — grim_set_atlas_frame @ 0x10008230¶
- Confirmed name:
set_atlas_frame - Confirmed C++ signature:
void __thiscall IGrim2D::set_atlas_frame(int atlas_size, int frame) - Notes: atlas size (cells per side) + frame index
- Ghidra signature:
void grim_set_atlas_frame(int atlas_size, int frame) - Call sites: 25 (unique funcs: 6)
- Sample calls: creature_render_type (
FUN_00418b60):L9704; creature_render_type (FUN_00418b60):L9715; creature_render_type (FUN_00418b60):L9759; creature_render_type (FUN_00418b60):L9770; creature_render_type (FUN_00418b60):L9819; creature_render_type (FUN_00418b60):L9830; bonus_hud_slot_update_and_render:L10630; projectile_render (FUN_00422c70):L16482 - First callsite: creature_render_type (
FUN_00418b60) (line 11841)
Live Binary Ninja shows ECX=this, two stack parameters, and retn 0x8.
The recovered source initializes four UV corners from the indexed atlas origin,
then advances the right and bottom edges by one cell (1 / atlas_size). VC6.5
reproduces all 31 instructions and all 15 masked references without codegen
scaffolding.
0x108 — grim_set_sub_rect @ 0x100082c0¶
- Confirmed name:
set_sub_rect - Confirmed C++ signature:
void __thiscall IGrim2D::set_sub_rect(int atlas_size, int width, int height, int frame) - Notes: atlas grid sub-rect;
atlas_sizeindexes a pointer table with entries at 2/4/8/16; explicit call uses(8, 2, 1, frame<<1) - Ghidra signature:
void grim_set_sub_rect(int atlas_size, int width, int height, int frame) - Call sites: 6 (unique funcs: 3)
- Sample calls: ui_render_hud (
FUN_0041aed0):L10950; ui_render_hud (FUN_0041aed0):L10961; ui_render_hud (FUN_0041aed0):L10964; ui_render_hud (FUN_0041aed0):L11488; bonus_render (FUN_004295f0):L18733; ui_text_input_render (FUN_004413a0):L27845 - First callsite: ui_render_hud (
FUN_0041aed0) (line 13087)
fStack_f8 = 1.12104e-44;
fStack_fc = 6.033625e-39;
(**(code **)(*DAT_0048083c + 0x108))();
fStack_fc = 32.0;
fStack_100 = 64.0;
Explicit parameterized call (bonus_render (FUN_004295f0)):
(**(code **)(*DAT_0048083c + 0x108))
(8,2,1,(&DAT_004d7a90)[(int)pfVar7[4] * 0x1f] << 1);
(**(code **)(*DAT_0048083c + 0x11c))();
Live Binary Ninja shows ECX=this, four stack parameters, and retn 0x10.
The recovered source loads one (u, v) origin, initializes all four current
UV corners, then advances the right edge by width / atlas_size and the
bottom edge by height / atlas_size. VC6.5 reproduces all 31 instructions and
all 15 masked references without codegen scaffolding.
Atlas pointer table setup (grim.dll init):
puVar9 = &DAT_1005bc78;
for (iVar7 = 0x10; iVar7 != 0; iVar7 = iVar7 + -1) {
*puVar9 = 0;
puVar9 = puVar9 + 1;
}
_DAT_1005bc80 = &grim_subrect_table_0;
_DAT_1005bc88 = &grim_subrect_table_1;
_DAT_1005bc98 = &grim_subrect_table_2;
_DAT_1005bcb8 = &DAT_1005a678;
0x10c — grim_set_uv_point @ 0x100083a0¶
- Confirmed name:
set_uv_point - Confirmed C++ signature:
void __thiscall IGrim2D::set_uv_point(int index, float u, float v) - Ghidra signature:
void grim_set_uv_point(int index, float u, float v) - Notes: called as four consecutive
set_uv_pointcalls (indices 0..3) to override per-corner UVs; u=0.625, v in {0, 0.25}. The native method performs no bounds check. Its vtable slot andretn 0xcestablish the member ABI. - Call sites: 4 (unique funcs: 1)
- Sample calls: projectile_render (
FUN_00422c70):L16721; projectile_render (FUN_00422c70):L16725; projectile_render (FUN_00422c70):L16728; projectile_render (FUN_00422c70):L16729 - First callsite: projectile_render (
FUN_00422c70) (line 18858)
fVar14 = fVar32;
do {
(**(code **)(*DAT_0048083c + 0x10c))(0,0x3f200000,0);
fVar26 = 0.25;
fVar24 = 0.625;
fVar21 = 1.4013e-45;
(**(code **)(*DAT_0048083c + 0x10c))(1,0x3f200000,0x3e800000);
fVar19 = 0.25;
in_stack_fffffd48 = 0.625;
(**(code **)(*DAT_0048083c + 0x10c))(2,0x3f200000,0x3e800000);
(**(code **)(*DAT_0048083c + 0x10c))(3,0x3f200000,0);
pfVar7 = &fStack_274;
The recovered VC6.5 source matches all 6 instructions and both masked
references. It consists solely of indexed U/V field assignments through the
same eight-byte UV array recovered by grim_set_uv.
0x110 — grim_set_color_ptr @ 0x10008040¶
- Confirmed name:
set_color_ptr - Confirmed C++ signature:
void __thiscall IGrim2D::set_color_ptr(float *rgba) - Notes: converts a four-float RGBA vector directly to packed ARGB and copies
it to all four corner slots. This method does not clamp; relevant callers
clamp inputs when needed. The vtable slot and
retn 0x4establish the member ABI even though the body does not otherwise readthis. - Ghidra signature:
void grim_set_color_ptr(float *rgba) - Call sites: 20 (unique funcs: 10)
- Sample calls: game_over_screen_update:L7098; game_over_screen_update:L7171; quest_results_screen_update (
FUN_00410d20):L7782; quest_results_screen_update (FUN_00410d20):L7851; creature_render_type (FUN_00418b60):L9717; creature_render_type (FUN_00418b60):L9772; creature_render_type (FUN_00418b60):L9832; creature_render_type (FUN_00418b60):L9894 - First callsite: game_over_screen_update (line 7485)
(**(code **)(*DAT_0048083c + 0x104))(8,iVar2);
}
(**(code **)(*DAT_0048083c + 0x110))(&fStack_58);
(**(code **)(*DAT_0048083c + 0xfc))(pfVar5[7] - 1.5707964);
(**(code **)(*DAT_0048083c + 0x11c))
((_DAT_00484fc8 + pfVar5[1]) - fVar10,(_DAT_00484fcc + pfVar5[2]) - fVar10,
pfVar5[9] * 1.07,pfVar5[9] * 1.07);
Clamped RGBA example (input_primary_just_pressed (FUN_00446030)):
if (0.0 <= afStack_8c[2]) {
if (1.0 < afStack_8c[2]) {
afStack_8c[2] = 1.0;
}
}
else {
afStack_8c[2] = 0.0;
}
if (0.0 <= afStack_8c[3]) {
if (1.0 < afStack_8c[3]) {
afStack_8c[3] = 1.0;
}
}
else {
afStack_8c[3] = 0.0;
}
(**(code **)(*DAT_0048083c + 0x110))(afStack_8c + 2);
The recovered VC6.5 source matches all 25 instructions and all 12 masked
references. A four-byte color union accounts for the native per-channel byte
writes; VC6 reuses the dead parameter home for that local after preserving the
input pointer in ESI.
0x114 — grim_set_color @ 0x10007f90¶
- Confirmed name:
set_color - Confirmed C++ signature:
void __thiscall IGrim2D::set_color(float r, float g, float b, float a) - Notes: clamps alpha to
[0, 1], converts four channels with the native x87_ftolpath, packs ARGB, and propagates the value to all four corner slots. RGB is not clamped inside this method. The vtable slot andretn 0x10establish the member ABI even though the body does not otherwise readthis. - Ghidra signature:
void grim_set_color(float r, float g, float b, float a) - Call sites: 203 (unique funcs: 37)
- Sample calls: FUN_00401dd0:L733; FUN_00401dd0:L741; FUN_00401dd0:L756; FUN_00401dd0:L764; FUN_00401dd0:L769; FUN_00401dd0:L787; FUN_00401dd0:L833; FUN_00402d50:L1451
- First callsite: FUN_00401dd0 (line 733)
uStack_3c = 0x3f19999a;
uStack_40 = 0x401e7d;
(**(code **)(*DAT_0048083c + 0x114))();
uStack_40 = 0;
puStack_44 = (undefined1 *)0x401e8d;
grim.dll packing:
DAT_1005bc04 = ((uVar1 & 0xff | iVar2 << 8) << 8 | uVar3 & 0xff) << 8 | uVar4 & 0xff;
DAT_1005bc10 = DAT_1005bc04;
The recovered VC6.5 source matches all 42 instructions and all 16 masked references. Its ordinary alpha clamp, unsigned byte packing, and chained color slot assignment reproduce the native compare, conversion, shift, and store order without matching-only constructs.
0x118 — grim_set_color_slot @ 0x100081c0¶
- Confirmed name:
set_color_slot - Confirmed C++ signature:
void __thiscall IGrim2D::set_color_slot(int index, float r, float g, float b, float a) - Notes: packs RGBA directly into color slot
index; draw calls consume slots 0..3. The method performs no channel clamp and no index bounds check. Its vtable slot andretn 0x14establish the member ABI even though the body does not otherwise readthis. - Ghidra signature:
void grim_set_color_slot(int index, float r, float g, float b, float a) - Call sites: 12 (unique funcs: 2)
- Sample calls: demo_purchase_screen_update (
FUN_0040b740):L6302; demo_purchase_screen_update (FUN_0040b740):L6308; demo_purchase_screen_update (FUN_0040b740):L6315; demo_purchase_screen_update (FUN_0040b740):L6322; projectile_render (FUN_00422c70):L15993; projectile_render (FUN_00422c70):L16000; projectile_render (FUN_00422c70):L16068; projectile_render (FUN_00422c70):L16075 - First callsite: demo_purchase_screen_update (
FUN_0040b740) (line 6689)
fVar10 = 0.0;
fsin((float10)(DAT_004808c0 % 1000) * (float10)6.2831855);
(**(code **)(*DAT_0048083c + 0x118))();
uStack_7c = 0x3e99999a;
puStack_80 = (undefined1 *)0x0;
grim.dll slot write:
The recovered VC6.5 source matches all 27 instructions and all 9 masked references. It uses the same ordinary ARGB expression as the exact all-slot setter, then writes the result through the caller-provided array index.
0x11c — grim_draw_quad @ 0x10008b10¶
- Confirmed name:
draw_quad - Confirmed C++ signature:
void __thiscall IGrim2D::draw_quad(float x, float y, float w, float h) - Notes: core draw call; uses per-corner color slots + UV array. Live callers
pass the interface in
ECX, and the implementation returns withretn 0x10. - Ghidra signature:
void grim_draw_quad(float x, float y, float w, float h) - Call sites: 100 (unique funcs: 21)
- Sample calls: demo_trial_overlay_render (
FUN_004047c0):L3132; ui_draw_clock_gauge:L3888; ui_draw_clock_gauge:L3894; ui_render_aim_indicators:L5701; demo_purchase_screen_update (FUN_0040b740):L6344; ui_draw_textured_quad:L9124; terrain_render (FUN_004188a0):L9613; creature_render_type (FUN_00418b60):L9720 - First callsite: demo_trial_overlay_render (
FUN_004047c0) (line 3132)
fStack_19c = *param_1 + 72.0;
pcStack_1a0 = (char *)0x4048ee;
(**(code **)(*DAT_0048083c + 0x11c))();
pcStack_1a0 = (char *)0x4048fc;
(**(code **)(*DAT_0048083c + 0xf0))();
grim.dll vertex fill (color + UV):
The recovered VC6.5 source matches all 195 instructions and all 68 masked
references. Its rotated path uses one Quake inverse-square-root refinement:
half_diag = 0.5 * length_sq * inverse_sqrt(length_sq). This is a close
approximation of 0.5 * sqrt(length_sq), not a CRT sqrt call.
0x120 — grim_draw_quad_xy @ 0x10008720¶
- Confirmed name:
draw_quad_xy - Confirmed C++ signature:
void __thiscall IGrim2D::draw_quad_xy(float *xy, float w, float h) - Notes: direct wrapper around
draw_quadusing an XY pointer. Live disassembly receivesthisinECX, forwards through slot0x11c, and returns withretn 0xc. - Ghidra signature:
void grim_draw_quad_xy(float *xy, float w, float h) - Call sites: 6 (unique funcs: 2)
- Sample calls: FUN_00417b80:L9255; FUN_00417b80:L9289; FUN_00417b80:L9317; terrain_generate_random:L9491; terrain_generate_random:L9524; terrain_generate_random:L9547
- First callsite: terrain_generate (
FUN_00417b80) (line 11392)
fStack_bc = fVar6;
fStack_b8 = fVar6;
(**(code **)(*DAT_0048083c + 0x120))();
iVar4 = iVar4 + 1;
iVar1 = DAT_0048f538 * DAT_0048f534 * 800;
grim.dll body:
The recovered VC6.5 source matches all 14 instructions with no masked-reference
debt. Its body is exactly draw_quad(xy[0], xy[1], w, h).
0x124 — grim_draw_quad_rotated_matrix @ 0x10008750¶
- Confirmed name:
draw_quad_rotated_matrix - Confirmed C++ signature:
void __thiscall IGrim2D::draw_quad_rotated_matrix(float x, float y, float w, float h) - Notes: emits a centered quad using the cached rotation matrix and current UV/color slots
- Call sites: 0 (unique funcs: 0)
- Sample calls: none found
- First callsite: not found in decompiled output
grim.dll body:
if (_DAT_10059e30 == 0.0) {
local_18 = x + w;
local_20 = x;
local_1c = y;
local_14 = y;
} else {
fVar3 = w * 0.5;
fStack_8 = fVar3 + x;
0x128 — grim_submit_vertices_transform @ 0x100085c0¶
- Confirmed name:
submit_vertices_transform - Confirmed C++ signature:
void __thiscall IGrim2D::submit_vertices_transform(float *verts, int count, float *offset, float *matrix) - Ghidra signature:
void grim_submit_vertices_transform(float * verts, int count, float * offset, float * matrix) - Notes: the render-disabled guard precedes a
count * 0x1cbulk copy. For positive counts, live Binary Ninja disassembly shows the exact transformsx' = y*m[1] + x*m[0]andy' = y*m[3] + x*m[2], followed by the XY offset and a seven-dword global write-pointer advance. The method then addscountto the counter's low word and calls virtual slot0xecat capacity;retn 0x10confirms the four-argument member ABI. - Call sites: 5 (unique funcs: 1)
- Sample calls: ui_element_render (
FUN_00446c40):L29980; ui_element_render (FUN_00446c40):L29985; ui_element_render (FUN_00446c40):L29986; ui_element_render (FUN_00446c40):L30065; ui_element_render (FUN_00446c40):L30107 - First callsite: ui_element_render (
FUN_00446c40) (line 32116) - Runtime evidence: the checked-in
ui_render_trace.jsonlcontains 13,940grim_submit_verticesevents withkind: transform.
pppcStack_84 = (char ***)pppfVar1;
ppfStack_80 = (float **)pppcVar2;
(**(code **)(*DAT_0048083c + 0x128))();
if (*(int *)(param_1 + 0x120) == 8) {
pppcStack_98 = (char ***)0x4;
grim.dll inner loop (stride + matrix):
The recovered VC6.5 source matches all 64 instructions and all 9 masked references. Ordinary scalar rotation accumulators preserve the native x87 evaluation order; no inline assembly, dummy references, or layout-only branches are used.
0x12c — grim_submit_vertices_offset @ 0x10008680¶
- Confirmed name:
submit_vertices_offset - Confirmed C++ signature:
void __thiscall IGrim2D::submit_vertices_offset(float *verts, int count, float *offset) - Ghidra signature:
void grim_submit_vertices_offset(float * verts, int count, float * offset) - Notes: copies a generic seven-float vertex stream, adds XY offsets in place,
advances the global batch pointer, and flushes at capacity. The decompiler
emits decimal vtable offset
+ 300(0x12c). Live disassembly receivesthisinECXand returns withretn 0xc. - Call sites: 4 (unique funcs: 1)
- Sample calls: ui_element_render (
FUN_00446c40):L30035; ui_element_render (FUN_00446c40):L30042; ui_element_render (FUN_00446c40):L30045; ui_element_render (FUN_00446c40):L30074 - First callsite: ui_element_render (
FUN_00446c40) (line 32196)
(**(code **)(*DAT_0048083c + 300))();
if (*(int *)(param_1 + 0x120) == 8) {
(**(code **)(*DAT_0048083c + 300))();
grim.dll body:
*DAT_10059e34 = *DAT_10059e34 + *offset;
DAT_10059e34[1] = offset[1] + DAT_10059e34[1];
grim_vertex_count._0_2_ = (ushort)grim_vertex_count + (short)count;
if (DAT_1005976c <= (ushort)grim_vertex_count) {
(**(code **)(*in_ECX + 0xec))();
}
The recovered VC6.5 source matches all 236 native instructions (953 code bytes) and all 81 masked references. With rotation disabled it emits the ordinary axis-aligned corners. Otherwise it constructs four half-width and half-height offsets, transforms each in place through the cached 2x2 matrix, then translates each point by the requested rectangle center.
The native x87 schedule distinguishes the likely source operations: each
point's matrix transform completes before its separate center translation.
Keeping those as two ordinary operations reproduces the compiler's stack-slot
reuse and all downstream vertex stores without volatile state or fake
references. The four resulting vertices use the same lazy-batch, per-corner
color/UV, low-word count, and capacity-flush path as grim_draw_quad.
The recovered VC6.5 source matches all 50 instructions and all 8 masked
references. An ordinary memcpy(count * 0x1c) produces the native bulk-copy
sequence; a local float pointer then updates X/Y without imposing an invented
vertex struct.
0x130 — grim_submit_vertices_offset_color @ 0x10008430¶
- Confirmed name:
submit_vertices_offset_color - Confirmed C++ signature:
void __thiscall IGrim2D::submit_vertices_offset_color(float *verts, int count, float *offset, unsigned long *color) - Ghidra signature (color incorrectly typed as float):
void grim_submit_vertices_offset_color(float * verts, int count, float * offset, float * color) - Notes: bulk-copies seven-dword vertices, adds an XY offset, and overwrites
field 4 with
*coloras packed ARGB. Live integer loads/stores establish the corrected color-pointer type;retn 0x10establishes the member ABI. - Call sites: 3 (unique funcs: 1)
- Sample calls: ui_element_render (
FUN_00446c40):L30006; ui_element_render (FUN_00446c40):L30014; ui_element_render (FUN_00446c40):L30018 - First callsite: ui_element_render (
FUN_00446c40) (line 32142)
fStack_6c = fStack_64 + *(float *)(param_1 + 0xc);
pppcStack_90 = (char ***)0x446fe3;
(**(code **)(*DAT_0048083c + 0x130))();
if (*(int *)(param_1 + 0x120) == 8) {
pfStack_74 = (float *)(*(float *)(param_1 + 0x1c) + 7.0);
grim.dll body:
*DAT_10059e34 = *offset + *DAT_10059e34;
DAT_10059e34[1] = offset[1] + DAT_10059e34[1];
DAT_10059e34[4] = *color;
The recovered VC6.5 source matches all 54 instructions and all 9 masked references. Its ordinary bulk copy, local XY temporaries, packed-color dword assignment, pointer advance, and low-word capacity check recover the complete native loop.
0x134 — grim_submit_vertices_transform_color @ 0x100084e0¶
- Confirmed name:
submit_vertices_transform_color - Confirmed C++ signature:
void __thiscall IGrim2D::submit_vertices_transform_color(float *verts, int count, float *offset, float *matrix, unsigned long *color) - Ghidra signature (color incorrectly typed as float):
void grim_submit_vertices_transform_color(float * verts, int count, float * offset, float * matrix, float * color) - Notes: the copy and matrix/offset loop match the non-color transform sibling,
then an integer load/store overwrites dword field 4 with
*coloras packed ARGB. The integer operation corrects the stale color-pointer prototype;retn 0x14confirms the five-argument member ABI. - Call sites: 5 (unique funcs: 2)
- Sample calls: effects_render (
FUN_0042e820):L20025; effects_render (FUN_0042e820):L20053; ui_element_render (FUN_00446c40):L29953; ui_element_render (FUN_00446c40):L29959; ui_element_render (FUN_00446c40):L29962 - First callsite: effects_render (
FUN_0042e820) (line 22162) - Runtime evidence: the checked-in
ui_render_trace.jsonlcontains 7,202grim_submit_verticesevents withkind: transform_color.
pfStack_60 = &fStack_40;
pfStack_64 = &fStack_48;
(**(code **)(*DAT_0048083c + 0x134))(puVar2 + 5,4);
}
puVar2 = puVar2 + 0x2f;
grim.dll body:
DAT_10059e34[1] = *DAT_10059e34 * matrix[2] + DAT_10059e34[1] * matrix[3];
*DAT_10059e34 = *DAT_10059e34 * *matrix + DAT_10059e34[1] * matrix[1];
*DAT_10059e34 = *offset + *DAT_10059e34;
DAT_10059e34[1] = offset[1] + DAT_10059e34[1];
DAT_10059e34[4] = *color;
The recovered VC6.5 source matches all 72 instructions and all 10 masked references. Its scalar rotation accumulators, packed-color dword assignment, and ordinary batch accounting reproduce the full native function without inline assembly, dummy references, or layout-only branches.
0x138 — grim_draw_quad_points @ 0x10009080¶
- Confirmed name:
draw_quad_points - Confirmed C++ signature:
void __thiscall IGrim2D::draw_quad_points(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) - Notes: pushes a quad using four explicit points and the current per-corner
color/UV slots. Live disassembly receives
thisinECX, returns withretn 0x20, and dispatches batch begin/flush through slots0xe8/0xec. - Ghidra signature:
void grim_draw_quad_points(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) - Call sites: 4 (unique funcs: 1)
- Sample calls: projectile_render (
FUN_00422c70):L16048; projectile_render (FUN_00422c70):L16204; projectile_render (FUN_00422c70):L16755; projectile_render (FUN_00422c70):L16768 - First callsite: projectile_render (
FUN_00422c70) (line 18185)
fStack_224 = (float)((float10)(float)(fVar9 * (float10)512.0 + (float10)fVar31) + fVar8 +
fVar11 * (float10)1.1);
(**(code **)(*DAT_0048083c + 0x138))();
fStack_244 = 6.07815e-39;
(**(code **)(*DAT_0048083c + 0xf0))();
grim.dll vertex fill (color + UV slots):
DAT_10059e34[4] = DAT_1005bc04;
DAT_10059e34[5] = DAT_1005b290;
DAT_10059e34[6] = DAT_1005b294;
DAT_10059e34 = DAT_10059e34 + 7;
*DAT_10059e34 = x1;
The recovered VC6.5 source matches all 130 instructions and all 59 masked references. It reuses one two-float stack temporary for the four incoming points, copies Z/RHW and UV as paired aggregates, writes one packed color per corner, and advances the native 28-byte vertex stream before its low-word batch count/capacity check.
0x13c — grim_draw_text_mono @ 0x100092b0¶
- Provisional name:
draw_text_mono(high) - Guess:
void draw_text_mono(float x, float y, const char *text) - Notes: fixed 16px grid; advances X before drawing each glyph (except after 0xA7); draws most glyphs at
y + 1.0; special-cases a few extended codes (0xA7, 0xE4, 0xE5, 0xF6). - Font texture is loaded into
grim_font_texturefrom Grim2D resources (resource id0x6f). [static] - Ghidra signature:
void grim_draw_text_mono(float x, float y, char *text) - Suggested signature:
void grim_draw_text_mono(float x, float y, const char *text) - Call sites: 5 (unique funcs: 3)
- Sample calls: FUN_00401dd0:L781; FUN_00401dd0:L804; FUN_00401dd0:L843; demo_purchase_screen_update (
FUN_0040b740):L6491; ui_render_hud (FUN_0041aed0):L11263 - First callsite: FUN_00401dd0 (line 781)
The recovered VC6 source shape identifies grim_font_scale at 0x1005cd08,
the shared two-float atlas temporary, and the native composite construction:
0xe4 is a plus ", 0xe5 is a plus ., and 0xf6 is o plus ".
The current scratch is an honest 94.39% WIP (298/308 instructions, all 41
masked references resolved). Its sole residual is a compiler tail merge that
deduplicates ten native instructions shared by the 0xe4 and 0xf6 suffixes.
}
else {
(**(code **)(*DAT_0048083c + 0x13c))
(0x41200000,(float)((iVar1 + 1) * 0x10) + *(float *)(param_1 + 0x1c),&DAT_004712c0);
iVar3 = *DAT_0048083c;
grim.dll body:
if (grim_font_texture_bound == '\0') {
(**(code **)(*DAT_10059dbc + 0xf4))(DAT_10059dbc,0,grim_font_texture);
}
(**(code **)(*in_ECX + 0xfc))(0);
(**(code **)(*in_ECX + 0xe8))();
0x140 — grim_draw_text_mono_fmt @ 0x10009940¶
- Confirmed name:
draw_text_mono_fmt - Confirmed C++ signature:
void __cdecl IGrim2D::draw_text_mono_fmt(float x, float y, char *fmt, ...) - Notes: this cdecl varargs member passes the argument tail after
fmtto the importedvsprintf, writes intogrim_printf_buffer, and dispatches(x, y, buffer)through the mono-font draw slot at0x13c. Live Binary Ninja disassembly confirms the explicit stackselfand plainret. - Ghidra signature:
void grim_draw_text_mono_fmt(IGrim2D *self, float x, float y, char *fmt, ...) - Call sites: 3 (unique funcs: 3)
- Sample calls: ui_render_keybind_help:L3374; FUN_00406350:L3950; ui_render_hud (
FUN_0041aed0):L11281 - First callsite: ui_render_keybind_help (line 3374)
(**(code **)(*DAT_0048083c + 0xd4))(param_1,0x44000000,0x43800000);
(**(code **)(*DAT_0048083c + 0x20))(0x18,0x3f4ccccd);
(**(code **)(*DAT_0048083c + 0x140))
(DAT_0048083c,*param_1 + 16.0,param_1[1] + 16.0,s_key_info_00471ffc);
(**(code **)(*DAT_0048083c + 0x114))(0x3f800000,0x3f800000,0x3f800000,uVar4);
grim.dll body:
The recovered VC6.5 source matches all 16 instructions and all 3 masked
references, including both grim_printf_buffer operands and the imported
vsprintf IAT slot.
0x144 — grim_draw_text_small @ 0x10009730¶
- Confirmed name:
draw_text_small - Confirmed C++ signature:
void grim_draw_text_small(float x, float y, char *text) - Atlas:
GRIM_Font2, with thesmallFnt.datbyte map and glyph widths - Call sites: 20 (unique funcs: 9)
- Sample calls: FUN_00401dd0:L760; FUN_00401dd0:L800; perk_selection_screen_update:L3808; demo_purchase_screen_update (
FUN_0040b740):L6398; demo_purchase_screen_update (FUN_0040b740):L6401; demo_purchase_screen_update (FUN_0040b740):L6409; demo_purchase_screen_update (FUN_0040b740):L6426; demo_purchase_screen_update (FUN_0040b740):L6428 - First callsite: FUN_00401dd0 (line 760)
(((float)*(int *)(param_1 + 0x18) + *(float *)(param_1 + 0x1c)) /
(float)*(int *)(param_1 + 0x18)) * 0.3);
(**(code **)(*DAT_0048083c + 0x144))
(DAT_00471140 - 210.0,
((float)*(int *)(param_1 + 0x18) + *(float *)(param_1 + 0x1c)) - 18.0,
The recovered source matches all 153 native instructions (515 code bytes;
references 18/0/0). It rejects disabled rendering and null strings, snaps the
origin to integral pixels, lazily resolves and binds GRIM_Font2, forces filter
mode 1 for the batch, and restores the previous 16-byte config value afterward.
Each non-newline byte maps to a glyph width and an inset UV rectangle before a
16-pixel-high quad is emitted; newlines reset x and advance y by 16 pixels.
The native x87 ordering also reveals a two-stage endpoint construction: the
atlas endpoint is first formed from width and row height, then both components
are inset by 1/512. Expressing those as ordinary two-float values reproduces
the compiler spill and all four UV arguments without artificial volatility or
unused state.
grim.dll body:
if ((DAT_10053070 != -1) ||
(DAT_10053070 = (**(code **)(*in_ECX + 0xc0))(s_GRIM_Font2_10053c3c), DAT_10053070 != -1)) {
(**(code **)(*in_ECX + 0xc4))(DAT_10053070,0);
uVar3 = (uint)(byte)(&DAT_1005a570)[(byte)text[iVar5]];
(**(code **)(*in_ECX + 0x100))
((float)(&DAT_1005b2c8)[uVar3 * 2] + 0.001953125,
(float)(&DAT_1005b2cc)[uVar3 * 2] + 0.001953125,
((float)*(byte *)((int)&DAT_1005bad8 + uVar3) * 0.00390625 +
(float)(&DAT_1005b2c8)[uVar3 * 2] + 0.001953125) - 0.001953125,
((float)(&DAT_1005b2cc)[uVar3 * 2] + 0.001953125 + 0.0625) - 0.001953125);
0x148 — grim_draw_text_small_fmt @ 0x10009980¶
- Confirmed name:
draw_text_small_fmt - Confirmed C++ signature:
void __cdecl IGrim2D::draw_text_small_fmt(float x, float y, char *fmt, ...) - Notes: the varargs member uses the cdecl member ABI, so
selfis an explicit first stack argument and the function returns with plainret. It passes the argument tail beginning afterfmtto the importedvsprintf, writes intogrim_printf_buffer_alt, then dispatches(x, y, buffer)through vtable slot0x144. - Ghidra signature:
void grim_draw_text_small_fmt(IGrim2D *self, float x, float y, char *fmt, ...) - Call sites: 86 (unique funcs: 15)
- Sample calls: demo_trial_overlay_render (
FUN_004047c0):L3140; demo_trial_overlay_render (FUN_004047c0):L3193; demo_trial_overlay_render (FUN_004047c0):L3197; demo_trial_overlay_render (FUN_004047c0):L3201; demo_trial_overlay_render (FUN_004047c0):L3206; demo_trial_overlay_render (FUN_004047c0):L3211; demo_trial_overlay_render (FUN_004047c0):L3214; demo_trial_overlay_render (FUN_004047c0):L3217 - First callsite: demo_trial_overlay_render (
FUN_004047c0) (line 3140)
piStack_1ac = DAT_0048083c;
fStack_1b0 = 5.903715e-39;
(**(code **)(*DAT_0048083c + 0x148))();
pcStack_1a0 = (char *)uStack_8;
fStack_1a4 = 1.0;
grim.dll body:
vsprintf(&DAT_1005b078,in_stack_00000010,&stack0x00000014);
(**(code **)(*(int *)x + 0x144))(y,fmt,&DAT_1005b078);
The recovered VC6.5 source matches all 16 instructions and all 3 masked
references. The reference audit independently identifies both uses of
grim_printf_buffer_alt and the vsprintf IAT slot at 0x1004c0b4; no
address-only placeholder is accepted.
0x14c — grim_measure_text_width @ 0x100096c0¶
- Confirmed name:
measure_text_width - Confirmed C++ signature:
int grim_measure_text_width(char *text) - Result: maximum line width, or zero for a null pointer
- Call sites: 14 (unique funcs: 10)
- Sample calls: tutorial_prompt_dialog (
FUN_00408530):L5007; bonus_render (FUN_004295f0):L18761; FUN_0042fd00:L20753; ui_checkbox_update (FUN_0043dc80):L26810; ui_menu_item_update:L27164; ui_button_update (FUN_0043e830):L27225; ui_text_input_update (FUN_0043ecf0):L27429; ui_list_widget_update (FUN_0043efc0):L27482 - First callsite: tutorial_prompt_dialog (
FUN_00408530) (line 5007)
float fStack_8;
iVar3 = (**(code **)(*DAT_0048083c + 0x14c))();
iVar6 = 1;
fStack_4c = 5.925313e-39;
The recovered source matches all 45 native instructions (98 code bytes; references
2/0/0). It scans the string once, accumulates mapped glyph widths, commits the
running maximum at each newline, and performs one final maximum update for the
last line. Declaration order naturally reproduces VC6's accumulator registers.
grim.dll body:
if (text[iVar6] == 10) {
if (iVar2 < iVar4) {
iVar2 = iVar4;
}
iVar4 = 0;
}
else {
iVar4 = iVar4 + (uint)*(byte *)((int)&DAT_1005bad8 +
(uint)(byte)(&DAT_1005a570)[(byte)text[iVar6]]);
}
grim.dll — joystick lifecycle @ 0x1000a110..0x1000a36d¶
- Confirmed callbacks:
grim_joystick_enum_device(0x1000a110) andgrim_joystick_configure_axis(0x1000a150) - Confirmed lifecycle:
grim_joystick_init(0x1000a1c0),grim_joystick_poll(0x1000a2b0), andgrim_joystick_shutdown(0x1000a330) - Confirmed lifecycle signatures:
bool grim_joystick_init(HWND hwnd),bool grim_joystick_poll(void), andvoid grim_joystick_shutdown(void)
Live Binary Ninja exposes the standard DirectInput source shape. Initialization
creates DirectInput8 when needed, enumerates attached game controllers, and
stops after the first device is created. It installs c_dfDIJoystick2, sets
cooperative flags 5, enumerates device objects, acquires the device, and
performs an initial poll. The axis callback applies DIPROP_RANGE by object ID
to every object whose type has an axis bit, using [-1000, 1000]; property
failure stops object enumeration. The null-window fallback calls
GetDesktopWindow without retaining its result, matching the keyboard and
mouse initializers.
Polling calls Poll first. A failure enters an Acquire loop only while the
result is DIERR_INPUTLOST, then returns false for that frame; a successful
poll reads the full 272-byte DIJOYSTATE2 and reports the GetDeviceState
HRESULT. Shutdown unacquires/releases the device and then releases the parent
DirectInput interface, nulling both globals.
Natural C++ reproduces all 183 instructions across the five functions, with
full prefixes and aggregate references 33/0/0 under the stock VC6.5 profile.
No inline assembly, volatile shaping, fake references, or forced addresses are
used.
grim.dll — keyboard init helper @ 0x1000a390¶
- Confirmed name:
grim_keyboard_init - Confirmed C++ signature:
bool grim_keyboard_init(HWND hwnd) - Notes: lazily creates the DirectInput8 interface and system-keyboard device, installs a ten-event buffer, acquires it, and performs an initial poll.
Live Binary Ninja identifies a 272-byte function. The recovered ordinary C++
uses IID_IDirectInput8A, GUID_SysKeyboard, c_dfDIKeyboard, cooperative
flags 0x16, and a 20-byte device-scoped dword property with value 10. It
reproduces all 89 native instructions, full prefix, and references 18/0/0.
The unusual null-window fallback is also preserved: GetDesktopWindow is
called but its return value is not assigned to the original argument.
grim.dll — keyboard poll helper @ 0x1000a4a0¶
- Confirmed name:
grim_keyboard_poll - Confirmed C++ signature:
bool grim_keyboard_poll(void) - Notes: acquires the DirectInput keyboard, snapshots its 256-byte state, and applies the returned 20-byte buffered events.
Live Binary Ninja identifies a 165-byte function. It retries Acquire only
for DIERR_INPUTLOST and DIERR_NOTACQUIRED, ignores the GetDeviceState
HRESULT, and gates event application on the GetDeviceData HRESULT and a
positive signed count. The native loop trusts both the returned count and each
event offset. Ordinary C++ reproduces all 60 native instructions, full prefix,
and references 9/0/0 with the stock MSVC 6.5 profile.
grim.dll — keyboard shutdown helper @ 0x1000a550¶
- Confirmed name:
grim_keyboard_shutdown - Confirmed C++ signature:
void grim_keyboard_shutdown(void) - Notes: unacquires and releases the keyboard device, nulls it, then releases and nulls the parent DirectInput interface.
Live Binary Ninja identifies a 62-byte function with COM vtable calls at
offsets 0x20 (Unacquire) and 0x08 (Release). Ordinary C++ reproduces
all 19 native instructions, full prefix, and references 5/0/0 with MSVC 6.5.
grim.dll — mouse init helper @ 0x1000a5a0¶
- Confirmed name:
grim_mouse_init - Confirmed C++ signature:
bool grim_mouse_init(void) - Notes: lazily creates the DirectInput8 interface and system-mouse device, installs the Mouse2 data format, acquires it, and performs an initial poll.
Live Binary Ninja identifies a 194-byte function using IID_IDirectInput8A,
GUID_SysMouse, c_dfDIMouse2, and cooperative flags 5. Ordinary C++
reproduces all 71 instructions, full prefix, and references 18/0/0. As in
keyboard initialization, the null-window GetDesktopWindow result is ignored.
grim.dll — mouse poll helper @ 0x1000a670¶
- Confirmed name:
grim_mouse_poll - Confirmed C++ signature:
bool grim_mouse_poll(void) - Notes: drains relative DirectInput state into per-frame deltas and cumulative position, then reacquires after failed state queries.
Live Binary Ninja identifies a 351-byte function. It clears the three frame
deltas and the 20-byte DIMOUSESTATE2, reads one state, then keeps clearing and
reading while any of X, Y, or wheel motion remains. The post-incremented guard
means samples numbered 0 through 100 are accumulated, for a maximum of 101
reads. More than two processed samples invoke the no-op diagnostic with
"stall" and the sample count.
A failed state read skips cumulative-position updates, calls Acquire once,
and repeats only while the result is DIERR_INPUTLOST; the function still
returns true. Successful samples update cumulative X/Y and the newly proven
float at 0x1005db4c, now named grim_mouse_wheel.
Ordinary C++ reproduces all 90 native instructions and all 42 masked references under the stock MSVC 6.5 profile. Resetting the frame deltas before zeroing the device state is semantically natural and reproduces the native instruction schedule without volatility or artificial state.
grim.dll — mouse shutdown helper @ 0x1000a7d0¶
- Confirmed name:
grim_mouse_shutdown - Confirmed C++ signature:
void grim_mouse_shutdown(void) - Notes: unacquires and releases the mouse device, nulls it, then releases and nulls the parent DirectInput interface.
Live Binary Ninja identifies the same 62-byte lifecycle shape as keyboard
shutdown, using vtable offsets 0x20 and 0x08. The natural mouse-specific
source reproduces all 19 instructions, full prefix, and references 5/0/0.
grim.dll — coordinate space conversion helper @ 0x10016944¶
- Provisional name:
grim_convert_vertex_space(medium) - Guess:
float *grim_convert_vertex_space(void *this, float *src) - Uses
this+0x1058as a vertex count and writes vec4s tothis+0x104c. -
Remaps coordinates between three space modes (
this+8andthis+0x1048) with explicit[-1, 1]↔[0, 1]conversions ((v + 1) * 0.5andv * 2 - 1). -
Inferred mapping:
- Mode 1: xyz in
[-1, 1], w in[-1, 1]. - Mode 2: xyz in
[0, 1], w in[-1, 1]. - Mode 3: xyz in
[0, 1], w in[0, 1]. - Mode 1↔2 converts xyz only; any conversion involving mode 3 also remaps w.
- Constructors feeding the mode parameter:
-
Mode 2: vtables
PTR_FUN_1004ccd0,PTR_FUN_1004cce0,PTR_FUN_1004ccf0,PTR_FUN_1004cd10,PTR_FUN_1004cd20,PTR_FUN_1004cd30(param2 is0x10or0x20). -
Mode 3: only
PTR_FUN_1004cd00(param20x20). - Mode 1: remaining constructors (
PTR_FUN_1004cb6c..PTR_FUN_1004cd80) using param2 values0,8,0x10,0x18,0x20,0x30,0x40.
grim.dll — pixel format init helper @ 0x100170f9¶
- Provisional name:
grim_pixel_format_init(low) - Notes:
-
Called by many format-specific constructors;
param_2 >> 3is stored as bytes-per-pixel (this+0x1068) and used to derive buffer pitches and sizes. -
param_3is stored atthis+8and later compared tothis+0x1048to decide whethergrim_convert_vertex_spaceshould run. -
param_1appears to be a descriptor block: width/height/stride fields copy intothis+0x1030..0x1044, and palette data (if present atparam_1[0x12]) is expanded into a 0x400-byte RGBA table atthis+0x34.
Constructor mapping (observed):
| Ctor addr | Vtable | bpp param | Mode |
|---|---|---|---|
0x1001a428 |
PTR_FUN_1004cb6c |
0x18 |
1 |
0x1001a53c |
PTR_FUN_1004cb8c |
0x20 |
1 |
0x1001a558 |
PTR_FUN_1004cb9c |
0x20 |
1 |
0x1001a579 |
PTR_FUN_1004cbac |
0x10 |
1 |
0x1001a781 |
PTR_FUN_1004cbdc |
0x10 |
1 |
0x1001a79d |
PTR_FUN_1004cbec |
0x10 |
1 |
0x1001aa8a |
PTR_FUN_1004cc10 |
0x10 |
1 |
0x1001aaa6 |
PTR_FUN_1004cc20 |
0x8 |
1 |
0x1001aac2 |
PTR_FUN_1004cc30 |
0x8 |
1 |
0x1001aade |
PTR_FUN_1004cc40 |
0x10 |
1 |
0x1001aafa |
PTR_FUN_1004cc50 |
0x10 |
1 |
0x1001aba3 |
PTR_FUN_1004cc60 |
0x20 |
1 |
0x1001ac2e |
PTR_FUN_1004cc70 |
0x20 |
1 |
0x1001ac4a |
PTR_FUN_1004caf8 |
0x0 |
1 |
0x1001ae3c |
PTR_FUN_1004cc80 |
0x10 |
1 |
0x1001ae74 |
PTR_FUN_1004cc90 |
0x8 |
1 |
0x1001ae90 |
PTR_FUN_1004cca0 |
0x8 |
1 |
0x1001aeac |
PTR_FUN_1004ccb0 |
0x10 |
1 |
0x1001aee4 |
PTR_FUN_1004ccc0 |
0x8 |
1 |
0x1001b001 |
PTR_FUN_1004ccd0 |
0x10 |
2 |
0x1001b01d |
PTR_FUN_1004cce0 |
0x10 |
2 |
0x1001b039 |
PTR_FUN_1004ccf0 |
0x20 |
2 |
0x1001b055 |
PTR_FUN_1004cd00 |
0x20 |
3 |
0x1001b071 |
PTR_FUN_1004cd10 |
0x20 |
2 |
0x1001b08d |
PTR_FUN_1004cd20 |
0x20 |
2 |
0x1001b0a9 |
PTR_FUN_1004cd30 |
0x20 |
2 |
0x1001b0c5 |
PTR_FUN_1004cd40 |
0x10 |
1 |
0x1001b0e1 |
PTR_FUN_1004cd50 |
0x10 |
1 |
0x1001b3a6 |
PTR_FUN_1004cd60 |
0x20 |
1 |
0x1001b3c2 |
PTR_FUN_1004cd70 |
0x30 |
1 |
0x1001b3de |
PTR_FUN_1004cd80 |
0x40 |
1 |