2
/* $Id: jzexe.c,v 1.1.1.1 2000/05/10 14:21:34 jholder Exp $
3
* --------------------------------------------------------------------
4
* see doc/License.txt for License Information
5
* --------------------------------------------------------------------
7
* File name: $Id: jzexe.c,v 1.1.1.1 2000/05/10 14:21:34 jholder Exp $
11
* Modification history:
13
* Revision 1.1.1.1 2000/05/10 14:21:34 jholder
18
* --------------------------------------------------------------------
24
* This program creates standalone game executables from Infocom
25
* format story files. It does so by concatenating the JZip executable
26
* with the story file and then patching the excutable to start reading
27
* the story from the correct file position.
29
* Written by Magnus Olsson (mol@df.lth.se), 20 November, 1995,
30
* as part of the JZip distribution.
31
* You may use this code in any way you like as long as it is
32
* attributed to its author.
51
/* The size of the buffer used when copying files */
54
static unsigned char *buf;
55
static unsigned char *jzipmagic = ( unsigned char * ) MAGIC_STRING;
57
#if defined(MSDOS) || defined(__MSDOS__)
58
#define READ_MODE "rb"
59
#define WRITE_MODE "wb"
62
#define WRITE_MODE "w"
65
#define NAME_LENGTH 200
66
static char in_name[NAME_LENGTH + 1];
67
static char out_name[NAME_LENGTH + 1];
69
/* The name of the JZip executable */
70
#define JZIP_NAME "jzip.exe"
72
void abort_program( int exit_code )
74
fprintf( stderr, "\nProgram aborted.\n" );
75
/* Remove output file; it's probably corrupted. */
80
FILE *open_file( char *name, int input )
87
strncpy( in_name, name, NAME_LENGTH );
88
/* If name is too long, in_name won't be null terminated: fix this,
90
in_name[NAME_LENGTH] = 0;
95
strncpy( out_name, name, NAME_LENGTH );
96
out_name[NAME_LENGTH] = 0;
99
f = fopen( name, mode );
102
fprintf( stderr, "Error opening file %s\n", name );
109
* Search through the file f for the magic string. If found, return
110
* the offset stored in the patch area, otherwise abort the program.
112
long search( FILE * f )
117
while ( ( c = getc( f ) ) > -1 )
119
if ( ( unsigned char ) c != jzipmagic[i] )
121
if ( ( unsigned char ) c == jzipmagic[0] )
126
else if ( ++i == MAGIC_END )
128
/* Found magic string. */
131
/* Next byte must be zero. */
132
if ( getc( f ) != 0 )
134
fprintf( stderr, "Error: standalone flag != 0\n" );
138
offset += 256L * getc( f );
139
offset += 65536L * getc( f );
143
fprintf( stderr, "Couldn't find magic string." );
148
* Copy the file in to the file out. Set length to the length in bytes
149
* of the input file, and set magic_pos to the offset of the first
150
* occurrence of MAGIC_STRING.
152
void copy_and_search( FILE * in, FILE * out, long *length, long *magic_pos )
155
size_t buf_idx, buf_end;
159
buf_idx = buf_end = 0;
165
if ( buf_idx >= buf_end )
167
/* Reached end of input buffer; read in a new chunk */
168
buf_end = fread( buf, 1, BUFSIZE, in );
171
/* Reached end of input file.
172
* pos is now the number of bytes read. */
176
if ( fwrite( buf, 1, buf_end, out ) != buf_end )
178
fprintf( stderr, "Write error on file %s\n", out_name );
186
c = buf[buf_idx++]; /* Get current character */
187
++pos; /* Offset of next byte */
189
/* The following search algorithm utilizes the fact that the
190
* first character of the magic string is unique. */
191
if ( ( unsigned char ) c != jzipmagic[magic_idx] )
192
if ( ( unsigned char ) c == jzipmagic[0] )
196
else if ( ++magic_idx == MAGIC_END )
198
/* Matched the entire magic string */
199
if ( *magic_pos > 0 )
201
/* If this condition occurs, the magic string isn't unique,
202
* with potentially bad consequences. Re-compile with a
203
* different magic string. */
204
fprintf( stderr, "Found more than one instance of the magic string.\n" );
215
* Copy the input file to the output file.
217
void copy( FILE * in, FILE * out )
224
bytes_read = fread( buf, 1, BUFSIZE, in );
225
if ( bytes_read > 0 )
226
if ( fwrite( buf, 1, bytes_read, out ) != bytes_read )
228
fprintf( stderr, "Write error on file %s\n", out_name );
235
while ( bytes_read > 0 );
238
fprintf( stderr, "Nothing read from file %s - probably an error.\n", in_name );
242
* Patch the output file so it will read Z code from the correct position.
244
void patch( FILE * f, long pos, long value )
249
if ( fseek( f, pos, SEEK_SET ) )
251
fprintf( stderr, "Seek error on file %s\n", out_name );
255
/* Set STANDALONE_FLAG to FALSE */
257
/* Write the length of the JZip interpreter to the file as a
258
* little-endian, 24-bit number. */
259
for ( i = 0; i < 3; ++i )
261
fputc( ( int ) ( value & 0xff ), f );
266
void create_executable( char *story, char *jzip )
270
char fn[NAME_LENGTH + 4];
274
strcpy( in_name, "" );
275
strcpy( out_name, "" );
277
if ( strlen( story ) > NAME_LENGTH || strlen( jzip ) > NAME_LENGTH )
279
fprintf( stderr, "Filename too long.\n" );
283
ext = strrchr( fn, '.' );
286
strcat( fn, ".exe" );
288
printf( "Creating standalone story %s\n", fn );
289
out = open_file( fn, FALSE );
291
printf( "Copying JZip interpreter (%s).\n", jzip );
292
in = open_file( jzip, TRUE );
294
copy_and_search( in, out, &length, &pos );
298
fprintf( stderr, "Error: couldn't find magic string.\n" );
299
fprintf( stderr, "Check that you have the right version of JZip.\n" );
303
printf( "Copying story file %s.\n", story );
304
in = open_file( story, TRUE );
308
printf( "Patching interpreter.\n" );
309
patch( out, pos, length );
315
void extract_zcode( char *filename )
319
char fn[NAME_LENGTH + 4];
323
strcpy( in_name, "" );
324
strcpy( out_name, "" );
326
if ( strlen( filename ) > NAME_LENGTH )
328
fprintf( stderr, "Filename too long.\n" );
332
in = open_file( filename, TRUE );
333
offset = search( in );
335
fseek( in, offset, SEEK_SET );
336
z_version = fgetc( in );
337
if ( z_version < 1 || z_version > 8 )
339
fprintf( stderr, "Unsupported Z code version: %d\n", z_version );
342
strcpy( fn, filename );
343
ext = strrchr( fn, '.' );
345
ext = fn + strlen( fn );
346
sprintf( ext, ".z%d", z_version );
348
printf( "Extracting Z code to story file %s\n", fn );
349
out = open_file( fn, FALSE );
351
fputc( z_version, out );
357
#define USAGE "JZexe Ver. 1.1 - creates standalone Infocom-format games for MS-DOS and Linux.\n\
358
(c) 1995 by Magnus Olsson (mol@df.lth.se)\n\n\
359
Usage: jzexe story_file\n\
360
jzexe story_file jzip_file\n\n\
361
These two commands create a standalone game from an Infocom story file.\n\
362
In the first case, it is assumed that the JZip interpreter is called jzip.exe\n\
363
and resides in the current directory.\n\n\
364
jzexe -x game.exe\n\n\
365
This command extracts the Z code from a standalone executable and creates\n\
366
a story file that can be played with JZip or any other interpreter.\n\
367
Note that the extension '.exe' of the game file must be given.\n"
369
void main( int argc, char **argv )
371
buf = ( unsigned char * ) malloc( BUFSIZE * sizeof ( unsigned char ) );
373
if ( argc == 3 && strcmp( argv[1], "-x" ) == 0 )
374
extract_zcode( argv[2] );
375
else if ( argc == 2 )
376
create_executable( argv[1], JZIP_NAME );
377
else if ( argc == 3 )
378
create_executable( argv[1], argv[2] );
380
fprintf( stderr, USAGE );