0
0
镜像自地址 https://github.com/THZoria/NX_Firmware.git 已同步 2026-04-09 10:41:13 +00:00

比较提交

..

27 次代码提交
21.2.0 ... main

作者 SHA1 备注 提交日期
Zoria
a9e20653cf Merge pull request #22 from JeremKOYTB/main
Massive Upgrade: Implement Strict NCA Crypto-Verification and Deterministic Archives
2026-04-08 21:07:07 +02:00
JérémKO
332d8f1f2f Update: Implement dynamic release body generation and streamline Python execution
-Removed the version argument from the Python script execution step.

-Removed the manual .nca file cleanup and ZIP creation steps.

-Added log extraction (tee and sed) to dynamically populate the GitHub Release body via $GITHUB_ENV.
2026-04-08 20:41:11 +02:00
JérémKO
5b7ae87b50 Massive Upgrade: Implement Detailed NCA Hash Verification and Deterministic Archive Generation
-Added SHA-256 hash verification for downloaded .nca files against .cnmt records.

-Implemented ZipInfo to hardcode ZIP metadata (timestamps, OS, permissions) for deterministic hashing.

-Changed compression method from ZIP_DEFLATED to ZIP_STORED.
2026-04-08 20:37:54 +02:00
Zoria
e642f574fd Merge pull request #21 from JeremKOYTB/main
Fix: Ignore case for exFAT file identification (Fixes 404 on 22.1.0)
2026-04-08 18:35:06 +02:00
JérémKO
fb77608da9 Fix: Ignore case for exFAT file identification (Fixes 404 on 22.1.0) 2026-04-08 18:04:07 +02:00
Zoria
cd09390cd5 Refactor firmware version check and download process 2026-03-17 07:11:08 +01:00
Zoria
df02467cdd Improve firmware download workflow and messages
Refactor firmware download workflow for clarity and accuracy.
2026-03-17 07:06:25 +01:00
Zoria
a3b5ff8eea Refactor firmware autodl workflow for improved checks 2026-03-17 07:04:25 +01:00
Zoria
bf849d454d Add lxml to Python module installation 2026-03-17 07:01:41 +01:00
Zoria
e5cac716b8 Refactor firmware autodl workflow for better handling
Updated the firmware autodl workflow to improve Python script and file handling.
2026-03-17 07:00:35 +01:00
Zoria
9f2d533eb2 Refactor firmware extraction and version handling 2026-03-17 06:52:05 +01:00
Zoria
f3d145640b Fix firmware version check and update logic 2026-02-22 00:28:25 +01:00
Zoria
acb5f7b500 Update firmware autodl workflow for version checks 2026-02-22 00:26:09 +01:00
Zoria
c368de2568 Refactor firmware version check and download script 2026-02-22 00:23:44 +01:00
Zoria
5dfb4b0301 Enhance firmware auto downloader workflow
Updated the firmware auto downloader workflow to improve release notes preparation and streamline version checking.
2026-02-22 00:22:38 +01:00
Zoria
e53232cb55 Update firmware auto downloader workflow to v21.0.0
Updated the firmware auto downloader workflow to include version 21.0.0, added new dependencies, and improved error handling.
2026-02-22 00:21:57 +01:00
Zoria
35141cd068 Enhance firmware version check for Switch 1
Updated the firmware version check to include version 21.0.0 and modified output messages for clarity.
2026-01-24 21:25:42 +01:00
Zoria
99860219d6 Enhance firmware autodl workflow with changelog check
Added check for changelog existence before reading.
2026-01-24 21:21:43 +01:00
Zoria
484397aaa8 Fix firmware version check and tag existence logic 2026-01-24 21:18:38 +01:00
Zoria
f145465763 Update version check logic in firmware-autodl.yml 2026-01-24 21:16:41 +01:00
Zoria
0f244f1ced Refactor firmware version check in workflow 2026-01-24 21:15:06 +01:00
Zoria
4fea53b06b Fix zip command and version check in workflow 2026-01-24 21:10:25 +01:00
Zoria
c9de5c55c5 Refactor firmware autodl workflow for clarity and efficiency
Updated the workflow to install Python dependencies separately and improved the logic for checking firmware versions and creating releases.
2026-01-24 21:05:26 +01:00
Zoria
818be8376e Refactor firmware workflow for version handling 2026-01-24 21:00:59 +01:00
Zoria
f5d2b4e265 Refactor firmware download workflow steps 2026-01-24 20:56:51 +01:00
Zoria
cfcf5e0da6 Enhance README with badges for Discord and version
Added Discord and version badges to README.
2026-01-15 19:30:17 +01:00
Zoria
ef691f15ff Create firmware zip while excluding variant files
Add step to create firmware zip excluding .nca.1 files
2026-01-13 08:22:10 +01:00
修改 3 个文件,包含 115 行新增61 行删除

