The executable is where all the game code is stored. It is located in the title code directory on Wii U and in the ExeFS on Switch.
Important note: unless otherwise stated, all addresses on this wiki are for the Switch 1.5.0 executable.
BotW's executable is a standard, compressed NSO which is larger than most other games (about 20MB compressed).
The build path is
D:\home\Cafe\U-King\trunk\Game\App\Rom\NX64\Product\code\U-King.nss. For build IDs, see Versions#Switch_versions.
BotW's executable is a standard, compressed RPX.
Full article: Software libraries
Full article: Subsystems
Unfortunately, release versions of The Legend of Zelda: Breath of the Wild are stripped and have absolutely no debugging symbols. This has been verified for Switch 1.0.0, Wii U 1.5.0, Switch 1.5.0, Switch 1.6.0 and Switch RID_Demo.
The Switch 1.5.0 and 1.6.0 executables only have some RTTI data for UI classes. This is likely also the case for all older versions.
However, the game uses lots of software libraries, some of which are included in games that do ship with complete symbols such as Super Mario Odyssey. BotW seems to have been compiled at a higher optimisation level that causes unused functions to be fully removed.
Init and main loop outline
Early init and main loop
- sead::GameFrameworkNx::initialize (implicitly calls nn::oe::Initialize again; this looks like a mistake from the Zelda developers)
- sead::HeapMgr and root heap
- Unknown 71025D1720
- uking::GameFramework (derived from sead::GameFrameworkNx)
- [Debug] sead::PrimitiveRenderer
- [Debug] sead::DebugFontMgrNvn
- [Debug] sead::DebugFontMgrJis1Nvn
- Constructs a sead::MethodTreeMgr (note: a custom, derived MethodTreeMgr is used in BotW)
- Constructs a sead::TaskMgr
- sead::TaskMgr::prepare (on the Prepare Thread)
- methodTreeMgr->calc (sead::SingleScreenMethodTreeMgr). Runs the following methods (sead::MethodTreeNode):
- methodTreeMgr->calc (sead::SingleScreenMethodTreeMgr). Runs the following methods (sead::MethodTreeNode):
- [Debug] sead::SeadMenuMgr
- [Debug] sead::HostIOMgr
- [?] sead::CuckooClock
- InfLoopChecker setup
- Step 0
- KingSystem heap
- The name is extremely misleading. What the thread does is set up sead::RegionLanguageMgr and initialise it by reading System/RegionLangMask.txt.
- Step 1
- Wait for the sound calc thread to finish executing, before destroying it
- Step 2
- Physics Memory System
- [Debug] CurrentResNameMgr
- [Debug] Revision
- [Debug] Visualizer-shader
- Loads Bootup_Graphics, Bootup and (in non-debug mode) Title and TitleBG packs.
- aocManager (in post-1.0.0 versions)
- Add-on content is mounted in aocManager::init (nn::fs::MountAddOnContent).
- Set up the resource system (sead::FileDeviceMgr::mount + path prefix mapping) and load the version file.
- Step 3
- Step 4
- ScreenFactory (again, which leaks the previous factory)
- [Release] Load AocMainField.pack
- Switch active pack to Bootup_Graphics.
- Unload Bootup_Graphics and set active pack to TitleBG.
- [Debug] Log resource loads to %PROJECT_ROOT%/Log/BootupPatrol/res_bootup_graphics.csv.
- Physics System Data
- Start of ActorInfoData init.
- End of ActorInfoData init.
- [Debug] MemoryProfiler
- [Debug] CPUProfiler
- [Debug] CameraEdtior (sic)
- [Debug] KingEditor
- PlayReport (see also Telemetry)
- Unknown 710261F9C8 (used from GameScene)
- [Debug] Replay
- [Debug] DebugInput
- AnimSequence (AS)
- [Debug] Visualizer
- aocManager init
- [Debug] ActorDebug
- [Debug] DebugFinder
- [Debug] ModelPicker
- [Debug] GameTool
- [Debug] AutoGenFramework
- [Debug] MessageCapture
- [Debug] InitResourceReload (resource live reloading)
- KingSystem heap is resized to fit.
- Attention: unknown init
- Step 5: noop
- Step 6: noop
- Step 7
- Unknown 71025D04D8
- Scene change functions
- MapStaticLoadMgr (loads Static.mubin)
- Unknown 71025D1790
- Unknown 71025D1A28
- Unknown 71025D04F0
- Debug board (?)
- Unknown 71025D16C0 (has to do with GameScene, E3Mgr and AIDef:Action/PlayerPullSword)
- Unknown 71025D1710
- Unknown 71025D1538 (has to do with frame and key input?)
- SeadController (derived from sead::Controller)
- Unknown 71025D2508
- Unknown 71025D24C0
- AI system init
- End of RootTask init
- Step 8
- Step 9
- Controller update (?)
- SaveSystem update (?)
1.0.0's nnMain prints "00000010", whereas 1.5.0 prints "0000002d".
1.6.0 prints "0000002e".
The game accepts three different options: -fhd (flag for Full HD?), -no_sead_log (flag), -out (string). However the code that handles these arguments does not exist in the release version.
Some development versions (1523941 to 1548881) generated unusable saves. These strings are still present in the release version but they are unused.
.rodata.2:0000007101DE98B0 a15239411548881 DCB "@1523941 ~ @1548881のROMからのセーブデータを利用しているようです。",0xA .rodata.2:0000007101DE98B0 DCB "バグ報告をせずに、セーブデータを消去してください。",0xA .rodata.2:0000007101DE98B0 DCB "@1523941 ~ @1548881からのセーブデータであるはずがないという場合のみ、バグ報告をしてください。",0xA .rodata.2:0000007101DE98B0 DCB "num_valid_normal_mode %d/%d, num_valid_hard_mode %d%d",0
Leftover debug or development tools
See bactcapt for more information.
The resource factory and resource classes are still present in release builds, but no ActorCapture parameter files are included in the ROM.
Used to generate movies (prerendered cutscenes) for event flows. Receives orders via HostIO (in BYML format) in
%UKING_ROOT%/../workdir/EventPatroller/order.byml and writes:
- A YAML report containing frame-by-frame data with camera position, actor information, etc. Written to
- Screenshots for the entire event (maximum 18000 frames) captured using agl::utl::ScreenshotMgr, and written to
%s/%05d.tga% (name, frame).
Throughout the execution of the order,
%%KSYS_ROOT%%/tools/EventPatroller/ChangeStatus.bat is spawned to update the order status on the host computer.
EventPatroller can apparently make use of Havok script files (hks) in dev builds. However, all script-related code is stubbed in release builds and both the
lua resource factories are unused.
bplacement resource factory is stubbed in release builds. The associated resource class is also stubbed. It is unknown what Placement files would have contained. (Configuration for the PlacementMgr?)
Demo ROM types
The game calls
sead::EnvUtil::getRomType() to get the ROM type. The result is printed to a debug log along with SD card, revision and AoC (DLC) information.
The ROM type is loaded from System/RegionLangMask.txt. Possible values are:
- "Normal": used in retail versions (at least 1.0.0 and 1.5.0)
- "Show_2017_1st": demo version, used for 12S?
- "RID_Demo": Retail Interactive Display demo (aka kiosk version). Seen in the Switch kiosk version.
- Anything else is treated as "Normal".
The release build crashes if the ROM type is set to Show_2017_1st or RID_Demo, presumably because these types activate code paths that require a global debug heap which never gets initialised in release versions.
ErrorViewer and Stage Select
There are references to debugging tools like Error (an in-game integrated bug tracker) and a stage select mode (uking::StageSelect + more strings). UI data and structures for the ErrorViewerTask still exist to a certain extent, but whether these features can be reactivated or not is still unknown.
In 1.5.0, the Patrol subsystem has a reference to BUILD_URL. Unfortunately, the function that is supposed to set the BUILD_URL string (sub_7100B0B728) is stubbed in the release build.
Similarly, the Revision subsystem is disabled in release builds. According to strings in the stage select screen function, it would have contained information about a program number (int), resource number (int) and a 'build from' string.
This is a rather large subsystem that interacts with the actor system and all related components such as demos, events and maps. It can print debug information about the current state and override actor creation behaviour.
A large amount of debug config strings have been left in the executable. These show the existence of debug game config files that were used to make testing more convenient during development.
The debug config files do not exist in the release version; the config loading code was removed as well.
.rodata.2:0000007101DC9625 aDebugGameconfi DCB "Debug/GameConfigSettingForGameROM.xml",0 .rodata.2:0000007101DC9625 ; DATA XREF: initDebugConfigFilePaths+18↑o .rodata.2:0000007101DC9625 ; initDebugConfigFilePaths+2C↑o .rodata.2:0000007101DC964B aDebugGameconfi_0 DCB "Debug/GameConfigForGameROM.xml",0 .rodata.2:0000007101DC964B ; DATA XREF: initDebugConfigFilePaths+20↑o .rodata.2:0000007101DC964B ; initDebugConfigFilePaths+38↑o .rodata.2:0000007101DC966A aDebugSystemgam DCB "Debug/SystemGameConfigForGameROM.xml",0 .rodata.2:0000007101DC966A ; DATA XREF: initDebugConfigFilePaths+24↑o .rodata.2:0000007101DC966A ; initDebugConfigFilePaths+3C↑o .rodata.2:0000007101DC968F aOpenworldOverr DCB "OpenWorld_OverrideStartPos",0 .rodata.2:0000007101DC96AA aOpenworldStart DCB "OpenWorld_StartX",0 .rodata.2:0000007101DC96BB aOpenworldStart_0 DCB "OpenWorld_StartY",0 .rodata.2:0000007101DC96CC aOpenworldStart_1 DCB "OpenWorld_StartZ",0 .rodata.2:0000007101DC96DD aOpenworlddesig DCB "OpenWorldDesign_OverrideStartPos",0 .rodata.2:0000007101DC96FE aOpenworlddesig_0 DCB "OpenWorldDesign_StartX",0 .rodata.2:0000007101DC9715 aOpenworlddesig_1 DCB "OpenWorldDesign_StartY",0 .rodata.2:0000007101DC972C aOpenworlddesig_2 DCB "OpenWorldDesign_StartZ",0 .rodata.2:0000007101DC9743 aIsdisabledpick DCB "IsDisabledPickUpDemo",0 .rodata.2:0000007101DC9758 aIsenabledcdung DCB "IsEnabledCDungeonEntranceDemo",0 .rodata.2:0000007101DC9776 aIsdisabledcdun DCB "IsDisabledCDungeonGoalTerminalDemo",0 .rodata.2:0000007101DC9799 aAlwayswaitscen DCB "AlwaysWaitSceneCreate",0 .rodata.2:0000007101DC97AF aIsequipweaponr DCB "IsEquipWeaponRandom",0 .rodata.2:0000007101DC97C3 aIsequiparmorra DCB "IsEquipArmorRandom",0 .rodata.2:0000007101DC97D6 aIssetweaponran DCB "IsSetWeaponRandom",0 .rodata.2:0000007101DC97E8 aIssetarmorrand DCB "IsSetArmorRandom",0 .rodata.2:0000007101DC97F9 aIssetarmorscri DCB "IsSetArmorScript",0 .rodata.2:0000007101DC980A aIssetitemrando DCB "IsSetItemRandom",0 .rodata.2:0000007101DC981A aIssetcookitemr DCB "IsSetCookItemRandom",0 .rodata.2:0000007101DC982E aDisabletimesto DCB "DisableTimeStop",0 .rodata.2:0000007101DC983E aSetdebugtime DCB "SetDebugTime",0 .rodata.2:0000007101DC984B aDefaultfinewea DCB "DefaultFineWeather",0 .rodata.2:0000007101DC985E aDefaulttoolenv DCB "DefaultToolEnv",0 .rodata.2:0000007101DC986D aDefaultplayren DCB "DefaultPlayreNoTired",0 .rodata.2:0000007101DC9882 aDefaultplayern DCB "DefaultPlayerNoTemperture",0 .rodata.2:0000007101DC989C aAutoplacement DCB "AutoPlacement",0 .rodata.2:0000007101DC98AA aTeam DCB "Team",0 .rodata.2:0000007101DC98AF aShowmergeddung DCB "ShowMergedDungeonActor",0 .rodata.2:0000007101DC98C6 aShowmergedgrud DCB "ShowMergedGrudgeActor",0 .rodata.2:0000007101DC98DC aIsopenmapall DCB "IsOpenMapAll",0 .rodata.2:0000007101DC98E9 aDebugcameraspe DCB "DebugCameraSpeed",0 .rodata.2:0000007101DC98FA aDebugcamerarep DCB "DebugCameraReplaceStick",0 .rodata.2:0000007101DC9912 aDebugcamerarev DCB "DebugCameraReverseVerticalRotation",0 .rodata.2:0000007101DC9935 aDebugcamerarev_0 DCB "DebugCameraReverseHorizontalRotation",0 .rodata.2:0000007101DC995A aDebugcameracon DCB "DebugCameraControlFovy",0 .rodata.2:0000007101DC9971 aIstexturememor DCB "IsTextureMemoryIncrese",0 .rodata.2:0000007101DC9988 aIstexturememor_0 DCB "IsTextureMemoryBothIncrease",0 .rodata.2:0000007101DC99A4 aIspilottexture DCB "IsPilotTextureMemoryIncrese",0 .rodata.2:0000007101DC99C0 aIspassstagesel DCB "IsPassStageSelect",0 .rodata.2:0000007101DC99D2 aIsstageselectr DCB "IsStageSelectRegularGameOver",0 .rodata.2:0000007101DC99EF aIsenableuidebu DCB "IsEnableUiDebugCommand",0 .rodata.2:0000007101DC9A06 aFirstscene DCB "FirstScene",0 .rodata.2:0000007101DC9A11 aToolFrontEnd DCB "Tool Front-End",0 .rodata.2:0000007101DC9A20 aDefaulttoolfro DCB "DefaultToolFrontEndPage",0
Option values are hardcoded in the Switch kiosk version as well.
script_name(in the order data) is a path to a hks file.