Infocom(tm) adventure game vocabulary list dumper
Mike Threepoint
linhart at cream.rutgers.edu
Sun Mar 17 02:51:45 AEST 1991
Hello sailor!
Introducing my latest hack...
Having solved every Infocom adventure game except the 5 I don't have,
what else was there to do but hack them?
This program extracts and translates the vocabulary list from the
Infocom adventure game data files bundles with the interpreters.
The data files should be the same format on any system. The program
should compile with only minor tweaks on any ANSI C compiler, but
for UNIX you'll want to remove the `swab()' calls to swap the byte
sex.
Enjoy...
<-- snip, snip
/* vocab.c -- A data dumper
* Copyleft (c) 1991 by Mike Threepoint. All rights reversed.
* Release 1 / Serial number 910310
*
* This program dumps the vocabulary list encoded in a standard Infocom(tm)
* adventure game data file. Having solved all the Infocom games I have,
* there's little left to do but hack them.
*
* Go ahead, reassure yourself you've seen every last Encyclopedia Frobizzica
* entry, learned every spell, and know all the magic wand's F-words.
* Discover obscure synonyms (like calling the Zorkian elvish sword Glamdring
* and the dragon Smaug) and trivia about the game's internal operations
* (like the internal `intnum' noun in several games, used when you type a
* number).
*
* I doubt Infocom's employees will complain, either of them. Alas, Infocom.
* I wore a black armband when you went under. If only you'd stayed solvent.
* (At least till I could buy Sherlock and Arthur! Can't purchase them
* anywhere anymore...)
*
* Email correspondence to linhart at remus.rutgers.edu.
*
* Disclaimer: This program works for me, at the moment. I've never seen
* any Infocom source code(*), and nobody at 55 CambridgePark
* Drive told me any technical details. I'm just an independent
* public domain software author. If I-Need-Magic sues, I'll
* cheerfully turn over all zero profits I made on this program.
*
* (* Well, maybe one function. I noticed the Beyond Zork MS-DOS interpreter
* was in MSC, so I mailed them a MSC function to get the screen size from
* the BIOS instead of the stupid SETUP.EXE method, so the interpreter
* could figure out when my VGA was in 50 line mode. Some time later, a
* new text game was released, with VisiClues. I started it in 50-line
* mode, but the screen was reset to 25-line CGA color mode. And then the
* text ran off the bottom of the screen and scrolled as if it were still
* 50 lines long. I'd mail another helpful letter, but it's too late now.)
*/
#define MAXCOL 79
#if !defined(__STDC__) && !defined(__TURBOC__)
#error This is an ANSI-complaint. It looks like you're not ANSI-compliant.
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#define S_BLANK 1
#define S_MACRO1 2
#define S_MACRO2 3
#define S_MACRO3 4
#define S_CAPS 5
#define S_PUNC 6
#define S_FILLER 6
#define S_OFF 7
struct bits {
unsigned ch3 : 5;
unsigned ch2 : 5;
unsigned ch1 : 5;
unsigned : 1;
};
/* the 5-bit character set */
const char err_chars[7] = {
/* null thrown in for string handling */
'\0',
/* special codes above */
' ', '1', '2', '3', '^', '@',
/* followed by: */
};
typedef const char alfabet[26];
alfabet
lower = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
},
upper = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
},
punct = {
'$', ' ',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'.', ',', '!', '?', '_', '#', '\'', '\"', '/', '\\', '-', ':', '(', ')'
};
struct info_header {
char type;
char qqc;
unsigned release;
unsigned qqc3;
unsigned qqc4;
unsigned vocab_offset;
unsigned off6;
unsigned qqc7;
unsigned qqc8;
unsigned nul9;
char rev_date[6];
unsigned macro_offset;
unsigned qqc14;
unsigned qqc15;
unsigned rest[17];
};
FILE *infile;
short column = 0;
char numbers = 1,
bits = 0,
wide = 1,
did_file = 0;
void
newline ( void )
{
putchar('\n');
column = 0;
}
char *
bits_to_bstr ( struct bits *chars )
{
static char buf[4] = {0, 0, 0, 0};
buf[0] = chars->ch1 + 1;
buf[1] = chars->ch2 + 1;
buf[2] = chars->ch3 + 1;
return buf;
}
char *
bstr_to_str ( char *s )
{
int len = strlen(s);
static char new[MAXCOL+1];
unsigned newlen = 0;
while (s[len-1] == S_FILLER)
s[--len] = '\0';
while (*s) {
switch (*s) {
case S_MACRO1:
case S_MACRO2:
case S_MACRO3:
if (*s == S_MACRO1 && *(s+1) == S_CAPS) {
s++;
break;
}
/* shouldn't appear in vocabulary list */
new [ newlen++ ] = err_chars[*s];
break;
case S_CAPS:
if (*(s+1) >= S_OFF)
new [ newlen++ ] = upper[*(++s) - S_OFF];
else
new [ newlen++ ] = err_chars[S_CAPS];
break;
case S_PUNC:
if (*(s+1) >= S_OFF)
new [ newlen++ ] = punct[*(++s) - S_OFF];
else
new [ newlen++ ] = err_chars[S_PUNC];
break;
case S_BLANK:
new [ newlen++ ] = ' ';
break;
default:
new [ newlen++ ] = lower[*s - S_OFF];
}
s++;
}
new [ newlen ] = '\0';
return new;
}
void
disp_ch ( char x )
{
putchar(x);
column++;
}
void
disp_str ( char *fmt, ... )
{
va_list argptr;
static char buf[16];
short len;
va_start(argptr, fmt);
vsprintf(buf, fmt, argptr);
va_end(argptr);
len = strlen(buf);
if (column + len > MAXCOL)
newline();
printf(buf);
column += len;
}
void
error ( char *fmt, ... )
{
va_list argptr;
fprintf(stderr, "Error: ");
va_start(argptr, fmt);
vfprintf(stderr, fmt, argptr);
va_end(argptr);
exit(1);
}
void
disp_bits ( char c )
{
disp_str(" %d%d%d%d%d%d%d%d",
!!(c & 0x80),
!!(c & 0x40),
!!(c & 0x20),
!!(c & 0x10),
!!(c & 0x08),
!!(c & 0x04),
!!(c & 0x02),
!!(c & 0x01));
}
void
frob_file ( const char *filename )
{
register unsigned count = 0;
unsigned words;
unsigned i;
unsigned char ch, n;
struct bits word[3];
char buf[10];
int entry_width;
struct info_header header;
unsigned long pos;
if((infile = fopen(filename, "rb")) == NULL)
error("Can't open \"%s\".\n", filename);
printf("%s:\n", filename);
fread(&header, sizeof(header), 1, infile);
swab((char *)&header.release, (char *)&i, 2);
printf("Release %u, updated %.6s\n", i, &header.rev_date[0]);
swab((char *)&header.vocab_offset, (char *)&i, 2);
#ifdef DEBUG
printf("Vocabulary table offset: %04X\n", i);
#endif
if (fseek(infile, pos = i, SEEK_SET) != 0)
error("Can't seek offset %04X.\n", pos);
/* skip leading info */
if (fread(&ch, sizeof(ch), 1, infile) < 1)
error("Can't read character at offset %04X.\n", ftell(infile));
if (fseek(infile, pos = ch, SEEK_CUR) != 0)
error("Can't skip %ld characters from offset %04X.\n", pos, ftell(infile));
fread(&ch, sizeof(ch), 1, infile);
/* 0x07 for 6 letters, 0x09 for 9 letters */
n = (ch < 9) ? 6 : 9;
if (fread(&i, sizeof(i), 1, infile) < 1)
error("Can't read word at offset %ld.\n", ftell(infile));
swab((char *)&i, (char *)&words, 2);
#ifdef DEBUG
printf("Vocabulary entries: %d\n", words);
#else
if (!numbers)
printf("%d vocabulary entries\n", words);
#endif
entry_width = n;
n /= 3;
if (numbers)
entry_width += 5;
if (bits)
entry_width += (wide ? 2 : 8) * 3 + 3;
while ( count < words ) {
if (fread(buf, sizeof(struct bits), n, infile) < n)
error("Can't read vocabulary word at offset %ld.\n", ftell(infile));
swab(buf, (char *)&word, sizeof(struct bits) * n);
++count;
if (numbers)
disp_str("%04d ", count);
strcpy(buf, bits_to_bstr(&word[0]));
strcat(buf, bits_to_bstr(&word[1]));
if (n >= 3)
strcat(buf, bits_to_bstr(&word[2]));
disp_str(n >= 3 ? "%-9s" : "%-6s", bstr_to_str(buf));
if (fread(buf, sizeof(char), 3, infile) < 3)
error("Can't read vocabulary flags at offset %ld.\n", ftell(infile));
if (bits)
if (wide)
disp_str(" %02x %02x %02x", buf[1], buf[2], buf[3]);
else {
disp_ch(' ');
disp_bits(buf[1]);
disp_bits(buf[2]);
disp_bits(buf[3]);
}
if (wide && column + entry_width + 2 < MAXCOL)
disp_str(" ");
else
newline();
}
if (column)
newline();
}
#ifndef LINT
const char sccsid[] = "@(#) " __FILE__ " by Mike Threepoint compiled " __DATE__;
#endif
void
info ( void )
{
puts("Display a vocabulary list of an Infocom(tm) adventure game data file.\n"
"\n"
"Usage: vocab [/#] [/1] [/b] file...\n"
"\n"
"\t/# toggle numbers\n"
"\t/1 toggle one-column list\n"
"\t/b toggle display of additional info encoded with each entry\n"
"\n");
exit(0);
}
void
parse ( char *parm )
{
if (*parm == '-' || *parm == '/')
switch (tolower(*++parm)) {
case '#':
case 'n':
numbers = !numbers;
break;
case '1':
wide = !wide;
break;
case 'b':
bits = !bits;
break;
case 'h':
case '?':
info();
}
else {
if (did_file) newline();
frob_file(parm);
did_file = 1;
}
}
int
main ( const unsigned argc, char *argv[] )
{
if (argc > 1) {
register count;
if (strcmp(argv[1], "?") == 0)
info();
for (count = 1; count < argc; count++)
parse(argv[count]);
if (did_file) return 0;
}
info();
return 1;
}
More information about the Alt.sources
mailing list