RLdev: a visual novel development kit

Hæleth

Version 1.40 — 2006-06-22


RLdev program and documentation copyright © 2006 Hæleth.

The programs, code, documentation, and data files in this package are distributed without warranty under the terms of the GNU General Public License; please refer to appendix B for details.

The additional libraries documented in chapter 6 are distributed without warranty under the terms of the GNU Lesser General Public License, with an exception permitting linking to proprietary code in specific circumstances; please refer to chapter 6 for details of the exception, and appendix C for the text of the license.

This is a development release. This software is incomplete; it is known to contain some bugs that may cause it to generate unsound code, and many implementation details are subject to change. You should not use this software for any purpose whatsoever unless you are sure you know what you're doing.

This manual contains references to a number of proprietary names, including but not limited to the RealLive brand. Where such names are trademarks, they should be understood to be the property of their respective owners. In particular, the author claims no affiliation to VisualArt's KK, who have not authorised or endorsed this package in any way.

Contents

Acknowledgements



A number of people have been of great help to me in my efforts to understand and document the RealLive system: Jagarl, on whose scn2kdump utility, now developed into xclannad, I built much of my work; roxfan, whose additional reverse-engineering work has revealed the details of the DLL plugin system and many other built-in functions and functionality; and, not least, the careless programmers of RealLive games themselves, whose consistent and inexplicable failure to remove vast quantities of debugging code from their software on release has been extremely enlightening.

My thanks are also due to those users who have contributed in various ways to RLdev's development: Ed Keyes of insani, who tested an earlier version to its limits with Planetarian, also generously giving permission for me to incorporate his g00 compression code into Vaconv; 258-shi, whose introduction of RLdev to a large userbase uncovered numerous bugs; MetaFX, who helped develop the Chinese support, and Arte, who helped with Korean support; and Soulfang, my first user, without whose request the program might never have become anything more than a toy.

Preface



The RLdev package provides tools for manipulating the data used by the VisualArt's RealLive virtual machine.1

Since the actual language used by the developers of these games is undocumented, a simple programming language of my own design, called `Kepago', is used instead. The present implementation is rather basic, but higher-level features will be introduced in future versions. Anyone who has used my older Kpac package (a similar unofficial development kit for the AVG32 virtual machine) will find much here familiar, including many of the function names, although they will doubtless miss the sophistication of that rather more mature Kepago implementation.

RLdev was designed and written to make a Clannad translation possible, and extended to support Insani's work on Planetarian and my own on Kanon when the RealLive edition of the latter was released; it has never been intended to supercede other free visual novel development systems (most of which are easier to use and come with fewer restrictions), and it is certainly not intended to replace the official VisualArt's toolkit, if you're considering licensing the RealLive engine. In short, I hope this toolkit is useful, but I make no apology if the programs have any deficiencies, inaccuracies, inadequacies, or inconsistencies. You have the source code: as the sage advises, “quicumque melior potest faceret”.


1
I use the term RealLive to cover the whole current-generation interpreter series from VisualArt's. Version 1.0 was called AVG2000; the name was changed to RealLive around version 1.0.0.8.

Chapter 1  Using RLdev

RLdev is split up into utilities according to the types of file each processes. Currently there are four: Kprl to handle RealLive scenario files (see section 4.2), both as archives and separately, Rlc to handle Kepago source code, Vaconv to handle conversions of bitmap files, and RlXml to handle conversions of certain other data formats (as of 1.21, just GAN animations).

All utilities have certain things in common: running them without arguments will display a help screen, and the options --help (displays the same help screen), --version (displays a brief copyright notice), and -v, --verbose (increases the amount of diagnostic information printed) are always available.

1.1  Kprl: the archiver/disassembler

Synopsis: kprl options (files | archive [ranges])
One option is always required, to select the operation to perform. Which of files and archive is used, and what other options (if any) are available, depends on the operation.

Where archive is used, in all cases other than for the -a command, ranges is a list of integers between 0 and 9,999. Ranges can be specified, e.g. `414-416'. The files actually processed will be the intersection of the set specified on the command line and the set of files present in the archive. Omitting ranges causes all files to be processed.

Common operations

