Compare commits

..

38 Commits
5.0.2 ... main

Author SHA1 Message Date
Zoria
c891cd56f6 Update cron schedule for firmware download
Change cron schedule to run hourly instead of every 5 minutes.
2025-12-07 21:51:36 +01:00
Zoria
1bd96a9bd6 Enable cron schedule for firmware auto downloader
Uncommented the schedule section to enable cron jobs for firmware downloading.
2025-12-07 17:33:30 +01:00
Zoria
23ac63ef72 Update tag check 2025-12-07 17:32:14 +01:00
Zoria
f2366a5ab6 Disable scheduled firmware download job
Comment out the scheduled cron job for firmware downloads.
2025-12-07 17:05:09 +01:00
Zoria
48eb6fa3c7 Adding an automatic download workflow 2025-12-07 13:02:41 +01:00
Zoria
8517028139 Firmware auto-download script 2025-12-07 12:57:55 +01:00
Zoria
d857e50ef5 update firmware database bot 2025-11-27 21:18:21 +01:00
Zoria
09c28060e7 Update guide update 2025-10-14 20:25:13 +02:00
Zoria
9626f410ec Update guide 2025-10-14 20:24:47 +02:00
Zoria
74bc192af8 update firmware database bot 2025-09-02 08:24:13 +02:00
Zoria
c202153ad8 update firmware database bot 2025-04-30 10:26:56 +02:00
Zoria
0b5ca628ca Add files via upload 2024-12-28 21:33:15 +01:00
Zoria
7fd352ded7 update firmware database bot
add firmware 19.0.1 update on database bot
2024-10-29 07:13:53 +01:00
Zoria
ccec27e625 update firmware database bot
add firmware 19.0.0 update on database bot
2024-10-08 22:18:53 +02:00
Zoria
226213a456 update firmware database bot
add firmware 18.1.0  rebootless update on database bot
2024-07-03 00:01:05 +02:00
Zoria
c2ff465f58 update firmware database bot
add firmware 18.1.0 on database bot
2024-06-17 23:21:01 +02:00
Zoria
877b9d125d update firmware database bot 2024-05-06 17:43:21 +02:00
Zoria
e7e2b68027 update firmware database bot 2024-03-26 16:09:23 +01:00
Zoria
bc739f042c update firmware database bot 2024-02-20 08:56:35 +01:00
Zoria
e55c320e11 update preview firmware 2023-05-04 09:01:16 +02:00
Zoria
c2160d3a08 update firmware database bot 2022-06-14 09:09:44 +02:00
Zoria
c2fbae6b08 update firmware database bot 2022-04-05 06:46:24 +02:00
Zoria
8e7f1b3e8b Fix link redirect bot Poyo 2022-03-22 14:33:30 +01:00
Zoria
9af5f4dda4 update firmware database bot 2022-03-22 07:11:33 +01:00
Zoria
5e45d24b85 fix link redirect bot 2022-03-01 09:52:14 +01:00
Zoria
6d8b3b082c Update README.md 2022-03-01 09:49:25 +01:00
Zoria
f4c179b9ca add bot link 2022-03-01 09:48:18 +01:00
Zoria
641c736955 update 13.2.1 and update poyo command 2022-03-01 09:41:13 +01:00
Zoria
4658233e67 Update README.md 2021-12-01 09:01:57 +01:00
Zoria
d0222452eb readme update for Firmware 13.1.0 2021-10-26 11:46:07 +02:00
Zoria
49eb0b00bb Readme update for firmware 13.0.0 2021-09-16 09:48:42 +02:00
THZoria
a5e8b73705 Update README.md 2021-07-06 06:50:07 +02:00
THZoria
a329de0df8 Update README.md 2021-07-03 08:14:42 +02:00
THZoria
60bc47e038 Update README.md 2021-07-02 18:18:46 +02:00
THZoria
da533782cd Delete changelog.md 2021-07-02 18:17:58 +02:00
THZoria
96e7460df0 Update changelog.md 2021-07-02 18:17:53 +02:00
THZoria
5aea9ea32d Update changelog.md 2021-07-02 18:16:54 +02:00
THZoria
6cf91a42be Create changelog.md 2021-07-02 18:15:06 +02:00
8 changed files with 735 additions and 1 deletions

105
.github/workflows/firmware-autodl.yml vendored Normal file
View File

@@ -0,0 +1,105 @@
name: 🎮 Firmware Auto Downloader
on:
schedule:
- cron: '0 * * * *'
workflow_dispatch:
jobs:
download_and_release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: ⬇️ Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 🐍 Setup Python and dependencies
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: ⚙️ Install required Python modules
run: |
pip install requests anynet beautifulsoup4
- name: ⬇️ Setup hactool-linux
run: |
cp hactool-linux hactool
chmod +x hactool
- name: 🔍 Check firmware version (Switch 1 only)
id: version_check
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
LATEST_VERSION=$(echo "$LATEST_TITLE" | grep -oP 'Switch \K[0-9.]+')
if [ -z "$LATEST_VERSION" ]; then
echo "::error::Failed to parse LATEST_VERSION from title: $LATEST_TITLE"
exit 1
fi
TAG_EXISTS=$(git ls-remote --tags origin $LATEST_VERSION)
if [ ! -z "$TAG_EXISTS" ]; then
echo "INFO: Tag $LATEST_VERSION already exists on GitHub. Stopping workflow to avoid re-upload."
echo "new_version=false" >> $GITHUB_OUTPUT
else
echo "INFO: New version $LATEST_VERSION found! Preparing to download..."
echo "new_version=true" >> $GITHUB_OUTPUT
fi
shell: bash
- name: 💻 Execute download script and capture changelog
id: download
if: steps.version_check.outputs.new_version == 'true'
run: |
python3 firmware_downloader.py | tee firmware_output.txt
FIRMWARE_VERSION=$(grep 'Folder: Firmware ' firmware_output.txt | awk '{print $NF}')
echo "firmware_version=$FIRMWARE_VERSION" >> $GITHUB_OUTPUT
tail -n 4 firmware_output.txt > changelog_body.txt
- name: 📝 Prepare Release Body
id: prepare_body
if: steps.version_check.outputs.new_version == 'true'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const changelogBody = fs.readFileSync('changelog_body.txt', 'utf8');
core.setOutput('release_body', changelogBody);
- name: 📦 Create Tag and Release
if: steps.version_check.outputs.new_version == 'true'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.download.outputs.firmware_version }}
name: Firmware ${{ steps.download.outputs.firmware_version }}
body: |
Automatic download of the official Nintendo Switch firmware version **${{ steps.download.outputs.firmware_version }}**.
---
**Downloaded file details:**
${{ steps.prepare_body.outputs.release_body }}
files: |
Firmware ${{ steps.download.outputs.firmware_version }}.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

