Skip to content

Instantly share code, notes, and snippets.

@TuxSH
Created September 17, 2024 19:47
Show Gist options
  • Save TuxSH/97dd54d279e45f6fc7759334aa63d92b to your computer and use it in GitHub Desktop.
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)
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 {repo_name} (status code {response.status_code})")
release_data = response.json()
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} from {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