paavohtl Claude Opus 4.6 (1M context) commited on
Commit
c39b4d4
·
1 Parent(s): 368f9b1

feat: port DDDisplay set_camera_offset, set_clip_rect, fill_rect to Rust

Browse files

Three new DDDisplay vtable methods ported plus a shared
flush_render_lock helper. All use the typed DDDisplayWrapper
fastcall dispatch for rendering operations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

crates/openwa-core/src/display/dd_display.rs CHANGED
@@ -325,3 +325,174 @@ pub unsafe extern "thiscall" fn flush_render(this: *mut DDDisplay) {
325
  let mut buf = FastcallResult::default();
326
  DDDisplayWrapper::get_renderer_surface_raw(wrapper, &mut buf);
327
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
325
  let mut buf = FastcallResult::default();
326
  DDDisplayWrapper::get_renderer_surface_raw(wrapper, &mut buf);
327
  }
328
+
329
+ /// Port of DDDisplay::SetCameraOffset (vtable slot 27, 0x56CC40).
330
+ ///
331
+ /// Converts Fixed-point camera coordinates to pixel integers and stores them.
332
+ ///
333
+ /// # Safety
334
+ /// `this` must be a valid `*mut DDDisplay` (actually a `*mut DisplayGfx`).
335
+ pub unsafe extern "thiscall" fn set_camera_offset(this: *mut DDDisplay, x: Fixed, y: Fixed) {
336
+ let gfx = this as *mut DisplayGfx;
337
+ (*gfx).camera_x = x.to_int();
338
+ (*gfx).camera_y = y.to_int();
339
+ }
340
+
341
+ /// Port of DDDisplay::SetClipRect (vtable slot 28, 0x56CC60).
342
+ ///
343
+ /// Converts Fixed-point clip rectangle to pixel integers, clamps to display
344
+ /// dimensions, stores in DisplayBase, and mirrors to the layer_0 object.
345
+ ///
346
+ /// # Safety
347
+ /// `this` must be a valid `*mut DDDisplay` (actually a `*mut DisplayGfx`).
348
+ /// `layer_0` (at +0x3D9C) must be initialized.
349
+ pub unsafe extern "thiscall" fn set_clip_rect(
350
+ this: *mut DDDisplay,
351
+ x1: Fixed,
352
+ y1: Fixed,
353
+ x2: Fixed,
354
+ y2: Fixed,
355
+ ) {
356
+ let gfx = this as *mut DisplayGfx;
357
+ let base = &mut (*gfx).base;
358
+
359
+ // Convert Fixed to pixel integers
360
+ let mut cx1 = x1.to_int();
361
+ let mut cy1 = y1.to_int();
362
+ let mut cx2 = x2.to_int();
363
+ let mut cy2 = y2.to_int();
364
+
365
+ // Store and clamp to display dimensions
366
+ base.clip_x1 = cx1;
367
+ base.clip_y1 = cy1;
368
+ base.clip_x2 = cx2;
369
+ base.clip_y2 = cy2;
370
+
371
+ if cx1 < 0 {
372
+ base.clip_x1 = 0;
373
+ cx1 = 0;
374
+ }
375
+ if cy1 < 0 {
376
+ base.clip_y1 = 0;
377
+ cy1 = 0;
378
+ }
379
+ if cx2 > base.display_width as i32 {
380
+ base.clip_x2 = base.display_width as i32;
381
+ cx2 = base.display_width as i32;
382
+ }
383
+ if cy2 > base.display_height as i32 {
384
+ base.clip_y2 = base.display_height as i32;
385
+ cy2 = base.display_height as i32;
386
+ }
387
+
388
+ // Mirror clip rect to layer_0 object (0x4C-byte layer context).
389
+ // Layout: +0x14 = width, +0x18 = height, +0x1C-0x28 = clip rect.
390
+ let layer = (*gfx).layer_0 as *mut u8;
391
+ let layer_w = *(layer.add(0x14) as *const i32);
392
+ let layer_h = *(layer.add(0x18) as *const i32);
393
+
394
+ *(layer.add(0x1C) as *mut i32) = cx1;
395
+ *(layer.add(0x20) as *mut i32) = cy1;
396
+ *(layer.add(0x24) as *mut i32) = cx2;
397
+ *(layer.add(0x28) as *mut i32) = cy2;
398
+
399
+ if cx1 < 0 {
400
+ *(layer.add(0x1C) as *mut i32) = 0;
401
+ }
402
+ if cy1 < 0 {
403
+ *(layer.add(0x20) as *mut i32) = 0;
404
+ }
405
+ if cx2 > layer_w {
406
+ *(layer.add(0x24) as *mut i32) = layer_w;
407
+ }
408
+ if cy2 > layer_h {
409
+ *(layer.add(0x28) as *mut i32) = layer_h;
410
+ }
411
+ }
412
+
413
+ /// Port of the render-lock flush helper at 0x56A330.
414
+ ///
415
+ /// If the render lock is held, releases it by calling
416
+ /// DDDisplayWrapper::unlock_surface_write (slot 18), then clears the flag.
417
+ /// Called by drawing methods (fill_rect, draw_line, etc.) before rendering.
418
+ ///
419
+ /// Original uses ESI = this (usercall).
420
+ ///
421
+ /// # Safety
422
+ /// `gfx` must be a valid `*mut DisplayGfx` with `layer_0` initialized.
423
+ /// `g_DDDisplayWrapper` must be initialized.
424
+ unsafe fn flush_render_lock(gfx: *mut DisplayGfx) {
425
+ if (*gfx).render_lock != 0 {
426
+ let wrapper = *(rb(va::G_DD_DISPLAY_WRAPPER) as *const *mut DDDisplayWrapper);
427
+ let layer = (*gfx).layer_0 as *mut u8;
428
+ let data = *(layer.add(0x08) as *const u32);
429
+ let mut buf = FastcallResult::default();
430
+ DDDisplayWrapper::unlock_surface_write_raw(wrapper, &mut buf, data);
431
+ (*gfx).render_lock = 0;
432
+ }
433
+ }
434
+
435
+ /// Port of DDDisplay::FillRect (vtable slot 18, 0x56B810).
436
+ ///
437
+ /// Fills a rectangle with a solid color. Pixel-integer coordinates are
438
+ /// offset by the camera position and clipped to the clip rect before
439
+ /// dispatching to DDDisplayWrapper::fill_rect (slot 19).
440
+ ///
441
+ /// # Safety
442
+ /// `this` must be a valid `*mut DDDisplay` (actually a `*mut DisplayGfx`).
443
+ /// `g_DDDisplayWrapper` must be initialized.
444
+ pub unsafe extern "thiscall" fn fill_rect(
445
+ this: *mut DDDisplay,
446
+ x1: i32,
447
+ y1: i32,
448
+ x2: i32,
449
+ y2: i32,
450
+ color: u32,
451
+ ) {
452
+ let gfx = this as *mut DisplayGfx;
453
+ let base = &(*gfx).base;
454
+
455
+ // Apply camera offset
456
+ let mut left = x1 + (*gfx).camera_x;
457
+ let mut top = y1 + (*gfx).camera_y;
458
+ let mut right = x2 + (*gfx).camera_x;
459
+ let mut bottom = y2 + (*gfx).camera_y;
460
+
461
+ // Early-out: no intersection with clip rect
462
+ if right <= base.clip_x1
463
+ || bottom <= base.clip_y1
464
+ || left >= base.clip_x2
465
+ || top >= base.clip_y2
466
+ {
467
+ return;
468
+ }
469
+
470
+ // Clamp to clip rect
471
+ if left < base.clip_x1 {
472
+ left = base.clip_x1;
473
+ }
474
+ if top < base.clip_y1 {
475
+ top = base.clip_y1;
476
+ }
477
+ if right > base.clip_x2 {
478
+ right = base.clip_x2;
479
+ }
480
+ if bottom > base.clip_y2 {
481
+ bottom = base.clip_y2;
482
+ }
483
+
484
+ flush_render_lock(gfx);
485
+
486
+ // DDDisplayWrapper::fill_rect takes (x, y, width, height, color)
487
+ let wrapper = *(rb(va::G_DD_DISPLAY_WRAPPER) as *const *mut DDDisplayWrapper);
488
+ let mut buf = FastcallResult::default();
489
+ DDDisplayWrapper::fill_rect_raw(
490
+ wrapper,
491
+ &mut buf,
492
+ left,
493
+ top,
494
+ right - left,
495
+ bottom - top,
496
+ color,
497
+ );
498
+ }
crates/openwa-dll/src/replacements/display.rs CHANGED
@@ -81,10 +81,13 @@ pub fn install() -> Result<(), String> {
81
 
82
  // Patch DDDisplay vtable (0x66A218): replace ported methods with Rust.
83
  vtable_replace!(DDDisplayVtable, va::DD_DISPLAY_VTABLE, {
84
- get_dimensions => dd_display::get_dimensions,
85
- flush_render => dd_display::flush_render,
 
 
 
86
  })?;
87
- let _ = log_line("[Display] DDDisplay: patched get_dimensions + flush_render → Rust");
88
  }
89
 
90
  Ok(())
 
81
 
82
  // Patch DDDisplay vtable (0x66A218): replace ported methods with Rust.
83
  vtable_replace!(DDDisplayVtable, va::DD_DISPLAY_VTABLE, {
84
+ get_dimensions => dd_display::get_dimensions,
85
+ fill_rect => dd_display::fill_rect,
86
+ flush_render => dd_display::flush_render,
87
+ set_camera_offset => dd_display::set_camera_offset,
88
+ set_clip_rect => dd_display::set_clip_rect,
89
  })?;
90
+ let _ = log_line("[Display] DDDisplay: patched 5 methods → Rust");
91
  }
92
 
93
  Ok(())