Commit Graph

7 Commits

Author SHA1 Message Date
Felix Rieseberg
c847467de6 Recover user files from orphaned state-vN.bin after a version bump (#365)
* Detect orphaned state-vN.bin and offer file recovery to a host folder

When STATE_VERSION is bumped, users previously lost their C:\ silently.
The Welcome card now detects an older state file (v4+), explains what
happened, and offers a one-click recovery: spin up a throwaway v86
(no boot), restore the legacy state to populate the hda dirty-block
overlay, walk the FAT32 tree reading overlay-if-dirty-else-base, and
copy any file the guest ever wrote out to ~/Downloads/Recovered C Drive.

Directories are created lazily so empty branches never appear; success
and failure render in the panel (no native dialogs). The geometry
constraint that keeps overlay+new-base valid is documented next to
STATE_VERSION. Also makes the dev-mode CDP port overridable via
WIN95_DEBUG_PORT so worktree instances don't fight over 9222.

* prettier
2026-04-12 12:38:28 -07:00
Felix Rieseberg
b74e6c7b0a Fix guest TCP recv() stalling when v86 NE2000 TX ring wraps (#363)
* Fix guest TCP recv() stalling under concurrent traffic

v86's fake_network stores TCPConnection routing fields (hsrc/hdest/
psrc/pdest) as zero-copy subarrays of the SYN frame, which is itself a
view into the NE2000 TX ring. Win95's driver uses a 12-slot ring; once
it wraps (any concurrent SMB/NBNS/ping while waiting for an upstream
reply), pump() emits segments with whatever IP now occupies that slot,
the guest RSTs them, and recv() blocks forever.

- libv86.js: copy the four address arrays at TCPConnection construction
  (matches felixrieseberg/v86@dd13099c on fake-network-copy-tcp-addrs,
  now merged into windows95-base)
- tools/probe-tcp.sh + net/tcp-trace.ts + tcp-relay.ts test stub +
  debug-harness WIN95_PROBE_RUN2: end-to-end regression harness
  (boot → ping -t → telnet → async write after ring wrap → assert ACK).
  All env-gated, no production-path change.
- docs/v86-patches.md: tracker for all fork patches + upstream PR state
- update-v86 SKILL.md: cross-link and new fork-branch entry

* Drop checked-in upstream PR description

Belongs on the GitHub PR, not in the repo.
2026-04-12 08:03:03 -07:00
Felix Rieseberg
6e73df11ae Enable CD-ROM via a synchronous fs-backed buffer (#362)
v86's async loaders leave the ATAPI drive in BSY across an event-loop
turn after a READ(10) CDB. Win95's ESDI_506 reads status twice, sees
BSY both times, and issues DEVICE RESET ~165 instructions later, which
cancels the in-flight read — the drive enumerates but D: never mounts.

Serve the ISO through a small fs.readSync-backed buffer so the data is
available before the next emulated instruction runs, and re-enable the
CD-ROM settings tab.

Also: WIN95_PROBE_CDROM / WIN95_PROBE_CDTRACE harness hooks, and pump
one screen-adapter frame before screenshotting so probe captures work
when the Electron window is occluded.
2026-04-11 19:31:57 -07:00
Felix Rieseberg
766497bd5d v86: defer V86-mode VBE disable; add DOS-box probe scenario (#359)
Rebuilds libv86.js from felixrieseberg/v86@windows95-base, which now
carries vga-defer-vbe-disable-v86: when a windowed DOS VM's vgabios
writes dispi[4]=0, Win9x's VDD passes that through (it doesn't know
about ports 1CE/1CF) while virtualising the rest of the mode-set, so
v86 used to drop out of LFB rendering with the legacy registers still
holding SVGA values and the screen turned to planar garbage. The fix
defers the disable until a legacy attribute-mode write actually
reaches the hardware.

debug-harness: WIN95_PROBE_DOSBOX=1 opens command from Run, types
dir, optionally Alt+Enters (WIN95_PROBE_DOSBOX_ALTENTER=1).
WIN95_PROBE_VGATRACE=1 wraps the VGA io.ports[] entries (not the
VGAScreen methods, which are captured by-value at registration) and
dumps [port, op, value, eip+VM/PE/CPL] tuples to
/tmp/win95-vgatrace.json — that EIP/mode column is what pinned the
leak on V86-mode vgabios at C000:2C8x.
2026-04-11 17:32:07 -07:00
Felix Rieseberg
85c44513cb Raw TCP relay for non-port-80 guest egress + DoH DNS with magic-name shim (#358)
The fetch network adapter only intercepts port 80 (parsed as HTTP and
replayed via fetch). Everything else was RST'd. This adds a second
tcp-connection bus listener that bridges any other destination port
straight to a Node net.Socket in the renderer — no extra process, no
WebSocket relay, no new dependencies.

- net/tcp-relay.ts: hook v86 tcp-connection, accept SYN synchronously,
  open a real socket to conn.psrc:conn.sport, buffer until connect,
  pipe both directions, tear down on either side closing. Loopback,
  link-local and multicast are refused; the rest of RFC1918 is left
  reachable so the guest can still talk to LAN devices.
- net/dns-shim.ts: dns_method is now "doh" so the guest gets real IPs
  for the relay to dial, but Cloudflare can't answer single-label names
  like "windows95" or the NNN.external localhost magic. The shim wraps
  global fetch, spots /dns-query POSTs for those names, and answers
  with the same 192.168.87.1 placeholder the static resolver used to
  hand out — the port-80 fetch path then takes over via the Host header
  exactly as before.
- emulator.tsx: wire both in next to the SMB hook; set dns_method.
- debug-harness.ts: WIN95_PROBE_RUN/_RUN_AFTER/_RUN_WAIT to type an
  arbitrary command into Start→Run (used to e2e-test the relay with
  telnet against a host echo server).
2026-04-11 15:36:20 -07:00
Felix Rieseberg
148f8e4874 SMB: long filenames, basename-derived share name, synthetic TOOLS share (#352)
Negotiate NT LM 0.12 (17-word response, Capabilities=0) so Win95 switches
from CMD_SEARCH (8.3-only) to TRANS2/FIND_FIRST2. Implement info level
0x104 (FILE_BOTH_DIRECTORY_INFO) with the win9x quirks Samba carries:
honor SearchCount/MaxDataCount (VREDIR drops the session if exceeded),
4-byte-align entries and the trans2 reply param/data blocks, dir
EndOfFile=0, FileName null-terminated with the null counted in
FileNameLength, and ShortNameLength=0 (a UCS-2 ShortName in an OEM
session GPFs shell32 on the single-directory probe Explorer does when
entering a subfolder). Add TRANS2_QUERY_FS_INFO (0x105/1/2), SMB_COM_SEEK
and core SMB_COM_READ, which the redirector falls back to with no
CAP_LARGE_READX. Sanitize >0xFF / Windows-reserved chars in display names
so emoji folder names don't truncate to '<' and wedge Explorer.

The user share is now named after path.basename of the mounted folder
(LANMAN-safe, ≤12 chars, with TOOLS/IPC$ collision avoided). A second
purely-synthetic TOOLS share holds _MAPZ.BAT and README.TXT so they
don't clutter the user's directory; treeConnect routes by share name to
a TID and every path-resolving handler branches on it so TOOLS never
touches the host fs.

48 protocol tests; verified end-to-end in the emulator (browse \\HOST,
open both shares, list Downloads with 85+ mixed-name entries, navigate
subfolders, open files in Notepad).
2026-04-11 12:26:54 -07:00
Felix Rieseberg
45f5a136b2 Add SMB1 server and host folder share
Windows 95 can now mount a host folder as a network drive at \\HOST\HOST.
Read-only, ~1500 lines, zero deps. Defaults to ~/Downloads, configurable in
Settings.

Protocol: NEGOTIATE (LANMAN2.1), SESSION_SETUP, TREE_CONNECT, TRANSACTION/RAP
(NetShareEnum, NetServerGetInfo, NetWkstaGetInfo), TRANSACTION2/FIND_FIRST2,
SEARCH (8.3 with ~N suffix mapping), OPEN_ANDX, NT_CREATE_ANDX, READ_ANDX,
CLOSE, QUERY_INFORMATION, CHECK_DIRECTORY. NetBIOS Name Service on UDP 137
answers Node Status and Name Query so \\HOST resolves.

v86 hook: monkeypatches adapter.on_tcp_connection (old API), shadows
adapter.receive during a port-80 probe to steal a TCPConnection without
side effects, re-aims it at port 139. Data via .on_data (Closure
dead-code-eliminated .on/.emit). Also registers tcp-connection bus event
for newer v86 builds.

Security: read-only, path traversal blocked lexically and through symlinks
(realpath the deepest existing ancestor, re-append tail, confirm under root).
Share path validated in main-process IPC.

BIOS updated to SeaBIOS 1.16.2 (compatible with old v86). v86 itself stays
on the Feb 2025 prod build — newer builds hang at the splash screen on fresh
boot (bisect tooling included in tools/).

Also: tools/update-v86.js builds wasm+libv86+BIOS from a local v86 checkout
and refuses to install JS/wasm pairs more than 14 days apart (copy.sh ships
mismatched pairs). tools/parcel-build.js dynamic-import patch made tolerant
of post-d4c5fa86 builds.
2026-04-11 01:03:34 -07:00