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.
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.
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.
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 partcir
stands for Command Intermediate Representation. It takes the syntax tree (the output ofsyn
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
- Resolving the item identifier (such as
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.
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
- Pip packages:
- 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
andsed
- Both should have Windows binary release which
- You can use my implementationcargo 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
You only need to run these steps once to setup the development environment- Install all the tools as listed above
- Clone the
Pistonite/botw-ist
repository from GitHubgit clone [email protected]:Pistonite/botw-ist
- 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:
- Follow the steps here
to acquire a BlueFlame image with the
uking-relocate
tool. - Rename the image
program.blfm
and put it in<repo>/packages/runtime/
- Run
task exec -- runtime-wasm:build
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>
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.
- 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
- 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
- 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.
- 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