Last active
October 11, 2024 17:57
-
-
Save TuxSH/97dd54d279e45f6fc7759334aa63d92b to your computer and use it in GitHub Desktop.
Luma3DS SD card prep/update script (.3dsx instead of .cia, can act as updater, doesn't download exploits)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import requests | |
import zipfile | |
import io | |
import sys | |
# Global variable containing repositories and settings for download | |
REPOSITORIES = [ | |
{ | |
"repo": "LumaTeam/Luma3DS", | |
"output_subfolder": "", # Extract directly to the root folder | |
"extract": True, | |
"file_filter": None # No filter, extract all | |
}, | |
{ | |
"repo": "astronautlevel2/Anemone3DS", | |
"output_subfolder": "3ds", | |
"extract": False, | |
"file_filter": lambda name: name.lower() == "anemone3ds.3dsx" | |
}, | |
{ | |
"repo": "mtheall/ftpd", | |
"output_subfolder": "3ds", | |
"extract": False, | |
"file_filter": lambda name: name.lower() == "ftpd.3dsx" | |
}, | |
{ | |
"repo": "BernardoGiordano/Checkpoint", | |
"output_subfolder": "3ds", | |
"extract": False, | |
"file_filter": lambda name: name.lower() == "checkpoint.3dsx", | |
"release_tag": "v3.7.4" # Use this specific release instead of the latest | |
}, | |
{ | |
"repo": "d0k3/GodMode9", | |
"output_subfolder": "", # Handle extraction separately | |
"extract": False, | |
"file_filter": None, | |
"is_archive": True # Indicates that this repo has an archive to be extracted | |
} | |
] | |
def download_release(repo, output_folder, file_filter=None, extract=False, release_tag=None, is_archive=False): | |
user, repo_name = repo.split('/') | |
if release_tag: | |
url = f"https://api.github.com/repos/{user}/{repo_name}/releases/tags/{release_tag}" | |
else: | |
url = f"https://api.github.com/repos/{user}/{repo_name}/releases/latest" | |
response = requests.get(url) | |
if response.status_code != 200: | |
raise Exception(f"Failed to fetch release information for {user}/{repo_name} (status code {response.status_code})") | |
release_data = response.json() | |
tag_name = release_data.get('tag_name', 'Unknown') | |
#print(f"Fetched release version {tag_name} for {repo_name}") | |
assets = release_data.get('assets', []) | |
for asset in assets: | |
download_url = asset['browser_download_url'] | |
file_name = asset['name'] | |
if file_filter and not file_filter(file_name): | |
continue | |
print(f"Downloading {file_name} ({tag_name}) from {user}/{repo_name}...") | |
download_response = requests.get(download_url) | |
if download_response.status_code == 200: | |
if is_archive and file_name.endswith('.zip'): | |
print(f"Extracting {file_name}...") | |
with zipfile.ZipFile(io.BytesIO(download_response.content)) as z: | |
# Extract gm9 folder to the root of the output folder | |
for member in z.namelist(): | |
if member.startswith('gm9/'): | |
z.extract(member, output_folder) | |
# Extract GodMode9.firm to output/luma/payloads | |
firm_path = 'GodMode9.firm' | |
if firm_path in z.namelist(): | |
firm_output_folder = os.path.join(output_folder, 'luma', 'payloads') | |
os.makedirs(firm_output_folder, exist_ok=True) | |
z.extract(firm_path, firm_output_folder) | |
else: | |
# Handle non-archive files | |
if extract and file_name.endswith('.zip'): | |
print(f"Extracting {file_name}...") | |
with zipfile.ZipFile(io.BytesIO(download_response.content)) as z: | |
z.extractall(output_folder) | |
else: | |
file_path = os.path.join(output_folder, file_name) | |
with open(file_path, 'wb') as file: | |
file.write(download_response.content) | |
print(f"Saved {file_name} to {file_path}") | |
return True | |
return False | |
def main(output_folder): | |
# Ensure the output folder exists | |
os.makedirs(output_folder, exist_ok=True) | |
# Iterate through repositories and process each | |
for repo_info in REPOSITORIES: | |
repo = repo_info["repo"] | |
output_subfolder = repo_info["output_subfolder"] | |
extract = repo_info["extract"] | |
file_filter = repo_info.get("file_filter", None) | |
release_tag = repo_info.get("release_tag", None) | |
is_archive = repo_info.get("is_archive", False) | |
# Determine the folder to save files (root or subfolder) | |
repo_output_folder = os.path.join(output_folder, output_subfolder) if output_subfolder else output_folder | |
os.makedirs(repo_output_folder, exist_ok=True) | |
# Download the specified release or the latest release | |
download_release(repo, repo_output_folder, file_filter, extract, release_tag, is_archive) | |
if __name__ == "__main__": | |
if len(sys.argv) < 2: | |
print("Usage: python script.py <output_folder>") | |
sys.exit(1) | |
output_folder = sys.argv[1] | |
main(output_folder) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment