Merge pull request #15 from danny8376/macos

Add macOS support
This commit is contained in:
zoogie
2023-10-31 23:51:15 -05:00
committed by GitHub
3 changed files with 475 additions and 90 deletions
+6 -1
View File
@@ -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
+11
View File
@@ -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
View File
@@ -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)