Tutorial  Updated

How to create Nintendo Switch games with Unity.

Note: NSPs build with a leaked SDK are not allowed to be shared on GBATemp (and most likely nowhere else too) because they were build with "stolen code". These NSPs can only be installed on hacked switches with sigpatches, so please only do projects for yourself instead of publishing them and making Nintendo mad & possibly even sue you.




Foreword:
If you are some random kid that wants to make a game for the switch without knowledge about Unity, Switch development and C/C++, stop right here. This is not meant for you and if you want to make a game that runs on the switch, use Godot. It is easy to make games with and they are legal to share. Dont expect any kind of help for making "illegal games", especially not if you cant read error messages to figure things out on your own.



Prerequisites
  • A leaked (or official) Nintendo Switch SDK from Nintendo (for example NX SDK 15.3.0) *
  • The corresponding Unity version (for example 2023.1.0b2)
  • The corresponding Unity "support for editor" addon. **
  • https://github.com/The-4n/hacPack on your harddrive and added to your PATH environment variable

* I will not provide links to any SDK, nor will i tell you where to find it. Don't even ask.
** These links, while not publicly shared, are not protected in any way by unity. If you have the link from their website, you can download them without any restriction. However i still wont provide any URLs.

The leaked SDKs that you can find online usually do not have the same folder structure. They are often incomplete, scrambled or in other ways messed up which makes it hard to write a unified guide for them. I will therefore base this guide on a 15.3.0 SDK that recently leaked. Your experience might be different and its a lot of trial and error to get it working.


Folder structure of the recently leaked SDK:
1675693698611.png




"Installing" the SDK

The complete SDK consists of many "addons" and most (if not all) of them are needed to make the build process work. You basically want to go through all folders and check if they have either a "NintendoSDK", "NintendoSDK-NEX", "NintendoSDK-NPLN" or "NintendoSDK-Pia6" subfolder and then merge them on your harddrive. Usually you will not overwrite any files except for maybe a license file. If you do this, you basically have a complete SDK with all addons installed to it. This is how it should look like on your harddrive once you are done (- the combined size is about 24GB.):
1675694161728.png



After you have extracted the necessary data, its time to add some environment variables. The folder path changes depending on where you put the SDK files obviously.
Restart your PC for them to take effect.
1675694314291.png





Installing Unity + Addon

Now you can install Unity and its "support for editor" addon. Make sure to install the correct version of both Unity and the addon, since the SDK is made for these specifically. You might get away with some versions lower and higher but there is absolutly no guarantee. Its best if you don't mix and match but choose the correct one. Usually the addon is delivered with the leaked SDK itself, which also tells you what Unity version to use.





Creating a game (or whatever)

It is highly advised that you read the documentation that comes with the SDK!

Stuff like character controller / HID and what not are all explained in the documentation. READ IT.

Once you are happy with your game (or whatever), click "File -> Build Settings" and change the Platform to Nintendo Switch. Let everything load, check the "Create NSP ROM File" checkbox and adjust all the NMETA settings in the "Player Settings" menu. I wont go over that stuff as its a lot and i am sure you can read and figure it out on your own. Click "Build" to create an NSP.

1675695144004.png






Making it installable

If you did everything correctly, you should now have an NSP file. But lets be honest, you do not own a dev unit to install the NSP on, do you?
Well, to be able to install and play your game on a retail switch, you need to repack it with your own retail unit keys. Use lockpick_rcm to get them and place them in (create if the folder does not exist):
Code:
C:\Users\<YOUR USER NAME>\.switch

You can then use hactool or any other method of your liking to repack the NSP. I created a simple repacker (check attachments) that does the job for me, however i wont guarantee that it works for you too. Either double-click the files or simply drag an NSP onto them. Its python code converted to an EXE so it might trigger your AV. You don't have to use it if you don't trust it but repacking research has to be done by you then.

Make sure that hacpack is on your harddrive and added to your PATH envoronment variable.
1675696284973.png


To use my repacker, the NSP needs to have its titleID somewhere inside the filename. You can check the titleID in Unity under "Player Settings -> Publishing Settings" as "Application ID" (the 0x is NOT part of the titleID). So you can rename your NSP to something like "mygame_01004b9000490000.nsp or whatever you like, then simply drag the NSP on repacker.exe.

Once you have a repacked NSP, you can install it to a retail unit running Atmosphere with sigpatches.
Have fun creating some games!





Result

The result could look like this:


2023020616251200-57B4628D2267231D57E0FC1078C0596D.jpg
PXL_20230111_050837650.jpg
 

Attachments

  • repack.7z
    5.9 MB · Views: 98