-a, --add
Updates files in archive; if the files listed are not present, they will be added, and if the archive does not exist, it will be created. ranges should be one or more filenames, which are taken to be the names of scenarios to add. Their names must begin seenN, where N is an integer between 0 and 9,999.
-k, --delete
Removes the files indicated by ranges from archive. If all files are removed, the archive will be deleted; to prevent accidents, ranges may not be omitted.
-l, --list
Lists files in archive, printing names, sizes, and compression ratio where applicable. The files listed can be restricted with ranges.
Additional options:
-N, --names
Instead of sizes, print a list of characters appearing in the scenario (not available in AVG2000).
-d, --disassemble
Disassembles files, producing Kepago source code corresponding to their RealLive bytecode. If an archive and ranges are given, files will be extracted from the archive automatically; otherwise files will be treated as a list of separate scenarios to be disassembled.
Additional options:
-o DIR, --outdir=DIR
Places output in DIR
-e ENC, --encoding=ENC
Selects a character encoding for output. Valid options always include cp9321, euc-jp, and utf-8. The default is normally cp932, but this may be configured differently depending on the settings used to build RLdev.
--bom
If the output encoding is UTF-8, causes a BOM (byte-order mark) to be prepended to all output files. The default is to leave the BOM out, but some programs (notably Microsoft's text editors) can not edit UTF-8 files without it.
-s, --single-file
By default, Kprl puts text strings into a separate resource file; this makes it much easier to translate text without being distracted by implementation details. If this option is supplied, all strings are included in the source code instead.
-S, --separate-all
Normally the only strings placed in the resource file are those which Kprl can identify as part of the game's script. If this option is supplied, all strings containing Japanese text are separated. This usually catches any parts of the game's script that the default mode might miss, but it can also clutter up resource files with false positives. It obviously cannot be used with -s.
-u, --unreferenced
By default, all code is disassembled, even where it can be proven that it is never executed (for example, code immediately following a goto or end()). Enabling this option will suppress any code that is provably unreferenced; this is normally safe.
-n, --annotate
Adds copious comments to the generated code. Two types of comment are created: offsets in the bytecode to each command are noted, and some function parameters are given labels that roughly indicate their meaning.
-r, --no-codes
By default, a number of functions which primarily affect text display are disassembled as control codes within string literals. Enabling this option will disable this behaviour.
-g, --debug-info
The official VisualArt's RealLive compiler puts a large amount of strictly unnecessary data in the bytecode for debugging purposes. By default it is ignored, but this flag causes Kprl to read it and include information from it in the source code generated.2
-t TARGET, --target=TARGET
Specifies the format of the data to disassemble (one of reallive, avg2k, or kinetic). By default, Kprl detects a format automatically. It is normally only necessary to use this option when disassembling code from a Kinetic Novel that uses kinetic.exe, since the kinetic format is subtly different from the reallive format but is always detected as the latter.
-f VER, --target-version=VER
Specifies the version of the bytecode to disassemble (either as a version number consisting of up to four integers separated by dots, or as the name of an interpreter executable to query for a version number). By default, Kprl detects a version automatically.

Less common operations

-x, --extract
Decompresses and decrypts files. As with -d, -x can process archives or standalone scenarios. The suffix .uncompressed is added to all file extensions, to prevent inadvertent clobbering.
Additional options:
-o DIR, --outdir=DIR
Places output in DIR
-b, --break
Extracts files from archive, without decompressing them.
Additional options:
-o DIR, --outdir=DIR
Places output in DIR
-c, --compress
Compresses and encrypts files. files must be a list of uncompressed scenarios. It is normally not necessary to compress files manually; Rlc compresses the bytecode it generates by default, and Kprl compresses files if necessary when archiving them. When compressing, Kprl removes the suffix .uncompressed from the file extension, if present.

1.2  Rlc: the compiler

Synopsis: rlc [options] file
file is the name of a Kepago source file to compile. Only one main file can be processed at a time, though it may include code from other files. It will be compiled to RealLive bytecode in a standalone scenario, which can either be interpreted directly or added to a scenario archive.

The following options are recognised:
-o FILE, --output=FILE
Sets name of output file to FILE. If no name is specified on the command line, Rlc will look for a #file directive in the source code; if none is found, the output file will be called rlas_output.txt.
-d DIR, --outdir=DIR
Places output file in DIR.
-i FILE, --ini=FILE
Specifies a gameexe.ini to use, or the directory containing one. Access to the gameexe.ini that will be used at runtime is required in order to read various configuration data. If none is specified on the command line, Rlc will look for an environment variable GAMEEXE; failing that, it will look in the directory containing the source file being compiled.
-e ENC, --encoding=ENC
Selects a character encoding for the input. The options and default are the same as in the disassembler (see above).
-x ENC, --transform-output=ENC
Selects a character encoding transformation for the output. See 3.5.2 for details.
-t TARGET, --target=TARGET
Specifies the output target (one of reallive, avg2k, or kinetic).

By default, Rlc attempts to detect the target automatically by looking for an interpreter executable in the directory containing the gameexe.ini being used; failing that, it falls back on RealLive as the most likely option. The #target directive can also be used to set a different target. This option takes precedence over the directive, which in turn takes precedence over auto-detected targets.
-f VER, --target-version=VER
Specifies the version of RealLive bytecode to compile for; see section 4.1.1 for the distinction between target and version. VER may be either a version number (between one and four integers separated by dots) or the path to an interpreter executable which will be queried for its version.

By default, Rlc attempts to detect a suitable version automatically by looking for an interpreter executable in the same way as for the --target option and querying its version number. If this fails, it defaults to version 1.0 (if the target is AVG2000) or 1.2.6 (for other targets). You can always use the #version directive to override this; unlike the #target directive, #version also overrides explicit version specifications.
-u, --uncompressed
Disables automatic compression and encryption of output. The output will be given a .uncompressed suffix, as though it had been compiled normally and then decompressed with kprl -x. This is only useful if you need to examine the generated bytecode.
-g, --no-debug
Strips debugging information and calls to debugging functions from the compiled bytecode. The output will be smaller and marginally faster.
--no-assert
Disables assertions (see assert()).
--safe-arrays
Enables runtime bounds checking for Kepago arrays. This option has no effect if --no-assert has also been specified.
--flag-labels
This feature is experimental and incomplete.

Enables automatic generation of the flag.ini symbol information file used by the RealLive debugger. Variables declared with the labelled directive will be added to flag.ini automatically.

Currently, flag.ini is created automatically in the same directory as the gameexe.ini in use; any existing flag.ini will be deleted automatically without confirmation, and you will have to make your own arrangements if you wish to combine labels from more than one source file or to make use of flag.ini features other than basic variable labels. The generated labels consist of the variable's name (and, in the case of arrays, its length). All labels are placed in flag group 0.

1.3  Vaconv: the graphic converter

Synopsis: vaconv [options] files
files is the names of the bitmap files to convert.

The following options are recognised:
-d [DIR], --outdir=[DIR]
Places output files in DIR. Passing this option without specifying DIR, or giving it the special value PRESERVE, causes output files to be placed in the same directory as their respective inputs. If this option is not passed at all, output files are placed in the current working directory.
-o FILE, --output=FILE
Sets name of output file to FILE. The default is the base name of the input file with an extension selected according to the target format. Note that this option can only be used when only one input file is supplied.
-f FMT, --format=FMT
Sets output format to FMT. Possible formats are PDT, G00, and PNG.

The default behaviour depends on the extension of the first input file: if it is .g00, the default output format is PNG, otherwise it is G00.
-g FMT, --g00=FMT
Allows you to select the G00 format to use (0, 1, or 2); see 1.3.1. The default behaviour is to select an appropriate format automatically based on the data.

This option implies -f G00; you should not use -f and -g together.
-i FMT, --input-format=FMT
If Vaconv is failing to recognise the format of the source file, you can bypass the auto-detection by specifying the format manually. Legal values of FMT are the same as for -f.
-m FILE, --metadata=FILE
For G00 output, allows you to supply a metadata file to specify features like regions; see 1.3.2. If no metadata file is specified, Vaconv looks for one accompanying the source file with the same base name and the extension .xml. If this is not found, default values are used.

For G00 input, allows you to override the name to be used if a metadata file is generated.

As with -o, this option can only be used when only one input file is supplied.
-q, --no-metadata
For G00 output, causes Vaconv to ignore all metadata. For G00 input, disables metadata output even where this would lead to loss of information.
-b, --best
Maximise compression. Where this has any effect, it runs significantly slower (by an order of magnitude in the worst cases) than the default algorithm; for G00 output in format 0, however, where the regular algorithm is particularly inefficient, I have seen file sizes almost halved, so it may be worth using for releases.

1.3.1  Bitmap formats

RealLive makes use of two proprietary bitmap formats: PDT and G00.

The PDT format is a holdover from the older AVG32 system, and is the standard bitmap format in AVG2000: it stores 8-bit paletted or 24-bit RGB bitmaps, together with an optional 8-bit alpha mask. In RealLive proper it is normally used only for cursor images.3

G00 is the true native format, used for most graphics in RealLive games. There are three G00 formats, each (naturally) with their own advantages and disadvantages:
0           24-bit RGB, no mask
1           8-bit paletted; palette is ARGB
2           32-bit ARGB, plus extra features (see below)

Formats 0 and 1 are usually the most efficient, depending on the type of data; format 2 is the most flexible. By default, Vaconv attempts to select the most appropriate format automatically when converting to G00.

1.3.2  Advanced G00 features

Format 2 G00 bitmaps can contain multiple sub-bitmaps, termed “regions”. These are used in some object functions (see 5.12) to create objects with multiple states, such as buttons; such functions generally have a parameter pattern which references a G00 region.

When creating a G00 bitmap, regions are defined by supplying an XML metadata file along with the source image. The format is very simple, and an example should be sufficient documentation. This is for one of the CG mode thumbnails in Clannad:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE vas_g00 SYSTEM "vas_g00.dtd"> <vas_g00 format="2"> <regions> <region x1="0" y1="0" x2="114" y2="86" /> <region x1="0" y1="0" x2="114" y2="86" /> <region x1="0" y1="87" x2="114" y2="173"/> <region x1="0" y1="87" x2="114" y2="173"/> <region x1="0" y1="0" x2="114" y2="86" /> </regions> </vas_g00>
Here five selectable regions are defined, but only two real regions. These are selectable in-game with commands such as
objOfFile (0, 'SCGMA00') // Initialises object 0. objPattNo (0, 1) // Selects region 1 (this is the same as 0, so has // no visible effect). objPattNo (0, 2) // Selects region 2 (switches to display the other // sub-image).


1.4  RlXml: the auxiliary data converter

Synopsis: rlxml [options] files
files is the names of the files to convert. Their format will be determined automatically from their extension. If they are RealLive data files, they will be converted to a human-readable XML format; if they are XML files, they will be converted back to the appropriate RealLive binary format.

Currently only two formats are recognised: GAN animation files (extension .gan), and their corresponding XML versions (extension .ganxml).

The following options are recognised:
-o NAME, --output=NAME
The meaning of this option depends on the number of files being converted. If only one is being converted at a time, then -o specifies the path and name of the converted file; if multiple files are being converted at once, -o specifies the directory in which the converted files should be placed, and their names will be derived automatically from the input files' names.

If the option is not given at all, then output is placed in the current working directory with automatically-generated names.

1
CP932 is Microsoft's version of Shift_JIS; this is RealLive's native character encoding.
2
In practice this just means the source will be cluttered up with #line directives giving the lineation of the original code from which the game was compiled, which is probably not incredibly useful.
3
Vaconv's implementation can read all PDT images, but it cannot write paletted PDTs. The paletted format is extremely rare, however.

Chapter 2  Getting started

Ease of use is a function of time, and I haven't had that much time to feed into it, so you'll have to do a bit of work to start a project with RLdev. Moreover, what's required will change (hopefully for the better) over time, so be sure to check this section again with each new release.

2.0.1  The project header file

If there is a file called global.kh in the same directory as a source file, Rlc will automatically include it, as though every source file began with the line #load 'global'. Placing configuration options and common definitions in such a file will automatically share them among every source file in the directory.

You can of course use a different name for your project header, but if you do, you will have to load it manually.

2.0.2  Memory allocation

You must identify two sections of memory for Rlc to use to allocate temporary variables. At a minimum you must provide a block of 100 local integers and a block of 10 local strings.

By default, Rlc uses the whole C[] array for temporary integers, and S[1900] to S[1999] for temporary strings. If you are writing a program from scratch, you can either avoid using these memory areas manually, or change them to something more convenient. If you are modifying an existing program, you will have to analyse its memory usage yourself to discover whether these defaults are suitable, and modify them if they are not. Be warned that any direct access or modification of a variable in a space that has been assigned to Rlc could cause your program to malfunction, but Rlc has no way to detect such conflicts automatically.

To modify the settings for memory allocation, call the rlcSetAllocation() function. You must call this function at the start of every file in your project — it is evaluated at compile-time, so it is not enough simply to call it at the start of the first scenario. The easiest thing is just to place it in your global.kh (see above), which is automatically included at the start of every scenario.

Chapter 3  Kepago

Kepago is a simple imperative programming language in the style of C or Pascal. It was designed as a substitute for the unknown language used by VisualArt's themselves. The reference implementation remains my kpc compiler for AVG32.

The present implementation of Kepago for RealLive is limited in scope; it currently lacks major features such as user-defined functions, which limits its expressivity considerably. It is, however, adequately usable for simple programming tasks.

3.1  Lexical structure

The encoding of a file is determined by the default encoding for which Rlc was compiled (this is CP932 in the standard configuration), and can be overridden on a case-by-case basis on the command line.

Identifiers can use almost any character defined in the current encoding. They are not case-sensitive. They may not begin with numbers, or with the reserved characters $ and @. Identifiers beginning and/or ending with two underscores are reserved for internal use: they are valid, but you should avoid using them except for the specific cases documented in this manual.

Numbers are decimal by default; digits can be separated with underscores, as in ML. Hexadecimal numbers are identified by a prefix $, as in Pascal. Binary numbers take the prefix $#, and octal numbers $%.

Comments come in two flavours. Line comments begin with //, and block comments (which behave like C comments, i.e. they cannot be nested) are delimited by {- and -}.

3.2  Structure of a file

A Kepago file is simply a text file containing a sequence of statements and definitions. There is no statement separator: statements are neither line-delimited like Basic nor semicolon-delimited like C and Pascal, although line breaks and commas can be used almost arbitrarily to separate them for clarity.

A file can be ended with the eof keyword; nothing following this will be processed by Rlc.

3.3  Expressions

Expressions are fairly ordinary. The following binary operators are recognised. Their meanings are identical to C, but their precedence is not:
<< >> * / % & // higher precedence + - | ^ >= > <= < == != && // lower precedence ||
In addition, the operators -~! are recognised as unary operators, again with the same semantics as in C; these all have equal precedence greater than any binary operation. Precedence can, as usual, be overridden with parentheses.

The arithmetic operators (from | upwards) can have an = appended to form assignment operators, again as in C; note however that these cannot be used on the right-hand side of an expression or within function calls. There are currently no unary increment/decrement operators and no address or pointer dereference operators.

Example:
x = 1 x += (strlen(s) + 2) * 3


When processing strings, the only permitted operator is +, which performs string concatenation. It is also legal, within integer expressions, to compare strings with the comparison operators. Apart from that it is not legal to mix strings and integers in expressions, though the various integer types may be mixed freely.

A number of Kepago constructs utilise the concept of a “constant expression”. This refers to any expression that can trivially be fully evaluated at compile-time. A constant expression may contain the full range of operations, and it may refer to constant symbols, but not variables. It may also contain calls to certain functions, such as max(), that can be evaluated at compile-time, and to a set of macros, such as defined?(), that are always evaluated at compile-time.

3.4  Statements

In addition to the statement types detailed in this section, it is also valid to use a string expression by itself as a statement, which is how text display is normally accomplished. This topic is discussed in depth in section 3.5.

3.4.1  Blocks and scopes

Anywhere a statement is valid, it can be replaced with a block. Blocks are opened with : and closed with ;, and function in much the same way as braces in C-style languages.

Statement blocks double as scoping constructors. Symbols are visible only within the scope in which they are defined, or scopes nested within it. For example:
int foo = 1 if (foo == 1): int bar = 2 'foo is \i{foo} and bar is \i{bar}' // prints "foo is 1 and bar is 2" pause; 'foo is \i{foo}' // prints "foo is 1" 'bar is \i{bar}' // compile-time error: bar is not visible in the outer scope pause


3.4.2  Labels

While higher-level control structures should be used where possible, program flow may also be controlled with goto, gosub, and related functions. Labels for use with goto statements can be defined at any point where a statement is valid; a label is any valid identifier beginning with @. The scope of a label is global; if the same label is defined multiple times, a warning is issued and the later definition is used.

Example:
@loop 'This text is displayed repeatedly in an infinite loop.' pause goto @loop
You should, of course, write this instead:
while 1: 'This text is displayed repeatedly in an infinite loop.' pause;
Rlc optimises conditional tests away when the conditions can be evaluated at compile-time, so it generates identical code in both cases.

3.4.3  Variable declarations

Declaration syntax

Declaration statements are of the form

type [(directives)] variables

type
is the type given to all the variables declared by this statement, directives is an optional parenthesised list of flags that control their declaration, and variables is a comma-separated list of declarations.

Each declaration in variables consists of an identifier which may optionally be followed by an array declaration (see next section), an initial value, and/or an address specifier. Initial values are declared with the form identifier = value; addresses are declared with the form identifier -> space.address. For example:
byte a // value undefined, address automatic byte b = 2 // initialised to 2, address automatic byte c -> MEMARR_A.4000 // value undefined, address A8b[4000] byte d = 2 -> MEMARR_Z.10 // initialised to 2, address Z8b[10]


The types currently supported are:
int           32-bit integer
byte           8-bit integer
bit4           4-bit integer
bit2           2-bit integer
bit           1-bit integer
str           character string

The directives currently supported are:

block
Ensures that all variables declared in the one statement are allocated contiguously, and, in the case of integers smaller than 32 bits, ensures that they are packed.

An example will illustrate the use of this:
int x, y, z sum (x, z) // value is undefined int(block) x, y, z // x, y, and z guaranteed to be contiguous sum (x, z) // guaranteed to equal x + y + z
zero
Ensure that the allocated memory is initialised. Integers not otherwise given initial values will be set to 0; strings will be set to the empty string.
labelled
If the --flag-labels option was passed to Rlc, entries will be created in flag.ini for variables declared with this directive.
ext
Variables declared ext are not constrained by enclosing blocks; they remain in scope permanently from the point of their declaration unless deallocated manually.

Arrays

Arrays are allocated in the same way as other variable declarations, with the addition of an array declaration immediately following the variable's identifier. This consists of the standard square brackets surrounding an integer constant giving the array's length.

An array can be initialised in any of three ways. Firstly, if the zero directive is included in the declaration, the memory allocated will be cleared automatically. Secondly, if a single expression is given as an initial value, every member of the array will be initialised to that value. Finally, individual members of the array can be initialised by providing a tuple as the initial value:
int a[5] = { 1, 2, 3, 4, 5 }
In this last case, and only in this case, the square brackets may be left empty, and the length of the array will be set automatically to the number of elements provided:
str strings[] = { 'foo', 'bar', 'baz' }


Only one-dimensional arrays are supported.

Caveats

Memory allocation is currently handled statically based on the settings passed to rlcSetAllocation().

All blocks allocated are word-aligned: that is to say, bit x, y, z allocates three whole words even though only three bits are being used. You can get round this by allocating variables of smaller sizes in arrays or with the block directive.

Be aware that the variable allocation system is not sound.
if 1: int x = 0 gosub @oops 'x = \i{x}' pause ; // end of scope, x implicitly deallocated end @oops int y = 1 // y allocated at the same address as x ret
prints x = 1, not x = 0, even though the value of the logical variable x was never modified! Safe usage of the current system requires that any variables be allocated outside the largest scope in which they are used; in the example above, x would have to be declared at the top level, and any variable which is to be used after a call to another scenario should be declared globally in a project header file to ensure that its memory will not be used for anything else unintentionally.

A future version of Rlc will hopefully introduce dynamic memory allocation, which will mitigate these problems to some extent.

3.4.4  Constant declarations

“Constant” implies that the value is always known to the compiler, not that the value is immutable; Kepago constants are actually more like compile-time variables than constants in the strict sense.

Constants can hold integer or string values, and can be used in expressions wherever a literal would be valid.

They are handled with the following directives (see also 3.4.5).

#define IDENT
Defines the symbol IDENT. Multiple symbols can be defined at once, separated by commas. The value bound to IDENT is a non-zero integer.

The scope of a symbol defined with this directive is not constrained by blocks; it remains defined in all code that is compiled subsequent to its definition. In the current state of the implementation, this effectively means that it remains defined to the end of the file. Once user-defined functions are implemented things will become a little more complicated.
#undef IDENTS
Undefines the given symbol(s).

At present, the only symbols which may be undefined manually are those which were defined globally with #define.
#const IDENT = EXPR
Defines a new constant symbol. The constant expression EXPR (see 3.3) is evaluated, and its value assigned to IDENT.

Multiple symbols can be defined at once, separated by commas.

The scope of a symbol defined with this directive is limited to the current block.
#set IDENT = EXPR
Mutates the value assigned to the symbol IDENT. This does not affect the symbol's scope.

As a shortcut, you can use the form #set foo += 1 instead of #set foo = foo + 1; the same goes for other arithmetic operators.

3.4.5  Directives

Like in C, directives (that is, statements which modify compiler state rather than generating code) begin with a hash sign #; unlike in C, these are processed by the compiler itself, not a preprocessor.

The following directives are recognised in the current version of Rlc:

#load 'FILE'
Loads a header file. Rlc looks first for FILE, then for FILE.kh, first in the same directory as the source file, and then in the RLdev library directory. The contents of the file are parsed at the current position in the same way as C's #include directive.
#file 'FILE'
Sets a default output filename. This can be overridden on the Rlc command line with the -o option.
#target TARGET
Selects a default target; TARGET should be RealLive, AVG2000, or Kinetic. This can be overridden on the Rlc command line with the -t option. Note that the parameter is an identifier, not a string.
#version A[.B[.C[.D]]]
Selects a bytecode version to generate code for; this is the equivalent of the -f command-line option, and functions identically.
#resource 'FILE'
Loads the named resource file, providing access to all the strings it contains. The scope of its definitions extends from the #resource directive to the end of the file; it must precede any #res directives referencing its contents.
#res<ID>
Inserts the resource string with the identifier ID at the current position. See section 3.6 for details.
#entrypoint INDEX
Places an entrypoint at the current location in the code; entrypoints function as labels for the jump() and farcall() functions to jump into a scenario. INDEX should be an integer between 0 and 99 inclusive.

If INDEX is 0, the directive is ignored. Entrypoint 0 always comes at the very start of the scenario, and Rlc defines it automatically. Superfluous entrypoint directives are accepted for compaitibility reasons.
#character 'NAME'
Adds NAME to the dramatis personae table in the header of a bytecode file. This is what kprl -lN reads.

Character name data appears not actually to be used by RealLive; its purpose is a mystery, but it's probably just debug information. It does not exist in AVG2000, so the #character directive is ignored when compiling for that target.
#line LINE
Tells Rlc to start counting lines from the given value, rather than the actual line position in the source file. May be of use if you want to preprocess code or use literate programming tools, but it's really only there to give the disassembler something to do when asked to read debug information.
#kidoku_type TYPE
Determines the format to use for kidoku markers in generated bytecode. If the executable is to be run with RealLive 1.2.6.6 or later, this should usually be 2, although it appears not to be critical that this be the case; in all other cases it must be left with its default value of 1.

Note that this is a global setting; the value that will be used for a given output file is that assigned most recently when the end of the file is reached.
#print EXPR
Causes the compiler to print the current value of EXPR to stderr. EXPR must be a constant expression, but it can be an integer or a string.
#warn EXPR
As #print, but the output is formatted as a compiler warning.
#error EXPR
As #print, but the output is formatted as a compiler error, and compilation is halted.

3.4.6  Conditional compilation

The code to be compiled can be selected at compile-time by using a further set of directives. As with the others, note that these are not related to any sort of preprocessor. They are block statements: they can only be used where a statement would be valid, and the code they enclose is analysed for syntactic validity even if it is not compiled.

#if EXPR
#elseif EXPR
#else
#endif
Select a block of code to compile based on the value of EXPR:

#const First = gameexe('SEEN_START') #if First == 1 #print 'The game begins with scenario 1' // Code for this case #elseif First == 2 #print 'The game begins with scenario 2' // Code for this case #else #error "The game begins with scenario \i{First}, and I don't \ know what to do" #endif


Unlike :; blocks, the contents of these directives are not counted as blocks for scoping purposes.
#ifdef EXPR
#ifndef EXPR
It is often convenient to be able to determine whether a given symbol has or has not been defined. These two directives are shorthand ways to do this: they are exactly equivalent to #if, except that every identifier in EXPR that is not directly compared with anything else is replaced with a call to the defined?() macro.

#ifndef foo && bar && baz > 1 // -> #if !defined?(foo) && !defined?(bar) && baz > 1 {- do stuff -} #endif

3.4.7  Control structures

if CONDITION STATEMENT
if CONDITION STATEMENT else STATEMENT
A standard conditional control structure. CONDITION is evaluated; if the result is non-zero, STATEMENT is executed. The optional else allows a statement to be specified for execution if the condition evaluates to zero.

Kepago adopts the usual rule to resolve the `dangling else' problem: each else is taken to apply to the innermost available if. This can be overridden by use of blocks:

if rnd(10) < 5: if SyscomEnabled, 'your code here'; else 'Without the :; above, this would attach to \ the inner `if\'.'
while CONDITION STATEMENT
If the value of CONDITION is non-zero, STATEMENT is executed repeatedly; CONDITION is checked after each iteration, and the loop exits when it evaluates to zero.

You can exit the loop prematurely with the break statement, or jump straight to the next iteration with the continue statement.
repeat STATEMENTS till CONDITION
The opposite of while. STATEMENTS is executed at least once, and the loop continues for as long as CONDITION is zero.

For historical reasons, and unlike the other structures in this section, the repeat loop is a block in itself: you can include multiple statements without a :; block.
for (INITIALISATION) (CONDITION) (INCREMENT) STATEMENT
A C-style `for' loop.

for (int i = 0) (i < 10) (i += 1): do_stuff(i);
is equivalent to
: int i = 0 while i < 10: do_stuff(i) i += 1;;
case EXPR
of CASE
other
ecase
EXPR is compared to each CASE in turn; if a match is found, the following code is executed. The other clause is equivalent to an of clause, but always matches; it is optional, and if present must be the last clause.

As with C's `switch' statement, each case should be terminated with a break statement, or execution will fall through to the next case. This is useful where multiple values require the same treatment:

int qual = SoundQuality 'Sound mode is ' if qual % 2, '16-bit ' else '8-bit ' case qual of 0 of 1 '11 kHz' break of 2 of 3 '22 kHz' break of 4 of 5 '44 kHz' break other '48 kHz' ecase pause
Rlc will attempt to optimise the test and jumps away if EXPR can be evaluated at compile-time. In certain cases it may be desirable to know whether this optimisation has succeeded (for example, when writing other clauses that raise an error, you may wish to raise the error at compile-time in such cases). Rlc therefore defines the symbol __ConstantCase__ when compiling case blocks; if it is non-zero, then the innermost case block currently being compiled has been optimised away.

3.4.8  Function calls

With one or two exceptions, function calls use the standard C-like syntax: the function name followed by a list of parameters, bracketed and comma-delimited.

Anything valid on the right-hand side of an assignment expression is also valid as a function parameter. Certain functions permit other constructs, such as tuples (parameters containing multiple expressions; in Kepago these are enclosed in curly braces), and one or two have wholly different syntaxes. All these exceptions are documented under the appropriate function in chapter 5.

Unknown functions

It may sometimes be desirable to make use of a function that exists in RealLive but is not currently exposed through the Kepago/RealLive API. There exists a `raw opcode' command that can be used to insert such functions: it has the format `op<type:module:opcodeoverload>', which can be followed with a parenthesised parameter list as any function (but cannot be used in assignments). The variables are all integers; type and module appear to be used to identify groups of related functions, opcode identifies a particular function (its value is only unique within a particular type/module combination), and finally overload identifies an instance of that opcode (different instances have the same broad semantics, but take varying numbers of parameters).

Mnemonic aliases are defined for some values of module, in the hope that this may give some hint as to the purpose of an unknown function when Kprl disassembles a scenario containing it. For example, the pause function can also be invoked with op<0:Msg:00017, 0>.

In the general case, however, it is preferable to add support for the function to the API than to use it with op<>. This can be accomplished by editing reallive.kfn, or more easily by providing the author with an example and description, upon which he'll happily do it for you.

3.5  String handling

Strings can be delimited with either ' or "; there is no semantic difference between the two. Due to the limitations of the RealLive system itself, only JIS characters are valid in strings. To use within a string a quote of the type being used to delimit that string, it must be escaped with a backslash; line breaks must also be escaped, and any whitespace at the start of the following line will be ignored, unless this too is escaped:
str s = "This string's value \ is a single line, spaced\ \ normally.")


