Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,21 +116,27 @@ implementing anything it covers.

| file | what it covers |
|---|---|
| `docs/getting-started.md` | Diablo IV from an empty Mac to a keypress; the only file here for using sake |
| `docs/roadmap.md` | phases, and the settled boundary between Swift and subprocesses |
| `docs/wine-build.md` | building Wine; the configure flags that must not be removed |
| `docs/runtime.md` | the three settings games need, the Play-button root cause, controllers, failure states |
| `docs/licensing.md` | what may not be redistributed, and what the app may not do for the user |
| `docs/layout.md` | on-disk layout, why nothing mutable goes in the bundle, relocatability |
| `docs/releasing.md` | how a release is cut, and the three things it needs that are not in this repository |

Two standing rules about that content:
Three standing rules about that content:

- **Claims in `docs/` carry their source and date.** Most were measured in the prototype and
not in sake; do not restate those as sake's own behaviour. Where sake has measured
something itself the section says so and gives the date — keep that distinction, and date
what you add.
- **Do not relitigate the Swift/subprocess boundary** without new information;
`docs/roadmap.md` records why it is where it is.
- **`docs/getting-started.md` is instructions, and stays that way.** It is written for somebody
using sake rather than building it: steps rather than prose, and **no dated claims at all** —
what has been run here, what has not, and on which hardware belongs in the files that already
carry it, because a page people follow is where that goes stale first. Screenshots go in
`assets/getting-started/`.

## Licence boundary

Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ deliberately does not offer a button that starts Diablo IV directly: the client
out a login token after that press, so a direct start reaches the game and then fails on the
token. `docs/runtime.md` has the measurements behind that.

**[`docs/getting-started.md`](docs/getting-started.md) walks one game through all of this
with screenshots** — Diablo IV, from a Mac with nothing on it to a character on screen.

## Why build Wine at all

The piece that makes DirectX 12 work on macOS is Apple's closed D3DMetal, and the Wine-side
Expand All @@ -108,8 +111,8 @@ unmounts it again. See `docs/licensing.md`.
| `Sources/SakeKit/` | the layout, a subprocess runner, the preflight checks, the source fetcher, the prefix build, the Wine build, the patch step, the D3DMetal step, the bottle, the import, the installer, the titles, starting one, how big a tree is, and the uninstall |
| `Sources/sake/` | the SwiftUI app — two windows, kept thin |
| `patches/` | the changes sake makes to Wine's own code — LGPL-2.1-or-later, not MIT; `patches/README.md` says where each came from |
| `docs/` | how the thing actually has to work, and what breaks when it doesn't |
| `assets/` | the app icon, the code that draws it, and the screenshot above |
| `docs/` | how the thing actually has to work, what breaks when it doesn't, and how to play one game |
| `assets/` | the app icon, the code that draws it, the screenshot above, and the guide's under `getting-started/` |
| `scripts/build-app.sh` | builds `target/Sake.app` |
| `scripts/make-icon.sh` | redraws `assets/Sake.icns` |
| `scripts/test.sh` | runs the tests |
Expand Down Expand Up @@ -137,6 +140,7 @@ and carry their own date.

| file | what it covers |
|---|---|
| `docs/getting-started.md` | the one written for using sake rather than building it: Diablo IV, step by step, with screenshots |
| `docs/roadmap.md` | the goal, the phases, and where Swift stops and subprocesses start |
| `docs/wine-build.md` | building Wine from CrossOver's sources; the flags that cannot be dropped |
| `docs/runtime.md` | creating a prefix, the three settings that make games run, what pressing Play actually does, controllers, taking a bottle down, and how to tell four failure states apart |
Expand Down
Binary file added assets/getting-started/01-this-mac.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/getting-started/02-sources.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/getting-started/03-libraries.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/getting-started/04-wine.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/getting-started/05-d3dmetal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/getting-started/06-bottle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/getting-started/07-bottle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/getting-started/08-install.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/getting-started/09-add-title.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/getting-started/10-title.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
126 changes: 126 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# Getting started: Diablo IV

Diablo IV on an Apple silicon Mac, from nothing installed to a character on screen.

## Before you start

- An Apple silicon Mac with Rosetta 2 — `softwareupdate --install-rosetta`.
- macOS 15 or newer, and the Xcode Command Line Tools — `xcode-select --install`.
- Apple's Game Porting Toolkit `.dmg` — **4.0 beta 2**, which is what the engine here is built
against — from <https://developer.apple.com/download/all/>. A free Apple ID is enough. sake
cannot fetch this for you; `docs/licensing.md` says why.
- A Battle.net account that owns Diablo IV, and Blizzard's installer for the client. sake does
not fetch that either.
- About 10 GB free for sake, and about 90 GB more for the game.

sake keeps the engine and the bottles in `~/Library/Sake`, and downloads, build output and logs
in `~/Library/Caches/Sake`. **Uninstall sake…** in the app menu takes both to the Trash.

## 1. Install sake

```sh
brew tap typester/sake
brew install --cask sake
```

The app is not notarised, so Gatekeeper stops it the first time: allow it in System Settings >
Privacy & Security, or install with `--no-quarantine`.