查看文件

@@ -17,72 +17,81 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- name: 🐍 Setup Python and dependencies - name: 🐍 Setup Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5
with: with:
python-version: '3.x' python-version: '3.x'
- name: ⚙️ Install required Python modules - name: ⚙️ Install required Python modules
run: | run: |
pip install requests anynet beautifulsoup4 pip install requests anynet beautifulsoup4
- name: ⬇️ Setup hactool-linux - name: ⬇️ Setup hactool-linux
run: | run: |
cp hactool-linux hactool if [ -f "hactool-linux" ]; then
chmod +x hactool cp hactool-linux hactool
chmod +x hactool
- name: 🔍 Check firmware version (Switch 1 only) else
id: version_check echo "Warning: hactool-linux non trouvé."
run: |
LATEST_TITLE=$(curl -s 'https://yls8.mtheall.com/ninupdates/feed.php' | \
grep '<title>Switch ' | \
grep -v '<title>Switch 2 ' | \
head -n 1)
if [ -z "$LATEST_TITLE" ]; then
echo "::error::Could not retrieve the firmware title for Switch 1 from the RSS feed."
exit 1
fi fi
LATEST_VERSION=$(echo "$LATEST_TITLE" | grep -oP 'Switch \K[0-9.]+') - name: 🔍 Check firmware version (Switch 1 only, >=21.0.0)
id: version_check
shell: bash
run: |
set +e
RSS=$(curl -sL --fail https://yls8.mtheall.com/ninupdates/feed.php)
if [ $? -ne 0 ] || [ -z "$RSS" ]; then
echo "new_version=false" >> $GITHUB_OUTPUT
exit 0
fi
# Extraction stricte Switch 1 (hac) + Tri version
LATEST_VERSION=$(echo "$RSS" | tr -d '\n' | sed 's/<item>/\n<item>/g' | \
grep 'sys=hac' | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | sort -V | tail -n 1)
if [ -z "$LATEST_VERSION" ]; then if [ -z "$LATEST_VERSION" ]; then
echo "::error::Failed to parse LATEST_VERSION from title: $LATEST_TITLE" echo "new_version=false" >> $GITHUB_OUTPUT
exit 1 exit 0
fi fi
TAG_EXISTS=$(git ls-remote --tags origin $LATEST_VERSION) MAJOR=$(echo "$LATEST_VERSION" | cut -d. -f1)
if [ "$MAJOR" -lt 21 ]; then
echo "new_version=false" >> $GITHUB_OUTPUT
exit 0
fi
if [ ! -z "$TAG_EXISTS" ]; then # Check si la Release existe
echo "INFO: Tag $LATEST_VERSION already exists on GitHub. Stopping workflow to avoid re-upload." HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
"https://api.github.com/repos/${{ github.repository }}/releases/tags/$LATEST_VERSION")
if [ "$HTTP_STATUS" = "200" ]; then
echo "new_version=false" >> $GITHUB_OUTPUT echo "new_version=false" >> $GITHUB_OUTPUT
else else
echo "INFO: New version $LATEST_VERSION found! Preparing to download..."
echo "new_version=true" >> $GITHUB_OUTPUT echo "new_version=true" >> $GITHUB_OUTPUT
echo "firmware_version=$LATEST_VERSION" >> $GITHUB_OUTPUT
fi fi
shell: bash set -e
- name: 💻 Execute download script and capture changelog - name: 💻 Execute download script & Extract Release Notes
id: download id: download
if: steps.version_check.outputs.new_version == 'true' if: steps.version_check.outputs.new_version == 'true'
run: | run: |
python3 firmware_downloader.py | tee firmware_output.txt VERSION="${{ steps.version_check.outputs.firmware_version }}"
FIRMWARE_VERSION=$(grep 'Folder: Firmware ' firmware_output.txt | awk '{print $NF}') # Exécution SANS paramètre pour que le script interroge lui-même l'API Nintendo
python3 firmware_downloader.py | tee script_output.log
echo "firmware_version=$VERSION" >> $GITHUB_OUTPUT
echo "firmware_version=$FIRMWARE_VERSION" >> $GITHUB_OUTPUT # Extraction stricte des dernières lignes générées par le script Python
sed -n '/Archive created:/,$p' script_output.log > changelog_body.txt
tail -n 4 firmware_output.txt > changelog_body.txt
- name: 📝 Prepare Release Body # Stockage sécurisé et multi-lignes du texte pour GitHub Actions
id: prepare_body EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
if: steps.version_check.outputs.new_version == 'true' echo "CHANGELOG_CONTENT<<$EOF" >> $GITHUB_ENV
uses: actions/github-script@v7 cat changelog_body.txt >> $GITHUB_ENV
with: echo "$EOF" >> $GITHUB_ENV
script: |
const fs = require('fs');
const changelogBody = fs.readFileSync('changelog_body.txt', 'utf8');
core.setOutput('release_body', changelogBody);
- name: 📦 Create Tag and Release - name: 📦 Create Tag and Release
if: steps.version_check.outputs.new_version == 'true' if: steps.version_check.outputs.new_version == 'true'
@@ -91,14 +100,10 @@ jobs:
tag_name: ${{ steps.download.outputs.firmware_version }} tag_name: ${{ steps.download.outputs.firmware_version }}
name: Firmware ${{ steps.download.outputs.firmware_version }} name: Firmware ${{ steps.download.outputs.firmware_version }}
body: | body: |
Automatic download of the official Nintendo Switch firmware version **${{ steps.download.outputs.firmware_version }}**. Automatic download of the official Nintendo Switch firmware version ${{ steps.download.outputs.firmware_version }}.
---
**Downloaded file details:**
${{ steps.prepare_body.outputs.release_body }}
Downloaded file details:
${{ env.CHANGELOG_CONTENT }}
files: | files: |
Firmware ${{ steps.download.outputs.firmware_version }}.zip Firmware ${{ steps.download.outputs.firmware_version }}.zip
env: env:

