FORMAT
Syntax: "format"
"{"
{ type name }
"}"
A format is used to define what data is going to be extracted or inserted
into the file. Format variables are static and must be prefixed by a star
("*") when referenced.
The following types are available.
byte 1 byte
word 2 bytes
long 4 bytes
The order of the entries is respected. The first item will be output in the
file first and the last item will be output in the file last.
Ex: format
{
byte health
byte max_health
word experience
}
This structure contains a byte (health), another byte
(max health), and a word (experience.)
Formats are generally used by data-based scripts. They have little use with
the other formats.
HEADER
Syntax: "header"
"{"
{ parameter value }
"}"
The header section is used to define certain script parameters.
base_offset The suggested starting offset, if the script has a
specific use.
comment_char Which character to use for script comments.
Defaults to ";"
definition A definition file to import. Multiple definitions can
be specified.
entry_count How many entries are editable. If 0 or absent,
no limit is set.
entry_size How big an entry is.
entry_label A label name to use instead of an index
(ex, "Dagger" instead of 0x4C.)
ptr_table See below.
ptr_size How big a pointer is, in bytes. The default is 4 bytes,
if unspecified.
ptr_start The starting offset of a pointer. If specified, the
pointer is added to this.
The entry size, if specified, will make Injector calculate the offset to
load an index from by multiplying it with the size (ex, index 4 for a
10 byte structure starting at 001150A8 would be (001150A8 + 4 x 10) at
001150D0.) If unspecified, various other mechanism will be used and
entries will have to be parsed one by one if the address of a pointer
table (ptr_table) cannot be provided.
Most parameters can be accessed in other scripts by prefixing their names
with an &. Definition is the exception to this.
Ex: header
{
definition "Final Fantasy V.def"
base_offset 001158B0
entry_count 22
entry_size 4
entry_label job_names
}
While headers are optional, their use is highly suggested.
LABELS
Syntax: "labels"
"{"
{ name "{" [{ parameter value }] [{ value ":" label }] "}" }
"}"
A label is a mapping between a value and a string. The chief use of this is
to label indexes (ex, "entry 0 is 'Fire 1', entry 1 is 'Ice 1', entry 2 is 'Bolt
1'...")
When using a label, the value being matched is looked up in the table. If
an entry for the value exists, the corresponding string is output.
The following optional parameters exist.
lbl_offset If specified, labels are loaded directly from the file at this
offset.
lbl_length If offset was specified, this indicates how long each
string is.
lbl_table Which table to use, if any.
If loading the labels from a file, it is not necessary to specify value:label pairs.
Ex: labels
{
names
{
00: "Bob"
01: "John"
02: "Rick"
03: "Steve"
}
job_names
{
offset 00115800
lenght 8
table main
}
}
This creates two labels. The first, names, maps 0x00
to "Bob", 0x01 to "John", so forth. The second,
job_names, loads the strings from the file at
0x00115800. Each string is 8 bytes in length and
mapped onto the "main" table.
No parameters are necessary for the first labels, just
like no data is necessary for the second labels.
Labels can be used in a variety of places. Most string-displaying
routines have some means of outputting them.
TABLE
Syntax: "tables"
"{"
{ name "{" { value ":" character } "}" }
"}"
A table is a mapping between a byte and a character or series of
characters. This is frequently necessary when working with ROMs as
the text is rarely stored directly as ASCII.
When parsing a string with a table, every byte in the string is looked up in
the table. If an entry for the byte exists, the corresponding string is
output.
Ex: tables
{
numbers
{
00:"0" 01:"1" 02:"2" 03:"3" 04:"4"
05:"5" 06:"6" 07:"7" 08:"8" 09:"9"
}
}
This creates a table named "numbers." This table says
that when a 0x00 is read, "0" will be output.
When 0x01 is read, "1" will be output. So forth until 0x09
and "9".
Tables can be used in a variety of places. Most string-loading routines
permit the optional use of a table (by default, every byte maps to ASCII.)
COMPILE
Syntax: "compile"
"{"
"root" "{" ... "}" { mode_name "{" ... "}" }
"}"
Compile is called when a script written by the user is being compiled by
Injector. It is only available in script mode.
The compile script is different from the other script formats. It's a
collection of scripts, one of which must be named "root". When first
called, a system variable (&mode) is set to root. The root script is
parsed start to finish. If there is no data left to be parsed, it stops.
Otherwise it calls whichever script is specified in &mode and starts over
again.
Ex: compile
{
root
{
# Read the next identifier.
$command: get_token();
# Handle the commands.
if $command = "Strength" then &mode: strength ;
if $command = "Agility" then &mode: agility ;
if $command = "Vitality" then &mode: vitality ;
if $command = "MagicPow" then &mode: magic_pow;
# If we're still in root, there's a problem.
if &mode = root then error("Unrecognized token: " $command);
}
strength { write_byte(0x00); write_byte(get_value()); &mode: root; }
agility { write_byte(0x01); write_byte(get_value()); &mode: root; }
vitality { write_byte(0x02); write_byte(get_value()); &mode: root; }
magic_pow { write_byte(0x03); write_byte(get_value()); &mode: root; }
}
This script reads a token and compares it to the four instructions it
supports: "Strength", "Agility", "Vitality", and "MagicPow". If it matches
one, it changes &mode to that instruction's mode. If none of the tests
passed and &mode is still root, the user is informed they have used an
unknown command. Since root ends, the script will call itself again to
parse with the next character.
Once root is done, if no error occured and &mode is set to something
else, we call that mode. In this case, all four modes just read a value
from the user's script, write the instruction and value, then return to
root mode.
An example script to be parsed by this would be as follows...
Strength 2
Agility 3
Vitality 2
MagicPow 4
Note that this script is a very simple one. See some of the ones
included with Injector for better examples.
DECOMPILE
DUMP
Syntax: ["decompile" | "dump"]
"{"
script
"}"
The decompile and dump scripts are used to extract information from the
source file and render it in a format easier for a human to work with. The
script is run on each entry to be decompiled/dumped once. It is meant
to treat a single entry, not the entire set at once.
Decompile is the exact opposite of compile. Only available for script
mode, it produces (or at least, should produce) a script which can be
recompiled immediately without any changes. This script is produced
directly from the source file.
Dump is a more flexible version of decompile. It is meant for display
purposes only and can be used with both scripts and data. Typically,
this would be used to export data in a readable format to a file. This
data should come from the format variables.
Ex: dump
{
$name: label(job_names ¤t_entry);
output(&comment_char " ID " ¤t_entry ": " $name "\n");
output("Strength " *strength "\n");
output("Agility " *agility "\n");
output("Vitality " *vitality "\n");
output("MagicPow " *magic_pow "\n");
}
The above would simply output the character's name and all four stats.
FUNCTIONS
Syntax: "functions"
"{"
{ function_name "{" ... "}" }
"}"
Like compile, functions is also slightly different from the standard script
format.
Any function defined in functions can be called using the call() function.
They are best used in definition files, where multiple scripts can call and
reuse them.
Ex: functions
{
# Takes what's in %param1 and %param2 and calculates the sum.
sum
{
%sum: %param1 + %param2
}
# Takes what's in %param1 and %param2 and calculates the average.
average
{
call(sum);
return(%sum / 2);
}
}
These two simple functions both use %param1 and %param2 as their
parameters. They can be called from any script.
READ
Syntax: "read"
"{"
script
"}"
A read script (only available in data mode) is used to pull the data from the
source file. Normally, you would fill up format's data with this.
Ex: read
{
*strength : read_byte(0);
*agility : read_byte(1);
*vitality : read_byte(2);
*magic_pow: read_byte(3);
}
Here, we read 4 bytes (0, 1, 2, and 3) and write them into the appropriate
format variable.
WRITE
Syntax: "write"
"{"
script
"}"
The write script, only available in data mode, writes back the format
variables into the source file.
Ex: inject
{
# Just write everything.
write_all();
}
Most scripts will simply do the above, which outputs every format variable
sequentially back into the source file. However, if data needs to be
packed using some special format, this is where it would be re-packed.