Note however that arbitrary characters may not be escaped: any escaped alphabetic character is taken to open a control code, as will an escaped left brace.

3.5.1  Displaying text

Display strings (that is, strings used directly as statements, rather than in expressions) are passed directly to RealLive's text display routines. When one is encountered, a text window of the currently active type is opened and the text printed in it. (Text window types are defined in gameexe.ini, as described in 7.2.3, and selected with the TextWindow() function.)

3.5.2  Usable characters

The RealLive bytecode format requires all textual data to be in the Shift_JIS encoding. By default, therefore, RLdev converts text to Shift_JIS: it follows that the only characters which are valid in Kepago strings are those in the JIS X 0201 or JIS X 0208 character sets. This is perfect for Japanese text, and sufficient for most English.

Other languages require characters not present in the JIS character sets. There are two solutions to this issue. One is to fake the required characters: some glyphs can be included as bitmaps with \em, others (such as accented letters) can be built up through overprinting by using \mv. A better solution — in some cases, such as Chinese, the only solution — is to use a non-JIS character set, with a non-standard encoding that has the same characteristics as Shift_JIS, and to arrange for this to be decoded on the fly in an intermediate layer between RealLive and the operating system.

In RLdev, this is accomplished in three stages:

First, the UTF-8 encoding should be used for source code files. This is the only supported input encoding that can handle non-Japanese text. If it is not the default encoding for your copy of RLdev, you will have to specify the option "-e utf8" when compiling the code.

