Difference between revisions of "AGI/Specifications/Internals"

From ScummVM :: Wiki
Jump to navigation Jump to search
(Added values for v20 (Computer type), v22 (Sound generator type) and v26 (Monitor type) used by the Amiga AGI. Based on running Amiga's Gold Rush in debug mode (Enter with "bird man") under WinUAE.)
m (colour -> color)
 
(7 intermediate revisions by 2 users not shown)
Line 8: Line 8:
* 256 8 bit variables, numbered 0--255 (Var);
* 256 8 bit variables, numbered 0--255 (Var);
* 256 flags, numbered 0--255 (Flag);
* 256 flags, numbered 0--255 (Flag);
* a number of objects controlled by the interpreter, one of which (the object 0) may be controlled by the player using the keyboard;
* a number of objects controlled by the interpreter, one of which (the object 0, ego) may be controlled by the player;
* a number of inventory items;
* a number of inventory items;
* 12 40-character string variables (string).
* 12 40-character string variables (string).


Some variables (0 - 26) and flags (0 - 15) are reserved by the interpreter, all others are free to be used by the programmer.
Variables 0 to 26 and flags 0 to 16 are reserved for internal use.
 
All others are available to be used by the programmer. The variables
The interpreter provides a common variable and flag space for all programs simultaneously loaded in the memory. The number of objects and things is determined by the OBJECT resource.
and flags are global to all scripts. Each variable, flag, object,
 
string, word, message, etc. has a unique ID number, and numbering of
Interpreter's actions are described using the commands of the interpreter's language. For example, there are commands to manage objects, load and unload resources, etc. Further we shall consider the commands in detail.
different data types is independent (for example, there can be a
 
variable number 5, a string number 5, and a flag number 5).
Note: Any variable, flag, object, string, word, message, etc. has a unique ID number, and numbering of different data types is independent (for example, there can be a variable number 5, a string number 5, and a flag number 5).


==Resources - the fundamental AGI data type==
==Resources - the fundamental AGI data type==


When we develop a game, we invent the plot, create objects of the game, animate them, develop scenery and a dictionary of words for the dialogue with the player. To describe all of these, resources are used. To create some of the resources, we use utilities included with AGDS, in this case the input of the utilities are resources. (Sounds weird, but that's literally what it says. --VB)
When we develop a game, we invent the plot, create objects of the game, animate them, develop scenery and a dictionary of words for the dialogue with the player. To describe all of these, resources are used. To create some of the resources, we use utilities included with AGDS, in this case the input of the utilities are resources. ''(Sounds weird, but that's literally what it says. --VB)''


Here is a list of all the existing resources. Resources are used to represent:
Here is a list of all the existing resources. Resources are used to represent:


* colour background drawings (PICTURE resource);
* color background drawings (PICTURE resource);
* colour animated images (VIEW resource);
* color animated images (VIEW resource);
* sound effects (music, noise) (SOUND resource);
* sound effects (music, noise) (SOUND resource);
* inventory items and other objects (OBJECT resource);
* inventory items and other objects (OBJECT resource);
Line 37: Line 36:
Let us now consider the interpreter algorithm and the purpose of reserved variables and flags.
Let us now consider the interpreter algorithm and the purpose of reserved variables and flags.


When interpreter starts, LOGIC resource number 0 is loaded in memory. It stays there during the whole play time and determines all the interpreter's actions related to the overall control of the game. The interpreter works in a loop, i.e. all its actions are described by the interpreter work cycle shown in the block diagram below.
When the interpreter is started it loads script 0, not unloading it
until the end of the game execution. This script is then executed
over and over, and is responsible for sequencing all other scripts
according to the current room. Since script 0 is executed in all rooms,
it's common to handle actions that can happen in different places
directly on script 0, or in a script called from script 0 but not
associated to a specific room number.


In each cycle the interpreter performs the following basic actions:
In each cycle the interpreter performs the following basic actions:


* time delay;
# time delay;
* clears the keyboard buffer;
# clear the keyboard buffer;
* polls the keyboard and the joystick;
# poll the keyboard and the joystick;
* analyses some of the reserved variables and flags (see block diagram);
# analyses some of the reserved variables and flags (see block diagram);
* for all controllable objects for which animate_obj, start_update and draw commands were issued, directions of motion are recalculated;
# for all controllable objects for which '''animate.obj''', '''start.update''' and '''draw''' commands were issued, directions of motion are recalculated;
* LOGIC resource 0 is executed, as well as any logics it calls--which, in turn, can call other logics. Depending on the state of variables and flags analyzed at step 4 the number of commands interpreted at stage 4 commands varies from one iteration of the cycle to another depending, for example, on a number of LOGIC resources to be called in the current situation;
# LOGIC resource 0 is executed, as well as any logics it calls -- which, in turn, can call other logics. Depending on the state of variables and flags analyzed at step 4 the number of commands interpreted at stage 4 commands varies from one iteration of the cycle to another depending, for example, on a number of LOGIC resources to be called in the current situation;
* tests if the new_room command has been issued;
# test if the '''new.room''' command has been issued;


then the cycle is repeated.
then the cycle is repeated.
Line 147: Line 152:


<span id="VariableTypes"></span>
<span id="VariableTypes"></span>
=Variable types=
=AGI data types=


Written by Lance Ewing, with additions/modifications by Claudio Matsuoka (Last updated: 22 May 1999).
Written by Lance Ewing, with additions/modifications by Claudio Matsuoka (Last updated: 22 May 1999).
Line 154: Line 159:
==Variable==
==Variable==


This is an unsigned eight-bit variable type equivalent of a byte, or unsigned char. Its values range from 0 to 255. There are 256 variables and in the LOGIC code (listed in section Variables used by the interpreter); they are numbered from 0 to 255 and are indentified by their number. (The original LOGIC source code that Sierra's programmers wrote would have had textual identifiers for these variables, but when the LOGIC source was compiled into the LOGIC codes, the original variable names were lost. To the interpreter, the variables are known by their index into the variable table.)
This is an unsigned eight-bit data type equivalent of a byte, or
unsigned char. Its values range from 0 to 255. There are 256 variables,
being the first 27 (listed in [[AGI/Specifications/Internals#tab-reserved-variables | Table 3-1]])
reserved for internal use by the interprter. On interpreter startup, all
variables are set to 0.
 
<span id="tab-reserved-variables"></span>
 
{| border="1"  cellspacing="0" cellpadding="5"
|+ '''Table 3-1. Reserved variables'''
|-
! Variable
! Description
|-
| 0
| Current room number, initially 0
|-
| 1
| Previous room number
|-
| 2
| Code of the border touched by ego, according to [[AGI/Specifications/Internals#tab-border-code | Table 3-2]]
|-
| 3
| Current score
|-
| 4
| Number of the object, if not ego, that touched the border
|-
| 5
| Code of border touched by the object in '''v4'''
|-
| 6
| Direction of ego's motion [[AGI/Specifications/Internals#fig-directions | Figure 3-2]]
|-
| 7
| Maximum score
|-
| 8
| Number of free 256-byte pages of memory
|-
| 9
| Number of the word in the user message that was not found in the dictionary
|-
| 10
| Interpreter cycle time in 20ths of second
|-
| 11
| Seconds (in the internal clock)
|-
| 12
| Minutes (in the internal clock)
|-
| 13
| Hours (in the internal clock)
|-
| 14
| Days (in the  internal clock)
|-
| 15
| Joystick sensitivity (if '''f8''' == 1)
|-
| 16
| View resource associated with ego
|-
| 17
| Interpreter error code
|-
| 18
| Error code parameter
|-
| 19
| Key pressed on the keyboard
|-
| 20
| Computer type (see [[AGI/Specifications/Internals#tab-computer-types | Table 3-3]])
|-
| 21
| Message window timer (if '''f15''' == 0, the window is automatically closed after 0.5 * '''v21''' seconds)
|-
| 22
| Sound type (see [[AGI/Specifications/Internals#tab-sound-types | Table 3-4]])
|-
| 23
| Sound volume (0 to 15)
|-
| 24
| Input buffer size (default 41)
|-
| 25
| Number of inventory item selected using the '''status''' command or 255 if none
|-
| 26
| Monitor type (see [[AGI/Specifications/Internals#tab-monitor-types | Table 3-5]])
|}
 
<span id="tab-border-code"></span>
 
{| border="1"  cellspacing="0" cellpadding="5"
|+ '''Table 3-2. Border codes'''
|-
! Code
! Description
|-
| 0
| Touched nothing
|-
| 1
| Top edge of the screen or the horizon
|-
| 2
| Right edge of the screen
|-
| 3
| Bottom edge of the screen
|-
| 4
| Left edge of the screen
|}
 
 
<span id="fig-directions"></span>
 
'''Figure 3-2. Ego's directions'''
 
                            1
                        8  |  2
                        \  |  /
                          \ | /
                    7 ------------- 3      0 - the object
                          / | \                is motionless
                        /  |  \
                        6  |  4
                            5
 
 
<span id="tab-computer-types"></span>
 
{| border="1"  cellspacing="0" cellpadding="5"
|+ '''Table 3-3. Computer Types'''
|-
! Code
! Description
|-
| 0
| IBM-PC
|-
| 4
| Atari ST
|-
| 5
| Amiga
|-
| 7
| Apple IIGS
|-
| 20
| Amiga's Space Quest I (Version 1.2, AGI 2.082)
|}
 
<span id="tab-sound-types"></span>
 
{| border="1"  cellspacing="0" cellpadding="5"
|+ '''Table 3-4. Sound Types'''
|-
! Code
! Description
|-
| 0
| IBM-PC
|-
| 3
| Tandy (This value is also used by the Amiga AGI and Apple IIGS AGI)
|-
| 8
| Apple IIGS's Gold Rush! (Version 1.0M 1989-02-28 (CE), AGI 3.003) uses value 8
|}
 
<span id="tab-monitor-types"></span>
 
{| border="1"  cellspacing="0" cellpadding="5"
|+ '''Table 3-5. Monitor Types'''
|-
! Code
! Description
|-
| 0
| CGA (This value is also used by the Apple IIGS AGI)
|-
| 2
| Hercules
|-
| 3
| EGA (This value is also used by the Amiga AGI)
|-
| 0
| Amiga's King's Quest I (Version 1.0U, AGI 2.082) and Space Quest I (Version 1.2, AGI 2.082)
|-
| 0
| Amiga's Space Quest I (Version 1.2, AGI 2.082)
|}
 
 


Variables are the most commonly used type. They feature in arithmetic commands such as addition and multiplication, and a lot of AGI commands have a version that has variable paramaters as an alternative to the normal constant parameter versions.
==Flag==
==Flag==


Flags are the boolean type of the AGI system. Their value can be either 1 or 0 (true or false). There are 256 flags that are numbered 0 to 255. (In the original LOGIC source code, they would have had textual identifiers, but in the compiled LOGIC code they are known only by their index into the interpreters flag table.)
Flags are 1-bit boolean data types whose value can be either 1 or 0
(true or false). There are 256 flags numbered from 0 to 255. The
first 16 flags (listed in [[AGI/Specifications/Internals#tab-reserved-flags | Table 3-6]] are
reserved by the interpreter for internal use. On the interpreter startup,
all flags are set to 0.
 
<span id="reserved-flags"></span>
 
 
{|  border="1"  cellspacing="0" cellpadding="5"
|+ '''Table 3-6. Reserved flags'''
|-
! Flag
! Description
|-
| 0
| Ego's base line is completely on water
|-
| 1
| Ego is completely obscured by another object
|-
| 2
| The player has entered a command
|-
| 3
| Ego touched a trigger line
|-
| 4
| '''said''' accepted user input
|-
| 5
| Room script executed for the first time
|-
| 6
| '''restart.game''' has been executed
|-
| 7
| Writing to the script buffer is blocked
|-
| 8
| If set, '''v15''' determines joystick sensitivity
|-
| 9
| Sound is enabled
|-
| 10
| Built-in debugger is enabled
|-
| 11
| Logic 0 is executed for the first time
|-
| 12
| '''restore.game''' has been executed
|-
| 13
| '''status''' to select items
|-
| 14
| Enable menu
|-
| 15
| Enable non-blocking windows. If set, the message window is left on the screen, otherwise the message window is closed when '''ENTER''' or '''ESC''' are pressed. If '''v21''' is not zero, the window is closed automatically after '''v21''' * 500 milliseconds.
|-
| 16
| (Related to game restart)
|}


Flags are used to indicate when certain things have taken place.
==String==
==String==


According to another source, there are only 12 strings available. I don't know if this is true, but it agrees with the minimum amout of space set aside for strings that I have seen in examining memory usage during a game. However, the majority of AGI games have enough room for exactly 24 strings: AGI interpreter versions 2.089, 2.411, 3.002.107 and 3.002.149 have room for 12 strings, the remaining versions have room for 24 strings.
Strings are 40 characters long, including the zero terminator.
String number zero is the input prompt (e.g. <tt>">"</tt> or <tt>"]"</tt>).
The majority of AGI interpreters have enough room for exactly 24 strings:
AGI interpreter versions 2.089, 2.411, 3.002.107 and 3.002.149 have room
for 12 strings, the remaining versions have room for 24 strings.


Whether the versions that have enough space for 24 strings do infact support 24 strings is not known. Strings are 40 characters long which includes the zero terminator. String number zero is usually the input prompt (e.g. ">" or "]").
Whether the versions that have enough space for 24 strings do infact
support 24 strings is not known.


==Word==
==Word==


Words are the words that the user types in. An input sentence is composed of a number of words. The important words (e.g. for the sentence "look at the tree", "look" and "tree" are important) are assigned to the words variables corresponding to their place in the sentence once unimportant words and punctuation has been taken out. For example, in the earlier example word(1) would be "look" and word(2) would be "tree". Words can be converted to strings.
Words are the words that the user types in. An input sentence is composed of a number of words. The important words (e.g. for the sentence "look at the tree", "look" and "tree" are important) are assigned to the words variables corresponding to their place in the sentence once unimportant words and punctuation has been taken out. For example, in the earlier example word(1) would be "look" and word(2) would be "tree". Words can be converted to strings.
==Inventory Item==
==Inventory Item==


There are a number of AGI commands that refer to inventory items (e.g. get(), drop()). One of the arguments to these commands will represent an inventory item number. In the original LOGIC source text, the programmer would have written things like get(dagger) but the interpreter knows them only as an index into the OBJECT table.
There are a number of AGI commands that refer to inventory items (e.g. '''get()''', '''drop()'''). One of the arguments to these commands will represent an inventory item number. In the original LOGIC source text, the programmer would have written things like '''get(dagger)''' but the interpreter knows them only as an index into the OBJECT table.
 
==Object==
==Object==


There can be a bit of confusion between this type and the inventory item because of the name of the object file. The object file has almost nothing to do with what the interpreter generally calls objects. There are a large number of AGI commands that deal with objects. For example,
Objects are instances of View resources in use in a given room,
and essentially an entry in the view table. Many objects can use
the same view resource for its appearance (e.g. in KQ1 with the
crocodile filled moat). View table entries have positional and
sequential attributes that only make sense for a on-screen


<pre>
There can be a bit of confusion between this type and the inventory item because of the name of the '''object''' file. The '''object''' file has almost nothing to do with what the interpreter generally calls objects, such as '''move.obj''',
    move.obj
'''animate.obj''', '''set.view''', '''set.cel''', '''set.loop''' and '''draw'''.
    animate.obj
    set.view
    set.cel
    set.loop
    draw
</pre>


In fact the interpreter calls its usage of the VIEW resource "objects". An object is one usage of a VIEW resource. It is essentially an entry in the object table (or VIEW table/VIEW list). Many objects can use the same VIEW resource for its appearance which can be seen in KQ1 and BC with the crocodile filled moats.
In fact the interpreter calls its usage of the VIEW resource "objects". An object is one usage of a VIEW resource. It is essentially an entry in the object table (or VIEW table/VIEW list). Many objects can use the same VIEW resource for its appearance which can be seen in KQ1 and BC with the crocodile filled moats.


So when an AGI command has an object as a parameter to it, the value of the parameter is an index number into a table of objects that the interpreter is currently controlling.
When an AGI command has an object as a parameter to it, the value of the parameter is an index number into a table of objects that the interpreter is currently controlling.
 
==Message==
==Message==


Line 195: Line 471:


Example:
Example:
<pre>
 
<syntax type="C++">
print ("Message 34 in logic.0 is %g34.");
print ("Message 34 in logic.0 is %g34.");
</pre>
</syntax>


Therefore messages in logic.0 can be displayed by any LOGIC in this way.
Therefore messages in logic.0 can be displayed by any LOGIC in this way.
<span id="Variables"></span>
=Variables used by the interpreter=
On interpreter startup all variables are set to 0.
* 0 - Current room number (parameter new_room cmd), initially 0.
* 1 - Previous room number.
* 2 - Code of the border touched by Ego:
** 0 - Touched nothing;
** 1 - Top edge of the screen or the horizon;
** 2 - Right edge of the screen;
** 3 - Bottom edge of the screen;
** 4 - Left edge of the screen.
* 3 - Current score.
* 4 - Number of object, other than Ego, that touched the border.
* 5 - The code of border touched by the object in Var (4).
* 6 - Direction of Ego's motion.
<pre>
                            1
                        8  |  2
                        \  |  /
                          \ | /
                    7 ------------- 3      0 - the object
                          / | \                is motionless
                        /  |  \
                        6  |  4
                            5
</pre>
* 7 - Maximum score.
* 8 - Number of free 256-byte pages of the interpreter's memory.
* 9 - If == 0, it is the number of the word in the user message that was not found in the dictionary. (I would assume they mean "if != 0", but that's what they say. --VB)
* 10 - Time delay between interpreter cycles in 1/20 second intervals.
* 11 - Seconds (interpreter's internal clock)
* 12 - Minutes (interpreter's internal clock)
* 13 - Hours (interpreter's internal clock)
* 14 - Days (interpreter's internal clock)
* 15 - Joystick sensitivity (if Flag (8) = 1).
* 16 - ID number of the view-resource associated with Ego.
* 17 - Interpreter error code (if == 0) (Again I would expect this to say "if != 0". --VB)
* 18 - Additional information that goes with the error code.
* 19 - Key pressed on the keyboard.
* 20 - Computer type. For IBM-PC it is always 0. For Atari ST it is 4. For Amiga it is 5.
* 21 - If Flag (15) == 0 (command reset 15 was issued) and Var (21) is not equal to 0, the window is automatically closed after 1/2 * Var (21) seconds.
* 22 - Sound generator type:
** 1 - PC;
** 3 - Tandy (This value is also used by the Amiga AGI).
* 23 - 0:F - sound volume (for Tandy).
* 24 - This variable stores the maximum number that can be entered in the input line. By default, this variable is set to 41 (29h). (information by Dark Minister)
* 25 - ID number of the item selected using status command or 0xFF if ESC was pressed.
* 26 - monitor type
** 0 - CGA;
** 2 - Hercules;
** 3 - EGA (This value is also used by the Amiga AGI).
<span id="Flags"></span>
=Flags used by the interpreter=
On the interpreter startup all flags are set to 0.
* 0 - Ego base line is completely on pixels with priority = 3 (water surface).
* 1 - Ego is invisible of the screen (completely obscured by another object).
* 2 - the player has issued a command line.
* 3 - Ego base line has touched a pixel with priority 2 (signal).
* 4 - said command has accepted the user input.
* 5 - The new room is executed for the first time.
* 6 - restart_game command has been executed.
* 7 - if this flag is 1, writing to the script buffer is blocked.
* 8 - if 1, Var(15) determines the joystick sensitivity.
* 9 - sound on/off.
* 10 - 1 turns on the built-in debugger.
* 11 - Logic 0 is executed for the first time.
* 12 - "restore_game" command has been executed.
* 13 - 1 allows the "status" command to select items.
* 14 - 1 allows the menu to work.
* 15 - Determines the output mode of `print' and `print_at' commands:
** 1 - message window is left on the screen
** 0 - message window is closed when ENTER or ESC key are pressed. If Var(21) is not 0, the window is closed automatically after 1/2 * Var(21) seconds


<span id="Memory"></span>
<span id="Memory"></span>
Line 310: Line 508:
Written by Lance Ewing with additions/modifications by Peter Kelly and Anders M. Olsson (Last updated: 3 March 1998).
Written by Lance Ewing with additions/modifications by Peter Kelly and Anders M. Olsson (Last updated: 3 March 1998).


Since the data formats for the different AGI interpreter versions are mostly identical or easily convertible to each other, we should expect to be able to run one games data with anothers interpreter. This sounds like a reasonable assumption but when you try it, the interpreter rejects the new data. The reason behind this is game IDs.
Since the data formats for the different AGI interpreter versions are mostly identical or easily convertible to each other, we should expect to be able to run one games data with anothers interpreter. This sounds like a reasonable assumption but when you try it, the interpreter rejects the new data. The reason behind this is ''game IDs''.
 
Every interpreter has got a game ID coded into it and it expects to be given data for that game. Somewhere in the initialization code for each game is a '''set.game.id()'''. When this command is encountered, the interpreter checks the given game ID and compares it with its own. If they are not the same, it quits immediately. Presumably the reason for this was to stop people running games with the wrong interpter version, which can cause problems (unless you know what you're doing).


Every interpreter has got a game ID coded into it and it expects to be given data for that game. Somewhere in the initialization code for each game is a set.game.id(). When this command is encountered, the interpreter checks the given game ID and compares it with its own. If they are not the same, it quits immediately. Presumably the reason for this was to stop people running games with the wrong interpter version, which can cause problems (unless you know what you're doing).
==How do we get around it?==
==How do we get around it?==


Line 327: Line 526:
* Leisure Suit Larry: 'L' 'L' 'L' 'L' 'L' 0x00 'X'
* Leisure Suit Larry: 'L' 'L' 'L' 'L' 'L' 0x00 'X'


The game ID itself is the null terminated string that ends at the 00h. The text that follows it is of no significance, it is simply to fill in the gap although it is useful when searching for the game ID because, as you can see, this text is always "eIDX"> (presubably from "gameIDX" before being overwritten by the actual ID) or a suffix of it (i.e. "IDX", "DX", or "X"). For most games, the game ID is two or three characters which means that you will be able to rely on the "IDX" string being there for these games.
The game ID itself is the null terminated string that ends at the 00h. The text that follows it is of no significance, it is simply to fill in the gap although it is useful when searching for the game ID because, as you can see, this text is always "<tt>eIDX</tt>" (presubably from "gameIDX" before being overwritten by the actual ID) or a suffix of it (i.e. "<tt>IDX</tt>", "<tt>DX</tt>", or "<tt>X</tt>"). For most games, the game ID is two or three characters which means that you will be able to rely on the "IDX" string being there for these games.


===Method 2: The easy way===
===Method 2: The easy way===


Using the above method will allow you to run a different game with an interpreter, but you will still only be able to run the game that has the specified game ID. There is another way around this, which involves patching the logic source. This can be accomplished using a program to edit the logics such as AGI Studio (for the Windows platform). What you can usually do is look at the top part of logic 0 and find out what the initialization logic is (usually around 90--100 -- you might have to look at a few logics before you find it). Then simply go to that logic, find the set.game.id command, remove it, and recompile the logic. Since the command is not used, the interpreter will not try and compare it with it's own ID, and it won't quit.
Using the above method will allow you to run a different game with an interpreter, but you will still only be able to run the game that has the specified game ID. There is another way around this, which involves patching the logic source. This can be accomplished using a program to edit the logics such as AGI Studio (for the Windows platform). What you can usually do is look at the top part of logic 0 and find out what the initialization logic is (usually around 90--100 -- you might have to look at a few logics before you find it). Then simply go to that logic, find the '''set.game.id''' command, remove it, and recompile the logic. Since the command is not used, the interpreter will not try and compare it with it's own ID, and it won't quit.


The only drawback to using this method is that the saved games no longer have the game ID in their name (so, for example, a savegame would be called sg.1 instead of kq2sg.1), but this is not a major hassle.
The only drawback to using this method is that the saved games no longer have the game ID in their name (so, for example, a savegame would be called '''sg.1''' instead of '''kq2sg.1'''), but this is not a major hassle.


==Why can't I find the game ID?==
==Why can't I find the game ID?==


This is because a lot of the AGI files themselves are encrypted. See section Encrypted AGI data for further information.
This is because a lot of the AGI files themselves are encrypted. See section [[AGI/Specifications/Internals#Encryption | Encrypted AGI Data]] for further information.


==Possibilities==
==Possibilities==
Line 350: Line 549:
Written by Lance Ewing, with additions/modifications by Peter Kelly and Anders M. Olsson (Last updated: 3 March 1998).
Written by Lance Ewing, with additions/modifications by Peter Kelly and Anders M. Olsson (Last updated: 3 March 1998).


Many AGI files are encrypted. This was probably to give some protection to their product which was quite unique at the time. You can tell the difference between an encrypted AGI file and a non-encrypted AGI file by the first two characters. If they are "MZ" (the MS-DOS executable file header), then it not encrypted.
Many AGI files are encrypted. This was probably to give some protection to their product which was quite unique at the time. <!-- Footnote --> ''(You can tell the difference between an encrypted AGI file and a non-encrypted AGI file by the first two characters. If they are "MZ" (the MS-DOS executable file header), then it not encrypted.)'' The AGI file is decrypted by the loader program. This is usually called '''sierra.com''' in MS-DOS but can also be named after the game (eg. '''kq1.com'''). In AGI version 1, the loader was called '''load'''. If an AGI game doesn't have a loader, then it shouldn't be encrypted. If an AGI game does have a loader, it does not necessarily mean that the AGI file is encrypted.
 
The AGI file is decrypted by the loader program. This is usually called sierra.com in MS-DOS but can also be named after the game (eg. kq1.com). In AGI version 1, the loader was called load. If an AGI game doesn't have a loader, then it shouldn't be encrypted. If an AGI game does have a loader, it does not necessarily mean that the AGI file is encrypted.
 
The decryption key was not originally embedded in the loader file. If you find a game where the key is embedded in the loader, it is because that game has had copy protection removed. There are several utilities to do that. Anders M Olsson's SUP is one of them. The CD re-releases have been unprotected by Sierra in exactly the same fashion.


The loader would read the decryption key from track 6 of the disk, load the executable file, decrypt and run it. Track 6 had a special format that was supposedly impossible to exactly reproduce by a standard PC floppy disk controller.
The decryption key was not originally embedded in the loader file. If you find a game where the key is embedded in the loader, it is because that game has had copy protection removed. There are several utilities to do that. Anders M Olsson's '''SUP''' is one of them. The CD re-releases have been unprotected by Sierra in exactly the same fashion.


An interesting note is that when a copy-protected Sierra game asked for the original disk one, you could insert disk one from any protected Sierra game. The contents of track 6 were always the same.
The loader would read the decryption key from track 6 of the disk, load the executable file, decrypt and run it. Track 6 had a special format that was supposedly impossible to exactly reproduce by a standard PC floppy disk controller. An interesting note is that when a copy-protected Sierra game asked for the original disk one, you could insert disk one from ''any'' protected Sierra game. The contents of track 6 were always the same.


But even though track 6 was the same, all games didn't use exactly the same encryption key. The two bytes in the loader, immediately following the string "keyOfs", gave an offset on track 6 from where the key would be loaded.
But even though track 6 was the same, all games didn't use exactly the same encryption key. The two bytes in the loader, immediately following the string "<tt>keyOfs</tt>", gave an offset on track 6 from where the key would be loaded.


So, if the decryption string consists of 128 `k' characters, it can actually mean one of two things: Either the AGI file is not encrypted, or the game is still copy-protected.
So, if the decryption string consists of 128 `k' characters, it can actually mean one of two things: Either the AGI file is not encrypted, or the game is still copy-protected.
Line 379: Line 574:
The start of a loader will look something like this:
The start of a loader will look something like this:


<pre>LOADER v3.0 (c) Copyright Sierra On-Line, Inc. 1987 keyOfs</pre>
  LOADER v3.0 (c) Copyright Sierra On-Line, Inc. 1987 keyOfs


There are two bytes in between the "keyOfs" string and the start of the description string. If the decryption key consists of 128 `k' characters, then the AGI file is not encrypted (or the game is still copy-protected). If it consists of a whole lot of random looking characters, then it is encrypted.
There are two bytes in between the "<tt>keyOfs</tt>" string and the start of the description string. If the decryption key consists of 128 `k' characters, then the AGI file is not encrypted (or the game is still copy-protected). If it consists of a whole lot of random looking characters, then it is encrypted.


As an aside, the decryption string is followed immediately by the stack and is usually marked with a whole string of `s' characters. Thus we have `k' for key and `s' for stack. The stack is usually 256 bytes long.
As an aside, the decryption string is followed immediately by the stack and is usually marked with a whole string of `s' characters. Thus we have `k' for key and `s' for stack. The stack is usually 256 bytes long.
==Decrypting the AGI file==
==Decrypting the AGI file==


Line 393: Line 589:
From the AGDS documentation, translated by Vassili Bykov (Last update: 31 August 1998).
From the AGDS documentation, translated by Vassili Bykov (Last update: 31 August 1998).


Note: This section is an excerpt from the description of the said command from section Reference of LOGIC commands.
Note: This section is an excerpt from the description of the '''said''' command from section [[AGI/Specifications/Logic#CommandRef | Reference of LOGIC commands]].


Here is how the input is matched. After the player types a message and presses ENTER, the input line is processed by the interpreter in the following way:
Here is how the input is matched. After the player types a message and presses '''ENTER''', the input line is processed by the interpreter in the following way:


# The interpreter removes all punctuation marks.
# The interpreter removes all punctuation marks.
Line 405: Line 601:


* The Interpreter removes from the sequence of codes all zeros (that means all vocabulary words with zero codes are ignored).
* The Interpreter removes from the sequence of codes all zeros (that means all vocabulary words with zero codes are ignored).
* f2 (the user has entered an input line) is set to 1
* '''f2''' (the user has entered an input line) is set to 1
* f4 (said command accepted the user input) is set to 0.
* '''f4''' ('''said''' command accepted the user input) is set to 0.
 
If the sequence of code produced by the interpreter is


<pre>
If the sequence of code produced by the interpreter is V(1), V(2),...V(m), the test is performed as follows:
    V(1), V(2),...V(m)
</pre>
 
The test is performed as follows:


<pre>
<pre>
Line 427: Line 617:
Otherwise W(i) should be equal to V(i).
Otherwise W(i) should be equal to V(i).


If all elements match, f4 (said accepted the user input) is set to 1 and the command returns TRUE. Otherwise, FALSE is returned.
If all elements match, '''f4''' ('''said''' accepted the user input) is set to 1 and the command returns TRUE. Otherwise, FALSE is returned.


<span id="InterpreterVersions"></span>
<span id="InterpreterVersions"></span>
Line 435: Line 625:
==Sorted by Game==
==Sorted by Game==


<pre>
Known Sierra AGI versions are listed in [[AGI/Specifications/Internals#tab-agi-games | AGI games and interpreter versions table]]. There are a number of different versions of the AGI interpeter but
    Game   Ver.    Int    Int. Ver.      Date
generally the data formats are the same or can be converted
    ------- ------- ------- --------------- ---------
between each other. [[AGI/Specifications/Internals#tab-agi-games | AGI interpreter versions and features table]] compares the
    AGID    ?.?    AGI    2.915          ??/??/??
number of commands and other features in these interpreter versions.
    BC      2.00   AGI    2.439           06/14/87
 
    BC      2.10    AGI    3.002.098      11/10/88
<span id="tab-agi-games"></span>
    GR      2.01   AGI    3.002.149       12/22/88
{| border="1"  cellspacing="0" cellpadding="5"
    KQ1    1.0U   AGI    2.272           Unknown
|+  '''AGI games and interpreter versions'''
    KQ1    2.0F   AGI    2.425           ??/??/87
|-
    KQ1    2.0F   AGI    2.917           Unknown
! Game
    KQ2    2.1     AGI    2.411           Unknown
! Version
    KQ2    2.2     AGI    2.426           Unknown
! Int. version
    KQ2    2.2     AGI    2.917           ??/??/87
! Date</thead><tbody>
    KQ3    1.01   AGI    2.272           11/08/86
|-  
    KQ3    2.00   AGI    2.435           05/25/87
|  The Black Cauldron 
    KQ3    2.14   AGI    2.936           03/15/88
| 2.00
    KQ4    2.0     AGI    3.002.086       07/27/88
| 2.439
    KQ4D    ?.??    AGI    3.002.102       ??/??/??
| 14 Jun 1987
    LSL1    1.00   AGI    2.440           06/01/87
|-
    LSL1    1.0    AGI    2.917          06/01/87
|  Gold Rush&#33; 
    MG      ?.??    AGI    2.915           Unknown
| 2.01
    MH1    1.22   AGI    3.002.102       08/30/88
| 3.002.149
    MH1    1.22   AGI    3.002.107       08/31/88
| 22 Dec 1988
    MH2    3.02B   AGI    3.002.149       07/26/89
|-
    MH2    3.03   AGI    3.002.149       08/17/89
|  King's Quest I
    PQ1    2.0G   AGI    2.917           12/03/87
| 1.0U
    SQ1    1.0X   AGI    2.089           Unknown
| 2.272
    SQ1    2.2     AGI    2.426           ??/??/??
| Unknown
    SQ1    2.2     AGI    2.917           ??/??/??
|-
    SQ1    2.2    AGI    2.917          ??/??/87
|  King's Quest I
    SQ2    2.0C   AGI    2.915           ??/??/87
| 2.0F
    SQ2    2.0C   AGI    2.917           Unknown
| 2.425
    SQ2    2.0D   AGI    2.936           Unknown
| 1987
    SQ2    2.0F   AGI    2.936           Unknown
|-
    XM86    ?.??    AGI    2.272           Unknown
|  King's Quest I
</pre>
| 2.0F
| 2.917
| Unknown
|-
|  King's Quest II
| 2.1
| 2.411
| Unknown
|-
|  King's Quest II
| 2.2
| 2.426
| Unknown
|-
|  King's Quest II
| 2.2
| 2.917
| 1987
|-
|  King's Quest III
| 1.01
| 2.272
|  8 Nov 1986
|-
|  King's Quest III
| 2.00
| 2.435
| 25 May 1987
|-
|  King's Quest III
| 2.14
| 2.936
| 15 Mar 1988
|-
|  King's Quest IV
| 2.0
| 3.002.086
| 27 Jul 1988
|-
|  King's Quest IV demo
| ?
| 3.002.102
| Unknown
|-
|  Leisure Suit Larry
| 1.00
| 2.440
1 Jun 1987
|-
|  Mixed-Up Mother Goose
| ?
| 2.915
| Unknown
|-
|  Man Hunter: New York
| 1.22
| 3.002.102
| 30 Aug 1988
|-
|  Man Hunter: New York
| 1.22
| 3.002.107
| 31 Aug 1988
|-
|  Man Hunter: San Francisco
| 3.02B
| 3.002.149
| 26 Jul 1989
|-
|  Man Hunter: San Francisco
| 3.03
| 3.002.149
| 17 Aug 1989
|-
|  Police Quest I
| 2.0G
| 2.917
|  3 Dec 1987
|-
|  Space Quest I
| 1.0X
| 2.089
| Unknown
|-
|  Space Quest I
| 2.2
| 2.426
| Unknown
|-
|  Space Quest I
| 2.2
| 2.917
| 1987
|-
|  Space Quest II
| 2.0C
| 2.915
| 1987
|-
|  Space Quest II
| 2.0C
| 2.917
| Unknown
|-
|  Space Quest II
| 2.0D
| 2.936
| Unknown
|-
|  Space Quest II
| 2.0F
| 2.936
| Unknown
|-
|  Christmas Card
| ?
| 2.272
| Unknown
|}


==Sorted by Int. Ver.==
==Sorted by Int. Ver.==
<pre>
 
    Game    Ver.    Int    Int. Ver.      Date
<span id="tab-agi-versions"></span>
    ------- ------- ------- --------------- ---------
 
    SQ1    1.0X    AGI    2.089           Unknown
{| border="1"  cellspacing="0" cellpadding="5"
    KQ1    1.0U    AGI    2.272           Unknown
|+ '''AGI interpreter versions and features'''
    KQ3    1.01    AGI    2.272          11/08/86
|-
    XM86    ?.??    AGI    2.272          Unknown
! AGI version
    KQ2    2.1    AGI    2.411           Unknown
! Int. size
    KQ1    2.0F    AGI    2.425          ??/??/87
! Agidata size
    KQ2    2.2    AGI    2.426          Unknown
! Commands
    SQ1    2.2    AGI    2.426          ??/??/??
! Encrypted objects
    KQ3    2.00    AGI    2.435           05/25/87
! LZW
    BC      2.00    AGI    2.439           06/14/87
|-  
    LSL1    1.00    AGI    2.440           06/01/87
| 2.089
    AGID    ?.?    AGI    2.915           ??/??/??
| 34305
    MG      ?.??    AGI    2.915          Unknown
| 6656
    SQ2    2.0C    AGI    2.915          ??/??/87
| 155
    KQ1    2.0F    AGI    2.917           Unknown
| No
    KQ2    2.2    AGI    2.917          ??/??/87
| No
    LSL1    1.0    AGI    2.917          06/01/87
|-
    PQ1    2.0G    AGI    2.917          12/03/87
| 2.272
    SQ1    2.2    AGI    2.917          ??/??/??
| 34816
    SQ1    2.2    AGI    2.917          ??/??/87
| 6656
    SQ2    2.0C    AGI    2.917          Unknown
| 161
    KQ3    2.14    AGI    2.936           03/15/88
| No
    SQ2    2.0D    AGI    2.936          Unknown
| No
    SQ2    2.0F    AGI    2.936          Unknown
|-
    KQ4    2.0    AGI    3.002.086       07/27/88
| 2.411
    BC      2.10    AGI    3.002.098       11/10/88
| 38400
    KQ4D    ?.??    AGI    3.002.102       ??/??/??
| 7680
    MH1    1.22    AGI    3.002.102      08/30/88
| 169
    MH1    1.22    AGI    3.002.107       08/31/88
| Yes
    GR      2.01    AGI    3.002.149       12/22/88
| No
    MH2    3.02B  AGI    3.002.149      07/26/89
|-
    MH2    3.03    AGI    3.002.149      08/17/89
| 2.435
</pre>
| 38400
| 7680
| 169
| Yes
| No
|-
| 2.439
| 38400
| 7680
| 169
| Yes
| No
|-
| 2.440
| 38400
| 7680
| 169
| Yes
| No
|-
| 2.915
| 39424
| 8192
| 173
| Yes
| No
|-
| 2.917
| 39424
| 8192
| 173
| Yes
| No
|-
| 2.936
| 39424
| 8192
| 175
| Yes
| No
|-
| 3.002.086
| 40866
| 8064
| 177
| Yes
| Yes
|-
| 3.002.098
| 40898
| 8080
| 181
| Yes
| Yes
|-
| 3.002.102
| 40898
| 8080
| 181
| Yes
| Yes
|-
| 3.002.107
| 40962
| 8080
| 181
| Yes
| Yes
|-
| 3.002.149
| 40520
| 7488
| 181
| Yes
| Yes
|}


<span id="VersionDifferences"></span>
<span id="VersionDifferences"></span>
Line 517: Line 900:
There are a number of different versions of the AGI interpeter but generally the data formats are the same or can easy be converted between each other. The following table is a list of AGI interpreter versions that I know of:
There are a number of different versions of the AGI interpeter but generally the data formats are the same or can easy be converted between each other. The following table is a list of AGI interpreter versions that I know of:


{| border="1" cellspacing="0" cellpadding="5" align="center"
{| border="1" cellspacing="0" cellpadding="5"
!AGI Version
!AGI Version
!Interp Size
!Interp Size
Line 630: Line 1,013:
* Firstly, as the interpreter version increased, the number of AGI commands supported increased with it. The last eleven we do not know the names of.
* Firstly, as the interpreter version increased, the number of AGI commands supported increased with it. The last eleven we do not know the names of.
* There are two main AGI versions: AGI v2 and AGI v3.
* There are two main AGI versions: AGI v2 and AGI v3.
* The early AGI v2 games did not encrypt the object file with the "Avis Durgan" string.
* The early AGI v2 games did not encrypt the object file with the "<tt>Avis Durgan</tt>" string.
* AGI v3 games use adaptive LZW to compress their LOGIC, VIEW, and SOUND files.
* AGI v3 games use adaptive LZW to compress their LOGIC, VIEW, and SOUND files.


Line 637: Line 1,020:
There are four commands that have changed the number of arguments that are passed to them. All this information is based on observations made of the above interpreter versions.
There are four commands that have changed the number of arguments that are passed to them. All this information is based on observations made of the above interpreter versions.


* The quit command had no arguments for version 2.089 whereas all the others above have one argument.
* The '''quit''' command had no arguments for version 2.089 whereas all the others above have one argument.
* The print.at and print.at.v commands had only three arguments for versions 2.089--2.400 and four for the other versions.
* The '''print.at''' and '''print.at.v''' commands had only three arguments for versions 2.089--2.400 and four for the other versions.
* Unknown command number 176 had one argument for version 3.002.086 but later versions had no arguments for this command.
* Unknown command number 176 had one argument for version 3.002.086 but later versions had no arguments for this command.



Latest revision as of 02:56, 23 January 2011

How the interpreter works

From the AGDS documentation, translated by Vassili Bykov (Last update: 27 January 1998).

The AGI interpreter contains:

  • 256 8 bit variables, numbered 0--255 (Var);
  • 256 flags, numbered 0--255 (Flag);
  • a number of objects controlled by the interpreter, one of which (the object 0, ego) may be controlled by the player;
  • a number of inventory items;
  • 12 40-character string variables (string).

Variables 0 to 26 and flags 0 to 16 are reserved for internal use. All others are available to be used by the programmer. The variables and flags are global to all scripts. Each variable, flag, object, string, word, message, etc. has a unique ID number, and numbering of different data types is independent (for example, there can be a variable number 5, a string number 5, and a flag number 5).

Resources - the fundamental AGI data type

When we develop a game, we invent the plot, create objects of the game, animate them, develop scenery and a dictionary of words for the dialogue with the player. To describe all of these, resources are used. To create some of the resources, we use utilities included with AGDS, in this case the input of the utilities are resources. (Sounds weird, but that's literally what it says. --VB)

Here is a list of all the existing resources. Resources are used to represent:

  • color background drawings (PICTURE resource);
  • color animated images (VIEW resource);
  • sound effects (music, noise) (SOUND resource);
  • inventory items and other objects (OBJECT resource);
  • system dictionary for communicating with the user (WORD resource);
  • programs in internal AGI programming language (LOGIC resource).

General principles of the interpreter operation

Let us now consider the interpreter algorithm and the purpose of reserved variables and flags.

When the interpreter is started it loads script 0, not unloading it until the end of the game execution. This script is then executed over and over, and is responsible for sequencing all other scripts according to the current room. Since script 0 is executed in all rooms, it's common to handle actions that can happen in different places directly on script 0, or in a script called from script 0 but not associated to a specific room number.

In each cycle the interpreter performs the following basic actions:

  1. time delay;
  2. clear the keyboard buffer;
  3. poll the keyboard and the joystick;
  4. analyses some of the reserved variables and flags (see block diagram);
  5. for all controllable objects for which animate.obj, start.update and draw commands were issued, directions of motion are recalculated;
  6. LOGIC resource 0 is executed, as well as any logics it calls -- which, in turn, can call other logics. Depending on the state of variables and flags analyzed at step 4 the number of commands interpreted at stage 4 commands varies from one iteration of the cycle to another depending, for example, on a number of LOGIC resources to be called in the current situation;
  7. test if the new.room command has been issued;

then the cycle is repeated.

All logics (programs and subroutines) simultaneously loaded in memory operate on a common set of variables, flags, and strings, each identified by a unique for each data type ID number.

The fact that the interpreter runs in a loop influences the general programming principles and style when programming for AGDS. This makes programming a little unusual and takes a certain time to get used to. For example, many cyclic activities requiring explicit loops in "conventional" programming languages are executed in the interpreter programs by default, provided the program has a proper structure.

General hints on how to reduce the time to adapt to the interpreter's language are given below, using an educational program Thunderstorm as an example. However, this does not reduce the usefulness of analyzing the game programs of Sierra On-Line, Inc.

Interpreter work cycle

                         +---------------------------+
                         |      1. delay time        |
                         +---------------------------+
                                       |
                                       V
                         +----------------------------+
                         |2. clear the keyboard buffer|
                         +----------------------------+
                                       |
                                       V
                         +---------------------------+
                         |      Flag (2) - > 0       |
                         |      Flag (4) - > 0       |
                         +---------------------------+
                                       |
                                       V
                    +-------------------------------------+
                    | 3. poll keyboard and joystick       |
                    +-------------------------------------+
                                       |
                                       V
                    +-------------------------------------+
                    | If the current mode is              |
                    | - program_control, the direction of |
                    |   Ego motion <-- var(6).            |
                    | - player.control, var (6) --> dir.  |
                    |   of Ego motion.                    |
                    +-------------------------------------+
                                       |
                                       V
    +---------------------------------------------------------------+
    | For all objects for which command animate.obj, start_update   |
    | and draw were carried out, the recalculation of the direction |
    | of movement is performed.                                     |
    +---------------------------------------------------------------+
                                       |
                                       V
    +---------------------------------------------------------------+
    | If the score has changed (var (3)) or the sound has been      |
    | turned on or off (Flag (9)), the status line is updated.      |
    +---------------------------------------------------------------+
                                       |
                                       +---------+
                                                 V
                                   +--------------------------+
                 +---------------->| 4 Logic 0 is executed    |
                 |                 +--------------------------+
                 |                               |
                 |                               V
                 |          +--------------------------------------+
                 |          | - Dir. of motion of Ego  <-- var (6) |
                 |          | - If score (var (3)) or Flag (9)     |
                 |          | have changed their values - update   |
                 |          | the status and score (on Var (3));   |
                 |          | - Var (5) - > 0;                     |
                 |          | - Var (4) - > 0;                     |
                 |          | - Flag (5) - > 0!!!!                 |
                 |          | - Flag (6) - > 0;                    |
                 |          | - Flag (12) - > 0.                   |
                 |          +--------------------------------------+
     +----------------------------+               |
     | Execute:                   |               V
     | ~~~~~~~~~~~~~~~~~~~~~~~~   |  +------------------------+
     | - stop.update;             |  | Update all controlled  |
     | - unanimate.all;           |  | objects on the screen. |
     | - destroy all logic        |  +------------------------+
     |   resources except         |                   |
     |   for logic 0;             |                   V
     | - player.control;          |            +--------------+
     | - unblock;                 |            | new.room n   |
     | - set_horizon 36;          |            | or           |
     | - var (1) = var (0);       |            | new.room.v n |
     | - var (0) = n | Var(n);    |            | was issued?  |
     | - var (4) = 0;             |            |              |
     | - var (5) = 0;             |            +-------+------+
     | - var (9) = 0;             |<-----------+  Yes  |  No  |
     | - var (16) = number of     |            +-------+--+---+
     | view assoc. w/Ego;         |                       |
     | - Ego coords from var (2); |                       |
     | - var (2) = 0;             |                       |
     | - flag (2) - > 0;          |                       V
     | - flag (5) - > 1!!!!       |               +--------------+
     | - score < - var (3);       |               | Go to step 1 |
     +----------------------------+               +--------------+

AGI data types

Written by Lance Ewing, with additions/modifications by Claudio Matsuoka (Last updated: 22 May 1999).

There is a number of data types used as AGI command parameters, listed below:

Variable

This is an unsigned eight-bit data type equivalent of a byte, or unsigned char. Its values range from 0 to 255. There are 256 variables, being the first 27 (listed in Table 3-1) reserved for internal use by the interprter. On interpreter startup, all variables are set to 0.

Table 3-1. Reserved variables
Variable Description
0 Current room number, initially 0
1 Previous room number
2 Code of the border touched by ego, according to Table 3-2
3 Current score
4 Number of the object, if not ego, that touched the border
5 Code of border touched by the object in v4
6 Direction of ego's motion Figure 3-2
7 Maximum score
8 Number of free 256-byte pages of memory
9 Number of the word in the user message that was not found in the dictionary
10 Interpreter cycle time in 20ths of second
11 Seconds (in the internal clock)
12 Minutes (in the internal clock)
13 Hours (in the internal clock)
14 Days (in the internal clock)
15 Joystick sensitivity (if f8 == 1)
16 View resource associated with ego
17 Interpreter error code
18 Error code parameter
19 Key pressed on the keyboard
20 Computer type (see Table 3-3)
21 Message window timer (if f15 == 0, the window is automatically closed after 0.5 * v21 seconds)
22 Sound type (see Table 3-4)
23 Sound volume (0 to 15)
24 Input buffer size (default 41)
25 Number of inventory item selected using the status command or 255 if none
26 Monitor type (see Table 3-5)

Table 3-2. Border codes
Code Description
0 Touched nothing
1 Top edge of the screen or the horizon
2 Right edge of the screen
3 Bottom edge of the screen
4 Left edge of the screen


Figure 3-2. Ego's directions

                           1
                       8   |   2
                        \  |  /
                         \ | /
                   7 ------------- 3      0 - the object
                         / | \                is motionless
                        /  |  \
                       6   |   4
                           5


Table 3-3. Computer Types
Code Description
0 IBM-PC
4 Atari ST
5 Amiga
7 Apple IIGS
20 Amiga's Space Quest I (Version 1.2, AGI 2.082)

Table 3-4. Sound Types
Code Description
0 IBM-PC
3 Tandy (This value is also used by the Amiga AGI and Apple IIGS AGI)
8 Apple IIGS's Gold Rush! (Version 1.0M 1989-02-28 (CE), AGI 3.003) uses value 8

Table 3-5. Monitor Types
Code Description
0 CGA (This value is also used by the Apple IIGS AGI)
2 Hercules
3 EGA (This value is also used by the Amiga AGI)
0 Amiga's King's Quest I (Version 1.0U, AGI 2.082) and Space Quest I (Version 1.2, AGI 2.082)
0 Amiga's Space Quest I (Version 1.2, AGI 2.082)


Flag

Flags are 1-bit boolean data types whose value can be either 1 or 0 (true or false). There are 256 flags numbered from 0 to 255. The first 16 flags (listed in Table 3-6 are reserved by the interpreter for internal use. On the interpreter startup, all flags are set to 0.


Table 3-6. Reserved flags
Flag Description
0 Ego's base line is completely on water
1 Ego is completely obscured by another object
2 The player has entered a command
3 Ego touched a trigger line
4 said accepted user input
5 Room script executed for the first time
6 restart.game has been executed
7 Writing to the script buffer is blocked
8 If set, v15 determines joystick sensitivity
9 Sound is enabled
10 Built-in debugger is enabled
11 Logic 0 is executed for the first time
12 restore.game has been executed
13 status to select items
14 Enable menu
15 Enable non-blocking windows. If set, the message window is left on the screen, otherwise the message window is closed when ENTER or ESC are pressed. If v21 is not zero, the window is closed automatically after v21 * 500 milliseconds.
16 (Related to game restart)

String

Strings are 40 characters long, including the zero terminator. String number zero is the input prompt (e.g. ">" or "]"). The majority of AGI interpreters have enough room for exactly 24 strings: AGI interpreter versions 2.089, 2.411, 3.002.107 and 3.002.149 have room for 12 strings, the remaining versions have room for 24 strings.

Whether the versions that have enough space for 24 strings do infact support 24 strings is not known.

Word

Words are the words that the user types in. An input sentence is composed of a number of words. The important words (e.g. for the sentence "look at the tree", "look" and "tree" are important) are assigned to the words variables corresponding to their place in the sentence once unimportant words and punctuation has been taken out. For example, in the earlier example word(1) would be "look" and word(2) would be "tree". Words can be converted to strings.

Inventory Item

There are a number of AGI commands that refer to inventory items (e.g. get(), drop()). One of the arguments to these commands will represent an inventory item number. In the original LOGIC source text, the programmer would have written things like get(dagger) but the interpreter knows them only as an index into the OBJECT table.

Object

Objects are instances of View resources in use in a given room, and essentially an entry in the view table. Many objects can use the same view resource for its appearance (e.g. in KQ1 with the crocodile filled moat). View table entries have positional and sequential attributes that only make sense for a on-screen

There can be a bit of confusion between this type and the inventory item because of the name of the object file. The object file has almost nothing to do with what the interpreter generally calls objects, such as move.obj, animate.obj, set.view, set.cel, set.loop and draw.

In fact the interpreter calls its usage of the VIEW resource "objects". An object is one usage of a VIEW resource. It is essentially an entry in the object table (or VIEW table/VIEW list). Many objects can use the same VIEW resource for its appearance which can be seen in KQ1 and BC with the crocodile filled moats.

When an AGI command has an object as a parameter to it, the value of the parameter is an index number into a table of objects that the interpreter is currently controlling.

Message

At the end of every LOGIC file is a message section. There need not be any messages in it, but it will still exist. Messages in logic.0 are global messages whereas all other messages can only be accessed from their own LOGIC code. AGI commands that have messages as parameters refer to a message number in their own LOGIC file. I say that those in logic.0 are global because messages and strings can contain format codes one of which is used to display messages from logic.0.

Example:

<syntax type="C++"> print ("Message 34 in logic.0 is %g34."); </syntax>

Therefore messages in logic.0 can be displayed by any LOGIC in this way.

Memory organization

Written by Lance Ewing (Last updated: 31 August 1997).

The following information gives a rough guide as to how Sierra's AGI interpreter uses its memory. You can view this in operation in MS-DOS by using a memory resident program like Game Wizard.

  • Length of first data area (2 bytes)
  • Game signature (8 bytes)
  • Variables (256 bytes)
  • Flags (256 bits, 32 bytes)
  • Timers, blocks, and other special AGI variables
  • Strings (12*40 bytes or 24*40 bytes)
  • unknown
  • "Press ENTER to quit" etc
  • Script command jump table
  • "Avis Durgan" encryption string
  • Rest of agidata.ovl is in here
  • unknown
  • words.tok file
  • object file
  • VIEW object table
  • logic.0
  • Other loaded resources

Game IDs and loaders

Written by Lance Ewing with additions/modifications by Peter Kelly and Anders M. Olsson (Last updated: 3 March 1998).

Since the data formats for the different AGI interpreter versions are mostly identical or easily convertible to each other, we should expect to be able to run one games data with anothers interpreter. This sounds like a reasonable assumption but when you try it, the interpreter rejects the new data. The reason behind this is game IDs.

Every interpreter has got a game ID coded into it and it expects to be given data for that game. Somewhere in the initialization code for each game is a set.game.id(). When this command is encountered, the interpreter checks the given game ID and compares it with its own. If they are not the same, it quits immediately. Presumably the reason for this was to stop people running games with the wrong interpter version, which can cause problems (unless you know what you're doing).

How do we get around it?

Method 1: The hard way.

Well, basically we have to find the game ID signature in the AGI interpreter file and change it to the ID of the game whose data we wish to be executed.

Here are a few examples of some game ID's and the data following them:

  • Police Quest: 'P' 'Q' 0x00 'e' 'I' 'D' 'X'
  • Mother Goose: 'M' 'G' 0x00 'e' 'I' 'D' 'X'
  • Manhunter 2: 'M' 'H' '2' 0x00 'I' 'D' 'X'
  • XMAS Demo: 'X' 'M' 'A' 'S' 0x00 'D' 'X'
  • Leisure Suit Larry: 'L' 'L' 'L' 'L' 'L' 0x00 'X'

The game ID itself is the null terminated string that ends at the 00h. The text that follows it is of no significance, it is simply to fill in the gap although it is useful when searching for the game ID because, as you can see, this text is always "eIDX" (presubably from "gameIDX" before being overwritten by the actual ID) or a suffix of it (i.e. "IDX", "DX", or "X"). For most games, the game ID is two or three characters which means that you will be able to rely on the "IDX" string being there for these games.

Method 2: The easy way

Using the above method will allow you to run a different game with an interpreter, but you will still only be able to run the game that has the specified game ID. There is another way around this, which involves patching the logic source. This can be accomplished using a program to edit the logics such as AGI Studio (for the Windows platform). What you can usually do is look at the top part of logic 0 and find out what the initialization logic is (usually around 90--100 -- you might have to look at a few logics before you find it). Then simply go to that logic, find the set.game.id command, remove it, and recompile the logic. Since the command is not used, the interpreter will not try and compare it with it's own ID, and it won't quit.

The only drawback to using this method is that the saved games no longer have the game ID in their name (so, for example, a savegame would be called sg.1 instead of kq2sg.1), but this is not a major hassle.

Why can't I find the game ID?

This is because a lot of the AGI files themselves are encrypted. See section Encrypted AGI Data for further information.

Possibilities

What this means is that with a few useful AGI utility programs, it is possible to run any set of game data with a compatible AGI interpreter. For example, games that use AGI versions 2.915, 2.917, and 2.936 should be able to be converted into AGIv3 format and run with an AGIv3 interpreter.

"Compatible" as it is used above refers not only to the data differences but also to some AGI command descrepencies. There are about four AGI commands that have changed the number of arguments passed to them as the interpreter developed. This sort of thing is the only real obstacle to running data on another interpreter.

Encrypted AGI data

Written by Lance Ewing, with additions/modifications by Peter Kelly and Anders M. Olsson (Last updated: 3 March 1998).

Many AGI files are encrypted. This was probably to give some protection to their product which was quite unique at the time. (You can tell the difference between an encrypted AGI file and a non-encrypted AGI file by the first two characters. If they are "MZ" (the MS-DOS executable file header), then it not encrypted.) The AGI file is decrypted by the loader program. This is usually called sierra.com in MS-DOS but can also be named after the game (eg. kq1.com). In AGI version 1, the loader was called load. If an AGI game doesn't have a loader, then it shouldn't be encrypted. If an AGI game does have a loader, it does not necessarily mean that the AGI file is encrypted.

The decryption key was not originally embedded in the loader file. If you find a game where the key is embedded in the loader, it is because that game has had copy protection removed. There are several utilities to do that. Anders M Olsson's SUP is one of them. The CD re-releases have been unprotected by Sierra in exactly the same fashion.

The loader would read the decryption key from track 6 of the disk, load the executable file, decrypt and run it. Track 6 had a special format that was supposedly impossible to exactly reproduce by a standard PC floppy disk controller. An interesting note is that when a copy-protected Sierra game asked for the original disk one, you could insert disk one from any protected Sierra game. The contents of track 6 were always the same.

But even though track 6 was the same, all games didn't use exactly the same encryption key. The two bytes in the loader, immediately following the string "keyOfs", gave an offset on track 6 from where the key would be loaded.

So, if the decryption string consists of 128 `k' characters, it can actually mean one of two things: Either the AGI file is not encrypted, or the game is still copy-protected.

How does the encryption work?

The loader contains a 128 byte string called the decryption key. Here's the process of decryption:

  1. The carry bit is zeroed.
  2. The first 128 bytes of the AGI file are XORed with the decryption key.
  3. The whole key string is rotated one bit to the right, including the carry-bit. (Bit 7 of the first byte is loaded from the carry.
  4. Bit 0 of the last byte is placed in the carry, where it will remain until the next rotation.
  5. The bit that was bit 0 of the last byte (now in carry) is ORed into bit 7 of the first byte.
  6. The next 128 bytes of the AGI file are XORed with the new key. If the end of the AGI file has not been reached, then go back to 3.

Where in the loader is the decryption key?

The start of a loader will look something like this:

 LOADER v3.0 (c) Copyright Sierra On-Line, Inc. 1987 keyOfs

There are two bytes in between the "keyOfs" string and the start of the description string. If the decryption key consists of 128 `k' characters, then the AGI file is not encrypted (or the game is still copy-protected). If it consists of a whole lot of random looking characters, then it is encrypted.

As an aside, the decryption string is followed immediately by the stack and is usually marked with a whole string of `s' characters. Thus we have `k' for key and `s' for stack. The stack is usually 256 bytes long.

Decrypting the AGI file

Decrypting the AGI file is simply a matter of writing a program to read the loader to get the decryption string, and then applying the process mentioned above to the AGI file. Once this has been done, the game ID can be located.

Player input parsing

From the AGDS documentation, translated by Vassili Bykov (Last update: 31 August 1998).

Note: This section is an excerpt from the description of the said command from section Reference of LOGIC commands.

Here is how the input is matched. After the player types a message and presses ENTER, the input line is processed by the interpreter in the following way:

  1. The interpreter removes all punctuation marks.
  2. All characters are converted to lowercase.
  3. All sequences of more than one space are replaced with a single space.
  4. Starting with the first word of the input, the interpreter looks up the vocabulary, trying to find the longest character sequence matching the input line.

If the search is unsuccessful, v9 is assigned the number of the word in the message that failed to match and the processing ends. If all the words have been assigned some codes:

  • The Interpreter removes from the sequence of codes all zeros (that means all vocabulary words with zero codes are ignored).
  • f2 (the user has entered an input line) is set to 1
  • f4 (said command accepted the user input) is set to 0.

If the sequence of code produced by the interpreter is V(1), V(2),...V(m), the test is performed as follows:

    If f2 == 0 or f4 == 1, return FALSE.

Compare parameters W(i) and codes V(i) as follows:

  • if W(i) = 1, it matches any V(i);
  • if W(i) = 9999, it matches the whole remaining input i.e. the codes V(i), V(i+1),...V(m).

Otherwise W(i) should be equal to V(i).

If all elements match, f4 (said accepted the user input) is set to 1 and the command returns TRUE. Otherwise, FALSE is returned.

AGI interpreter versions

Written by Jeremy Hayes, with modifications by Claudio Matsuoka (Last update: 22 May 1999).

Sorted by Game

Known Sierra AGI versions are listed in AGI games and interpreter versions table. There are a number of different versions of the AGI interpeter but generally the data formats are the same or can be converted between each other. AGI interpreter versions and features table compares the number of commands and other features in these interpreter versions.

AGI games and interpreter versions
Game Version Int. version Date</thead><tbody>
The Black Cauldron 2.00 2.439 14 Jun 1987
Gold Rush! 2.01 3.002.149 22 Dec 1988
King's Quest I 1.0U 2.272 Unknown
King's Quest I 2.0F 2.425 1987
King's Quest I 2.0F 2.917 Unknown
King's Quest II 2.1 2.411 Unknown
King's Quest II 2.2 2.426 Unknown
King's Quest II 2.2 2.917 1987
King's Quest III 1.01 2.272 8 Nov 1986
King's Quest III 2.00 2.435 25 May 1987
King's Quest III 2.14 2.936 15 Mar 1988
King's Quest IV 2.0 3.002.086 27 Jul 1988
King's Quest IV demo ? 3.002.102 Unknown
Leisure Suit Larry 1.00 2.440 1 Jun 1987
Mixed-Up Mother Goose ? 2.915 Unknown
Man Hunter: New York 1.22 3.002.102 30 Aug 1988
Man Hunter: New York 1.22 3.002.107 31 Aug 1988
Man Hunter: San Francisco 3.02B 3.002.149 26 Jul 1989
Man Hunter: San Francisco 3.03 3.002.149 17 Aug 1989
Police Quest I 2.0G 2.917 3 Dec 1987
Space Quest I 1.0X 2.089 Unknown
Space Quest I 2.2 2.426 Unknown
Space Quest I 2.2 2.917 1987
Space Quest II 2.0C 2.915 1987
Space Quest II 2.0C 2.917 Unknown
Space Quest II 2.0D 2.936 Unknown
Space Quest II 2.0F 2.936 Unknown
Christmas Card ? 2.272 Unknown

Sorted by Int. Ver.

AGI interpreter versions and features
AGI version Int. size Agidata size Commands Encrypted objects LZW
2.089 34305 6656 155 No No
2.272 34816 6656 161 No No
2.411 38400 7680 169 Yes No
2.435 38400 7680 169 Yes No
2.439 38400 7680 169 Yes No
2.440 38400 7680 169 Yes No
2.915 39424 8192 173 Yes No
2.917 39424 8192 173 Yes No
2.936 39424 8192 175 Yes No
3.002.086 40866 8064 177 Yes Yes
3.002.098 40898 8080 181 Yes Yes
3.002.102 40898 8080 181 Yes Yes
3.002.107 40962 8080 181 Yes Yes
3.002.149 40520 7488 181 Yes Yes

Version differences

Written by Lance Ewing (Last updated: 27 January 1998).

There are a number of different versions of the AGI interpeter but generally the data formats are the same or can easy be converted between each other. The following table is a list of AGI interpreter versions that I know of:

AGI Version Interp Size Agidata Size Num commands Object Encrypted LZW
2.089 34305 6556 155 No No
2.272 34816 6656 161 No No
2.411 38400 7680 169 Yes No
2.435 38400 7680 169 Yes No
2.439 38400 7680 169 Yes No
2.440 38400 7680 169 Yes No
2.915 39424 8192 173 Yes No
2.917 39424 8192 173 Yes No
2.936 39424 8192 177 Yes No
3.002.086 40866 8064 177 Yes Yes
3.002.098 40898 8080 181 Yes Yes
3.002.102 40898 8080 177 Yes Yes
3.002.107 40962 8080 177 Yes Yes
3.002.149 40520 7488 177 Yes Yes


This table illustrates a number of things:

  • Firstly, as the interpreter version increased, the number of AGI commands supported increased with it. The last eleven we do not know the names of.
  • There are two main AGI versions: AGI v2 and AGI v3.
  • The early AGI v2 games did not encrypt the object file with the "Avis Durgan" string.
  • AGI v3 games use adaptive LZW to compress their LOGIC, VIEW, and SOUND files.

Command argument number descrepancies

There are four commands that have changed the number of arguments that are passed to them. All this information is based on observations made of the above interpreter versions.

  • The quit command had no arguments for version 2.089 whereas all the others above have one argument.
  • The print.at and print.at.v commands had only three arguments for versions 2.089--2.400 and four for the other versions.
  • Unknown command number 176 had one argument for version 3.002.086 but later versions had no arguments for this command.

Number of strings

There may be some differences in the number of strings supported by some interpreters as well. All interpreters have at least 12 strings. Most interpreters have space for 24 strings but I don't know if the extra space is used for strings or not.