BIN
Atmosphere-config.zip Normal file

Binary file not shown.

BIN
PRODINFO.bin Normal file

Binary file not shown.

View File

@@ -4,9 +4,31 @@ Firmware for the Nintendo Switch
# Utility
Firmware database for a discord bot
![image](https://user-images.githubusercontent.com/50277488/124234780-c5d79180-db14-11eb-8e18-2b9bf9309de6.png)
<img width="787" height="819" alt="image" src="https://github.com/user-attachments/assets/e338a3f5-e54b-4ae0-be8c-524944f1855b" />
# 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.
- Download and extract a Nintendo Switch update available from the links above into the folder you created : [Latest Firmware](https://github.com/THZoria/NX_Firmware/releases/latest)
- Eject your microSD card and start your console in CFW.
- Go to the HBMenu and launch DayBreak.
- Click Install.
- Select the folder you created earlier.
- Wait a few moments (DayBreak is checking the integrity of your files).
- Click Continue, then Preserve Settings, then Install (FAT32+exFAT), then Continue again.
- Wait a few moments (DayBreak is installing your update) and finally, click Reboot to restart your Nintendo Switch.
# More information
More information will be detailed in the [wiki](https://github.com/THZoria/NX_Firmware/wiki), both the new versions that will be released, as well as their technical details.
# To find us
[![Discord](https://img.shields.io/discord/643436008452521984.svg?logo=discord&logoColor=white&label=Discord&color=7289DA
)](https://discord.gg/6zRbG3FsJH)
# Add our bot Poyo
Our bot, currently developed in Discord JS V13 (click to be redirected to the link to add the bot)
[![poyo](https://user-images.githubusercontent.com/50277488/156135958-a87fadb8-841e-4eec-bfb8-32340417fa17.png)](https://discord.com/oauth2/authorize?client_id=854048178907512884&scope=bot&code=GhN3fCiOkdvULwgGFbPp134oJo1FW5&guild_id=55540872135914291520applications.commands)

62
certificat.pem Normal file
View File

@@ -0,0 +1,62 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCq7oPfY/FW1NSU
6DOo4Pr+6/zgJNop9Eg1zps1vCaWpQKu0KzrqfT2uHrRcP/mRaCSwNiqXxM3EwDb
cxerRc3dl6iHgCaPtd0QOzE5eRCuJcfTpdfhMRnzv6zJ+3xPo4ejTRzfoEB2s2XP
pAE9vh0f9FbgP06eKA6U9i0ZNucAHm8z2l52HVBjiG5/VbY43guxlU0b4U96bG0d
hH+zogPJNFcAI28R6+D2YaF0O6QfmF8SYgiD2Y94lx5cMQkIMO9Ypbh4BYlElLSP
6bRcmIqeiM9+QUDlrMd2mVTW362qsXiv6zpxIqRxZ9f9LwKyMo3kFA4f+Xjd6l2Z
xIJB0HMXAgMBAAECggEAMTKrdC3A9fwWH7HhxJbUx3DG/QC+ZlrwOcR4ufOoq61c
41Ieo0nsvuKEPyomDXI28GZfNlJdJnnqwj8TLfHOehw/npiNAMoYkaZn2aH14hnv
tQRD6YYHcTfXFN+0fPuVe7QjPl5Sj5e9ExGiZdIFcgASzUKLb0waPlfIzTwXKtoe
enAOEbCJhyxDbZBLc8qusfLU/zopQLw+u0DXMjppvn7mVtQ1n/9XuMGaXo77oedi
zVDv9XWz3DA+hZESRHb22Vgm6RjD6QG6L13G+6oyKVfcjSL0yjOtMtYQ1uLYC0W/
xVrrwcai9rH4k+DwQbHopSCkZRoHnhgOxgz0hlr7IQKBgQDqJ4m1N7MGiKK5nZZU
TsS9F77SUiiVfU0C7GvmhM6DZwz9St0cVxpfIhDaWO6ka82an6HLzEMuI+zAR3OY
vl0y8d1a9LalHSzfibCSuG+Q3p6m7VgljrysRZ6pFJgFkkg/vgBEXe3FV752DPqW
+pZfL5I+K4+1+M+U1tRgvDxK0QKBgQC64PzLsR0XMkmLU/8gtzCvJ2U50fiPf2cb
YATKmJE+3/QC8uw6pqpDFjbsGbjBGSvVxQL297Q4PNGiR9QsV4t2ldXPrGFLTP14
JylspSlz9D8JTBiRRi8uak1CF26bnxyD95FYyPOHLeXQHTbt/tTP1uutRPRYGUqn
ITWp6V0JZwKBgQDdas3ZQvZTM1lM1CuXRXTcqluTXoVp9ragERhnPyZZM1UhH22+
os8ZLolpL7daW2Zs1I6WvGy6cNYrbGVvBnPy/Ksom0crE0+fggAqfaViBGFZ0A4O
Vc8ryBmkIQkwG4eeXvi5rWhtt+8uik9b/VG8aKuNhnu6w7DnOc5SOy14QQKBgCHg
8dNXi4eOBUkt9B2ROFE4miBOUXpqRV3wHU5BNGuqnAkA0xSa7Fj3KQPKcfUHxCuf
6LavF0vusq5xySQ0xsK7zSQ2AvlsvrLOnrLkikxMJjVgRz/m8ly+xrzJlLK/uKKv
+fiXjZafGn+NHE0vGpOHSfiCfK2QADmmSp1AAi7XAoGBAPy0SGEVy/Wzvhoc7vBJ
C8vQtIJoQnunSEVqH2BRnu1eXncSvgavzeQGjil8M3Kii+TwsOu1ssCYh5gZIOJD
exm6/gwcho3m6mAamGyl2W8c7LwsNVAG7xPQuV4pzn++dwnpdbCGW3jMNiAW1IY7
bk1HW2gdAFiFiA+6owV5Vr2z
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIF5jCCBM6gAwIBAgIRAI1H0xvyAM36HGTTZcv/NTUwDQYJKoZIhvcNAQELBQAw
ZjELMAkGA1UEBhMCSlAxDjAMBgNVBAgTBUt5b3RvMQ4wDAYDVQQHEwVLeW90bzEa
MBgGA1UEChMRTmludGVuZG8gQ28uLEx0ZC4xGzAZBgNVBAMTEk5pbnRlbmRvTlhD
QTJQcm9kMTAeFw0xNjEwMDUxMDQ3NTZaFw00OTEyMDgxMjAwMDBaMIGAMQswCQYD
VQQGEwJKUDEOMAwGA1UECAwFS3lvdG8xDjAMBgNVBAcMBUt5b3RvMRowGAYDVQQK
DBFOaW50ZW5kbyBDby4sTHRkLjE1MDMGA1UEAwwsTlggUHJvZCAxIC0gOEQ0N0Qz
MUJGMjAwQ0RGQTFDNjREMzY1Q0JGRjM1MzUwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQCq7oPfY/FW1NSU6DOo4Pr+6/zgJNop9Eg1zps1vCaWpQKu0Kzr
qfT2uHrRcP/mRaCSwNiqXxM3EwDbcxerRc3dl6iHgCaPtd0QOzE5eRCuJcfTpdfh
MRnzv6zJ+3xPo4ejTRzfoEB2s2XPpAE9vh0f9FbgP06eKA6U9i0ZNucAHm8z2l52
HVBjiG5/VbY43guxlU0b4U96bG0dhH+zogPJNFcAI28R6+D2YaF0O6QfmF8SYgiD
2Y94lx5cMQkIMO9Ypbh4BYlElLSP6bRcmIqeiM9+QUDlrMd2mVTW362qsXiv6zpx
IqRxZ9f9LwKyMo3kFA4f+Xjd6l2ZxIJB0HMXAgMBAAGjggJyMIICbjAMBgNVHRMB
Af8EAjAAMB0GA1UdDgQWBBRpZWqkbnQ91sVY3bFPUPrmRyrnSjAfBgNVHSMEGDAW
gBRycQS2kVZUFTuZ1q/6AzD0gAzZBjCCAhwGA1UdEQSCAhMwggIPoIICCwYBaaCC
AgQEggIAM0EwOEUzRjMzQzU5MEQ4NDdBMkY3MDlBNjIyNjg1OTgwQTc2RTEzNEE5
MjJBQzZFRTIxODcyRkY2MDQyNDJGQ0E3ODdGQjQxMjgzQzE0OEM4MDAyNkY3NTVG
RDk4MTJBNjYzQjNGOUI5MEE0MDVFMzFENjA2MDBFNDE4NjU5MDJBQjY0RTRDRUZE
NkM3REEyNUYwMzJEMDQ0Qjc3NjE3ODY3NjcwMzE5RTEwNjQ5NjFFNDIyNzkwRjUx
N0VBNjY3MEEwRDYyODlFRUQyQkEwNzg3QjJBMUNFNkI5MzU1ODFGRjM5MTA5NEVC
QzQwOEMxQTVCQ0EzMEI4MEIzQzRFNzEyRUZDMEZCRDMwNTRFMDhFOTA2NTlBNzMz
NzhCNDkxQUY2QzVDODYwRUFCOTUxRUYzOTJCOTU0N0RCRDQ0RTZEN0U3QjhCRDA1
NDJBQUMyMjExRjQ5ODBCQzFEQjA1MzY0QTQ3NTZCMzhCRTQyNjYyNEFGRkZGRjlF
MjlDNTdGNjU4QUZEOUJDMzE5OTM1OTJCNEVFNkI0MDE2RjUxQ0QxMkUxNEYwMDhC
RDI1M0M2RENENjIwQkVGODBDMzU1NzVCOEVCNjkxNjgzQzJFMEY1ODBGQThGODc2
Mzc3N0U0RTE3NEFFODEwRUQyMDlDQzM1QTJDRkYxMzhCRjJCQUIwDQYJKoZIhvcN
AQELBQADggEBALe6QJ2aSDMtiN7e6WPmDmKgpFquQslvdIwA+To2PLMOqr6QcgdK
429ZGCWQqnQDW4+R0dE5bl9S/BkbGvkJ9URr/zZLu73mfx19ZnMKOCSv7XXR+80c
8Bhd3QxVocENmQJdFEFWohLLBfzytsH2I0Pwet9aIFDWc7zxUMSkbv7EPICMCkP3
Gr3tWt3sJs4cpwu+62xubwr2O+G61ZD+TohulzfTKW8/8jiLPFkT2uAeqfQvkcBm
06IGCz0S9dRgZwK5CTBnW0W7MO+6pfXLWXSSoX92j31xhV6hwRhrOh5sF6zHGyAq
MA3jCo/qvrzZdFdg6A28DEPZQkTxIvx6uqM=
-----END CERTIFICATE-----

323
firmware_downloader.py Normal file
View File

@@ -0,0 +1,323 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import hashlib
import warnings
from struct import unpack
from binascii import hexlify
from glob import glob
from shutil import rmtree
from subprocess import run, PIPE
from os import makedirs, remove
from os.path import basename, exists, join
from configparser import ConfigParser
from sys import argv
from zipfile import ZipFile, ZIP_DEFLATED
from requests import request
from requests.exceptions import HTTPError
try:
from anynet import tls
except ImportError:
print("Module 'anynet' not found. Install it with: pip install anynet")
exit(1)
warnings.filterwarnings("ignore")
ENV = "lp1"
VERSION = argv[1] if len(argv) > 1 else ""
def readdata(f, addr, size):
f.seek(addr)
return f.read(size)
def utf8(s):
return s.decode("utf-8")
def sha256(s):
return hashlib.sha256(s).digest()
def readint(f, addr=None):
if addr is not None:
f.seek(addr)
return unpack("<I", f.read(4))[0]
def readshort(f, addr=None):
if addr is not None:
f.seek(addr)
return unpack("<H", f.read(2))[0]
def hexify(s):
return hexlify(s).decode("utf-8")
def ihexify(n, b):
return hex(n)[2:].zfill(b * 2)
def dlfile(url, out):
try:
run([
"aria2c", "--no-conf", "--console-log-level=error",
"--file-allocation=none", "--summary-interval=0",
"--download-result=hide",
"--certificate=keys/switch_client.crt",
"--private-key=keys/switch_client.key",
f"--header=User-Agent: {user_agent}",
"--check-certificate=false",
f"--out={out}", "-c", url
], check=True)
except FileNotFoundError:
print(f"downloading {basename(out)} via requests")
resp = request(
"GET", url,
cert=("keys/switch_client.crt", "keys/switch_client.key"),
headers={"User-Agent": user_agent},
stream=True, verify=False
)
resp.raise_for_status()
with open(out, "wb") as f:
for chunk in resp.iter_content(1024*1024):
f.write(chunk)
def dlfiles(dltable):
with open("dl.tmp", "w") as f:
for url, dirc, fname, fhash in dltable:
f.write(f"{url}\n\tout={fname}\n\tdir={dirc}\n\tchecksum=sha-256={fhash}\n")
try:
run([
"aria2c", "--no-conf", "--console-log-level=error",
"--file-allocation=none", "--summary-interval=0",
"--download-result=hide",
"--certificate=keys/switch_client.crt",
"--private-key=keys/switch_client.key",
f"--header=User-Agent: {user_agent}",
"--check-certificate=false",
"-x", "16", "-s", "16", "-i", "dl.tmp"
], check=True)
except FileNotFoundError:
for url, dirc, fname, fhash in dltable:
makedirs(dirc, exist_ok=True)
out = join(dirc, fname)
dlfile(url, out)
finally:
try:
remove("dl.tmp")
except FileNotFoundError:
pass
def nin_request(method, url, headers=None):
if headers is None:
headers = {}
headers.update({"User-Agent": user_agent})
resp = request(
method, url,
cert=("keys/switch_client.crt", "keys/switch_client.key"),
headers=headers, verify=False
)
resp.raise_for_status()
return resp
def parse_cnmt(nca):
ncaf = basename(nca)
# --- MODIFICATION CLÉ ---
# Force l'utilisation de l'exécutable hactool dans le répertoire courant.
# Dans le workflow, hactool-linux a été renommé en hactool et rendu exécutable.
hactool_bin = "hactool.exe" if os.name == "nt" else "./hactool"
# -----------------------
cnmt_temp_dir = f"cnmt_tmp_{ncaf}"
# Le script tente de lancer './hactool'
run(
[hactool_bin, "-k", "prod.keys", nca, "--section0dir", cnmt_temp_dir],
stdout=PIPE, stderr=PIPE
)
cnmt_file = glob(f"{cnmt_temp_dir}/*.cnmt")[0]
entries = []
with open(cnmt_file, "rb") as c:
c_type = readdata(c, 0xc, 1)
if c_type[0] == 0x3:
n_entries = readshort(c, 0x12)
offset = readshort(c, 0xe)
base = 0x20 + offset
for i in range(n_entries):
c.seek(base + i*0x10)
title_id = unpack("<Q", c.read(8))[0]
version = unpack("<I", c.read(4))[0]
entries.append((ihexify(title_id, 8), version))
else:
n_entries = readshort(c, 0x10)
offset = readshort(c, 0xe)
base = 0x20 + offset
for i in range(n_entries):
c.seek(base + i*0x38)
h = c.read(32)
nid = hexify(c.read(16))
entries.append((nid, hexify(h)))
rmtree(cnmt_temp_dir)
return entries
seen_titles = set()
queued_ncas = set()
def dltitle(title_id, version, is_su=False):
global update_files, update_dls, sv_nca_fat, sv_nca_exfat, seen_titles, queued_ncas, ver_string_simple
key = (title_id, version, is_su)
if key in seen_titles:
return
seen_titles.add(key)
p = "s" if is_su else "a"
try:
cnmt_id = nin_request(
"HEAD",
f"https://atumn.hac.{ENV}.d4c.nintendo.net/t/{p}/{title_id}/{version}?device_id={device_id}"
).headers["X-Nintendo-Content-ID"]
except HTTPError as e:
if e.response is not None and e.response.status_code == 404:
print(f"INFO: Title {title_id} version {version} not found (404).")
if title_id == "010000000000081B":
sv_nca_exfat = ""
return
raise
ver_dir = f"Firmware {ver_string_simple}"
makedirs(ver_dir, exist_ok=True)
cnmt_nca = f"{ver_dir}/{cnmt_id}.cnmt.nca"
update_files.append(cnmt_nca)
dlfile(
f"https://atumn.hac.{ENV}.d4c.nintendo.net/c/{p}/{cnmt_id}?device_id={device_id}",
cnmt_nca
)
if is_su:
for t_id, ver in parse_cnmt(cnmt_nca):
dltitle(t_id, ver)
else:
for nca_id, nca_hash in parse_cnmt(cnmt_nca):
if title_id == "0100000000000809":
sv_nca_fat = f"{nca_id}.nca"
elif title_id == "010000000000081B":
sv_nca_exfat = f"{nca_id}.nca"
if nca_id not in queued_ncas:
queued_ncas.add(nca_id)
update_files.append(f"{ver_dir}/{nca_id}.nca")
update_dls.append((
f"https://atumn.hac.{ENV}.d4c.nintendo.net/c/c/{nca_id}?device_id={device_id}",
ver_dir,
f"{nca_id}.nca",
nca_hash
))
def zipdir(src_dir, out_zip):
with ZipFile(out_zip, "w", compression=ZIP_DEFLATED) as zf:
for root, _, files in os.walk(src_dir):
for name in files:
full = os.path.join(root, name)
rel = os.path.relpath(full, start=src_dir)
zf.write(full, arcname=rel)
if __name__ == "__main__":
if not exists("certificat.pem"):
print("File 'certificat.pem' not found in root directory.")
exit(1)
pem_data = open("certificat.pem", "rb").read()
cert = tls.TLSCertificate.parse(pem_data, tls.TYPE_PEM)
priv = tls.TLSPrivateKey.parse(pem_data, tls.TYPE_PEM)
makedirs("keys", exist_ok=True)
cert.save("keys/switch_client.crt", tls.TYPE_PEM)
priv.save("keys/switch_client.key", tls.TYPE_PEM)
if not exists("prod.keys"):
print("File 'prod.keys' not found in root directory.")
exit(1)
prod_keys = ConfigParser(strict=False)
with open("prod.keys") as f:
prod_keys.read_string("[keys]" + f.read())
if not exists("PRODINFO.bin"):
print("File 'PRODINFO.bin' not found in root directory.")
exit(1)
with open("PRODINFO.bin", "rb") as pf:
if pf.read(4) != b"CAL0":
print("Invalid PRODINFO (invalid header)!")
exit(1)
device_id = utf8(readdata(pf, 0x2b56, 0x10))
print("Device ID:", device_id)
user_agent = f"NintendoSDK Firmware/11.0.0-0 (platform:NX; did:{device_id}; eid:{ENV})"
if VERSION == "":
print("INFO: No version specified, searching for the latest version...")
su_meta = nin_request(
"GET",
f"https://sun.hac.{ENV}.d4c.nintendo.net/v1/system_update_meta?device_id={device_id}"
).json()
ver_raw = su_meta["system_update_metas"][0]["title_version"]
ver_major = ver_raw // 0x4000000
ver_minor = (ver_raw - ver_major*0x4000000) // 0x100000
ver_sub1 = (ver_raw - ver_major*0x4000000 - ver_minor*0x100000) // 0x10000
ver_sub2 = ver_raw - ver_major*0x4000000 - ver_minor*0x100000 - ver_sub1*0x10000
ver_string_raw = f"{ver_major}.{ver_minor}.{ver_sub1}.{str(ver_sub2).zfill(4)}"
ver_string_simple = f"{ver_major}.{ver_minor}.{ver_sub1}"
else:
ver_string_simple = VERSION
parts = list(map(int, VERSION.split(".")))
if len(parts) == 3:
parts.append(0)
ver_raw = parts[0]*0x4000000 + parts[1]*0x100000 + parts[2]*0x10000 + parts[3]
ver_string_raw = f"{parts[0]}.{parts[1]}.{parts[2]}.{str(parts[3]).zfill(4)}"
ver_dir = f"Firmware {ver_string_simple}"
print(f"Downloading firmware. Internal version: {ver_string_raw}. Folder: {ver_dir}")
update_files = []
update_dls = []
sv_nca_fat = ""
sv_nca_exfat = ""
seen_titles.clear()
queued_ncas.clear()
dltitle("0100000000000816", ver_raw, is_su=True)
dlfiles(update_dls)
if not sv_nca_exfat:
print("INFO: exFAT not found via meta — direct attempt 010000000000081B…")
dltitle("010000000000081B", ver_raw, is_su=False)
if sv_nca_exfat:
dlfiles(update_dls)
else:
print("INFO: No separate SystemVersion exFAT found for this firmware version.")
failed = False
for fpath in update_files:
if not exists(fpath):
print(f"DOWNLOAD FAILED: {fpath} missing")
failed = True
if failed:
exit(1)
out_zip = f"{ver_dir}.zip"
if exists(out_zip):
remove(out_zip)
zipdir(ver_dir, out_zip)
print("\nDOWNLOAD COMPLETE!")
print(f"Archive created: {out_zip}")
print(f"SystemVersion NCA FAT: {sv_nca_fat or 'Not Found'}")
print(f"SystemVersion NCA exFAT: {sv_nca_exfat or 'Not Found'}")
print("Verify hashes before installation!")

BIN
hactool-linux Normal file

Binary file not shown.

222
prod.keys Normal file
View File

@@ -0,0 +1,222 @@
aes_kek_generation_source = 4d870986c45d20722fba1053da92e8a9
aes_key_generation_source = 89615ee05c31b6805fe58f3da24f7aa8
bis_kek_source = 34c1a0c48258f8b4fa9e5e6adafc7e4f
bis_key_00 = e4a4ffc9ed2ea374a3640c21866e3e354c13b9bc5ed8e4f3e8c23d4f84d8e891
bis_key_01 = fa75c2f34ed18a87ae43dec46dede848de9e0c761a2b67604b7de88d9bd476d9
bis_key_02 = f421b1376196944e7fd1b41b166e2dbe201165d2f480781543af1a8da83935b7
bis_key_03 = f421b1376196944e7fd1b41b166e2dbe201165d2f480781543af1a8da83935b7
bis_key_source_00 = f83f386e2cd2ca32a89ab9aa29bfc7487d92b03aa8bfdee1a74c3b6e35cb7106
bis_key_source_01 = 41003049ddccc065647a7eb41eed9c5f44424edab49dfcd98777249adc9f7ca4
bis_key_source_02 = 52c2e9eb09e3ee2932a10c1fb6a0926c4d12e14b2a474c1c09cb0359f015f4e4
device_key_4x = f94322f4b1123c1f79d0ad499b004a3e
eticket_rsa_kek = 19c8b441d318802bad63a5beda283a84
eticket_rsa_kek_personalized = d3ea417566cfc41495d24bf9c0bad036
eticket_rsa_kek_source = dba451124ca0a9836814f5ed95e3125b
eticket_rsa_kekek_source = 466e57b74a447f02f321cde58f2f5535
eticket_rsa_keypair = d277bfef675633d05bd71031e8237f68beed2b8c0f45edc2a0b1d9253cccf6d34243645e080d2b4e71a6ed51a30b090a0af317970296905c1eb2225e77d79f0e60ee00c4f84a5c6f0dc1b9d1cefb1989f68da93342504b70c67d136955b442c85de3973941e2bb360d9552bcfe031afc64d0cad85ff38ba1542984fb3ebf6772b4a0490bd1f256cd51cba8f36bb72c6384c41b38c17bd12130ccd8a6dfb0b1ecab994e496b26364e50dea3053fe2a6ec6ae5844c36347c459b4b5fb237a208692a095fbe19a0aa2c3863e8d239a3a600f73a98e731f5b55ab0511fd8370fde1cf170af80a9535564d53668549683cb63a1b8541dc194182b8e2dc5804202c091f11b1b1511946e330e6000c19077878561e9c56e1b4bddc2dbabcac80633f8092b576fb3cdfc5f5d9aa6c59e9a492e7c1ac84b9db061d09b133d0723d9467a5c0772a39a91cbacb891bd4be2ead0d79a02a3723951fd79a03d100f47644d2153a8e6e347a906ffc1e49ec9350d670bd0d5a21b98d2ba56ae62009ad076782410f1013e92e6152dfcd3ec728d8e396a431afb1bce3135a07f5e9f1dec6d85e959d7f77deed12cc6f3161149995e96bb0b17ae83b1a881826f54b5031d503fa22560c5ebed409aed738e0180e09ff4e1721e1a7c9cd91a7733d167f3d11a9fea729b27c0eefde0795c6eaf859c68cbb37ffc62300b7cedd656f2815ca71b3ae68900010001000000000000000000000000
header_kek_source = 1f12913a4acbf00d4cde3af6d523882a
header_key = aeaab1ca08adf9bef12991f369e3c567d6881e4e4a6a47a51f6e4877062d542d
header_key_source = 5a3ed84fdec0d82631f7e25d197bf5d01c9b7bfaf628183d71f64d73f150b9d2
key_area_key_application_00 = ef979e289a132c23d39c4ec5a0bba969
key_area_key_application_01 = cdedbab97b69729073dfb2440bff2c13
key_area_key_application_02 = 75716ed3b524a01dfe21456ce26c7270
key_area_key_application_03 = f428306544cf5707c25eaa8bc0583fd1
key_area_key_application_04 = 798844ec099eb6a04b26c7c728a35a4d
key_area_key_application_05 = a57c6eecc5410ada22712eb3ccbf45f1
key_area_key_application_06 = 2a60f6c4275df1770651d5891b8e73ec
key_area_key_application_07 = 32221bd6ed19b938bec06b9d36ed9e51
key_area_key_application_08 = fb20aa9e3dbf67350e86479eb431a0b3
key_area_key_application_09 = ce8d5fa79e220d5f48470e9f21be018b
key_area_key_application_0a = 38b865725adcf568a81d2db3ceaa5bcc
key_area_key_application_0b = bbddfd40a59d0ff555c0954239972213
key_area_key_application_0c = 3fee7204e21c6b0ff1373226c0c3e055
key_area_key_application_0d = 7b05d214fa554bc3e91b044fb412fc0d
key_area_key_application_0e = 061667d7668b76a423e3f1aea52a8baa
key_area_key_application_0f = 7ee19b046987ba2588e852cc24bc2953
key_area_key_application_10 = fd8a4be923d9a464793cd2f3a27557ee
key_area_key_application_11 = d8178dba2fb20ed3141612b6cb2e8e9d
key_area_key_application_12 = 56debb519556d05e8ab3ddb9a1e4c1d9
key_area_key_application_13 = 76a3a1e5963759e5c0c178a93b547880
key_area_key_application_14 = dbd99c9834f2bb2bf8dacef3f8e090d5
key_area_key_application_source = 7f59971e629f36a13098066f2144c30d
key_area_key_ocean_00 = b33813e4c9c4399c75fabc673ab4947b
key_area_key_ocean_01 = c54166efa8c9c0f6511fa8b580191677
key_area_key_ocean_02 = 3061ce73461e0b0409d6a33da85843c8
key_area_key_ocean_03 = 06f170025a64921c849df168e74d37f2
key_area_key_ocean_04 = dc857fd6dc1c6213076ec7b902ec5bb6
key_area_key_ocean_05 = 131d76b70bd8a60036d8218c15cb610f
key_area_key_ocean_06 = 17d565492ba819b0c19bed1b4297b659
key_area_key_ocean_07 = 37255186f7678324bf2b2d773ea2c412
key_area_key_ocean_08 = 4115c119b7bd8522ad63c831b6c816a6
key_area_key_ocean_09 = 792bfc652870cca7491d1685384be147
key_area_key_ocean_0a = dfcc9e87e61c9fba54a9b1c262d41e4d
key_area_key_ocean_0b = 66fe3107f5a6a8d8eda2459d920b07a1
key_area_key_ocean_0c = b79b6bf3d6cdc5ec10277fc07a4fec93
key_area_key_ocean_0d = 9a20ffbdcb03cfc5b8e88b058d27ae6c
key_area_key_ocean_0e = 1e8bba40c91ca4d55163cdfb779a2f4e
key_area_key_ocean_0f = 2a51262c614e175f22cb0bf7907418b0
key_area_key_ocean_10 = 97b66913f9683a9e7b733b96a35cabf3
key_area_key_ocean_11 = 42da6ca5bc5dc88dac81ba0729414af1
key_area_key_ocean_12 = 0a9a14c74c9f46a3e0826c6e0857d199
key_area_key_ocean_13 = f74d1295fdadce4a54d142e6f93f8f4f
key_area_key_ocean_14 = 632f252b1553707e7293ab860a1af19f
key_area_key_ocean_source = 327d36085ad1758dab4e6fbaa555d882
key_area_key_system_00 = 6dd02aa15b440d6231236b6677de86bc
key_area_key_system_01 = 4ab155e7f29a292037fd147592770b12
key_area_key_system_02 = b7a74adeaf89c2a198c327bdff322d7d
key_area_key_system_03 = d5aab1acd23a8aec284a316df859d377
key_area_key_system_04 = 9b44b45b37de9d14754b1d22c2ca742c
key_area_key_system_05 = 0012e957530d3dc7af34fbbe6fd44559
key_area_key_system_06 = 01744e3b0818445cd54ee9f89da43192
key_area_key_system_07 = d0d30e46f5695b875f11522c375c5a80
key_area_key_system_08 = bd06cb1b86bd5c433667470a09eb63de
key_area_key_system_09 = e19f788f658eda8bbf34a1dd2a9503a9
key_area_key_system_0a = 7070e7ff5cfe448630143a9874903c38
key_area_key_system_0b = 3fa471d4483e58b8f7756fcb64f63890
key_area_key_system_0c = 7bfd381df3369407ab1c6bdd9fabf522
key_area_key_system_0d = 53ed531cd657edf443b551a964f44ecc
key_area_key_system_0e = fa9d4958e8f8f2c8c8ae33b1034a0a02
key_area_key_system_0f = 91eae4eeb5335cc5a706c4fe81d8d8af
key_area_key_system_10 = ae11fa6821b123419e0a54f3a89d9a8b
key_area_key_system_11 = 6cb02ff14b6bb1145345dcbe6daaa0a9
key_area_key_system_12 = 9ba3e06e93313a726e23bd2d32c494a2
key_area_key_system_13 = 2215008857d08cba3fe69764d7adb0a4
key_area_key_system_14 = eae7d84910294d0c5e5c9b1b26b315b5
key_area_key_system_source = 8745f1bba6be79647d048ba67b5fda4a
keyblob_key_source_00 = df206f594454efdc7074483b0ded9fd3
keyblob_key_source_01 = 0c25615d684ceb421c2379ea822512ac
keyblob_key_source_02 = 337685ee884aae0ac28afd7d63c0433b
keyblob_key_source_03 = 2d1f4880edeced3e3cf248b5657df7be
keyblob_key_source_04 = bb5a01f988aff5fc6cff079e133c3980
keyblob_key_source_05 = d8cce1266a353fcc20f32d3b517de9c0
keyblob_mac_key_source = 59c7fb6fbe9bbe87656b15c0537336a5
mariko_master_kek_source_05 = 77605ad2ee6ef83c3f72e2599dac5e56
mariko_master_kek_source_06 = 1e80b8173ec060aa11be1a4aa66fe4ae
mariko_master_kek_source_07 = 940867bd0a00388411d31adbdd8df18a
mariko_master_kek_source_08 = 5c24e3b8b4f700c23cfd0ace13c3dc23
mariko_master_kek_source_09 = 8669f00987c805aeb57b4874de62a613
mariko_master_kek_source_0a = 0e440cedb436c03faa1daebf62b10982
mariko_master_kek_source_0b = e541acecd1a7d1abed0377f127caf8f1
mariko_master_kek_source_0c = 52719bdfa78b61d8d58511e48e4f74c6
mariko_master_kek_source_0d = d268c6539d94f9a8a5a8a7c88f534b7a
mariko_master_kek_source_0e = ec61bc821e0f5ac32b643f9dd619222d
mariko_master_kek_source_0f = a5ec16391a3016082ecf096f5e7ceea9
mariko_master_kek_source_10 = 8dee9e11363a9b0a6ac7bbe9d103f780
mariko_master_kek_source_11 = 4f413c3bfb6a012a689f83e953bd16d2
mariko_master_kek_source_12 = 31be25fbdbb4ee495c7705c2369f3480
mariko_master_kek_source_13 = 1a316287a809caf8691545c26baa5a8a
mariko_master_kek_source_14 = ebf35b2d4a2dce453a6f61380b003b46
master_kek_05 = 94a92da1d73c2b3e165c891ced5607fc
master_kek_06 = a6c7b7870e42d5302fe6110883aa3889
master_kek_07 = 5cf8c1d58063aff640aaa681f0ce426c
master_kek_08 = e42f1ec8002043d746575ae6dd9f283f
master_kek_09 = cec2885fbeef5f6a989db84a4cc4b393
master_kek_0a = dd1a730232522b5cb4590cd43869ab6a
master_kek_0b = fc6f0c891d42710180724ed9e112e72a
master_kek_0c = 43f7fc20fcec22a5b2a744790371b094
master_kek_0d = 8dc9a8223671daa73ccd8b93cdaaed9f
master_kek_0e = f3f857257c3f63ca63b9c9710b8f673e
master_kek_0f = 1e8f01c4927a76a66097df44c3bad27d
master_kek_10 = 8b523b9d476508daadc2036582ce5aa8
master_kek_11 = c618d3fd0ee15ffcea22bc98ad2489b5
master_kek_12 = f540a14ea2cce8d0ede62a56586bfb0e
master_kek_13 = 5ab6b61cf29dd5d8e40fbb43e459fa88
master_kek_14 = 2698651a8c2984253767669ba71b9346
master_kek_source_06 = 374b772959b4043081f6e58c6d36179a
master_kek_source_07 = 9a3ea9abfd56461c9bf6487f5cfa095c
master_kek_source_08 = dedce339308816f8ae97adec642d4141
master_kek_source_09 = 1aec11822b32387a2bedba01477e3b67
master_kek_source_0a = 303f027ed838ecd7932534b530ebca7a
master_kek_source_0b = 8467b67f1311aee6589b19af136c807a
master_kek_source_0c = 683bca54b86f9248c305768788707923
master_kek_source_0d = f013379ad56351c3b49635bc9ce87681
master_kek_source_0e = 6e7786ac830a8d3e7db766a022b76e67
master_kek_source_0f = 99220957a7f95e94fe787f41d6e756e6
master_kek_source_10 = 71b9a6c0ff976b0cb440b9d5815d8190
master_kek_source_11 = 00045df04dcd14a31cbfde4855ba35c1
master_kek_source_12 = d76374464eba780a7c9db3e87a3d71e3
master_kek_source_13 = a36b0ab56f574c5e00fd5621f5066bd1
master_kek_source_14 = 92bf37800e79568c5775720a48d81539
master_key_00 = c2caaff089b9aed55694876055271c7d
master_key_01 = 54e1b8e999c2fd16cd07b66109acaaa6
master_key_02 = 4f6b10d33072af2f250562bff06b6da3
master_key_03 = 84e04ec20b9373818c540829cf147f3d
master_key_04 = cfa2176790a53ff74974bff2af180921
master_key_05 = c1dbedcebf0dd6956079e506cfa1af6e
master_key_06 = 0aa90e6330cdc12d819b3254d11a4e1e
master_key_07 = 929f86fbfe4ef7732892bf3462511b0e
master_key_08 = 23cfb792c3cb50cd715da0f84880c877
master_key_09 = 75c93b716255319b8e03e14c19dea64e
master_key_0a = 73767484c73088f629b0eeb605f64aa6
master_key_0b = 8500b14bf4766b855a26ffc614097a8f
master_key_0c = b3c503709135d4b35de31be4b0b9c0f7
master_key_0d = 6d2b26416ab030dc504cbfd6bb2977b7
master_key_0e = 3b995e3bf23207c3cacb07f8c57415e6
master_key_0f = ff22454d86237004c750e2dcb4b16c80
master_key_10 = 252c7d95f296d07f2369bdba6d42c615
master_key_11 = 03d1d722e91bf7f2c8f3c00283bf5c6c
master_key_12 = 32ecadc8986540f930f54d159fcba88e
master_key_13 = 5146f2e7096bd2f8fa14030b883a98d0
master_key_14 = 4c9b499cc482b0fe8c6c2f698e19ff64
master_key_source = d8a2410ac6c59001c61d6a267c513f3c
package2_key_00 = a35a19cb14404b2f4460d343d178638d
package2_key_01 = a0dd1eacd438610c85a191f02c1db8a8
package2_key_02 = 7e5ba2aafd57d47a85fd4a57f2076679
package2_key_03 = bf03e9889fa18f0d7a55e8e9f684323d
package2_key_04 = 09df6e361e28eb9c96c9fa0bfc897179
package2_key_05 = 444b1a4f9035178b9b1fe262462acb8e
package2_key_06 = 442cd9c21cfb8914587dc12e8e7ed608
package2_key_07 = 70c821e7d6716feb124acbac09f7b863
package2_key_08 = 8accebcc3d15a328a48365503f8369b6
package2_key_09 = f562a7c6c42e3d4d3d13ffd504d77346
package2_key_0a = 0803167ec7fc0bc753d8330e5592a289
package2_key_0b = 341db6796aa7bdb8092f7aae6554900a
package2_key_0c = 4e97dc4225d00c6ae33d49bddd17637d
package2_key_0d = db13c2de2c313540b18a32b4f106d4a1
package2_key_0e = 254d393b26e6d98963c1c8c4fa6d11e2
package2_key_0f = 1c87f9650cca54af03df3590021e457d
package2_key_10 = 2d64ee13cece88746b375f1a43b9fdf6
package2_key_11 = 73a9680bbd12d3a05c6eddb9545c4077
package2_key_12 = 64f022a4150139a118608f55e5621c72
package2_key_13 = 56adb5ca4e65d0ce48b2d70129cb87e1
package2_key_14 = d8118afae97877041ae563eff14de93c
package2_key_source = fb8b6a9c7900c849efd24d854d30a0c7
per_console_key_source = 4f025f0eb66d110edc327d4186c2f478
retail_specific_aes_key_source = e2d6b87a119cb880e822888a46fba195
save_mac_kek_source = d89c236ec9124e43c82b038743f9cf1b
save_mac_key = d798cbbd382b7891c3c6ae909da62936
save_mac_key_source = e4cd3d4ad50f742845a487e5a063ea1f
save_mac_sd_card_kek_source = 0489ef5d326e1a59c4b7ab8c367aab17
save_mac_sd_card_key_source = 6f645947c56146f9ffa045d595332918
sd_card_custom_storage_key_source = 370c345e12e4cefe21b58e64db52af354f2ca5a3fc999a47c03ee004485b2fd0
sd_card_kek_source = 88358d9c629ba1a00147dbe0621b5432
sd_card_nca_key_source = 5841a284935b56278b8e1fc518e99f2b67c793f0f24fded075495dca006d99c2
sd_card_save_key_source = 2449b722726703a81965e6e3ea582fdd9a951517b16e8f7f1f68263152ea296a
sd_seed = 8b9a52ccb21081c5062bd5df46eef75f
ssl_rsa_kek = b011100660d1dccbad1b1b733afa9f95
ssl_rsa_kek_personalized = 57564664130531a71731195fb31069e2
ssl_rsa_kek_source = 9a383bf431d0bd8132534ba964397de3
ssl_rsa_kekek_source = 7f5bb0847b25aa67fac84be23d7b6903
ssl_rsa_key = 2a395466565d0ca12145e9a01c50432bdbda09961a7a3dd9a655dccb31663a83e5215c0ace43a046e1655f4b0903b47b4ce3ec796241b11497c12f8204946761c6f6be5fe33b61ae8440a7364990afc9ab78a74bbb2da584777e1bd9474721f9758b61d3836fdd0a15b885b4965692444dbaf3187ffa1c7f4e9d9ad05d7499f8e7add7a0854925e72248edb478b936142cdd9ab0fcd61f9a939a795e6e448513acfa9aa2841e8ddf08a70bdd25e6c0c05b4b0884e3615aa96ba8ae99f91d234cf075e7d5ee113cab36f28700957c7e8973056d35fc5dc65b7bb2fb642be88f3a235020fde51e3754fecc90cad3e32d22e93a60ab5234497b2163bd3418fefe1d
titlekek_00 = 62a24d6e6d0d0e0abf3554d259be3dc9
titlekek_01 = 8821f642176969b1a18021d2665c0111
titlekek_02 = 5d15b9b95a5739a0ac9b20f600283962
titlekek_03 = 1b3f63bcb67d4b06da5badc7d89acce1
titlekek_04 = e45c1789a69c7afbbf1a1e61f2499459
titlekek_05 = ddc67f7189f4527a37b519cb051eee21
titlekek_06 = b1532b9d38ab036068f074c0d78706ac
titlekek_07 = 81dc1b1783df268789a6a0edbf058343
titlekek_08 = 47dfe4bf0eeda88b17136b8005ab08ea
titlekek_09 = adaa785d90e1a9c182ac07bc276bf600
titlekek_0a = 42daa957c128f75bb1fda56a8387e17b
titlekek_0b = d08903363f2c8655d3de3ccf85d79406
titlekek_0c = be2682599db34caa9bc7ebb2cc7c654c
titlekek_0d = 41071f95beddc4114a03e0072e6ccab7
titlekek_0e = e342365a0fa0fa4a28a7bc00e45b3f68
titlekek_0f = 105999eaf8b71d199bf201f525b2c68d
titlekek_10 = 3796fcdb27351d58cc3f3379dda04202
titlekek_11 = b16d793f4be5394e60a6e426e172c16a
titlekek_12 = 0cd263cbddcbeca9ffa779edbe708664
titlekek_13 = 8d4d403cd8c5ec76c3d777ac55c7ef35
titlekek_14 = 2d44b28c78de6ef226d9d70e2f6362a2
titlekek_source = 1edc7b3b60e6b4d878b81715985e629b