Welcome

eat axe in slot 3;
get 3 apple[life = true, time = false]
get 4 arrow;
unequip arrow;
get "古代箭"
eat axe in slot 3;
eat inf in slot 5
pick-up 1 weapon;

equip ice_arrow
use bow; freeze meat

use food 3 times
'''note
'''
'''note
a
'''
sync
'''some-tag
'''
sync
'''note
text
'''
'''note
text

get  1 apple
'''

Discord

Join my Discord and get the BOTW Tools role to get access to the botw-ist channel to ask questions about or discuss features of the IST Simulator App.

User Manual

This section covers how to use the Inventory Slot Transfer (IST) Simulator App, such as how to write a simulator script, reference of the commands, and various features of the app.

The IST Primer covers basic information about IST, such as terminologies used throughout this manual.

IST Primer

What is IST

IST stands for Inventory Slot Transfer. It is a glitch in Breath of the Wild that exploits behavior of the inventory when the number of the items in the inventory tracked by the game is less than the number of items actually in the inventory.

The developers made sure that these two values are kept in sync during normal gameplay. However, in very specific scenarios, the game removes the item slot from the inventory while subtracting the number of items twice, resulting in the game tracking 1 fewer item slots in the inventory. By repeating the action, we can make the game track fewer and fewer items.

The difference between the number of items in the inventory and number tracked by the game is called Offset or number of Broken Slots. “Offset” is technically more correct, but because it’s ambiguous in some contexts, this manual will refer to this number as “Broken Slots”. The action to create the Broken Slots is referred to as Breaking Slots.

Note

Technical Detail

The inventory is stored as a doubly-linked list. The linked list implementation stores the length of the list separately as it’s inefficient to calculate the length of a linked list. This variable is often referred to as mCount, which is its name in the decompilation project. This is the number of items the game tracks. The actual number of items in the inventory is the number of elements in the linked list of items.

Info

When considering the inventory items as a list, the left-most item in the inventory (in the left-most tab) is the first item. Then the list follows row-major order (i.e. the item to the right of the left-most item is the second in the list; the first item in the second row is after the last item in the first row). Empty spaces and empty tabs in the inventory do not count. For example, if the tabs are “Weapon” followed by “Bow”, the first bow is directly after the last weapon in the list. The spaces (unoccupied and unupgraded slots) in the “Weapon” tab does not affect how the items are stored.

Why is it called IST

Scopes

Scope is a concept that the simulator runtime uses to enforce the validity of the setup, and automatically update different parts of the simulation. For example:

  • You cannot use an item while talking to a NPC
  • GDT Inventory is synced when closing inventory
  • If the game crashes, you can’t do anything until you restart it

Currently, there are three scopes in the simulator:

  • Game Scope: The game is running, and the player has control of Link
  • Inventory Scope: The player is looking at the inventory (pressed +)
  • Dialog Scope: The player doesn’t have control of Link (for example, talking to an NPC)

Automatic Scope Management

For the most part, scopes are managed automatically by the simulator runtime based on the command, so you don’t have to worry about them.

For example, consider the following script

get 1 apple
eat 1 apple
get 1 apple

At first, the simulation state is not in any scope. To get an item, you must have control of Link, so the get action requires game, !paused scope, so the simulator automatically activate the game scope by starting a new game.

The next eat action requires game, inventory scope because you need to be in the inventory to eat an item. The simulator infers that you want to pause the game to eat the item, so it automatically activates the inventory scope.

Finally, the last get action requires the game to be not paused, so the simulator automatically deactivates the inventory scope to allow the action to be performed.

Manual Scope Management

Certain actions like pause can be used to change the scope manually. When the scope is activated manually, the simulator will not automatically change the scope until the manual scope is deactivated.

For example, consider the following script, which is the same as above except that it manually pauses the game before eat

get 1 apple
pause
eat 1 apple
get 1 wood # Error!

Now the simulator will not automatically deactivate the inventory scope for get 1 wood. Instead, it will give an error saying you cannot get new item while paused.

Scope Conflict

Another error that the scope system checks for is conflicting scopes. For example, you cannot access inventory while talking to an NPC. This is implemented by you cannot activate inventory scope while the dialog scope is active.

The following script is valid:

sell 1 apple
eat 1 apple

Here, sell activates the dialog scope, and eat deactivates it to activate the inventory scope.

However, if you manually activate the inventory scope before sell, you will have an error because the dialog scope cannot be automatically activated while inventory scope is in use.

talk-to shopkeeper
sell 1 apple
eat 1 apple # Error!

Crashes

Most commands require the game scope, which can be automatically activated. The only exception is when the game crashes. In this case, you must manually activate the game scope with an action like new-game or reload

Testing

Scope can be tested using the !assert-scope command. The special not-paused keyword can be used to test that game is the top-level scope

!assert-scope game
!assert-scope inventory
!assert-scope game not-paused

Actions

Actions map to actions you can do in the game

Get

Syntax


Developer Manual

This section covers how to use the IST Simulator as a developer, such as contributing to the project, building extensions, or integrating your own tools with the IST Simulator.

The following chapters assume you have basic programming knowledge. The IST Simulator is built with TypeScript, Rust and some Python scripts for data processing. Familiarity in at least one of these languages will help with understanding.

Architecture

To get started, you must understand the architecture of the IST simulator. This will help you find the relevant code to start digging into the project.

The Three Parts

The simulator app can be divded into 3 distinct parts:

  • The “Runtime”: This is what parses the script and runs the simulation
  • The “Application”: This is what handles the UI, the states associated with the UI, and data such as item parameters and localization.
  • The “Extension“s: These are individual encapsulation of some functionality that works with the Application.

