mirror of
https://github.com/felixrieseberg/macintosh.js.git
synced 2024-06-03 07:29:40 +00:00
Compare commits
72 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
6b84c60051 | ||
|
5cdaad84cc | ||
|
fb930035c7 | ||
|
b3991ef1d0 | ||
|
7dda450bf6 | ||
|
d68097b59b | ||
|
34eccc706c | ||
|
cc94f198e2 | ||
|
f5ef5da750 | ||
|
1482f6d443 | ||
|
1533ba7ec0 | ||
|
8a8aa0e7eb | ||
|
05136c28c1 | ||
|
e5b24cc04a | ||
|
d58444bc18 | ||
|
dc0b8fe19f | ||
|
895ce4c743 | ||
|
35b769949c | ||
|
4acf8bb312 | ||
|
8821a4749f | ||
|
4d57fc494c | ||
|
ede11165a9 | ||
|
bdb1033958 | ||
|
78411c9fa4 | ||
|
47aba5bb36 | ||
|
da495a9b39 | ||
|
c30866e58e | ||
|
6c1ffff0e0 | ||
|
e3c6d96785 | ||
|
60a3e787f5 | ||
|
57ad789c61 | ||
|
47863bb45e | ||
|
9c98bf1eb8 | ||
|
6df468db4d | ||
|
5320b29eed | ||
|
d5688df03f | ||
|
e121c457af | ||
|
45d061f051 | ||
|
d9a0353091 | ||
|
9622100513 | ||
|
55b97251a0 | ||
|
576200b489 | ||
|
da7c0b1513 | ||
|
f2d8191b0d | ||
|
3bd800d296 | ||
|
5e9570023b | ||
|
64d8f4a382 | ||
|
de601111f9 | ||
|
5c83088633 | ||
|
c73331bf6a | ||
|
9a1bbd40fb | ||
|
1861b3b818 | ||
|
99c4e2e31c | ||
|
ec32c63b29 | ||
|
09f4a9a3ed | ||
|
d86d40f90d | ||
|
7e32d63415 | ||
|
13f8d8bd7b | ||
|
473033ba7b | ||
|
5a4e6e6b37 | ||
|
3aae0c143c | ||
|
e5c008887b | ||
|
9cd62be64d | ||
|
c72e1294c6 | ||
|
f4c1e4dedb | ||
|
e2206e2ade | ||
|
7aa3b3612f | ||
|
5b6079eb85 | ||
|
15834f9059 | ||
|
26f44cecd6 | ||
|
5ccdddde63 | ||
|
a0aef16192 |
3
.gitattributes
vendored
3
.gitattributes
vendored
|
@ -1 +1,2 @@
|
|||
* text eol=lf
|
||||
text eol=lf
|
||||
rom -text
|
BIN
.github/images/linux.png
vendored
Normal file
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
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
BIN
.github/images/windows.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
52
.github/workflows/build.yml
vendored
52
.github/workflows/build.yml
vendored
|
@ -3,24 +3,24 @@ name: Build & Release
|
|||
on:
|
||||
push:
|
||||
branches:
|
||||
# - master
|
||||
- master
|
||||
tags:
|
||||
- v*
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 12.x
|
||||
node-version: lts/*
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- uses: actions/cache@v1
|
||||
- uses: actions/cache@v3
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
|
@ -28,26 +28,42 @@ 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:
|
||||
os: [ macOS-latest, ubuntu-latest, windows-latest ]
|
||||
# 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-20.04, windows-latest ]
|
||||
arch: [ x64, arm64 ]
|
||||
include:
|
||||
- os: windows-latest
|
||||
arch: ia32
|
||||
- os: ubuntu-20.04
|
||||
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
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 12.x
|
||||
node-version: lts/*
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- uses: actions/cache@v1
|
||||
- uses: actions/cache@v3
|
||||
if: matrix.os != 'macOS-latest'
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
|
@ -63,6 +79,7 @@ jobs:
|
|||
MACOS_CERT_PASSWORD: ${{ secrets.MACOS_CERT_PASSWORD }}
|
||||
- name: Set Windows signing certificate
|
||||
if: matrix.os == 'windows-latest'
|
||||
continue-on-error: true
|
||||
id: write_file
|
||||
uses: timheuer/base64-to-file@v1
|
||||
with:
|
||||
|
@ -82,18 +99,12 @@ 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 }}
|
||||
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:
|
||||
|
@ -105,6 +116,7 @@ jobs:
|
|||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
draft: true
|
||||
files: |
|
||||
out/**/*.deb
|
||||
out/**/*.dmg
|
||||
|
|
|
@ -4,6 +4,8 @@ This app by <a href="https://www.felixrieseberg.com">Felix Rieseberg</a>. The re
|
|||
|
||||
**Emulator**: Basilisk II, a 68k Macintosh emulator, by [Christian Bauer et al](http://basilisk.cebix.net), modified and compiled [with Emscripten](https://jamesfriend.com.au/basilisk-ii-classic-mac-emulator-in-the-browser) by [James Friend](https://jamesfriend.com.au).
|
||||
|
||||
**Runtime**: The developers behind Electron, electron-forge, Chromium, Node.js.
|
||||
|
||||
**Installed software** from vintage computing archives: [WinWorldPC](https://winworldpc.com), [Macintosh Garden](https://macintoshgarden.org), and [Macintosh Repository](https://www.macintoshrepository.org/).
|
||||
|
||||
This software is not affiliated with nor authorized by Apple. It is provided for educational purposes only. This is an unstable toy and should not be expected to work properly.
|
||||
|
|
105
README.md
105
README.md
|
@ -6,13 +6,100 @@ This is Mac OS 8, running in an [Electron](https://electronjs.org/) app pretendi
|
|||
|
||||
## Downloads
|
||||
|
||||
| | Windows | macOS | Linux |
|
||||
|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Standalone Download | 📦[Standalone, 32-bit](https://github.com/felixrieseberg/macintosh.js/releases/download/v1.0.1/macintosh.js-win32-ia32-1.0.1.zip) <br /> 📦[Standalone, 64-bit](https://github.com/felixrieseberg/macintosh.js/releases/download/v1.0.1/macintosh.js-win32-x64-1.0.1.zip) | 📦[Standalone](https://github.com/felixrieseberg/macintosh.js/releases/download/v1.0.1/macintosh.js-darwin-x64-1.0.1.zip) | |
|
||||
| Installer | 💽[Setup, 64-bit](https://github.com/felixrieseberg/macintosh.js/releases/download/v1.0.1/macintoshjs-1.0.1-setup-x64.exe) <br /> 💽[Setup, 32-bit](https://github.com/felixrieseberg/macintosh.js/releases/download/v1.0.1/macintoshjs-1.0.1-setup-ia32.exe) | | 💽[deb, 64-bit](https://github.com/felixrieseberg/macintosh.js/releases/download/v1.0.1/macintosh.js_1.0.1_amd64.deb) <br /> 💽[rpm, 64-bit](https://github.com/felixrieseberg/macintosh.js/releases/download/v1.0.1/macintosh.js-1.0.1-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/macintosh.js/releases/download/v1.2.0/macintoshjs-1.2.0-setup-ia32.exe">
|
||||
💿 Installer
|
||||
</a> |
|
||||
<a href="https://github.com/felixrieseberg/macintosh.js/releases/download/v1.2.0/macintosh.js-win32-ia32-1.2.0.zip">
|
||||
📦 Standalone Zip
|
||||
</a>
|
||||
<br />
|
||||
<span>64-bit</span>
|
||||
<a href="https://github.com/felixrieseberg/macintosh.js/releases/download/v1.2.0/macintoshjs-1.2.0-setup-x64.exe">
|
||||
💿 Installer
|
||||
</a> |
|
||||
<a href="https://github.com/felixrieseberg/macintosh.js/releases/download/v1.2.0/macintosh.js-win32-x64-1.2.0.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/macintosh.js/releases/download/v1.1.0/macintosh.js-darwin-x64-1.1.0.zip">
|
||||
📦 Standalone Zip
|
||||
</a><br />
|
||||
<span>Apple M1 Processor</span>
|
||||
<a href="https://github.com/felixrieseberg/macintosh.js/releases/download/v1.1.0/macintosh.js-darwin-arm64-1.1.0.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>32-bit</span>
|
||||
<a href="https://github.com/felixrieseberg/macintosh.js/releases/download/v1.1.0/macintosh.js-1.1.0-1.i386.rpm">
|
||||
💿 rpm
|
||||
</a> |
|
||||
<a href="https://github.com/felixrieseberg/macintosh.js/releases/download/v1.1.0/macintosh.js_1.1.0_i386.deb">
|
||||
💿 deb
|
||||
</a><br />
|
||||
<span>64-bit</span>
|
||||
<a href="https://github.com/felixrieseberg/macintosh.js/releases/download/v1.1.0/macintosh.js-1.1.0-1.x86_64.rpm">
|
||||
💿 rpm
|
||||
</a> |
|
||||
<a href="https://github.com/felixrieseberg/macintosh.js/releases/download/v1.1.0/macintosh.js_1.1.0_amd64.deb">
|
||||
💿 deb
|
||||
</a><br />
|
||||
<span>ARM64</span>
|
||||
<a href="https://github.com/felixrieseberg/macintosh.js/releases/download/v1.1.0/macintosh.js-1.1.0-1.arm64.rpm">
|
||||
💿 rpm
|
||||
</a> |
|
||||
<a href="https://github.com/felixrieseberg/macintosh.js/releases/download/v1.1.0/macintosh.js_1.1.0_arm64.deb">
|
||||
💿 deb
|
||||
</a><br />
|
||||
<span>ARMv7 (armhf)</span>
|
||||
<a href="https://github.com/felixrieseberg/macintosh.js/releases/download/v1.1.0/macintosh.js-1.1.0-1.armv7hl.rpm">
|
||||
💿 rpm
|
||||
</a> |
|
||||
<a href="https://github.com/felixrieseberg/macintosh.js/releases/download/v1.1.0/macintosh.js_1.1.0_armhf.deb">
|
||||
💿 deb
|
||||
</a><br />
|
||||
<span>
|
||||
❓ Don't know what kind of chip you have? Run `uname -m` in the console.
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr />
|
||||
|
||||
## Does it work?
|
||||
Yes! Quite well, actually - on macOS, Windows, and Linux. Bear in mind that this is written entirely in JavaScript, so please adjust your expectations. The virtual machine is emulating a 1991 Macintosh Quadra 900 with a Motorola CPU, which Apple used before switching to IBM's PowerPC architecture in the late 1990s.
|
||||
Yes! Quite well, actually - on macOS, Windows, and Linux. Bear in mind that this is written entirely in JavaScript, so please adjust your expectations. The virtual machine is emulating a 1991 Macintosh Quadra 900 with a Motorola CPU, which Apple used before switching to the PowerPC architecture (Apple/IBM/Motorola) in the mid 1990s.
|
||||
|
||||
## Should this have been a native app?
|
||||
Absolutely.
|
||||
|
@ -30,6 +117,14 @@ Yes, you can. Click on the "Help" button at the bottom of the running app to see
|
|||
|
||||
No. For what it's worth, the web was quite different 30 years ago - and you wouldn't be able to open even Google. However, Internet Explorer and Netscape are installed, as is the "Web Sharing Server", if you want to play around a bit.
|
||||
|
||||
## Should I use this for [serious application]?
|
||||
|
||||
Probably not. This is a toy - it's not the best nor the most performant way to emulate an old Macintosh. It is, however, a quick and easy way to experience a bit of nostalgia if you're _not_ trying to do anything serious with it.
|
||||
|
||||
## Credits
|
||||
|
||||
Please check out the [CREDITS](CREDITS.md)! This app wouldn't be possible without the hard work of [Christian Bauer](https://www.cebix.net/) and [James Friend](https://jamesfriend.com.au/), who did everything that seems like computing magic here.
|
||||
|
||||
## License
|
||||
|
||||
This project is provided for educational purposes only. It is not affiliated with and has
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -75,7 +75,15 @@ module.exports = {
|
|||
},
|
||||
{
|
||||
name: '@electron-forge/maker-deb',
|
||||
platforms: ['linux']
|
||||
platforms: ['linux'],
|
||||
options: {
|
||||
maintainer: 'Felix Rieseberg',
|
||||
homepage: 'https://github.com/felixrieseberg/macintosh.js',
|
||||
categories: [
|
||||
'Education',
|
||||
],
|
||||
icon: path.resolve(__dirname, 'assets', 'icon.png')
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '@electron-forge/maker-rpm',
|
||||
|
|
22
package.json
22
package.json
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "macintosh.js",
|
||||
"productName": "macintosh.js",
|
||||
"version": "1.0.2",
|
||||
"version": "1.2.0",
|
||||
"description": "Macintosh's System 8 in an Electron app. I'm sorry.",
|
||||
"main": "src/main/index.js",
|
||||
"scripts": {
|
||||
|
@ -9,8 +9,9 @@
|
|||
"package": "electron-forge package",
|
||||
"make": "electron-forge make",
|
||||
"publish": "electron-forge publish",
|
||||
"lint": "npx prettier --check src/{main,renderer}/*.{js,css}",
|
||||
"fix": "npx prettier --write src/{main,renderer}/*.{js,css}"
|
||||
"lint": "npx prettier --check src/{main,renderer}/*.{js,css} && npm run check-links",
|
||||
"fix": "npx prettier --write \"src/{main,renderer}/**.{js,css}\"",
|
||||
"check-links": "node tools/check-links.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": {
|
||||
|
@ -23,14 +24,15 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"update-electron-app": "^1.5.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-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": "10.0.0-beta.12"
|
||||
"@electron-forge/cli": "6.0.5",
|
||||
"@electron-forge/maker-deb": "6.0.5",
|
||||
"@electron-forge/maker-rpm": "6.0.5",
|
||||
"@electron-forge/maker-squirrel": "6.0.5",
|
||||
"@electron-forge/maker-zip": "6.0.5",
|
||||
"electron": "23.1.3",
|
||||
"node-fetch": "^2.6.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { error } = require("console");
|
||||
|
||||
const homeDir = require("os").homedir();
|
||||
const macDir = path.join(homeDir, "macintosh.js");
|
||||
|
@ -10,7 +9,25 @@ const macintoshCopyPath = path.join(__dirname, "user_files");
|
|||
let userDataPath;
|
||||
|
||||
function getUserDataDiskPath() {
|
||||
return path.join(userDataPath, 'disk');
|
||||
return path.join(userDataPath, "disk");
|
||||
}
|
||||
|
||||
// File type utilities
|
||||
|
||||
function isFile(v = "") {
|
||||
return fs.statSync(path.join(macDir, v)).isFile();
|
||||
}
|
||||
|
||||
function isHiddenFile(filename = '') {
|
||||
return filename.startsWith('.');
|
||||
}
|
||||
|
||||
function isCDImage(filename = '') {
|
||||
return filename.endsWith('.iso') || filename.endsWith('.toast');
|
||||
}
|
||||
|
||||
function isDiskImage(filename = '') {
|
||||
return filename.endsWith('.img') || filename.endsWith('.dsk') || filename.endsWith('.hda');
|
||||
}
|
||||
|
||||
function cleanupCopyPath() {
|
||||
|
@ -32,51 +49,47 @@ function getUserDataDiskImage() {
|
|||
}
|
||||
|
||||
const diskImageUserPath = getUserDataDiskPath();
|
||||
const diskImagePath = path.join(__dirname, 'disk');
|
||||
const diskImagePath = path.join(__dirname, "disk");
|
||||
|
||||
// If there's a disk image, move it over
|
||||
if (fs.existsSync(diskImageUserPath)) {
|
||||
// Delete a possible basilisk disk image
|
||||
if (fs.existsSync(diskImagePath)) {
|
||||
console.log(`Disk image ${diskImageUserPath} exists, deleting ${diskImagePath}`);
|
||||
fs.unlinkSync(diskImagePath);
|
||||
if (!fs.existsSync(diskImageUserPath)) {
|
||||
try {
|
||||
fs.renameSync(diskImagePath, diskImageUserPath);
|
||||
} catch (error) {
|
||||
// This is _probably_ a permissions thing, let's copy the file
|
||||
fs.copyFileSync(diskImagePath, diskImageUserPath);
|
||||
}
|
||||
|
||||
fs.renameSync(diskImageUserPath, diskImagePath);
|
||||
} else {
|
||||
console.log(`getUserDataDiskImage: No image in user data dir, not doing anything`);
|
||||
console.log(
|
||||
`getUserDataDiskImage: Image in user data dir, not doing anything`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Taken a given path, it'll look at all the files in there,
|
||||
// copy them over to the basilisk folder, and then add them
|
||||
// to MEMFS
|
||||
function copyFilesAtPath(module, sourcePath) {
|
||||
function preloadFilesAtPath(module, initalSourcePath) {
|
||||
try {
|
||||
const absoluteSourcePath = path.join(macDir, sourcePath);
|
||||
const absoluteTargetPath = path.join(macintoshCopyPath, sourcePath);
|
||||
const targetPath = `/macintosh.js${sourcePath ? `/${sourcePath}` : ""}`;
|
||||
const files = fs.readdirSync(absoluteSourcePath).filter((v) => {
|
||||
const sourcePath = path.join(macDir, initalSourcePath);
|
||||
const targetPath = `/macintosh.js${
|
||||
initalSourcePath ? `/${initalSourcePath}` : ""
|
||||
}`;
|
||||
const files = fs.readdirSync(sourcePath).filter((v) => {
|
||||
// Remove hidden, iso, and img files
|
||||
return !v.startsWith('.') && !v.endsWith(".iso") && !v.endsWith(".img");
|
||||
return !isHiddenFile(v) && !isDiskImage(v) && !isCDImage(v);
|
||||
});
|
||||
|
||||
(files || []).forEach((fileName) => {
|
||||
try {
|
||||
// If not, let's move on
|
||||
const fileSourcePath = path.join(absoluteSourcePath, fileName);
|
||||
const copyPath = path.join(absoluteTargetPath, fileName);
|
||||
const fileSourcePath = path.join(sourcePath, fileName);
|
||||
const relativeSourcePath = `${
|
||||
sourcePath ? `${sourcePath}/` : ""
|
||||
initalSourcePath ? `${initalSourcePath}/` : ""
|
||||
}${fileName}`;
|
||||
const fileUrl = `user_files/${relativeSourcePath}`;
|
||||
|
||||
// Check if directory
|
||||
if (fs.statSync(fileSourcePath).isDirectory()) {
|
||||
if (!fs.existsSync(copyPath)) {
|
||||
fs.mkdirSync(copyPath);
|
||||
}
|
||||
|
||||
try {
|
||||
const virtualDirPath = `${targetPath}/${fileName}`;
|
||||
module.FS.mkdir(virtualDirPath);
|
||||
|
@ -84,21 +97,15 @@ function copyFilesAtPath(module, sourcePath) {
|
|||
console.log(error);
|
||||
}
|
||||
|
||||
copyFilesAtPath(module, relativeSourcePath);
|
||||
preloadFilesAtPath(module, relativeSourcePath);
|
||||
return;
|
||||
}
|
||||
|
||||
// We copy the files over and then add them as preload
|
||||
console.log(`copyFilesAtPath: Adding ${fileName}`);
|
||||
fs.copyFileSync(fileSourcePath, copyPath);
|
||||
|
||||
module.FS_createPreloadedFile(
|
||||
targetPath,
|
||||
fileName,
|
||||
fileUrl,
|
||||
true,
|
||||
true
|
||||
);
|
||||
createPreloadedFile(module, {
|
||||
parent: targetPath,
|
||||
name: fileName,
|
||||
url: fileSourcePath,
|
||||
});
|
||||
} catch (error) {
|
||||
postMessage("showMessageBoxSync", {
|
||||
type: "error",
|
||||
|
@ -106,7 +113,10 @@ function copyFilesAtPath(module, sourcePath) {
|
|||
message: `We tried to transfer ${fileName} to the virtual machine, but failed. The error was: ${error}`,
|
||||
});
|
||||
|
||||
console.error(`copyFilesAtPath: Failed to preload ${fileName}`, error);
|
||||
console.error(
|
||||
`preloadFilesAtPath: Failed to preload ${fileName}`,
|
||||
error
|
||||
);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
|
@ -116,20 +126,24 @@ function copyFilesAtPath(module, sourcePath) {
|
|||
message: `We tried to transfer files to the virtual machine, but failed. The error was: ${error}`,
|
||||
});
|
||||
|
||||
console.error(`copyFilesAtPath: Failed to copyFilesAtPath`, error);
|
||||
console.error(`preloadFilesAtPath: Failed to preloadFilesAtPath`, error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createPreloadedFile(module, options) {
|
||||
const parent = options.parent || `/`;
|
||||
const name = options.name || path.basename(options.url);
|
||||
const url = options.url;
|
||||
|
||||
console.log(`Adding preload file`, { parent, name, url });
|
||||
module.FS_createPreloadedFile(parent, name, url, true, true);
|
||||
}
|
||||
|
||||
function addAutoloader(module) {
|
||||
const loadDatafiles = function () {
|
||||
module.autoloadFiles.forEach((filepath) => {
|
||||
const parent = `/`;
|
||||
const name = path.basename(filepath);
|
||||
|
||||
console.log(`Adding preload file`, { parent, name, url: filepath });
|
||||
|
||||
module.FS_createPreloadedFile(parent, name, filepath, true, true);
|
||||
});
|
||||
module.autoloadFiles.forEach(({ url, name }) =>
|
||||
createPreloadedFile(module, { url, name })
|
||||
);
|
||||
|
||||
// If the user has a macintosh.js dir, we'll copy over user
|
||||
// data
|
||||
|
@ -138,7 +152,7 @@ function addAutoloader(module) {
|
|||
}
|
||||
|
||||
// Load user files
|
||||
copyFilesAtPath(module, "");
|
||||
preloadFilesAtPath(module, "");
|
||||
};
|
||||
|
||||
if (module.autoloadFiles) {
|
||||
|
@ -182,22 +196,23 @@ function writeSafely(filePath, fileData) {
|
|||
});
|
||||
}
|
||||
|
||||
function getPrefs(userImages = []) {
|
||||
function writePrefs(userImages = []) {
|
||||
try {
|
||||
const prefsTemplatePath = path.join(__dirname, "prefs_template");
|
||||
const prefsPath = path.join(__dirname, "prefs");
|
||||
const prefsPath = path.join(userDataPath, "prefs");
|
||||
|
||||
let prefs = fs.readFileSync(prefsTemplatePath, { encoding: "utf-8" });
|
||||
|
||||
// Replace line endings, just in case
|
||||
prefs = prefs.replaceAll('\r\n', '\n');
|
||||
prefs = prefs.replaceAll("\r\n", "\n");
|
||||
|
||||
if (userImages && userImages.length > 0) {
|
||||
console.log(`getPrefs: Found ${userImages.length} user images`);
|
||||
userImages.forEach((file) => {
|
||||
if (file.endsWith(".iso")) {
|
||||
prefs += `\ncdrom ${file}`;
|
||||
} else if (file.endsWith(".img")) {
|
||||
prefs += `\ndisk ${file}`;
|
||||
console.log(`writePrefs: Found ${userImages.length} user images`);
|
||||
userImages.forEach(({ name }) => {
|
||||
if (isCDImage(name)) {
|
||||
prefs += `\ncdrom ${name}`;
|
||||
} else if (isDiskImage(name)) {
|
||||
prefs += `\ndisk ${name}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -206,81 +221,60 @@ function getPrefs(userImages = []) {
|
|||
|
||||
fs.writeFileSync(prefsPath, prefs);
|
||||
} catch (error) {
|
||||
console.error(`getPrefs: Failed to set prefs`, error);
|
||||
console.error(`writePrefs: Failed to set prefs`, error);
|
||||
}
|
||||
|
||||
return "prefs";
|
||||
}
|
||||
|
||||
function isMacDirFileOfType(extension = "", v = "") {
|
||||
const isType = v.endsWith(`.${extension}`);
|
||||
const isMatch = isType && fs.statSync(path.join(macDir, v)).isFile();
|
||||
|
||||
console.log(`isMacDirFileOfType: ${v} is file and ${extension}: ${isMatch}`);
|
||||
return isMatch;
|
||||
}
|
||||
|
||||
function copyUserImages() {
|
||||
function getUserImages() {
|
||||
const result = [];
|
||||
|
||||
try {
|
||||
// No need if the macDir doesn't exist
|
||||
if (!fs.existsSync(macDir)) {
|
||||
console.log(`autoMountImageFiles: ${macDir} does not exist, exit`);
|
||||
console.log(`getUserImages: ${macDir} does not exist, exit`);
|
||||
return result;
|
||||
}
|
||||
|
||||
const macDirFiles = fs.readdirSync(macDir);
|
||||
const imgFiles = macDirFiles.filter((v) => isMacDirFileOfType("img", v));
|
||||
const isoFiles = macDirFiles.filter((v) => isMacDirFileOfType("iso", v));
|
||||
const imgFiles = macDirFiles.filter((v) => isFile(v) && isDiskImage(v));
|
||||
const isoFiles = macDirFiles.filter((v) => isFile(v) && isCDImage(v));
|
||||
const isoImgFiles = [...isoFiles, ...imgFiles];
|
||||
|
||||
console.log(`copyUserImages: iso and img files`, isoImgFiles);
|
||||
console.log(`getUserImages: iso and img files`, isoImgFiles);
|
||||
|
||||
isoImgFiles.forEach((fileName, i) => {
|
||||
const sourcePath = path.join(macDir, fileName);
|
||||
const url = path.join(macDir, fileName);
|
||||
const sanitizedFileName = `user_image_${i}_${fileName.replace(
|
||||
/[^\w\s\.]/gi,
|
||||
""
|
||||
)}`;
|
||||
const targetPath = path.join(__dirname, sanitizedFileName);
|
||||
|
||||
if (fs.existsSync(targetPath)) {
|
||||
const sourceStat = fs.statSync(sourcePath);
|
||||
const targetStat = fs.statSync(targetPath);
|
||||
|
||||
// Copy if the length is different
|
||||
if (sourceStat.size !== targetStat.size) {
|
||||
fs.copyFileSync(sourcePath, targetPath);
|
||||
} else {
|
||||
console.log(
|
||||
`autoMountImageFiles: ${sourcePath} already exists in ${targetPath}, not copying`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
fs.copyFileSync(sourcePath, targetPath);
|
||||
}
|
||||
|
||||
console.log(`Copied over ${targetPath}`);
|
||||
result.push(sanitizedFileName);
|
||||
});
|
||||
|
||||
// Delete all old files
|
||||
const imagesCopyFiles = fs.readdirSync(__dirname);
|
||||
imagesCopyFiles.forEach((v) => {
|
||||
if (v.startsWith("user_image_") && !result.includes(v)) {
|
||||
fs.unlinkSync(path.join(__dirname, v));
|
||||
}
|
||||
result.push({ url, name: sanitizedFileName });
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`copyUserImages: Encountered error`, error);
|
||||
console.error(`getUserImages: Encountered error`, error);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getAutoLoadFiles(userImages = []) {
|
||||
const autoLoadFiles = ["disk", "rom", "prefs", ...userImages];
|
||||
const autoLoadFiles = [
|
||||
{
|
||||
name: "disk",
|
||||
url: path.join(userDataPath, "disk"),
|
||||
},
|
||||
{
|
||||
name: "rom",
|
||||
url: path.join(__dirname, "rom"),
|
||||
},
|
||||
{
|
||||
name: "prefs",
|
||||
url: path.join(userDataPath, "prefs"),
|
||||
},
|
||||
...userImages,
|
||||
];
|
||||
|
||||
return autoLoadFiles;
|
||||
}
|
||||
|
||||
|
@ -362,7 +356,6 @@ self.onmessage = async function (msg) {
|
|||
if (msg && msg.data === "disk_save") {
|
||||
const diskData = Module.FS.readFile("/disk");
|
||||
const diskPath = getUserDataDiskPath();
|
||||
const basiliskDiskPath = path.join(__dirname, 'disk');
|
||||
|
||||
// I wish we could do this with promises, but OOM crashes kill that idea
|
||||
try {
|
||||
|
@ -373,14 +366,6 @@ self.onmessage = async function (msg) {
|
|||
console.error(`Failed to write disk`, error);
|
||||
}
|
||||
|
||||
try {
|
||||
if (fs.existsSync(basiliskDiskPath) && !(Module && Module.isDevMode)) {
|
||||
fs.unlinkSync(basiliskDiskPath);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to delete ${basiliskDiskPath}`);
|
||||
}
|
||||
|
||||
// Now, user files
|
||||
console.log(`Saving user files`);
|
||||
await saveFilesInPath("/macintosh.js");
|
||||
|
@ -483,14 +468,23 @@ function startEmulator(parentConfig) {
|
|||
|
||||
let AudioConfig = null;
|
||||
let AudioBufferQueue = [];
|
||||
const userImages = copyUserImages();
|
||||
|
||||
// Check for user images
|
||||
const userImages = getUserImages();
|
||||
|
||||
// Write prefs to user data dir
|
||||
writePrefs(userImages);
|
||||
|
||||
// Assemble preload files
|
||||
const autoloadFiles = getAutoLoadFiles(userImages);
|
||||
|
||||
// Set arguments
|
||||
const arguments = ["--config", "prefs"];
|
||||
|
||||
Module = {
|
||||
autoloadFiles: getAutoLoadFiles(userImages),
|
||||
|
||||
userImages: userImages,
|
||||
|
||||
arguments: ["--config", getPrefs(userImages)],
|
||||
autoloadFiles,
|
||||
userImages,
|
||||
arguments,
|
||||
canvas: null,
|
||||
|
||||
blit: function blit(bufPtr, width, height, depth, usingPalette) {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const { app, dialog } = require("electron");
|
||||
const { getIsDevMode } = require("./devmode");
|
||||
|
||||
// If the app doesn't run from the /Applications folder,
|
||||
// we don't get to create files, which keeps the emulator from
|
||||
|
@ -8,6 +9,10 @@ function moveToAppFolderMaybe() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (getIsDevMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (app.isInApplicationsFolder()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { app, BrowserWindow, shell } = require("electron");
|
||||
const { BrowserWindow, shell } = require("electron");
|
||||
const path = require("path");
|
||||
|
||||
const { getIsDevMode } = require("./devmode");
|
||||
|
@ -61,18 +61,31 @@ function createWindow() {
|
|||
width: 900,
|
||||
height: 730,
|
||||
useContentSize: true,
|
||||
frame: false,
|
||||
frame: true,
|
||||
transparent: true,
|
||||
resizable: false,
|
||||
resizable: true,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
nodeIntegrationInWorker: true,
|
||||
nativeWindowOpen: true,
|
||||
contextIsolation: false,
|
||||
navigateOnDragDrop: false,
|
||||
nodeIntegrationInWorker: true,
|
||||
sandbox: false,
|
||||
},
|
||||
});
|
||||
|
||||
// Ensure that we have access to SharedArrayBuffer
|
||||
mainWindow.webContents.session.webRequest.onHeadersReceived(
|
||||
(details, callback) => {
|
||||
details.responseHeaders["Cross-Origin-Opener-Policy"] = ["same-origin"];
|
||||
details.responseHeaders["Cross-Origin-Embedder-Policy"] = [
|
||||
"require-corp",
|
||||
];
|
||||
callback({ responseHeaders: details.responseHeaders });
|
||||
}
|
||||
);
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile(path.join(__dirname, "../renderer/index.html"));
|
||||
|
||||
|
|
|
@ -43,8 +43,9 @@ function tryToSendInput() {
|
|||
switch (inputEvent.type) {
|
||||
case "mousemove":
|
||||
hasMouseMove = true;
|
||||
mouseMoveX += inputEvent.dx;
|
||||
mouseMoveY += inputEvent.dy;
|
||||
// Make change according to https://github.com/felixrieseberg/macintosh.js/issues/6#issuecomment-665981700
|
||||
mouseMoveX = inputEvent.dx;
|
||||
mouseMoveY = inputEvent.dy;
|
||||
break;
|
||||
case "mousedown":
|
||||
case "mouseup":
|
||||
|
|
|
@ -1,43 +1,63 @@
|
|||
const { videoModeBufferView } = require("./video");
|
||||
const { audioContext } = require("./audio");
|
||||
|
||||
const SCREEN_WIDTH = 800;
|
||||
const SCREEN_HEIGHT = 600;
|
||||
const SCREEN_BUFFER_SIZE = SCREEN_WIDTH * SCREEN_HEIGHT * 4; // 32bpp;
|
||||
const BITS = 4;
|
||||
const SCREEN_BUFFER_SIZE = 800 * 600 * BITS; // 32bpp;
|
||||
|
||||
const screenBuffer = new SharedArrayBuffer(SCREEN_BUFFER_SIZE);
|
||||
const screenBufferView = new Uint8Array(screenBuffer);
|
||||
|
||||
canvas.width = SCREEN_WIDTH;
|
||||
canvas.height = SCREEN_HEIGHT;
|
||||
let screenWidth = 800;
|
||||
let screenHeight = 600;
|
||||
|
||||
canvas.width = screenWidth;
|
||||
canvas.height = screenHeight;
|
||||
|
||||
const canvasCtx = canvas.getContext("2d");
|
||||
const imageData = canvasCtx.createImageData(SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
let imageData = canvasCtx.createImageData(screenWidth, screenHeight);
|
||||
|
||||
window.addEventListener("resize", () => {
|
||||
screenHeight = window.innerHeight - 35;
|
||||
screenWidth = Math.floor(screenHeight * (4 / 3));
|
||||
if (window.innerWidth < screenWidth) {
|
||||
screenWidth = window.innerWidth;
|
||||
screenHeight = Math.floor(screenWidth * 0.75);
|
||||
}
|
||||
canvas.width = screenWidth;
|
||||
canvas.height = screenHeight;
|
||||
imageData = canvasCtx.createImageData(screenWidth, screenHeight);
|
||||
});
|
||||
|
||||
let stopDrawing = false;
|
||||
|
||||
function drawScreen() {
|
||||
if (stopDrawing) return;
|
||||
const pixelsRGBA = imageData.data;
|
||||
const numPixels = SCREEN_WIDTH * SCREEN_HEIGHT;
|
||||
const numPixels = screenWidth * screenHeight;
|
||||
const expandedFromPalettedMode = videoModeBufferView[3];
|
||||
const start = audioContext.currentTime;
|
||||
|
||||
if (expandedFromPalettedMode) {
|
||||
for (var i = 0; i < numPixels; i++) {
|
||||
for (let i = 0; i < numPixels; i++) {
|
||||
// palette
|
||||
pixelsRGBA[i * 4 + 0] = screenBufferView[i * 4 + 0];
|
||||
pixelsRGBA[i * 4 + 1] = screenBufferView[i * 4 + 1];
|
||||
pixelsRGBA[i * 4 + 2] = screenBufferView[i * 4 + 2];
|
||||
pixelsRGBA[i * 4 + 3] = 255; // full opacity
|
||||
pixelsRGBA[i * BITS + 0] = screenBufferView[i * BITS + 0];
|
||||
pixelsRGBA[i * BITS + 1] = screenBufferView[i * BITS + 1];
|
||||
pixelsRGBA[i * BITS + 2] = screenBufferView[i * BITS + 2];
|
||||
pixelsRGBA[i * BITS + 3] = 255; // full opacity
|
||||
}
|
||||
} else {
|
||||
for (var i = 0; i < numPixels; i++) {
|
||||
// ARGB
|
||||
pixelsRGBA[i * 4 + 0] = screenBufferView[i * 4 + 1];
|
||||
pixelsRGBA[i * 4 + 1] = screenBufferView[i * 4 + 2];
|
||||
pixelsRGBA[i * 4 + 2] = screenBufferView[i * 4 + 3];
|
||||
pixelsRGBA[i * 4 + 3] = 255; // full opacity
|
||||
for (let i = 0; i < screenHeight; i++) {
|
||||
for (let j = 0; j < screenWidth; j++) {
|
||||
// ARGB
|
||||
const xRatio = 800 / screenWidth;
|
||||
const yRatio = 600 / screenHeight;
|
||||
const px = Math.floor(j * xRatio);
|
||||
const py = Math.floor(i * yRatio);
|
||||
pixelsRGBA[(i * screenWidth + j) * 4 + 0] =
|
||||
screenBufferView[(py * 800 + px) * 4 + 1]; //- lineMult];
|
||||
pixelsRGBA[(i * screenWidth + j) * 4 + 1] =
|
||||
screenBufferView[(py * 800 + px) * 4 + 2]; //- lineMult];
|
||||
pixelsRGBA[(i * screenWidth + j) * 4 + 2] =
|
||||
screenBufferView[(py * 800 + px) * 4 + 3]; //- lineMult];
|
||||
pixelsRGBA[(i * screenWidth + j) * 4 + 3] = 255; // full opacity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,7 +89,7 @@ module.exports = {
|
|||
screenBufferView,
|
||||
SCREEN_BUFFER_SIZE,
|
||||
drawScreen,
|
||||
SCREEN_WIDTH,
|
||||
SCREEN_HEIGHT,
|
||||
SCREEN_WIDTH: screenWidth,
|
||||
SCREEN_HEIGHT: screenHeight,
|
||||
setCanvasBlank,
|
||||
};
|
||||
|
|
38
tools/check-links.js
Normal file
38
tools/check-links.js
Normal 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()
|
Loading…
Reference in New Issue
Block a user