查看文件

@@ -6,6 +6,10 @@ Firmware database for a discord bot
<img width="618" height="670" alt="image" src="https://github.com/user-attachments/assets/279d9c21-0712-4ea8-927b-c6bb9789bb1b" /> <img width="618" height="670" alt="image" src="https://github.com/user-attachments/assets/279d9c21-0712-4ea8-927b-c6bb9789bb1b" />
[![Discord](https://img.shields.io/discord/454099185416011776?label=Rejoindre%20le%20Discord&logo=discord&logoColor=white&style=for-the-badge)](https://discord.sighya.fr) <br>
[![Dernière version](https://img.shields.io/github/v/release/THZoria/NX_Firmware?label=Dernière%20Version&color=05c09a&style=for-the-badge)](https://github.com/THZoria/NX_Firmware/releases/latest)
[![Téléchargements](https://img.shields.io/github/downloads/THZoria/NX_Firmware/total?label=Téléchargements&color=blue&style=for-the-badge)](https://github.com/THZoria/NX_Firmware)
# How to update a modified Switch / Under custom firmware # How to update a modified Switch / Under custom firmware
- Create a folder at the root of your microSD card and name it whatever you want. - Create a folder at the root of your microSD card and name it whatever you want.

查看文件

@@ -13,7 +13,7 @@ from os import makedirs, remove
from os.path import basename, exists, join from os.path import basename, exists, join
from configparser import ConfigParser from configparser import ConfigParser
from sys import argv from sys import argv
from zipfile import ZipFile, ZIP_DEFLATED from zipfile import ZipFile, ZIP_STORED, ZipInfo
from requests import request from requests import request
from requests.exceptions import HTTPError from requests.exceptions import HTTPError
@@ -121,15 +121,15 @@ def nin_request(method, url, headers=None):
def parse_cnmt(nca): def parse_cnmt(nca):
ncaf = basename(nca) ncaf = basename(nca)
# --- MODIFICATION CLÉ --- # --- KEY MODIFICATION ---
# Force l'utilisation de l'exécutable hactool dans le répertoire courant. # Force the use of the hactool executable in the current directory.
# Dans le workflow, hactool-linux a été renommé en hactool et rendu exécutable. # In the workflow, hactool-linux was renamed to hactool and made executable.
hactool_bin = "hactool.exe" if os.name == "nt" else "./hactool" hactool_bin = "hactool.exe" if os.name == "nt" else "./hactool"
# ----------------------- # -----------------------
cnmt_temp_dir = f"cnmt_tmp_{ncaf}" cnmt_temp_dir = f"cnmt_tmp_{ncaf}"
# Le script tente de lancer './hactool' # The script attempts to run './hactool'
run( run(
[hactool_bin, "-k", "prod.keys", nca, "--section0dir", cnmt_temp_dir], [hactool_bin, "-k", "prod.keys", nca, "--section0dir", cnmt_temp_dir],
stdout=PIPE, stderr=PIPE stdout=PIPE, stderr=PIPE
@@ -181,7 +181,7 @@ def dltitle(title_id, version, is_su=False):
except HTTPError as e: except HTTPError as e:
if e.response is not None and e.response.status_code == 404: if e.response is not None and e.response.status_code == 404:
print(f"INFO: Title {title_id} version {version} not found (404).") print(f"INFO: Title {title_id} version {version} not found (404).")
if title_id == "010000000000081B": if title_id.lower() == "010000000000081b":
sv_nca_exfat = "" sv_nca_exfat = ""
return return
raise raise
@@ -201,9 +201,9 @@ def dltitle(title_id, version, is_su=False):
dltitle(t_id, ver) dltitle(t_id, ver)
else: else:
for nca_id, nca_hash in parse_cnmt(cnmt_nca): for nca_id, nca_hash in parse_cnmt(cnmt_nca):
if title_id == "0100000000000809": if title_id.lower() == "0100000000000809":
sv_nca_fat = f"{nca_id}.nca" sv_nca_fat = f"{nca_id}.nca"
elif title_id == "010000000000081B": elif title_id.lower() == "010000000000081b":
sv_nca_exfat = f"{nca_id}.nca" sv_nca_exfat = f"{nca_id}.nca"
if nca_id not in queued_ncas: if nca_id not in queued_ncas:
@@ -217,12 +217,22 @@ def dltitle(title_id, version, is_su=False):
)) ))
def zipdir(src_dir, out_zip): def zipdir(src_dir, out_zip):
with ZipFile(out_zip, "w", compression=ZIP_DEFLATED) as zf: with ZipFile(out_zip, "w", compression=ZIP_STORED) as zf:
for root, _, files in os.walk(src_dir): for root, dirs, files in os.walk(src_dir):
for name in files: dirs.sort()
for name in sorted(files):
full = os.path.join(root, name) full = os.path.join(root, name)
rel = os.path.relpath(full, start=src_dir) rel = os.path.relpath(full, start=src_dir)
zf.write(full, arcname=rel) os.utime(full, (1780315200, 1780315200))
zinfo = ZipInfo.from_file(full, arcname=rel)
zinfo.date_time = (2026, 1, 1, 0, 0, 0)
zinfo.create_system = 0
zinfo.external_attr = 0
zinfo.compress_type = ZIP_STORED
with open(full, 'rb') as f:
zf.writestr(zinfo, f.read())
if __name__ == "__main__": if __name__ == "__main__":
if not exists("certificat.pem"): if not exists("certificat.pem"):
@@ -296,8 +306,8 @@ if __name__ == "__main__":
dlfiles(update_dls) dlfiles(update_dls)
if not sv_nca_exfat: if not sv_nca_exfat:
print("INFO: exFAT not found via meta — direct attempt 010000000000081B") print("INFO: exFAT not found via meta — direct attempt 010000000000081b")
dltitle("010000000000081B", ver_raw, is_su=False) dltitle("010000000000081b", ver_raw, is_su=False)
if sv_nca_exfat: if sv_nca_exfat:
dlfiles(update_dls) dlfiles(update_dls)
else: else:
@@ -311,13 +321,48 @@ if __name__ == "__main__":
if failed: if failed:
exit(1) exit(1)
print("\nINFO: Starting detailed verification of NCA hashes...")
hash_failed = False
for url, dirc, fname, expected_hash in update_dls:
fpath = join(dirc, fname)
if exists(fpath):
h = hashlib.sha256()
with open(fpath, "rb") as f:
for chunk in iter(lambda: f.read(1048576), b""):
h.update(chunk)
actual_hash = h.hexdigest()
if actual_hash == expected_hash:
print(f"[OK] {fname}")
print(f" -> Verified Hash: {actual_hash}")
else:
print(f"[ERROR] {fname}")
print(f" Expected : {expected_hash}")
print(f" Actual : {actual_hash}")
hash_failed = True
else:
print(f"[MISSING] {fname}")
hash_failed = True
if hash_failed:
print("\nCRITICAL: Hash verification failed for one or more files. Archive will not be created.")
exit(1)
else:
print("\nINFO: All files successfully verified against CNMT records.")
out_zip = f"{ver_dir}.zip" out_zip = f"{ver_dir}.zip"
if exists(out_zip): if exists(out_zip):
remove(out_zip) remove(out_zip)
zipdir(ver_dir, out_zip) zipdir(ver_dir, out_zip)
h = hashlib.sha256()
with open(out_zip, "rb") as f:
for chunk in iter(lambda: f.read(1048576), b""):
h.update(chunk)
zip_sha256 = h.hexdigest()
print("\nDOWNLOAD COMPLETE!") print("\nDOWNLOAD COMPLETE!")
print(f"Archive created: {out_zip}") print(f"Archive created: {out_zip}")
print(f"SystemVersion NCA FAT: {sv_nca_fat or 'Not Found'}") print(f"SystemVersion NCA FAT: {sv_nca_fat or 'Not Found'}")
print(f"SystemVersion NCA exFAT: {sv_nca_exfat or 'Not Found'}") print(f"SystemVersion NCA exFAT: {sv_nca_exfat or 'Not Found'}")
print("Verify hashes before installation!") print(f"Archive SHA256: {zip_sha256}")
print("Verify hashes before installation!")