• Hey, guest user. Hope you're enjoying GameParadise! Have you considered registering for an account? Come join us and add your take to the daily discourse.

Hacking Python - XCI to LayeredFS

 
 

admin

Chad
Staff member
85%
Joined
Jan 25, 2024
Messages
12,301
Points
38
Age
39
Location
USA
Website
gameparadise.org
Credits
206,669
Wrote this up pretty quickly to help people having issues with using hactool. [Also I realize I put this in the wrong section... If a mod would move it that would be cool... I wasn't paying attention when I posted]
Requirements
  • Windows OS
  • Python 2.7
  • Tkinter (Python library, google it)
  • hactool
  • keys.dat
Setup
  1. Put the python file, hactool, and your keys.dat (don't ask me for it) in the same directory.
  2. Run 'python xci_to_lfs.py' in cmd or powershell (while in the file directory)
  3. Use the GUI to set your TitleID and pick your XCI
  4. Wait a minute. I didn't put anything in the script to notify you when its done. For a 2GB game it takes about a minute. YMMV. The last thing it does is delete the tmp folder, so when its gone you are good to go.
  5. Copy the Atmosphere folder to the root of your SD Card
  6. Follow the rest of the tutorial found in the tutorials section [How to use LayeredFS for Backup Loading] (Can't post links)

Note that YMMV.

DOWNLOAD: https://mega.nz/#!Y6ByFbxI!3-whFid8d0UwMuDMEqrwM879d33SRZ9-GOuHwlpCkZU

FAQ
(But you just posted o_O ... Yeah but I know you are going to ask)
Q: I'm getting a Python not found error
A: Install python and make sure it is in your PATH variable. Google is your friend

Q: WhErE Do I GEt KEYS.dat
A: Find it yourself or follow the tutorial in the tutorials section

Q: Why isn't there a romfs.bin or romfs.romfs?!?
A: Folder works too

Q: Do I need to edit NPDM?
A: Nope.

Q: Game X doesnt work for Game Y!?!
A: Go check the tutorial mentioned above for a list of working redirects.

Q: WHERES THE DOWNLOAD
A: ... Read up.

Q: Why not Python 3?
A:
Python:
import sys, os
from binascii import hexlify as hx, unhexlify as uhx
from struct import pack as pk, unpack as upk

hactoolPath =

# directory = input("Input directory? ")
directory = os.path.dirname(__file__)

for root, dirs, items in os.walk(directory):
    for item in items:
        if item.endswith(".xci"):
            # TID to write
            newTID = input("New TID to write in " + item + "? ")
            
            outputFolder = root + "\\atmosphere\\titles\\" + newTID
            os.makedirs(outputFolder, exist_ok=True)
        
            # Extract the secure partition
            filePath = os.path.join(root, item)
            commandLine = hactoolPath + " -t xci " + filePath + " --securedir=" + outputFolder + " 1>nul 2>nul"
            print("Extracting " + filePath + "...")
            os.system(commandLine)
            
            # Find the biggest nca
            biggestSize = 0
            biggestNCA  = ""
            for file in os.listdir(outputFolder):
                if file.endswith(".nca"):
                    size = os.path.getsize(os.path.join(outputFolder, file))
                    if size > biggestSize:
                        biggestSize = size
                        biggestNCA  = file
            
            # Decrypt it
            filePath = os.path.join(outputFolder, biggestNCA)
            commandLine = hactoolPath + " " + filePath + " --exefsdir=" + outputFolder + "\\exefs --romfs=" + outputFolder + "\\romfs.bin 1>nul 2>nul"
            print("Decrypting " + filePath + "...")
            os.system(commandLine)
            
            # Delete NCAs
            print("Deleting NCA files...")
            for file in os.listdir(outputFolder):
                if file.endswith(".nca"):
                    os.remove(os.path.join(outputFolder, file))
            
            # Patch main.npdm
            with open(outputFolder + "\\exefs\\main.npdm", "rb+") as npdm:
                # Pack TID
                newTID = pk("<Q", int(newTID, 16))
            
                # TID offset & length
                hexa = bytearray(npdm.read())
                TIDoffset = hexa.find(b"ACI0") + 0x10
                TIDlength = 0x8
                
                # Replace TID
                print("Writing new TID...")
                hexa[TIDoffset:TIDoffset+TIDlength] = newTID
                npdm.seek(0)
                npdm.write(hexa)
                print("Done\n")


Changelog
V1.0.2 -- Made the files extract into game folders (same name as .xci) for use with Game Redirector (PyNX)
V1.0.1 -- Added notifications, changed the NPDM writing to not use hardcoded offsets
V1.0.0 -- Rough script. Needs cleaned and organized but seems to work as a POC for now.
 
This Python script converts XCI (Nintendo Switch game cartridge image) files into LayeredFS format, allowing users to load game mods and additional content easily. By parsing the XCI file structure and extracting the necessary data, it creates a LayeredFS-compatible directory structure, enabling seamless integration of modifications into the game. This tool enhances the customization and flexibility of Nintendo Switch gaming experiences.
 
 

Recent Content

Newest Downloads

Tutorials

Back
Top