mirror of
https://github.com/griches/PS5_Sunshine.git
synced 2026-10-17 13:00:40 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e01f5db170 | ||
|
|
768a56cc43 |
No files matched your search
+11
-25
@@ -9,7 +9,7 @@ header, and may also be used under that licence alone.
|
||||
| --- | --- | --- |
|
||||
| `ps5/tools/png-to-title-dds.py` (encoder from PS5 RetroArch, by Mihawk) | GPL-3.0-or-later | [`licenses/GPL-3.0.txt`](licenses/GPL-3.0.txt) |
|
||||
| `ps5/src/platform.c`, `ps5/src/platform.h` and the tools taken from the PS5 Vulkan Template (by Mihawk) | MIT | [`licenses/MIT.txt`](licenses/MIT.txt) |
|
||||
| The rest of `ps5/src/`, `ps5/tools/`, `ps5/cmake/`, `ps5/kit/`, `ps5/opengl/`, `ps5/probe/` and `ps5/art/` (written for this port) | MIT | [`licenses/MIT.txt`](licenses/MIT.txt) |
|
||||
| The rest of `ps5/src/`, `ps5/tools/`, `ps5/cmake/`, `ps5/opengl/`, `ps5/probe/` and `ps5/art/` (written for this port) | MIT | [`licenses/MIT.txt`](licenses/MIT.txt) |
|
||||
| `ps5/sdl/` (SDL2 drivers for the console) and `ps5/patches/sdl.patch` | Zlib, as SDL | [`licenses/Zlib.txt`](licenses/Zlib.txt) |
|
||||
| `ps5/patches/sms-pc-port.patch` | MIT, as sms-pc-port | [`licenses/MIT.txt`](licenses/MIT.txt) |
|
||||
| `ps5/art/kenney/` and the button prompts in `ps5/res/prompts/` made from it (Kenney's input prompts) | CC0-1.0 | in `ps5/art/kenney/` |
|
||||
@@ -27,37 +27,23 @@ project, under that project's licence.
|
||||
| Windowing, input and sound | [SDL2](https://github.com/libsdl-org/SDL) | Zlib |
|
||||
| The link recipe, the C runtime start-up and the fake-SELF tool | [PS5_Vulkan](https://github.com/mihawk-99/PS5_Vulkan) | GPL-3.0 |
|
||||
| The platform layer and SDK | [PS5_PayloadSDK](https://github.com/mihawk-99/PS5_PayloadSDK), a fork of ps5-payload-dev/sdk | GPL-3.0 |
|
||||
| Smaller libraries | stb, bcdec, xxHash; zlib, in the kit's Linux build of the `eboot.bin` tool | each its own, all permissive |
|
||||
| Smaller libraries | stb, bcdec, xxHash | each its own, all permissive |
|
||||
|
||||
The builder kit fetches its compiler and build tools the first time it runs,
|
||||
from their projects' own releases: [LLVM](https://github.com/llvm/llvm-project)
|
||||
(Apache-2.0 with LLVM exceptions), [CMake](https://github.com/Kitware/CMake)
|
||||
(BSD-3-Clause) and [Ninja](https://github.com/ninja-build/ninja) (Apache-2.0),
|
||||
and for the optional texture pack [Pillow](https://github.com/python-pillow/Pillow)
|
||||
(MIT-CMU), [etcpak](https://github.com/K0lb3/etcpak) (MIT and BSD-3-Clause),
|
||||
[archspec](https://github.com/archspec/archspec) (Apache-2.0 or MIT) and, on
|
||||
Linux, [7-Zip](https://github.com/ip7z/7zip) (LGPL-2.1, with its unRAR restriction). None is in the kit.
|
||||
## The release
|
||||
|
||||
## A built title is not for distribution
|
||||
|
||||
A built `eboot.bin` holds the game's own code, compiled from the
|
||||
decompilation. Whatever the licences above say about the rest, and whatever
|
||||
licence the decompilation is published under, that code is a reconstruction of
|
||||
Nintendo's program: a built title stays on the machine that made it.
|
||||
|
||||
The builder kit (`ps5/tools/package-kit.sh`) is what is shared: the same build
|
||||
without that part. It holds compiled forms of the projects above, under their
|
||||
licences, with `SOURCES.txt` naming each one's repository and revision, and
|
||||
nothing of the game. Whoever uses it compiles the game's code on their own
|
||||
machine, from the public sources, and plays it with their own disc image.
|
||||
A release (`ps5/tools/package-release.sh`) is the built title folder: its
|
||||
`eboot.bin` links the parts above and is distributed under GPL-3.0 as a whole,
|
||||
with `SOURCE.txt` naming the repository and revision of each part and
|
||||
`licenses/` holding their licence texts. The game's code in it is compiled from
|
||||
the decompilation; nothing from a disc is in it.
|
||||
|
||||
## What is not covered
|
||||
|
||||
- **The game.** Super Mario Sunshine, its disc image and everything on it
|
||||
belong to Nintendo. None of it is in this repository or in a kit, and no
|
||||
licence above covers it.
|
||||
belong to Nintendo. None of it is in this repository or in a release, and no
|
||||
licence above covers it; the title plays your own disc image.
|
||||
- **Texture packs.** The port can load Dolphin-format texture packs. None is in
|
||||
this repository or in a kit; each belongs to its authors.
|
||||
this repository or in a release; each belongs to its authors.
|
||||
- **Artwork.** `ps5/art/icon0-source.png` and `ps5/art/pic-source.png` (a harbour
|
||||
town on a sunny island), and the `ps5/sce_sys/icon0.png`, `pic0.dds` and
|
||||
`pic1.dds` made from them, are the port's own: made for it by Gary Riches with
|
||||
|
||||
@@ -24,14 +24,15 @@ drawn with OpenGL through the [PS5 OpenGL SDK](https://github.com/blackbearreloa
|
||||
(Mesa over the console's own GPU interface).
|
||||
|
||||
It needs a PS5 that already runs homebrew, and **your own disc image of the
|
||||
game** (North America, `GMSE01`). No game code, game data, Nintendo artwork,
|
||||
Sony software or keys are included here.
|
||||
game** (North America, `GMSE01`): the title is compiled from the
|
||||
decompilation and reads everything else (models, levels, sound, movies) from
|
||||
your disc. No game data, Nintendo artwork, Sony software or keys are included.
|
||||
|
||||
This is an unofficial port. It is not part of sms-pc-port and is not supported
|
||||
by it: **please do not take PS5 problems to chasem-dev.** Report them
|
||||
[here](https://github.com/griches/PS5_Sunshine/issues).
|
||||
|
||||
## This is an early release: 0.9.0
|
||||
## This is an early release: 0.10.0
|
||||
|
||||
**It is not fully tested.** It was brought up and played on one console over a
|
||||
single day (2026-10-13), through the opening, Delfino Plaza and the first
|
||||
@@ -39,33 +40,114 @@ episode of Bianco Hills. Everything after that is unplayed on a PS5. Expect
|
||||
faults; several serious ones were found and fixed only by playing, and there
|
||||
will be more further into the game. Keep a copy of your save.
|
||||
|
||||
New in 0.9.0: sms-pc-port's save states (L1 + R1) and its faster wipe into the
|
||||
map, from its revision of 2026-10-14, and a builder kit that no longer needs
|
||||
Docker.
|
||||
New in 0.10.0: a ready-made title (copy it over and add your disc), and the HD
|
||||
texture pack goes in as its authors publish it, with the console sizing its
|
||||
largest images itself.
|
||||
|
||||
## How to get it: the builder kit
|
||||
## Installing
|
||||
|
||||
A built title holds the game's own code, so none is offered here. The
|
||||
[release](https://github.com/griches/PS5_Sunshine/releases/latest) is a
|
||||
**builder kit**: everything of a build except that code. On your own computer
|
||||
it downloads the public source code, compiles it and links the title. One
|
||||
command, about 5 to 20 minutes, on a Mac with Apple silicon, on Linux, or on
|
||||
Windows in WSL. No Docker: on Ubuntu and WSL the compiler, LLVM 22, comes from
|
||||
LLVM's own packages (apt.llvm.org); on a Mac it and the build tools are fetched
|
||||
from their own releases the first time, checked, and kept for next time.
|
||||
You need a PS5 that runs homebrew, with etaHEN (or an equivalent),
|
||||
ShadowMountPlus (which shows folders in `/data/homebrew` as games) and an FTP
|
||||
server, and **your own disc image** of the North American release (`GMSE01`,
|
||||
revision 0) as `.iso`, `.gcm` or Dolphin's `.ciso`. Other regions are not
|
||||
supported.
|
||||
|
||||
1. Download `sunshine-ps5-kit-<version>.zip` from the
|
||||
1. Download `Sunshine-PS5-v<version>.zip` from the
|
||||
[releases page](https://github.com/griches/PS5_Sunshine/releases/latest) and unzip it.
|
||||
2. Get your computer ready once (a Mac: Apple's Command Line Tools; Windows: WSL
|
||||
and LLVM 22; the kit's README has the commands), then open a terminal in the
|
||||
kit's folder and run `./make-title.sh`.
|
||||
3. Copy the folder it makes, `out/PPSA99312`, to `/data/homebrew/` on the console (FTP).
|
||||
4. Copy your own disc image (`.iso`, `.gcm` or `.ciso`) into that folder, beside `eboot.bin`.
|
||||
2. With an FTP program (FileZilla, Cyberduck, WinSCP), copy the **whole
|
||||
`PPSA99312` folder** into `/data/homebrew/` on the console.
|
||||
3. Copy your disc image into that folder, beside `eboot.bin`:
|
||||
|
||||
The kit's own [README](ps5/kit/README.md) has every step, the controls, the
|
||||
settings menu, the steps for Windows and what to do when something goes wrong.
|
||||
```text
|
||||
/data/homebrew/PPSA99312/eboot.bin
|
||||
/data/homebrew/PPSA99312/sce_sys/
|
||||
/data/homebrew/PPSA99312/sce_module/
|
||||
/data/homebrew/PPSA99312/res/
|
||||
/data/homebrew/PPSA99312/licenses/
|
||||
/data/homebrew/PPSA99312/GMSE01.iso (your disc image; any name)
|
||||
```
|
||||
|
||||
**Never share the folder the kit makes, or its `eboot.bin`.**
|
||||
"Super Mario Sunshine" appears on the home screen. If it does not, check the
|
||||
folder is exactly `/data/homebrew/PPSA99312/` with `eboot.bin` directly inside
|
||||
it (not `PPSA99312/PPSA99312/`), and that ShadowMountPlus is running. If it
|
||||
starts and closes at once, the disc image is missing or is not the North
|
||||
American one.
|
||||
|
||||
To update, copy the new release's `PPSA99312` folder over the old one. Your
|
||||
saves, settings, disc image and texture packs are left alone.
|
||||
|
||||
## Playing
|
||||
|
||||
| DualSense | In the game |
|
||||
| --- | --- |
|
||||
| Cross | A (jump, confirm) |
|
||||
| Circle | B (dive, back) |
|
||||
| Square | X |
|
||||
| Triangle | Y (first-person view) |
|
||||
| R1 | Z |
|
||||
| L1 + R1 | **Save state** (in a level); in the pause menu, the list of save states to load |
|
||||
| L2, R2 | L, R (R sprays FLUDD) |
|
||||
| Options | Start (pause, map) |
|
||||
| Touch pad button | **Settings** |
|
||||
| Sticks, D-pad | as on a GameCube |
|
||||
|
||||
The prompts the game shows on screen are the DualSense's own.
|
||||
|
||||
**Settings.** Press the touch pad's button at any time. The D-pad moves and
|
||||
changes, Cross steps through a row's choices, Circle or the touch pad closes it.
|
||||
|
||||
| Row | What it does |
|
||||
| --- | --- |
|
||||
| Invert FLUDD Aim X / Y | flips the left stick while you aim FLUDD |
|
||||
| Invert Camera X / Y | flips the right stick's camera |
|
||||
| Camera Shake | OFF, HALF or FULL: the game's earthquakes (off to begin with) |
|
||||
| Performance Overlay | OFF, MINIMAL (the frame rate alone) or FULL |
|
||||
| Heat Haze | the heat shimmer |
|
||||
| Resolution \* | 960p, 1440p, 1920p or 2160p |
|
||||
| Frame Rate \* | 30 or 60 |
|
||||
| Picture \* | 4:3 or 16:9 |
|
||||
| Smoothing (FXAA) \* | on or off |
|
||||
|
||||
Rows marked \* take effect the next time you start the game. Below them are
|
||||
the PC port's cheats (god mode, infinite lives and so on). Saves and settings
|
||||
are kept in `/data/homebrew/PPSA99312/userdata/`.
|
||||
|
||||
**Save states.** Hold L1 and press R1 in a level to save the moment. Pause
|
||||
(Options) and press L1 + R1 again to see your save states, with a picture of
|
||||
each, and load one. They are kept in `userdata/savestates`. A state loads
|
||||
exactly only in the release that saved it; with a newer release, older states put
|
||||
Mario back where he was in that level and keep your progress, but the level
|
||||
starts afresh.
|
||||
|
||||
## HD textures (optional)
|
||||
|
||||
The port loads texture packs made for the Dolphin emulator. The one it was
|
||||
tested with is the
|
||||
[Super Mario Sunshine UHD Texture Pack](https://github.com/qashto/Super_Mario_Sunshine_UHD_Texture_Pack)
|
||||
(by qashto and razius), with chasem-dev's
|
||||
[HD texture extras](https://github.com/chasem-dev/sms-hd-texture-extras):
|
||||
|
||||
1. Download `GMS.7z` from the UHD pack's
|
||||
[release 2.1.1](https://github.com/qashto/Super_Mario_Sunshine_UHD_Texture_Pack/releases/tag/2.1.1)
|
||||
and `sms-hd-texture-extras-1.1.1.zip` from the extras'
|
||||
[release 1.1.1](https://github.com/chasem-dev/sms-hd-texture-extras/releases/tag/v1.1.1).
|
||||
2. Unpack both on your computer (7-Zip on Windows; a Mac opens the `.7z` with
|
||||
The Unarchiver or `tar -xf GMS.7z`).
|
||||
3. Copy them to the console so that you have:
|
||||
|
||||
```text
|
||||
/data/homebrew/PPSA99312/mods/textures/GMS/ (the GMS folder from GMS.7z's Textures folder)
|
||||
/data/homebrew/PPSA99312/mods/textures/sms-hd-texture-extras/ (the extras' zip, unpacked)
|
||||
```
|
||||
|
||||
The packs are not part of this project and are not stored by it: they belong
|
||||
to their authors.
|
||||
|
||||
**The first visit to each area is slower.** The UHD pack's largest images
|
||||
(4096 to 16384 pixels) are more than the console's graphics driver can hold,
|
||||
so the title halves each one to 2048 pixels or less the first time it is used
|
||||
and keeps the result in `userdata/.cache`. After that, loads read the small
|
||||
copy. Deleting that folder only means the work is done again.
|
||||
|
||||
## Status
|
||||
|
||||
@@ -79,31 +161,26 @@ read in the title's log. Nothing else is claimed.
|
||||
| Start-up | The logos, the opening movie, the title screen, file select; a saved file loaded | A new game from an empty memory card |
|
||||
| Rendering | 3840x2160; Delfino Plaza and Bianco Hills episode 1 look right; the plaza's gates show their movie of the level beyond | Everything past Bianco Hills episode 1; a comparison with a GameCube |
|
||||
| Frame rate | A locked 60 frames a second (16.7 ms) in the plaza and Bianco Hills, at 2160p, with the HD texture pack | Later, heavier areas; a long session |
|
||||
| Loading | Delfino Plaza in about 5 seconds with the console-sized HD pack; a "LOADING..." notice during long loads | |
|
||||
| Loading | Delfino Plaza in about 6 seconds with the HD pack (9 the first time, while its largest images are halved); a "LOADING..." notice during long loads | |
|
||||
| Input | Walking, jumping, the camera, FLUDD, menus, the DualSense's own button prompts | Every button in play; rumble; a second controller |
|
||||
| Settings | The touch pad's menu: inverted FLUDD aim and camera, camera shake, the performance overlay, resolution, frame rate; choices kept across a restart | Every row; the cheats below them |
|
||||
| Saving | A save made earlier loads | Saving in play, checked after a restart |
|
||||
| Save states | Saving with L1 + R1 in a level, the pause menu's list with pictures, loading a state, and loading after a restart | States across many levels; a state from another build (loads the position only, by design) |
|
||||
| The map (R1) | The wipe into it, now quick | |
|
||||
| Sound | Music, effects and the movies' sound play | A careful listen for faults |
|
||||
| Texture packs | The UHD pack and the HD extras, sized for the console (see below) | Other packs |
|
||||
| Memory | Two levels and back without running out, with the console-sized pack | Many levels in one session |
|
||||
| Builder kit | Built without Docker on a Mac (Apple silicon, macOS's own tools) and on Linux (arm64), and the Mac's title played on the console. On x86-64 Linux: LLVM's build fetched and run, a game file compiled identically, `eboot.bin` made byte for byte the same | A whole build on Windows (WSL) or x86-64 Linux; the `--docker` route |
|
||||
| Texture packs | The UHD pack and the HD extras as published, their largest images halved on the console (see below) | Other packs |
|
||||
| Memory | Two levels and back without running out, with the HD pack at the sizes the console uses | Bianco Hills since the console does the halving itself; many levels in one session |
|
||||
|
||||
### Known problems
|
||||
|
||||
- **Only the start of the game has been played.** Anything from Bianco Hills
|
||||
episode 2 onward is untested on a PS5.
|
||||
- **Large texture packs must be shrunk.** The console's driver needs about ten
|
||||
times a texture's size in memory. The UHD pack as published made loads take
|
||||
17 seconds and ran the console out of memory in the second level. The kit
|
||||
makes a console-sized copy for you.
|
||||
- **Camera shake is off to begin with.** The game's earthquakes (a boss
|
||||
landing, a building rising) are strong on a large screen. Turn them back on
|
||||
in the settings menu if you want the original feel.
|
||||
- **Save states belong to the build that saved them.** L1 + R1 saves one in a
|
||||
- **Save states belong to the release that saved them.** L1 + R1 saves one in a
|
||||
level, and loads one from the pause menu (sms-pc-port's own). A state loads
|
||||
exactly only in the build that saved it: after a rebuild it puts Mario back
|
||||
exactly only in the release that saved it: in a newer one it puts Mario back
|
||||
where he was and keeps the progress, but the level starts afresh. Keep a
|
||||
copy of your memory card save as well.
|
||||
|
||||
@@ -149,11 +226,11 @@ tools; no Vulkan driver is linked) and the PS5 OpenGL SDK 1.0.2.
|
||||
```sh
|
||||
git clone https://github.com/griches/PS5_Sunshine.git && cd PS5_Sunshine
|
||||
git clone https://github.com/chasem-dev/sms-pc-port.git vendor/sms-pc-port
|
||||
git -C vendor/sms-pc-port checkout c8fb4a3e81a2176588574395302ecf862f9a440a # see ps5/kit: REVISIONS
|
||||
git -C vendor/sms-pc-port checkout c8fb4a3e81a2176588574395302ecf862f9a440a # the revision SOURCE.txt names
|
||||
git -C vendor/sms-pc-port submodule update --init decomp
|
||||
export PS5_VULKAN_DIR=/path/to/PS5_Vulkan PS5_OPENGL_PREFIX=/path/to/ps5-opengl-sdk/sdk
|
||||
ps5/tools/build.sh # applies the patch, builds dist/PPSA99312/
|
||||
ps5/tools/package-kit.sh # the builder kit, dist/kit/
|
||||
ps5/tools/build.sh # applies the patch, builds dist/PPSA99312/
|
||||
ps5/tools/package-release.sh # the release archive, dist/Sunshine-PS5-v<version>.zip
|
||||
```
|
||||
|
||||
`ps5/tools/setup-source.sh --save` writes changes made under
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
# Super Mario Sunshine for the PlayStation 5 - the builder kit's share of the build: the game's code alone.
|
||||
#
|
||||
# kit/build-title.sh configures the PC port (fetched and patched on the machine
|
||||
# that runs it) with the payload SDK's toolchain and this file as
|
||||
# CMAKE_PROJECT_sms_port_INCLUDE, then builds the one target sms_game: the
|
||||
# archive of the game's own code, which is the part a kit cannot bring. The
|
||||
# flags are the ones ps5-game.cmake gives the full build, so the archive is the
|
||||
# one the kit's other objects were linked with when it was tested.
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
if(NOT PS5_KIT_DIR)
|
||||
message(FATAL_ERROR "ps5-kit-game.cmake: PS5_KIT_DIR is not set (use the kit's build-title.sh)")
|
||||
endif()
|
||||
|
||||
add_compile_options(-ffunction-sections -fdata-sections -fno-omit-frame-pointer)
|
||||
set(CMAKE_C_COMPILER_LAUNCHER sh ${PS5_KIT_DIR}/ps5/tools/cc-init-array.sh)
|
||||
set(CMAKE_CXX_COMPILER_LAUNCHER sh ${PS5_KIT_DIR}/ps5/tools/cc-init-array.sh)
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
set(CMAKE_CXX_SCAN_FOR_MODULES OFF)
|
||||
set(SMS_ARCH 64 CACHE STRING "" FORCE)
|
||||
set(SMS_GX_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||
|
||||
# The port's other targets are configured but never built here: SDL2 only has
|
||||
# to exist by name for them.
|
||||
if(NOT TARGET SDL2::SDL2)
|
||||
add_library(SDL2::SDL2 INTERFACE IMPORTED GLOBAL)
|
||||
endif()
|
||||
@@ -1,25 +0,0 @@
|
||||
# Super Mario Sunshine for the PlayStation 5 - the builder kit's container: a Linux with what
|
||||
# build-title.sh needs (LLVM 22, CMake, Ninja, git, Python 3, a host C++ compiler) and the kit.
|
||||
#
|
||||
# docker build -t sunshine-ps5-kit .
|
||||
# docker run --rm -v "$PWD/out":/out sunshine-ps5-kit /out
|
||||
#
|
||||
# make-title.sh does both. Nothing of the game is in the image: its source code
|
||||
# is fetched when the container runs, and what it makes goes to /out.
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
FROM ubuntu:24.04
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
ca-certificates wget git patch python3 g++ zlib1g-dev cmake ninja-build \
|
||||
python3-pip python3-pil 7zip \
|
||||
&& pip3 install --break-system-packages --no-cache-dir etcpak \
|
||||
&& wget -qO /etc/apt/trusted.gpg.d/apt.llvm.org.asc https://apt.llvm.org/llvm-snapshot.gpg.key \
|
||||
&& echo "deb https://apt.llvm.org/noble/ llvm-toolchain-noble-22 main" > /etc/apt/sources.list.d/llvm.list \
|
||||
&& apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends clang-22 lld-22 llvm-22 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
COPY . /kit
|
||||
RUN bash /kit/build-title.sh --tools
|
||||
WORKDIR /out
|
||||
ENTRYPOINT ["bash", "/kit/build-title.sh"]
|
||||
@@ -1,319 +0,0 @@
|
||||
# Super Mario Sunshine for the PlayStation 5: builder kit
|
||||
|
||||
This kit builds a native PS5 version of Super Mario Sunshine (GameCube) on
|
||||
your own computer, for a PS5 that has homebrew enabled. It plays **your own
|
||||
copy** of the game, from your own disc image, at 4K and 60 frames a second.
|
||||
|
||||
You run one command. Your computer does the rest in about 5 to 20 minutes.
|
||||
Nothing needs installing first on a Mac or Linux beyond what the steps below
|
||||
say; on Windows it runs in WSL.
|
||||
|
||||
> **There is no game in this kit.** No game code, no game data, nothing of
|
||||
> Nintendo's. The build fetches the public source code it needs and compiles
|
||||
> it on your machine, and you supply the disc image. What the kit makes is
|
||||
> yours alone: **never share the folder it produces, or its `eboot.bin`.**
|
||||
|
||||
---
|
||||
|
||||
## Before you start
|
||||
|
||||
You need three things.
|
||||
|
||||
**1. A PS5 that runs homebrew**, with:
|
||||
|
||||
- etaHEN (or an equivalent)
|
||||
- ShadowMountPlus, which shows folders in `/data/homebrew` as games
|
||||
- an FTP server running on the console
|
||||
|
||||
**2. Your disc image of the game.** The North American release (`GMSE01`,
|
||||
revision 0), as `.iso`, `.gcm` or Dolphin's `.ciso`. Other regions are not
|
||||
supported.
|
||||
|
||||
**3. A computer**, one of:
|
||||
|
||||
- a Mac with Apple silicon (M1 or later), macOS 13 or later
|
||||
- Windows 10 or 11, with WSL (Windows' own Linux, installed in step 2)
|
||||
- Linux on x86_64 or arm64 (Ubuntu, Debian, Fedora, ...)
|
||||
|
||||
with an internet connection, about 6 GB of free disk space and 8 GB of memory
|
||||
or more. (An Intel Mac works too if you install LLVM 22 yourself: see
|
||||
*If something goes wrong*.)
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Unzip the kit
|
||||
|
||||
Unzip `sunshine-ps5-kit-<version>.zip` anywhere. You get a folder with this
|
||||
`README.md`, `make-title.sh` and the rest of the kit in it.
|
||||
|
||||
## Step 2: Get your computer ready (once)
|
||||
|
||||
**Mac.** Open Terminal and run:
|
||||
|
||||
```sh
|
||||
xcode-select --install
|
||||
```
|
||||
|
||||
and click Install. These are Apple's Command Line Tools (git, Python and a
|
||||
compiler). If it says they are already installed, you are ready.
|
||||
|
||||
**Windows.** Open PowerShell **as administrator** and run:
|
||||
|
||||
```powershell
|
||||
wsl --install
|
||||
```
|
||||
|
||||
Restart when it asks, then open **Ubuntu** from the Start menu and choose a
|
||||
user name and password. Then, in Ubuntu, install what the build uses and the
|
||||
compiler, LLVM 22, from [apt.llvm.org](https://apt.llvm.org) (LLVM's own
|
||||
packages for Ubuntu):
|
||||
|
||||
```sh
|
||||
sudo apt update && sudo apt install -y git python3 patch curl xz-utils unzip lsb-release wget gnupg software-properties-common
|
||||
curl -fsSL https://apt.llvm.org/llvm.sh -o /tmp/llvm.sh && sudo bash /tmp/llvm.sh 22 && sudo apt install -y llvm-22
|
||||
```
|
||||
|
||||
Move the kit into Ubuntu's own disk (building from the Windows drive is
|
||||
several times slower). If you unzipped it to your Downloads folder:
|
||||
|
||||
```sh
|
||||
cp -r /mnt/c/Users/<you>/Downloads/sunshine-ps5-kit-<version> ~/
|
||||
cd ~/sunshine-ps5-kit-<version>
|
||||
```
|
||||
|
||||
**Linux.** You need git, Python 3, `patch` and curl, and LLVM 22 is best
|
||||
installed from [apt.llvm.org](https://apt.llvm.org). On Ubuntu or Debian:
|
||||
|
||||
```sh
|
||||
sudo apt install -y git python3 patch curl xz-utils lsb-release wget gnupg software-properties-common
|
||||
curl -fsSL https://apt.llvm.org/llvm.sh -o /tmp/llvm.sh && sudo bash /tmp/llvm.sh 22 && sudo apt install -y llvm-22
|
||||
```
|
||||
|
||||
On another Linux, the build fetches LLVM's own build of LLVM 22 instead, if
|
||||
the libraries it needs are there (it says what to do if they are not).
|
||||
|
||||
## Step 3: Build the title
|
||||
|
||||
Open a terminal **in the kit's folder** (on Windows, the Ubuntu window from
|
||||
step 2) and run:
|
||||
|
||||
```sh
|
||||
./make-title.sh
|
||||
```
|
||||
|
||||
The first time, it fetches what it needs and your computer does not have: on a
|
||||
Mac the compiler (LLVM 22, about 1.5 GB), and CMake and Ninja (small build
|
||||
tools), from their projects' own releases. It checks each download and keeps
|
||||
it for next time (in `~/Library/Caches/sunshine-ps5-kit` on a Mac,
|
||||
`~/.cache/sunshine-ps5-kit` on Linux). It installs nothing system-wide and
|
||||
never asks for your password.
|
||||
|
||||
### What you will see
|
||||
|
||||
| Stage | What it does | How long |
|
||||
| --- | --- | --- |
|
||||
| First run only | Fetches what is missing: on a Mac the compiler, CMake and Ninja (about 1.6 GB) | a few minutes |
|
||||
| `fetching` | Downloads the PC port and the decompilation from GitHub | a minute |
|
||||
| `configuring` | Prepares the build | under a minute |
|
||||
| `compiling the game's code` | Compiles about 600 files for the PS5. **This is the long one.** | 3 to 15 minutes |
|
||||
| `linking` | Joins it to the kit's parts and makes `eboot.bin` | under a minute |
|
||||
|
||||
It is finished when it prints the path of a folder ending in `PPSA99312`.
|
||||
|
||||
## Step 4: Copy it to the PS5
|
||||
|
||||
The result is the folder `out/PPSA99312` inside the kit's folder (about 50 MB).
|
||||
On Windows, open it from File Explorer at
|
||||
`\\wsl$\Ubuntu\home\<you>\sunshine-ps5-kit-<version>\out`.
|
||||
|
||||
With an FTP program (FileZilla, Cyberduck, WinSCP), connect to your PS5 and
|
||||
copy that **whole folder** into `/data/homebrew/`. Then copy **your disc
|
||||
image** into it, beside `eboot.bin`, so that you end up with:
|
||||
|
||||
```text
|
||||
/data/homebrew/PPSA99312/eboot.bin
|
||||
/data/homebrew/PPSA99312/sce_sys/
|
||||
/data/homebrew/PPSA99312/sce_module/
|
||||
/data/homebrew/PPSA99312/res/
|
||||
/data/homebrew/PPSA99312/licenses/
|
||||
/data/homebrew/PPSA99312/GMSE01.iso (your disc image; any name)
|
||||
```
|
||||
|
||||
## Step 5: Play
|
||||
|
||||
"Super Mario Sunshine" appears on the PS5's home screen. Start it like any game.
|
||||
|
||||
| DualSense | In the game |
|
||||
| --- | --- |
|
||||
| Cross | A (jump, confirm) |
|
||||
| Circle | B (dive, back) |
|
||||
| Square | X |
|
||||
| Triangle | Y (first-person view) |
|
||||
| R1 | Z |
|
||||
| L1 + R1 | **Save state** (in a level); in the pause menu, the list of save states to load |
|
||||
| L2, R2 | L, R (R sprays FLUDD) |
|
||||
| Options | Start (pause, map) |
|
||||
| Touch pad button | **Settings** |
|
||||
| Sticks, D-pad | as on a GameCube |
|
||||
|
||||
The prompts the game shows on screen are the DualSense's own.
|
||||
|
||||
### The settings menu
|
||||
|
||||
Press the touch pad's button at any time. The D-pad moves and changes, Cross
|
||||
steps through a row's choices, Circle or the touch pad closes it.
|
||||
|
||||
| Row | What it does |
|
||||
| --- | --- |
|
||||
| Invert FLUDD Aim X / Y | flips the left stick while you aim FLUDD |
|
||||
| Invert Camera X / Y | flips the right stick's camera |
|
||||
| Camera Shake | OFF, HALF or FULL: the game's earthquakes (off to begin with) |
|
||||
| Performance Overlay | OFF, MINIMAL (the frame rate alone) or FULL |
|
||||
| Heat Haze | the heat shimmer |
|
||||
| Resolution \* | 960p, 1440p, 1920p or 2160p |
|
||||
| Frame Rate \* | 30 or 60 |
|
||||
| Picture \* | 4:3 or 16:9 |
|
||||
| Smoothing (FXAA) \* | on or off |
|
||||
|
||||
Rows marked \* take effect the next time you start the game. Below them are
|
||||
the PC port's cheats (god mode, infinite lives and so on).
|
||||
|
||||
Your saves and settings are kept on the console in
|
||||
`/data/homebrew/PPSA99312/userdata/`.
|
||||
|
||||
### Save states
|
||||
|
||||
Hold L1 and press R1 in a level to save the moment. Pause (Options) and press
|
||||
L1 + R1 again to see your save states, with a picture of each, and load one.
|
||||
They are kept beside your memory card, in a `savestates` folder in
|
||||
`/data/homebrew/PPSA99312/userdata/`. A state loads exactly only in the build
|
||||
that saved it; after you rebuild, older states put Mario back where he was in
|
||||
that level and keep your progress, but the level starts afresh.
|
||||
|
||||
---
|
||||
|
||||
## Optional: HD textures
|
||||
|
||||
The port loads texture packs made for the Dolphin emulator. One command
|
||||
downloads the [Super Mario Sunshine UHD Texture Pack](https://github.com/qashto/Super_Mario_Sunshine_UHD_Texture_Pack)
|
||||
(by qashto and razius) and the [HD texture extras](https://github.com/chasem-dev/sms-hd-texture-extras)
|
||||
(by chasem-dev) from their authors and makes a copy sized for the console:
|
||||
|
||||
```sh
|
||||
./make-texture-pack.sh
|
||||
```
|
||||
|
||||
(On Windows, in the Ubuntu window, as in step 3.) The first time it fetches
|
||||
the two Python packages it uses, Pillow and etcpak, and on Linux 7-Zip, into
|
||||
the same cache as the compiler.
|
||||
|
||||
It downloads about 1 GB, needs about 5 GB of free disk space while it works,
|
||||
and leaves `out/textures` (1.3 GB). Copy that folder to the console as:
|
||||
|
||||
```text
|
||||
/data/homebrew/PPSA99312/mods/textures/
|
||||
```
|
||||
|
||||
The packs are not part of this kit and are not stored with it: they belong to
|
||||
their authors, and the copy you make is for your own use.
|
||||
|
||||
**Why a console-sized copy?** The console's graphics driver needs about ten
|
||||
times a texture's size in memory and takes texture data slowly. With the pack
|
||||
as published (images up to 8192 pixels), Delfino Plaza took 17 seconds to load
|
||||
and a second level ran the console out of memory. With nothing larger than
|
||||
2048 pixels the plaza loads in about 5 seconds. Any other Dolphin pack can be
|
||||
shrunk the same way with `tools/shrink-texture-pack.py SOURCE DEST`.
|
||||
|
||||
---
|
||||
|
||||
## If something goes wrong
|
||||
|
||||
**"this Mac needs Apple's Command Line Tools"**
|
||||
Run `xcode-select --install`, wait for it to finish, and run the command
|
||||
again.
|
||||
|
||||
**"missing: git python3 ..."**
|
||||
Install what it names (step 2 has the command for Ubuntu and Debian).
|
||||
|
||||
**"LLVM's own Linux build would not run here: it needs libxml2.so.2"**
|
||||
Newer Linux releases (Ubuntu 26.04, Debian 13) no longer have that library.
|
||||
Install LLVM 22 from apt.llvm.org as in step 2; the build then uses it.
|
||||
|
||||
**"LLVM publishes no build for Darwin on x86_64"** (an Intel Mac)
|
||||
Install LLVM 22 with Homebrew, then point the kit at it:
|
||||
|
||||
```sh
|
||||
brew install llvm@22 lld@22
|
||||
PS5_CLANG=$(brew --prefix llvm@22)/bin/clang ./make-title.sh
|
||||
```
|
||||
|
||||
**The fetch fails**
|
||||
The build needs to reach github.com the first time. Check the connection and
|
||||
run the command again.
|
||||
|
||||
**The build stops part way with "Killed"**
|
||||
It ran out of memory. Run fewer compilers at once:
|
||||
|
||||
```sh
|
||||
JOBS=4 ./make-title.sh
|
||||
```
|
||||
|
||||
On Windows, WSL may also need more memory: see Microsoft's notes on
|
||||
`.wslconfig`.
|
||||
|
||||
**It was interrupted**
|
||||
Run the same command again. It carries on from where it stopped, including a
|
||||
download.
|
||||
|
||||
**A download fails or "did not download correctly"**
|
||||
Run the command again. Every download is checked against the checksum the kit
|
||||
expects, and a bad one is thrown away.
|
||||
|
||||
**The game does not appear on the PS5**
|
||||
Check the folder is exactly `/data/homebrew/PPSA99312/` with `eboot.bin`
|
||||
directly inside it (not `PPSA99312/PPSA99312/`), and that ShadowMountPlus is
|
||||
running.
|
||||
|
||||
**It starts and closes at once**
|
||||
The disc image is missing, or is not the North American release. It must be
|
||||
in `/data/homebrew/PPSA99312/`, beside `eboot.bin`.
|
||||
|
||||
## Updating to a newer kit
|
||||
|
||||
Unzip the new kit, build again, and copy the new `out/PPSA99312` over the old
|
||||
one on the console. Your saves, settings, disc image and texture packs are
|
||||
left alone.
|
||||
|
||||
## With Docker instead
|
||||
|
||||
If you would rather build in a container (Docker Desktop or Podman), the kit
|
||||
still can: `./make-title.sh --docker` and `./make-texture-pack.sh --docker`.
|
||||
On Windows without WSL, in PowerShell:
|
||||
|
||||
```powershell
|
||||
docker build -t sunshine-ps5-kit .
|
||||
docker run --rm -v ${PWD}\out:/out sunshine-ps5-kit /out
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What is in the kit
|
||||
|
||||
| Path | What |
|
||||
| --- | --- |
|
||||
| `make-title.sh` | Builds the title (`--docker`: in a container, with `Dockerfile`). |
|
||||
| `build-title.sh` | The build itself: fetch, patch, compile, link. |
|
||||
| `tools.sh` | Finds LLVM 22, CMake and Ninja, or fetches them once, checked. |
|
||||
| `lib/` | The port's own compiled parts: the platform layer, the graphics layer, SDL2. |
|
||||
| `opengl/` | The PS5 OpenGL SDK's driver, as this port links it. |
|
||||
| `vulkan/` | PS5_Vulkan's link recipe and the tool that makes `eboot.bin` (no Vulkan driver), built for Linux in `vulkan/prebuilt/`. |
|
||||
| `sdk/` | The payload SDK's compiler wrappers, headers, libraries and link scripts. |
|
||||
| `ps5/` | The console's patch to the PC port, the link script, the title's icon and pictures. |
|
||||
| `make-texture-pack.sh`, `tools/` | Downloads an HD texture pack and sizes it for the console. |
|
||||
| `REVISIONS`, `SOURCES.txt`, `LICENSING.md`, `licenses/` | What the build fetches, where each part's source is, and its licence. |
|
||||
|
||||
The project, with what works, what does not, and how the port is made:
|
||||
<https://github.com/griches/PS5_Sunshine>
|
||||
|
||||
This project is not affiliated with, endorsed or sponsored by Nintendo or Sony
|
||||
Interactive Entertainment.
|
||||
@@ -1,154 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Super Mario Sunshine for the PlayStation 5 - the builder kit: make the title on your own machine.
|
||||
#
|
||||
# build-title.sh [OUT] OUT/PPSA99312/ (OUT defaults to ./out)
|
||||
# build-title.sh --tools only build the kit's host tools (the container's image does)
|
||||
#
|
||||
# The kit is a build of the port without the game's own code. This script
|
||||
# fetches the two public source trees that code is compiled from (the PC port,
|
||||
# chasem-dev/sms-pc-port, and the decompilation it builds, chasem-dev/sms-english,
|
||||
# both at the revisions SOURCES.txt names), applies the console's patch,
|
||||
# compiles the game's code with the kit's SDK and links it with the kit's
|
||||
# objects, by the recipe the kit was tested with. Nothing of the game's data is
|
||||
# involved: the title plays your own disc image, which you copy beside
|
||||
# eboot.bin on the console.
|
||||
#
|
||||
# Runs on macOS (Apple silicon; with Apple's Command Line Tools) and on Linux
|
||||
# (x86_64 or arm64; on Windows, in WSL), with git, python3, patch and curl.
|
||||
# LLVM 22, CMake and Ninja come from this machine if it has them, and are
|
||||
# otherwise fetched once into a cache (tools.sh says from where). On Linux the
|
||||
# kit's own ps5-native-tool is used; elsewhere it is compiled here.
|
||||
# make-title.sh --docker runs this in a container instead.
|
||||
#
|
||||
# JOBS compilers at once (default: one a processor)
|
||||
# PS5_CLANG LLVM 22's clang, to use instead of looking for one
|
||||
# KIT_CACHE where fetched tools are kept
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
set -euo pipefail
|
||||
kit=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
||||
sdk="$kit/sdk" vulkan="$kit/vulkan"
|
||||
say() { printf '==> %s\n' "$*"; }
|
||||
fail() { echo "build-title.sh: $*" >&2; exit 2; }
|
||||
# shellcheck source=tools.sh
|
||||
source "$kit/tools.sh"
|
||||
|
||||
# ELF -> the console's SELF: the kit's own build for Linux, or compiled here
|
||||
build_tools() {
|
||||
local host_cxx=${CXX:-c++} native="$vulkan/tooling/native"
|
||||
local prebuilt="$vulkan/prebuilt/$kit_os-$kit_arch/ps5-native-tool"
|
||||
[[ $kit_arch == arm64 && $kit_os == Linux ]] && prebuilt="$vulkan/prebuilt/Linux-aarch64/ps5-native-tool"
|
||||
if [[ ! -x $vulkan/build/host/ps5-native-tool && -f $prebuilt ]]; then
|
||||
mkdir -p "$vulkan/build/host"
|
||||
cp "$prebuilt" "$vulkan/build/host/ps5-native-tool"
|
||||
chmod +x "$vulkan/build/host/ps5-native-tool"
|
||||
fi
|
||||
if [[ ! -x $vulkan/build/host/ps5-native-tool ]]; then
|
||||
command -v "$host_cxx" > /dev/null || fail "a C++ compiler (c++) is needed to build the kit's ps5-native-tool"
|
||||
mkdir -p "$vulkan/build/host"
|
||||
"$host_cxx" -std=c++20 -O2 "$native/native_app_builder.cpp" "$native/self_container.cpp" \
|
||||
"$native/elf_object.cpp" "$native/sce_module_writer.cpp" -lz -o "$vulkan/build/host/ps5-native-tool"
|
||||
fi
|
||||
}
|
||||
if [[ ${1:-} == --tools ]]; then
|
||||
build_tools
|
||||
exit 0
|
||||
fi
|
||||
case ${1:-} in -h | --help) sed -n '2,25p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'; exit 0 ;; esac
|
||||
|
||||
out=${1:-$PWD/out}
|
||||
mkdir -p "$out"
|
||||
out=$(cd -- "$out" && pwd)
|
||||
work="$out/.work"
|
||||
jobs=${JOBS:-$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)}
|
||||
title_id=$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["titleId"])' "$kit/ps5/sce_sys/param.json")
|
||||
app="$out/$title_id"
|
||||
mkdir -p "$work/bin" "$app"
|
||||
|
||||
# ---- the tools: this machine's, or fetched once (tools.sh)
|
||||
# (an unzip that dropped the executable bits: the SDK's wrappers are run directly)
|
||||
chmod +x "$sdk"/bin/* 2>/dev/null || true
|
||||
kit_check_basics || exit 2
|
||||
kit_find_llvm || exit 2
|
||||
kit_find_cmake || exit 2
|
||||
kit_find_ninja || exit 2
|
||||
real_clang=$kit_clang
|
||||
llvm_bin=$(dirname "$real_clang")
|
||||
say "compiler: $real_clang; $(cmake --version | head -1); ninja $(ninja --version)"
|
||||
|
||||
# ---- the compiler: LLVM 22, with the kit's builtins for the console's processor
|
||||
export LLVM_CONFIG="$llvm_bin/llvm-config"
|
||||
cat > "$work/bin/clang" <<WRAPPER
|
||||
#!/bin/sh
|
||||
if [ "\$#" -eq 1 ] && [ "\$1" = "--print-resource-dir" ]; then echo "$kit/clang-rt"; exit 0; fi
|
||||
exec "$real_clang" "\$@"
|
||||
WRAPPER
|
||||
chmod +x "$work/bin/clang"
|
||||
export PS5_CLANG="$work/bin/clang"
|
||||
for tool in git cmake ninja patch python3; do
|
||||
command -v "$tool" > /dev/null || fail "$tool was not found"
|
||||
done
|
||||
|
||||
build_tools
|
||||
|
||||
# ---- the sources of the game's code, at the revisions the kit was built from
|
||||
port_url=$(sed -n 's/^port_url=//p' "$kit/REVISIONS")
|
||||
port_rev=$(sed -n 's/^port_rev=//p' "$kit/REVISIONS")
|
||||
decomp_rev=$(sed -n 's/^decomp_rev=//p' "$kit/REVISIONS")
|
||||
source="$work/sms-pc-port"
|
||||
stamp="$port_rev $decomp_rev $(kit_sha256 "$kit/ps5/patches/sms-pc-port.patch")"
|
||||
if [[ $(cat "$work/.source-stamp" 2>/dev/null) != "$stamp" ]]; then
|
||||
say "fetching the PC port and the decompilation (about 150 MB)"
|
||||
rm -rf -- "$source" "$work/game"
|
||||
git init -q "$source"
|
||||
git -C "$source" remote add origin "$port_url"
|
||||
git -C "$source" fetch -q --depth 1 origin "$port_rev"
|
||||
git -C "$source" -c advice.detachedHead=false checkout -q FETCH_HEAD
|
||||
git -C "$source" submodule update -q --init --depth 1 decomp
|
||||
[[ $(git -C "$source/decomp" rev-parse HEAD) == "$decomp_rev" ]] ||
|
||||
fail "the decompilation is at $(git -C "$source/decomp" rev-parse HEAD), not $decomp_rev"
|
||||
say "applying the console's patch"
|
||||
git -C "$source" apply --whitespace=nowarn "$kit/ps5/patches/sms-pc-port.patch"
|
||||
echo "$stamp" > "$work/.source-stamp"
|
||||
fi
|
||||
|
||||
# ---- the game's code
|
||||
build="$work/game"
|
||||
if [[ ! -f $build/build.ninja ]]; then
|
||||
say "configuring"
|
||||
mkdir -p "$build"
|
||||
cmake -S "$source" -B "$build" -G Ninja \
|
||||
-DCMAKE_TOOLCHAIN_FILE="$sdk/toolchain/prospero.cmake" \
|
||||
-DCMAKE_PROJECT_sms_port_INCLUDE="$kit/ps5/cmake/ps5-kit-game.cmake" \
|
||||
-DPS5_KIT_DIR="$kit" > "$build/configure.log" 2>&1 ||
|
||||
{ tail -40 "$build/configure.log" >&2; mv "$build" "$build.failed-$$"; rm -rf "$build.failed-$$"; fail "the configuration failed"; }
|
||||
fi
|
||||
say "compiling the game's code, $jobs files at a time (this is the long step)"
|
||||
ninja -C "$build" -j "$jobs" sms_game
|
||||
[[ -f $build/libsms_game.a ]] || fail "the game's code did not build"
|
||||
|
||||
# ---- the link: the kit's objects in the order they were tested in, with that archive
|
||||
say "linking"
|
||||
inputs=()
|
||||
while IFS= read -r line; do
|
||||
case $line in
|
||||
@GAMECODE@) inputs+=("$build/libsms_game.a") ;;
|
||||
-*) inputs+=("$line") ;;
|
||||
*) inputs+=("$kit/lib/$line") ;;
|
||||
esac
|
||||
done < "$kit/lib/link.txt"
|
||||
PS5_DIST_DIR="$out" PS5_OPENGL_PREFIX="$kit/opengl/sdk" SMS_SAVESTATE_RANGES="$source/tools/savestate_ranges.py" \
|
||||
bash "$kit/ps5/tools/link-title.sh" "$work" "$vulkan" "$sdk" "${inputs[@]}"
|
||||
mkdir -p "$app/res"
|
||||
cp -R "$kit/ps5/res/." "$app/res/"
|
||||
cp "$kit/LICENSING.md" "$app/licenses/"
|
||||
|
||||
# a container runs as root: hand the result to whoever owns the output folder
|
||||
if [[ $(id -u) -eq 0 && $kit_os == Linux ]]; then
|
||||
owner=$(stat -c '%u:%g' "$out")
|
||||
[[ $owner == 0:0 ]] || chown -R "$owner" "$out" 2>/dev/null || true
|
||||
fi
|
||||
say "$app"
|
||||
echo " copy the folder $title_id to /data/homebrew/ on the console (FTP), and your own"
|
||||
echo " disc image (USA, GMSE01: .iso, .gcm or .ciso) into it, beside eboot.bin."
|
||||
echo " The title you built holds the game's code: it is yours to play, not to share."
|
||||
@@ -1,64 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Super Mario Sunshine for the PlayStation 5 - an HD texture pack for the console.
|
||||
#
|
||||
# make-texture-pack.sh [OUT] OUT/textures/ (OUT defaults to ./out), made on this machine
|
||||
# make-texture-pack.sh --docker [OUT] the same, in the kit's container (Docker, or Podman: CONTAINER=podman)
|
||||
#
|
||||
# Optional. Downloads the UHD texture pack and the HD extras from their
|
||||
# authors, makes a copy sized for the console (tools/get-texture-pack.py) and
|
||||
# leaves it in OUT/textures: copy that to the console as
|
||||
# /data/homebrew/PPSA99312/mods/textures. Needs Python 3, an internet
|
||||
# connection and about 5 GB of free disk space. The Python packages it uses
|
||||
# (Pillow, etcpak) and, on Linux, 7-Zip are fetched once into the kit's cache
|
||||
# (tools.sh); a Mac's own tar opens the pack.
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
set -euo pipefail
|
||||
kit=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
||||
|
||||
if [[ ${1:-} == --docker ]]; then
|
||||
shift
|
||||
out=${1:-$PWD/out}
|
||||
mkdir -p "$out"
|
||||
out=$(cd -- "$out" && pwd)
|
||||
container=${CONTAINER:-docker}
|
||||
command -v "$container" > /dev/null || { echo "make-texture-pack.sh: $container was not found: install Docker Desktop (or Podman), or run without --docker" >&2; exit 2; }
|
||||
tag=sunshine-ps5-kit:$(<"$kit/VERSION")
|
||||
"$container" image inspect "$tag" > /dev/null 2>&1 || "$container" build -t "$tag" "$kit"
|
||||
exec "$container" run --rm --entrypoint python3 -v "$out":/out "$tag" /kit/tools/get-texture-pack.py /out
|
||||
fi
|
||||
|
||||
out=${1:-$PWD/out}
|
||||
mkdir -p "$out"
|
||||
out=$(cd -- "$out" && pwd)
|
||||
# shellcheck source=tools.sh
|
||||
source "$kit/tools.sh"
|
||||
command -v python3 > /dev/null || { echo "make-texture-pack.sh: Python 3 is needed" >&2; exit 2; }
|
||||
if [[ $kit_os == Darwin ]] && ! xcode-select -p > /dev/null 2>&1; then
|
||||
echo "make-texture-pack.sh: this Mac needs Apple's Command Line Tools (for Python): run xcode-select --install" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Pillow and etcpak, for this Python
|
||||
packages=$(python3 "$kit/tools/fetch-wheels.py" "$kit_cache/python") || exit 2
|
||||
export PYTHONPATH="$packages${PYTHONPATH:+:$PYTHONPATH}"
|
||||
|
||||
# 7-Zip on Linux (GNU tar cannot open a .7z; a Mac's tar can)
|
||||
if [[ $kit_os == Linux ]] && ! command -v 7z > /dev/null && ! command -v 7zz > /dev/null && ! command -v 7za > /dev/null; then
|
||||
case $kit_arch in
|
||||
x86_64) asset=7z2604-linux-x64.tar.xz sha=fc0327ba27e89bd086cf426dff17d77de582953cdbbc10a6576540a06853ffcd ;;
|
||||
aarch64 | arm64) asset=7z2604-linux-arm64.tar.xz sha=5b0ac3aa91c1e3499f011d6af69d7f6b4de3ef2f011d9e2ff44c58ebd04aa388 ;;
|
||||
*) echo "make-texture-pack.sh: 7-Zip is needed (7z, 7zz or 7za)" >&2; exit 2 ;;
|
||||
esac
|
||||
dest="$kit_cache/7zip-26.04"
|
||||
if [[ ! -x $dest/7zz ]]; then
|
||||
echo "==> fetching 7-Zip 26.04"
|
||||
kit_fetch "https://github.com/ip7z/7zip/releases/download/26.04/$asset" "$sha" "$kit_cache/$asset" || exit 2
|
||||
mkdir -p "$dest"
|
||||
tar -xJf "$kit_cache/$asset" -C "$dest" 7zz
|
||||
rm -f "$kit_cache/$asset"
|
||||
fi
|
||||
PATH=$dest:$PATH
|
||||
fi
|
||||
|
||||
exec python3 "$kit/tools/get-texture-pack.py" "$out"
|
||||
@@ -1,31 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Super Mario Sunshine for the PlayStation 5 - the builder kit: make the title.
|
||||
#
|
||||
# make-title.sh [OUT] OUT/PPSA99312/ (OUT defaults to ./out), built on this machine
|
||||
# make-title.sh --docker [OUT] the same, built in a container (Docker, or Podman: CONTAINER=podman)
|
||||
#
|
||||
# On this machine (macOS on Apple silicon, Linux, or WSL on Windows) it runs
|
||||
# build-title.sh, which uses the compiler and build tools this machine has, or
|
||||
# fetches them once (LLVM 22 is about 1.5 GB). Each title build fetches the
|
||||
# game's source code from GitHub the first time (it is kept in OUT/.work
|
||||
# afterwards). See README.md.
|
||||
#
|
||||
# JOBS compilers at once (default: one a processor)
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
set -euo pipefail
|
||||
kit=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
||||
|
||||
if [[ ${1:-} != --docker ]]; then
|
||||
exec bash "$kit/build-title.sh" "$@"
|
||||
fi
|
||||
shift
|
||||
out=${1:-$PWD/out}
|
||||
mkdir -p "$out"
|
||||
out=$(cd -- "$out" && pwd)
|
||||
container=${CONTAINER:-docker}
|
||||
command -v "$container" > /dev/null || { echo "make-title.sh: $container was not found: install Docker Desktop (or Podman), or build without --docker" >&2; exit 2; }
|
||||
tag=sunshine-ps5-kit:$(<"$kit/VERSION")
|
||||
|
||||
"$container" image inspect "$tag" > /dev/null 2>&1 || "$container" build -t "$tag" "$kit"
|
||||
exec "$container" run --rm ${JOBS:+-e JOBS="$JOBS"} -v "$out":/out "$tag" /out
|
||||
@@ -1,243 +0,0 @@
|
||||
# Super Mario Sunshine for the PlayStation 5 - the builder kit's tools: found on this machine, or fetched once.
|
||||
#
|
||||
# Sourced by build-title.sh. Sets kit_clang (LLVM 22's clang, with lld and the
|
||||
# LLVM tools beside it), and puts CMake and Ninja on PATH. Each comes from this
|
||||
# machine when it has a version that works:
|
||||
#
|
||||
# LLVM 22 PS5_CLANG, /usr/lib/llvm-22 (apt.llvm.org), clang-22 on PATH, or
|
||||
# Homebrew's llvm@22 with lld beside it
|
||||
# CMake 3.20 to 3.x on PATH (CMake 4 drops what SDL's build still asks for)
|
||||
# Ninja on PATH
|
||||
#
|
||||
# and otherwise from its project's own release for this kind of machine, checked
|
||||
# against the SHA-256 below and kept in the kit's cache (KIT_CACHE; by default
|
||||
# ~/Library/Caches/sunshine-ps5-kit on a Mac, ~/.cache/sunshine-ps5-kit on Linux),
|
||||
# so it is fetched once for every kit. LLVM's release is the large one: about
|
||||
# 1.5 GB to fetch (1.9 GB for Linux), of which about 450 MB is kept.
|
||||
#
|
||||
# On Ubuntu and Debian (and WSL), apt.llvm.org's packages are the better way to
|
||||
# LLVM 22, and the kit's README says so: LLVM's own Linux build needs
|
||||
# libxml2.so.2, which Ubuntu 26.04 and Debian 13 no longer have (they moved to
|
||||
# libxml2.so.16). It is fetched only where the libraries it needs are there.
|
||||
#
|
||||
# Bash 3.2 (a Mac's) runs this: no associative arrays.
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
kit_llvm_version=22.1.8
|
||||
kit_cmake_version=3.31.12
|
||||
kit_ninja_version=1.13.2
|
||||
|
||||
kit_os=$(uname -s)
|
||||
kit_arch=$(uname -m)
|
||||
case $kit_os-$kit_arch in
|
||||
Darwin-arm64)
|
||||
kit_llvm_asset=LLVM-$kit_llvm_version-macOS-ARM64.tar.xz
|
||||
kit_llvm_sha=f260f4f7c0d430828a81ae8a3826a1d63fc0963ec2459489308cc23b1f7eab4f
|
||||
;;
|
||||
Linux-x86_64)
|
||||
kit_llvm_asset=LLVM-$kit_llvm_version-Linux-X64.tar.xz
|
||||
kit_llvm_sha=df0e1ecf16caf3489a272a5eea4eec9b0d82878f6477fa309504f918a0006384
|
||||
;;
|
||||
Linux-aarch64 | Linux-arm64)
|
||||
kit_llvm_asset=LLVM-$kit_llvm_version-Linux-ARM64.tar.xz
|
||||
kit_llvm_sha=805efad2bb91cb4967fa569e0881d10c0f69c04461cf671cccbae19f547acc34
|
||||
;;
|
||||
*) kit_llvm_asset= ;; # (an Intel Mac: LLVM publishes no build for it)
|
||||
esac
|
||||
case $kit_os-$kit_arch in
|
||||
Darwin-*)
|
||||
kit_cmake_asset=cmake-$kit_cmake_version-macos-universal.tar.gz
|
||||
kit_cmake_sha=799af7fd545db9bf1b9cfe72f8095880e727a2d4e0df0e3dffc3bc7b95c2d3b0
|
||||
kit_cmake_bin=CMake.app/Contents/bin
|
||||
kit_ninja_asset=ninja-mac.zip
|
||||
kit_ninja_sha=c99048673aa765960a99cf10c6ddb9f1fad506099ff0a0e137ad8960a88f321b
|
||||
;;
|
||||
Linux-x86_64)
|
||||
kit_cmake_asset=cmake-$kit_cmake_version-linux-x86_64.tar.gz
|
||||
kit_cmake_sha=0dc2e9a6860f06bf10bd8fadc03e35d9eeb4df46e33763a7e480e987758f385c
|
||||
kit_cmake_bin=bin
|
||||
kit_ninja_asset=ninja-linux.zip
|
||||
kit_ninja_sha=5749cbc4e668273514150a80e387a957f933c6ed3f5f11e03fb30955e2bbead6
|
||||
;;
|
||||
Linux-aarch64 | Linux-arm64)
|
||||
kit_cmake_asset=cmake-$kit_cmake_version-linux-aarch64.tar.gz
|
||||
kit_cmake_sha=83f8fd91d2038a56556e1400390fcfe42f79602940c494f6c6f1cdae7f9e7f40
|
||||
kit_cmake_bin=bin
|
||||
kit_ninja_asset=ninja-linux-aarch64.zip
|
||||
kit_ninja_sha=fd2cacc8050a7f12a16a2e48f9e06fca5c14fc4c2bee2babb67b58be17a607fc
|
||||
;;
|
||||
*) kit_cmake_asset= kit_ninja_asset= ;;
|
||||
esac
|
||||
|
||||
if [[ -n ${KIT_CACHE:-} ]]; then
|
||||
kit_cache=$KIT_CACHE
|
||||
elif [[ $kit_os == Darwin ]]; then
|
||||
kit_cache=$HOME/Library/Caches/sunshine-ps5-kit
|
||||
else
|
||||
kit_cache=${XDG_CACHE_HOME:-$HOME/.cache}/sunshine-ps5-kit
|
||||
fi
|
||||
|
||||
kit_sha256() { python3 -c 'import hashlib,sys; h=hashlib.sha256(); f=open(sys.argv[1],"rb"); [h.update(b) for b in iter(lambda: f.read(1<<20), b"")]; print(h.hexdigest())' "$1"; }
|
||||
|
||||
# kit_fetch URL SHA256 FILE: download, check, and keep it only if it matches
|
||||
kit_fetch() {
|
||||
local url=$1 sha=$2 file=$3
|
||||
mkdir -p "$(dirname "$file")"
|
||||
# (one already here, from a run stopped while unpacking it)
|
||||
[[ -f $file && $(kit_sha256 "$file") == "$sha" ]] && return 0
|
||||
if command -v curl > /dev/null; then
|
||||
curl -fL --retry 3 --progress-bar -o "$file.part" "$url" || { rm -f "$file.part"; return 1; }
|
||||
elif command -v wget > /dev/null; then
|
||||
wget -q --show-progress -O "$file.part" "$url" || { rm -f "$file.part"; return 1; }
|
||||
else
|
||||
echo "tools.sh: curl or wget is needed to download $url" >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ $(kit_sha256 "$file.part") != "$sha" ]]; then
|
||||
rm -f "$file.part"
|
||||
echo "tools.sh: $url did not download correctly (its SHA-256 is not the expected one); try again" >&2
|
||||
return 1
|
||||
fi
|
||||
mv "$file.part" "$file"
|
||||
}
|
||||
|
||||
kit_is_llvm22() {
|
||||
[[ -x $1 ]] && "$1" --version 2>/dev/null | head -1 | grep -q "version 22\." || return 1
|
||||
local bin
|
||||
bin=$(dirname "$(kit_realpath "$1")")
|
||||
local tool
|
||||
for tool in ld.lld llvm-ar llvm-nm llvm-config llvm-objcopy llvm-strip; do
|
||||
[[ -x $bin/$tool ]] || return 1
|
||||
done
|
||||
# (lld needs a library clang does not: it has to run too)
|
||||
"$bin/ld.lld" --version > /dev/null 2>&1
|
||||
}
|
||||
|
||||
# The shared libraries LLVM's own Linux build needs, that this machine lacks
|
||||
kit_llvm_missing_libraries() {
|
||||
[[ $kit_os == Linux ]] || return 0
|
||||
local cache library
|
||||
cache=$( (ldconfig -p || /sbin/ldconfig -p) 2>/dev/null)
|
||||
for library in libz.so.1 libstdc++.so.6 libgcc_s.so.1 libxml2.so.2; do
|
||||
grep -q "^[[:space:]]*$library " <<< "$cache" || printf '%s ' "$library"
|
||||
done
|
||||
}
|
||||
|
||||
kit_llvm_advice() {
|
||||
echo "tools.sh: install LLVM 22 from apt.llvm.org (about 150 MB), then run this again:" >&2
|
||||
echo " sudo apt install -y lsb-release wget gnupg software-properties-common" >&2
|
||||
echo " curl -fsSL https://apt.llvm.org/llvm.sh -o /tmp/llvm.sh && sudo bash /tmp/llvm.sh 22 && sudo apt install -y llvm-22" >&2
|
||||
}
|
||||
|
||||
kit_realpath() { python3 -c 'import os,sys; print(os.path.realpath(sys.argv[1]))' "$1"; }
|
||||
|
||||
kit_find_llvm() {
|
||||
local candidate
|
||||
if [[ -n ${PS5_CLANG:-} ]]; then
|
||||
kit_is_llvm22 "$PS5_CLANG" && { kit_clang=$(kit_realpath "$PS5_CLANG"); return 0; }
|
||||
echo "tools.sh: PS5_CLANG ($PS5_CLANG) is not LLVM 22's clang with lld, llvm-ar, llvm-nm, llvm-config, llvm-objcopy and llvm-strip beside it" >&2
|
||||
return 1
|
||||
fi
|
||||
for candidate in /usr/lib/llvm-22/bin/clang "$(command -v clang-22 || true)" \
|
||||
/opt/homebrew/opt/llvm@22/bin/clang /usr/local/opt/llvm@22/bin/clang \
|
||||
"$kit_cache/llvm-$kit_llvm_version/bin/clang"; do
|
||||
[[ -n $candidate ]] && kit_is_llvm22 "$candidate" && { kit_clang=$(kit_realpath "$candidate"); return 0; }
|
||||
done
|
||||
[[ -n $kit_llvm_asset ]] || {
|
||||
echo "tools.sh: LLVM 22 was not found, and LLVM publishes no build for $kit_os on $kit_arch to fetch." >&2
|
||||
echo "tools.sh: install LLVM 22 with lld (Homebrew: brew install llvm@22 lld@22) and set PS5_CLANG to its clang." >&2
|
||||
return 1
|
||||
}
|
||||
local missing
|
||||
missing=$(kit_llvm_missing_libraries)
|
||||
if [[ -n $missing ]]; then
|
||||
echo "tools.sh: LLVM 22 was not found, and LLVM's own Linux build would not run here: it needs ${missing% }." >&2
|
||||
kit_llvm_advice
|
||||
return 1
|
||||
fi
|
||||
local dest="$kit_cache/llvm-$kit_llvm_version" top=${kit_llvm_asset%.tar.xz}
|
||||
echo "==> fetching LLVM $kit_llvm_version, the compiler (about 1.5 GB, once: it is kept in $kit_cache)"
|
||||
kit_fetch "https://github.com/llvm/llvm-project/releases/download/llvmorg-$kit_llvm_version/$kit_llvm_asset" \
|
||||
"$kit_llvm_sha" "$kit_cache/$kit_llvm_asset" || return 1
|
||||
echo "==> unpacking the parts of it the build uses"
|
||||
rm -rf "$dest.part"
|
||||
mkdir -p "$dest.part"
|
||||
local members=() name
|
||||
for name in clang clang-22 clang++ lld ld.lld llvm-ar llvm-ranlib llvm-nm llvm-config llvm-objcopy llvm-strip \
|
||||
llvm-readobj llvm-readelf; do
|
||||
members+=("$top/bin/$name")
|
||||
done
|
||||
members+=("$top/lib/clang/22/include")
|
||||
tar -xJf "$kit_cache/$kit_llvm_asset" -C "$dest.part" "${members[@]}" || return 1
|
||||
rm -rf "$dest"
|
||||
mv "$dest.part/$top" "$dest"
|
||||
rm -rf "$dest.part" "$kit_cache/$kit_llvm_asset"
|
||||
kit_is_llvm22 "$dest/bin/clang" || {
|
||||
echo "tools.sh: the LLVM fetched into $dest does not run here" >&2
|
||||
[[ $kit_os == Linux ]] && kit_llvm_advice
|
||||
return 1
|
||||
}
|
||||
kit_clang=$dest/bin/clang
|
||||
}
|
||||
|
||||
kit_cmake_works() {
|
||||
local version
|
||||
version=$("$1" --version 2>/dev/null | head -1 | sed -n 's/^cmake version \([0-9]*\)\.\([0-9]*\).*/\1 \2/p')
|
||||
[[ -n $version ]] || return 1
|
||||
set -- $version
|
||||
[[ $1 -eq 3 && $2 -ge 20 ]]
|
||||
}
|
||||
|
||||
kit_find_cmake() {
|
||||
local found
|
||||
found=$(command -v cmake || true)
|
||||
[[ -n $found ]] && kit_cmake_works "$found" && return 0
|
||||
local dest="$kit_cache/cmake-$kit_cmake_version"
|
||||
if [[ ! -x $dest/$kit_cmake_bin/cmake ]]; then
|
||||
[[ -n $kit_cmake_asset ]] || { echo "tools.sh: CMake 3.20 or later (but not 4) is needed" >&2; return 1; }
|
||||
echo "==> fetching CMake $kit_cmake_version (about 60 MB, once)"
|
||||
kit_fetch "https://github.com/Kitware/CMake/releases/download/v$kit_cmake_version/$kit_cmake_asset" \
|
||||
"$kit_cmake_sha" "$kit_cache/$kit_cmake_asset" || return 1
|
||||
rm -rf "$dest" "$dest.part"
|
||||
mkdir -p "$dest.part"
|
||||
tar -xzf "$kit_cache/$kit_cmake_asset" -C "$dest.part" || return 1
|
||||
mv "$dest.part/${kit_cmake_asset%.tar.gz}" "$dest"
|
||||
rm -rf "$dest.part" "$kit_cache/$kit_cmake_asset"
|
||||
fi
|
||||
PATH=$dest/$kit_cmake_bin:$PATH
|
||||
}
|
||||
|
||||
kit_find_ninja() {
|
||||
ninja --version > /dev/null 2>&1 && return 0
|
||||
local dest="$kit_cache/ninja-$kit_ninja_version"
|
||||
if [[ ! -x $dest/ninja ]]; then
|
||||
[[ -n $kit_ninja_asset ]] || { echo "tools.sh: Ninja is needed" >&2; return 1; }
|
||||
echo "==> fetching Ninja $kit_ninja_version"
|
||||
kit_fetch "https://github.com/ninja-build/ninja/releases/download/v$kit_ninja_version/$kit_ninja_asset" \
|
||||
"$kit_ninja_sha" "$kit_cache/$kit_ninja_asset" || return 1
|
||||
mkdir -p "$dest"
|
||||
python3 -m zipfile -e "$kit_cache/$kit_ninja_asset" "$dest" && chmod +x "$dest/ninja" || return 1
|
||||
rm -f "$kit_cache/$kit_ninja_asset"
|
||||
fi
|
||||
PATH=$dest:$PATH
|
||||
}
|
||||
|
||||
# What no download supplies: say what to install, for this machine
|
||||
kit_check_basics() {
|
||||
local missing=() tool
|
||||
for tool in git python3 patch tar; do
|
||||
command -v "$tool" > /dev/null || missing+=("$tool")
|
||||
done
|
||||
# a Mac's python3 and git are stand-ins until the Command Line Tools are installed
|
||||
if [[ $kit_os == Darwin ]] && ! xcode-select -p > /dev/null 2>&1; then
|
||||
echo "build-title.sh: this Mac needs Apple's Command Line Tools (git, Python and a C++ compiler)." >&2
|
||||
echo "build-title.sh: run xcode-select --install and start this again once they are installed." >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ ${#missing[@]} -gt 0 ]]; then
|
||||
echo "build-title.sh: missing: ${missing[*]}" >&2
|
||||
[[ $kit_os == Linux ]] && echo "build-title.sh: on Ubuntu or Debian (and WSL): sudo apt install git python3 patch xz-utils curl" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
@@ -706,10 +706,215 @@ index 393b8f9..16f82e6 100644
|
||||
signalFlag(*ready);
|
||||
uint64_t tail = s_tail.load();
|
||||
diff --git a/platform/gx/src/gx_hires.cpp b/platform/gx/src/gx_hires.cpp
|
||||
index bcd4174..3887f46 100644
|
||||
index bcd4174..937d1c2 100644
|
||||
--- a/platform/gx/src/gx_hires.cpp
|
||||
+++ b/platform/gx/src/gx_hires.cpp
|
||||
@@ -1049,6 +1049,9 @@ static bool beginUpload(Upload& u) {
|
||||
@@ -348,6 +348,8 @@ static void queryFormats() {
|
||||
// file's own mip levels, or 32-bit RGBA/BGRA. Blocks the GL cannot take are
|
||||
// decoded here; the levels a file leaves out are made by completeChain.
|
||||
enum { DDS_BC1 = BLOCK_BC1, DDS_BC2 = BLOCK_BC2, DDS_BC3 = BLOCK_BC3, DDS_BC7 = BLOCK_BC7, DDS_RGBA, DDS_BGRA };
|
||||
+static const GLenum kGl[] = {0, 0x83F1, 0x83F2, 0x83F3, 0x8E8C}; // S3TC DXT1/3/5 RGBA, BPTC UNORM
|
||||
+static bool gpuTakes(int fmt) { return (fmt >= DDS_BC1 && fmt <= DDS_BC3 && s_s3tc) || (fmt == DDS_BC7 && s_bptc); }
|
||||
|
||||
static bool readFile(const std::string& path, std::vector<uint8_t>& out) {
|
||||
FILE* f = fopen(path.c_str(), "rb");
|
||||
@@ -392,8 +394,7 @@ static bool decodeDds(const std::string& path, Loaded* L) {
|
||||
for (int m = std::max(w, h); m > 1; m >>= 1) full++;
|
||||
// Supported blocks stay compressed whatever levels the file has: the
|
||||
// missing ones are encoded in the same format (completeChain).
|
||||
- bool gpu = (fmt <= DDS_BC3 && s_s3tc) || (fmt == DDS_BC7 && s_bptc);
|
||||
- static const GLenum kGl[] = {0, 0x83F1, 0x83F2, 0x83F3, 0x8E8C}; // S3TC DXT1/3/5 RGBA, BPTC UNORM
|
||||
+ bool gpu = gpuTakes(fmt);
|
||||
L->w = w;
|
||||
L->h = h;
|
||||
L->compressed = gpu ? kGl[fmt] : 0;
|
||||
@@ -660,6 +661,23 @@ static std::vector<uint8_t> nextBlockLevel(int fmt, const std::vector<uint8_t>&
|
||||
return dst;
|
||||
}
|
||||
|
||||
+// The next level of an RGBA one (lw x lh), by the same 2x2 box filter.
|
||||
+static std::vector<uint8_t> nextRgbaLevel(const std::vector<uint8_t>& src, int lw, int lh) {
|
||||
+ int nw = std::max(1, lw / 2), nh = std::max(1, lh / 2);
|
||||
+ std::vector<uint8_t> dst(size_t(nw) * nh * 4);
|
||||
+ for (int y = 0; y < nh; y++)
|
||||
+ for (int x = 0; x < nw; x++) {
|
||||
+ int x0 = std::min(2 * x, lw - 1), x1 = std::min(2 * x + 1, lw - 1);
|
||||
+ int y0 = std::min(2 * y, lh - 1), y1 = std::min(2 * y + 1, lh - 1);
|
||||
+ for (int c = 0; c < 4; c++) {
|
||||
+ int sum = src[(size_t(y0) * lw + x0) * 4 + c] + src[(size_t(y0) * lw + x1) * 4 + c] +
|
||||
+ src[(size_t(y1) * lw + x0) * 4 + c] + src[(size_t(y1) * lw + x1) * 4 + c];
|
||||
+ dst[(size_t(y) * nw + x) * 4 + c] = uint8_t((sum + 2) / 4);
|
||||
+ }
|
||||
+ }
|
||||
+ return dst;
|
||||
+}
|
||||
+
|
||||
// Complete every replacement's mip chain down to 1x1 with 2x2 box filtering,
|
||||
// whatever levels the pack supplied: the sampler minifies a replacement as far
|
||||
// as its original (samplerFor), and without the lower levels a far wall
|
||||
@@ -683,22 +701,9 @@ static void completeChain(Loaded* L, bool idle = false) {
|
||||
return;
|
||||
}
|
||||
while (lw > 1 || lh > 1) {
|
||||
- int nw = std::max(1, lw / 2), nh = std::max(1, lh / 2);
|
||||
- const std::vector<uint8_t>& src = L->levels.back();
|
||||
- std::vector<uint8_t> dst(size_t(nw) * nh * 4);
|
||||
- for (int y = 0; y < nh; y++)
|
||||
- for (int x = 0; x < nw; x++) {
|
||||
- int x0 = std::min(2 * x, lw - 1), x1 = std::min(2 * x + 1, lw - 1);
|
||||
- int y0 = std::min(2 * y, lh - 1), y1 = std::min(2 * y + 1, lh - 1);
|
||||
- for (int c = 0; c < 4; c++) {
|
||||
- int sum = src[(size_t(y0) * lw + x0) * 4 + c] + src[(size_t(y0) * lw + x1) * 4 + c] +
|
||||
- src[(size_t(y1) * lw + x0) * 4 + c] + src[(size_t(y1) * lw + x1) * 4 + c];
|
||||
- dst[(size_t(y) * nw + x) * 4 + c] = uint8_t((sum + 2) / 4);
|
||||
- }
|
||||
- }
|
||||
- L->levels.push_back(std::move(dst));
|
||||
- lw = nw;
|
||||
- lh = nh;
|
||||
+ L->levels.push_back(nextRgbaLevel(L->levels.back(), lw, lh));
|
||||
+ lw = std::max(1, lw / 2);
|
||||
+ lh = std::max(1, lh / 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -783,18 +788,20 @@ static void loadCachedLevels(const std::string& name, const PackFile& f, Loaded*
|
||||
for (auto& lv : levels) L->levels.push_back(std::move(lv));
|
||||
}
|
||||
|
||||
-// Keeps the levels a completion made (L->levels from L->first on).
|
||||
-static void storeCachedLevels(const std::string& name, const PackFile& f, const Loaded& L) {
|
||||
+// Keeps the levels a completion made (L->levels from L->first on); with
|
||||
+// ".top", a shrunk replacement's top level (shrinkToLimit).
|
||||
+static const char kTopMagic[8] = {'S', 'M', 'S', 'T', 'O', 'P', '0', '1'};
|
||||
+static void storeCachedLevels(const std::string& name, const PackFile& f, const Loaded& L, const char* ext = ".mips") {
|
||||
const std::string& dir = mipCacheDir();
|
||||
MipsHeader hd{};
|
||||
if (dir.empty() || L.levels.empty() || !sourceStamp(f.path, &hd.size, &hd.mtime)) return;
|
||||
- memcpy(hd.magic, kMipsMagic, 8);
|
||||
+ memcpy(hd.magic, strcmp(ext, ".top") ? kMipsMagic : kTopMagic, 8);
|
||||
hd.w = uint32_t(L.w);
|
||||
hd.h = uint32_t(L.h);
|
||||
hd.blocks = uint32_t(L.blocks);
|
||||
hd.first = uint32_t(L.first);
|
||||
hd.count = uint32_t(L.levels.size());
|
||||
- const std::string path = dir + "/" + name + ".mips", part = path + ".part";
|
||||
+ const std::string path = dir + "/" + name + ext, part = path + ".part";
|
||||
FILE* out = fopen(part.c_str(), "wb");
|
||||
if (!out) return;
|
||||
bool ok = fwrite(&hd, sizeof hd, 1, out) == 1;
|
||||
@@ -822,6 +829,85 @@ static void storeCachedLevels(const std::string& name, const PackFile& f, const
|
||||
if (!ok || ec) std::filesystem::remove(part, ec);
|
||||
}
|
||||
|
||||
+// ------------------------------------------------------------------ replacements too large to hold
|
||||
+// SMS_TEXTURE_PACK_MAX_SIZE=n halves a replacement longer than n pixels until
|
||||
+// it is not, for a GPU that cannot hold a pack's largest images (the PS5's
|
||||
+// driver: the UHD pack's 4096 to 16384 pixel files ran it out of memory, and
|
||||
+// took 17 seconds to send in one load). The file's own lower levels serve
|
||||
+// where it has them; otherwise the top level is made from the largest, by
|
||||
+// nextBlockLevel, and kept on disk as <name>.top beside the completed levels,
|
||||
+// so each large file is read once. Unset or 0 keeps every replacement whole.
|
||||
+static int maxSize() {
|
||||
+ static const int n = getenv("SMS_TEXTURE_PACK_MAX_SIZE") ? std::max(0, atoi(getenv("SMS_TEXTURE_PACK_MAX_SIZE"))) : 0;
|
||||
+ return n;
|
||||
+}
|
||||
+
|
||||
+static int halvings(int w, int h) {
|
||||
+ int k = 0;
|
||||
+ while (maxSize() > 0 && std::max(w, h) > maxSize()) {
|
||||
+ w = std::max(1, w / 2);
|
||||
+ h = std::max(1, h / 2);
|
||||
+ k++;
|
||||
+ }
|
||||
+ return k;
|
||||
+}
|
||||
+
|
||||
+static bool ddsSize(const std::string& path, int* w, int* h) {
|
||||
+ uint8_t d[20];
|
||||
+ FILE* f = fopen(path.c_str(), "rb");
|
||||
+ if (!f) return false;
|
||||
+ bool ok = fread(d, sizeof d, 1, f) == 1 && !memcmp(d, "DDS ", 4);
|
||||
+ fclose(f);
|
||||
+ if (!ok) return false;
|
||||
+ *h = int(rd32(d + 12));
|
||||
+ *w = int(rd32(d + 16));
|
||||
+ return *w > 0 && *h > 0;
|
||||
+}
|
||||
+
|
||||
+// The top level an earlier shrinkToLimit kept, for a file of w x h halved k times.
|
||||
+static bool loadShrunk(const std::string& name, const PackFile& f, int w, int h, int k, Loaded* L) {
|
||||
+ const std::string& dir = mipCacheDir();
|
||||
+ uint64_t size;
|
||||
+ int64_t mtime;
|
||||
+ if (dir.empty() || !sourceStamp(f.path, &size, &mtime)) return false;
|
||||
+ FILE* in = fopen((dir + "/" + name + ".top").c_str(), "rb");
|
||||
+ if (!in) return false;
|
||||
+ MipsHeader hd;
|
||||
+ std::vector<uint8_t> top;
|
||||
+ const int sw = std::max(1, w >> k), sh = std::max(1, h >> k);
|
||||
+ if (fread(&hd, sizeof hd, 1, in) == 1 && !memcmp(hd.magic, kTopMagic, 8) && hd.size == size && hd.mtime == mtime &&
|
||||
+ hd.w == uint32_t(sw) && hd.h == uint32_t(sh) && hd.first == 0 && hd.count == 1 && gpuTakes(int(hd.blocks))) {
|
||||
+ top.resize(blockLevelBytes(int(hd.blocks), sw, sh));
|
||||
+ if (fread(top.data(), top.size(), 1, in) != 1) top.clear();
|
||||
+ }
|
||||
+ fclose(in);
|
||||
+ if (top.empty()) return false;
|
||||
+ L->w = sw;
|
||||
+ L->h = sh;
|
||||
+ L->blocks = int(hd.blocks);
|
||||
+ L->compressed = kGl[L->blocks];
|
||||
+ L->levels.push_back(std::move(top));
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
+// Halves L k times, from the deepest of the levels it has that is still too
|
||||
+// large. True if that made a level (rather than only dropping the file's own).
|
||||
+static bool shrinkToLimit(Loaded* L, int k) {
|
||||
+ const int drop = std::min(k, int(L->levels.size()) - 1);
|
||||
+ L->levels.erase(L->levels.begin(), L->levels.begin() + drop);
|
||||
+ L->w = std::max(1, L->w >> drop);
|
||||
+ L->h = std::max(1, L->h >> drop);
|
||||
+ for (int i = drop; i < k; i++) {
|
||||
+ std::vector<uint8_t> next = L->compressed ? nextBlockLevel(L->blocks, L->levels[0], L->w, L->h, false)
|
||||
+ : nextRgbaLevel(L->levels[0], L->w, L->h);
|
||||
+ L->levels.clear();
|
||||
+ L->levels.push_back(std::move(next));
|
||||
+ L->w = std::max(1, L->w / 2);
|
||||
+ L->h = std::max(1, L->h / 2);
|
||||
+ }
|
||||
+ return drop < k;
|
||||
+}
|
||||
+
|
||||
// `whole`: also the compressed levels completeChain makes, else only the
|
||||
// pack's and the cached ones (RGBA images are always completed: they are
|
||||
// decoded anyway).
|
||||
@@ -830,6 +916,13 @@ static Loaded* decode(const std::string& name, bool whole, bool cached = true) {
|
||||
Loaded* L = new Loaded;
|
||||
L->arbitrary = f.arbitrary;
|
||||
if (endsWith(f.path, ".dds") || endsWith(f.path, ".DDS")) {
|
||||
+ int fw = 0, fh = 0;
|
||||
+ const int k = maxSize() > 0 && ddsSize(f.path, &fw, &fh) ? halvings(fw, fh) : 0;
|
||||
+ if (k && loadShrunk(name, f, fw, fh, k, L)) {
|
||||
+ if (cached) loadCachedLevels(name, f, L);
|
||||
+ if (whole) completeChain(L);
|
||||
+ return L;
|
||||
+ }
|
||||
if (!decodeDds(f.path, L)) {
|
||||
logmsg("texture pack: cannot read %s (not a DDS of a supported format)", f.path.c_str());
|
||||
L->failed = true;
|
||||
@@ -847,6 +940,10 @@ static Loaded* decode(const std::string& name, bool whole, bool cached = true) {
|
||||
L->levels.push_back(std::move(mip.levels.front()));
|
||||
}
|
||||
}
|
||||
+ if (k && !L->failed && shrinkToLimit(L, k)) {
|
||||
+ logmsg("texture pack: %s is %dx%d, used at %dx%d", f.path.c_str(), fw, fh, L->w, L->h);
|
||||
+ if (L->compressed) storeCachedLevels(name, f, *L, ".top");
|
||||
+ }
|
||||
if (cached && !L->failed && L->compressed) loadCachedLevels(name, f, L);
|
||||
if (whole || !L->compressed) completeChain(L);
|
||||
return L;
|
||||
@@ -1049,6 +1146,9 @@ static bool beginUpload(Upload& u) {
|
||||
// Sends u's levels, at least one piece and then while `sent` is under
|
||||
// `allowance`; true once they are all in. A completion's levels already have
|
||||
// their storage (finishUpload), so they only fill it.
|
||||
@@ -719,7 +924,7 @@ index bcd4174..3887f46 100644
|
||||
static bool sendLevels(Upload& u, int unit, size_t allowance, size_t* sent) {
|
||||
Loaded* L = u.item.image.get();
|
||||
const bool fill = u.item.request.kind == REST;
|
||||
@@ -1062,7 +1065,7 @@ static bool sendLevels(Upload& u, int unit, size_t allowance, size_t* sent) {
|
||||
@@ -1062,7 +1162,7 @@ static bool sendLevels(Upload& u, int unit, size_t allowance, size_t* sent) {
|
||||
const int n = L->first + int(u.level);
|
||||
const int lw = std::max(1, L->w >> n), lh = std::max(1, L->h >> n);
|
||||
size_t bytes = lv.size();
|
||||
@@ -728,7 +933,7 @@ index bcd4174..3887f46 100644
|
||||
if (L->compressed)
|
||||
glCompressedTexImage2D(GL_TEXTURE_2D, n, L->compressed, lw, lh, 0, GLsizei(bytes), lv.data());
|
||||
else
|
||||
@@ -1212,13 +1215,23 @@ static bool takeDecoded(Upload& u, bool pack) { // with s_mu held
|
||||
@@ -1212,13 +1312,23 @@ static bool takeDecoded(Upload& u, bool pack) { // with s_mu held
|
||||
// Sends decoded textures into GL: all of the ones resources asked for when
|
||||
// `wait` (a load: until none is left to decode), else up to a frame's
|
||||
// allowance, or 2 ms. The whole pack's wait for loads (uploadPack).
|
||||
@@ -752,7 +957,7 @@ index bcd4174..3887f46 100644
|
||||
if (s_uploads.empty()) {
|
||||
Upload u;
|
||||
{
|
||||
@@ -1226,10 +1239,14 @@ static void uploadPending(bool wait) {
|
||||
@@ -1226,10 +1336,14 @@ static void uploadPending(bool wait) {
|
||||
if (wait) lk.lock();
|
||||
else if (!lk.try_lock()) break;
|
||||
auto loaded = [] { return s_decodedDemand == 0 && s_queue.empty() && !s_busy; };
|
||||
@@ -767,7 +972,7 @@ index bcd4174..3887f46 100644
|
||||
continue;
|
||||
}
|
||||
Upload& u = s_uploads.front();
|
||||
@@ -1237,11 +1254,14 @@ static void uploadPending(bool wait) {
|
||||
@@ -1237,11 +1351,14 @@ static void uploadPending(bool wait) {
|
||||
s_uploads.pop_front();
|
||||
continue;
|
||||
}
|
||||
@@ -783,7 +988,7 @@ index bcd4174..3887f46 100644
|
||||
if (!wait && (sent >= allowance || Clock::now() - start >= std::chrono::milliseconds(2))) break;
|
||||
}
|
||||
}
|
||||
@@ -1325,20 +1345,32 @@ static void touchFresh() {
|
||||
@@ -1325,20 +1442,32 @@ static void touchFresh() {
|
||||
glcInvalidate();
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"conceptId": "99312",
|
||||
"contentBadgeType": 1,
|
||||
"contentId": "UP9000-PPSA99312_00-SUNSHINE00000000",
|
||||
"contentVersion": "00.009.000",
|
||||
"contentVersion": "00.010.000",
|
||||
"downloadDataSize": 256,
|
||||
"gameIntent": {
|
||||
"permittedIntents": [
|
||||
|
||||
@@ -149,6 +149,10 @@ ps5_startup(void)
|
||||
PC's budget of 1536 cannot fit: the title died at about 1,070. From 512
|
||||
the oldest are let go, which keeps about 3 GiB spare through a load. */
|
||||
set("SMS_TEXTURE_PACK_MB", "512", 0);
|
||||
/* ... and the largest images it can take: the UHD pack's 4096 to 16384
|
||||
pixel files are halved to 2048 (or less) the first time each is used, and
|
||||
kept so in userdata/.cache, so the pack goes in as its authors publish it */
|
||||
set("SMS_TEXTURE_PACK_MAX_SIZE", "3072", 0);
|
||||
if (stat("/app0/mods/hd-cutscenes", &status) == 0)
|
||||
set("SMS_HD_CUTSCENES", "/app0/mods/hd-cutscenes", 0);
|
||||
find_disc();
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Fetches the texture tools' Python packages for this Python, without pip.
|
||||
|
||||
fetch-wheels.py DIR
|
||||
|
||||
Downloads Pillow and etcpak (and archspec, which etcpak imports) as wheels from PyPI, the ones built for this
|
||||
Python and this machine, checks each against PyPI's SHA-256, and unpacks them
|
||||
into DIR/<python version>, which it prints: put that on PYTHONPATH. A fresh
|
||||
Ubuntu (or WSL) has neither pip nor venv, and a Mac's Command Line Tools have
|
||||
Python 3.9, which newer Pillows no longer support. Run again, it does nothing.
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
"""
|
||||
|
||||
import hashlib, json, os, platform, re, sys, urllib.request, zipfile
|
||||
|
||||
# (name, version): Pillow 12 needs Python 3.10, so 3.9 gets the last that supports it
|
||||
PACKAGES = [("archspec", "0.2.6"), ("etcpak", "0.9.15"), ("pillow", "12.3.0" if sys.version_info >= (3, 10) else "11.3.0")]
|
||||
|
||||
|
||||
def compatible(filename):
|
||||
"""A wheel this Python can import here: its CPython tag (or abi3 from an
|
||||
older one) and its platform; or pure Python."""
|
||||
if filename.endswith("-py3-none-any.whl"):
|
||||
return True
|
||||
m = re.match(r"[^-]+-[^-]+-(cp(\d)(\d+))-(cp\d+m?|abi3)-(.+)\.whl$", filename)
|
||||
if not m:
|
||||
return False
|
||||
major, minor, abi, plats = int(m.group(2)), int(m.group(3)), m.group(4), m.group(5).split(".")
|
||||
here = sys.version_info
|
||||
if abi == "abi3":
|
||||
if (major, minor) > (here.major, here.minor):
|
||||
return False
|
||||
elif (major, minor) != (here.major, here.minor):
|
||||
return False
|
||||
machine = platform.machine().lower()
|
||||
for plat in plats:
|
||||
if sys.platform == "darwin":
|
||||
want = "arm64" if machine == "arm64" else "x86_64"
|
||||
if plat.startswith("macosx_") and (plat.endswith("_" + want) or plat.endswith("_universal2")):
|
||||
return True
|
||||
elif sys.platform.startswith("linux"):
|
||||
want = {"arm64": "aarch64"}.get(machine, machine)
|
||||
g = re.match(r"manylinux_(\d+)_(\d+)_(.+)$", plat) or re.match(r"manylinux(2014)_(.+)$", plat)
|
||||
if not g:
|
||||
continue
|
||||
if g.re.pattern.startswith("manylinux(2014)"):
|
||||
need, arch = (2, 17), g.group(2)
|
||||
else:
|
||||
need, arch = (int(g.group(1)), int(g.group(2))), g.group(3)
|
||||
libc = os.confstr("CS_GNU_LIBC_VERSION").split()[-1].split(".") if hasattr(os, "confstr") else ["0", "0"]
|
||||
if arch == want and (int(libc[0]), int(libc[1])) >= need:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
root = os.path.join(sys.argv[1], "python%d.%d" % sys.version_info[:2])
|
||||
done = os.path.join(root, ".done-" + "-".join(n + v for n, v in PACKAGES))
|
||||
if not os.path.exists(done):
|
||||
os.makedirs(root, exist_ok=True)
|
||||
for name, version in PACKAGES:
|
||||
with urllib.request.urlopen(f"https://pypi.org/pypi/{name}/{version}/json") as r:
|
||||
files = json.load(r)["urls"]
|
||||
wheel = next((f for f in files if compatible(f["filename"])), None)
|
||||
if not wheel:
|
||||
sys.exit(f"fetch-wheels.py: no {name} {version} for Python {platform.python_version()} on "
|
||||
f"{sys.platform} {platform.machine()}")
|
||||
print(f"==> fetching {wheel['filename']}", file=sys.stderr)
|
||||
with urllib.request.urlopen(wheel["url"]) as r:
|
||||
data = r.read()
|
||||
if hashlib.sha256(data).hexdigest() != wheel["digests"]["sha256"]:
|
||||
sys.exit(f"fetch-wheels.py: {wheel['filename']} did not download correctly; try again")
|
||||
path = os.path.join(root, wheel["filename"])
|
||||
with open(path, "wb") as f:
|
||||
f.write(data)
|
||||
with zipfile.ZipFile(path) as z:
|
||||
for member in z.infolist():
|
||||
z.extract(member, root)
|
||||
# a zip keeps the executable bit in its Unix attributes; extract() drops it
|
||||
mode = member.external_attr >> 16
|
||||
if mode:
|
||||
os.chmod(os.path.join(root, member.filename), mode)
|
||||
os.remove(path)
|
||||
open(done, "w").close()
|
||||
print(root)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,108 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Super Mario Sunshine for the PlayStation 5 - an HD texture pack sized for the console.
|
||||
|
||||
get-texture-pack.py [OUT] OUT/textures/ (OUT defaults to ./out)
|
||||
|
||||
Downloads the Super Mario Sunshine UHD Texture Pack (qashto, razius) and the HD
|
||||
texture extras (chasem-dev) from where their authors publish them, checks each
|
||||
against the release this was tested with, and makes a copy with nothing larger
|
||||
than 2048 pixels (shrink-texture-pack.py: the console's driver cannot hold the
|
||||
4096 and 8192 pixel images). Copy OUT/textures to the console as
|
||||
/data/homebrew/PPSA99312/mods/textures.
|
||||
|
||||
Neither pack is part of this project or stored by it; each belongs to its authors.
|
||||
Needs Python with Pillow and etcpak, and 7-Zip (7z, 7zz or 7za) or bsdtar to
|
||||
unpack. About 1 GB to download, 5 GB of disk while it works, 1.3 GB at the end.
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
"""
|
||||
import hashlib, os, shutil, subprocess, sys, urllib.request, zipfile
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
PACKS = [
|
||||
{"name": "Super Mario Sunshine UHD Texture Pack v2.1.1 (qashto, razius)",
|
||||
"page": "https://github.com/qashto/Super_Mario_Sunshine_UHD_Texture_Pack",
|
||||
"url": "https://github.com/qashto/Super_Mario_Sunshine_UHD_Texture_Pack/releases/download/2.1.1/GMS.7z",
|
||||
"file": "GMS.7z", "md5": "274985dba9214b857b3e5605d2ee49b2", "inside": "GMS/Textures/GMS", "as": "GMS"},
|
||||
{"name": "Super Mario Sunshine HD texture extras v1.1.1 (chasem-dev)",
|
||||
"page": "https://github.com/chasem-dev/sms-hd-texture-extras",
|
||||
"url": "https://github.com/chasem-dev/sms-hd-texture-extras/releases/download/v1.1.1/sms-hd-texture-extras-1.1.1.zip",
|
||||
"file": "sms-hd-texture-extras-1.1.1.zip", "md5": "a658c43cce0751e585e991f8563e5f41", "inside": "", "as": "sms-hd-texture-extras"},
|
||||
]
|
||||
|
||||
|
||||
def say(text):
|
||||
print("==> " + text, flush=True)
|
||||
|
||||
|
||||
def md5_of(path):
|
||||
digest = hashlib.md5()
|
||||
with open(path, "rb") as source:
|
||||
for chunk in iter(lambda: source.read(1 << 20), b""):
|
||||
digest.update(chunk)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
def fetch(pack, folder):
|
||||
path = os.path.join(folder, pack["file"])
|
||||
if os.path.isfile(path) and md5_of(path) == pack["md5"]:
|
||||
return path
|
||||
say("downloading %s" % pack["name"])
|
||||
request = urllib.request.Request(pack["url"], headers={"User-Agent": "PS5_Sunshine texture pack installer"})
|
||||
with urllib.request.urlopen(request) as reply, open(path + ".part", "wb") as out:
|
||||
shutil.copyfileobj(reply, out, 1 << 20)
|
||||
os.replace(path + ".part", path)
|
||||
got = md5_of(path)
|
||||
if got != pack["md5"]:
|
||||
os.remove(path)
|
||||
sys.exit("get-texture-pack.py: %s is not the release this was tested with (MD5 %s, expected %s); see %s"
|
||||
% (pack["file"], got, pack["md5"], pack["page"]))
|
||||
return path
|
||||
|
||||
|
||||
def unpack(archive, folder):
|
||||
shutil.rmtree(folder, ignore_errors=True)
|
||||
os.makedirs(folder)
|
||||
if archive.endswith(".zip"):
|
||||
with zipfile.ZipFile(archive) as source:
|
||||
source.extractall(folder)
|
||||
return
|
||||
for tool in ("7z", "7zz", "7za"):
|
||||
if shutil.which(tool):
|
||||
subprocess.run([tool, "x", "-y", "-o" + folder, archive], check=True, stdout=subprocess.DEVNULL)
|
||||
return
|
||||
for tool in ("bsdtar", "tar"): # libarchive reads 7z; GNU tar does not, and says so
|
||||
if shutil.which(tool) and subprocess.run([tool, "-xf", archive, "-C", folder], stderr=subprocess.DEVNULL).returncode == 0:
|
||||
return
|
||||
sys.exit("get-texture-pack.py: 7-Zip (7z, 7zz or 7za) or bsdtar is needed to unpack " + os.path.basename(archive))
|
||||
|
||||
|
||||
def main():
|
||||
out = os.path.abspath(sys.argv[1] if len(sys.argv) > 1 else "out")
|
||||
downloads, original, result = (os.path.join(out, name) for name in (".texture-downloads", ".texture-original", "textures"))
|
||||
os.makedirs(downloads, exist_ok=True)
|
||||
shutil.rmtree(original, ignore_errors=True)
|
||||
for pack in PACKS:
|
||||
archive = fetch(pack, downloads)
|
||||
say("unpacking %s" % pack["file"])
|
||||
scratch = os.path.join(out, ".texture-unpack")
|
||||
unpack(archive, scratch)
|
||||
os.makedirs(original, exist_ok=True)
|
||||
shutil.move(os.path.join(scratch, pack["inside"]) if pack["inside"] else scratch, os.path.join(original, pack["as"]))
|
||||
shutil.rmtree(scratch, ignore_errors=True)
|
||||
# only the textures: the UHD pack also carries an archive of its sources
|
||||
for root, _, files in os.walk(original):
|
||||
for name in files:
|
||||
if not name.lower().endswith((".dds", ".png")):
|
||||
os.remove(os.path.join(root, name))
|
||||
say("making the console's copy (nothing larger than 2048 pixels)")
|
||||
shutil.rmtree(result, ignore_errors=True)
|
||||
subprocess.run([sys.executable, os.path.join(HERE, "shrink-texture-pack.py"), original, result, "2048"], check=True)
|
||||
shutil.rmtree(original, ignore_errors=True)
|
||||
say(result)
|
||||
print(" copy it to the console as /data/homebrew/PPSA99312/mods/textures (FTP)")
|
||||
print(" the packs belong to their authors: this copy is for your own use")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -12,7 +12,7 @@
|
||||
# console's own GPU interface, EGL for the screen), a static archive group and
|
||||
# its two import stubs. The title folder, dist/<TITLE_ID>/, gets the signed
|
||||
# eboot.bin, sce_sys/ and the licences. OBJECT may be an archive.
|
||||
# PS5_DIST_DIR is where the title folder goes instead of dist/ (the builder kit).
|
||||
# PS5_DIST_DIR is where the title folder goes instead of dist/.
|
||||
# PS5_OPENGL_PREFIX is the OpenGL SDK's sdk/ directory.
|
||||
#
|
||||
# Copyright (C) 2026 Mihawk
|
||||
@@ -180,7 +180,7 @@ cp "$param" "$app/sce_sys/param.json"
|
||||
for asset in icon0.png pic0.dds pic1.dds snd0.at9; do
|
||||
[[ -f $ps5/sce_sys/$asset ]] && cp "$ps5/sce_sys/$asset" "$app/sce_sys/$asset"
|
||||
done
|
||||
# (Python, not sha256sum: the builder kit runs this on a Mac too)
|
||||
# (Python, not sha256sum, which a Mac does not have)
|
||||
python3 - "$vulkan/runtime/libc.prx" "$vulkan/runtime/libc.prx.sha256" <<'CHECK'
|
||||
import hashlib, sys
|
||||
want = open(sys.argv[2]).read().split()[0]
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Super Mario Sunshine for the PlayStation 5 - the builder kit: everything of a build but the game.
|
||||
#
|
||||
# ps5/tools/package-kit.sh [VERSION] dist/kit/sunshine-ps5-kit-<VERSION>/ and its .zip
|
||||
#
|
||||
# A built title is not shared: eboot.bin holds the game's code, compiled from
|
||||
# the decompilation. The kit is the rest of one, as it stands after
|
||||
# tools/build.sh: the port's own objects (the platform layer, the graphics
|
||||
# layer, SDL2), the OpenGL driver, the SDK's headers, libraries and link
|
||||
# scripts, and kit/build-title.sh, which on somebody else's machine fetches the
|
||||
# PC port and the decompilation, compiles the game's code and links the same
|
||||
# title with the same recipe (tools/link-title.sh). What goes in is listed
|
||||
# here, file by file; the game's code (libsms_game.a) and anything from a disc
|
||||
# never do, and the script checks that before it finishes.
|
||||
#
|
||||
# The link line is the build's own (ninja's), so the kit links what was tested.
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
set -euo pipefail
|
||||
|
||||
ps5=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
|
||||
root=$(dirname "$ps5")
|
||||
vulkan=$(cd -- "${PS5_VULKAN_DIR:-$root/../PS5_Vulkan}" && pwd)
|
||||
opengl=${PS5_OPENGL_PREFIX:?set PS5_OPENGL_PREFIX to the sdk/ directory of the PS5 OpenGL SDK}
|
||||
sdk=$(bash "$ps5/tools/setup-sdk.sh")
|
||||
build=${PS5_BUILD_DIR:-$root/build/game}
|
||||
ninja=$(command -v ninja || echo "$HOME/.local/bin/ninja")
|
||||
export PS5_CLANG=${PS5_CLANG:-$(command -v clang || true)}
|
||||
version=${1:-$(python3 -c 'import json,sys; v=json.load(open(sys.argv[1]))["contentVersion"].split("."); print("%d.%d.%d" % tuple(int(x) for x in v))' "$ps5/sce_sys/param.json")}
|
||||
name=sunshine-ps5-kit-$version
|
||||
kit="$root/dist/kit/$name"
|
||||
source="$root/vendor/sms-pc-port"
|
||||
|
||||
# the kit is the build as the sources stand now: bring it up to date first
|
||||
bash "$ps5/tools/build.sh"
|
||||
|
||||
rm -rf -- "$kit"
|
||||
mkdir -p "$kit/lib" "$kit/ps5/tools"
|
||||
|
||||
# ---- the title's link inputs, in the build's order, without the game's code
|
||||
# (ninja's command: ... link-title.sh BUILD VULKAN SDK INPUT... && :)
|
||||
mapfile -t inputs < <("$ninja" -C "$build" -t commands sms | tail -1 |
|
||||
sed -e 's/.*link-title\.sh //' -e 's/ && :$//' | tr ' ' '\n' | sed -n '4,$p' | sed '/^$/d')
|
||||
strip="$sdk/bin/llvm-strip"
|
||||
: > "$kit/lib/link.txt"
|
||||
game_code=0
|
||||
for input in "${inputs[@]}"; do
|
||||
case $input in
|
||||
-*) echo "$input" >> "$kit/lib/link.txt"; continue ;;
|
||||
libsms_game.a | */libsms_game.a) echo "@GAMECODE@" >> "$kit/lib/link.txt"; game_code=$((game_code + 1)); continue ;;
|
||||
esac
|
||||
file=$input
|
||||
[[ $file == /* ]] || file="$build/$input"
|
||||
[[ -f $file ]] || { echo "package-kit.sh: link input $input is missing" >&2; exit 2; }
|
||||
relative=${file#"$build"/}
|
||||
[[ $relative != /* ]] || { echo "package-kit.sh: link input $input is outside the build tree" >&2; exit 2; }
|
||||
if [[ ! -f $kit/lib/$relative ]]; then
|
||||
mkdir -p "$(dirname "$kit/lib/$relative")"
|
||||
# no debug information: it names this machine's folders, and is most of the size
|
||||
"$strip" --strip-debug -o "$kit/lib/$relative" "$file"
|
||||
fi
|
||||
echo "$relative" >> "$kit/lib/link.txt"
|
||||
done
|
||||
[[ $game_code -eq 1 ]] || { echo "package-kit.sh: expected the game's code once in the link line, found it $game_code times" >&2; exit 2; }
|
||||
|
||||
# ---- the port: the link, the title's own files, and what the game's code is built with
|
||||
cp "$ps5/tools/link-title.sh" "$ps5/tools/cc-init-array.sh" "$kit/ps5/tools/"
|
||||
cp -R "$ps5/sce_sys" "$kit/ps5/sce_sys"
|
||||
cp -R "$ps5/res" "$kit/ps5/res"
|
||||
mkdir -p "$kit/ps5/stubs" "$kit/ps5/cmake" "$kit/ps5/patches" "$kit/ps5/opengl"
|
||||
cp "$ps5/stubs/libSceMouse_stub.c" "$kit/ps5/stubs/"
|
||||
cp "$ps5/cmake/ps5-kit-game.cmake" "$kit/ps5/cmake/"
|
||||
cp "$ps5/patches/sms-pc-port.patch" "$ps5/patches/sdl.patch" "$kit/ps5/patches/"
|
||||
cp "$ps5/opengl/build-runtime.sh" "$kit/ps5/opengl/"
|
||||
cp "$ps5/kit/build-title.sh" "$ps5/kit/make-title.sh" "$ps5/kit/tools.sh" "$ps5/kit/Dockerfile" "$kit/"
|
||||
cp "$ps5/kit/README.md" "$kit/README.md"
|
||||
chmod +x "$kit/build-title.sh" "$kit/make-title.sh"
|
||||
mkdir -p "$kit/tools"
|
||||
cp "$ps5/tools/shrink-texture-pack.py" "$ps5/tools/get-texture-pack.py" "$ps5/tools/fetch-wheels.py" "$kit/tools/"
|
||||
cp "$ps5/kit/make-texture-pack.sh" "$kit/"
|
||||
chmod +x "$kit/make-texture-pack.sh"
|
||||
# the revisions build-title.sh fetches: the ones this build was made from
|
||||
port_rev=$(git -C "$source" rev-parse HEAD)
|
||||
decomp_rev=$(git -C "$source/decomp" rev-parse HEAD)
|
||||
{
|
||||
echo "port_url=$(git -C "$source" remote get-url origin)"
|
||||
echo "port_rev=$port_rev"
|
||||
echo "decomp_rev=$decomp_rev"
|
||||
} > "$kit/REVISIONS"
|
||||
# ... and the patch must be the tree's whole difference from that revision
|
||||
bash "$ps5/tools/setup-source.sh" > /dev/null
|
||||
|
||||
# ---- the payload SDK: compiler wrappers, link scripts, the CMake toolchain, headers, libraries
|
||||
mkdir -p "$kit/sdk/target"
|
||||
cp -R "$sdk/bin" "$sdk/ldscripts" "$sdk/toolchain" "$kit/sdk/"
|
||||
cp -R "$sdk/target/include" "$sdk/target/lib" "$kit/sdk/target/"
|
||||
cp "$sdk/.ps5-sdk-revision" "$kit/sdk/"
|
||||
|
||||
# ---- PS5_Vulkan: the link recipe, the CRT, the fake-SELF tool's source, libc.prx
|
||||
mkdir -p "$kit/vulkan/tools" "$kit/vulkan/tooling/psbc" "$kit/vulkan/tooling/native" "$kit/vulkan/runtime"
|
||||
cp "$vulkan/LICENSE" "$vulkan/NOTICE.md" "$kit/vulkan/"
|
||||
cp "$vulkan/tools/radv-link.sh" "$kit/vulkan/tools/"
|
||||
cp "$vulkan/tooling/prospero-clang18" "$kit/vulkan/tooling/"
|
||||
cp "$vulkan/tooling/psbc/ps5-pie-unwind.ld" "$kit/vulkan/tooling/psbc/"
|
||||
cp "$vulkan"/tooling/native/*.cpp "$vulkan"/tooling/native/*.hpp "$vulkan"/tooling/native/*.map \
|
||||
"$vulkan"/tooling/native/*.ld "$kit/vulkan/tooling/native/"
|
||||
cp "$vulkan/runtime/libc.prx" "$vulkan/runtime/libc.prx.sha256" "$vulkan/runtime/README.md" "$kit/vulkan/runtime/"
|
||||
|
||||
# ---- the fake-SELF tool built for Linux, static, so a Linux (or WSL) needs no C++
|
||||
# compiler: x86_64 and arm64, with zlib compiled in. A Mac builds its own from the
|
||||
# source above (its Command Line Tools have a compiler and zlib).
|
||||
zlib_version=1.3.2
|
||||
zlib="$root/.deps/zlib-$zlib_version"
|
||||
if [[ ! -f $zlib/zlib.h ]]; then
|
||||
mkdir -p "$root/.deps"
|
||||
curl -fsSL -o "$zlib.tar.gz" "https://github.com/madler/zlib/releases/download/v$zlib_version/zlib-$zlib_version.tar.gz"
|
||||
echo "bb329a0a2cd0274d05519d61c667c062e06990d72e125ee2dfa8de64f0119d16 $zlib.tar.gz" | sha256sum --check --quiet
|
||||
tar -xzf "$zlib.tar.gz" -C "$root/.deps"
|
||||
rm -f "$zlib.tar.gz"
|
||||
fi
|
||||
for target in x86_64:x86_64-linux-gnu- aarch64:aarch64-linux-gnu-; do
|
||||
arch=${target%%:*} prefix=${target#*:}
|
||||
# the host's own compiler for its own processor
|
||||
[[ $(uname -m) == "$arch" ]] && prefix=
|
||||
command -v "${prefix}g++" > /dev/null ||
|
||||
{ echo "package-kit.sh: ${prefix}g++ is missing (apt install g++-${arch//_/-}-linux-gnu)" >&2; exit 2; }
|
||||
objects="$build/native-tool-$arch"
|
||||
rm -rf "$objects"
|
||||
mkdir -p "$objects" "$kit/vulkan/prebuilt/Linux-$arch"
|
||||
for file in adler32 crc32 deflate inflate inffast inftrees trees zutil; do
|
||||
"${prefix}gcc" -O2 -c "$zlib/$file.c" -o "$objects/$file.o"
|
||||
done
|
||||
"${prefix}g++" -std=c++20 -O2 -static -I"$zlib" "$vulkan"/tooling/native/{native_app_builder,self_container,elf_object,sce_module_writer}.cpp \
|
||||
"$objects"/*.o -o "$kit/vulkan/prebuilt/Linux-$arch/ps5-native-tool"
|
||||
"${prefix}strip" "$kit/vulkan/prebuilt/Linux-$arch/ps5-native-tool"
|
||||
rm -rf "$objects"
|
||||
done
|
||||
cp "$zlib/LICENSE" "$kit/vulkan/prebuilt/zlib-LICENSE.txt"
|
||||
|
||||
# ---- the PS5 OpenGL SDK: the driver's archive and the console's two GPU import stubs
|
||||
mkdir -p "$kit/opengl/sdk/lib"
|
||||
cp "$opengl/../LICENSE" "$opengl/../THIRD_PARTY_NOTICES.md" "$kit/opengl/"
|
||||
# (as released: libPS5OpenGL.a is a linker script naming the others)
|
||||
cp -R "$opengl"/lib/. "$kit/opengl/sdk/lib/"
|
||||
rm -rf "$kit/opengl/sdk/lib/cmake" "$kit/opengl/sdk/lib/pkgconfig"
|
||||
[[ -f $opengl/sunshine-runtime-defines.txt ]] && cp "$opengl/sunshine-runtime-defines.txt" "$kit/opengl/"
|
||||
|
||||
# ---- Clang's builtins for the console's processor: a Linux on arm64 does not install x86-64's
|
||||
builtins="$("$PS5_CLANG" --print-resource-dir)/lib/linux/libclang_rt.builtins-x86_64.a"
|
||||
[[ -f $builtins ]] || { echo "package-kit.sh: missing $builtins" >&2; exit 2; }
|
||||
mkdir -p "$kit/clang-rt/lib/linux"
|
||||
cp "$builtins" "$kit/clang-rt/lib/linux/"
|
||||
|
||||
# ---- licences, and where each part's source is
|
||||
mkdir -p "$kit/licenses"
|
||||
cp "$root/LICENSE" "$kit/LICENSE"
|
||||
cp "$root/LICENSING.md" "$kit/"
|
||||
cp "$root"/licenses/* "$kit/licenses/"
|
||||
cp "$source/LICENSE" "$kit/licenses/sms-pc-port-LICENSE.txt"
|
||||
sdl_revision=$(git -C "$root/.deps/SDL2" describe --tags --always 2>/dev/null || echo "release-2.32.10")
|
||||
own_revision=$(git -C "$root" rev-parse HEAD 2>/dev/null || echo "(this kit's own files: ps5/ in the kit)")
|
||||
{
|
||||
echo "The builder kit $version of Super Mario Sunshine for the PlayStation 5 was built from:"
|
||||
echo
|
||||
echo "PS5_Sunshine https://github.com/griches/PS5_Sunshine $own_revision"
|
||||
echo "sms-pc-port https://github.com/chasem-dev/sms-pc-port $port_rev (+ ps5/patches/sms-pc-port.patch)"
|
||||
echo "sms-english https://github.com/chasem-dev/sms-english $decomp_rev (fetched by build-title.sh, not in the kit)"
|
||||
echo "PS5 OpenGL SDK https://github.com/blackbearreloaded/ps5-opengl release 1.0.2, with three runtime objects rebuilt (ps5/opengl/build-runtime.sh; defines in opengl/sunshine-runtime-defines.txt)"
|
||||
echo "PS5_PayloadSDK https://github.com/mihawk-99/PS5_PayloadSDK $(<"$sdk/.ps5-sdk-revision")"
|
||||
echo "PS5_Vulkan https://github.com/mihawk-99/PS5_Vulkan $(git -C "$vulkan" rev-parse HEAD) (its link recipe, CRT and native tool; no Vulkan driver is linked)"
|
||||
echo "SDL2 https://github.com/libsdl-org/SDL $sdl_revision (+ ps5/patches/sdl.patch and the console's drivers)"
|
||||
echo "Clang builtins https://github.com/llvm/llvm-project $("$PS5_CLANG" --version | head -1)"
|
||||
echo "zlib https://github.com/madler/zlib $zlib_version (in vulkan/prebuilt's Linux tools)"
|
||||
echo
|
||||
echo "LICENSING.md says which licence covers which part."
|
||||
} > "$kit/SOURCES.txt"
|
||||
echo "$version" > "$kit/VERSION"
|
||||
|
||||
# ---- nothing of the game: no game code archive or object, no decompilation, no disc image or save
|
||||
leaks=$(find "$kit" \( -name 'libsms_game*' -o -path '*/sms_game.dir/*' -o -path '*/decomp/*' -o -path '*/patched/*' \
|
||||
-o -iname '*.iso' -o -iname '*.gcm' -o -iname '*.ciso' -o -iname '*.rvz' -o -iname '*.gci' -o -iname '*.szs' -o -iname '*.thp' \
|
||||
-o -iname '*.dol' -o -iname '*.arc' \) -print)
|
||||
[[ -z $leaks ]] || { echo "package-kit.sh: these must not be in a kit:" >&2; echo "$leaks" >&2; exit 1; }
|
||||
# (the files symlinks point to, not the links: Windows' own unzip, and Python's,
|
||||
# make a link a file holding its target's name, and the SDK has header links)
|
||||
(cd "$root/dist/kit" && rm -f "$name.zip" && zip -qr "$name.zip" "$name")
|
||||
printf '==> %s (%s unpacked, %s zipped)\n' "$root/dist/kit/$name.zip" \
|
||||
"$(du -sh "$kit" | cut -f1)" "$(du -sh "$root/dist/kit/$name.zip" | cut -f1)"
|
||||
sha256sum "$root/dist/kit/$name.zip"
|
||||
Executable
+88
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env bash
|
||||
# Super Mario Sunshine for the PlayStation 5 - pack the built title folder as a release archive.
|
||||
#
|
||||
# ps5/tools/package-release.sh [VERSION] (through tools/vm.sh package-release)
|
||||
#
|
||||
# Writes dist/Sunshine-PS5-vVERSION.zip: the title folder dist/<TITLE_ID>/ as the
|
||||
# console takes it (eboot.bin, sce_sys/, sce_module/, res/, licenses/), with
|
||||
# SOURCE.txt beside eboot.bin naming the revision of every part the build
|
||||
# linked. A built title is under GPL-3.0: that file is how a release says where
|
||||
# its source is. No disc image, save or texture pack goes in, and it refuses if
|
||||
# one is in the folder. VERSION defaults to param.json's contentVersion.
|
||||
#
|
||||
# After PS5_SwitchWaker's package-release.sh.
|
||||
# SPDX-License-Identifier: MIT
|
||||
set -euo pipefail
|
||||
ps5=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
|
||||
root=$(dirname "$ps5")
|
||||
vulkan=$(cd -- "${PS5_VULKAN_DIR:-$root/../PS5_Vulkan}" && pwd)
|
||||
opengl=${PS5_OPENGL_PREFIX:?set PS5_OPENGL_PREFIX to the sdk/ directory of the PS5 OpenGL SDK}
|
||||
source="$root/vendor/sms-pc-port"
|
||||
param="$ps5/sce_sys/param.json"
|
||||
version=${1:-$(python3 -c 'import json,sys; v=json.load(open(sys.argv[1]))["contentVersion"].split("."); print("%d.%d.%d" % tuple(int(x) for x in v))' "$param")}
|
||||
title_id=$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["titleId"])' "$param")
|
||||
app="$root/dist/$title_id"
|
||||
for file in eboot.bin sce_sys/param.json sce_sys/icon0.png sce_sys/pic0.dds sce_sys/pic1.dds sce_module/libc.prx \
|
||||
res/prompts/playstation.png; do
|
||||
[[ -e $app/$file ]] || { echo "missing $app/$file: run build.sh first" >&2; exit 2; }
|
||||
done
|
||||
cmp -s "$param" "$app/sce_sys/param.json" || { echo "the title's param.json is not this tree's: run build.sh first" >&2; exit 2; }
|
||||
# a console's own files, a disc, a save or a pack are not for a release
|
||||
stray=$(find "$app" \( -iname '*.iso' -o -iname '*.ciso' -o -iname '*.gcm' -o -iname '*.rvz' -o -iname '*.gci' \
|
||||
-o -iname '*.dol' -o -iname '*.szs' -o -path "$app/userdata" -o -path "$app/mods" -o -name env.txt \
|
||||
-o -name settings.txt -o -name bindings.txt \) -print -quit)
|
||||
[[ -z $stray ]] || { echo "game data or a save in the title folder ($stray): a release never carries it" >&2; exit 2; }
|
||||
# the submodule's working tree carries the console's patch: that is not a change here
|
||||
[[ -z $(git -C "$root" status --porcelain --ignore-submodules=dirty) ]] ||
|
||||
{ echo "uncommitted changes: a release is built from a commit" >&2; exit 2; }
|
||||
sdk=$(bash "$ps5/tools/setup-sdk.sh")
|
||||
sdl_revision=$(git -C "$root/.deps/SDL2" describe --tags --always 2>/dev/null || echo "release-2.32.10")
|
||||
mkdir -p "$app/licenses"
|
||||
cp "$root/licenses/GPL-3.0.txt" "$root/licenses/MIT.txt" "$root/licenses/Zlib.txt" "$app/licenses/"
|
||||
cp "$source/LICENSE" "$app/licenses/sms-pc-port-LICENSE.txt"
|
||||
cp "$source/decomp/LICENSE" "$app/licenses/sms-english-LICENSE.txt"
|
||||
cp "$root/LICENSING.md" "$app/licenses/LICENSING.md"
|
||||
cat > "$app/SOURCE.txt" <<SOURCE
|
||||
Super Mario Sunshine for PlayStation 5, version $version
|
||||
An unofficial homebrew port of chasem-dev's sms-pc-port. Not affiliated with
|
||||
Nintendo or Sony Interactive Entertainment.
|
||||
|
||||
This title is distributed under the GNU General Public License, version 3
|
||||
(licenses/GPL-3.0.txt). Its source is these projects at these revisions:
|
||||
|
||||
The port (patch, SDL2 drivers, platform layer, build)
|
||||
$(git -C "$root" remote get-url origin 2>/dev/null || echo "this repository")
|
||||
$(git -C "$root" rev-parse HEAD)
|
||||
The PC port: sms-pc-port (MIT), with ps5/patches/sms-pc-port.patch
|
||||
https://github.com/chasem-dev/sms-pc-port
|
||||
$(git -C "$source" rev-parse HEAD)
|
||||
The game's code: the sms-english decompilation it builds (CC0-1.0)
|
||||
https://github.com/chasem-dev/sms-english
|
||||
$(git -C "$source/decomp" rev-parse HEAD)
|
||||
OpenGL: the PS5 OpenGL SDK (GPL-3.0-or-later), release 1.0.2, with three runtime
|
||||
objects rebuilt by ps5/opengl/build-runtime.sh
|
||||
https://github.com/blackbearreloaded/ps5-opengl
|
||||
SDL2 (Zlib), with ps5/patches/sdl.patch and the drivers in ps5/sdl
|
||||
https://github.com/libsdl-org/SDL
|
||||
$sdl_revision
|
||||
The link recipe, CRT, libc.prx and packaging: PS5_Vulkan (GPL-3.0)
|
||||
https://github.com/mihawk-99/PS5_Vulkan
|
||||
$(git -C "$vulkan" rev-parse HEAD)
|
||||
The platform layer and SDK: PS5_PayloadSDK (GPL-3.0), over ps5-payload-dev/sdk
|
||||
https://github.com/mihawk-99/PS5_PayloadSDK
|
||||
$(<"$sdk/.ps5-sdk-revision")
|
||||
Compiler: $(${PS5_CLANG:-clang} --version | head -1)
|
||||
|
||||
No game data is included. Super Mario Sunshine belongs to Nintendo; this title
|
||||
does nothing without a GameCube disc image of the USA disc (GMSE01, revision 0,
|
||||
.iso, .gcm or .ciso) that you provide, placed beside eboot.bin. The pictures in
|
||||
sce_sys/ are the port's own, and res/prompts holds button symbols drawn from
|
||||
Kenney's CC0 Input Prompts.
|
||||
|
||||
eboot.bin sha256: $(sha256sum "$app/eboot.bin" | cut -d' ' -f1)
|
||||
SOURCE
|
||||
out="$root/dist/Sunshine-PS5-v$version.zip"
|
||||
rm -f "$out"
|
||||
(cd "$root/dist" && zip -q -r -X "$out" "$title_id" -x '*/.DS_Store')
|
||||
printf '==> %s (%s bytes)\n' "$out" "$(stat -c %s "$out")"
|
||||
sha256sum "$out"
|
||||
@@ -1,84 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Super Mario Sunshine for the PlayStation 5 - a texture pack sized for the console.
|
||||
|
||||
shrink-texture-pack.py SOURCE DEST [MAX]
|
||||
|
||||
Copies a Dolphin texture pack, making every BC7 .dds wider or taller than MAX
|
||||
(2048) that size with a full mipmap chain. The PS5's OpenGL driver takes
|
||||
texture data slowly (about 35 MiB a second) and a title's graphics memory is
|
||||
limited: the UHD pack's 4096 and 8192 pixel images are two thirds of its 3 GB,
|
||||
cost Delfino Plaza a 17 second load, and ran Bianco Hills out of memory.
|
||||
Needs Python with Pillow and etcpak.
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
"""
|
||||
import os, shutil, struct, sys
|
||||
from multiprocessing import Pool
|
||||
from PIL import Image
|
||||
import etcpak
|
||||
|
||||
Image.MAX_IMAGE_PIXELS = None
|
||||
|
||||
|
||||
def blocks(image):
|
||||
w, h = image.size
|
||||
pw, ph = (w + 3) // 4 * 4, (h + 3) // 4 * 4
|
||||
if (pw, ph) != (w, h): # the small levels: repeat the edge into the 4x4 block
|
||||
image = image.resize((pw, ph), Image.NEAREST)
|
||||
# the parameters are not optional: without them etcpak crashes
|
||||
return etcpak.compress_bc7(image.tobytes(), pw, ph, etcpak.BC7CompressBlockParams())
|
||||
|
||||
|
||||
def convert(job):
|
||||
source, dest, limit = job
|
||||
header = bytearray(open(source, "rb").read(148))
|
||||
height, width = struct.unpack_from("<II", header, 12)
|
||||
dxgi = struct.unpack_from("<I", header, 128)[0] if header[84:88] == b"DX10" else 0
|
||||
if max(width, height) <= limit or dxgi not in (98, 99):
|
||||
shutil.copyfile(source, dest)
|
||||
return 0
|
||||
image = Image.open(source).convert("RGBA")
|
||||
scale = limit / max(width, height)
|
||||
w, h = max(4, round(width * scale)), max(4, round(height * scale))
|
||||
levels = []
|
||||
while True:
|
||||
level = image.resize((w, h), Image.LANCZOS)
|
||||
levels.append(blocks(level))
|
||||
if w == 1 and h == 1:
|
||||
break
|
||||
w, h = max(1, w // 2), max(1, h // 2)
|
||||
w, h = max(4, round(width * scale)), max(4, round(height * scale))
|
||||
struct.pack_into("<I", header, 8, struct.unpack_from("<I", header, 8)[0] | 0x20000 | 0x80000) # mip count, linear size
|
||||
struct.pack_into("<II", header, 12, h, w)
|
||||
struct.pack_into("<I", header, 20, len(levels[0]))
|
||||
struct.pack_into("<I", header, 28, len(levels))
|
||||
struct.pack_into("<I", header, 108, 0x401008) # complex, mipmapped texture
|
||||
with open(dest, "wb") as out:
|
||||
out.write(header)
|
||||
for level in levels:
|
||||
out.write(level)
|
||||
return 1
|
||||
|
||||
|
||||
def main():
|
||||
source, dest = sys.argv[1], sys.argv[2]
|
||||
limit = int(sys.argv[3]) if len(sys.argv) > 3 else 2048
|
||||
jobs = []
|
||||
for root, _, files in os.walk(source):
|
||||
target = os.path.join(dest, os.path.relpath(root, source))
|
||||
os.makedirs(target, exist_ok=True)
|
||||
for name in files:
|
||||
if name.startswith("."):
|
||||
continue
|
||||
path = os.path.join(root, name)
|
||||
if name.endswith(".dds"):
|
||||
jobs.append((path, os.path.join(target, name), limit))
|
||||
else:
|
||||
shutil.copyfile(path, os.path.join(target, name))
|
||||
with Pool() as pool:
|
||||
made = sum(pool.imap_unordered(convert, jobs, chunksize=4))
|
||||
print("%d images, %d made smaller" % (len(jobs), made))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
# ps5/tools/vm.sh console-files ps5/tools/console-files.sh (your sm64.o2r, over FTP)
|
||||
# ps5/tools/vm.sh deploy ps5/tools/deploy.sh (FTP to the console)
|
||||
# ps5/tools/vm.sh run ps5/tools/run.sh (launch and follow klog)
|
||||
# ps5/tools/vm.sh package-kit [V] ps5/tools/package-kit.sh (the builder kit)
|
||||
# ps5/tools/vm.sh package-release [V] ps5/tools/package-release.sh (the release archive)
|
||||
#
|
||||
# The stack (PS5_Vulkan with its RADV archive, PS5_Mesa, PS5_PayloadSDK) lives in
|
||||
# the Lima VM "ps5" under ~/ps5, built with LLVM 22 (~/ps5/env.sh); this folder is
|
||||
|
||||
Reference in new issue
Block a user