Adds a persistent TCG code cache to Unicorn via a version-portable build-time patch system.
## Patch infrastructure
Brovan/native/unicorn/ holds Brovan's additions to the Unicorn tree; Brovan.Unicorn.targets applies them after extraction through a RoslynCodeTaskFactory inline task — no git/patch/python dependency, works on Windows, Linux and Android build hosts. Edits are single-line anchors and whole-file appends rather than hunks, so they survive upstream drift; every rule is idempotent, and a missing anchor fails the build naming all misses. The patch-set hash is part of the source and build directory names, so editing a patch forces a clean re-extract. Validated against the previous two Unicorn tags.
## Code cache
Saves the TCG buffer and reloads it on the next launch. Helper addresses route through an indirect slot table; uc/tcg_ctx are pinned in a replayed address reservation chosen from a probed candidate list. Every restored block is re-verified against the guest bytes it was translated from, and page_addr/hash are recomputed from the live mapping so self-modifying-code invalidation still finds them. A save-time audit scans emitted code for host pointers a reload could not repoint and refuses rather than writing a poisoned blob. it caught a real miss (the i386 backend tail-jumps into qemu_st_helpers rather than calling it). Blocks in pages the loader hasn't reached yet are retried later instead of re-translated, and the blob is dropped once mostly dead code, bounding growth.
## Inline hooks stay enabled
Slots carry a kind. image-relative for helpers, hook identity for callbacks re-resolved from uc->hook[] on load. so the cache no longer has to disable Unicorn's inline-hook path. That was costing ~13% throughput.
Per-block exit poll inlined. Unicorn calls a helper on every block to test icount_decr. this emits a load and a not-taken branch, with the helper kept for the slow path, following QEMU's shape of branching out of the block to a trailing label.
## Direct register access
brov_reg_ptr hands out host pointers into the guest CPU state, used by ReadRegister/WriteRegister through unsafe loads and stores. Only registers Unicorn stores verbatim are exposed, and only those it would write with a plain store are writable — the program counter is read-only because writing it also raises quit_request and flushes blocks, and nothing is exposed in 16/32-bit mode where the same storage is reached under different truncation rules.
## Android
JIT caching switch in Settings, default on. The Vulkan shim is a guest PE, so it now ships as an APK asset and is deployed into the guest's System32/SysWOW64 on launch, refreshed on app update. The cross-build no longer shares a CMake cache with the host build.
## API set map from the installed image
Read from the .apiset section of the imported apisetschema.dll instead of being dumped from the host or synthesised, so the contract names match the DLLs actually installed. Regenerated when the schema is newer than the map.
Adds a persistent TCG code cache to Unicorn via a version-portable build-time patch system, plus the TCG and Android work that came out of making it releasable.
Patch infrastructure. Brovan/native/unicorn/ holds Brovan's additions to the Unicorn tree; Brovan.Unicorn.targets applies them after extraction through a RoslynCodeTaskFactory inline task — no git/patch/python dependency, works on Windows, Linux and Android build hosts. Edits are single-line anchors and whole-file appends rather than hunks, so they survive upstream drift; every rule is idempotent, and a missing anchor fails the build naming all misses. The patch-set hash is part of the source and build directory names, so editing a patch forces a clean re-extract. Validated against the previous two Unicorn tags.
Code cache. Saves the TCG buffer and reloads it on the next launch. Helper addresses route through an indirect slot table; uc/tcg_ctx are pinned in a replayed address reservation chosen from a probed candidate list. Every restored block is re-verified against the guest bytes it was translated from, and page_addr/hash are recomputed from the live mapping so self-modifying-code invalidation still finds them. A save-time audit scans emitted code for host pointers a reload could not repoint and refuses rather than writing a poisoned blob — it caught a real miss (the i386 backend tail-jumps into qemu_st_helpers rather than calling it). Blocks in pages the loader hasn't reached yet are retried later instead of re-translated, and the blob is dropped once mostly dead code, bounding growth.
Inline hooks stay enabled. Slots carry a kind — image-relative for helpers, hook identity for callbacks re-resolved from uc->hook[] on load — so the cache no longer has to disable Unicorn's inline-hook path. That was costing ~13% throughput.
Per-block exit poll inlined. Unicorn calls a helper on every block to test icount_decr; this emits a load and a not-taken branch, with the helper kept for the slow path, following QEMU's shape of branching out of the block to a trailing label.
Direct register access. brov_reg_ptr hands out host pointers into the guest CPU state, used by ReadRegister/WriteRegister through unsafe loads and stores. Only registers Unicorn stores verbatim are exposed, and only those it would write with a plain store are writable — the program counter is read-only because writing it also raises quit_request and flushes blocks, and nothing is exposed in 16/32-bit mode where the same storage is reached under different truncation rules.
Android. JIT caching switch in Settings, default on. The Vulkan shim is a guest PE, so it now ships as an APK asset and is deployed into the guest's System32/SysWOW64 on launch, refreshed on app update. The cross-build no longer shares a CMake cache with the host build.
API set map from the installed image. Read from the .apiset section of the imported apisetschema.dll instead of being dumped from the host or synthesised, so the contract names match the DLLs actually installed. Regenerated when the schema is newer than the map.
Replaced hardcoded register constants for CPUID leaves 0x80000002-0x80000004 with a shared helper that builds each 16-byte chunk from a single `ProcessorBrandString` value (`Intel(R) Core(TM) i7-9700K CPU @ 3.60GHz`). This keeps leaf output consistent, correctly little-endian encoded. as previously the hand-written one contained misspellings.
Add a dedicated `GuestImagePath` in the emulator and switch Windows guest/process metadata, startup parameters, and module lookups to use guest-visible paths instead of raw host paths. Extend IO path handling with Windows mount mappings and Windows-path helpers so PE images launched on non-Windows hosts resolve to valid in-guest paths and path-based APIs behave correctly across platforms.
Removes the custom Windows pack pipeline and all pack-path handling, so Windows DLLs and registry hives are now read directly from extracted files on disk. This simplifies library loading, registry access, and file stream behavior across the emulator. this also fixes issues related to Android & Linux support, and software runs much faster than with it.
Adds a new VisualCppRuntimeImporter that pulls current x86/x64 VC++ runtime DLLs from Microsoft’s Visual Studio manifests, wires it into --install-windows, and introduces --install-runtimes for runtime-only setup. CLI help and license messaging were updated accordingly.
Also added to not autostart the emulator in Android if developer mode is on.
Brovan cannot run Windows programs without the Windows system files:
the user-mode DLLs, the NLS tables and the registry hives. Linux and
Android hosts had no method to get them. This change adds an importer
that reads them from Microsoft installation media.
The new code is in Brovan/Core/Helpers/WindowsImage. It contains a UDF
reader, a WIM and ESD reader with solid resource support, and the
XPRESS, LZX and LZMS decoders. The media can be a local file, a file
descriptor from the Android file picker, or a remote ISO read with HTTP
range requests. The importer reads only the data that it extracts.
The importer extracts in stream order, so it decodes each 64 MB chunk
one time only. The cache size and the thread count come from a memory
budget, not from a chunk count, so one resource cannot hold hundreds of
megabytes on a phone.
On Android the files go into a compressed pack of about 900 MB. A loose
copy is 2.2 GB. The emulator decompresses each block in memory when the
guest reads it. A switch keeps the files loose for more speed.
New command line options: --install-windows, --windows-iso,
--windows-pack, --windows-image and --accept-windows-license. The
Android app has a new Windows files screen with a licence switch, a
progress bar, and buttons to open the Microsoft download page or to
select an ISO from the device.
Brovan does not contain Microsoft software. It downloads the media only
from the Microsoft servers, the user must start the download, and the
user must have a valid Windows licence.
This change also sets SSL_CERT_DIR to the Android certificate
directory, because HTTPS does not operate without it. It removes the
KVM and WHP options from the Android app, because these backends do not
operate there.
Brovan now runs on Android as a third host alongside Windows and Linux.
New Brovan android embedding layer. The emulator becomes a NativeAOT shared library driven by a host app rather than a process with a Main:
- BrovanAndroidApi - exported C ABI (init, surface, start, input injection, window enumeration, debugger commands).
- AndroidWinManager - IDisplayConnection over ANativeWindow, plus IGdiRenderSupport.
- AndroidGdiSurface - software rasteriser (lines, rects, ellipses, polygons) into a per-guest-window backbuffer posted via ANativeWindow_lock/ unlockAndPost, white background.
- AndroidVulkanWsi and a generator branch - VK_KHR_android_surface instead of Win32/Xcb.
- AndroidInput, AndroidLog, AndroidHost, AndroidGuestWindows, JNI shim, Java bindings.
- Launcher app: Material 3 library, in-app program import via SAF, settings, on-screen joystick/D-pad/touchpad controls, opt-in developer console wired to the debugger.
- build-apk.sh - Unicorn cross-build, linux-bionic-arm64 publish, bundled OpenSSL, APK assembly.
Changes to shared emulator code:
- Case-insensitive shipped-DLL resolution in GetWindowsLibPath. Import tables say KERNEL32.dll, System32 ships kernel32.dll; broken on any case-sensitive host, and it surfaced as a guest loading zero modules.
- GlobalPropertiesToRemove on the generator ProjectReference. Target-shaped properties leaked into the analyzer, csc silently refused to load it (CS8034 is only a warning), and every source generator emitted nothing.
- GdiPrimitive.Hwnd. The four EnqueueGdi* helpers already had the guest HWND and dropped it, making per-window compositing impossible.
- Program.SplitCommandLine widened to internal for embedders. Windows and Linux behaviour is unchanged; the new paths are gated on the RID or !IsWindows.
Brovan now runs on Android as a third host alongside Windows and Linux.
New Brovan android embedding layer. The emulator becomes a NativeAOT shared
library driven by a host app rather than a process with a Main:
- BrovanAndroidApi - exported C ABI (init, surface, start, input injection,
window enumeration, debugger commands).
- AndroidWinManager - IDisplayConnection over ANativeWindow, plus
IGdiRenderSupport.
- AndroidGdiSurface - software rasteriser (lines, rects, ellipses, polygons)
into a per-guest-window backbuffer posted via ANativeWindow_lock/
unlockAndPost, white background.
- AndroidVulkanWsi and a generator branch - VK_KHR_android_surface instead of
Win32/Xcb.
- AndroidInput, AndroidLog, AndroidHost, AndroidGuestWindows, JNI shim, Java
bindings.
- Launcher app: Material 3 library, in-app program import via SAF, settings,
on-screen joystick/D-pad/touchpad controls, opt-in developer console wired to
the debugger.
- build-apk.sh - Unicorn cross-build, linux-bionic-arm64 publish, bundled
OpenSSL, APK assembly.
Changes to shared emulator code:
- Case-insensitive shipped-DLL resolution in GetWindowsLibPath. Import tables
say KERNEL32.dll, System32 ships kernel32.dll; broken on any case-sensitive
host, and it surfaced as a guest loading zero modules.
- GlobalPropertiesToRemove on the generator ProjectReference. Target-shaped
properties leaked into the analyzer, csc silently refused to load it (CS8034
is only a warning), and every source generator emitted nothing.
- GdiPrimitive.Hwnd. The four EnqueueGdi* helpers already had the guest HWND and
dropped it, making per-window compositing impossible.
- Program.SplitCommandLine widened to internal for embedders.
Windows and Linux behaviour is unchanged; the new paths are gated on the RID or
!IsWindows.
Wrap emulation startup warnings/prompts and binary diagnostics in a `!SilentMode` block so silent runs avoid interactive output while at the same time avoid unnecessary work.
Implement emulation for `NtSetIoCompletion` and `NtRemoveIoCompletion`, including wait-state handling, timeout behavior, and completion packet release. Wire file handles to completion ports via `FileCompletionInformation` in `NtSetInformationFile`, store completion key/port on `WinFile`, and enqueue completion packets for completed `NtDeviceIoControlFile` requests. Also update waiter wake-up logic to resume threads waiting on generic handles (including IO completion objects), and improve invalid-memory issue logs with resolved Windows address context and stack module frames.
Implement emulation for `NtSetIoCompletion` and `NtRemoveIoCompletion`, including wait-state handling, timeout behavior, and completion packet release. Wire file handles to completion ports via `FileCompletionInformation` in `NtSetInformationFile`, store completion key/port on `WinFile`, and enqueue completion packets for completed `NtDeviceIoControlFile` requests. Also update waiter wake-up logic to resume threads waiting on generic handles (including IO completion objects), and improve invalid-memory issue logs with resolved Windows address context and stack module frames.
Improve mapped-region bookkeeping so `UnmapMemory` correctly handles partial overlaps by splitting surviving segments and updating pointers. Track each region’s original buffer base to prevent invalid frees when regions are split, and only release buffers when no surviving alias remains. Disposal now also deduplicates buffer frees to avoid double-free risks.
Implements several missing NT/Win32k syscall handlers, including `NtOpenProcessTokenEx`, `NtQueryMultipleValueKey`, and window/input-related user syscalls (`GetCursorPos`, `MoveWindow`, `SetForegroundWindow`, `SetWindowLong`, raw input registration stub). Refactors `NtOpenProcessToken` to share logic with the Ex variant, extends Win32k state to track cursor coordinates, and adds `POINT` struct serialization support. Also expand the Vulkan forward generator to include buffer view creation/destruction and `vkBindBufferMemory2`/`vkBindImageMemory2` entry points.
This fixes compatibility with several more programs.
Add a new `KsecCngProviders` implementation for `\Device\KsecDD` IOCTL `0x390400` request `0x00020000`, including request parsing, algorithm/interface matching, and construction of a pointer-size-correct self-relative `CRYPT_PROVIDER_REFS` response for the Microsoft Primitive Provider. Update `KsecDevice` to treat `0x390400` as a multiplexed CNG request channel, dispatch provider-resolution requests to the new handler, and keep existing generic output behavior for other request kinds.
This fixed bugs inside bcrypt/bcryptprimitives.
The sType constants for MEMORY_HOST_POINTER_PROPERTIES_EXT (1000178001)
and PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT (1000178002)
were swapped, so QueryImportAlignment tagged its pNext struct with the
wrong sType. Drivers leave unrecognised pNext structs untouched, so this
failed silently: minImportedHostPointerAlignment came back 0, the device
import was never registered, and every host-visible allocation fell back
to SyncAllMappingsToHost, which full-copies every live mapping on every
vkQueueSubmit.
On DELTARUNE Ch.5 that fallback copied ~250 MB per submit, 282 GB over a
60 s run. With the constants corrected: 9/9 host-visible allocations
imported, zero bytes copied, submits over a fixed 45 s window 1006 ->
2798, and peak private bytes 2229 MB -> 1833 MB. The 340 MB that no
longer needs duplicating was the boot memory spike.
TryGetHostPointer and IsRangeMapped walked every 4 KB page through
_mappedPages to prove host contiguity. A batch mapping lays one backing
allocation over consecutive guest pages and BackingAllocation.LivePages
is only ever decremented by ReleaseBacking, so an untouched LivePages
proves the whole run is still mapped where it was. TryGetIntactBackingEnd
resolves it in two dictionary lookups instead of one per page. Mirrored
into the KVM backend, which has the same structure. This only matters
where host import is unavailable; on a device that supports it the copy
path is now dead.
Whp caches XMM so a context-switch write is deferred into the
StoreRegisters call that already happens, and a read is lazy. XMM is
deliberately not folded into LoadRegisters: reading XMM makes WHP extract
the full FP state, and a GP load runs on every VM exit while an XMM read
is only needed on a context switch, so merging there measured
LoadRegisters 1.7 s -> 16.8 s. Net of the store merge alone, register
hypercalls drop from 13604 ms to 11472 ms per 50 s. A lone dirty XMM
takes its own 16-name store rather than riding the GP path, which would
push a stale _regsCache into the processor.
Adds BROVVULK_IMPORT_STATS=1 to report why import falls back and what the
copy path costs, since a wrong sType is otherwise invisible. It writes to
error_log.log because these runs use --silent, and Utils.FlushLog exists
because a force-killed run never reaches the ProcessExit flush.
Replaced the old `GuestSessionRegistry` request path with a split session model (`GuestSession`, `GuestSessionMailbox`, `RemoteGuestProcess`) and moved remote process operations behind explicit APIs for memory read/write, allocation, thread creation, and termination. This also updates process/thread handle types to use remote-backed objects and wires startup publishing/consumption so spawned guests are only used after their PEB and process parameters are ready.
Process creation and query flows were improved to populate `PS_CREATE_INFO` and `SECTION_IMAGE_INFORMATION` consistently (including PE header parsing in the launcher), and `NtAllocateVirtualMemory` now exposes shared allocation logic used for remote requests.
These changes fixed lots of bugs related to process creation.
Also replaced directory mask regex matching with `FileSystemName.MatchesSimpleExpression` for simpler, culture-invariant wildcard handling.
Adds a region-index based free-address finder in `BinaryEmulator` and switches `NtAllocateVirtualMemory` to use it instead of bounded probing loops. Introduces `SimdStringHelpers` for fast zero/NUL scans and ASCII/UTF-16 decoding, then wires those paths into `StructSerializer` and `Unicorn` string reads. Also reduces allocation/call overhead by writing context blocks via stack buffers and replacing per-argument byte-array conversions with direct primitive memory writes.
Window titles are now consistently branded by appending " - Brovan" in both Linux and Windows window manager implementations to let the user know this is Brovan-(owned/made)
# DPI awareness
The emulator gave every program a screen of 96 DPI. It also made the host window with no DPI awareness. A program that asks for a different DPI got the wrong screen size and a window that is not sharp.
The emulator now reads the DPI awareness of the program. It reads it from the application manifest in the image, from an external manifest file, or from the NtUserSetProcessDpiAwarenessContext system call.
user32 does not ask the kernel for most DPI values. It calculates them from a packed value in the CLIENTINFO block of the thread and in the window structure. The emulator writes this value into the two locations. If it does not, the program reads one DPI from user32 and a different DPI from the system calls.
The emulator gives the same awareness to the host window. An aware program gets a window in true pixels. An unaware program keeps the window that the desktop manager makes larger for it.
The emulator also sets the composited flag in the desktop data. Without this flag, user32 uses a different code path. That path gives 96 DPI to a program that is aware.
# Guest process launch
A guest process could not start a different program. The emulator did not have the NtCreateUserProcess system call.
The emulator now starts a second Brovan for each new guest process. One guest process is one host process. The window manager, the Vulkan device, the scheduler and the caches are global to a process. Two guests in one emulator need a process identity in all of them.
The parent sends the image path, the command line and the directory to the child. It encodes these values with base64. Without base64, the host command line divides the values and joins them again. A value with a quotation mark or a space does not stay correct.
# Session registry
The Brovan instances of one session share a table in a memory-mapped file. Each instance writes one row. An instance that must stop a different guest process writes a request into the row of that process. The owner of the row reads the request and stops itself. This is the only correct method. The memory of that process is in a different host process.
The emulator counts the rows before it starts a child. It permits a maximum of six guest processes in one session.
# New options
`--cwd <dir>` sets the directory in which the program starts.
`--guest-cmdline <s>` sets the command line of the program. The two options also accept a value in the form `"base64:<value>"`.
This change fixes several process-management edge cases in Windows emulation. Reused guest session slots now clear mailbox state before reassignment to prevent stale sequence numbers from being treated as new requests. Newly created user processes are added to `WinProcesses` so they are tracked consistently. `NtReadVirtualMemory` now writes `BytesRead` using the active pointer size instead of always 8 bytes, improving x86/x64 correctness. The main process PID is now initialized by adopting the host process ID when available, falling back to random generation only when needed.
# DPI awareness
The emulator gave every program a screen of 96 DPI. It also made the host
window with no DPI awareness. A program that asks for a different DPI got
the wrong screen size and a window that is not sharp.
The emulator now reads the DPI awareness of the program. It reads it from
the application manifest in the image, from an external manifest file, or
from the NtUserSetProcessDpiAwarenessContext system call.
user32 does not ask the kernel for most DPI values. It calculates them
from a packed value in the CLIENTINFO block of the thread and in the
window structure. The emulator writes this value into the two locations.
If it does not, the program reads one DPI from user32 and a different DPI
from the system calls.
The emulator gives the same awareness to the host window. An aware program
gets a window in true pixels. An unaware program keeps the window that the
desktop manager makes larger for it.
The emulator also sets the composited flag in the desktop data. Without
this flag, user32 uses a different code path. That path gives 96 DPI to a
program that is aware.
# Guest process launch
A guest process could not start a different program. The emulator did not
have the NtCreateUserProcess system call. DELTARUNE stopped at its menu
for this reason. The game starts a new process when you select a chapter.
The emulator now starts a second Brovan for each new guest process. One
guest process is one host process. The window manager, the Vulkan device,
the scheduler and the caches are global to a process. Two guests in one
emulator need a process identity in all of them. The host operating system
gives this isolation at no cost.
The parent sends the image path, the command line and the directory to the
child. It encodes these values with base64. Without base64, the host
command line divides the values and joins them again. A value with a
quotation mark or a space does not stay correct.
# Session registry
The Brovan instances of one session share a table in a memory-mapped file.
Each instance writes one row. An instance that must stop a different guest
process writes a request into the row of that process. The owner of the row
reads the request and stops itself. This is the only correct method. The
memory of that process is in a different host process.
The emulator counts the rows before it starts a child. It permits a maximum
of six guest processes in one session.
# New options
--cwd <dir> sets the directory in which the program starts.
--guest-cmdline <s> sets the command line of the program.
The two options also accept a value in the form "base64:<value>".
Decouples anonymous kernel object naming from PID generation by adding `GenerateAnonymousObjectId/Name` and updating object-creation syscalls and helper factories to use them. It also centralizes named object reuse through `HandleManager.GetObjectByObjectId<T>` (replacing ad-hoc lists/lookups for events, timers, jobs, and ETW registration tracking), and improves shared buffer memory behavior by trimming oversized retained buffers after sustained small requests to avoid long-lived LOH pressure.
Reworked the GUI threading path to be wake-driven instead of poll-heavy: command handling now uses a lightweight queued command struct, present-state coalescing, backend WaitForEvents/Wake hooks, and concurrent window maps on both Windows and Linux. The host input queue was replaced with a ring buffer that coalesces WM_MOUSEMOVE events to reduce backlog latency.
Also fixed GDI lifecycle issues by validating/freeing handles correctly, reusing slot indices with uniqueness tracking, and reclaiming HDC handles in win32k to prevent table exhaustion under repaint storms. In addition, Windows guest environment/profile paths were aligned with WinSysHelper constants and registry synthetic data, and the temporary MLFQ spin-wait heuristic was removed from the emulator scheduler.
Reworks the MLFQ scheduler to reduce starvation under frequent wakeups by tracking skipped levels, and switches aging/last-run accounting to scheduler slices for more consistent boosting behavior. Adds Windows syscall coverage and reliability improvements: new NtAlpcConnectPortEx/NtAlpcQueryInformation handlers, shared ALPC connect path/logging, support for SystemRecommendedSharedDataAlignment, corrected thread priority-delta handling in NtSetInformationThread, and richer NtRaiseHardError diagnostics with parameter and stack/module tracing.
Adds backend-level XMM0-15 transfer APIs (KVM, Unicorn, WHP) and wires them into thread context save/restore so SIMD state survives scheduling. Introduces new Win32k syscall handlers for atom lookup, window message registration, hook register/unregister, class unregister, and related WinSyscallsHelper state management. Also improves wake scheduling behavior (yield slice after alert wake), tightens token syscall semantics/status codes, and adds DXVK/VK env passthrough plus BrovVulk issue-level diagnostics.
Introduces Win32k syscall handlers for monitor/display queries (`EnumDisplayMonitors`, `EnumDisplaySettings`, `GetDisplayConfigBufferSizes`, `GetDpiForMonitor`) and adds guest-callback trampoline support so kernel-side handlers can invoke guest callbacks safely. It also improves runtime robustness by adding PID generation fallback behavior, wiring primary monitor handle/rect helpers, and exposing display frequency via `EnumDisplaySettingsW`.
On the Vulkan side, the generator now tracks optional members and emits that metadata into generated struct descriptors, and rebuild-time validation now rejects non-optional null arrays with non-zero declared counts.
Improve Vulkan marshalling compatibility by generating native field offsets/sizes, using a pNext offset macro, and adding scalar width-aware serialize/deserialize helpers for struct bodies (including nested/out/chain paths). This fixes writeback behavior for struct and array outputs across mixed host/guest layouts.
Update ICD build scripts to optionally build and deploy a 32-bit vulkan-1.dll to SysWOW64 when an x86 toolchain is available. Also harden NtDeviceIoControlFile output handling by renting zeroed output buffers from ArrayPool, writing back only on successful status, and returning non-pending buffers to the pool.
Enable WOW64-aware path handling for 32-bit PE emulation. The emulator now toggles a global WOW64 file redirect flag for x86 guests, applies System32/SysWOW64/Sysnative remapping with Windows exclusion folders, and prefers SysWOW64 lookups for leaf-only library resolution. Registry key checks now normalize paths and try a redirected HKLM/HKCU SOFTWARE\Wow6432Node view first for x86 guests, then fall back to exact lookup.
Extend the WHP backend to support both x86-32 and x86-64 guests by mode-gating segment attributes, register initialization, and syscall trap setup. Rework GDT/IDT descriptor construction with shared helpers, add explicit selector constants and GDT limit values, and align TSS placement with the new layout. Also expand IDT coverage to 256 vectors, introduce software-interrupt vector handling, and keep 32-bit FS/GS descriptor bases synchronized when segment bases are updated.
Refactors multiple Windows NT syscall handlers to use pointer-size-aware read/write helpers instead of hardcoded 64-bit memory operations. This unifies `NtCreateFile` device handle creation into a single method, updates thread/token syscalls to use `WritePointer`/`ReadPointer`, and fixes attribute parsing/layout in `NtCreateThreadEx` and `NtQuerySecurityAttributesToken` for x86/x64 correctness. It also centralizes pseudo-handle signed conversion with `HandleManager.ToSignedHandle` and removes unused `CurrentVer` handling in `NtInitializeNlsFiles`.
Adds Win32k quit-message support via `NtUserPostQuitMessage` and queue handling so `WM_QUIT` is surfaced through message APIs and wait checks. Improves window teardown by posting `WM_DESTROY`/`WM_NCDESTROY`, retaining destroyed windows briefly for dispatch, and forgetting them after `WM_NCDESTROY`. Host close handling is unified with `HostEventQueue.RequestClose`/`Reset`, and process termination now hides the desktop window before stopping emulation.
This also fixes a bug in Vulkan rendering where it assumed the window still existed and crashed with an access violation.
Introduces MMIO mapping support in the WHP backend, including trapped-page integration and periodic host-page refresh callbacks. Reworks WHP GPA mapping updates to support incremental dirty-range rebuilds (with full rebuild fallback), and defers CS/SS segment writes into the normal register flush path for cleaner register state updates.
Also optimizes Windows PE relocation patching by processing relocation tables and page patches in pooled buffers, and adds registry hive path/root caching to reduce repeated traversal overhead. Includes minor register-assignment formatting cleanup in BinaryEmulator.
Reworked the post-build CFG handling in `Brovan.csproj` by replacing the Windows batch `editbin.exe` flow with an inline MSBuild `RoslynCodeTaskFactory` task (`DisableGuardCF`). The new task directly edits the built `.exe` PE header to clear the GuardCF bit when present, removing the dependency on VsDevCmd/editbin availability.
Replace dictionary-based syscall resolution with compact dispatch tables for both Windows and Linux guests, including cached table reuse and a shared lookup helper. Generic guest register name resolution now uses a reverse map instead of linear scans. Also simplify memory region snapshot insertion with TryGetValue and direct assignment.
This change removes many x64-only syscall paths and makes Windows emulation pointer-size aware so x86/WOW64 flows work end-to-end. It introduces shared argument/pointer/IO_STATUS helpers, adds x86 thread/context and TEB/PEB initialization, wires WOW64 gate/system data setup, and implements missing WOW64-related syscalls. It also adds Unicorn GDTR writing support for x86 segment setup and updates runtime messaging to mark WOW64 as experimental.
But not all syscalls are fixed and not all backends supports it yet. Only unicorn for now.
Introduces a full WHP emulation backend and native WinHvPlatform bindings. Also updates Windows behavior by handling `NtTerminateProcess(0, ...)` as process-wide thread termination cleanup and initializing KUSER shared data QPC bytes so that the game gets a consistant speed instead of based on FPS.
Extends the Vulkan forward/shim pipeline to support host-pointer-backed device memory via VK_EXT_external_memory_host, including device capability tracking, imported-memory allocation/free/map handling, and mapping sync rules to avoid redundant guest/host copies. Also wires a new GetHostPointer API through emulation backends (KVM/Unicorn/BinaryEmulator), adds wait-timeout polling behavior for Vulkan wait-style calls, and updates generated dispatch/proc handling for additional Vulkan commands and aliases.
Refactors Kvm.cs to reduce per-run overhead and avoid unnecessary work: mapped pages now use cached sorted keys plus fast page lookup, memslot rebuilds are gated by dirty flags, and register/special-register access uses ref-backed caches with deferred dirty writes. It also narrows pre-run instruction scanning to only active hook types, throttles MMIO region refreshes, and fixes run-loop EINTR/immediate_exit handling to prevent spin loops. Includes small correctness fixes such as validating read callbacks in mmio mapping and using safer span-based string/stack frame parsing.
Removed the early return in `Kvm.cs` that skipped registering instruction hooks whenever `NoHooks` was enabled (except `Invalid`). This fixes the issue of NoHooks option not working for KVM.
This updates Linux windowing from a minimal X11 stub to a fuller backend. richer X11 interop bindings, window state/decorations/title handling, event translation to Win32-style messages, keyboard translation, and GDI primitive/text rendering support. Host repaint/input queuing was centralized into a shared `HostEventQueue` and wired into both Windows and win32k draining so host events work consistently across platforms.
It also hardens Vulkan forwarding/serialization by respecting `enabledExtensionCount` (instead of scanning for null terminators), allocating room for C-string terminators when unpacking strings, and rejecting null dispatchable Vulkan handles to avoid invalid command dispatch.
Avoid repeatedly calling `Console.OpenStandardOutput()` by introducing a static cached `Stdout` stream and reusing it in both `ConsoleWrite` overloads.
Adds MMIO mapping support to the emulation backend and significantly improves KVM memory handling, including trapped read/write hooks, MMIO access completion, page tracking, safer backing allocation lifetime, and register cache flush/invalidate behavior around vCPU runs. It also fixes KVM constants and tightens hook validation/error paths. On the Windows side, guest time and performance counter behavior are made more consistent by using wall-clock plus skew tick accounting, exposing QPC frequency in KUSER_SHARED_DATA, scaling QueryPerformanceCounter values, and improving loader-tracker synchronization through syscall/image-map driven pumping.
Refactors the Vulkan forward generator to have extension/core type metadata from vk.xml, enforce an extension allowlist with diagnostics, and compute pNext-forwardable structs dynamically. It also expands command coverage (including extension/property enumeration, pipeline cache data, queue submit2, and 64-bit return handling), improves array/optional length handling, and increases generated output buffer sizing for count+array calls.
On the ICD side, this removes hardcoded extension property tables and routes extension enumeration through generated host-filtered data. It also adds in-shim implementations for descriptor update template and private data slot APIs, plus support-layer helpers for writing raw byte payloads and advertising filtered instance/device extension lists.
Extends the Vulkan forward generator and metadata to support many more commands and parameter shapes, including allocation callbacks, fixed-size scalar arrays, chain/pNext outputs, descriptor-type-selected arrays, and stricter length/result validation for handles and blobs. It also adds host/guest mapped-memory handling (`vkAllocate/Free/Map/UnmapMemory`, flush/invalidate ranges, queue-submit sync), tracks mappings in `GenState`, and updates the guest shim to manage mapped allocations and command recording per command buffer.
Changes struct serializer generator to require explicit [GenerateStructSerializer] attribute instead of auto-detecting fields with [EmulatedInline]/[EmulatedPointer]. Major internal refactoring includes:
- Replace implicit detection with explicit opt-in via marker attribute
- Refactor field processing into FieldSlot-based recursive walking for better nested struct/array support
- Add diagnostic reporting for unsupported field types and nesting depth limits
Add a Roslyn source generator (StructSerializerGenerator) to emit fast struct (de)serialization helpers. Enable PublishAot and related csproj settings and add trimming/AOT-friendly annotations (DynamicallyAccessedMembers, UnconditionalSuppressMessage, RequiresUnreferencedCode/RequiresDynamicCode) across serializers and interactive helpers. Improve StructSerializer (partial) with pooled buffers and safer object init (RuntimeHelpers.GetUninitializedObject). Optimize BinaryEmulator memory management: sorted memory lists, comparer, binary-search-based freed-memory operations and insertion, and related API fixes. Use AppContext.BaseDirectory for data file paths (ntdll, apisetmap.bin, WinReg, Dump, WindowsLibs, VirtualFS). Add simple Windows devices (CMApiDevice, CMNotifyDevice). Misc: replace multiple WriteMemory calls with BinaryPrimitives+stackalloc, make DllImport resolver registration robust, add handle-manager TryGet/TryRemove helpers, and fix enum GetValues usage for AOT.
Now AOT can finally fully run inside the emulator, with 60% more speed than the JIT version.
Gate expensive TriggerEventMessage calls behind Settings.Flags to avoid unnecessary formatting/log churn. Add batched register read/write passthrough in UnicornBackend and add syscall register batching/caching in LinuxGuest and WinSyscallsHelper (BeginSyscall/EndSyscall + cached args) to reduce per-register overhead. Cache CurrentThread in BinaryEmulator and simplify stack allocation. Replace numerous allocations in StructSerializer with ArrayPool+spans and make memory reads/writes more robust (open.cs reads across regions). Misc whitespace/BOM cleanups and other small fixes to reduce overhead and improve reliability.
Tighten the Vulkan ICD build script for Windows linking and Visual Studio detection, and add a syscall log when `NtQueryAttributesFile` returns `STATUS_OBJECT_NAME_NOT_FOUND` for missing non-synthetic paths. Also ignore the generated `Brovan/Properties` folder.
Refactor the extension pointer iteration logic to use a fixed capacity (1024) with null-terminator detection instead of relying on a dynamic count read, which prevents OOB reads and other issues.
Add a batch of win32k syscall and supporting window-manager plumbing for dialogs, system menus, fonts, cursors, DPI metrics, and GDI state. Also updates the shared window manager to track resizability and system-menu mutations so the host window stays in sync with guest window styles and menu changes.
Use `Unsafe.SizeOf` for PE/interop layout sizing, batch GPR state transfer, and speed up blittable struct serialization with pooled buffers. Also add section and guest-path caching, unify Windows handle tracking, and tighten path normalization and handle registration.
Adds native batch register read/write support to Unicorn and uses it to load/save thread CPU context more efficiently. Also factors syscall rule matching into a reusable helper so Windows syscall handling can reuse the resolved rule instead of scanning twice.
Adds basic GDI primitive handling plus `GetMessage`/`MsgWaitForMultipleObjectsEx` wait state support. Host input events are now forwarded into the guest message queue, and keyboard translation is used for `TranslateMessage`.
This PR adds Win32 GDI text-rendering emulation and fixes several handle/sizing bugs in the window manager subsystem.
Text rendering (new)
- Added ITextRenderSupport and ITextMetricsSupport interfaces (WindowManager.cs) exposing RenderText, MeasureText, and GetTextMetrics.
- Implemented on both backends:
- Linux (LinuxWinManager.cs): renders text via X11 (XCreateGC, XDrawString, XQueryFont, etc.)
- Windows (WindowsWinManager.cs): renders text via native GDI (ExtTextOutW, GetTextExtentPoint32W, GetTextMetricsW) using a cached memory DC for metrics queries.
GDI handle table emulation (WinSyscallsHelper.cs)
- Replaced the simplistic incrementing GDI handle counter with a proper emulated GDI handle table (allocate/free/validate handles, encrypted kernel object pointers, per-type entry initialization) mirroring the real Windows GDI handle layout.
- Added GDI batch buffer flushing (FlushGdiBatch) that parses TEB-resident TextOut batch commands and forwards them to the new text-render pipeline.
- Wired MeasureText, GetTextMetrics, and EnqueueTextRender through to the active GuiThreadManager.
Window manager threading (WindowManager.cs, Win32kHelper.cs)
- WindowManagerFactory.Create() now wraps the platform-specific display connection (WindowsWinManager or LinuxWinManager) in a GuiThreadManager, and window presentation was refactored to enqueue presents/text renders through it instead of mutating window state directly.
- Win32kHelper now allocates GDI device-context handles through the real GDI handle table (AllocateGdiHandle) instead of a local counter, and adds GetHwndFromDc / IsKnownDc lookups plus host-repaint draining after window invalidation.
Bug fixes
- WindowsWinManager create/resize path: window dimensions are now resolved from client size to outer (window) size via AdjustWindowRectEx, instead of treating the requested client size as the outer window size. Fixes windows being created or resized smaller than requested.
- BinaryEmulator: newly mapped stack memory is now zero-initialized after allocation, rather than left uninitialized.
Now the emulator supports user callbacks.
- Added NtCallbackReturn syscall
- Implement user callbacks for windows messages and added the correct size of user connect.
Some interesting changes!
- Add initial support for KVM backend as a fast alternative to Unicorn for linux. Still has bugs, but will fix them later.
- Replaced the version of Microsoft.CodeAnalysis.CSharp to 4.10.0 since it seemed that some machines couldn't compile the emulator with a newer version of this dependency.
In this commit i added IEmulationBackend abstraction over Unicorn, and most of the code base (if not all) are now neutral so it can work on other backends that's i'm gonna add.
This means that we can add hypervisors or other emulators in addition to Unicorn. this is just an initial commit though, no other backend is added yet.
Several changes took place in this commit.
- Optimized memory read/writes, with a speedup percentage of about 95% by allocating our own memory and reading/writing to it directly using `unsafe` pointers.
- Removed timing remainings at `finally` in `TryHandleSyscall` from the syscall dispatcher and added a check so we don't iterate over the rules on every syscall dispatch unless there's actually rules.
- Now the name of the syscall are directly stored so we don't have to use reflection to get it on dispatch time.
- Correct the comparer logic.
- Fixed threads terminating early because of nested exceptions, which was a bad idea to add to begin with.
- Correctly implement workers and add what was missing.
- Fix NtTerminateProcess not terminating the current thread.
Some optimizations and cleanups of the code.
- BinaryFile.cs -- Avoiding allocations and unnecessary O(2) loop in the "ParseDotNetFunctions"
- BinaryEmulator.cs -- Changing some fixed values to readonly and optimizing the sorting of memory region indexes. and also optimizing GetThreadsSnapshot by not using LINQ.
- WindowsGuest.cs -- Avoiding LINQ in the rules path and caching ntdll module. also avoiding BitConverter.GetBytes and reusing the thread state.
The rest of the commit are to ensure the state of the thread is cleared after it's death.
Some good changes in this commit.
- Added initial GUI support for Windows emulation, both Windows and Linux can render a windows window now, although it needs more work
- Optimized installation of synthetic registry values and keys. (HUGE startup performance gain noted specifically for linux)
- Fixed some stuff in ApiPort.cs and user32, although it is still broken
Add policy-enforced socket abstraction
* Introduce `BrovanSocket` as a centralized wrapper around `System.Net.Sockets.Socket`
* Enforce `NetworkAccessPolicy` consistently across socket operations
* Route network access through a single validation layer to reduce security checks duplication
* Prevent any possible accidental bypasses caused by missing policy checks in individual call sites
* Improve maintainability by consolidating network access control logic
This prevents missing or forgetting checks, and makes the validation much more stricter.