Last edited by Slluxx,

Slluxx

GBATemp Mayor
OP
Developer
Joined
Jul 17, 2019
Messages
607
Trophies
0
XP
2,147
Country
Germany
unless you look at the cached page from where that leak got posted from that site which got taken down by clicking the three bullet list on the google search page, but i won't tell or show the link here cause of piracy reasons.
I don't get what you want to say. The leak is everywhere if you know how to use Google. You can even find it in web.archive, and that surely won't get taken down.

So are there any ways to make unity read files in homebrew?
No. Official apps/nsp created with the sdk can not access the sdcard. Homebrews can because they are not limited by sdk restrictions. You can not use unity in homebrews though.

If you want to make a game as a homebrew, you can use Godot. There is a repo that lets you export Godot games as nro.

If you want to use unity and you have to read or store files, you have to read the documentation on how savegames work. You won't get sdcard access though.
 

pokewiz

New Member
Newbie
Joined
Mar 28, 2023
Messages
4
Trophies
0
Age
25
XP
51
Country
China
Have you ever tried to build an AssetBundle?
I have build that for switch version.
But all sprites will go wrong when local play, and cannot be loaded on switch. :sad:
 

Slluxx

GBATemp Mayor
OP
Developer
Joined
Jul 17, 2019
Messages
607
Trophies
0
XP
2,147
Country
Germany
Have you ever tried to build an AssetBundle?
I have build that for switch version.
But all sprites will go wrong when local play, and cannot be loaded on switch. :sad:
No, did not try that. I have not tried it and i am just guessing but i would assume you have to put assetbundles in romfs and load them from there. I dont know how you have to write paths for it though, never tried it
 
  • Like
Reactions: pokewiz

pokewiz

New Member
Newbie
Joined
Mar 28, 2023
Messages
4
Trophies
0
Age
25
XP
51
Country
China
You are right, I guess there might be something wrong with the path, I will try and share the correct way for build assetbundle here later.
 
  • Like
Reactions: Slluxx

CraftingHut

New Member
Newbie
Joined
May 9, 2023
Messages
1
Trophies
0
Age
19
XP
16
Country
Netherlands
Hello there,

Do you reckon with enough research of how Unity compiles the shader code for the switch, we would be able to reverse engineer the shaders from built games BACK to shaderlab code?

Might sound like a dumb question, I apologize if it does and is, haha
 

NOOP9090

New Member
Newbie
Joined
May 22, 2023
Messages
2
Trophies
0
Age
54
XP
12
Country
United States
Jesus, no. You completely don't understand how this works.


No, you didn't. I recommend to read what this word means.


It stores whole game's code on Switch if there is no NRO file in romfs.


This file stores only strings and function signatures. It doesn't store any game's executable code.
Only two files can store CPU executable code on Switch - NSO and NRO. Nothing more.


You would need to write very comprehensive wrapper to replace any android specific data with something equivalent to HOS release + fix every possible serializing issue with romfs.


If you would know how executable is build, you wouldn't ask such question. Because you would know it would be a huge challenge worth years of work without any knowledge if this will work or not. That's why everybody with some experience does not do it without being handsomely paid.


You can't. You can only extract function signatures and strings to apply to disassembler.

I feel so much hype from your post and equivalent level of complete lack of knowledge.
I always find myself coming back to GBATemp to check out what's new once in a while and also because some of the people here are awesome. I used to share game hacks and lil proggies to edit games an such until one day I stopped, I still make them, and I still hack on stuff any time I can, but engaging with toxic trash like you made me decide to scan over forums and make quick touch downs like a UFO when needed and quickly move on. but reading your 1337est barf made me feel sad for you, you're probably really alone because you have such low self esteem you lash out at anyone and no-one wants you around so you find yourself online but cant control how you treat people here either.

One time I decrypted some things and then used them to leverage an exploit in a game I had UNPACKED.. but instead of using the word UNPACK I used decompile.. there were some people that were thankful and then there were some super lames like you that think being asinine on the internet is the best thing in the world, probably because you haven't experienced anything with a female yet. Imagine 20 years from now, none of these posts will matter, and everyone will have had a fun time learning and sharing and then there will be you, TidePod Timmy.... still alone, still an asshole...
 

masagrator

The patches guy
Developer
Joined
Oct 14, 2018
Messages
6,279
Trophies
3
XP
12,046
Country
Poland
I always find myself coming back to GBATemp to check out what's new once in a while and also because some of the people here are awesome. I used to share game hacks and lil proggies to edit games an such until one day I stopped, I still make them, and I still hack on stuff any time I can, but engaging with toxic trash like you made me decide to scan over forums and make quick touch downs like a UFO when needed and quickly move on. but reading your 1337est barf made me feel sad for you, you're probably really alone because you have such low self esteem you lash out at anyone and no-one wants you around so you find yourself online but cant control how you treat people here either.

