mirror of
https://github.com/hacks-guide/MSET9.git
synced 2026-05-03 14:40:28 +00:00
@@ -10,5 +10,10 @@ Error 09: Could not change back into SD directory. Ensure the SD Card has been r
|
||||
Error 10: Database problem. Follow the troubleshooting to reset the DBs.
|
||||
Error 11: Running as MacOS. MacOS is not supported.
|
||||
Error 12: Multiple ID1s, follow MSET9 Troublshooting page.
|
||||
Error 13: Device doesn't exist.
|
||||
Error 14: Can't open device.
|
||||
Error 15: Not FAT32 formatted or corrupted filesystem.
|
||||
Error 16: Unable to umount SD card.
|
||||
Error 17: Root privilege is required.
|
||||
|
||||
MSET9 Troublshooting Page: https://3ds.hacks.guide/troubleshooting#installing-boot9strap-mset9
|
||||
MSET9 Troublshooting Page: https://3ds.hacks.guide/troubleshooting#installing-boot9strap-mset9
|
||||
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
if which python3 >/dev/null; then
|
||||
# use exec here to release shell and thus sd card, allow it to be umounted
|
||||
exec python3 "$(cd "$(dirname "$0")" && pwd)/mset9.py"
|
||||
else
|
||||
echo "Python 3 is not installed."
|
||||
echo "Please install Python 3 and try again."
|
||||
echo "https://www.python.org/downloads/"
|
||||
echo "Press ENTER to exit ..."
|
||||
read DUMMY
|
||||
fi
|
||||
+458
-89
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python3
|
||||
import os, platform, time, shutil, binascii
|
||||
import abc, os, platform, time, binascii
|
||||
|
||||
VERSION = "v1.1"
|
||||
VERSION = "v1.2"
|
||||
|
||||
def prgood(content):
|
||||
print(f"[\033[0;32m✓\033[0m] {content}")
|
||||
@@ -12,16 +12,404 @@ def prbad(content):
|
||||
def prinfo(content):
|
||||
print(f"[*] {content}")
|
||||
|
||||
def exitOnEnter(errCode = 0):
|
||||
def cleanup(remount=False):
|
||||
pass
|
||||
|
||||
def exitOnEnter(errCode = 0, remount=False):
|
||||
cleanup(remount)
|
||||
input("[*] Press Enter to exit...")
|
||||
exit(errCode)
|
||||
|
||||
# wrapper for fs operations. can use pyfilesystem2 directly,
|
||||
# but try to avoid extra dependency on non-darwin system
|
||||
class FSWrapper(metaclass=abc.ABCMeta):
|
||||
@abc.abstractmethod
|
||||
def exists(self, path):
|
||||
pass
|
||||
@abc.abstractmethod
|
||||
def mkdir(self, path):
|
||||
pass
|
||||
@abc.abstractmethod
|
||||
def open(self, path, mode='r'):
|
||||
pass
|
||||
@abc.abstractmethod
|
||||
def getsize(self, path):
|
||||
pass
|
||||
@abc.abstractmethod
|
||||
def remove(self, path):
|
||||
pass
|
||||
@abc.abstractmethod
|
||||
def rename(self, src, dst):
|
||||
pass
|
||||
@abc.abstractmethod
|
||||
def rmtree(self, path):
|
||||
pass
|
||||
@abc.abstractmethod
|
||||
def copytree(self, src, dst):
|
||||
pass
|
||||
@abc.abstractmethod
|
||||
def walk(self, path, topdown=False):
|
||||
pass
|
||||
@abc.abstractmethod
|
||||
def is_writable(self):
|
||||
pass
|
||||
@abc.abstractmethod
|
||||
def ensurespace(self, size):
|
||||
pass
|
||||
@abc.abstractmethod
|
||||
def close(self):
|
||||
pass
|
||||
@abc.abstractmethod
|
||||
def reload(self):
|
||||
pass
|
||||
|
||||
def remove_extra():
|
||||
pass
|
||||
|
||||
osver = platform.system()
|
||||
thisfile = os.path.abspath(__file__)
|
||||
systmp = None
|
||||
|
||||
if osver == "Darwin":
|
||||
prbad("Error 11: macOS is not supported!")
|
||||
prinfo("Please use a Windows or Linux computer.")
|
||||
exitOnEnter()
|
||||
# ======== macOS / iOS? ========
|
||||
import sys
|
||||
|
||||
tmpprefix = "mset9-macos-run-"
|
||||
|
||||
def tmp_cleanup():
|
||||
global tmpprefix, systmp
|
||||
prinfo("Removing temporary folders...")
|
||||
import tempfile, shutil
|
||||
if systmp is None:
|
||||
systmp = tempfile.gettempdir()
|
||||
for dirname in os.listdir(systmp):
|
||||
if dirname.startswith(tmpprefix):
|
||||
shutil.rmtree(f"{systmp}/{dirname}")
|
||||
prinfo("Temporary folders removed!")
|
||||
|
||||
def run_diskutil_and_wait(command, dev):
|
||||
import subprocess
|
||||
return subprocess.run(["diskutil", command, dev], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
if not thisfile.startswith("/Volumes/"):
|
||||
prbad("Error 01: Couldn't find Nintendo 3DS folder! Ensure that you are running this script from the root of the SD card.")
|
||||
# should we add some macos specific message?
|
||||
exitOnEnter()
|
||||
prinfo("Resolving device...")
|
||||
device = None
|
||||
devid = os.stat(thisfile).st_dev
|
||||
for devname in os.listdir("/dev"):
|
||||
if not devname.startswith("disk"):
|
||||
continue
|
||||
devpath = f"/dev/{devname}"
|
||||
if os.stat(devpath).st_rdev == devid:
|
||||
device = devpath
|
||||
break
|
||||
if device is None:
|
||||
#prbad("Error :")
|
||||
prbad("Can't find matching device, this shouldn't happen...")
|
||||
exitOnEnter()
|
||||
|
||||
prinfo("Finding previous temporary folder...")
|
||||
import shutil, tempfile, time
|
||||
systmp = tempfile.gettempdir()
|
||||
tmpdir = None
|
||||
for dirname in os.listdir(systmp):
|
||||
if dirname.startswith(tmpprefix):
|
||||
dirpath = f"{systmp}/{dirname}"
|
||||
script = f"{dirpath}/mset9.py"
|
||||
if os.path.exists(script) and os.stat(script).st_mtime > os.stat(thisfile).st_mtime:
|
||||
tmpdir = dirpath
|
||||
break
|
||||
else:
|
||||
shutil.rmtree(dirpath)
|
||||
if tmpdir is None:
|
||||
prinfo("Creating temporary folder...")
|
||||
tmpdir = tempfile.mkdtemp(prefix=tmpprefix)
|
||||
shutil.copyfile(thisfile, f"{tmpdir}/mset9.py")
|
||||
|
||||
prinfo("Trying to unmount SD card...")
|
||||
ret = 1
|
||||
count = 0
|
||||
while count < 10:
|
||||
ret = run_diskutil_and_wait("umount", device)
|
||||
if ret == 0:
|
||||
break
|
||||
else:
|
||||
count += 1
|
||||
time.sleep(1)
|
||||
|
||||
if ret == 1:
|
||||
prbad("Error 16: Unable to umount SD card.")
|
||||
prinfo("Please ensure there's no other app using your SD card.")
|
||||
#tmp_cleanup()
|
||||
exitOnEnter()
|
||||
|
||||
os.execlp(sys.executable, sys.executable, f"{tmpdir}/mset9.py", device)
|
||||
prbad("WTF???")
|
||||
|
||||
device = sys.argv[1]
|
||||
if len(sys.argv) == 3:
|
||||
systmp = sys.argv[2]
|
||||
if not os.path.exists(device):
|
||||
prbad("Error 13: Device doesn't exist.")
|
||||
prinfo("Ensure your SD card is inserted properly.")
|
||||
prinfo("Also, don't eject SD card itself in disk utility, unmount the partition only.")
|
||||
#tmp_cleanup()
|
||||
exitOnEnter()
|
||||
|
||||
# auto venv
|
||||
venv_path = os.path.dirname(thisfile)
|
||||
venv_bin = f"{venv_path}/bin"
|
||||
venv_py = f"{venv_bin}/python3"
|
||||
|
||||
def activate_venv():
|
||||
global venv_path, venv_bin, venv_py, device, systmp
|
||||
#import site
|
||||
os.environ["PATH"] = os.pathsep.join([venv_bin, *os.environ.get("PATH", "").split(os.pathsep)])
|
||||
os.environ["VIRTUAL_ENV"] = venv_path
|
||||
os.environ["VIRTUAL_ENV_PROMPT"] = "(mset9)"
|
||||
|
||||
if systmp is None:
|
||||
os.execlp(venv_py, venv_py, __file__, device)
|
||||
else:
|
||||
os.execlp(venv_py, venv_py, __file__, device, systmp)
|
||||
|
||||
#prev_length = len(sys.path)
|
||||
#for lib in "__LIB_FOLDERS__".split(os.pathsep):
|
||||
# path = os.path.realpath(os.path.join(venv_bin, lib))
|
||||
# site.addsitedir(path)
|
||||
# sys.path[:] = sys.path[prev_length:] + sys.path[0:prev_length]
|
||||
#sys.real_prefix = sys.prefix
|
||||
#sys.prefix = venv_path
|
||||
|
||||
if "VIRTUAL_ENV" not in os.environ and os.path.exists(venv_py):
|
||||
prinfo("venv found, activate it...")
|
||||
activate_venv()
|
||||
|
||||
try:
|
||||
from pyfatfs.PyFatFS import PyFatFS
|
||||
except ModuleNotFoundError:
|
||||
prinfo("PyFatFS not found, setting up venv for installing automatically...")
|
||||
import venv, subprocess
|
||||
venv.create(venv_path, with_pip=True)
|
||||
subprocess.run(["bin/pip", "install", "pyfatfs"], cwd=venv_path)
|
||||
activate_venv()
|
||||
|
||||
# self elevate
|
||||
if os.getuid() != 0:
|
||||
# run with osascript won't have raw disk access by default...
|
||||
# thanks for the perfect security of macos
|
||||
#args = [sys.executable, thisfile, device]
|
||||
#escaped_args = map(lambda x: f"\\\"{x}\\\"", args)
|
||||
#cmd = " ".join(escaped_args)
|
||||
#osascript = " ".join([
|
||||
# f"do shell script \"{cmd}\"",
|
||||
# "with administrator privileges",
|
||||
# "without altering line endings"
|
||||
#])
|
||||
#try:
|
||||
# os.execlp("osascript", "osascript", "-e", osascript)
|
||||
prinfo("Input the password of your computer if prompted.")
|
||||
prinfo("(It won't show anything while you're typing, just type it blindly)")
|
||||
try:
|
||||
import tempfile
|
||||
os.execlp("sudo", "sudo", sys.executable, thisfile, device, tempfile.gettempdir())
|
||||
except:
|
||||
prbad("Error 17: Root privilege is required.")
|
||||
#tmp_cleanup()
|
||||
exitOnEnter(remount=True)
|
||||
|
||||
from pyfatfs.PyFatFS import PyFatFS
|
||||
from pyfatfs.FATDirectoryEntry import FATDirectoryEntry, make_lfn_entry
|
||||
from pyfatfs.EightDotThree import EightDotThree
|
||||
from pyfatfs._exceptions import PyFATException
|
||||
import struct
|
||||
|
||||
def make_8dot3_name(dir_name, parent_dir_entry):
|
||||
dirs, files, _ = parent_dir_entry.get_entries()
|
||||
dir_entries = [e.get_short_name() for e in dirs + files]
|
||||
extsep = "."
|
||||
def map_chars(name: bytes) -> bytes:
|
||||
_name: bytes = b''
|
||||
for b in struct.unpack(f"{len(name)}c", name):
|
||||
if b == b' ':
|
||||
_name += b''
|
||||
elif ord(b) in EightDotThree.INVALID_CHARACTERS:
|
||||
_name += b'_'
|
||||
else:
|
||||
_name += b
|
||||
return _name
|
||||
dir_name = dir_name.upper()
|
||||
# Shorten to 8 chars; strip invalid characters
|
||||
basename = os.path.splitext(dir_name)[0][0:8].strip()
|
||||
if basename.isascii():
|
||||
basename = basename.encode("ascii", errors="replace")
|
||||
basename = map_chars(basename).decode("ascii")
|
||||
else:
|
||||
basename = "HAX8D3FN"
|
||||
# Shorten to 3 chars; strip invalid characters
|
||||
extname = os.path.splitext(dir_name)[1][1:4].strip()
|
||||
if basename.isascii():
|
||||
extname = extname.encode("ascii", errors="replace")
|
||||
extname = map_chars(extname).decode("ascii")
|
||||
elif len(extname) != 0:
|
||||
extname = "HAX"
|
||||
if len(extname) == 0:
|
||||
extsep = ""
|
||||
# Loop until suiting name is found
|
||||
i = 0
|
||||
while len(str(i)) + 1 <= 7:
|
||||
if i > 0:
|
||||
maxlen = 8 - (1 + len(str(i)))
|
||||
basename = f"{basename[0:maxlen]}~{i}"
|
||||
short_name = f"{basename}{extsep}{extname}"
|
||||
if short_name not in dir_entries:
|
||||
return short_name
|
||||
i += 1
|
||||
raise PyFATException("Cannot generate 8dot3 filename, "
|
||||
"unable to find suiting short file name.",
|
||||
errno=errno.EEXIST)
|
||||
EightDotThree.make_8dot3_name = staticmethod(make_8dot3_name)
|
||||
|
||||
class FatFS(FSWrapper):
|
||||
def __init__(self, device):
|
||||
self.device = device
|
||||
self.reload()
|
||||
def exists(self, path):
|
||||
return self.fs.exists(path)
|
||||
def mkdir(self, path):
|
||||
self.fs.makedir(path)
|
||||
def open(self, path, mode='r'):
|
||||
return self.fs.open(path, mode)
|
||||
def getsize(self, path):
|
||||
return self.fs.getsize(path)
|
||||
def remove(self, path):
|
||||
self.fs.remove(path)
|
||||
def rename(self, src, dst):
|
||||
srcdir, srcname = f"/{src}".rstrip("/").rsplit("/", 1)
|
||||
dstdir, dstname = f"/{dst}".rstrip("/").rsplit("/", 1)
|
||||
if srcdir == dstdir and all(not EightDotThree.is_8dot3_conform(n) for n in [srcname, dstname]):
|
||||
# cursed rename, lfn and same folder only
|
||||
pdentry = self.fs._get_dir_entry(srcdir)
|
||||
dentry = pdentry._search_entry(srcname)
|
||||
lfn_entry = make_lfn_entry(dstname, dentry.name)
|
||||
dentry.set_lfn_entry(lfn_entry)
|
||||
self.fs.fs.update_directory_entry(pdentry)
|
||||
self.fs.fs.flush_fat()
|
||||
elif self.fs.getinfo(src).is_dir:
|
||||
self.fs.movedir(src, dst, create=True)
|
||||
else:
|
||||
self.fs.move(src, dst, create=True)
|
||||
def rmtree(self, path):
|
||||
self.fs.removetree(path)
|
||||
def copytree(self, src, dst):
|
||||
self.fs.copydir(src, dst, create=True)
|
||||
def walk(self, path, topdown=False): # topdown is ignored
|
||||
for dir_path, dirs, files in self.fs.walk(path):
|
||||
yield dir_path, list(map(lambda x: x.name, dirs)), list(map(lambda x: x.name, files))
|
||||
def is_writable(self):
|
||||
try:
|
||||
with self.open("test.txt", "w") as f:
|
||||
f.write("test")
|
||||
f.close()
|
||||
self.remove("test.txt")
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
def ensurespace(self, size):
|
||||
try:
|
||||
first = self.fs.fs.allocate_bytes(size)[0]
|
||||
self.fs.fs.free_cluster_chain(first)
|
||||
return True
|
||||
except PyFATException:
|
||||
return False
|
||||
def close(self):
|
||||
try:
|
||||
self.fs.close()
|
||||
except AttributeError:
|
||||
pass
|
||||
def reload(self):
|
||||
self.close()
|
||||
self.fs = PyFatFS(filename=self.device)
|
||||
|
||||
try:
|
||||
fs = FatFS(device)
|
||||
except PyFATException as e:
|
||||
msg = str(e)
|
||||
if "Cannot open" in msg:
|
||||
prbad("Error 14: Can't open device.")
|
||||
prinfo("Please ensure your SD card is unmounted in disk utility.")
|
||||
elif "Invalid" in msg:
|
||||
prbad("Error 15: Not FAT32 formatted or corrupted filesystem.")
|
||||
prinfo("Please ensure your SD card is properly formatted")
|
||||
prinfo("Consult: https://wiki.hacks.guide/wiki/Formatting_an_SD_card")
|
||||
#tmp_cleanup()
|
||||
exitOnEnter()
|
||||
|
||||
def remove_extra():
|
||||
tmp_cleanup()
|
||||
|
||||
def cleanup(remount=False):
|
||||
global fs, device
|
||||
fs.close()
|
||||
if remount:
|
||||
prinfo("Trying to remount SD card...")
|
||||
run_diskutil_and_wait("mount", device)
|
||||
#tmp_cleanup()
|
||||
|
||||
|
||||
else:
|
||||
# ======== Windows / Linux ========
|
||||
import shutil
|
||||
|
||||
class OSFS(FSWrapper):
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.reload()
|
||||
def abs(self, path):
|
||||
return os.path.join(self.root, path)
|
||||
def exists(self, path):
|
||||
return os.path.exists(self.abs(path))
|
||||
def mkdir(self, path):
|
||||
os.mkdir(self.abs(path))
|
||||
def open(self, path, mode='r'):
|
||||
return open(self.abs(path), mode)
|
||||
def getsize(self, path):
|
||||
return os.path.getsize(self.abs(path))
|
||||
def remove(self, path):
|
||||
os.remove(self.abs(path))
|
||||
def rename(self, src, dst):
|
||||
os.rename(self.abs(src), self.abs(dst))
|
||||
def rmtree(self, path):
|
||||
shutil.rmtree(self.abs(path))
|
||||
def copytree(self, src, dst):
|
||||
shutil.copytree(self.abs(src), self.abs(dst))
|
||||
def walk(self, path, topdown=False):
|
||||
return os.walk(self.abs(path), topdown=topdown)
|
||||
def is_writable(self):
|
||||
writable = os.access(self.root, os.W_OK)
|
||||
try: # Bodge for windows
|
||||
with open("test.txt", "w") as f:
|
||||
f.write("test")
|
||||
f.close()
|
||||
os.remove("test.txt")
|
||||
except:
|
||||
writable = False
|
||||
return writable
|
||||
def ensurespace(self, size):
|
||||
return shutil.disk_usage(self.root).free >= size
|
||||
def close(self):
|
||||
pass
|
||||
def reload(self):
|
||||
try:
|
||||
os.chdir(self.root)
|
||||
except Exception:
|
||||
prbad("Error 09: Couldn't reapply working directory, is SD card reinserted?")
|
||||
exitOnEnter()
|
||||
|
||||
fs = OSFS(os.path.dirname(thisfile))
|
||||
|
||||
def clearScreen():
|
||||
if osver == "Windows":
|
||||
@@ -29,16 +417,8 @@ def clearScreen():
|
||||
else:
|
||||
os.system("clear")
|
||||
|
||||
cwd = os.path.dirname(os.path.abspath(__file__))
|
||||
try:
|
||||
os.chdir(cwd)
|
||||
except Exception:
|
||||
prbad("Failed to set cwd: " + cwd)
|
||||
prbad("This should pretty much never happen. Try running the script again.")
|
||||
exitOnEnter()
|
||||
|
||||
# Section: insureRoot
|
||||
if not os.path.exists("Nintendo 3DS/"):
|
||||
if not fs.exists("Nintendo 3DS/"):
|
||||
prbad("Error 01: Couldn't find Nintendo 3DS folder! Ensure that you are running this script from the root of the SD card.")
|
||||
prbad("If that doesn't work, eject the SD card, and put it back in your console. Turn it on and off again, then rerun this script.")
|
||||
prinfo(f"Current dir: {cwd}")
|
||||
@@ -46,17 +426,9 @@ if not os.path.exists("Nintendo 3DS/"):
|
||||
|
||||
# Section: sdWritable
|
||||
def writeProtectCheck():
|
||||
global fs
|
||||
prinfo("Checking if SD card is writeable...")
|
||||
writeable = os.access(cwd, os.W_OK)
|
||||
try: # Bodge for windows
|
||||
with open("test.txt", "w") as f:
|
||||
f.write("test")
|
||||
f.close()
|
||||
os.remove("test.txt")
|
||||
except:
|
||||
writeable = False
|
||||
|
||||
if not writeable:
|
||||
if not fs.is_writable():
|
||||
prbad("Error 02: Your SD card is write protected! If using a full size SD card, ensure that the lock switch is facing upwards.")
|
||||
prinfo("Visual aid: https://nintendohomebrew.com/assets/img/nhmemes/sdlock.png")
|
||||
exitOnEnter()
|
||||
@@ -65,14 +437,13 @@ def writeProtectCheck():
|
||||
|
||||
# Section: SD card free space
|
||||
# ensure 16MB free space
|
||||
freeSpace = shutil.disk_usage(cwd).free
|
||||
if freeSpace < 16777216:
|
||||
if not fs.ensurespace(16777216):
|
||||
prbad(f"Error 06: You need at least 16MB free space on your SD card, you have {(freeSpace / 1000000):.2f} bytes!")
|
||||
prinfo("Please free up some space and try again.")
|
||||
exitOnEnter()
|
||||
|
||||
clearScreen()
|
||||
print(f"MSET9 {VERSION} SETUP by zoogie and Aven")
|
||||
print(f"MSET9 {VERSION} SETUP by zoogie, Aven and DannyAAM")
|
||||
print("What is your console model and version?")
|
||||
print("Old 3DS has two shoulder buttons (L and R)")
|
||||
print("New 3DS has four shoulder buttons (L, R, ZL, ZR)")
|
||||
@@ -100,7 +471,7 @@ while 1:
|
||||
except KeyboardInterrupt:
|
||||
print()
|
||||
prgood("Goodbye!")
|
||||
exitOnEnter()
|
||||
exitOnEnter(remount=True)
|
||||
except:
|
||||
sysModelVerSelect = 42
|
||||
if sysModelVerSelect == 1:
|
||||
@@ -157,7 +528,7 @@ regionTable = {
|
||||
|
||||
homeDataPath, miiDataPath, homeHex, miiHex = "", "", 0x0, 0x0
|
||||
def sanity():
|
||||
global haxState, realId1Path, id0, id1, homeDataPath, miiDataPath, homeHex, miiHex
|
||||
global fs, haxState, realId1Path, id0, id1, homeDataPath, miiDataPath, homeHex, miiHex
|
||||
menuExtdataGood = False
|
||||
miiExtdataGood = False
|
||||
|
||||
@@ -185,15 +556,15 @@ def sanity():
|
||||
if checkTitledb or checkImportdb:
|
||||
prbad("Error 10: Database(s) malformed or missing!")
|
||||
if not (
|
||||
os.path.exists(realId1Path + "/dbs/import.db")
|
||||
or os.path.exists(realId1Path + "/dbs/title.db")
|
||||
fs.exists(realId1Path + "/dbs/import.db")
|
||||
or fs.exists(realId1Path + "/dbs/title.db")
|
||||
):
|
||||
if not os.path.exists(realId1Path + "/dbs"):
|
||||
os.mkdir(realId1Path + "/dbs")
|
||||
if not fs.exists(realId1Path + "/dbs"):
|
||||
fs.mkdir(realId1Path + "/dbs")
|
||||
if checkTitledb:
|
||||
open(realId1Path + "/dbs/title.db", "x").close()
|
||||
fs.open(realId1Path + "/dbs/title.db", "x").close()
|
||||
if checkImportdb:
|
||||
open(realId1Path + "/dbs/import.db", "x").close()
|
||||
fs.open(realId1Path + "/dbs/import.db", "x").close()
|
||||
|
||||
prinfo("Created empty databases.")
|
||||
prinfo("Please initialize the title database by navigating to System Settings -> Data Management -> Nintendo 3DS -> Software -> Reset, then rerun this script.")
|
||||
@@ -202,16 +573,16 @@ def sanity():
|
||||
else:
|
||||
prgood("Databases look good!")
|
||||
|
||||
if os.path.exists(realId1Path + "/extdata/" + trigger):
|
||||
if fs.exists(realId1Path + "/extdata/" + trigger):
|
||||
prinfo("Removing stale trigger...")
|
||||
os.remove(realId1Path + "/extdata/" + trigger)
|
||||
fs.remove(realId1Path + "/extdata/" + trigger)
|
||||
|
||||
extdataRoot = realId1Path + "/extdata/00000000"
|
||||
|
||||
prinfo("Checking for HOME Menu extdata...")
|
||||
for i in homeMenuExtdata:
|
||||
extdataRegionCheck = extdataRoot + f"/{i:08X}"
|
||||
if os.path.exists(extdataRegionCheck):
|
||||
if fs.exists(extdataRegionCheck):
|
||||
prgood(f"Detected {regionTable[i]} HOME Menu data!")
|
||||
homeHex = i
|
||||
homeDataPath = extdataRegionCheck
|
||||
@@ -228,7 +599,7 @@ def sanity():
|
||||
prinfo("Checking for Mii Maker extdata...")
|
||||
for i in miiMakerExtdata:
|
||||
extdataRegionCheck = extdataRoot + f"/{i:08X}"
|
||||
if os.path.exists(extdataRegionCheck):
|
||||
if fs.exists(extdataRegionCheck):
|
||||
prgood("Found Mii Maker data!")
|
||||
miiHex = i
|
||||
miiDataPath = extdataRegionCheck
|
||||
@@ -241,38 +612,38 @@ def sanity():
|
||||
exitOnEnter()
|
||||
|
||||
def injection():
|
||||
global realId1Path, id1
|
||||
global fs, realId1Path, id1
|
||||
|
||||
if not os.path.exists(id0 + "/" + hackedId1):
|
||||
if not fs.exists(id0 + "/" + hackedId1):
|
||||
prinfo("Creating hacked ID1...")
|
||||
hackedId1Path = id0 + "/" + hackedId1
|
||||
os.mkdir(hackedId1Path)
|
||||
os.mkdir(hackedId1Path + "/extdata")
|
||||
os.mkdir(hackedId1Path + "/extdata/00000000")
|
||||
fs.mkdir(hackedId1Path)
|
||||
fs.mkdir(hackedId1Path + "/extdata")
|
||||
fs.mkdir(hackedId1Path + "/extdata/00000000")
|
||||
else:
|
||||
prinfo("Reusing existing hacked ID1...")
|
||||
hackedId1Path = id0 + "/" + hackedId1
|
||||
|
||||
if not os.path.exists(hackedId1Path + "/dbs"):
|
||||
if not fs.exists(hackedId1Path + "/dbs"):
|
||||
prinfo("Copying databases to hacked ID1...")
|
||||
shutil.copytree(realId1Path + "/dbs", hackedId1Path + "/dbs")
|
||||
fs.copytree(realId1Path + "/dbs", hackedId1Path + "/dbs")
|
||||
|
||||
prinfo("Copying extdata to hacked ID1...")
|
||||
if not os.path.exists(hackedId1Path + f"/extdata/00000000/{homeHex:08X}"):
|
||||
shutil.copytree(homeDataPath, hackedId1Path + f"/extdata/00000000/{homeHex:08X}")
|
||||
if not os.path.exists(hackedId1Path + f"/extdata/00000000/{miiHex:08X}"):
|
||||
shutil.copytree(miiDataPath, hackedId1Path + f"/extdata/00000000/{miiHex:08X}")
|
||||
if not fs.exists(hackedId1Path + f"/extdata/00000000/{homeHex:08X}"):
|
||||
fs.copytree(homeDataPath, hackedId1Path + f"/extdata/00000000/{homeHex:08X}")
|
||||
if not fs.exists(hackedId1Path + f"/extdata/00000000/{miiHex:08X}"):
|
||||
fs.copytree(miiDataPath, hackedId1Path + f"/extdata/00000000/{miiHex:08X}")
|
||||
|
||||
prinfo("Injecting trigger file...")
|
||||
triggerFilePath = id0 + "/" + hackedId1 + "/extdata/" + trigger
|
||||
if not os.path.exists(triggerFilePath):
|
||||
with open(triggerFilePath, "w") as f:
|
||||
if not fs.exists(triggerFilePath):
|
||||
with fs.open(triggerFilePath, "w") as f:
|
||||
f.write("plz be haxxed mister arm9, thx")
|
||||
f.close()
|
||||
|
||||
if os.path.exists(realId1Path) and realId1BackupTag not in realId1Path:
|
||||
if fs.exists(realId1Path) and realId1BackupTag not in realId1Path:
|
||||
prinfo("Backing up real ID1...")
|
||||
os.rename(realId1Path, realId1Path + realId1BackupTag)
|
||||
fs.rename(realId1Path, realId1Path + realId1BackupTag)
|
||||
id1 += realId1BackupTag
|
||||
realId1Path = f"{id0}/{id1}"
|
||||
else:
|
||||
@@ -282,12 +653,12 @@ def injection():
|
||||
prgood("MSET9 successfully injected!")
|
||||
|
||||
def remove():
|
||||
global realId1Path, id0, id1
|
||||
global fs, realId1Path, id0, id1
|
||||
prinfo("Removing MSET9...")
|
||||
|
||||
if os.path.exists(realId1Path) and realId1BackupTag in realId1Path:
|
||||
if fs.exists(realId1Path) and realId1BackupTag in realId1Path:
|
||||
prinfo("Renaming original Id1...")
|
||||
os.rename(realId1Path, id0 + "/" + id1[:32])
|
||||
fs.rename(realId1Path, id0 + "/" + id1[:32])
|
||||
else:
|
||||
prgood("Nothing to remove!")
|
||||
return
|
||||
@@ -295,44 +666,44 @@ def remove():
|
||||
# print(id1_path, id1_root+"/"+id1[:32])
|
||||
for id1Index in range(1,5): # Attempt to remove *all* hacked id1s
|
||||
maybeHackedId = bytes.fromhex(encodedId1s[id1Index]).decode("utf-16le")
|
||||
if os.path.exists(id0 + "/" + maybeHackedId):
|
||||
if fs.exists(id0 + "/" + maybeHackedId):
|
||||
prinfo("Deleting hacked ID1...")
|
||||
shutil.rmtree(id0 + "/" + maybeHackedId)
|
||||
fs.rmtree(id0 + "/" + maybeHackedId)
|
||||
id1 = id1[:32]
|
||||
realId1Path = id0 + "/" + id1
|
||||
prgood("Successfully removed MSET9!")
|
||||
|
||||
def softcheck(keyfile, expectedSize = None, crc32 = None, retval = 0):
|
||||
shortname = keyfile.rsplit("/")[-1]
|
||||
if not os.path.exists(keyfile):
|
||||
prbad(f"{shortname} does not exist on SD card!")
|
||||
return retval
|
||||
elif expectedSize:
|
||||
fileSize = os.path.getsize(keyfile)
|
||||
global fs
|
||||
split = keyfile.rsplit("/", 1)
|
||||
if len(split) == 1:
|
||||
dirname = "/"
|
||||
filename = split[0]
|
||||
else:
|
||||
dirname, filename = split
|
||||
if not fs.exists(keyfile):
|
||||
keyfile = os.path.join(dirname, filename.upper()) # this is literally for b9
|
||||
if not fs.exists(keyfile):
|
||||
prbad(f"{filename} does not exist on SD card!")
|
||||
return retval
|
||||
if expectedSize:
|
||||
fileSize = fs.getsize(keyfile)
|
||||
if expectedSize != fileSize:
|
||||
prbad(f"{shortname} is size {fileSize:,} bytes, not expected {expectedSize:,} bytes")
|
||||
prbad(f"{filename} is size {fileSize:,} bytes, not expected {expectedSize:,} bytes")
|
||||
return retval
|
||||
elif crc32:
|
||||
with open(keyfile, "rb") as f:
|
||||
with fs.open(keyfile, "rb") as f:
|
||||
checksum = binascii.crc32(f.read())
|
||||
if crc32 != checksum:
|
||||
prbad(f"{shortname} was not recognized as the correct file")
|
||||
prbad(f"{filename} was not recognized as the correct file")
|
||||
f.close()
|
||||
return retval
|
||||
f.close()
|
||||
prgood(f"{shortname} looks good!")
|
||||
prgood(f"{filename} looks good!")
|
||||
return 0
|
||||
|
||||
def reapplyWorkingDir():
|
||||
try:
|
||||
os.chdir(cwd)
|
||||
return True
|
||||
except Exception:
|
||||
prbad("Error 09: Couldn't reapply working directory, is SD card reinserted?")
|
||||
return False
|
||||
|
||||
# Section: sdwalk
|
||||
for root, dirs, files in os.walk("Nintendo 3DS/", topdown=True):
|
||||
for root, dirs, files in fs.walk("Nintendo 3DS/", topdown=True):
|
||||
|
||||
for name in dirs:
|
||||
# If the name doesn't contain sdmc (Ignores MSET9 exploit folder)
|
||||
@@ -345,7 +716,7 @@ for root, dirs, files in os.walk("Nintendo 3DS/", topdown=True):
|
||||
if type(hexVerify) is int:
|
||||
# Check if the folder (which is either id1 or id0) has the extdata folder
|
||||
# if it does, it's an id1 folder
|
||||
if os.path.exists(os.path.join(root, name) + "/extdata"):
|
||||
if fs.exists(os.path.join(root, name) + "/extdata"):
|
||||
id1Count += 1
|
||||
id1 = name
|
||||
id0 = root
|
||||
@@ -383,7 +754,7 @@ if id1Count != 1:
|
||||
exitOnEnter()
|
||||
|
||||
clearScreen()
|
||||
print(f"MSET9 {VERSION} SETUP by zoogie and Aven")
|
||||
print(f"MSET9 {VERSION} SETUP by zoogie, Aven and DannyAAM")
|
||||
print(f"Using {consoleModel} {consoleFirmware}")
|
||||
|
||||
print("\n-- Please type in a number then hit return --\n")
|
||||
@@ -402,11 +773,7 @@ while 1:
|
||||
except:
|
||||
sysModelVerSelect = 42
|
||||
|
||||
try:
|
||||
os.chdir(cwd)
|
||||
except Exception:
|
||||
prbad("Error 09: Couldn't reapply working directory, is SD card reinserted?")
|
||||
exitOnEnter()
|
||||
fs.reload()
|
||||
|
||||
if sysModelVerSelect == 1:
|
||||
sanity()
|
||||
@@ -418,11 +785,13 @@ while 1:
|
||||
exitOnEnter()
|
||||
elif sysModelVerSelect == 3:
|
||||
remove()
|
||||
exitOnEnter()
|
||||
remove_extra()
|
||||
exitOnEnter(remount=True)
|
||||
elif sysModelVerSelect == 4 or "exit":
|
||||
prgood("Goodbye!")
|
||||
break
|
||||
else:
|
||||
prinfo("Invalid input, try again. Valid inputs: 1, 2, 3, 4")
|
||||
|
||||
time.sleep(2)
|
||||
cleanup(remount=True)
|
||||
prgood("Goodbye!")
|
||||
time.sleep(2)
|
||||
|
||||
Reference in New Issue
Block a user