Releases: andrewnakas/skate3-level-loader
Release list
v0.1.6 — Windows: New San Van trees no longer draw as spikes
Windows: New San Van trees no longer draw as spikes
On the converted New San Van map, the trees and props in two mountain cells were drawn as long stray triangles across the map. This happened on both D3D12 and Vulkan.
The converted pack is not at fault. All 42,048 of its meshes check out: every index is in range and every vertex is inside its bounding box. What goes wrong is at runtime. For every mesh in those two cells, the game resolves the vertex and index buffer addresses into the wrong memory block. The native renderer read garbage from there and drew it.
Fixed: the native renderer now checks every mesh's indices against its vertex count before uploading it. A mesh that points past its own vertices is skipped instead of drawn. It keeps being retried, and the log shows a recovered line if one ever reads correctly.
Known limitation: the affected meshes, about 33 in those two cells, are hidden rather than repaired, so a few trees and props there are missing. Fixing the address resolution itself is still open.
Tested on an RTX 4050 laptop: on New San Van, at the spots that showed the glitch, the spikes are gone.
Assets
skate3-engine-0.1.6-windows-x86_64.zip: unzip it and run skate3.exe. Your ISO and Title Update 3 are asked for on first launch, and map packs go in the dlc folder beside the exe, as in v0.1.5. Linux, Steam Deck and macOS engines stay on v0.1.5. The launcher archives are added by CI after this release is created.
No game data is included, and none can be: supply your own disc image and title update.
v0.1.5 — Windows build with an add-on picker, and Steam Deck touchpad holds
Windows: pick your add-on at startup
skate3-engine-0.1.5-windows-x86_64.zip is new in this release. Unzip it and run skate3.exe. The first launch asks for your Skate 3 ISO and Title Update 3 and installs the game into game\ beside the exe.
The zip comes with an empty dlc folder beside skate3.exe, with a README in it. To add maps, drag each pack's folder into it:
dlc\MYMAP\mymap_00000000.big (beside skate3.exe)
%APPDATA%\skate3\dlc\MYMAP\... (also scanned)
A bare .big works, and so does one with its .header beside it. With more than one pack, the game opens Choose an add-on before it boots. Pick one with the controller, keyboard or mouse, or choose Base game only. Your last choice is remembered. In game, the pack is under pause → challenge map → Locations, as the last entry.
Fixed: the v0.1.3 Windows build froze on that picker. Windows marked it "Not Responding" and closed it, and in some cases it crashed. Two causes:
- The picker never pumped Windows messages, so it never painted, and it never noticed that you had made a choice.
- It read pack headers and your last choice through a file handle from the wrong C runtime.
Tested on an RTX 4050 laptop:
- two packs at the picker, pick one, staged, in gameplay in about 14 s
- a pack with a loose
.headerloaded into gameplay - the saved last choice was read back on the next launch
Not yet on Windows: the SDK half of v0.1.4 (the suspend/resume fix) and v0.1.5 (touchpad). That work is still on the Linux build machine. This Windows engine is the v0.1.3 engine plus the picker fix. macOS stays on the v0.1.3 engine.
The launcher archives (skate3loader-0.1.5-*) are attached as well; the version check that stopped them publishing since v0.1.2 is fixed. On Windows the recommended way to play is the in-game picker above. On a brand-new profile the launcher's automation currently stalls at the first-run difficulty screen.
Steam Deck touchpad fix, on top of everything in v0.1.4.
Holding a preload on the right touchpad now works
Pulling down on the right touchpad and holding it — an ollie preload — kept collapsing back to centre with the thumb still planted, and flip tricks were unreliable as a result.
The pad reports lifts that never happen. Tracing a play session found 29 of 74 lifts were phantoms: an UP immediately followed by a DOWN in the same place, eleven of them in the same millisecond, with the thumb motionless throughout. Each one either completed a release — snapping the stick to centre and popping a trick nobody asked for — or was taken as a fresh gesture, and a fresh gesture deliberately starts the stick at centre before travelling back out to the finger. Both send the stick through centre mid-hold, on roughly forty percent of gestures.
The hold logic itself was never at fault: the same trace shows the stick sitting at full deflection for three and a half seconds whenever the pad did not interrupt it.
A lift is now judged by whether the thumb was moving when it arrived.
- Moving — a flick, acted on immediately, exactly as before. Tricks are as sharp as they were.
- Stationary — a planted thumb, which is the only case the pad lies about, and the case where waiting costs nothing because nothing is moving. The stick keeps its position briefly, and a finger reappearing nearby is treated as the same contact continuing rather than a new gesture.
New setting under Select + Start → Controls → Touchpads: Reacquire window, default 120 ms, which covers every phantom lift observed. Set it to 0 for the old behaviour. It cannot affect how responsive flicks feel — a moving lift never goes through this path.
Also in this release
hid_sdl_touchpad_trace logs every right-pad event and the resulting stick value, for anyone diagnosing a pad that behaves differently. Off by default.
Assets
linux and steamdeck are the same binary; Deck defaults are applied on detection at runtime. macOS and Windows remain at v0.1.3 and do not yet carry the suspend/resume fix from v0.1.4.
No game data is included, and none can be: supply your own disc image and title update.
v0.1.4 — Steam Deck, and a suspend/resume fix for every platform
Engine build with Steam Deck support, and a suspend/resume fix that matters on every platform.
Suspend and resume no longer freeze the game
Suspending the machine mid-game and waking it left the picture frozen on the last frame while the game carried on running underneath — audio looping, input dead, nothing in the log to explain it. This is fixed.
The cause was a signed overflow in a timer deadline. The game asks for "16 ms from now" as a negative number; across a suspend its own subtraction goes negative and flips the sign, so the request arrives as an absolute date a few milliseconds after the 1601 epoch. Real hardware treats an absolute time in the past as "fire immediately", so the game never notices. Converting that date to the scheduler's clock means subtracting about 425 years, which fits comfortably in the guest clock's 100 ns units but overflows int64 once widened to nanoseconds — wrapping a deadline in the deep past into one roughly 160 years in the future. The timer was queued correctly and never fired. The guest thread waiting on it waited forever, the game's main thread waited on that thread, and every other thread waited on the main thread. The renderer was not in that chain, so it went on drawing a world that could no longer change.
Two changes fix it: an absolute due time already in the past now fires immediately, matching hardware, and the conversion that overflowed clamps before widening so this class of wrap cannot recur.
Not a Steam Deck problem — any machine that suspends can hit it, including a laptop closing its lid.
Also fixed along the way:
- A one-second suspend was not detected at all. Resume handling only triggered above two seconds, so the shortest suspends — the common ones — bypassed every recovery path. The threshold is now 250 ms.
- The controller is re-attached after waking. It is a USB device and re-enumerates across a suspend; SDL does not reliably re-announce it, leaving the driver polling a handle that no longer referred to anything.
- The SDL event pump could wedge permanently. A one-shot latch was never cleared if queueing was refused, silently killing input for the rest of the session.
Steam Deck
Runs on the Deck, with defaults applied automatically on detection (both LCD and OLED).
- Touchpads: right pad as an absolute right stick, left pad as a press-activated d-pad, tunable under Select + Start → Controls → Touchpads
- Select + Start opens the settings menu — the old chord was unusable while skating, since RB is held constantly and Start is pause
- The screen no longer dims mid-session; a controller looks like idleness to a desktop idle timer
- The handheld video preset and the panel's frame cap now actually persist
Note for players on other hardware: the touchpad mappings are on by default. On a controller with a single touchpad (DualSense, DS4) that pad becomes a d-pad. It does nothing in this game otherwise, and it can be switched off in the same settings page.
Frame rate
- Turning off "VRR / Tearing" no longer disables triple buffering. Mailbox presentation never tears, but it was being switched off along with the modes that do — leaving plain vsync, which quantizes hard. On a 90 Hz panel that is 90, or 45, and nothing between, so a game running in the seventies displayed as 45.
- The automatic frame cap works on Linux for the first time. It derives its target from the display refresh rate, and the GTK backend never implemented the query — so it always answered "unknown", fell back to the explicit cap (zero, meaning uncapped) and read as enabled while doing nothing. If you preferred it uncapped, turn the automatic cap off.
- The detected refresh rate is now logged once at startup, since it silently sets the ceiling for everything above it.
Packaging
The archive no longer ships a desktop shortcut with an absolute path compiled into it, which was wrong on every machine except the one that built it. Run install-desktop-entry.sh to add a menu entry pointing at wherever you unpacked the folder.
Assets
linux and steamdeck are the same binary — Deck defaults are applied on detection at runtime. macOS and Windows are unchanged from v0.1.3 and do not yet carry the suspend fix.
No game data is included, and none can be: supply your own disc image and title update, as before.
v0.1.3 — Linux and macOS engine: performance presets and drop-in custom maps
Linux and macOS engine builds, focused on performance and on making custom maps work without a launcher.
This is the engine, not the game. You supply your own Xbox 360 disc image and Title Update 3; the first launch installs them. No game data is included.
| download | for |
|---|---|
skate3-engine-0.1.3-macos-arm64.zip |
Apple Silicon Mac (M1 or newer), macOS 13+ |
skate3-engine-0.1.3-linux-x86_64.tar.gz |
Linux x86-64 with AVX2 (2013 or newer) |
Installing
macOS — unzip and double-click Skate 3.app. macOS will refuse the first launch: the app is signed, but not by a paid Apple Developer account, and anything downloaded is quarantined. Right-click the app → Open, then confirm. Once, not every time. If it still refuses:
xattr -dr com.apple.quarantine "Skate 3.app"MoltenVK is bundled inside the app — nothing to install, no Homebrew, no Vulkan SDK.
Linux — extract and run ./play-skate3.sh. To play it on a Steam Deck: Add a Non-Steam Game, point it at play-skate3.sh. A controller works throughout, including the setup screens and the map picker.
On both, the first launch asks for your Skate 3 ISO and installs the game once.
Performance
macOS
Measured on an M1 MacBook Air (7-core GPU, 8GB) at 2560x1600 Retina:
| settings | result |
|---|---|
quality — AO, bloom, god rays, soft shadows, 2x MSAA, 2x draw distance |
holds 60fps |
ultra — the above at 2x render scale and 4x MSAA |
~52fps (p50 16.7ms, but p95 32.6ms), -13% vs quality |
potato — effects off |
116–130fps, GPU busy only 18% of the frame — +93% to +117% vs quality |
That is the smallest Apple Silicon GPU there is, holding 60 with the effects on. The frame is GPU-bound with headroom: the CPU side is about 8ms of the 16.7ms budget.
New here: Start + Select opens a performance page showing frame rate, frame time and GPU wait, and says in one line what is limiting you — the frame cap, the graphics card, or the emulation. Turning quality down only buys frames while it says the graphics card.
Linux
The video defaults were a desktop discrete GPU's, applied to every machine: the world drawn at twice the window with 4x MSAA. On integrated graphics that is unplayable. Presets now come from the hardware on the first run, and are one click in Escape → Video → Preset.
Measured on an integrated Intel UHD (Raptor Lake-P, 48 EU) at 1280x800, median over 600-frame windows of real gameplay:
| preset | fps |
|---|---|
quality (what every machine used to get) |
7.4 |
deck — auto-selected on a Steam Deck or any machine with no discrete GPU |
36.0 (+386%) |
performance — no shadows, bloom or ambient occlusion |
42.2 (+470% vs quality, +17% vs deck) |
Past the deck preset, turning effects off buys very little on a weak GPU: halving the draw distance changed nothing (36.3), quartering it changed nothing (36.0), and quarter resolution reached only 37.3. Shadows are the one exception, which is what performance takes. Supersampling is the whole story — the rest is close to free.
Linux binaries are built for x86-64-v3 (AVX2), which the recompiled PowerPC vector code uses directly: 225.2 fps against 186.7 for the same build at the old generic baseline (+20.6%), measured on a discrete RTX 4050 where the CPU is what limits. Needs a 2013-or-newer CPU (Intel Haswell, AMD Zen) — every Steam Deck and modern handheld qualifies. This flag does nothing on Apple Silicon, which has no AVX2; the Mac build gets the presets and not the CPU gain.
An explicitly set cvar always beats a preset, in both directions.
Windows
First Windows build — windows-release didn't exist as a CMake preset before this release; it does now, on clang-cl, with both D3D12 and Vulkan built (gpu_backend picks at runtime).
Measured on a laptop with both a discrete NVIDIA RTX 4050 and an Intel iGPU as separate GPUs, real Career-mode gameplay, uncapped fps:
| GPU | config | avg fps |
|---|---|---|
| RTX 4050 (discrete) | quality preset |
159 |
| RTX 4050 (discrete) | performance preset |
167 (+5%) |
| RTX 4050 (discrete) | performance + HDR/haze off + 0.5x draw/LOD distance |
255 (+53% vs performance, +60% vs quality) |
| Intel iGPU (integrated) | performance preset |
177 |
On this discrete GPU the game is CPU-bound, not GPU-bound — switching presets alone only buys +5%, the AVX2 build is what does the real work (same story as Linux). On integrated graphics it's the reverse: GPU-bound, so preset choice is what matters. HDR and haze are on by default regardless of preset; turning them off plus halving draw/LOD distance is worth another +53% over the plain performance preset with no further gain found pushing further.
Custom maps without a launcher
Drop map packs in dlc/, either shape:
dlc/MYMAP/mymap_00000000.big (with its .header beside it)
dlc/SomeOfficialDLCContainer (a LIVE/CON package)
On Linux that is dlc/ beside the binary. On macOS it is dlc/ beside Skate 3.app, or ~/.local/share/skate3/dlc — both are scanned.
No packs boots the base game. One loads it silently. More than one asks which to play at startup, with controller, keyboard and mouse — before the game boots, because the choice has to be made before the content is staged and the boot scan reads it.
Only the chosen pack is installed — the game's content scan does not survive a large collection installed at once, and every installed pack piles its locations into the same menu. Packs you are not playing are parked and come back instantly when reselected. Your files are never moved out of dlc/.
Once in the game, a custom map is reached the same way official DLC is: pause → challenge map → tab across to Locations → your pack is the last entry.
Also fixed
macOS, so the app runs on a machine that is not the one that built it:
- It loaded whatever MoltenVK the machine happened to have. The Vulkan library was found by bare name, so a Mac with Homebrew got Homebrew's copy and a Mac without got nothing at all. The app now loads the copy inside its own bundle.
- The game folder was a build machine's path. On another Mac that is
/Users/someone-else/skate3/game, which cannot even be created — the install failed with a permission error naming a stranger's home directory. - Game data landed beside the app's folder instead of in it.
game/,dlc/andsaves/now all live under~/.local/share/skate3. An existing install at the old location is still used, so nobody's game is orphaned. - A
dlc/folder next toSkate 3.appwas never scanned — inside a bundle, "beside the executable" is three levels below where anyone sees the app.
Both platforms:
- The game quit immediately after a content scan: it restarts itself by the retail executable name (
sk83_na_f.xex), which a recomp install does not have. Now aliased to the real entry point. - Console noise: 898 log lines a session down to 344, and 18 errors down to 0. Almost none of it was a fault — texture re-decodes are normal (event ads, lightmap pages, CAS composites), and the
XmaContext ... exceeds buffer sizeerror is an audio packet boundary the decoder resynchronises past inaudibly. The renderer's per-mesh bookkeeping is now behindskate3_native_render_scene_verbose. - The DLC scan no longer warns about the
README.txtthedlc/folder ships with.
Platform builds
| platform | status |
|---|---|
| macOS arm64 | attached — bundled MoltenVK, ad-hoc signed, measured on an M1 Air |
| Linux x86_64 | attached — AVX2, measured |
| Windows x86_64 | attached — AVX2, D3D12/Vulkan both built, measured on a discrete RTX 4050 and an Intel iGPU |
Known issues
The first-run difficulty screen draws its options as blank rows. The list works — press A/Enter to take the highlighted one — and it is shown once, before your profile exists.
The macOS app is ad-hoc signed rather than notarised, which is why Gatekeeper needs the right-click. Notarisation needs a paid Apple Developer account.
Linux tarball rebuilt 2026-08-29. The original build was compiled against glibc 2.43 and would not start on anything older, failing with version 'GLIBC_2.38' not found and similar. It is now built against glibc 2.35 and ships its own libstdc++, so it runs on Ubuntu 22.04+, Debian 12+, SteamOS 3.5+, Fedora 36+ and current rolling distros. Same engine, same commit — only the toolchain changed. Re-download if you hit that error.
v0.1.2
Maps load in 25 s instead of 40 s. Most of the wait was the launcher, not the game.
What changed
Boot to skating, phase by phase (scripts/boottime.py, Maloof Money Cup, warm):
before now
host + guest boot 3.0s 3.8s
press-start 2.4s 2.3s
boot world load 4.7s 4.6s
macro settle 2.5s 0.8s
macro 22.0s 7.6s <-
activate 4.6s 5.3s
TOTAL 40.1s 25.3s
The menu macro was more than half the wait, and almost all of that was ten tab
presses waiting 1500 ms each. The tab strip clamps on Locations — the presses
are idempotent, so the long wait bought nothing. 300 ms now, with the same ten
presses of margin against a swallowed input.
Verified by screenshot, which is the only thing that can tell which world you
actually landed in: Maloof Money Cup 4/4 matching its confirmed reference, and a
second map landing exactly where the old macro put it.
If a boot is slow on your machine, every timing is an environment variable:
SKATE3LOADER_SETTLE_MS=2500 SKATE3LOADER_TAB_DELAY_MS=1500 ./skate3loaderStill the launcher only
Unpack the engine archive into an engine/ folder beside the launcher — on
macOS beside the .app, never inside it.
skate3loader-0.1.2-linux-x86_64.tar.gz— glibc 2.35+skate3loader-0.1.2-windows-x86_64.zip—skate3loader-gui.exeto playskate3loader-0.1.2-macos-arm64.zip— Apple Silicon, macOS 15+
macOS: xattr -dr com.apple.quarantine "Skate 3 Level Loader.app", or
System Settings → Privacy & Security → Open Anyway.
v0.1.1
The launcher opens on the game itself, and takes maps by drag and drop.
What changed
Skate 3 is the first card. A fresh install could not play anything from the
launcher until it had a map pack. Now the game as it ships is right there — no
DLC staged, no menu automation, straight into Port Carverton.
Drag and drop a .big onto the window — or a whole folder of them — and it
is installed. Import pack… and skate3loader scan still work.
Packs you do not have are no longer shown. The launcher ships knowing 43
packs by name, including which of their maps load and which hang, but not the
files. Those records are memory now, not cards: drop the matching .big in and
the pack arrives complete, with its tested status intact. v0.1.0 showed all 43
as dead entries and claimed "121 playable".
Setup screen: no dangling "or" when there is no existing install, the Title
Update field says what that file is, and a missing engine is reported before you
pick anything rather than after.
The engine is still separate
These are the launcher only. Unpack the engine archive into an engine/ folder
beside the launcher — on macOS beside the .app, never inside it, or its
signature breaks:
Skate 3 Level Loader.app skate3loader/ skate3loader\
engine/ engine/ engine\
skate3 skate3 skate3.exe
librexruntime.dylib librexruntime.so rexruntime.dll
Downloads
skate3loader-0.1.1-linux-x86_64.tar.gz— glibc 2.35+skate3loader-0.1.1-windows-x86_64.zip—skate3loader-gui.exeto playskate3loader-0.1.1-macos-arm64.zip— Apple Silicon, macOS 15+
macOS is not notarized: xattr -dr com.apple.quarantine "Skate 3 Level Loader.app",
or System Settings → Privacy & Security → Open Anyway.
v0.1.0
First packaged build of the Skate 3 Level Loader.
Pick a map, a loading screen covers the boot, and you drop into it with no
menus. Quit the game and the library comes back.
The engine is not in this release yet
These archives are the launcher only. It drives a patched build of
skate3recomp — the in-engine
loading cover, the level picker and the warp all live there — and that build is
attached separately. Without it the launcher opens, shows your library, and
stops at setup.
Unpack the engine into an engine/ folder beside the launcher — on macOS
beside the .app, never inside it, because writing into the bundle invalidates
its signature and it will not start:
Skate 3 Level Loader.app skate3loader/ skate3loader\
engine/ engine/ engine\
skate3 skate3 skate3.exe
librexruntime.dylib librexruntime.so rexruntime.dll
Downloads
skate3loader-0.1.0-linux-x86_64.tar.gz— glibc 2.35+ (Ubuntu 22.04, Debian 12, Fedora 37+)skate3loader-0.1.0-windows-x86_64.zip—skate3loader-gui.exeto play,skate3loader.exefor the commandsskate3loader-0.1.0-macos-arm64.zip— Apple Silicon, macOS 15+
Checksums in SHA256SUMS.
Your own game files
First run asks for your Skate 3 ISO and Title Update 3 package and hands them to
the engine's own installer — the launcher extracts nothing itself. If a
Skate3Recomp install is already on the machine it offers that instead.
The map catalog
43 packs and 134 maps come with the launcher: names, world ids, spawn nodes, and
for the ones that have been through the sweep, whether they load or hang. The
pack files are not included and are not ours to distribute, so those records
show FILE MISSING until you point them at your own copy with
Locate pack file…. skate3loader scan ~/Downloads imports new packs.
macOS
Not notarized, so Gatekeeper blocks it until you either run
xattr -dr com.apple.quarantine "Skate 3 Level Loader.app"
or use System Settings → Privacy & Security → Open Anyway.
Known gaps
- Guest-memory handover detection is Linux-only; on Windows and macOS the
launcher falls back to the log going quiet, so the hand-off from the loading
screen is a little coarser. - The test harness under
scripts/(sweep, verifyspot, screenshot verification)
is Linux-only by design and is not in these builds.