Second, an output encoding transformation is applied. This is done by passing the "-x ENC" option when compiling the code, where ENC is the name of a supported transformation (see below).

The final step is to arrange for the RealLive interpreter to be able to decode the transformed text. This can be done either by modifying the interpreter itself, or by using an extension DLL to hook into it at runtime. A suitable DLL is provided in the form of the rlBabel library (see 6.1).

RLdev currently supports the following transformations:
None
For Japanese or ASCII text. This is the default.
Chinese
For Simplified Chinese text. The output uses a non-standard encoding of the GB2312 character set (roughly the same encoding as the Key Fans Club's Clannad translation, but with a few subtle differences).
Korean
For Korean text. The output uses a non-standard encoding and character set. The characters encoded comprise the non-hanja parts of KS X 1001, plus the 4000-odd additional precomposed hangul from the ill-fated Unicode 1.1: this appears to be the simplest practical compromise for encoding modern Korean text, though the encoding could potentially be expanded further if necessary.
Western
For Western European text. The output uses a non-standard encoding of the Windows CP1252 character set (ISO-8859-1 with extensions).

Since this encoding uses double-byte characters to represent some half-width characters, it should be used with the rlBabel lineation library (6.1.2) to ensure correct character spacing.

3.5.3  Control codes

Control code syntax is TEX-style, \identifier{}, where what goes between the curly braces has the same syntax as the contents of the parentheses in a normal function call — in most cases the braces are either empty or contain a single integer or variable. Note that for control codes of more than one letter, braces are always required, even if no parameters are being passed to the code.

Descriptions of the basic control codes currently available follow. In addition to these, there are also more complex codes \name and \g, described in the following subsections, and a set of control codes only valid in resource files, described in section 3.6.

\n
Forces a line break at the current position, retaining indentation.
\r
Forces a line break at the current position, resetting indentation.
\p
Inserts a pause. Text display will halt until the player clicks to advance, but it will then continue where it left off, without clearing the screen.
\wait{time}
Pauses time ms before continuing to print text.
\m{name}
\l{name}
Inserts the value of a name variable: \m for global names, and \l for local names. name can be either an integer (the index of the variable, as used in calls to the GetName() family) or one or two alphabetic characters (`A' to 'ZZ', as used in the #NAME variables in gameexe.ini). See 4.4.2 for details of name variables.

A second argument may optionally be supplied, which should be a constant integer. If this is given, it identifies a single character of the name to be printed (zero-indexed).
\i{int}
Displays the value of int, which can be an arbitrary integer expression.

You may optionally supply a minimum length, of the format \i:length{int}; if this is supplied, the number will be left-padded with zeroes to ensure that it is always at least length digits.
\s{str}
Displays the value of the string variable str.
\size{[pixels]}
pixels is optional. If it is given, the font size will be set to that size; if it is not, the font size will be reset to the default.
\c{idx[bg_idx]}
Sets the colour of the following text. The values are indices to the game's palette, which is defined in gameexe.ini with the #COLOR_TABLE command. \c can also be used without any arguments, which resets the colours to their defaults.

For example, if COLOR_TABLE.001 were red and COLOR_TABLE.002 were green, someone addicted to pointless examples could write
'I like using \c{1}red\c{} text, and sometimes \ \c{1, 2}red with a green shadow\c{}.'


This control code behaves in the same way as the FontColour() function: the colour will be reset automatically at the end of the string. If you want to set multiple strings in the same non-default colour, you will have to use \c at the start of all of them.
\ruby{text}={gloss}
Used to display interlinear glosses or ruby, also known as furigana. text will be displayed normally, with gloss printed above it in small type.

You must set the #WINDOW.LUBY_SIZE (sic) variable for the current window to an appropriate size in order to use this control code.
\e{index, [size]}
\em{...}
Prints a bitmapped character or icon at the current point in the text. \e prints the bitmap in full colour; \em takes its alpha channel and displays that in the current font colour.

Bitmaps are drawn from files defined with the #E_MOJI settings in gameexe.ini. You must define at least E_MOJI.000 in order to use these codes. If multiple files are defined, they should contain the same characters at different sizes.

The bitmap used is always the largest available that is in height smaller than or equal to the current font size; if no matching bitmap is available, a space is left. The optional size argument can be used to force the font size to a particular size temporarily, to select a particular size manually. Note however that in the current implementation it also resets the font size to the window's default - you will have to follow it with a \size code if you were working at a different size.
\mv{x y}
\mvx{x}
\mvy{y}
Move the insertion point (i.e. the offset of the next character) by (x, y) pixels.

If the new offset is beyond the right-hand margin of the text window, the text will automatically wrap, effectively placing it at the start of the next line instead. Other margins are not checked, but text will always be clipped to the window margins; you can place text above or to the left of the window, but only those parts of letters within the margins will be displayed.

There are three major uses for these codes. The first is character composition by overprinting:
#const cw = gameexe('WINDOW.000.MOJI_SIZE') / 2 + gameexe('WINDOW.000.MOJI_REP', 0) / 2 'A t^\mvx{-cw}ete-`\mvx{-cw}a-t^\mvx{-cw}ete with Sayuri' page
displays “a tête-à-tête with Sayuri” fairly reliably, even though the accented characters do not exist in most Japanese fonts. (Note the code that calculates the width in pixels of a Latin character - this would need modifying if you were not printing to window 0, or not using the default font size.)

The second use is printing superscript and subscript text:
'\size{26}x\size{16}\mvy{-1}2\mvy{1}\size{26}' // x^{2} ' +\mvx{8}' // + 'log\mv{1,13}\size{18}16\size{26}\mv{6,-13}y' // \log_{16} y '\mvx{8}=\mvx{8}z\size{}' // = z page
displays a reasonably nicely spaced version of “x2 + log16 y = z” in most fonts (observe the use of \mvx{8} to insert a narrower-than-usual space).

The final use is printing non-square bitmapped characters: '\e{0}\mvx{-4}' would be a suitable way to display a bitmap containing a character four pixels narrower than the font height.

These codes are also used by the rlBabel DLL to implement proportional text output (see 6.1.2 and the rlBabel source code for implementation details).
\pos{x y}
\posx{x}
\posy{y}
As \mv etc., except that the coordinates are interpreted as absolute offsets from the origin, rather than relative to the current insertion point.

3.5.4  Names

As display strings are normally used for game text, RealLive naturally provides means for identifying character names. This is accomplished with the \name control code.

The format of this code is \{text}. text is arbitrary text, which is used as the current character name.

Note two unique features of this code. Firstly, it has no identifier; you can use \name{} as well, but \{} is the canonical form. This is just to save typing, as this is the most common control code. Secondly, if it is followed by a space, that is gobbled. This permits you to write '\{Foo} "..."', and have it appear correctly spaced in the output. If you actually wanted a space after the name, escape it with another backslash: '\{Foo}\ "..."'. (You probably won't like the results.)

The effect this has is can be controlled to a great extent by settings in gameexe.ini. If #WINDOW.NAME_MOD is non-zero for the current window style, the name will be displayed in a separate smaller window, based on the other #WINDOW.NAME settings. If #WINDOW.NAME_MOD is zero, the name will be printed inline at the current position, and followed with a space; in this latter case, if #WINDOW.INDENT_USE is non-zero, an indent point will then be set at the new x offset in the text window, meaning that subsequent lines of text will begin at that offset.

A separate but related topic is the use of name variables to store customisable character names. As described in section 4.4.2, these can be referenced directly from within display strings with the \l and \m control codes. For example, a common idiom to set the speaker name to the player is the code \{\m{A}}.

3.5.5  Glosses



Kepago provides for hypertext glosses using a control code \g. The syntax is \g{text}={gloss}. text is arbitrary text, which is always output in the normal way, and may be highlighted in some way. When it is clicked on, the value of gloss is passed as a string to a handler routine, which would typically display it in a subsidiary window.

Support for these is limited in Rlc. The control code is always accepted, but it is only processed when the rlBabel library is in use for dynamic lineation (see 6.1.2); in all other cases, text is simply displayed as normal text.

3.5.6  Lineation

Since the RealLive system is designed for use with Japanese only, it does not implement the more complex line breaking logic required for Western languages. In Japanese, it is acceptable to break a line anywhere, including in the middle of words, so this is the behaviour RealLive adopts. This makes it necessary to implement lineation specially for Western text.

Versions of RLdev up to 1.03 implemented static lineation. Since this is impossible to do correctly, however, this feature has been removed and replaced with various dynamic lineation techniques, which ensure that text is always correctly lineated even in the presence of variable-length elements. These are implemented as libraries, and disabled by default; see section 6 for usage details.

3.6  Resource files and resource strings

It can often be desirable to separate program logic from the game's script; for example, if one is releasing a game in several languages, it is more convenient to provide translators with just the text, while if one is releasing versions of the same game with different code (for example, adult and all-age versions) it can be convenient to be able to use the same script with both. Resource files provide a simple means of accomplishing this.

3.6.1  Resource file syntax

A resource file is basically an association table of keys to strings.

A key is defined by enclosing it in angle brackets. Keys can contain any combination of characters, with a few restrictions. Firstly, purely numerical keys are treated as numbers: <$00d> identifies the same string as <13> and <0013>. Secondly, keys may not contain spaces, line breaks, or the characters > and }. Both of these restrictions can be worked round by quoting the key; such a key uses the normal string literal syntax, can contain any character, and distinguishes between different representations of integers.

Each occurrence of a key begins a new string; the text between it and the next key or the end of the file is interpreted as a resource string. The syntax of a resource string is broadly similar to that of a display string literal (see section 3.5), with a few exceptions:

3.6.2  Additional control codes

There are also some additional control codes that can be used in resource strings.

\d
Where two versions of a script are being produced, and one requires fewer strings than the base version, you can use \d to remove superfluous strings without modifying the Kepago source code.

The resource string becomes a `remove string' command; referencing it will remove the reference, and if it is referenced by a display string command that is followed by a pause() call, the pause will also be removed.
\a{[str]}
Where two versions of a script are being produced, and one requires more strings than the base version, you can use \a to add extra strings without modifying the Kepago source code.

The current resource string is processed as normal, but when it is referenced by a display string command, the resource string <str> will be added after it as another display string command; if the referencing command was followed by a pause() call, another pause() will be added after the new string. If multiple \a codes appear in one resource string, the extra strings will be added in the order of the \a codes.

str is optional. If it is not given (an `anonymous reference'), the next string in the resource file will be used; see 3.6.3 below for how multiple anonymous references are resolved.

3.6.3  Anonymous references

If multiple anonymous references are given in a single string, they are resolved sequentially and recursively: that is, anonymous references within a string referenced anonymously are resolved before any further anonymous references in the original string.

An example may make this clearer:
<foo> This is foo.\a\a <bar> This is bar.\a <baz> This is baz. <quux> This is quux.
This is equivalent to writing
<foo> This is foo.\a{bar}\a{quux} <bar> This is bar.\a{baz} <baz> This is baz. <quux> This is quux.
Note how the second \a in <foo> resolves to <quux>, because <baz> has been taken by the \a in <bar>.

Resource strings for use with anonymous references can be anonymous themselves: that is, you could also write
<foo> This is foo.\a <> This is anonymous.
This can be clearer to read, and has the advantage that anonymous resource strings normally generate an error, so you will be able to tell whether you have included the correct number.

3.6.4  Using resource strings

To load a resource file and make the strings it contains available to your code, use the #resource directive at the start of the Kepago source file.

To reference a resource string, use the #res directive with the key of the string you wish to include.
1
LATEX users may expect spacing to be automatic, but the syntactic similarities to LATEX are coincidental; `and!the', where the ! is a line break, will produce `andthe', not `and the'.

Chapter 4  The RealLive system

4.1  Overview

RealLive is an engine designed to power bishōjo games such as visual novels and simple simulations; it is based around a fast Turing-complete bytecode interpreter, and provides functionality for text and graphics, sound, music, and simple animation, along the lines that such games require. Notable products based on the RealLive engine include Key's Clannad, Planetarian, and Kanon Standard Edition, and 130cm's Princess Bride (not to be confused with the book/film of the same name).

A RealLive game is made up of four main parts: the interpreter (avg2000.exe, reallive.exe, or kinetic.exe), the configuration file (gameexe.ini), the scenario file (typically seen.txt1), and accompanying data files, which vary in type and number, but typically include graphics (in the g00 and pdt formats), animations (in the gan and anm formats), and music (in the nwa format). Of these parts, RLdev deals only with the scenario file and graphics, although Rlc reads certain settings from gameexe.ini when compiling.2

Note that in the case of DRM-protected games, the configuration and data files are further compacted into a single file (typically kineticdata.pak), which is encrypted using a rather stronger method that I have not yet discovered how to unlock. It is not possible to access the files this contains directly with RLdev, although it is simple enough to extract the contents manually from a memory dump of a running game.

There exists a clone of the RealLive interpreter, Jagarl's xclannad (currently at http://www.creator.club.ne.jp/~jagarl/xclannad.html). The latest version at the time of writing, 0.06, did not implement enough features to be a viable replacement, but it promises to become such with time.

4.1.1  Targets and versions

The family of interpreters I refer to here as RealLive actually comprises three more-or-less distinct interpreters: AVG2000, RealLive, and Kinetic. While the API and basic bytecode format used by all three is essentially identical, they are not compatible: AVG2000 (the original successor to AVG32, later renamed to RealLive) uses a different scenario header and encoding scheme, and Kinetic (a special DRM-enabled interpreter used only for the Kinetic Novel series) reassigns a large number of fundamental operations in what appears to be a futile attempt to make reverse-engineering harder.

To complicate matters, however, the API itself is under constant development. There are significant differences between versions. For example, the grpRotate() functions were introduced in 1.1.5, and auto mode early in the 1.2 series. There is no fixed mapping between the API/bytecode version (the “version”) and the header/basic operation types (the “target”): the AVG2000 interpreter was retired at around version 1.0.0.8, and the Kinetic interpreter introduced at version 1.2.6.4, but RealLive-proper interpreters can be found at all stages of development.

When compiling for a RealLive interpreter, therefore, the target and version must both be chosen separately. Bytecode compiled for a 1.1-series RealLive will normally run perfectly on a 1.3-series RealLive,3 but the converse is not true, and bytecode compiled for RealLive 1.2.7.0 is incredibly unlikely to run in Kinetic 1.2.7.0. Since different versions of games sometimes use incompatible interpreters, you will probably have to recompile code with different flags or directives if you are working on such a game. In most cases, however, Rlc will manage to generate suitable code for any interpreter from any source code, given the correct compilation settings.

Except where otherwise specified, the version documented here is basically 1.2.6.8 (as supplied with Kanon Standard Edition). Other versions have not been exhaustively tested: differences and limitations are documented where I'm aware of them, but in general you should not assume that any feature mentioned in this manual is necessarily available, particularly if you're working with a 1.0- or 1.1-series interpreter. Note that very few tests have been carried out with the 1.0.0.8 interpreter, and practically none with AVG2000.

4.2  Scenarios

The scenario file contains the game logic. It is a simple archive of up to 9,999 individual compilation units, termed `scenarios', which are named seen0001.txt to seen9999.txt; each of these is an individually compressed and self-contained block of RealLive bytecode.

The scenarios represent the major divisions of a program; only code from one scenario can be accessed at any one time, though switching between them can easily be done with the jump() and farcall() functions, and up to 100 entrypoints may be defined within each scenario, effectively permitting access to arbitrary locations. A common idiom is to define several related functions (particularly where they share code) in one scenario, and use farcall() with an entrypoint index to access them from the rest of the game; this works on the same principle as linking a library into a C program, but in Rlc it must be done by hand.

It is not necessary to build a scenario file in order to run code: if a file of the form seenNNNN.txt exists in the same directory as the scenario file, its contents will override the scenario of that number. This can be convenient when debugging, and is the only trivial way to inject custom code into a DRM-protected game.

4.3  Debugging

The RealLive interpreter contains a convenient debugger, which can be enabled by defining #MEMORY in gameexe.ini. The various debugging features can be accessed with function keys or from drop-down menus. Of particular use are the single-step execution mode (F3), the memory editor (F5) and the graphics DC viewer (F7).

Debug mode also enables a number of runtime warning messages relating to matters such as memory management; these do not always represent problems, but it's probably wise to try to eliminate them anyway.

Finally, there are various message-box and input-related functions which are only processed in debug mode. See 5.15 for a full list.

From version 1.3 onwards, single-step mode is replaced with a simple source debugger. This is usable with Kepago - you must rename your source file to have the extension .ORG and set up the path to that file in the debugging options dialog within the interpreter - but does not work very well with Kepago features like header files, and it is not likely to be usable with disassembled code (in which lineation information is never preserved), so its utility to developers using RLdev is limited. It does, however, provide a slightly more intrusive form of single-step execution regardless of whether source code is available or not.

4.4  Memory

Only statically allocated memory is available, in the form of a number of arrays of variables; there is no stack.

Variables are divided into `local' and `global' types. The values of local variables are reset when the program is executed, and stored in saved games. Global variables are persistent, and their values are shared between all saved games.

4.4.1  Integers

Integers

RealLive provides unsigned 1, 2, 4, and 8-bit integers, and signed 32-bit integers; these share the same memory, so individual bits and bytes can be examined and modified without resorting to bitwise operators and shifts. There are eight separate integer spaces, each of 8,000 bytes.

Local 32-bit integers are stored in A[0] to A[1999], and likewise B[] to F[]; G[] and Z[] are global equivalents.

The smaller elements are accessed through arrays A8b[], A4b[], A2b[], Ab[], and similarly named equivalents for B[] to G[] and Z[]. Indexing is based on the element size: the 8-bit arrays have elements from 0 to 7,999, arranged such that the four bytes of A[100] can be accessed as A8b[400] to A8b[403], in the little-endian order; this pattern is consistent, so for example Gb[42784] shares the same memory as the least significant bit of G[1337].

These memory spaces are accessed through variables, either those that you define yourself, or a set of built-in arrays that correspond directly to the memory spaces. To avoid restricting useful single-character identifiers, Rlc adds the prefix `int' to the names in declaring these arrays. For example, the memory cell A[100] may be accessed by referencing the variable intA[100].

The store register

There also exists a special integer variable store. This is a register used to return values from functions such as strlen() and select(); in all other respects it behaves exactly like any other variable.

While technically these functions do not have return values, Kepago permits you to treat them as though they did: for example, the code that RealLive bytecode represents literally as
strlen(strS[100]) intA[10] = store
can also be written
int x -> MEMARR_A.10 str s -> MEMARR_S.100 x = strlen(s)
It is sometimes more efficient to access store directly:
strlen(s) if store > 5 && store < 10...
generates code marginally more efficient than either repeating the strlen() call or assigning its value to a variable would. Note however that Rlc makes no guarantee that it will not generate code that affects the value of store; in general you cannot assume that its value will remain unchanged between two statements.

4.4.2  Strings

String variables

RealLive strings are null-terminated character arrays; allocation is handled automatically. Their length is not limited at runtime, but only up to 10,000 characters are stored in saved games.

The array S[0] to S[1999] holds local string variables. In RealLive (but not in AVG2000) there is also an array M[], of the same size, the contents of which are global.

As with integers, this memory can be accessed either through variables you declare or through two built-in arrays, strS[] and strM[].

Name variables

In addition to the normal string variables, there also exist some special variables designed primarily to hold character names. These cannot be accessed directly in source code, but they can be included inline in strings.

There are 702 global name variables; they can each hold between 12 and 20 characters, depending on the #NAME_MAXLEN setting. Their default values are set in gameexe.ini with the #NAME variables (NAME.A, NAME.B, and so on). Their values can be read and modified with the GetName() and SetName() functions. Within strings, they are included with the control code \m: the first is \m{A}, the second \m{B}, through \m{Z}, \m{AA} to \m{AZ}, and so on up to \m{ZZ}.

Names can also be accessed numerically, such that 0 is A, 26 is AA, and 701 is ZZ. The numerical form is valid everywhere but in gameexe.ini, and the letter form is valid everywhere but in function parameters.4

In RealLive (but not in AVG2000) there is a parallel set of local name variables, which are to the normal set as S[] is to M[]. These are introduced inline with \l in place of \m, the getter/setter functions are GetLocalName() and SetLocalName(), and the gameexe.ini variables defining their default values are called #LOCALNAME.

For example, Clannad uses the global names A and B for the player's family name and personal name respectively, and various local names for certain addresser/addressee combinations that have to change over the course of the game, such as the way the player addresses Nagisa (\l{C}).

4.4.3  Call variables

Version 1.3 of RealLive introduced a set of “call variables”: these are K[] (three string variables) and L[] (40 integer variables). These can be used as ordinary variables, but they are intended for use with the functions gosub_with() and farcall_with().

They can be accessed from Kepago with two more built-in arrays, strK[0] to strK[2] and intL[0] to intL[39].

4.5  The system command menu

RealLive provides a context menu system to handle most actions and configuration settings. The system command menu is configured with the #SYSCOM variables in gameexe.ini. It can be disabled by setting #SYSCOM_USE to 0, and if a #CANCELCALL hook is defined it will never be used at all (Clannad does this, although it uses the internal flags associated with the system command menu to control its own menu system).

The menu is displayed manually by right-cl