50 Commits

Author SHA1 Message Date
Felix Rieseberg
62f8eb2696 v3.1.2 2023-07-14 12:40:08 -07:00
Felix Rieseberg
da4b0dd728 Update v86 2023-07-14 12:39:57 -07:00
Felix Rieseberg
6cc05fa042 Upgrade dependencies 2023-07-14 11:52:30 -07:00
Felix Rieseberg
dda3707a23 Merge pull request #292 from fjbecerra/fix_docker_doc
fix docker mac doc
2023-03-15 11:10:24 -07:00
Francis Becerra
a4bcd7fb61 fix docker mac doc 2023-02-11 22:08:41 +00:00
Felix Rieseberg
17a8139346 Update links 2022-10-17 09:48:30 -07:00
Felix Rieseberg
489c7312d0 v3.1.1 2022-10-08 13:12:13 +02:00
Felix Rieseberg
c3537ae330 Handle promises 2022-10-08 13:12:08 +02:00
Felix Rieseberg
c483871df9 v3.1.0 2022-10-04 17:20:52 +02:00
Felix Rieseberg
e66cbd70db Update dependencies (Electron 18 > 21, Forge 63 > 66) 2022-10-04 17:07:41 +02:00
Felix Rieseberg
19a1bbc002 Update v86 to 5d02960 2022-10-04 10:09:26 +02:00
Felix Rieseberg
ef57e3a7fe Update links 2022-06-26 15:23:34 -07:00
Felix Rieseberg
7eae250c2a Fix OSFMount link 2022-06-26 14:05:49 -07:00
Felix Rieseberg
33db389d59 Start with a larger zoom 2022-06-26 13:01:05 -07:00
Felix Rieseberg
61f3269a45 Add loading gif 2022-06-26 11:55:09 -07:00
Felix Rieseberg
e5d897c663 Change how we build, hoping it'll fix macOS 2022-06-25 22:29:26 -07:00
Felix Rieseberg
a7ae665adc Build things step by step 2022-05-06 18:39:22 -07:00
Felix Rieseberg
bea2267f42 Fix Linux arch 2022-05-05 10:25:14 -07:00
Felix Rieseberg
a55d08fafc Update dist 2022-05-05 09:00:01 -07:00
Felix Rieseberg
97702cb01b Update Node in GitHub Workflows 2022-05-04 15:11:00 -07:00
Felix Rieseberg
12160a1ac4 3.0.0 2022-05-04 14:58:37 -07:00
Felix Rieseberg
3dd50db272 Cleanup 2022-05-04 14:51:49 -07:00
Felix Rieseberg
7b92d33584 Update v86, patch to use fs.readFile 2022-04-26 12:20:59 -07:00
Felix Rieseberg
24a1c30502 Update dependencies 2022-04-21 15:07:15 -07:00
Felix Rieseberg
7ce0863ae8 Merge pull request #228 from barfin/master
fixing the armv7hf linux rpm link in readme
2022-04-21 14:51:34 -07:00
Felix Rieseberg
90ec67fb16 Merge pull request #242 from hmsjy2017/patch-1
Correct the wrong links
2022-04-21 14:51:04 -07:00
Tony
9cab8e46f6 Correct the wrong links
I found that several quoted links were wrong, which caused the downloaded files to not meet expectations.
2021-08-24 01:30:04 +08:00
barfin
03b39d76b5 Update README.md 2021-04-27 13:21:04 +04:30
Felix Rieseberg
d8b4a139ac Merge pull request #203 from maacarbo/patch-1
macOS download links inverted for Intel/M1
2021-02-07 12:52:49 -08:00
Felix Rieseberg
9f4771bf26 chore: Update Readme 2021-01-31 09:24:51 -08:00
Maarten
552b97eec5 macOS download links inverted for Intel/M1 2021-01-07 10:31:39 +01:00
Felix Rieseberg
6c0f00170c Update README.md 2021-01-05 10:53:35 -08:00
Felix Rieseberg
e3b9a839f5 build: Update Node version 2021-01-04 10:31:33 -08:00
Felix Rieseberg
238b07b7dd build: Add a check-links tool 2021-01-04 09:54:20 -08:00
Felix Rieseberg
9dc1e422ff chore: One more readme update 2021-01-03 19:30:55 -08:00
Felix Rieseberg
ebe7427385 chore: Include images 2021-01-03 19:27:11 -08:00
Felix Rieseberg
3e3bee2062 chore: Pretty readme 2021-01-03 19:26:06 -08:00
Felix Rieseberg
c93b6878a9 chore: Update the readme again 2021-01-03 19:18:38 -08:00
Felix Rieseberg
d2e26ef5d1 chore: Update readme 2021-01-03 18:52:39 -08:00
Felix Rieseberg
c41befae64 build: Update dependencies 2021-01-03 12:58:37 -08:00
Felix Rieseberg
8b720750db build: Try to build for all archs 2021-01-02 15:38:15 -08:00
Felix Rieseberg
ee317ec5aa v2.3.0 2020-12-13 16:37:37 -08:00
Felix Rieseberg
d7c657e671 build: Build on ARM 2020-12-13 16:32:44 -08:00
Felix Rieseberg
7a8a54c76b build: Update Electron & React 2020-12-13 16:14:18 -08:00
Felix Rieseberg
c29f98b6bc Merge pull request #183 from felixrieseberg/dependabot/npm_and_yarn/electron-9.3.1
build(deps-dev): bump electron from 9.1.2 to 9.3.1
2020-11-08 09:48:10 -08:00
dependabot[bot]
8d1847a8d1 build(deps-dev): bump electron from 9.1.2 to 9.3.1
Bumps [electron](https://github.com/electron/electron) from 9.1.2 to 9.3.1.
- [Release notes](https://github.com/electron/electron/releases)
- [Changelog](https://github.com/electron/electron/blob/master/docs/breaking-changes.md)
- [Commits](https://github.com/electron/electron/compare/v9.1.2...v9.3.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-06 19:02:36 +00:00
Felix Rieseberg
194f4fabaf Merge pull request #169 from PeterVatistas/patch-1
Fix amd64.deb download link
2020-08-04 16:00:56 -07:00
Peter Vatistas
3f4a5e97fa Revised all download links
All 7 download links were fixed using the latest release.
2020-08-04 10:01:46 -07:00
Peter Vatistas
3eb789d055 Fix amd64.deb download link 2020-08-03 19:33:08 -07:00
Felix Rieseberg
8a8f064864 docs Update readme 2020-08-03 15:50:13 -07:00
35 changed files with 4294 additions and 4338 deletions

BIN
.github/images/linux.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
.github/images/macos.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
.github/images/windows.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -16,7 +16,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: 12.x
node-version: 18.x
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
@@ -28,22 +28,38 @@ jobs:
restore-keys: |
${{ runner.os }}-yarn-
- name: Install
run: yarn
run: yarn --frozen-lockfile
- name: lint
run: yarn lint
build:
needs: lint
name: Build (${{ matrix.os }})
name: Build (${{ matrix.os }} - ${{ matrix.arch }})
runs-on: ${{ matrix.os }}
strategy:
matrix:
# Build for supported platforms
# https://github.com/electron/electron-packager/blob/ebcbd439ff3e0f6f92fa880ff28a8670a9bcf2ab/src/targets.js#L9
# 32-bit Linux unsupported as of 2019: https://www.electronjs.org/blog/linux-32bit-support
os: [ macOS-latest, ubuntu-latest, windows-latest ]
arch: [ x64, arm64 ]
include:
- os: windows-latest
arch: ia32
- os: ubuntu-latest
arch: armv7l
# Publishing artifacts for multiple Windows architectures has
# a bug which can cause the wrong architecture to be downloaded
# for an update, so until that is fixed, only build Windows x64
exclude:
- os: windows-latest
arch: arm64
steps:
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: 12.x
node-version: 18.x
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
@@ -83,23 +99,13 @@ jobs:
run: yarn
- name: Make
if: startsWith(github.ref, 'refs/tags/')
run: yarn make
run: yarn make --arch=${{ matrix.arch }}
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
WINDOWS_CODESIGN_FILE: ${{ steps.write_file.outputs.filePath }}
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
- name: Make (ia32)
if: matrix.os == 'windows-latest' && startsWith(github.ref, 'refs/tags/')
run: yarn make -- --arch=ia32
env:
WINDOWS_CODESIGN_FILE: ${{ steps.write_file.outputs.filePath }}
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
# - name: Archive production artifacts
# uses: actions/upload-artifact@v2
# with:
# name: ${{ matrix.os }}
# path: out/make/**/*
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')

6
.gitignore vendored
View File

@@ -3,4 +3,8 @@ out
src/images
.DS_Store
images
dist
images_new
dist
!.github/images
*.code-workspace
*.pfx

View File

@@ -3,10 +3,95 @@
This is Windows 95, running in an [Electron](https://electronjs.org/) app. Yes, it's the full thing. I'm sorry.
## Downloads
| | Windows | macOS | Linux |
|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Standalone Download | 📦[Standalone, 32-bit](https://github.com/felixrieseberg/windows95/releases/download/v2.2.1/windows95-2.2.1-win32-standalone-ia32.zip) <br /> 📦[Standalone, 64-bit](https://github.com/felixrieseberg/windows95/releases/download/v2.2.1/windows95-2.2.1-win32-standalone-x64.zip) | 📦[Standalone](https://github.com/felixrieseberg/windows95/releases/download/v2.2.1/windows95-macos-2.2.1.zip) | |
| Installer | 💽[Setup, 64-bit](https://github.com/felixrieseberg/windows95/releases/download/v2.2.1/windows95-2.2.1-setup-win32-x64.exe) <br /> 💽[Setup, 32-bit](https://github.com/felixrieseberg/windows95/releases/download/v2.2.1/windows95-2.2.1-setup-win32-ia32.exe) | | 💽[deb, 64-bit](https://github.com/felixrieseberg/windows95/releases/download/v2.2.1/windows95-linux-2.2.1_amd64.deb) <br /> 💽[rpm, 64-bit](https://github.com/felixrieseberg/windows95/releases/download/v2.2.1/windows95-linux-2.2.1.x86_64.rpm) |
<table class="is-fullwidth">
</thead>
<tbody>
</tbody>
<tr>
<td>
<img src="./.github/images/windows.png" width="24"><br />
Windows
</td>
<td>
<span>32-bit</span>
<a href="https://github.com/felixrieseberg/windows95/releases/download/v3.1.1/windows95-3.1.1-setup-ia32.exe">
💿 Installer
</a> |
<a href="https://github.com/felixrieseberg/windows95/releases/download/v3.1.1/windows95-win32-ia32-3.1.1.zip">
📦 Standalone Zip
</a>
<br />
<span>64-bit</span>
<a href="https://github.com/felixrieseberg/windows95/releases/download/v3.1.1/windows95-3.1.1-setup-x64.exe">
💿 Installer
</a> |
<a href="https://github.com/felixrieseberg/windows95/releases/download/v3.1.1/windows95-win32-x64-3.1.1.zip">
📦 Standalone Zip
</a><br />
<span>ARM64</span>
<a href="https://github.com/felixrieseberg/windows95/releases/download/v3.1.1/windows95-3.1.1-setup-arm64.exe">
💿 Installer
</a> |
<a href="https://github.com/felixrieseberg/windows95/releases/download/v3.1.1/windows95-win32-arm64-3.1.1.zip">
📦 Standalone Zip
</a><br />
<span>
❓ Don't know what kind of chip you have? Hit start, enter "processor" for info.
</span>
</td>
</tr>
<tr>
<td>
<img src="./.github/images/macos.png" width="24"><br />
macOS
</td>
<td>
<span>Intel Processor</span>
<a href="https://github.com/felixrieseberg/windows95/releases/download/v3.1.1/windows95-darwin-x64-3.1.1.zip">
📦 Standalone Zip
</a><br />
<span>Apple M1 Processor</span>
<a href="https://github.com/felixrieseberg/windows95/releases/download/v3.1.1/windows95-darwin-arm64-3.1.1.zip">
📦 Standalone Zip
</a><br />
<span>
❓ Don't know what kind of chip you have? Learn more at <a href="https://support.apple.com/en-us/HT211814">apple.com</a>.
</span>
</td>
</tr>
<tr>
<td>
<img src="./.github/images/linux.png" width="24"><br />
Linux
</td>
<td>
<span>64-bit</span>
<a href="https://github.com/felixrieseberg/windows95/releases/download/v3.1.1/windows95-3.1.1-1.x86_64.rpm">
💿 rpm
</a> |
<a href="https://github.com/felixrieseberg/windows95/releases/download/v3.1.1/windows95_3.1.1_amd64.deb">
💿 deb
</a><br />
<span>ARM64</span>
<a href="https://github.com/felixrieseberg/windows95/releases/download/v3.1.1/windows95-3.1.1-1.arm64.rpm">
💿 rpm
</a> |
<a href="https://github.com/felixrieseberg/windows95/releases/download/v3.1.1/windows95_3.1.1_arm64.deb">
💿 deb
</a><br />
<span>ARMv7 (armhf)</span>
<a href="https://github.com/felixrieseberg/windows95/releases/download/v3.1.1/windows95-3.1.1-1.armv7hl.rpm">
💿 rpm
</a> |
<a href="https://github.com/felixrieseberg/windows95/releases/download/v3.1.1/windows95_3.1.1_armhf.deb">
💿 deb
</a>
</td>
</tr>
</table>
<hr />
![Screenshot](https://user-images.githubusercontent.com/1426799/44532591-4ceb3680-a6a8-11e8-8c2c-bc29f3bfdef7.png)
@@ -23,7 +108,7 @@ You'll likely be better off with an actual virtualization app, but the short ans
## Credits
99% of the work was done over at [v86](https://github.com/copy/v86/) by Copy.
99% of the work was done over at [v86](https://github.com/copy/v86/) by Copy aka Fabian Hemmer and his contributors.
## Contributing
@@ -44,6 +129,8 @@ Unpack the `images` folder into the `src` folder, creating this layout:
Once you've done so, run `npm install` and `npm start` to run your local build.
If you want to tinker with the image or make a new one, check out the [QEMU docs](./docs/qemu.md).
## Other Questions
* [MS-DOS seems to brick the screen](./HELP.md#ms-dos-seems-to-brick-the-screen)

BIN
assets/boot.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -35,5 +35,5 @@ xhost +
```
4. run
```
docker run -it -e DISPLAY=host.docker.internal:1 toolboc/windows95
docker run -it -e DISPLAY=host.docker.internal:0 toolboc/windows95
```

28
docs/qemu.md Normal file
View File

@@ -0,0 +1,28 @@
# QEMU Instructions
The image built here was made with QEMU. In this doc, I'm keeping instructions
around.
Disk image creation
```sh
qemu-img create -f qcow2 win95.qcow2 1G
```
Installation
```sh
qemu-system-i386 -netdev user,id=mynet0 -device ne2k_isa,netdev=mynet0 -hda win95.qcow2 -soundhw sb16 -m 128 -cpu pentium -device cirrus-vga,vgamem_mb=64 -fda boot_floppy.img -cdrom Win95_OSR25.iso -boot a -soundhw pcspk
```
Running
With `ne2k_isa`
```sh
qemu-system-i386 -netdev user,id=mynet0 -device ne2k_isa,netdev=mynet0 -drive file=win95.img,format=raw,index=0,media=disk -soundhw sb16 -m 128 -cpu pentium -device cirrus-vga,vgamem_mb=16 -soundhw pcspk -cdrom Win95_OSR25.iso
```
With `ne2k_pci`
```sh
qemu-system-i386 -net nic,model=ne2k_pci -net user -drive file=win95_ne2k_pci.img,format=raw,index=0,media=disk -soundhw sb16 -m 128 -cpu pentium -device cirrus-vga,vgamem_mb=16 -soundhw pcspk -cdrom Win95_OSR25.iso --enable-kvm
```

View File

@@ -62,6 +62,8 @@ module.exports = {
exe: 'windows95.exe',
noMsi: true,
remoteReleases: '',
iconUrl: 'https://raw.githubusercontent.com/felixrieseberg/windows95/master/assets/icon.ico',
loadingGif: './assets/boot.gif',
setupExe: `windows95-${package.version}-setup-${arch}.exe`,
setupIcon: path.resolve(__dirname, 'assets', 'icon.ico'),
certificateFile: process.env['WINDOWS_CODESIGN_FILE'],

View File

@@ -1,16 +1,18 @@
{
"name": "windows95",
"productName": "windows95",
"version": "2.2.2",
"version": "3.1.2",
"description": "Windows 95, in an app. I'm sorry.",
"main": "./dist/src/main/main",
"main": "./dist/src/main/main.js",
"scripts": {
"start": "rimraf ./dist && electron-forge start",
"package": "electron-forge package",
"make": "electron-forge make",
"publish": "electron-forge publish",
"lint": "prettier --write src/**/*.{ts,tsx}",
"less": "node ./tools/lessc.js"
"lint": "prettier --write src/**/*.{ts,tsx} && npm run check-links",
"less": "node ./tools/lessc.js",
"tsc": "tsc -p tsconfig.json --noEmit",
"check-links": "node tools/check-links.js"
},
"keywords": [],
"author": "Felix Rieseberg, felix@felixrieseberg.com",
@@ -20,31 +22,30 @@
},
"dependencies": {
"electron-squirrel-startup": "^1.0.0",
"fs-extra": "^9.0.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"tslib": "^2.0.0",
"update-electron-app": "^1.5.0"
"fs-extra": "^10.1.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"tslib": "^2.4.0",
"update-electron-app": "^2.0.1"
},
"devDependencies": {
"@electron-forge/cli": "^6.0.0-beta.52",
"@electron-forge/maker-deb": "^6.0.0-beta.52",
"@electron-forge/maker-flatpak": "^6.0.0-beta.52",
"@electron-forge/maker-rpm": "^6.0.0-beta.52",
"@electron-forge/maker-squirrel": "^6.0.0-beta.52",
"@electron-forge/maker-zip": "^6.0.0-beta.52",
"@electron-forge/publisher-github": "^6.0.0-beta.52",
"@types/fs-extra": "^9.0.1",
"@types/node": "^12.12.14",
"@types/react": "^16.9.44",
"@types/react-dom": "^16.9.8",
"electron": "9.1.2",
"less": "^3.12.2",
"node-abi": "^2.18.0",
"parcel-bundler": "^1.12.4",
"prettier": "^2.0.5",
"rimraf": "^3.0.2",
"standard": "^14.3.4",
"typescript": "^3.9.7"
"@electron-forge/cli": "6.2.1",
"@electron-forge/maker-deb": "6.2.1",
"@electron-forge/maker-flatpak": "^6.2.1",
"@electron-forge/maker-rpm": "^6.2.1",
"@electron-forge/maker-squirrel": "^6.2.1",
"@electron-forge/maker-zip": "^6.2.1",
"@electron-forge/publisher-github": "^6.2.1",
"@types/fs-extra": "^9.0.13",
"@types/node": "^12.19.9",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"electron": "25.3.0",
"less": "^3.13.0",
"node-abi": "^3.45.0",
"parcel-bundler": "^1.12.5",
"prettier": "^3.0.0",
"rimraf": "^5.0.1",
"typescript": "^5.1.6"
}
}

View File

@@ -11,27 +11,22 @@ export async function clearCache() {
}
}
export function clearStorageData() {
return new Promise((resolve) => {
if (!session.defaultSession) {
return resolve();
}
export async function clearStorageData() {
if (!session.defaultSession) {
return;
}
session.defaultSession.clearStorageData(
{
storages: [
"appcache",
"cookies",
"filesystem",
"indexdb",
"localstorage",
"shadercache",
"websql",
"serviceworkers",
],
quotas: ["temporary", "persistent", "syncable"],
},
resolve
);
await session.defaultSession.clearStorageData({
storages: [
"appcache",
"cookies",
"filesystem",
"indexdb",
"localstorage",
"shadercache",
"websql",
"serviceworkers",
],
quotas: ["temporary", "persistent", "syncable"],
});
}

View File

@@ -1,13 +1,9 @@
import { remote, app } from "electron";
import * as path from "path";
const _app = app || remote.app;
export const CONSTANTS = {
IMAGE_PATH: path.join(__dirname, "../../images/windows95.img"),
IMAGE_DEFAULT_SIZE: 1073741824, // 1GB
DEFAULT_STATE_PATH: path.join(__dirname, "../../images/default-state.bin"),
STATE_PATH: path.join(_app.getPath("userData"), "state-v2.bin"),
};
export const IPC_COMMANDS = {
@@ -28,4 +24,7 @@ export const IPC_COMMANDS = {
// Machine events
MACHINE_STARTED: "MACHINE_STARTED",
MACHINE_STOPPED: "MACHINE_STOPPED",
// Else
APP_QUIT: "APP_QUIT",
GET_STATE_PATH: "GET_STATE_PATH",
};

14
src/main/ipc.ts Normal file
View File

@@ -0,0 +1,14 @@
import { ipcMain, app } from "electron";
import * as path from "path";
import { IPC_COMMANDS } from "../constants";
export function setupIpcListeners() {
ipcMain.handle(IPC_COMMANDS.GET_STATE_PATH, () => {
return path.join(app.getPath("userData"), "state-v3.bin");
});
ipcMain.handle(IPC_COMMANDS.APP_QUIT, () => {
app.quit();
});
}

View File

@@ -6,6 +6,7 @@ import { shouldQuit } from "./squirrel";
import { setupUpdates } from "./update";
import { getOrCreateWindow } from "./windows";
import { setupMenu } from "./menu";
import { setupIpcListeners } from "./ipc";
/**
* Handle the app's "ready" event. This is essentially
@@ -14,6 +15,7 @@ import { setupMenu } from "./menu";
export async function onReady() {
if (!isDevMode()) process.env.NODE_ENV = "production";
setupIpcListeners();
getOrCreateWindow();
setupAboutPanel();
setupMenu();

View File

@@ -1,4 +1,4 @@
import { BrowserWindow } from "electron";
import { BrowserWindow, shell } from "electron";
let mainWindow: BrowserWindow | null = null;
@@ -14,14 +14,30 @@ export function getOrCreateWindow(): BrowserWindow {
nodeIntegration: true,
sandbox: false,
webviewTag: false,
contextIsolation: false,
},
});
// mainWindow.webContents.toggleDevTools();
mainWindow.loadFile("./dist/static/index.html");
mainWindow.webContents.on("will-navigate", (event, url) =>
handleNavigation(event, url)
);
mainWindow.webContents.on("new-window", (event, url) =>
handleNavigation(event, url)
);
mainWindow.on("closed", () => {
mainWindow = null;
});
return mainWindow;
}
function handleNavigation(event: Electron.Event, url: string) {
if (url.startsWith("http")) {
event.preventDefault();
shell.openExternal(url);
}
}

View File

@@ -53,8 +53,14 @@ export class CardDrive extends React.Component<CardDriveProps, CardDriveState> {
<p>
Windows 10 cannot mount raw disk images (ironically, macOS and Linux
can). However, tools exist that let you mount this drive, like the
freeware tool <a href="https://google.com">OSFMount</a>. I am not
affiliated with it, so please use it at your own risk.
freeware tool{" "}
<a
target="_blank"
href="https://www.osforensics.com/tools/mount-disk-images.html"
>
OSFMount
</a>
. I am not affiliated with it, so please use it at your own risk.
</p>
{this.renderMountButton("Windows Explorer")}
</fieldset>

View File

@@ -1,12 +1,14 @@
import * as React from "react";
import * as fs from "fs-extra";
import { CONSTANTS } from "../constants";
import { getStatePath } from "./utils/get-state-path";
interface CardSettingsProps {
bootFromScratch: () => void;
setFloppy: (file: File) => void;
setCdrom: (cdrom: File) => void;
floppy?: File;
cdrom?: File;
}
interface CardSettingsState {
@@ -21,6 +23,7 @@ export class CardSettings extends React.Component<
super(props);
this.onChangeFloppy = this.onChangeFloppy.bind(this);
this.onChangeCdrom = this.onChangeCdrom.bind(this);
this.onResetState = this.onResetState.bind(this);
this.state = {
@@ -39,6 +42,8 @@ export class CardSettings extends React.Component<
</h2>
</div>
<div className="card-body">
{this.renderCdrom()}
<hr />
{this.renderFloppy()}
<hr />
{this.renderState()}
@@ -48,6 +53,44 @@ export class CardSettings extends React.Component<
);
}
public renderCdrom() {
// CD is currently not working, so.. let's return nothing.
return null;
const { cdrom } = this.props;
return (
<fieldset>
<legend>
<img src="../../static/cdrom.png" />
CD-ROM
</legend>
<input
id="cdrom-input"
type="file"
onChange={this.onChangeCdrom}
style={{ display: "none" }}
/>
<p>
windows95 comes with a virtual CD drive. It can mount images in the
"iso" format.
</p>
<p id="floppy-path">
{cdrom ? `Inserted CD: ${cdrom?.path}` : `No CD mounted`}
</p>
<button
className="btn"
onClick={() =>
(document.querySelector("#cdrom-input") as any).click()
}
>
<img src="../../static/select-cdrom.png" />
<span>Mount CD</span>
</button>
</fieldset>
);
}
public renderFloppy() {
const { floppy } = this.props;
@@ -148,12 +191,32 @@ export class CardSettings extends React.Component<
}
}
/**
* Handle a change in the cdrom input
*
* @param event
*/
private onChangeCdrom(event: React.ChangeEvent<HTMLInputElement>) {
const CdromFile =
event.target.files && event.target.files.length > 0
? event.target.files[0]
: null;
if (CdromFile) {
this.props.setCdrom(CdromFile);
} else {
console.log(`Cdrom: Input changed but no file selected`);
}
}
/**
* Handle the state reset
*/
private async onResetState() {
if (fs.existsSync(CONSTANTS.STATE_PATH)) {
await fs.remove(CONSTANTS.STATE_PATH);
const statePath = await getStatePath();
if (fs.existsSync(statePath)) {
await fs.remove(statePath);
}
this.setState({ isStateReset: true });

View File

@@ -85,7 +85,7 @@ export class EmulatorInfo extends React.Component<
}
// TypeScript think's we're using a Node.js setInterval. We're not.
this.cpuInterval = (setInterval(this.cpuCount, 500) as unknown) as number;
this.cpuInterval = setInterval(this.cpuCount, 500) as unknown as number;
// Disk
emulator.add_listener("ide-read-start", this.onIDEReadStart);

View File

@@ -1,7 +1,7 @@
import * as React from "react";
import * as fs from "fs-extra";
import * as path from "path";
import { ipcRenderer, remote, shell } from "electron";
import { ipcRenderer, shell } from "electron";
import { CONSTANTS, IPC_COMMANDS } from "../constants";
import { getDiskImageSize } from "../utils/disk-image-size";
@@ -10,12 +10,14 @@ import { StartMenu } from "./start-menu";
import { CardSettings } from "./card-settings";
import { EmulatorInfo } from "./emulator-info";
import { CardDrive } from "./card-drive";
import { getStatePath } from "./utils/get-state-path";
export interface EmulatorState {
currentUiCard: string;
emulator?: any;
scale: number;
floppyFile?: File;
cdromFile?: File;
isBootingFresh: boolean;
isCursorCaptured: boolean;
isInfoDisplayed: boolean;
@@ -41,7 +43,9 @@ export class Emulator extends React.Component<{}, EmulatorState> {
isRunning: false,
currentUiCard: "start",
isInfoDisplayed: true,
scale: 1,
// We can start pretty large
// If it's too large, it'll just grow until it hits borders
scale: 2,
};
this.setupInputListeners();
@@ -95,11 +99,11 @@ export class Emulator extends React.Component<{}, EmulatorState> {
this.isQuitting = true;
setImmediate(() => {
remote.app.quit();
ipcRenderer.invoke(IPC_COMMANDS.APP_QUIT);
});
};
window.onbeforeunload = (event) => {
window.onbeforeunload = (event: Event) => {
if (this.isQuitting || this.isResetting) {
console.log(`Unload: Not preventing`);
return;
@@ -178,7 +182,7 @@ export class Emulator extends React.Component<{}, EmulatorState> {
* 🤡
*/
public renderUI() {
const { isRunning, currentUiCard, floppyFile } = this.state;
const { isRunning, currentUiCard, floppyFile, cdromFile } = this.state;
if (isRunning) {
return null;
@@ -190,8 +194,10 @@ export class Emulator extends React.Component<{}, EmulatorState> {
card = (
<CardSettings
setFloppy={(floppyFile) => this.setState({ floppyFile })}
setCdrom={(cdromFile) => this.setState({ cdromFile })}
bootFromScratch={this.bootFromScratch}
floppy={floppyFile}
cdrom={cdromFile}
/>
);
} else if (currentUiCard === "drive") {
@@ -260,7 +266,6 @@ export class Emulator extends React.Component<{}, EmulatorState> {
const imagePath = path.join(__dirname, "../../images/windows95.img");
console.log(`Showing disk image in ${imagePath}`);
``;
shell.showItemInFolder(imagePath);
}
@@ -271,26 +276,37 @@ export class Emulator extends React.Component<{}, EmulatorState> {
private async startEmulator() {
document.body.classList.remove("paused");
const imageSize = await getDiskImageSize();
const cdrom: any = {};
const cdromFile: any = this.state.cdromFile;
if (cdromFile?.path) {
cdrom.url = cdromFile.path;
cdrom.async = true;
cdrom.size = await getDiskImageSize(cdromFile.path);
}
const options = {
wasm_path: path.join(__dirname, "build/v86.wasm"),
memory_size: 128 * 1024 * 1024,
video_memory_size: 32 * 1024 * 1024,
vga_memory_size: 32 * 1024 * 1024,
screen_container: document.getElementById("emulator"),
bios: {
url: "../../bios/seabios.bin",
url: path.join(__dirname, "../../bios/seabios.bin"),
},
vga_bios: {
url: "../../bios/vgabios.bin",
url: path.join(__dirname, "../../bios/vgabios.bin"),
},
hda: {
url: "../../images/windows95.img",
url: CONSTANTS.IMAGE_PATH,
async: true,
size: imageSize,
size: await getDiskImageSize(CONSTANTS.IMAGE_PATH),
},
fda: {
buffer: this.state.floppyFile,
},
cdrom: cdrom,
boot_order: 0x132,
// One day, maybe!
// network_relay_url: "ws://localhost:8080/"
};
console.log(`🚜 Starting emulator with options`, options);
@@ -315,6 +331,7 @@ export class Emulator extends React.Component<{}, EmulatorState> {
this.lockMouse();
this.state.emulator.run();
this.state.emulator.screen_set_scale(this.state.scale);
}, 500);
}
@@ -344,7 +361,7 @@ export class Emulator extends React.Component<{}, EmulatorState> {
await this.saveState();
this.unlockMouse();
emulator.stop();
await emulator.stop();
this.setState({ isRunning: false });
document.body.classList.add("paused");
@@ -366,34 +383,27 @@ export class Emulator extends React.Component<{}, EmulatorState> {
*/
private async saveState(): Promise<void> {
const { emulator } = this.state;
const statePath = await getStatePath();
return new Promise((resolve) => {
if (!emulator || !emulator.save_state) {
console.log(`restoreState: No emulator present`);
return resolve();
}
if (!emulator || !emulator.save_state) {
console.log(`restoreState: No emulator present`);
return;
}
emulator.save_state(async (error: Error, newState: ArrayBuffer) => {
if (error) {
console.warn(`saveState: Could not save state`, error);
return resolve();
}
await fs.outputFile(CONSTANTS.STATE_PATH, Buffer.from(newState));
console.log(`saveState: Saved state to ${CONSTANTS.STATE_PATH}`);
resolve();
});
});
try {
const newState = await emulator.save_state();
await fs.outputFile(statePath, Buffer.from(newState));
} catch (error) {
console.warn(`saveState: Could not save state`, error);
}
}
/**
* Restores state to the emulator.
*/
private restoreState() {
private async restoreState() {
const { emulator } = this.state;
const state = this.getState();
const state = await this.getState();
// Nothing to do with if we don't have a state
if (!state) {
@@ -405,7 +415,7 @@ export class Emulator extends React.Component<{}, EmulatorState> {
}
try {
this.state.emulator.restore_state(state);
await this.state.emulator.restore_state(state);
} catch (error) {
console.log(
`State: Could not read state file. Maybe none exists?`,
@@ -420,9 +430,10 @@ export class Emulator extends React.Component<{}, EmulatorState> {
*
* @returns {ArrayBuffer}
*/
private getState(): ArrayBuffer | null {
const statePath = fs.existsSync(CONSTANTS.STATE_PATH)
? CONSTANTS.STATE_PATH
private async getState(): Promise<ArrayBuffer | null> {
const expectedStatePath = await getStatePath();
const statePath = fs.existsSync(expectedStatePath)
? expectedStatePath
: CONSTANTS.DEFAULT_STATE_PATH;
if (fs.existsSync(statePath)) {

View File

@@ -1,4 +1,4 @@
Copyright (c) 2012-2018, Fabian Hemmer
Copyright (c) 2012, The v86 contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -19,8 +19,4 @@ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

BIN
src/renderer/lib/build/v86.wasm Executable file

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
import { ipcRenderer } from "electron";
import { IPC_COMMANDS } from "../../constants";
let _statePath = "";
export async function getStatePath(): Promise<string> {
if (_statePath) {
return _statePath;
}
const statePath = await ipcRenderer.invoke(IPC_COMMANDS.GET_STATE_PATH);
return (_statePath = statePath);
}

View File

@@ -7,9 +7,9 @@ import { CONSTANTS } from "../constants";
*
* @returns {number}
*/
export async function getDiskImageSize() {
export async function getDiskImageSize(path: string) {
try {
const stats = await fs.stat(CONSTANTS.IMAGE_PATH);
const stats = await fs.stat(path);
if (stats) {
return stats.size;

BIN
static/cdrom.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 B

View File

@@ -7,7 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../src/less/vendor/95css.css">
<link rel="stylesheet" href="../src/less/root.less">
<script src="../src/renderer/lib/libv86.js"></script>
<!-- libv86 -->
</head>
<body class="paused windows95">
<div id="app"></div>

BIN
static/select-cdrom.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 519 B

38
tools/check-links.js Normal file
View File

@@ -0,0 +1,38 @@
const fs = require('fs/promises')
const path = require('path')
const fetch = require('node-fetch')
const LINK_RGX = /(http|ftp|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?/g;
async function main() {
const readmePath = path.join(__dirname, '../README.md')
const readme = await fs.readFile(readmePath, 'utf-8')
const links = readme.match(LINK_RGX)
let failed = false
for (const link of links) {
try {
const response = await fetch(link, { method: 'HEAD' })
if (!response.ok) {
// If we're inside GitHub's release asset server, we just ran into AWS not allowing
// HEAD requests, which is different from a 404.
if (!response.url.startsWith('https://github-production-release-asset')) {
throw new Error (`HTTP Error Response: ${response.status} ${response.statusText}`)
}
}
console.log(`${link}`);
} catch (error) {
failed = true
console.log(`${link}\n${error}`)
}
}
if (failed) {
process.exit(-1);
}
}
main()

View File

@@ -2,6 +2,27 @@
const Bundler = require('parcel-bundler')
const path = require('path')
const fs = require('fs-extra')
async function copyLib() {
const target = path.join(__dirname, '../dist/static')
const lib = path.join(__dirname, '../src/renderer/lib')
const index = path.join(target, 'index.html')
// Copy in lib
await fs.copy(lib, target)
// Patch so that fs.read is used
const libv86path = path.join(target, 'libv86.js')
const libv86 = fs.readFileSync(libv86path, 'utf-8')
const patchedLibv86 = libv86.replace('v86util.load_file="undefined"===typeof XMLHttpRequest', 'v86util.load_file="undefined"!==typeof XMLHttpRequest')
fs.writeFileSync(libv86path, patchedLibv86)
// Overwrite
const indexContents = fs.readFileSync(index, 'utf-8');
const replacedContents = indexContents.replace('<!-- libv86 -->', '<script src="libv86.js"></script>')
fs.writeFileSync(index, replacedContents)
}
async function compileParcel (options = {}) {
const entryFiles = [
@@ -38,8 +59,12 @@ async function compileParcel (options = {}) {
// Run the bundler, this returns the main bundle
// Use the events if you're using watch mode as this promise will only trigger once and not for every rebuild
await bundler.bundle()
await copyLib();
}
module.exports = {
compileParcel
}

6410
yarn.lock

File diff suppressed because it is too large Load Diff