Skip to content

kaimitai/FaxIScripts

Repository files navigation

FaxIScripts - Assembler and Disassembler for scripting in Faxanadu (NES)


Welcome to the FaxIScripts code repository and release page. The code is standard C++20, and the project files were created using Microsoft Visual Studio Community 2022. You can compile the application from source, or get the latest precompiled Windows x64 build under the repository releases.

This tool is used for extracting data from Faxanadu (NES) ROMs - scripts and music - which can be edited in text editors and injected back into the ROM.

The aim of this assembler is to extract script code to human-readable formats reminiscent of assembly code. We aim to stay at the highest possible layer of abstraction without losing any extracted information.

The application is compatible out of the box with the following ROM regions: US, US Revision A, EU, JP and a well-known English Translation Hack.

Make sure to read the documentation for a detailed overview of the syntax and structure of the assembly files we will be editing, as well as a list of all available opcodes.


This application has a natural companion in Echoes of Eolis, which is a graphical editor that can patch the other dynamically sized data portions in Faxanadu. To see, or change, which NPCs in Faxanadu are connected to a given interactions script, for example, Echoes of Eolis can can be used.

Echoes of Eolis will always be bundled with the latest version of this application.


The interaction script (iScript) layer

Interaction scripts are used for several things; interacting with NPCs and shops - and some scripts are called on certain events - like dying and picking up or using items.

This scripting layer contains strings, shop data and actual code. The strings are stored in a separate section, but the shop data and code live together in one combined section. The shop data gets moved to its own section in our assembly files, and any opcode referencing a shop uses its index - which is only resolved to an actual address during linking. This provides a zero-cost abstraction - no extra bytes, no layout penalties.

There are two ROM sections we can use when patching, and users can choose between different patching modes.

An example of an extracted iScript:

IScript example

Faxanadu iScript #67 - Free full plate from a man in the Victim pub, if your rank is Soldier or higher


The behavior script (bScript) layer

This layer contains the scripts which define sprite behavior - for both NPCs, enemies and items. There are 101 entries, one for each sprite in the game.

Here too, there are two ROM sections we can use when patching, and the users can choose between different patching modes.

IScript example

Faxanadu bScript #42 - Monodron behavior


The music (mScript) layer

We also support extracting and patching the music layer of Faxanadu - both at the assembly level, and at a higher level of abstraction - as MML (music macro language) files.

For MML editing there is a separate documentation for a detailed overview of the syntax and structure of the file format - as well as the necessary technical information needed to compose music effectively.

The assembly format is provided for completeness' sake, and because the MML compiler needs it. For actual music composition we recommend using MML over mScript assembly.

An example of the beginning of an extracted tune:

MML example


Miscellaneous static data

While not an assembly format, the application also has a mode to extract miscellaneous data to a textual format which can be re-injected to ROM after editing. This allows users to change strings, enemy, weapon and magic parameters and such. The mode is region-agnostic, and abstracts away the complexity of different string formats having different character palettes for example, so it should be a good alternative to a hex editor.


There is also a mantra-mode, which can encode and decode mantra strings.


Assembler Capabilities

The assembler has the following features:

  • Byte-fidelity with respect to size and functionality will be retained when extracting and patching content; for both strings, shops, music and script code. There is no change to game code itself, just a clean patching of dynamically sized data.
  • When extracting an assembly file from ROM, the constant defines will be populated automatically and used in the code
  • For iScripts, strings will be inlined in the code and can be used as operands directly. The assembler will discard all duplicates and ensure that all string-calling functions point to the correct index. Reserved string indexes (six strings which are used by index directly from the action handlers, even if no scripts reference them) are defined in the asm automatically when extracting, and the assembler will not consider these for re-indexing or discarding.
  • Extracted iScript asm-files can show shop contents as comments wherever they are used as operands
  • Strict-mode; where we don't patch a ROM if we spend more bytes than the original ROM did, tightly packed in one section (applies to iScripts and bScripts)
  • For iScripts and bScripts, we provide a smart static linker; The shop data and code stream starts within the first safe region, and if we overflow the static linker redirects code to the second region while patching all required labels, jumps, pointer table entries and instruction offsets. This is done without inserting a synthetic jump-node. bScripts too can be extended in this way with a tail end code relocation, although it is in its entirety a code section.
  • Automatic ROM region deduction, ensuring that the assembly code is extracted from, and injected to, the correct ROM locations. Assembly fails if data sections get too big, so no ROM file will be corrupted.

How it works

The application can disassemble - that is extract - the scripting or music layer data into text files - from a Faxanadu NES rom. These files can then be modified by the user via our internal assembly languages.

A command-line instruction will extract and disassemble the scripting layer data from ROM.

The command faxiscripts x "Faxanadu (U).nes" faxanadu.asm will extract the interaction script data from file "Faxanadu (U).nes" and write it to file faxanadu.asm.

Another instruction will pack your data and assemble your code, and patch it back to ROM.

Using the command xb instead of x will extract behavior scripts instead.

The command faxiscripts b faxanadu.asm "Faxanadu (U).nes" will patch "Faxanadu (U).nes" with the interaction script code from faxanadu.asm as long as the code was valid.

Using the command bb instead of b will assemble behavior scripts instead.

The asm-files may look a little daunting at first, but I am sure it will be very manageable for most people who have an interest in editing scripts. The documentation is detailed and contains concrete examples you can follow.

There is little static code analysis available for the time being, but before actually patching the ROM we ensure the code is good by trying to traverse all code paths from all entry points to verify that the code the game can potentially use can actually be parsed.

The command faxiscripts xmml "Faxanadu (U).nes" faxanadu.mml will extract the music layer from file "Faxanadu (U).nes" and write it to file faxanadu.mml.