## 2. Set sake up

Open sake. The wizard opens itself until the six steps are done; afterwards **Set Up…** in the
toolbar brings it back.

### This Mac

![The This Mac step with five requirements met](../assets/getting-started/01-this-mac.png)

Five checks, and nothing below unlocks until they all pass. Install whatever is missing, then
press **Check Again**.

### Sources

![The Sources step, eleven downloads waiting](../assets/getting-started/02-sources.png)

Press **Download**. Eleven files, ten of them checked against a hash sake carries; CrossOver's
own tarball is the exception. Nothing is built yet.

### Libraries

![The Libraries step, nine libraries waiting](../assets/getting-started/03-libraries.png)

Press **Build**. Minutes. SDL2 is one of these, and it is what makes a controller work later.

### Wine

![The Wine step, before the build](../assets/getting-started/04-wine.png)

Press **Build** and leave it alone. The app warns of tens of minutes; on ten cores here it took
4m40s.

### D3DMetal

![The D3DMetal step, before installing](../assets/getting-started/05-d3dmetal.png)

Download Game Porting Toolkit 4.0 beta 2 from Apple, open the `.dmg`, and press **Install**. sake
copies what it needs out of the image and unmounts it. You only need the image once.

### Bottle

![The Bottle step, before the bottle is made](../assets/getting-started/06-bottle.png)

Press **Create**. A bottle is one Windows environment and the game goes inside it. This one is
called `default`.

## 3. Install the Battle.net client

Download `Battle.net-Setup.exe` from Blizzard, then select the bottle in the library.

![The bottle selected, with its buttons](../assets/getting-started/07-bottle.png)

**Install from an Installer…**, choose the `.exe`, **Install**. Blizzard's installer runs in a
window of its own; click through it as you would on Windows.

![The Install from an Installer sheet](../assets/getting-started/08-install.png)

When it closes, the sheet offers **Add a Title…**, which is the next step.

If Diablo IV is already installed under CrossOver, the bottle offers **Import from CrossOver…**
instead. It clones the game rather than copying it, so it costs no disk.

## 4. Add the launcher as a title

![The Add a Title sheet with the launcher chosen and its arguments filled in](../assets/getting-started/09-add-title.png)

**Add a Title…**, then **Choose Program…**. The panel opens inside the bottle; the launcher is
`Program Files (x86)/Battle.net/Battle.net Launcher.exe`. The name and the arguments fill in by
themselves. **Add**.

**Leave the arguments alone.** Without `--in-process-gpu` the login form is drawn but never
appears, and without the two ANGLE flags the client's GPU process exits. `docs/runtime.md` has
the measurements.

## 5. Install the game

Select the title and press **Play**. Sign in, and install Diablo IV from inside the client:
about 90 GB.

![A title selected, with Play and the arguments it starts with](../assets/getting-started/10-title.png)

## 6. Play

Start Diablo IV from the Battle.net launcher.

Enjoy!

## A controller

Nothing to set up. The engine is built with SDL2, so a controller should work.

## If it does not start

- Every run writes a log under `~/Library/Caches/Sake/build` — `title-<id>.log` for a title,
`install-<name>.log` for an installer. The window shows only the last line of it.
- **Wine Tools** on the bottle opens winecfg, regedit, the uninstaller and the task manager.
- `docs/runtime.md` tells four failure states apart by thread count, memory and Metal mappings.
Read that before deciding a build is broken.
14 changes: 12 additions & 2 deletions docs/runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,14 +322,24 @@ reasonable descriptor and then sends **no input reports at all** until it has be
Nintendo's handshake, which lives in SDL's HIDAPI driver. An IOHID-only build gives a
controller that is plugged in, visible to macOS, and completely dead in the game.

Verified by playing the game with a Switch Pro Controller over USB, 2026-09-17. Bluetooth
and other pads are untested.
Verified by playing the game with a Switch Pro Controller over USB, 2026-09-17.

**The same controller and cable played Diablo IV through sake on 2026-09-20.** Reported by
the owner, not instrumented — there is no log of that run. What it settles is that the SDL2
requirement above carries over to sake's own engine and bottle; it is not new evidence about
the driver.

**And over Bluetooth on 2026-09-21** — the same pad, no cable, played in the game. The owner's
report again, which is what takes "Bluetooth is untested" off this section. Other pads are
still untested.

**That pad is an 8BitDo Ultimate 2 Bluetooth Controller**, not Nintendo hardware: it claims
Nintendo's own ids, `Vendor 0x057E / Product 0x2009`, and macOS lists it as `Pro Controller`.
Read here from `system_profiler SPBluetoothDataType`, 2026-09-21. So "Switch Pro Controller"
above is the identity the pad presents rather than who built it — and that identity is the
thing that matters, because the ids are what send it down SDL's Switch driver and Nintendo's
handshake.

Use SDL2 newer than CrossOver's 2.30.12: 2.32.2 fixed a crash initialising with controllers
already connected on macOS, 2.32.6 fixed reliability of initializing Switch controllers on
macOS, and 2.32.10 fixed thumbstick range and calibration for Switch Pro Controllers by
Expand Down