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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 19 additions & 44 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,22 @@ jobs:
tagged-release:
name: Tagged Release
runs-on: windows-2022 # windows-2025 dropped InnoSetup/ISCC
environment:
name: pypi
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.12.x'
- name: Install Poetry
uses: abatilo/actions-poetry@v2.3.0
with:
poetry-version: '1.6.1'
- name: Configure poetry
run: poetry config virtualenvs.in-project true
- name: Install dependencies
run: |
python -m pip install --upgrade pip wheel
poetry install -E gui
uses: actions/setup-python@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Install the project
run: uv sync --locked --all-extras --group pack
- name: Cache prod.keys
id: cache-prod-keys
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: prod.keys
key: prod-keys
Expand All @@ -36,50 +33,28 @@ jobs:
env:
PROD_KEYS: ${{ secrets.PROD_KEYS }}
run: $env:PROD_KEYS | Out-File prod.keys
- name: Download Homebrew NROs
run: |
aria2c https://switch.cdn.fortheusers.org/zips/hbmenu.zip
7z x hbmenu.zip hbmenu.nro
aria2c https://switch.cdn.fortheusers.org/zips/aio-switch-updater.zip
7z x aio-switch-updater.zip switch/aio-switch-updater/aio-switch-updater.nro
Move-Item -Path .\switch\aio-switch-updater\aio-switch-updater.nro -Destination .
- name: Pre-build some NSP forwarders
run: |
poetry run nton build "hbmenu.nro" --name "Homebrew Menu" --sdmc "/hbmenu.nro"
poetry run nton build "aio-switch-updater.nro" --name "AIO-Switch-Updater" --sdmc "/switch/aio-switch-updater/aio-switch-updater.nro"
- name: Upload NSPs
uses: actions/upload-artifact@v4
with:
name: Pre-built NSPs
path: C:/Users/runneradmin/Desktop/NTON/*.nsp
- name: Build EXE with PyInstaller
run: poetry run python pyinstaller.py
run: uv run python pyinstaller.py
- name: Create Windows Installer with Inno Setup
run: |
iscc setup.iss
mv dist/NTON-*.exe .
- name: Build Portable EXE with PyInstaller
shell: pwsh
run: |
poetry run python pyinstaller.py --one-file
$env:NTON_VERSION = uvx hatch version
uv run python pyinstaller.py --one-file
mv dist/NTON.exe NTON-${{ github.ref_name }}-portable.exe
- name: Build a wheel
run: poetry build
- name: Upload wheel
uses: actions/upload-artifact@v4
with:
name: Python Wheel
path: "dist/*.whl"
- name: Build project
run: uv build
- name: Deploy release
uses: marvinpinto/action-automatic-releases@latest
with:
prerelease: false
repo_token: "${{ secrets.GITHUB_TOKEN }}"
files: |
dist/*.whl
NTON-*.exe
NTON-*-portable.exe
C:/Users/runneradmin/Desktop/NTON/*.nsp
- name: Publish to PyPI
env:
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }}
run: poetry publish
run: uv publish
47 changes: 24 additions & 23 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,40 @@ on:
branches: [ master ]

jobs:
lint:
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Install the project
run: uv sync --locked --all-extras --group lint --group form --group type
- name: Install pre-commit patched for uv
run: uv tool install pre-commit --with pre-commit-uv --force-reinstall
- name: Run pre-commit to do linting, formatting, type-checking, and more
run: uv run pre-commit run --all-files --show-diff-on-failure
build:

runs-on: windows-latest
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
poetry-version: [1.6.1]

python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install poetry
uses: abatilo/actions-poetry@v2.3.0
with:
poetry-version: ${{ matrix.poetry-version }}
- name: Install project
run: |
poetry install --no-dev
python -m pip install flake8
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Install the project
run: uv sync --locked --extra gui
- name: Build project
run: poetry build
run: uv build
- name: Cache prod.keys
id: cache-prod-keys
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: prod.keys
key: prod-keys
Expand All @@ -49,4 +50,4 @@ jobs:
PROD_KEYS: ${{ secrets.PROD_KEYS }}
run: $env:PROD_KEYS | Out-File prod.keys
- name: Build basic NSP forwarder
run: poetry run nton build "sdl-hello.nro" --name "Hello World" --publisher "vgmoose" --version "1.1" --sdmc "/switch/sdl-hello.nro"
run: uv run nton build "sdl-hello.nro" --name "Hello World" --publisher "vgmoose" --version "1.1" --sdmc "/switch/sdl-hello.nro"
37 changes: 37 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks

exclude: |
(?x)^(
nton/gui/main\.py|
nton/gui/resources/icons_rc\.py
)$
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.13
hooks:
- id: ruff-check
language: system
types: [ python ]
- id: ruff-format
language: system
types: [ python ]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.19.1
hooks:
- id: mypy
language: system
types: [ python ]
args: [] # override defaults to empty
- repo: https://github.com/pycqa/isort
rev: 6.1.0
hooks:
- id: isort
language: system
types: [ python ]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
args: [--markdown-linebreak-ext=md]
10 changes: 10 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"recommendations": [
"EditorConfig.EditorConfig",
"ms-python.python",
"ms-python.vscode-pylance",
"charliermarsh.ruff",
"ms-python.isort",
"ms-python.mypy-type-checker"
]
}
6 changes: 3 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Special characters like "é" etc. are no longer garbled in the built NSP. This was a bug when HacBrewPack set the
name and publisher, yet that was never intended anyway. Now NTON itself fully manages setting the Name and Publisher.
- Multiple language data being included but with missing data is now fixed. Only AmericanEnglish is enabled, and only
AmericanEnglish has language data. Similarly to the previous issue, this was caused by HacBrewPack in the same way.
AmericanEnglish has language data. Similarly to the previous issue, this was caused by HacBrewPack in the same way.

## [2.0.1] - 2023-08-26

Expand Down Expand Up @@ -114,7 +114,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- Fixed API call within `get_game_title_ids()`, though it still sometimes errors with HTTP 500.
- Fixed API call within `get_game_title_ids()`, though it still sometimes errors with HTTP 500.

## [1.2.2] - 2023-04-20

Expand All @@ -137,7 +137,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
folders, or `atmosphere` & `bootloader` folders. This is much more reliable than simply assuming any drive that
isn't the C drive to be your microSD card.
- The NRO path is no longer forced to be within `/retroarch/cores` when `--rom` is used. This is to allow use of
`--rom` with other Homebrew, e.g., MGBA.
`--rom` with other Homebrew, e.g., MGBA.

### Fixed

Expand Down
85 changes: 42 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,40 @@
# NTON
<p align="center">
<a href="https://github.com/rlaphoenix/nton">NTON</a>
<br/>
<sup><em>Nintendo Switch NRO-to-NSP Builder</em></sup>
</p>

<p align="center">
<a href="https://github.com/rlaphoenix/nton/blob/master/LICENSE">
<img src="https://img.shields.io/:license-GPL%203.0-blue.svg" alt="License">
</a>
<a href="https://pypi.org/project/nton">
<img src="https://img.shields.io/badge/python-3.9%2B-informational" alt="Python version">
</a>
<a href="https://github.com/astral-sh/uv">
<img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Onyx-Nostalgia/uv/refs/heads/fix/logo-badge/assets/badge/v0.json" alt="Manager: uv">
</a>
<a href="https://github.com/astral-sh/ruff">
<img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Linter: Ruff">
</a>
<a href="https://github.com/rlaphoenix/nton/actions/workflows/ci.yml">
<img src="https://github.com/rlaphoenix/nton/actions/workflows/ci.yml/badge.svg" alt="Build status">
</a>
</p>

[![Build status](https://github.com/rlaphoenix/nton/actions/workflows/ci.yml/badge.svg)](https://github.com/rlaphoenix/nton/actions/workflows/ci.yml)
[![PyPI version](https://img.shields.io/pypi/v/nton)](https://pypi.python.org/pypi/nton)
[![Python versions](https://img.shields.io/pypi/pyversions/nton)](https://pypi.python.org/pypi/nton)
<a href="https://github.com/rlaphoenix/nton/blob/master/LICENSE">
<img align="right" src="https://img.shields.io/badge/license-GPLv3-blue" alt="License (GPLv3)"/>
</a>
* * *

<img src="https://github.com/rlaphoenix/nton/assets/17136956/c6306192-9a57-41f2-8840-cc8db03fef93" style="width:300px" align="right" />
<img src="https://github.com/user-attachments/assets/e8cd0c5f-b8b5-4a56-ab66-8777dacdff66" style="width:505px" />

NTON is a Nintendo Switch NRO to NSP Forwarder for firmware 12.0.0 and newer.
<img src="https://github.com/rlaphoenix/nton/assets/17136956/c6306192-9a57-41f2-8840-cc8db03fef93" style="width:300px" align="right" />

A forwarder lets you open Homebrew files from your SD card through the Nintendo Switch Home Screen instead
of the Homebrew Launcher.
NTON is a PC utility that let's you build an NSP application that launches right into an NRO file.
Firmware 12.0.0 and newer is supported.

> [!TIP]
> Want to generate NSP forwarders directly on your Switch via Homebrew? Check out [TooTallNate's switch-nsp-forwarder](https://github.com/TooTallNate/switch-nsp-forwarder)!

<img src="https://github.com/user-attachments/assets/e8cd0c5f-b8b5-4a56-ab66-8777dacdff66" style="width:505px" />
If you want to generate NSP forwarders directly on your Switch, then check out [TooTallNate's switch-nsp-forwarder](https://github.com/TooTallNate/switch-nsp-forwarder)

> [!CAUTION]
> Installing NSP files, like NRO to NSP forwarders, can result in your console getting banned as they do not contain valid signatures verified by Nintendo.
> This applies to all forms of NRO to NSP forwarders, including homebrew and web versions. We do not possess Nintendo's private key to generate valid signatures.
> NSP forwarders are only safe if used on an emuMMC with all Nintendo servers blocked with [DNS.mitm](https://github.com/Atmosphere-NX/Atmosphere/blob/master/docs/features/dns_mitm.md).
> Do not install them on sysMMC (system eMMC/NAND). Do not use 90DNS or any manual DNS server approach. DNS.mitm is a much safer and faster approach.
> To run NSP forwarders you need to make further modifications to the boot process and your system. I do not support Piracy on any Nintendo device therefore support on that is not provided.
> Installing these kinds of NSP files (NRO to NSP forwarders) can result in a console ban. This applies not just to this software but to all forms of NRO to NSP forwarder generators, including homebrew and web-app versions. To run these NSP files, you need to make further modifications to your system. I do not support Piracy on any Nintendo device therefore support on that is not provided.

## Features

Expand Down Expand Up @@ -188,8 +198,8 @@ There are numerous reasons why this could be, see the following main reasons:
forwarder has a hardcoded file path that it loads the NRO from when launched.
2. You may have since updated Atmosphere or your Firmware which broke the changes you made to the bootloader that enabled
the use of custom NSP files. As this project does not support piracy on any Nintendo system, support is not provided.
4. It's possible a firmware update has broken the Forwarder ROM binary (source under /rom in this repo) and needs
to be re-compiled against a newer libnx / devkitpro version. Firmware 9.0.0, 12.0.0, 19.0.0, and 21.0.0 have previously
3. It's possible a firmware update has broken the Forwarder ROM binary (source under /rom in this repo) and needs
to be re-compiled against a newer libnx / devkitpro version. Firmware 9.0.0, 12.0.0, 19.0.0, and 21.0.0 have previously
broken different forwarder ROMs requiring updates. If you believe this to be the case then please make an Issue.

If after reading all of these troubleshooting steps, you still cannot get the NSP forwarder to work, then I do not
Expand All @@ -198,30 +208,19 @@ recommend the use of them and instead recommend using the Homebrew launcher from

## Development

The following steps are basic instructions on downloading and working on the code under a [Poetry] environment.

1. Follow Poetry's Docs to [Install Poetry].
2. Download NTON's latest code, `git clone https://github.com/rlaphoenix/nton`
3. Navigate to the downloaded code repository, `cd nton`
4. _Optionally_ have Poetry install the virtual-env in the project, `poetry config virtualenvs.in-project true`
5. Install NTON's dependencies and development tools, `poetry install -E gui`
6. Run NTON from within the Poetry venv, `poetry run nton --help`

> [!NOTE]
> If you plan to work on or use the GUI during development, then add `-E gui` during Step 5.
1. Install [uv]
2. `uv sync --all-extras --all-groups`
3. `.venv\Scripts\activate` (or `source .venv/bin/activate` on macOS and Linux)
4. `uv tool install pre-commit --with pre-commit-uv --force-reinstall`
5. `pre-commit install`

As shown, running the `nton` executable is somewhat different to a normal installation. This is because Poetry installs
all dependencies and the `nton` shim itself within a virtual-environment, which is like a clone of your Python install
stripped clean, with only NTON's dependencies installed. That way you don't mess around with any dependencies from any
other installed Python applications, nor the other way around. A secluded environment.
Now feel free to work on the project however you like, all code will be checked before committing.
You can run nton with the GUI with `nton` or without the GUI with `nton --help`.

I recommend taking a look at [Poetry's Docs] for further information, why not get started by reading Poetry's guide on
[Using Your Virtual Environment].
If you make any changes to the QT UI file (main.ui) or any of the icon/image files, then you must
run `.\make` to re-compile them to Python files.

[Poetry]: <https://python-poetry.org>
[Install Poetry]: <https://python-poetry.org/docs/#installation>
[Poetry's Docs]: <https://python-poetry.org/docs>
[Using Your Virtual Environment]: <https://python-poetry.org/docs/basic-usage/#using-your-virtual-environment>
[uv]: <https://docs.astral.sh/uv>

## Credit

Expand Down
3 changes: 3 additions & 0 deletions make.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@echo off
pyside6-rcc nton\gui\resources\icons.qrc -o nton\gui\resources\icons_rc.py
pyside6-uic nton\gui\main.ui -o nton\gui\main.py --absolute-imports --python-paths "."
3 changes: 3 additions & 0 deletions make.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env sh
pyside6-rcc nton/gui/resources/icons.qrc -o nton/gui/resources/icons_rc.py
pyside6-uic nton/gui/main.ui -o nton/gui/main.py --absolute-imports --python-paths "."
2 changes: 1 addition & 1 deletion nton/assets/game_title_ids.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion nton/bin/hacbrewpack.license
Original file line number Diff line number Diff line change
Expand Up @@ -336,4 +336,4 @@ This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
Public License instead of this License.
2 changes: 1 addition & 1 deletion nton/bin/hptnacp-hacpack.license
Original file line number Diff line number Diff line change
Expand Up @@ -336,4 +336,4 @@ This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
Public License instead of this License.
2 changes: 1 addition & 1 deletion nton/bin/nstool.license
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.
2 changes: 1 addition & 1 deletion nton/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Binaries:


class Files:
keys_home = (Path.home() / ".switch" / "prod.keys")
keys_home = Path.home() / ".switch" / "prod.keys"
keys_cwd = Path("./prod.keys").absolute()
keys = keys_cwd if keys_cwd.is_file() else keys_home
game_title_ids = Directories.assets / "game_title_ids.json"
Expand Down
Loading
Loading