FUCKYOU
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user