Another instruction will pack your music, and patch it back to ROM.

The command faxiscripts bmml faxanadu.mml "Faxanadu (U).nes" will patch "Faxanadu (U).nes" with the music from faxanadu.mml.

We also have commands for rendering Faxanadu music as midi files, both directly from ROM or from an MML file. In addition to that, we can export MML files, or music directly from ROM, to the LilyPond format, which can provide musical scores.

The command faxiscripts xmisc "Faxanadu (U).nes" faxanadu.txt will extract miscellaneous data from file "Faxanadu (U).nes" and write it to file faxanadu.txt.

Another instruction will translate the textual data and patch it back to ROM.

The command faxiscripts bmisc faxanadu.txt "Faxanadu (U).nes" will patch "Faxanadu (U).nes" with the misc data from faxanadu.txt.

There is also an instruction which will encode and decode mantras. This functionality works with a non-standard number of spawn points in the game; to be used if you added new spawn locations with Echoes of Eolis for example.

The command faxiscripts m <argument list> will encode or decode a mantra based on the argument list. The possible arguments are too numerous to mention here, but they are documented.


Roadmap

We prioritize fixing bugs if any are discovered, but here are some ideas for future features:

  • Add an option to let the linker insert a jump-instruction to bridge the gap between the safe ROM regions for iScripts and bScripts. This could potentially save some bytes over the current bridging strategy.
  • Allow Japanese characters directly in strings for the jp region
  • Emit more statistics from MML compilation; like for example total fractional drift (if any) and byte size per channel.
  • Create Notepad++ syntax highlighting definition files for bScripts, mScripts and MML

Version History

  • 2026-05-02: version 0.81

    • Fixed a bug where semicolons inside strings were incorrectly treated as comment delimiters. While semicolons are not supported in the original iScript strings, this change allows custom ROMs with custom character mappings to use them without issues.
  • 2026-04-09: version 0.8

    • Full mantra encoding and decoding
      • The CLI now supports encoding and decoding mantras. This includes full compatibility with ROMs that use extended spawn‑point counts.
      • Arguments used when encoding a mantra support unambiguous prefix matching.
    • Shop indexes assigned during iScript disassembly now based on ROM address, not order of discovery
      • This means that if you disassemble a script section you previously assembled, you will get the same indexes you used in the first place
  • 2026-02-22: version 0.7

    • Added new miscellaneous data interface for changing static data:
      • Title Screen strings
      • Player Status strings
      • Item strings
      • Rank strings
      • XP requirement per rank
      • Starting gold per rank
      • Enemy XP, health, damage, magic defense, drops (coin values, bread healing values, nothing)
      • Weapon and Magic damage
      • Armor defense
      • Wing Boot timers
    • Added support for dynamic resizing of the interaction script pointer table, allowing the script count itself to be changed (although we still enforce a minimum count of 152 - the game code has direct reference to index 151), and a maximum count of 255 (index 255 is an end-of-stream delimiter and can not be used in the game)
    • ROM loaders will now determine interaction script count and music track count from ROM data, rather than relying on external configuration
    • Added support for configuration file user overrides (eoe_config_override.xml) so that users who want overrides do not need to performa a config merge for each new release
  • 2026-02-04: version 0.6

  • 2026-01-18: version 0.51

    • Added support for exporting music from MML files, or music directly from ROM, to the LilyPond format. This can be used to engrave your music and provides an alternative way to convert music to midi. Some new directives were added to the MML format so that composers can set time signatures for their songs, or set clefs per channel, in the LilyPond output. There is also an option for adding a drum staff for the percussion channel.
    • Fixed a subtle bug where the MML bytecode generator would emit two set-length commands in a row.
  • 2026-01-12: version 0.5

    • Added support for extracting Faxanadu's music layer as MML files, which raises the level of abstraction and makes music editing easier for composers.
    • Added support for using binary constants in iScripts. Prefix binary constants with 0b or % - for example 0b00100110.
  • 2025-12-20: version 0.4

    • Added support for extracting the music layer as an assembly file in the context of Faxanadu's music engine (mScripts).
  • 2025-11-22: version 0.3

    • Added configuration xml file with necessary constants for the major ROM regions as well as for two ROM hacks. This configuration file is also compatible with Echoes of Eolis.
    • Added command-line option (-r) for overriding the automatic ROM region deduction
  • 2025-11-13: version 0.2

    • Use inline strings for both disassembly and assembly. The assembler will deduplicate all strings in the code, and allocate string indexes automatically during builds. Reserved strings will retain their indexes
    • A consequence of the assembler allocating strings is that unused strings (strings not referenced in code and reserved strings) will be discarded. In the original game data we save 460 bytes by deduplicating strings and discarding unreferenced ones
    • Include IScript syntax highlighting for Notepad++, in a new util-folder
    • Opcode EndGame will be treated as end-of-stream
    • Added better error messages in places
    • Removed "extended ROM mode" as it had no reasonable use case
  • 2025-11-09: version 0.1

    • Initial release

Credits

Special thanks to ChipX86/Christian Hammond - For entirely mapping out the scripting languages and the music engine in his Faxanadu disassembly - and for coining the terms iScript, bScript and mScript. This project would not have existed without his resources.

Another special thanks to Jessica for testing out the MML compiler and improving the MML documentation - and for providing example music files which were also added to the docs.


  • You can find me on the Faxanadu Randomizer & Romhacking Discord server, the main hub for all things Faxanadu.

    Discord

  • Dont't miss Root of Decay - An upcoming Faxanadu ROM hack by Ok Impala!

  • Check out Jessica's Alternate Soundtrack hack, a full music replacement hack for Faxanadu!

About

FaxIScripts - Faxanadu Script Assembler, Disassembler and Linker

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages