HexManiac Automation Scripts - haven1433/HexManiacAdvance GitHub Wiki
Background
When working in HexManiacAdvance, you may want to copy some data from one file to another. In another hex editor, you would just copy raw bytes and then paste the raw bytes into the new file. However, if you're not careful, this can overwrite existing data, or the data you're wanting to replace may be at a different location within the second file.
To help support this and other use cases (such as pasting into a file, or into a chat message, or into a spreadsheet), HexManiacAdvance does not copy raw bytes. Instead, it copies formatted data, which loosely matches the way the data is displayed. This means that as well as copying data, HexManiacAdvance needs to copy metadata. This allows for a number of intuitive scenarios to work the way you'd expect them to:
- Cut a block of text and paste it elsewhere in the same file. Pointers to that text will update automatically.
- Copy a few elements as a template and paste them on to the end of the same table. The table will expand (and possible repoint) and update any constants needed in the process.
- Deep copy to get a bunch of rows of data from a table of pointers. Then paste that data over some null pointers to automatically make new copies of the original, complete with formatting.
With all of these capabilities, adding on just a few extra metadata-related commands allows for a light-weight pseudo-scripting language with fairly intuitive properties. This gives rise to the idea of "paste-scripts" or automation scripts. These scripts can be typed in manually, or dropped from a file (see the Scripts Directory). What follows is an explanation of the capabilities of automation scripts, going from the most basic to the most complex.
Data
Raw content is interpreted as a raw byte, numbers, text, tuple, or enum, depending on the expected format of the selected byte. For example, if you were pasting into the trainer stats, you could paste:
Moves LEADER~2 129 117 "MISTY" "SUPER POTION" ???????? ???????? ???????? 0 - CheckBadMove TryToFaint CheckViability / 2
To represent Misty's stats.
129 117
are interpreted as decimal numbers"MISTY"
is interpreted as text. Note that the open quote is optional, but the close quote is required so that HMA knows to end the text and move to the next field."SUPER POTION" and
????????` are interpreted as enums for items. Note that items that have a space in the name must be wrapped in quotes so that the whole name is recognized as a single token. Quotes on other enums are optional.- CheckBadMove TryToFaint CheckViability /
is recognized as a list of named bits. In this case, it's Misty's AI behaviors.
There are also tuples, raw hex, and a few custom formats that are used for mixed lists like the egg moves. But this is the basic format.
Goto
Typing a @
lets HexManiacAdvance know that what's coming is supposed to be interpreted as an address. This lets you easily jump around the file.
@720000
-> goto address 720000@data.pokemon.names
-> goto the anchor named data.pokemon.names.@data.pokemon.type.names/3
-> goto the name of type 3 (usually Poison).@data.trainer.stats/brock
-> goto the first element in the data.trainer.stats table named "brock". If there's multiple elements with the same name, you can use the ~ character with a number to choose between them. See the MISTY data above for an example.@data.pokemon.stats/charizard/type2
-> Goto charizard's second type (normally flying)
Already you can probably see how the combination of going to an address and writing some data allow for the creation of human-readable 'patch' files. This is the basic inspiration for HexManiac Automation scripts.
Goto anchors that don't exist
If you try to go to an anchor that doesn't exist, the automation script will error. However, if you put () with a number after the anchor, the goto command can instead create the anchor.
@thumb.FillNames(28)
Will go to thumb.FillNames
if it exists, and if it doesn't, will find 0x28 bytes of freespace and create a new anchor there... and then goto it.
Follow pointers
If the current selection is a pointer, you can type @{
to follow that pointer, and then @}
to get back. This sounds a little strange, so here's an example:
@{ "The foe is struck\nwith a long tail,\nvines, etc." @}
This will follow a pointer, write some data, and then come back. This is useful when working with data that contains pointers where you don't care about the pointer, but you instead care about the data.
If the pointer is null, new data will be created with the correct format in freespace, and then the goto will happen as normal.
The @{
and @}
operators can be nested.
Other commands
.thumb / .end
Using .thumb
and .end
, it's possible to put thumb code directly into the editor and have it interpreted correctly. The .
prefix is used over the @
prefix used by other commands. This is to maximize compatibility with any thumb scripts found online.
!importimage(path Greedy)
Path is relative to the automation script. So if the image is in the same folder as the script, you could overwrite bulbasaur's front sprite by doing this:
@graphics.pokemon.sprites.front/bulbasaur/sprite/
@!importimage(1.png greedy)
The import options here are Greedy
, Cautious
, and Smart
, same as when importing interactively.
!exportimage(path)
Path is relative to the automation script. So if the image is in the same folder as the script, you could export bulbasaur's front sprite by doing this:
@graphics.pokemon.sprites.front/bulbasaur/sprite/
@!exportimage(1.png)
!lz
Using @!lz(length)
lets you create blank compressed data. This is important for pasting compressed data into freespace, since compressed data has different tokens then raw data.
The length is in base 10, and you usually want it to be a multiple of 32 for things like sprites and palettes.
If @!lz
is used over existing data with a different format, HexManiacAdvance will show an error. If @!lz
is used at the start of an existing. It will not produce the error if the existing data is compressed data with the exact same length. This is to allow for things like copying sprite data and pasting it over another sprite.
!00
Using @!00(length)
lets you create blank data. The data gets filled with 00
. Length is in base 10.
If used at the start of a table and the length is a multiple of the table's element length and is no larger than the table, data will be cleared from the table. If used at the start of some other data stream, if the length matches the data stream length, no data is cleared and no error is given. This is because clearing isn't really needed in this case. Finally, if used in an area that doesn't have enough freespace, HexManiacAdvance will show an error.
!put
Using @!put(value)
will write some number of bytes where you are as raw bytes, without moving the cursor. This is probably the most dangerous command, because after changing the bytes, metadata will not be automatically updated to match. So you could break compressed data, break text, or break pointers if you're not careful. Nevertheless, the ability to write a bit of data without moving the cursor is useful for a variety of situations.
.python / .end
While .thumb
/ .end
allow you to write thumb code directly to the game, .python
/ .end
allow you to gain access to HMA's python ui automation tool, so that you can make .hma scripts that can do more interesting things with loops. For example, here's an example .hma script that uses the .python directive to change all pokemon EV yields to 0:
# Change all EV yields to 0
.python
for pokemon in table['data.pokemon.stats']:
pokemon.evs = 0
.end
!game
Finally, @!game(code)
is a way to conditionally run part of a paste script, depending on the game code of the file you're editing. For example, @!game(bpre0)
will only run the following parts of the script (up to the next game
command) if you're working with a FireRed 1.0 base. Likewise, @!game(bpre0_bpre1)
will run the following parts of the script for both FireRed 1.0 and FireRed 1.1.