Architecture Diagram

The relationships between the 3 parts can be summarized as:

  • The Application stores the state (such as what the script is), and uses the Runtime to drive the simulation
  • The Extensions uses the Application to access the current state and functionality of the Runtime
    • Note the Extensions don’t talk to each other, and they don’t talk to the Runtime directly.

The APIs between the 3 parts are well-defined in the @pistonite/skybook-api package. The API library is built upon my own worker RPC library workex and my own TypeScript library pure.

The Runtime

The runtime contains the very basic functionality of the simulator:

  • Parsing the simulator script
  • Executing the simulation

If you are looking into adding new commands, changing existing commands, updating the syntax, or supporting the simulator script in other applications, this is where to look.

Parser

The skybook-parser package implements parsing the commands. You can use this package to support the simulator script in other tools. For example, the parser is used to highlight the simulator script syntax in this manual.

The parser has 2 stages: syn and cir:

  • syn is the syntax parsing part
  • cir stands for Command Intermediate Representation. It takes the syntax tree (the output of syn stage) and convert it to runnable commands. This process includes:
    • Resolving the item identifier (such as royal_claymore) and search queries to actor names
    • Parses other properties such as item metadata to ensure they are valid in the context

The parser also has an injected dependency: the QuotedItemResolver. This is used to resolve quoted item search queries such as "Royal Claymore". In the simulator web app, quoted strings are searched using the items’ localized names so users can use their native language in the script. The parser’s implementation is generic and can (and must) take an externally implemented QuotedItemResolver

Runtime

TODO

Contributing

This page contains important information for contributing for the project. You should read everything here before thinking about contributing.

Allowed Files

The repository must not contain any assets or data from BOTW, or any file derived from assets or data from the game. PRs containing such files will be closed.

Info

Please reach out to me if you want to add something that depends on the game files

Requirement

The following tools are required:

  • Rust Toolchain
    • MSVC on Windows is likely needed (Install Visual Studio Build Tools)
  • Node v20
  • PNPM
  • Bun
  • Python v3
    • Pip packages: pip install pyyaml
  • Task
  • Magoo (cargo install magoo)
  • jq is only needed for some workflows

For developing the runtime, you also need a copy of the game.

Development workflows are only tested (by me) on Linux, for Windows, there are a few extra requirements to get everything working. The recommended option is use WSL unless you enjoy tinkering with these stuff:

  • PowerShell 7 is recommended since it doesn’t have weird aliases like PowerShell 5 (such as curl)
  • MSVC: See https://rust-lang.github.io/rustup/installation/windows-msvc.html
  • GNU Coreutils
    • You might need to replace some shell alias/commands with the GNU version
  • GNU wget and sed - Both should have Windows binary release
  • which - You can use my implementation cargo install dotbin-windows --git https://github.com/Pistonite/dotbin

It’s also a good idea to join my Discord in case you have questions or need to discuss something.

Setup

Danger

These steps don’t work yet

You only need to run these steps once to setup the development environment
  1. Install all the tools as listed above
  2. Clone the Pistonite/botw-ist repository from GitHub
    git clone [email protected]:Pistonite/botw-ist
    
  3. Run the commands below to setup the dependencies
    magoo install
    task exec -- research:install
    task install-cargo-extra-tools
    task build-artifacts
    task install
    

Now, you can run the web app in development mode. This is enough if you are not planning on changing the Runtime.

If you do need to build the Runtime, you need to:

  1. Follow the steps here to acquire a BlueFlame image with the uking-relocate tool.
  2. Rename the image program.blfm and put it in <repo>/packages/runtime/
  3. Run task exec -- runtime-wasm:build

Note

Everytime the parser or runtime is changed, you need to run the runtime-wasm:build task to re-build the WASM module to see the changes in the web app

Keeping Up-to-date

If you are a returning contributor, it’s always a good idea to check this page again in case the requirements change.

You generally only need to run task install to install the dependencies again. If you are returning after a long period of time (> 1 month), it’s a good idea to run rustup update && cargo clean && task icets to update the Rust toolchain and tools.

Repo Structure

All the code are organized into packages in the packages directory, you should only need to care about what’s inside packages.

You can list all the packages with:

ls packages

To see what tasks are available, run:

task list

To execute an task from the repo root, run:

task <task> # replace <task> with the task to execute

To list and execute a task from a package, you can run:

task list -- <package>
task exec -- <package>:<task>

# OR:

cd packages/<package>
task --list
task <task>

Tip

The most common use case is to automatically fix formatting issues. For example task exec -- parser:fix will fix all formatting issues with the parser

Build and Testing

TODO

PR Guidelines

Please use these guidelines to ensure your PR can be effectively reviewed and merged.

  1. Make small changes: Small fixes can be reviewed faster. Please reach out first if you are planning on a moderate or big change, or adding something new
  2. Run checks locally: PRs must pass the workflows to be merged, and the workflows require my approval to run. To reduce the turn-around time, you should always run the checks locally to make sure the workflows pass. To run the checks:
    task check
    task test
    task build
    
  3. Test: You almost always want to add unit tests for your change. If something should be tested but no new tests are added, I will ask for it. See above for how each component can be tested. If you are changing the UI, consider adding screenshots for before and after, if it’s not a simple fix.
  4. Documentation: If you are adding new functions, make sure it has a doc comment. The comment should detail the behavior of the function. i.e. I should be able to tell what the function returns for an input, without looking at the implementation

Integrate IST Simulator Externally