This commit is contained in:
xyou
2026-07-31 13:53:49 +08:00
parent 5ea84fac97
commit f52157b7af
241 changed files with 26843 additions and 8155 deletions
+438
View File
@@ -0,0 +1,438 @@
//! Deaicup Software — Windows PE64 entry point.
//!
//! The original application runs on eframe/egui. For the Deshab UTSM PE
//! compatibility layer we keep the egui UI logic but replace the eframe
//! runtime with a minimal hand-written Win32 backend:
//! user32 window + message pump -> egui RawInput events
//! egui tessellation -> SoftRaster (pure Rust, raster.rs)
//! 32bpp DIB backbuffer -> GDI BitBlt onto the window DC
//!
//! No CRT: the PE entry is `mainCRTStartup` (see .cargo/config.toml) and the
//! process ends via ExitProcess.
#![no_main]
#![allow(static_mut_refs)]
mod raster;
#[allow(dead_code)]
mod rt;
mod win32;
use egui::{self, Color32, Context, Event, Key, Modifiers, PointerButton, Pos2, Rect};
use raster::SoftRaster;
use win32::*;
/* ============================================================
* UI 逻辑(移植自原 eframe 应用 CODE/Deaicup Software/src)
* ========================================================== */
#[derive(Debug, PartialEq, Clone, Copy, Default)]
enum Page {
#[default]
Home,
Tool,
Game,
LinuxWare,
WindowsWare,
Set,
About,
}
#[derive(Default)]
struct DeaicupSoftware {
current_page: Page,
}
impl DeaicupSoftware {
fn ui(&mut self, ctx: &Context) {
// 原应用顶部是远程图片 https://deaicup.com/.../top.png。
// PE 环境无网络,用标题栏替代,保留 75px 高度布局。
egui::TopBottomPanel::top("top_image_panel").show(ctx, |ui| {
ui.set_min_height(75.0);
ui.vertical_centered(|ui| {
ui.add_space(15.0);
ui.label(egui::RichText::new("Deaicup Software").size(30.0).strong());
});
});
// main
egui::SidePanel::left("navigation_panel")
.default_width(200.0)
.resizable(true)
.show(ctx, |ui| {
ui.heading("Menu");
ui.separator();
ui.vertical(|ui| {
if ui.selectable_label(self.current_page == Page::Home, "HOME").clicked() {
self.current_page = Page::Home;
}
if ui.selectable_label(self.current_page == Page::Tool, "TOOL").clicked() {
self.current_page = Page::Tool;
}
if ui.selectable_label(self.current_page == Page::Game, "GAME").clicked() {
self.current_page = Page::Game;
}
if ui
.selectable_label(self.current_page == Page::LinuxWare, "LINUXWARE")
.clicked()
{
self.current_page = Page::LinuxWare;
}
if ui
.selectable_label(self.current_page == Page::WindowsWare, "WINDOWSWARE")
.clicked()
{
self.current_page = Page::WindowsWare;
}
if ui.selectable_label(self.current_page == Page::Set, "SET").clicked() {
self.current_page = Page::Set;
}
if ui
.selectable_label(self.current_page == Page::About, "ABOUT")
.clicked()
{
self.current_page = Page::About;
}
})
});
egui::CentralPanel::default().show(ctx, |ui| {
ui.separator();
match self.current_page {
Page::Home => ui.label("Home"),
Page::Tool => ui.label("Tool"),
Page::Game => ui.label("Game"),
Page::LinuxWare => ui.label("LinuxWare"),
Page::WindowsWare => ui.label("WindowsWare"),
Page::Set => ui.label("Set"),
Page::About => ui.label("About"),
}
});
}
}
/* ============================================================
* 全局输入状态(单线程:wnd_proc 只被 DispatchMessageW 同步调用)
* ========================================================== */
static mut EVENTS: Vec<Event> = Vec::new();
static mut POINTER: Pos2 = Pos2::ZERO;
static mut MOD_ALT: bool = false;
static mut MOD_CTRL: bool = false;
static mut MOD_SHIFT: bool = false;
static mut QUIT: bool = false;
fn current_modifiers() -> Modifiers {
unsafe {
Modifiers {
alt: MOD_ALT,
ctrl: MOD_CTRL,
shift: MOD_SHIFT,
mac_cmd: false,
command: MOD_CTRL,
}
}
}
#[inline]
fn lparam_x(l: LPARAM) -> i32 {
(l as i32 & 0xFFFF) as i16 as i32
}
#[inline]
fn lparam_y(l: LPARAM) -> i32 {
((l as i32 >> 16) & 0xFFFF) as i16 as i32
}
fn push_pointer_button(button: PointerButton, pressed: bool, l: LPARAM) -> LRESULT {
unsafe {
POINTER = Pos2::new(lparam_x(l) as f32, lparam_y(l) as f32);
EVENTS.push(Event::PointerButton {
pos: POINTER,
button,
pressed,
modifiers: current_modifiers(),
});
}
0
}
fn vk_to_egui_key(vk: usize) -> Option<Key> {
// 字符键不需要映射为 egui::Key —— 文本输入走 WM_CHAR -> Event::Text。
Some(match vk {
VK_LEFT => Key::ArrowLeft,
VK_UP => Key::ArrowUp,
VK_RIGHT => Key::ArrowRight,
VK_DOWN => Key::ArrowDown,
VK_BACK => Key::Backspace,
VK_DELETE => Key::Delete,
VK_RETURN => Key::Enter,
VK_TAB => Key::Tab,
VK_ESCAPE => Key::Escape,
VK_SPACE => Key::Space,
_ => return None,
})
}
fn on_key(vk: usize, pressed: bool) -> LRESULT {
unsafe {
match vk {
VK_SHIFT => MOD_SHIFT = pressed,
VK_CONTROL => MOD_CTRL = pressed,
VK_MENU => MOD_ALT = pressed,
_ => {}
}
if let Some(key) = vk_to_egui_key(vk) {
EVENTS.push(Event::Key {
key,
physical_key: None,
pressed,
repeat: false,
modifiers: current_modifiers(),
});
}
}
0
}
extern "system" fn wnd_proc(hwnd: HWND, msg: u32, w: WPARAM, l: LPARAM) -> LRESULT {
unsafe {
match msg {
WM_CLOSE | WM_DESTROY => {
QUIT = true;
PostQuitMessage(0);
0
}
WM_MOUSEMOVE => {
POINTER = Pos2::new(lparam_x(l) as f32, lparam_y(l) as f32);
EVENTS.push(Event::PointerMoved(POINTER));
0
}
WM_LBUTTONDOWN => push_pointer_button(PointerButton::Primary, true, l),
WM_LBUTTONUP => push_pointer_button(PointerButton::Primary, false, l),
WM_RBUTTONDOWN => push_pointer_button(PointerButton::Secondary, true, l),
WM_RBUTTONUP => push_pointer_button(PointerButton::Secondary, false, l),
WM_MBUTTONDOWN => push_pointer_button(PointerButton::Middle, true, l),
WM_MBUTTONUP => push_pointer_button(PointerButton::Middle, false, l),
WM_KEYDOWN => on_key(w, true),
WM_KEYUP => on_key(w, false),
WM_CHAR => {
if let Some(c) = char::from_u32(w as u32) {
if c >= ' ' && c != '\x7f' {
EVENTS.push(Event::Text(c.to_string()));
}
}
0
}
WM_ERASEBKGND => 1, // 背景由 egui 全量绘制
_ => DefWindowProcW(hwnd, msg, w, l),
}
}
}
/* ============================================================
* 计时
* ========================================================== */
fn now_secs() -> f64 {
unsafe {
static mut FREQ: i64 = 0;
if FREQ == 0 {
QueryPerformanceFrequency(&mut FREQ);
if FREQ == 0 {
FREQ = 1000;
}
}
let mut c: i64 = 0;
QueryPerformanceCounter(&mut c);
c as f64 / FREQ as f64
}
}
/* ============================================================
* PE 入口
* ========================================================== */
/// ExitProcess wrapper: restore the kernel GS base before leaving.
fn exit_process(code: u32) -> ! {
unsafe {
rt::restore_tls();
ExitProcess(code);
}
}
#[no_mangle]
pub extern "C" fn mainCRTStartup() {
unsafe {
// Fake TEB/TLS must precede any std facility that might touch
// thread locals (parking_lot, std::io, etc.).
rt::install_fake_tls();
}
log("[deaicup] mainCRTStartup\n");
unsafe {
let hinst = GetModuleHandleW(core::ptr::null());
let class_name = wide("DeaicupWindow");
let wc = WNDCLASSEXW {
cbSize: core::mem::size_of::<WNDCLASSEXW>() as u32,
style: 0,
lpfnWndProc: wnd_proc,
cbClsExtra: 0,
cbWndExtra: 0,
hInstance: hinst,
hIcon: 0,
hCursor: LoadCursorW(0, IDC_ARROW),
hbrBackground: 0,
lpszMenuName: core::ptr::null(),
lpszClassName: class_name.as_ptr(),
hIconSm: 0,
};
RegisterClassExW(&wc);
// 屏幕尺寸(shim 报 framebuffer 尺寸),窗口 800x600,超出则收缩
let sw = GetSystemMetrics(SM_CXSCREEN);
let sh = GetSystemMetrics(SM_CYSCREEN);
let mut ww = 800;
let mut wh = 600;
if sw > 0 && ww > sw {
ww = sw;
}
if sh > 0 && wh > sh {
wh = sh;
}
let title = wide("Deaicup Software");
let hwnd = CreateWindowExW(
0,
class_name.as_ptr(),
title.as_ptr(),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
ww,
wh,
0,
0,
hinst,
core::ptr::null_mut(),
);
if hwnd == 0 {
log("[deaicup] CreateWindowExW failed\n");
exit_process(1);
}
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
// 32bpp top-down DIB 后台缓冲
let hdc = GetDC(hwnd);
let memdc = CreateCompatibleDC(hdc);
let mut bmi = BITMAPINFO::default();
bmi.bmiHeader.biSize = core::mem::size_of::<BITMAPINFOHEADER>() as u32;
bmi.bmiHeader.biWidth = ww;
bmi.bmiHeader.biHeight = -wh; // top-down
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
let mut bits: *mut core::ffi::c_void = core::ptr::null_mut();
let hbmp = CreateDIBSection(memdc, &bmi, DIB_RGB_COLORS, &mut bits, 0, 0);
if hbmp == 0 || bits.is_null() {
log("[deaicup] CreateDIBSection failed\n");
exit_process(1);
}
SelectObject(memdc, hbmp);
log("[dc] SelectObject ok\n");
/* 二分探针:逐项验证 std 设施,定位 Context::default 崩溃点 */
{
let mut v: Vec<u8> = Vec::with_capacity(16);
for i in 0..64u8 { v.push(i); }
log("[dc] vec ok\n");
let s = String::from("hello");
let _ = s.len();
log("[dc] string ok\n");
let mut h: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
h.insert(1, 2); h.insert(3, 4);
let _ = h.get(&1);
log("[dc] hashmap ok\n");
let a = std::sync::Arc::new(42u32);
let b = a.clone();
let _ = *b;
log("[dc] arc ok\n");
let m = std::sync::Mutex::new(7u32);
*m.lock().unwrap() = 8;
log("[dc] mutex ok\n");
}
let ctx = Context::default();
log("[dc] Context::default ok\n");
ctx.set_pixels_per_point(1.0);
let mut app = DeaicupSoftware::default();
let mut raster = SoftRaster::new();
let mut msg = MSG::default();
let mut frame: u64 = 0;
log("[dc] app+raster ok\n");
while !QUIT {
// 消息泵:把 Win32 消息转成 egui 事件(由 wnd_proc 推入 EVENTS)
while PeekMessageW(&mut msg, 0, 0, 0, PM_REMOVE) != 0 {
if msg.message == WM_QUIT {
QUIT = true;
break;
}
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
if QUIT {
break;
}
if frame < 3 { log("[dc] msgpump ok\n"); }
let events = core::mem::take(&mut *core::ptr::addr_of_mut!(EVENTS));
let had_events = !events.is_empty();
let input = egui::RawInput {
screen_rect: Some(Rect::from_min_size(
Pos2::ZERO,
egui::vec2(ww as f32, wh as f32),
)),
time: Some(now_secs()),
modifiers: current_modifiers(),
events,
..Default::default()
};
if frame < 3 { log("[dc] rawinput ok\n"); }
let full = ctx.run(input, |ctx| app.ui(&ctx));
if frame < 3 { log("[dc] ctx.run ok\n"); }
let repaint_after = full
.viewport_output
.values()
.next()
.map(|v| v.repaint_delay)
.unwrap_or(std::time::Duration::from_millis(16));
raster.apply_delta(&full.textures_delta);
if frame < 3 { log("[dc] apply_delta ok\n"); }
let prims = ctx.tessellate(full.shapes, full.pixels_per_point);
if frame < 3 { log("[dc] tessellate ok\n"); }
let dst = core::slice::from_raw_parts_mut(bits as *mut u32, (ww * wh) as usize);
raster::clear(dst, Color32::from_rgb(0x30, 0x34, 0x3d));
raster.paint(dst, ww, wh, &prims);
if frame < 3 { log("[dc] paint ok\n"); }
BitBlt(hdc, 0, 0, ww, wh, memdc, 0, 0, SRCCOPY);
if frame < 3 { log("[dc] bitblt ok\n"); }
frame += 1;
if frame > 2 && !had_events && repaint_after > std::time::Duration::from_millis(5) {
let ms = repaint_after.as_millis().min(50) as u32;
Sleep(if ms == 0 { 5 } else { ms });
}
}
log("[deaicup] quit, ExitProcess(0)\n");
exit_process(0);
}
}
+235
View File
@@ -0,0 +1,235 @@
//! Pure-Rust software rasterizer for egui tessellated primitives.
//! Renders `ClippedPrimitive` meshes (font atlas + color triangles) into a
//! 32bpp 0xAARRGGBB backbuffer that is later blitted via GDI BitBlt.
use egui::epaint::Primitive;
use egui::{ClippedPrimitive, Color32, Rect, TextureFilter, TextureId, TexturesDelta};
struct Texture {
w: usize,
h: usize,
/// RGBA8, row-major.
rgba: Vec<u8>,
linear: bool,
}
pub struct SoftRaster {
/// Small texture store (font atlas + user images). Vec-based to avoid
/// std HashMap RandomState (which would import an OS RNG API).
textures: Vec<(TextureId, Texture)>,
}
impl SoftRaster {
pub fn new() -> Self {
SoftRaster {
textures: Vec::new(),
}
}
fn get(&self, id: TextureId) -> Option<&Texture> {
self.textures.iter().find(|(tid, _)| *tid == id).map(|(_, t)| t)
}
/// Apply egui's texture updates (font atlas etc).
pub fn apply_delta(&mut self, delta: &TexturesDelta) {
for (id, img_delta) in &delta.set {
let size = img_delta.image.size();
let mut rgba = Vec::with_capacity(size[0] * size[1] * 4);
match &img_delta.image {
egui::ImageData::Color(color) => {
for p in &color.pixels {
rgba.extend_from_slice(&p.to_array());
}
}
egui::ImageData::Font(font) => {
// Coverage -> straight-alpha white texel (255,255,255,a).
for c in font.srgba_pixels(None) {
let a = c.a();
rgba.extend_from_slice(&[255, 255, 255, a]);
}
}
}
let linear = img_delta.options.magnification == TextureFilter::Linear;
let tex = Texture {
w: size[0],
h: size[1],
rgba,
linear,
};
if let Some(slot) = self.textures.iter_mut().find(|(tid, _)| tid == id) {
slot.1 = tex;
} else {
self.textures.push((*id, tex));
}
}
for id in &delta.free {
self.textures.retain(|(tid, _)| tid != id);
}
}
/// Paint all clipped primitives into `dst` (0xAARRGGBB pixels, dw*dh).
pub fn paint(&self, dst: &mut [u32], dw: i32, dh: i32, prims: &[ClippedPrimitive]) {
for cp in prims {
if let Primitive::Mesh(mesh) = &cp.primitive {
let Some(tex) = self.get(mesh.texture_id) else {
continue;
};
let clip = clip_rect_px(&cp.clip_rect, dw, dh);
if clip.2 <= clip.0 || clip.3 <= clip.1 {
continue;
}
for tri in mesh.indices.chunks_exact(3) {
let v0 = &mesh.vertices[tri[0] as usize];
let v1 = &mesh.vertices[tri[1] as usize];
let v2 = &mesh.vertices[tri[2] as usize];
raster_tri(dst, dw, dh, clip, v0, v1, v2, tex);
}
}
}
}
}
/// (x0, y0, x1, y1) integer clip bounds from an egui rect (points == pixels here).
fn clip_rect_px(r: &Rect, dw: i32, dh: i32) -> (i32, i32, i32, i32) {
let x0 = (r.min.x.floor() as i32).clamp(0, dw);
let y0 = (r.min.y.floor() as i32).clamp(0, dh);
let x1 = (r.max.x.ceil() as i32).clamp(0, dw);
let y1 = (r.max.y.ceil() as i32).clamp(0, dh);
(x0, y0, x1, y1)
}
#[inline]
fn edge(ax: f32, ay: f32, bx: f32, by: f32, px: f32, py: f32) -> f32 {
(px - ax) * (by - ay) - (py - ay) * (bx - ax)
}
fn raster_tri(
dst: &mut [u32],
dw: i32,
_dh: i32,
clip: (i32, i32, i32, i32),
v0: &egui::epaint::Vertex,
v1: &egui::epaint::Vertex,
v2: &egui::epaint::Vertex,
tex: &Texture,
) {
let x0 = v0.pos.x;
let y0 = v0.pos.y;
let x1 = v1.pos.x;
let y1 = v1.pos.y;
let x2 = v2.pos.x;
let y2 = v2.pos.y;
let area = edge(x0, y0, x1, y1, x2, y2);
if area.abs() < 1e-6 {
return;
}
let inv_area = 1.0 / area;
let min_x = ((x0.min(x1).min(x2)).floor() as i32).max(clip.0);
let min_y = ((y0.min(y1).min(y2)).floor() as i32).max(clip.1);
let max_x = ((x0.max(x1).max(x2)).ceil() as i32).min(clip.2);
let max_y = ((y0.max(y1).max(y2)).ceil() as i32).min(clip.3);
if min_x >= max_x || min_y >= max_y {
return;
}
for py in min_y..max_y {
let fy = py as f32 + 0.5;
let mut px = min_x;
// Row start barycentrics
let mut w0 = edge(x1, y1, x2, y2, px as f32 + 0.5, fy) * inv_area;
let mut w1 = edge(x2, y2, x0, y0, px as f32 + 0.5, fy) * inv_area;
let mut w2 = 1.0 - w0 - w1;
// Per-pixel increments (dx = +1)
let dw0 = (y1 - y2) * inv_area;
let dw1 = (y2 - y0) * inv_area;
while px < max_x {
if w0 >= 0.0 && w1 >= 0.0 && w2 >= 0.0 {
let u = w0 * v0.uv.x + w1 * v1.uv.x + w2 * v2.uv.x;
let v = w0 * v0.uv.y + w1 * v1.uv.y + w2 * v2.uv.y;
let t = sample(tex, u, v);
let c0 = v0.color;
let c1 = v1.color;
let c2 = v2.color;
let cr = (w0 * c0.r() as f32 + w1 * c1.r() as f32 + w2 * c2.r() as f32) as u32;
let cg = (w0 * c0.g() as f32 + w1 * c1.g() as f32 + w2 * c2.g() as f32) as u32;
let cb = (w0 * c0.b() as f32 + w1 * c1.b() as f32 + w2 * c2.b() as f32) as u32;
let ca = (w0 * c0.a() as f32 + w1 * c1.a() as f32 + w2 * c2.a() as f32) as u32;
// src color = vertex * texture (straight), alpha = va * ta
let sa = (ca * t[3] as u32 + 127) / 255;
if sa != 0 {
let sr = (cr * t[0] as u32 + 127) / 255;
let sg = (cg * t[1] as u32 + 127) / 255;
let sb = (cb * t[2] as u32 + 127) / 255;
let idx = (py * dw + px) as usize;
let d = dst[idx];
let dr = (d >> 16) & 0xFF;
let dg = (d >> 8) & 0xFF;
let db = d & 0xFF;
let inv = 255 - sa;
let or = (sr * sa + dr * inv + 127) / 255;
let og = (sg * sa + dg * inv + 127) / 255;
let ob = (sb * sa + db * inv + 127) / 255;
dst[idx] = 0xFF00_0000 | (or << 16) | (og << 8) | ob;
}
}
px += 1;
w0 += dw0;
w1 += dw1;
w2 = 1.0 - w0 - w1;
}
}
}
#[inline]
fn sample(tex: &Texture, u: f32, v: f32) -> [u8; 4] {
if tex.linear {
sample_linear(tex, u, v)
} else {
sample_nearest(tex, u, v)
}
}
#[inline]
fn sample_nearest(tex: &Texture, u: f32, v: f32) -> [u8; 4] {
let x = (u * tex.w as f32 - 0.5).round() as i32;
let y = (v * tex.h as f32 - 0.5).round() as i32;
texel(tex, x, y)
}
fn sample_linear(tex: &Texture, u: f32, v: f32) -> [u8; 4] {
let fx = u * tex.w as f32 - 0.5;
let fy = v * tex.h as f32 - 0.5;
let x0 = fx.floor() as i32;
let y0 = fy.floor() as i32;
let tx = ((fx - x0 as f32) * 256.0) as i32;
let ty = ((fy - y0 as f32) * 256.0) as i32;
let p00 = texel(tex, x0, y0);
let p10 = texel(tex, x0 + 1, y0);
let p01 = texel(tex, x0, y0 + 1);
let p11 = texel(tex, x0 + 1, y0 + 1);
let mut out = [0u8; 4];
for i in 0..4 {
let top = (p00[i] as i32 * (256 - tx) + p10[i] as i32 * tx) >> 8;
let bot = (p01[i] as i32 * (256 - tx) + p11[i] as i32 * tx) >> 8;
out[i] = ((top * (256 - ty) + bot * ty) >> 8) as u8;
}
out
}
#[inline]
fn texel(tex: &Texture, x: i32, y: i32) -> [u8; 4] {
let x = x.clamp(0, tex.w as i32 - 1) as usize;
let y = y.clamp(0, tex.h as i32 - 1) as usize;
let i = (y * tex.w + x) * 4;
[tex.rgba[i], tex.rgba[i + 1], tex.rgba[i + 2], tex.rgba[i + 3]]
}
/// Clear the whole backbuffer to an opaque color.
pub fn clear(dst: &mut [u32], c: Color32) {
let px = 0xFF00_0000 | ((c.r() as u32) << 16) | ((c.g() as u32) << 8) | c.b() as u32;
dst.fill(px);
}
+600
View File
@@ -0,0 +1,600 @@
//! Minimal CRT / libm runtime for the no-CRT PE64 build.
//!
//! The UTSM PE shim only resolves DLL imports (kernel32/user32/gdi32).
//! Symbols below are *link-time* references emitted by LLVM/std, so they must
//! live inside the PE image itself:
//! - string intrinsics: memcpy / memmove / memset / memcmp
//! - MSVC runtime: __chkstk, _fltused, _tls_index, _tls_used,
//! __CxxFrameHandler3 (never called, panic = abort)
//! - f32 libm: floorf/ceilf/roundf, sinf/cosf, atan2f, powf,
//! exp2f, acosf, cbrtf, _hypotf
//!
//! Math routines are ported from musl-libm (MIT) where accuracy matters
//! (sin/cos/atan) and use simple polynomial/series elsewhere. Visual use by
//! egui only needs ~1e-5 relative accuracy.
//!
//! TLS: Rust std on windows-msvc checks `_tls_used` and (if nonzero) accesses
//! thread locals through gs:[0x58] -> slots[_tls_index]. We are Ring0 with no
//! TEB, so mainCRTStartup installs a fake GS base pointing at a zeroed TLS
//! block (install_fake_tls) and restores it before ExitProcess.
#![allow(non_snake_case)]
use core::arch::asm;
/* ============================================================
* string intrinsics
* ========================================================== */
/// # Safety
/// Standard C contract.
#[no_mangle]
pub unsafe extern "C" fn memcpy(dst: *mut u8, src: *const u8, n: usize) -> *mut u8 {
let orig = dst;
asm!(
"rep movsb",
inout("rcx") n => _,
inout("rsi") src => _,
inout("rdi") dst => _,
options(nostack, preserves_flags)
);
orig
}
/// # Safety
/// Standard C contract.
#[no_mangle]
pub unsafe extern "C" fn memmove(dst: *mut u8, src: *const u8, n: usize) -> *mut u8 {
let d = dst as usize;
let s = src as usize;
if d < s || d >= s.wrapping_add(n) {
memcpy(dst, src, n)
} else {
// Backward copy without touching DF (ABI requires DF=0).
let mut i = n;
while i > 0 {
i -= 1;
core::ptr::write_volatile(dst.add(i), core::ptr::read_volatile(src.add(i)));
}
dst
}
}
/// # Safety
/// Standard C contract.
#[no_mangle]
pub unsafe extern "C" fn memset(dst: *mut u8, val: i32, n: usize) -> *mut u8 {
let orig = dst;
asm!(
"rep stosb",
inout("rcx") n => _,
inout("rdi") dst => _,
in("al") val as u8,
options(nostack, preserves_flags)
);
orig
}
/// # Safety
/// Standard C contract.
#[no_mangle]
pub unsafe extern "C" fn memcmp(a: *const u8, b: *const u8, n: usize) -> i32 {
// Volatile reads keep LLVM from idiom-recognizing this back into memcmp.
for i in 0..n {
let x = core::ptr::read_volatile(a.add(i));
let y = core::ptr::read_volatile(b.add(i));
if x != y {
return x as i32 - y as i32;
}
}
0
}
/* ============================================================
* MSVC runtime bits
* ========================================================== */
/// Stack probe. Our stack is one large committed region (see /STACK flag),
/// so probing pages is unnecessary. An empty body compiles to `ret`,
/// which trivially satisfies the preserve-everything contract.
#[no_mangle]
pub extern "C" fn __chkstk() {}
/// Referenced by std's unwind tables (.pdata personality). With
/// panic = "abort" it is never invoked; loop loudly if it ever is.
#[no_mangle]
pub extern "C" fn __CxxFrameHandler3() -> ! {
loop {
core::hint::spin_loop();
}
}
/// CRT float-usage marker (data symbol only).
#[no_mangle]
pub static _fltused: i32 = 0x9875;
/// TLS slot index of this module. We use slot 0 of the fake TLS array.
#[no_mangle]
pub static _tls_index: u32 = 0;
/// IMAGE_TLS_DIRECTORY64 stand-in (all zeros: no raw template, no callbacks).
/// std only checks that its address is nonzero to decide TLS is supported.
#[no_mangle]
pub static _tls_used: [u8; 0x30] = [0; 0x30];
/* ---- fake TEB / TLS so std thread_local access can't fault ---- */
const IA32_GS_BASE: u32 = 0xC000_0101;
static mut OLD_GS_BASE: u64 = 0;
static mut TLS_READY: bool = false;
fn rdmsr(msr: u32) -> u64 {
let lo: u32;
let hi: u32;
unsafe {
asm!("rdmsr", in("ecx") msr, out("eax") lo, out("edx") hi,
options(nomem, nostack, preserves_flags));
}
((hi as u64) << 32) | lo as u64
}
fn wrmsr(msr: u32, val: u64) {
unsafe {
asm!("wrmsr", in("ecx") msr, in("eax") val as u32,
in("edx") (val >> 32) as u32, options(nomem, nostack, preserves_flags));
}
}
/// Point GS at a minimal fake TEB whose ThreadLocalStoragePointer (0x58)
/// references a zeroed slots array; slot 0 covers `_tls_index == 0`.
/// All const-init thread locals in std are zero-valid (None/0).
///
/// # Safety
/// Ring0 only; call once at PE entry before touching std facilities.
pub unsafe fn install_fake_tls() {
static mut TEB: [u8; 0x100] = [0; 0x100];
static mut SLOTS: [usize; 64] = [0; 64];
static mut TLS_BLOCK: [u8; 8192] = [0; 8192];
if TLS_READY {
return;
}
let teb = core::ptr::addr_of_mut!(TEB);
let slots = core::ptr::addr_of_mut!(SLOTS);
let block = core::ptr::addr_of_mut!(TLS_BLOCK);
(*slots)[0] = block as usize;
*((*teb).as_mut_ptr().add(0x58) as *mut usize) = slots as usize;
OLD_GS_BASE = rdmsr(IA32_GS_BASE);
wrmsr(IA32_GS_BASE, teb as u64);
TLS_READY = true;
}
/// Restore the kernel's GS base (call before ExitProcess).
///
/// # Safety
/// Ring0 only; pair with install_fake_tls.
pub unsafe fn restore_tls() {
if TLS_READY {
wrmsr(IA32_GS_BASE, OLD_GS_BASE);
TLS_READY = false;
}
}
/* ============================================================
* f32 libm
* ========================================================== */
#[inline(always)]
fn asu32(x: f32) -> u32 {
x.to_bits()
}
#[inline(always)]
fn asf32(x: u32) -> f32 {
f32::from_bits(x)
}
#[no_mangle]
pub extern "C" fn floorf(x: f32) -> f32 {
// |x| >= 2^23 is already integral; NaN passes the compare false and
// degrades to 0.0, which egui never feeds us anyway.
if x.abs() >= 8388608.0 {
return x;
}
let i = x as i32; // truncate toward zero
let f = i as f32;
if f > x {
f - 1.0
} else {
f
}
}
#[no_mangle]
pub extern "C" fn ceilf(x: f32) -> f32 {
if x.abs() >= 8388608.0 {
return x;
}
let i = x as i32;
let f = i as f32;
if f < x {
f + 1.0
} else {
f
}
}
/// C semantics: round half away from zero.
#[no_mangle]
pub extern "C" fn roundf(x: f32) -> f32 {
if x >= 0.0 {
floorf(x + 0.5)
} else {
ceilf(x - 0.5)
}
}
#[no_mangle]
pub extern "C" fn truncf(x: f32) -> f32 {
if x.abs() >= 8388608.0 {
return x;
}
x as i32 as f32
}
/// f64 truncation toward zero (LLVM libcall on SSE2-only targets).
#[no_mangle]
pub extern "C" fn trunc(x: f64) -> f64 {
// 2^52: every f64 with |x| >= 2^52 is already integral.
if x.abs() >= 4503599627370496.0 {
return x;
}
x as i64 as f64
}
/* ---- sin/cos: musl __sindf/__cosdf + medium Cody-Waite reduction ---- */
// |sin(x)/x - s(x)| < 2**-27.5 on the reduced range.
const S1: f64 = -0.166666666416265235595;
const S2: f64 = 0.0083333293858894631756;
const S3: f64 = -0.000198393348360966317347;
const S4: f64 = 0.0000027183114939898219064;
const C0: f64 = -0.499999997251031003120;
const C1: f64 = 0.0416666233237390631894;
const C2: f64 = -0.00138867637746099294692;
const C3: f64 = 0.0000243904487962774090654;
const PIO2_HI: f64 = 1.57079625129699707031e+00;
const PIO2_LO: f64 = 7.54978941586159635335e-08;
const INV_PIO2: f64 = 6.36619772367581382433e-01; // 2/pi
const TOINT: f64 = 1.5 / f64::EPSILON;
const TWO_PI: f64 = 6.28318530717958647693;
#[inline]
fn sindf(x: f64) -> f64 {
let z = x * x;
let w = z * z;
let r = S3 + z * S4;
let s = z * x;
(x + s * (S1 + z * S2)) + s * w * r
}
#[inline]
fn cosdf(x: f64) -> f64 {
let z = x * x;
let w = z * z;
let r = C2 + z * C3;
((1.0 + z * C0) + w * C1) + (w * z) * r
}
/// Reduce x modulo pi/2 (Cody-Waite, double precision). Returns (n, y)
/// with y = x - n*pi/2 accurate for moderate |x|.
#[inline]
fn rem_pio2_medium(x: f64) -> (i32, f64) {
// Coarse pre-reduction keeps the accurate path valid for any angle egui
// can produce (ellipse/bezier angles are already tiny).
let x = if x.abs() > 105.0 {
x - (x / TWO_PI).trunc() * TWO_PI
} else {
x
};
let fn_ = x * INV_PIO2 + TOINT - TOINT;
let n = fn_ as i32;
(n, x - fn_ * PIO2_HI - fn_ * PIO2_LO)
}
#[no_mangle]
pub extern "C" fn sinf(x: f32) -> f32 {
if x.abs() < 1.0e-30 {
return x;
}
let (n, y) = rem_pio2_medium(x as f64);
(match n & 3 {
0 => sindf(y),
1 => cosdf(y),
2 => sindf(-y),
_ => -cosdf(y),
}) as f32
}
#[no_mangle]
pub extern "C" fn cosf(x: f32) -> f32 {
let (n, y) = rem_pio2_medium(x as f64);
(match n & 3 {
0 => cosdf(y),
1 => sindf(-y),
2 => -cosdf(y),
_ => sindf(y),
}) as f32
}
/* ---- atan2f: musl atanf + quadrant fix ---- */
const ATAN_HI: [f32; 4] = [
4.6364760399e-01,
7.8539812565e-01,
9.8279371262e-01,
1.5707962513e+00,
];
const ATAN_LO: [f32; 4] = [
8.8023035407e-08,
1.2093354704e-08,
1.2587686365e-08,
7.5497894159e-08,
];
const AT: [f32; 5] = [
3.3333328366e-01,
-1.9999158382e-01,
1.4253635705e-01,
-1.0648017377e-01,
6.1687607318e-02,
];
const PI_F: f32 = 3.1415927410e+00;
fn atanf(x: f32) -> f32 {
let mut x = x;
let ix = asu32(x);
let sign = ix >> 31;
let aix = ix & 0x7fffffff;
if aix >= 0x4c800000 {
// |x| >= 2^26
if x.is_nan() {
return x;
}
let z = ATAN_HI[3] + 7.5231638e-37;
return if sign != 0 { -z } else { z };
}
let id: i32;
if aix < 0x3ee00000 {
// |x| < 0.4375
if aix < 0x39800000 {
return x; // |x| < 2^-12: atan(x) ~ x
}
id = -1;
} else {
x = x.abs();
if aix < 0x3f980000 {
if aix < 0x3f300000 {
id = 0;
x = (2.0 * x - 1.0) / (2.0 + x);
} else {
id = 1;
x = (x - 1.0) / (x + 1.0);
}
} else if aix < 0x401c0000 {
id = 2;
x = (x - 1.5) / (1.0 + 1.5 * x);
} else {
id = 3;
x = -1.0 / x;
}
}
let z = x * x;
let w = z * z;
let s1 = z * (AT[0] + w * (AT[2] + w * AT[4]));
let s2 = w * (AT[1] + w * AT[3]);
if id < 0 {
return x - x * (s1 + s2);
}
let i = id as usize;
let z2 = ATAN_HI[i] - ((x * (s1 + s2) - ATAN_LO[i]) - x);
if sign != 0 {
-z2
} else {
z2
}
}
#[no_mangle]
pub extern "C" fn atan2f(y: f32, x: f32) -> f32 {
if y == 0.0 {
return if x < 0.0 { PI_F } else { y }; // crude, matches C for our uses
}
if x == 0.0 {
return if y < 0.0 { -PI_F / 2.0 } else { PI_F / 2.0 };
}
let ax = x.abs();
let ay = y.abs();
let z = if ax > ay {
atanf(ay / ax)
} else {
PI_F / 2.0 - atanf(ax / ay)
};
if x < 0.0 {
if y < 0.0 {
z - PI_F
} else {
PI_F - z
}
} else if y < 0.0 {
-z
} else {
z
}
}
/* ---- exp2f / log2f / powf ---- */
/// ldexp-style scaling by 2^n (n clamped to f32 range by callers).
fn scalbnf(mut r: f32, mut n: i32) -> f32 {
while n > 127 {
r *= asf32(0x7F00_0000); // 2^127
n -= 127;
}
while n < -126 {
r *= asf32(0x0080_0000); // 2^-126
n += 126;
}
r * asf32(((127 + n) as u32) << 23)
}
#[no_mangle]
pub extern "C" fn exp2f(x: f32) -> f32 {
if x > 128.0 {
return f32::INFINITY;
}
if x < -150.0 {
return 0.0;
}
// n = round-to-nearest-even(x) via the magic-number trick.
let t = x + 12582912.0; // 1.5 * 2^23
let nf = t - 12582912.0;
let n = nf as i32;
let f = x - nf; // in [-0.5, 0.5]
// 2^f: Taylor in ln2, degree 6 -> ~1e-10 rel error on [-0.5, 0.5].
let p = 1.0
+ f * (0.6931471805599453
+ f * (0.24022650695910070
+ f * (0.05550410866482158
+ f * (0.009618129107628477
+ f * (0.0013333558146428443 + f * 0.00015403530393381609)))));
scalbnf(p, n)
}
/// log2(x) for x > 0; accuracy ~1e-7 via atanh series in double.
fn log2f(x: f32) -> f32 {
let ix = asu32(x);
let e = ((ix >> 23) as i32) - 127;
let m = asf32((ix & 0x007F_FFFF) | 0x3F80_0000); // [1, 2)
let t = ((m - 1.0) / (m + 1.0)) as f64;
let t2 = t * t;
let mut sum = 0.0f64;
let mut term = t;
for k in 0..8 {
sum += term / (2 * k + 1) as f64;
term *= t2;
}
let ln_m = 2.0 * sum;
(e as f64 + ln_m * 1.4426950408889634) as f32
}
#[no_mangle]
pub extern "C" fn powf(x: f32, y: f32) -> f32 {
if y == 0.0 {
return 1.0;
}
if x == 1.0 {
return 1.0;
}
if x == 0.0 {
return 0.0;
}
if x == 2.0 {
return exp2f(y);
}
if x < 0.0 {
let yi = y as i32;
if yi as f32 == y {
let r = exp2f(y * log2f(-x));
return if yi & 1 != 0 { -r } else { r };
}
return f32::NAN;
}
exp2f(y * log2f(x))
}
/* ---- acosf: musl ---- */
const PIO2_HI_F: f32 = 1.5707962513e+00;
const PIO2_LO_F: f32 = 7.5497894159e-08;
const PS0: f32 = 1.6666586697e-01;
const PS1: f32 = -4.2743422091e-02;
const PS2: f32 = -8.6563630030e-03;
const QS1: f32 = -7.0662963390e-01;
#[inline]
fn acos_r(z: f32) -> f32 {
z * (PS0 + z * (PS1 + z * PS2)) / (1.0 + z * QS1)
}
#[no_mangle]
pub extern "C" fn acosf(x: f32) -> f32 {
let hx = asu32(x);
let ix = hx & 0x7fffffff;
if ix >= 0x3f800000 {
// |x| >= 1
if ix == 0x3f800000 {
if hx >> 31 != 0 {
return 2.0 * PIO2_HI_F; // acos(-1) = pi
}
return 0.0; // acos(1) = 0
}
return f32::NAN;
}
if ix < 0x3f000000 {
// |x| < 0.5
if ix <= 0x32800000 {
return PIO2_HI_F; // |x| < 2^-26
}
return PIO2_HI_F - (x - (PIO2_LO_F - x * acos_r(x * x)));
}
if hx >> 31 != 0 {
// x < -0.5
let z = (1.0 + x) * 0.5;
let s = z.sqrt();
let w = acos_r(z) * s - PIO2_LO_F;
return 2.0 * PIO2_HI_F - 2.0 * (s + w);
}
// x > 0.5
let z = (1.0 - x) * 0.5;
let s = z.sqrt();
let df = asf32(asu32(s) & 0xFFFFF000); // keep df*df rounding-exact
let c = (z - df * df) / (s + df);
let w = acos_r(z) * s + c;
2.0 * (df + w)
}
/* ---- cbrtf / hypotf ---- */
#[no_mangle]
pub extern "C" fn cbrtf(x: f32) -> f32 {
if x == 0.0 {
return x;
}
let r = exp2f(log2f(x.abs()) / 3.0);
if x < 0.0 {
-r
} else {
r
}
}
#[no_mangle]
pub extern "C" fn _hypotf(x: f32, y: f32) -> f32 {
let ax = x.abs();
let ay = y.abs();
let (a, b) = if ax > ay { (ax, ay) } else { (ay, ax) };
if a == 0.0 {
return 0.0;
}
let r = b / a;
a * (1.0 + r * r).sqrt()
}
/// Plain alias in case LLVM references the C99 spelling.
#[no_mangle]
pub extern "C" fn hypotf(x: f32, y: f32) -> f32 {
_hypotf(x, y)
}
+315
View File
@@ -0,0 +1,315 @@
//! Minimal Win32 bindings used by the Deaicup PE backend.
//! Only the API subset that the Deshab UTSM PE shim implements is declared here.
//! All imports use raw-dylib so rust-lld can emit the import table without
//! any MSVC import libraries.
#![allow(non_snake_case, non_camel_case_types, dead_code)]
pub type HWND = isize;
pub type HDC = isize;
pub type HINSTANCE = isize;
pub type HICON = isize;
pub type HCURSOR = isize;
pub type HBRUSH = isize;
pub type HMENU = isize;
pub type HGDIOBJ = isize;
pub type HBITMAP = isize;
pub type HANDLE = isize;
pub type ATOM = u16;
pub type WPARAM = usize;
pub type LPARAM = isize;
pub type LRESULT = isize;
pub type BOOL = i32;
pub type UINT = u32;
pub type DWORD = u32;
pub type WORD = u16;
pub type LPCWSTR = *const u16;
pub const CW_USEDEFAULT: i32 = 0x80000000u32 as i32;
pub const WS_OVERLAPPED: u32 = 0x00000000;
pub const WS_CAPTION: u32 = 0x00C00000;
pub const WS_SYSMENU: u32 = 0x00080000;
pub const WS_THICKFRAME: u32 = 0x00040000;
pub const WS_MINIMIZEBOX: u32 = 0x00020000;
pub const WS_MAXIMIZEBOX: u32 = 0x00010000;
pub const WS_VISIBLE: u32 = 0x10000000;
pub const WS_OVERLAPPEDWINDOW: u32 = WS_OVERLAPPED
| WS_CAPTION
| WS_SYSMENU
| WS_THICKFRAME
| WS_MINIMIZEBOX
| WS_MAXIMIZEBOX;
pub const SW_SHOW: i32 = 5;
pub const WM_CREATE: u32 = 0x0001;
pub const WM_DESTROY: u32 = 0x0002;
pub const WM_SIZE: u32 = 0x0005;
pub const WM_PAINT: u32 = 0x000F;
pub const WM_CLOSE: u32 = 0x0010;
pub const WM_QUIT: u32 = 0x0012;
pub const WM_ERASEBKGND: u32 = 0x0014;
pub const WM_KEYDOWN: u32 = 0x0100;
pub const WM_KEYUP: u32 = 0x0101;
pub const WM_CHAR: u32 = 0x0102;
pub const WM_TIMER: u32 = 0x0113;
pub const WM_MOUSEMOVE: u32 = 0x0200;
pub const WM_LBUTTONDOWN: u32 = 0x0201;
pub const WM_LBUTTONUP: u32 = 0x0202;
pub const WM_RBUTTONDOWN: u32 = 0x0204;
pub const WM_RBUTTONUP: u32 = 0x0205;
pub const WM_MBUTTONDOWN: u32 = 0x0207;
pub const WM_MBUTTONUP: u32 = 0x0208;
pub const PM_NOREMOVE: u32 = 0x0000;
pub const PM_REMOVE: u32 = 0x0001;
pub const VK_BACK: usize = 0x08;
pub const VK_TAB: usize = 0x09;
pub const VK_RETURN: usize = 0x0D;
pub const VK_SHIFT: usize = 0x10;
pub const VK_CONTROL: usize = 0x11;
pub const VK_MENU: usize = 0x12;
pub const VK_ESCAPE: usize = 0x1B;
pub const VK_SPACE: usize = 0x20;
pub const VK_LEFT: usize = 0x25;
pub const VK_UP: usize = 0x26;
pub const VK_RIGHT: usize = 0x27;
pub const VK_DOWN: usize = 0x28;
pub const VK_DELETE: usize = 0x2E;
pub const IDC_ARROW: usize = 32512;
pub const COLOR_WINDOW: u32 = 5;
pub const GWLP_USERDATA: i32 = -21;
pub const GWLP_WNDPROC: i32 = -4;
pub const SM_CXSCREEN: i32 = 0;
pub const SM_CYSCREEN: i32 = 1;
pub const SRCCOPY: u32 = 0x00CC0020;
pub const DIB_RGB_COLORS: u32 = 0;
pub const BI_RGB: u32 = 0;
pub const WHITE_BRUSH: i32 = 0;
pub const STD_OUTPUT_HANDLE: u32 = 0xFFFFFFF5;
pub const STD_ERROR_HANDLE: u32 = 0xFFFFFFF4;
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct POINT {
pub x: i32,
pub y: i32,
}
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct RECT {
pub left: i32,
pub top: i32,
pub right: i32,
pub bottom: i32,
}
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct MSG {
pub hwnd: HWND,
pub message: u32,
pub wParam: WPARAM,
pub lParam: LPARAM,
pub time: u32,
pub pt: POINT,
}
#[repr(C)]
pub struct WNDCLASSEXW {
pub cbSize: UINT,
pub style: UINT,
pub lpfnWndProc: WNDPROC,
pub cbClsExtra: i32,
pub cbWndExtra: i32,
pub hInstance: HINSTANCE,
pub hIcon: HICON,
pub hCursor: HCURSOR,
pub hbrBackground: HBRUSH,
pub lpszMenuName: LPCWSTR,
pub lpszClassName: LPCWSTR,
pub hIconSm: HICON,
}
pub type WNDPROC = extern "system" fn(HWND, u32, WPARAM, LPARAM) -> LRESULT;
#[repr(C)]
pub struct PAINTSTRUCT {
pub hdc: HDC,
pub fErase: BOOL,
pub rcPaint: RECT,
pub fRestore: BOOL,
pub fIncUpdate: BOOL,
pub rgbReserved: [u8; 32],
}
impl Default for PAINTSTRUCT {
fn default() -> Self {
unsafe { core::mem::zeroed() }
}
}
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct BITMAPINFOHEADER {
pub biSize: u32,
pub biWidth: i32,
pub biHeight: i32,
pub biPlanes: u16,
pub biBitCount: u16,
pub biCompression: u32,
pub biSizeImage: u32,
pub biXPelsPerMeter: i32,
pub biYPelsPerMeter: i32,
pub biClrUsed: u32,
pub biClrImportant: u32,
}
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct RGBQUAD {
pub rgbBlue: u8,
pub rgbGreen: u8,
pub rgbRed: u8,
pub rgbReserved: u8,
}
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct BITMAPINFO {
pub bmiHeader: BITMAPINFOHEADER,
pub bmiColors: [RGBQUAD; 1],
}
#[link(name = "user32", kind = "raw-dylib")]
extern "system" {
pub fn RegisterClassExW(lpwcx: *const WNDCLASSEXW) -> ATOM;
pub fn CreateWindowExW(
dwExStyle: DWORD,
lpClassName: LPCWSTR,
lpWindowName: LPCWSTR,
dwStyle: DWORD,
x: i32,
y: i32,
nWidth: i32,
nHeight: i32,
hWndParent: HWND,
hMenu: HMENU,
hInstance: HINSTANCE,
lpParam: *mut core::ffi::c_void,
) -> HWND;
pub fn DestroyWindow(hWnd: HWND) -> BOOL;
pub fn ShowWindow(hWnd: HWND, nCmdShow: i32) -> BOOL;
pub fn UpdateWindow(hWnd: HWND) -> BOOL;
pub fn GetMessageW(lpMsg: *mut MSG, hWnd: HWND, wMsgFilterMin: u32, wMsgFilterMax: u32) -> BOOL;
pub fn PeekMessageW(
lpMsg: *mut MSG,
hWnd: HWND,
wMsgFilterMin: u32,
wMsgFilterMax: u32,
wRemoveMsg: UINT,
) -> BOOL;
pub fn TranslateMessage(lpMsg: *const MSG) -> BOOL;
pub fn DispatchMessageW(lpMsg: *const MSG) -> LRESULT;
pub fn PostQuitMessage(nExitCode: i32);
pub fn DefWindowProcW(hWnd: HWND, msg: u32, wParam: WPARAM, lParam: LPARAM) -> LRESULT;
pub fn GetClientRect(hWnd: HWND, lpRect: *mut RECT) -> BOOL;
pub fn InvalidateRect(hWnd: HWND, lpRect: *const RECT, bErase: BOOL) -> BOOL;
pub fn BeginPaint(hWnd: HWND, lpPaint: *mut PAINTSTRUCT) -> HDC;
pub fn EndPaint(hWnd: HWND, lpPaint: *const PAINTSTRUCT) -> BOOL;
pub fn GetDC(hWnd: HWND) -> HDC;
pub fn ReleaseDC(hWnd: HWND, hDC: HDC) -> i32;
pub fn LoadCursorW(hInstance: HINSTANCE, lpCursorName: usize) -> HCURSOR;
pub fn SetCursor(hCursor: HCURSOR) -> HCURSOR;
pub fn GetCursorPos(lpPoint: *mut POINT) -> BOOL;
pub fn ScreenToClient(hWnd: HWND, lpPoint: *mut POINT) -> BOOL;
pub fn GetAsyncKeyState(vKey: i32) -> i16;
pub fn SetTimer(hWnd: HWND, nIDEvent: usize, uElapse: UINT, lpTimerFunc: usize) -> usize;
pub fn KillTimer(hWnd: HWND, uIDEvent: usize) -> BOOL;
pub fn MessageBeep(uType: UINT) -> BOOL;
pub fn GetSystemMetrics(nIndex: i32) -> i32;
}
#[link(name = "gdi32", kind = "raw-dylib")]
extern "system" {
pub fn CreateCompatibleDC(hdc: HDC) -> HDC;
pub fn CreateDIBSection(
hdc: HDC,
pbmi: *const BITMAPINFO,
usage: UINT,
ppvBits: *mut *mut core::ffi::c_void,
hSection: HANDLE,
offset: DWORD,
) -> HBITMAP;
pub fn SelectObject(hdc: HDC, h: HGDIOBJ) -> HGDIOBJ;
pub fn DeleteObject(ho: HGDIOBJ) -> BOOL;
pub fn DeleteDC(hdc: HDC) -> BOOL;
pub fn BitBlt(
hdc: HDC,
x: i32,
y: i32,
cx: i32,
cy: i32,
hdcSrc: HDC,
x1: i32,
y1: i32,
rop: DWORD,
) -> BOOL;
pub fn GetStockObject(i: i32) -> HGDIOBJ;
pub fn SetDIBitsToDevice(
hdc: HDC,
xDest: i32,
yDest: i32,
w: DWORD,
h: DWORD,
xSrc: i32,
ySrc: i32,
startScan: UINT,
numLines: UINT,
lpvBits: *const core::ffi::c_void,
lpbmi: *const BITMAPINFO,
colorUse: UINT,
) -> i32;
pub fn GdiFlush() -> BOOL;
}
#[link(name = "kernel32", kind = "raw-dylib")]
extern "system" {
pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HINSTANCE;
pub fn GetStdHandle(nStdHandle: DWORD) -> HANDLE;
pub fn WriteFile(
hFile: HANDLE,
lpBuffer: *const u8,
nNumberOfBytesToWrite: DWORD,
lpNumberOfBytesWritten: *mut DWORD,
lpOverlapped: *mut core::ffi::c_void,
) -> BOOL;
pub fn ExitProcess(uExitCode: UINT) -> !;
pub fn GetCommandLineA() -> *const u8;
pub fn GetTickCount() -> DWORD;
pub fn QueryPerformanceCounter(lpPerformanceCount: *mut i64) -> BOOL;
pub fn QueryPerformanceFrequency(lpFrequency: *mut i64) -> BOOL;
pub fn Sleep(dwMilliseconds: DWORD);
}
/// UTF-16 helper: build a null-terminated wide string.
pub fn wide(s: &str) -> Vec<u16> {
s.encode_utf16().chain(core::iter::once(0)).collect()
}
/// Debug log through the shim's stdout (serial).
pub fn log(s: &str) {
unsafe {
let h = GetStdHandle(STD_OUTPUT_HANDLE);
let mut written: DWORD = 0;
WriteFile(h, s.as_ptr(), s.len() as DWORD, &mut written, core::ptr::null_mut());
}
}