-
Notifications
You must be signed in to change notification settings - Fork 3
Environment Setup: Where's the code in Ultimate, and how does it work?
We can consider the code as being divided into three general filetypes, each for their own purposes.
In the exefs folder, the main file is an NSO that controls the game at the lowest possible level. This file controls loading files from the data.arc into memory, setting everything up for other agents to take over, and most importantly for us, exporting functions for all these other agents to use. Reading this executable can be done with GHIDRA or other disassembler/decompilers.
SaltyNX allows us to interface directly with this file at runtime in memory, including the ability to read symbols from the main and perform memcpys on it; this ends up making up the base for the majority of what this framework does.
Now in the romfs/data.arc, we can find all the character files as precompiled binaries in root/prebuilt:/nro/release/, of the format lua2cpp_[character].nro, and the common file that all characters import from lua2cpp_common.nro. Reading this executable can be done with GHIDRA or other disassembler/decompilers. There's a lot going on with these files.
Originally, Nintendo wrote these character files in Lua, had them transpiled into C++ using a program called lua2cpp, and finally compiled them down into ARM code that the Switch can use in the NRO format. The Lua transpilation step provides a tremendous amount of obfuscation to what used to be quite simple code, but it is still readable once the conventions are learned.
A character's NRO file is simple in terms of organization. It communicates where all its scripts are using its exported functions, which include:
| Name | Functions |
|---|---|
| AnimCMD scripts |
|
| Status scripts | lua2cpp::create_agent_fighter_status_script_[character] |
| AI scripts |
|
AnimCMD controls how each of a character's animations work, and it is split into four categories: game, effect, expression, and sound. AnimCMD is frame-based, and takes care of a lot of the scripting required to make up what we consider a character's moveset. For example, almost all normal hitboxes are defined in a character's game AnimCMD scripts. From here on, "ACMD" and "AnimCMD" will be used interchangeably.
Status script functions are more of the code that the character uses at its base. You can think of it as the lowest layer that controls how a character functions. Once a character is created and set up, it moves from status to status, depending on inputs, where the character is, a natural progression from one status to another, everything. A list of STATUS_KINDs can be found here.
(In Smash 4, these were exactly MSC scripts, and we called statuses "actions".)
Furthermore, animations and their ACMD code are called from status scripts. Just as well, there are many special moves and other actions whose main behavior is instead found in these status scripts (usually because they can't be expressed in ACMD), and pretty much all functionality common to all characters (ledge mechanics, respawning, ... pretty much everything) is controlled by the status scripts in lua2cpp_common.nro.
AI scripts control how CPUs behave. Their code is responsible for setting the CPU's virtual controller inputs (button combinations and stick position), evaluating the positions/speeds of the CPU, the target, the stage, and many other factors to make decisions. AI scripts exhibit the most usage of Lua-like features, such as a table of named global variables (hashed, ex: "lifetime_", "update_count", "phase_func", etc), or the use of variables to store functions to perform routines.
Menu and item code are found scattered throughout the data.arc in the form of .lc files, precompiled Lua files. These are decipherable using luadec for 5.3, but as of now editing them is more difficult because of the current filesize limitation for data.arc mods.