Anyone know anything about Saga Plus?

I’ve spent some time trying to make sense of the database of the Saga Plus version of Questprobe featuring Spider-Man, and I was wondering if anyone else has had a look at this format.

The IBM PC version has a nice plain-text database in a single text document, complete with comments, so overall it is pretty straightforward. The encoding of the actions is in some ways less convoluted than the TRS-80 ScottFree format. Arguments follow their commands in a logical fashion, and the individual commands seem to be pretty much unchanged from their ScottFree counterparts.

The conditions have me completely stumped, however. They seem to vary randomly from action to action, even when you think they would be the same.

Disassembly of the IBM and Apple 2 versions has yielded nothing useful. Scott Adams mentions in an interview that the system is written in IBM BASIC, but I have not been able to extract anything that looks like BASIC, so I suppose it is compiled to hard-to-follow native code.

I have found no information about the format online, apart from Scott Adams talking up it in old interviews. There is a reference to Source code printout - “Saga Plus Number 1,” Scott Adams, 1985 at the Brian Sutton-Smith Library & Archives of Play, but as far as I can tell you would have to go there in person in order to read it.


If anyone wants to take a stab at this, I’ve uploaded some text documents to Github. They are mostly extracted from the file SPL52.DAT in the MS DOS version of Questprobe featuring Spider-Man. Everything is very messy and preliminary, however, so it is probably incomprehensible to everybody else.

Here follows an attempt to describe the problem:

This is an action in the non-plus ScottFree / TRS-80 version, decompiled with ScottKit:

action LOOK SHAF when at Shaft (6) and !exists Gem2 (4)
	print "I see"
	print "mostly empty tool niches."

(The terminology is pretty confusing, as different tools use “actions” to mean different things, but here an “action” is the entire structure, verb + noun + conditions + commands.)

And here is the corresponding action in the Saga Plus version. Everything to the right of the pure numbers is my added comments:

3	"I see"
26	"mostly empty tool niches."

The first byte (49) is one mystery I haven’t been able to figure out. Every action starts with a single byte before the verb and noun, and I think it might tell the interpreter where one action ends and the next one starts, and possibly also where the conditions end and the commands start, but it is nothing straightforward like the number of bytes or commands.

The last two lines, 3 and 26, are commands that simply print the messages with the corresponding indices, just like in the ScottFree format.

The biggest mystery, however, is the seven bytes between the noun (19, SHAFT) and the first printed message (3, “I see”.) Somehow they encode the conditions “if player is in room 6 (Shaft) and object 4 (Gem2) is uncreated”, but there is nothing in there that looks like a 6 or a 4.

In the TRS-80 format, the condition player in room has value 4 and uncreated has value 14, so you might expect something like 4 6 14 4 (in room 6, uncreated 4) but there is nothing like that. Of course, the TRS-80 format has its own crazy encoding of these values (condition + 20 * argument) so something similar might be going on here.

Here is another example:

action LOOK SKY when at Skyscraper (17) and !exists cloud (28)
	print "I see"
	print something!
	drop cloud

And the corresponding Saga Plus action:

25	SKY
3	"I see"
34	"something!"
28	"strange cloud"
28	"strange cloud"

This has the same conditions as the previous action we looked at, but different arguments. Note that here, the first three condition bytes (203, 195, 193) and the last (142) are the same as in the previous action, while the other three (2, 36, 131) are not.

This might indicate that 203, 195, 193 encode the conditions "player in room " and “uncreated”, while the next three (2, 36, 131) encode the two arguments (17 and 28), and that 142 marks the end of conditions.

Have you tried asking Adams what he might have in his archives? He’s pretty accessible.

I did, actually. His reply was basically that his current enterprise is still using Saga Plus, so it is kind of a trade secret, but that he was going to ask his team if it was alright to share some information.


Nearly got it.

In the first action discussed above, the last two unknown bytes are 128 and 142. In binary, this is:

1000 0000
1000 1110

Starting from the end, the last five bits are 0 1110, or 14, which is the index of the “uncreated” condition. The eight preceding bits are 0000 0100, or 4, the index of object Gem2.

Moving up, we have 0 and 196:

0000 0000
1100 0100

The last five bits are 0 0100, or 4, the index of the “player in room” condition. The eight bits before are 0000 0110, or 6, the index of room Shaft.

This seems to check out for all the actions I’ve looked at so far: skip the first three bits, read the next eight, and you get the argument. Read the remaining five and you get the condition.

Now that we have the two conditions and argument values that we were looking for, the question remains what the first three bytes after the noun mean (203, 195, 193). Applying the same method to them gives no sensible results.

EDIT: They seem to be the three prepositions IN, AT, or NONE (no preposition). You get these (11, 5 and 1) by ORing the numbers with 0x3f (stripping the two top bits).


Some more things I’ve figured out about the action format:

The first byte is usually last nibble of the first byte is the number of command bytes, i.e. the remaining length of the action after reading the conditions. First nibble ORed with last nibble seems to give the correct value most of the time, though it might be that it fails because the number of preceding conditions is miscalculated.

EDIT 2: The first nibble is in fact the number of bytes used for extra input words; words the parser will look for in addition to the verb and the noun (which are always the second and third byte of the action, respectively), usually a preposition (when bit 7 is set) plus a second noun, sometimes several preposition-noun pairs.

The extra input words, if any, start at the fourth byte of the action, and are followed by the conditions.

Condition 31 means “negate the following condition(s)”, so that AT becomes NOTAT, CARRIED becomes NOTCARRIED and so on. It takes one argument that I haven’t figured out the meaning of. Sometimes it seems to negate just the one following condition, sometimes all of the following. EDIT: Argument 1 4 seems to mean “negate all the following” and argument 3 “negate the next condition only”.

I don’t know what the condition values that used to mean NOTAT and so on in the old format are used for now, if anything.


Here is a screenshot of my progress so far:

I also uploaded some new files with my guesses of how the conditions and the commands work.

The main hurdle is condition 31, the ”modifier” condition. It can have a lot of different arguments, and it isn’t easy to guess what they all do.

I still think that an argument of 3 means ”negate the following condition,” and that an argument of 4 means "negate all the following conditions.”

if the argument is 1, the following conditions seem to be ORed rather than ANDed, so that only one of them has to be true in order for the action to fire. I think ORing is switched off with argument 0.

The main mystery is argument 8. There are several places in Buckaroo Banzai where this pattern (numbers in parentheses are arguments to condition 31, letters are other conditions)

(8) (6) (0)  a b  (7) (6)  c d  (1) (7) (0)  e f g ...

is used to mean IF ((a AND b) OR (c AND d)) AND e AND f AND g ...

See for example action 291, and its following actions.

My first guess was that argument 6 meant a left parenthesis and 7 a right parenthesis, and I tried implementing this, but things still won’t work.

There is also one place in In Questprobe 2 where the pattern

(8)  a  b  (1)  c

seems to mean IF (a AND b) OR (c).

I guess I’ll have to take another look at the disassembly to see if I still can’t make sense of anything.

EDIT: If anyone wants to take a look, the source of the Spatterlight driver is here.
It shouldn’t be too hard to port to any Glk implementation.