One time I decrypted some things and then used them to leverage an exploit in a game I had UNPACKED.. but instead of using the word UNPACK I used decompile.. there were some people that were thankful and then there were some super lames like you that think being asinine on the internet is the best thing in the world, probably because you haven't experienced anything with a female yet. Imagine 20 years from now, none of these posts will matter, and everyone will have had a fun time learning and sharing and then there will be you, TidePod Timmy.... still alone, still an asshole...
Wow, first post and wall of text. And I'm lonely? :rofl2:
 

RGeeK2

New Member
Newbie
Joined
Mar 22, 2023
Messages
4
Trophies
0
Age
24
XP
49
Country
France
hi, I created an empty project and tried to build it (I installed the sdk and everything), but I get this error:
1688124224906.png

the folder in question looks like that:
1688124299647.png
 

Slluxx

GBATemp Mayor
OP
Developer
Joined
Jul 17, 2019
Messages
607
Trophies
0
XP
2,147
Country
Germany
hi, I created an empty project and tried to build it (I installed the sdk and everything), but I get this error:
View attachment 380968
the folder in question looks like that:
View attachment 380969
I don't know french so I can't tell you what's wrong. But I can see that unity complains about the SDK version so you should fix that first before trying to compile something
 

RGeeK2

New Member
Newbie
Joined
Mar 22, 2023
Messages
4
Trophies
0
Age
24
XP
49
Country
France
it just means that the file can't be find
Post automatically merged:

On the other hand, I don't understand: if I trust the first image you sent, the folders you have in your archive have exactly the same name and the same size as mine, so we have the same archive, but it's weird because you're in version 15.3.0 while I'm in version 15.3.2 with the same archive, did you replace files with another leak?
1688129680987.png

1688130682875.png

The documentation seems to indicate that there is no compatibility issue (I'm using Unity 2022.2.4f1)
 
Last edited by RGeeK2,
  • Like
Reactions: I pwned U!

I pwned U!

I am pleased to beat you!
Member
Joined
Jun 14, 2013
Messages
927
Trophies
3
Age
28
Website
gbatemp.net
XP
680
Country
United States
  • Like
Reactions: RGeeK2

RGeeK2

New Member
Newbie
Joined
Mar 22, 2023
Messages
4
Trophies
0
Age
24
XP
49
Country
France
How do I get the Nintendo SDK for Unity? I already registered as a developer but I can't find it
At first you only have access to software for the Wii U and 3DS, Unity is no longer distributed for these platforms, if you want to have access to switch software, you have to fill out a form but I tried and Nintendo declined it without further info.
 

Coolsonickirby

Well-Known Member
Member
Joined
Dec 6, 2015
Messages
331
Trophies
0
Age
22
Website
coolsonickirby.com
XP
2,509
Country
United States
Yesterday I've decided to try and get into Unity Switch Building. I've ran into a few issues and managed to resolve them, so I'll be putting something akin to a "FAQ" (a PAQ if you will (for potentially asked questions :))) here.

Building Problems PAQ:

- Could not find <ProjectDir>\%NINTENDO_SDK_ROOT%\<SomePath>\Application_<stuff>.nmeta \ %NINTENDO_SDK_ROOT% is not set
For these 2 errors, I made sure to set the %NINTENDO_SDK_ROOT% environment variable, then restarted my computer. That fixed this issue.

- Building Library\Bee\artificats\AppPkg\<some id>\main.npdm failed with output: The system cannot find the path specified
This error took me a while to fix, but after snooping through everything, I managed to figure out the issue. You need to install the dotnet 6.0 SDK x64 (Make sure you reboot your computer after installing it.)

- Building Building Library\Bee\artifacts\SwitchPlayerBuildProgram\24pfd\GameAssembly.nso failed with output:

MakeNso INPUT=<nss path>, OUTPUT=<nso path>
An attempt was made to load a program with an incorrect format. (0x8007000B)

This issue comes from trying to use the x86 dotnet SDK. To fix it, make sure you download the x64 dotnet SDK, then re-order them in the PATH environment variable so that the x64 version is over the x86 (aka put the `dotnet` folder that's the child of `Program Files` over the `dotnet` folder that's the child of `Program Files (x86)` (picture example below)) and save it. After that, close all Unity instances and the Unity Hub, then re-open them and build the project.
1698542871190.png

Post automatically merged:

Decompiled OP's executable back into a python script for those who want to be able to modify the repacking process / integrate it into a custom workflow / just doesn't trust random executables.

Python:
import os, sys, shutil, re

#region Setting up the global tools variable used for reference
tools = {
    "authoringTool": "%s/Tools/CommandLineTools/AuthoringTool/AuthoringTool.exe" % os.environ['NINTENDO_SDK_ROOT'],
    "hacPack": "hacpack"
}
#endregion

#region Getting the prod.keys file
prod_keys = os.path.join(os.environ['userprofile'], ".switch", "prod.keys")
if os.path.exists(prod_keys) == False:
    prod_keys = "prod.keys"
    if os.path.exists(prod_keys) == False:
        print("Could not find prod.keys!")
        exit()
#endregion

#region Setting up the NSP File variables
nsp_filePath = sys.argv[1] if len(sys.argv) > 1 else input("Enter the nsp filepath: ")
nsp_fileName = os.path.basename(nsp_filePath)
nsp_parentPath = os.path.dirname(nsp_filePath)
#endregion

#region Getting and setting up the Title ID variable
nsp_titleID = ""
search = re.search(re.compile('0100[A-Za-z0-9]{12}'), nsp_filePath)
if search == None:
    print("Could not find title ID anywhere in the path! Manual Entry Required.")
    nsp_titleID = input("Enter the NSP's title ID: ")
else:
    nsp_titleID = search.group()
    print("Found title ID in path! Title ID: %s" % nsp_titleID)
#endregion

#region Setting up the paths used for extracting
repacker_extract = os.path.join(nsp_parentPath, 'repacker_extract')
tmp_path = os.path.join(os.environ['temp'], "NCA")
#endregion

#region Cleaning up folders made before
shutil.rmtree(repacker_extract, ignore_errors=True)
shutil.rmtree(tmp_path, ignore_errors=True)
shutil.rmtree('hacpack_backup', ignore_errors=True)
#endregion

#region Running the authoring tool and getting the ExeFS and RomFS folders
os.makedirs(repacker_extract, exist_ok=True)
os.system(' '.join([tools["authoringTool"], "extract", "-o", '"%s"' % repacker_extract, '"%s"' % nsp_filePath]))
control_path = None
program_path = None
for x in os.listdir(repacker_extract):
    full_path = os.path.join(repacker_extract, x)
    if os.path.isfile(full_path):
        continue
    if os.path.exists(os.path.join(full_path, "fs0", "control.nacp")):
        control_path = full_path
    elif os.path.exists(os.path.join(full_path, "fs0", "main.npdm")):
        program_path = full_path
    if control_path != None and program_path != None:
        break
if control_path == None:
    print("Could not find control path!")
if program_path == None:
    print("Could not find program path!")
if program_path == None or control_path == None:
    exit()
#endregion

#region Extracting program to get the Program NCA
output = os.popen(' '.join([tools["hacPack"], '-k', '"%s"' % prod_keys, '-o', '"%s"' % tmp_path, '--titleid', nsp_titleID, '--type', 'nca', '--ncatype', 'program', '--exefsdir', '"%s/fs0"' % program_path, '--romfsdir', '"%s/fs1"' % program_path, '--logodir', '"%s/fs2"' % program_path])).read()
print(output)
tmp_program_nca_path = None
for line in output.splitlines():
    if '----> Created Program NCA:' in line:
        tmp_program_nca_path = line[len('----> Created Program NCA:') + 1:].strip()
        break
if tmp_program_nca_path == None:
    print("Failed getting the Program NCA!")
    exit()
#endregion

#region Extracting control to get the Control NCA
output = os.popen(' '.join([tools["hacPack"], '-k', '"%s"' % prod_keys, '-o', '"%s"' % tmp_path, '--titleid', nsp_titleID, '--type', 'nca', '--ncatype', 'control', '--romfsdir', '"%s/fs0"' % control_path])).read()
print(output)
tmp_control_nca_path = None
for line in output.splitlines():
    if '----> Created Control NCA:' in line:
        tmp_control_nca_path = line[len('----> Created Control NCA:') + 1:].strip()
        break
if tmp_control_nca_path == None:
    print("Failed getting the Control NCA!")
    exit()
#endregion

#region Creating Repacked NCA
os.system(' '.join([tools["hacPack"], '-k', '"%s"' % prod_keys, '-o', '"%s"' % tmp_path, '--titleid', nsp_titleID, '--type', 'nca', '--ncatype', 'meta', '--titletype', 'application', '--programnca', '"%s"' % tmp_program_nca_path, '--controlnca', '"%s"' % tmp_control_nca_path]))
#endregion

#region Creating Repacked NSP
output_nsp = "%s_repacked.nsp" % nsp_filePath[:nsp_filePath.rindex(".nsp")]
if os.path.exists(output_nsp):
    os.remove(output_nsp)
os.system(' '.join([tools["hacPack"], '-k', '"%s"' % prod_keys, '-o', '"%s"' % nsp_parentPath, '--titleid', nsp_titleID, '--type', 'nsp', '--ncadir', '"%s"' % tmp_path]))
os.rename(os.path.join(nsp_parentPath, "%s.nsp" % nsp_titleID), output_nsp)
shutil.rmtree(repacker_extract, ignore_errors=True)
shutil.rmtree(tmp_path, ignore_errors=True)
shutil.rmtree('hacpack_backup', ignore_errors=True)
#endregion
 
Last edited by Coolsonickirby,
  • Like
Reactions: luzifix

luzifix

Member
Newcomer
Joined
Dec 26, 2023
Messages
7
Trophies
0
Age
30
XP
24
Country
Germany
Hi, I wrote a little post build script to streamline the build process. It will repack the nsp file and optionally launch it directly in Yuzu.

For this you need to set two env variables NINTENDO_HECPACK_REPACK_BINARY with the path to the repacker.exe from Slluxx. This will automatically repack all generated nsp files. The second optional env variable is NINTENDO_EMULATOR_BINARY which points to yuzu.exe to automatically start the build nsp in the yuzu emulator.

After setting up the env variable and restarting your PC, you will need to create a SwitchPostBuildScript.cs file in your project and paste this code into it.
C#:
#if UNITY_SWITCH
public class SwitchPostBuildScript : MonoBehaviour
{
    private const string ENV_PATH_HECPACK_REPACK = "NINTENDO_HECPACK_REPACK_BINARY";
    private const string ENV_PATH_EMULATOR = "NINTENDO_EMULATOR_BINARY";

    [PostProcessBuild(1)]
    public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject)
    {
        if (target != BuildTarget.Switch)
            return;

        UnityEngine.Debug.Log("Repack NSP for retail switch");

        string hecPackRepackPath = Environment.GetEnvironmentVariable(ENV_PATH_HECPACK_REPACK);
        if (string.IsNullOrEmpty(hecPackRepackPath))
        {
            UnityEngine.Debug.LogError($"Environment variable {ENV_PATH_HECPACK_REPACK} is not set");
            return;
        }

        bool success = RunExternalExe(hecPackRepackPath, pathToBuiltProject);
        string emulatorPath = Environment.GetEnvironmentVariable(ENV_PATH_EMULATOR);

        if (success && !string.IsNullOrEmpty(emulatorPath))
        {
            UnityEngine.Debug.Log("Run yuzu.exe");
            var path = Path.GetDirectoryName(pathToBuiltProject);
            var fileName = Path.GetFileNameWithoutExtension(pathToBuiltProject);

            UnityEngine.Debug.Log($"Command: {emulatorPath} -f -g \"{path}\\{fileName}_repacked.nsp\"");
            Process.Start(emulatorPath, $"-g \"{path}\\{fileName}_repacked.nsp\"");
        }
    }

    private static bool RunExternalExe(string filename, string arguments = null)
    {
        var process = new Process();

        process.StartInfo.FileName = filename;
        if (!string.IsNullOrEmpty(arguments))
            process.StartInfo.Arguments = arguments;

        process.StartInfo.CreateNoWindow = true;
        process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
        process.StartInfo.UseShellExecute = false;

        process.StartInfo.RedirectStandardError = true;
        process.StartInfo.RedirectStandardOutput = true;
        var stdOutput = new StringBuilder();
        stdOutput.AppendLine("=== NSP repack process log ===");

        process.OutputDataReceived += (sender, args) => stdOutput.AppendLine(args.Data);

        try
        {
            process.Start();
            process.BeginOutputReadLine();
            UnityEngine.Debug.Log(process.StandardError.ReadToEnd());
            process.WaitForExit();

            UnityEngine.Debug.Log(stdOutput.ToString());
        }
        catch (Exception e)
        {
            UnityEngine.Debug.Log(stdOutput.ToString());

            throw new Exception("OS error while executing " + Format(filename, arguments) + ": " + e.Message, e);
        }

        return process.ExitCode == 0;
    }

    private static string Format(string filename, string arguments)
    {
        return "'" + filename + ((string.IsNullOrEmpty(arguments)) ? string.Empty : " " + arguments) + "'";
    }
}
#endif
 
Last edited by luzifix,

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
  • No one is chatting at the moment.
    K3Nv2 @ K3Nv2: Lol rappers still promoting crypto