~ubuntu-branches/ubuntu/wily/gargoyle-free/wily-proposed

« back to all changes in this revision

Viewing changes to tads/tads2/msdos/dosmktrx.c

  • Committer: Bazaar Package Importer
  • Author(s): Sylvain Beucler
  • Date: 2009-09-11 20:09:43 UTC
  • Revision ID: james.westby@ubuntu.com-20090911200943-idgzoyupq6650zpn
Tags: upstream-2009-08-25
ImportĀ upstreamĀ versionĀ 2009-08-25

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* 
 
2
 *   Copyright (c) 1990, 2002 Michael J. Roberts.  All Rights Reserved.
 
3
 *   
 
4
 *   Please see the accompanying license file, LICENSE.TXT, for information
 
5
 *   on using and copying this software.  
 
6
 */
 
7
/*
 
8
Name
 
9
  dosmktrx - DOS TRX builder
 
10
Function
 
11
  Combines the generic runtime module (TRX) with a game data file (.GAM) to
 
12
  produce a single file that contains the entire game (code + data).
 
13
Notes
 
14
  Define MKTRX_SETICON to include icon-setting code.  The icon-setting
 
15
  code is applicable only to 32-bit Windows executables (PE format files).
 
16
Modified
 
17
  11/16/92 MJRoberts     - add end signature
 
18
  11/11/91 MJRoberts     - creation
 
19
*/
 
20
 
 
21
#include <dos.h>
 
22
#include <io.h>
 
23
#include <stdio.h>
 
24
#include <string.h>
 
25
#include <stdlib.h>
 
26
 
 
27
#ifndef FALSE
 
28
#define FALSE 0
 
29
#endif
 
30
#ifndef TRUE
 
31
#define TRUE 1
 
32
#endif
 
33
 
 
34
/* ------------------------------------------------------------------------ */
 
35
/*
 
36
 *   Icon-setting code for PE (win32 exe) files 
 
37
 */
 
38
#ifdef MKTRX_SETICON
 
39
 
 
40
#include <Windows.h>
 
41
 
 
42
/*
 
43
 *   Icon resource file format structures 
 
44
 */
 
45
 
 
46
#pragma pack( push )
 
47
#pragma pack( 2 )
 
48
 
 
49
/* icon directory entry in ICO file */
 
50
typedef struct
 
51
{
 
52
    BYTE        bWidth;          // Width, in pixels, of the image
 
53
    BYTE        bHeight;         // Height, in pixels, of the image
 
54
    BYTE        bColorCount;     // Number of colors in image (0 if >=8bpp)
 
55
    BYTE        bReserved;       // Reserved ( must be 0)
 
56
    WORD        wPlanes;         // Color Planes
 
57
    WORD        wBitCount;       // Bits per pixel
 
58
    DWORD       dwBytesInRes;    // How many bytes in this resource?
 
59
    DWORD       dwImageOffset;   // Where in the file is this image?
 
60
} ICONDIRENTRY, *LPICONDIRENTRY;
 
61
 
 
62
/* icon directory in ICO file */
 
63
typedef struct
 
64
{
 
65
    WORD           idReserved;   // Reserved (must be 0)
 
66
    WORD           idType;       // Resource Type (1 for icons)
 
67
    WORD           idCount;      // How many images?
 
68
    ICONDIRENTRY   idEntries[1]; // An entry for each image (idCount of 'em)
 
69
} ICONDIR, *LPICONDIR;
 
70
 
 
71
/* group icon directory entry in EXE file */
 
72
typedef struct{
 
73
    BYTE   bWidth;               // Width, in pixels, of the image
 
74
    BYTE   bHeight;              // Height, in pixels, of the image
 
75
    BYTE   bColorCount;          // Number of colors in image (0 if >=8bpp)
 
76
    BYTE   bReserved;            // Reserved
 
77
    WORD   wPlanes;              // Color Planes
 
78
    WORD   wBitCount;            // Bits per pixel
 
79
    DWORD  dwBytesInRes;         // how many bytes in this resource?
 
80
    WORD   nID;                  // the ID
 
81
} GRPICONDIRENTRY, *LPGRPICONDIRENTRY;
 
82
 
 
83
/* group icon directory header in EXE file */
 
84
typedef struct {
 
85
    WORD            idReserved;   // Reserved (must be 0)
 
86
    WORD            idType;       // Resource type (1 for icons)
 
87
    WORD            idCount;      // How many images?
 
88
    GRPICONDIRENTRY idEntries[1]; // The entries for each image
 
89
} GRPICONDIR, *LPGRPICONDIR;
 
90
 
 
91
#pragma pack( pop )    
 
92
 
 
93
 
 
94
/* ------------------------------------------------------------------------ */
 
95
/*
 
96
 *   exit with an error 
 
97
 */
 
98
static void errexit(const char *msg)
 
99
{
 
100
    printf("%s\n", msg);
 
101
    exit(1);
 
102
}
 
103
 
 
104
/*
 
105
 *   Resource directory information structure 
 
106
 */
 
107
typedef struct res_info_t res_info_t;
 
108
struct res_info_t
 
109
{
 
110
    /* seek address of the resource directory */
 
111
    DWORD  dir_pos;
 
112
 
 
113
    /* virtual address base of resource data */
 
114
    DWORD  base_addr;
 
115
};
 
116
 
 
117
/*
 
118
 *   Look up the resource directory information.  Fills in the res_info
 
119
 *   structure with the resource data.  Returns zero on success, or a
 
120
 *   non-zero error code on failure.  
 
121
 */
 
122
static int find_resource_dir(FILE *fp, res_info_t *res_info)
 
123
{
 
124
    IMAGE_DOS_HEADER doshdr;
 
125
    IMAGE_NT_HEADERS nthdr;
 
126
    DWORD cnt;
 
127
    DWORD lastsect;
 
128
    DWORD start;
 
129
    
 
130
    /* read the old-style DOS header */
 
131
    if (fread(&doshdr, sizeof(doshdr), 1, fp) != 1)
 
132
        return 1;
 
133
    if (doshdr.e_magic != IMAGE_DOS_SIGNATURE)
 
134
        return 2;
 
135
 
 
136
    /* seek to the PE header and read that */
 
137
    fseek(fp, doshdr.e_lfanew, SEEK_SET);
 
138
    if (fread(&nthdr, sizeof(nthdr), 1, fp) != 1)
 
139
        return 3;
 
140
    if (nthdr.Signature != IMAGE_NT_SIGNATURE)
 
141
        return 4;
 
142
 
 
143
    /* find the first image section */
 
144
    start = doshdr.e_lfanew
 
145
            + FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader)
 
146
            + nthdr.FileHeader.SizeOfOptionalHeader;
 
147
    fseek(fp, start, SEEK_SET);
 
148
 
 
149
    /* read image sections */
 
150
    for (lastsect = 0, cnt = 0 ; cnt < nthdr.FileHeader.NumberOfSections ;
 
151
         ++cnt)
 
152
    {
 
153
        IMAGE_SECTION_HEADER imghdr;
 
154
 
 
155
        /* read this section header */
 
156
        if (fread(&imghdr, sizeof(imghdr), 1, fp) != 1)
 
157
            return 5;
 
158
 
 
159
        /* if this is the resource section, return it */
 
160
        if (memcmp(imghdr.Name, ".rsrc", IMAGE_SIZEOF_SHORT_NAME) == 0)
 
161
        {
 
162
            /* fill in the information */
 
163
            res_info->dir_pos = imghdr.PointerToRawData;
 
164
            res_info->base_addr = imghdr.VirtualAddress;
 
165
 
 
166
            /* return success */
 
167
            return 0;
 
168
        }
 
169
    }
 
170
 
 
171
    /* 
 
172
     *   we went through all the sections and failed to find a resource
 
173
     *   section - return an error 
 
174
     */
 
175
    return 6;
 
176
}
 
177
 
 
178
/*
 
179
 *   Get a file offset and size for a resource, given the resource type
 
180
 *   and either the identifier or the index.  If 'id_is_index' is true,
 
181
 *   the id is the 0-based index; otherwise, it's the ID.  Returns zero if
 
182
 *   the resource is not found, otherwise returns the seek offset in the
 
183
 *   file of the resource's data, and fills in *data_size with the size of
 
184
 *   the data.  
 
185
 */
 
186
static long find_resource(FILE *fp, long *data_size,
 
187
                          const res_info_t *res_info,
 
188
                          LPSTR res_type, int res_id, int id_is_index)
 
189
{
 
190
    IMAGE_RESOURCE_DIRECTORY dirhdr;
 
191
    int i;
 
192
    int found;
 
193
    IMAGE_RESOURCE_DIRECTORY_ENTRY direntry;
 
194
    IMAGE_RESOURCE_DATA_ENTRY dataentry;
 
195
 
 
196
    /* read the root directory, which contains the type index */
 
197
    fseek(fp, res_info->dir_pos, SEEK_SET);
 
198
    if (fread(&dirhdr, sizeof(dirhdr), 1, fp) != 1)
 
199
        return 0;
 
200
 
 
201
    /* find the desired type */
 
202
    for (i = 0, found = FALSE ;
 
203
         i < dirhdr.NumberOfNamedEntries + dirhdr.NumberOfIdEntries ; ++i)
 
204
    {
 
205
        /* read the next entry */
 
206
        if (fread(&direntry, sizeof(direntry), 1, fp) != 1)
 
207
            return 0;
 
208
 
 
209
        /* check to see if it's the type we want */
 
210
        if (direntry.Id == (WORD)res_type)
 
211
        {
 
212
            found = TRUE;
 
213
            break;
 
214
        }
 
215
    }
 
216
 
 
217
    /* if we didn't find the type we want, give up */
 
218
    if (!found)
 
219
        return 0;
 
220
 
 
221
    /* we found the type index - read its directory header */
 
222
    fseek(fp, res_info->dir_pos + direntry.OffsetToDirectory, SEEK_SET);
 
223
    if (fread(&dirhdr, sizeof(dirhdr), 1, fp) != 1)
 
224
        return 0;
 
225
 
 
226
    /* now search the directory for our index or ID */
 
227
    for (i = 0, found = FALSE ;
 
228
         i < dirhdr.NumberOfNamedEntries + dirhdr.NumberOfIdEntries ; ++i)
 
229
    {
 
230
        /* read the next entry */
 
231
        if (fread(&direntry, sizeof(direntry), 1, fp) != 1)
 
232
            return 0;
 
233
 
 
234
        /* if this is the one we want, return it */
 
235
        if ((id_is_index && res_id == i)
 
236
            || (!id_is_index && !direntry.NameIsString
 
237
                && direntry.Id == res_id))
 
238
        {
 
239
            found = TRUE;
 
240
            break;
 
241
        }
 
242
    }
 
243
 
 
244
    /* if we didn't find it, give up */
 
245
    if (!found)
 
246
        return 0;
 
247
 
 
248
    /* 
 
249
     *   got it - this entry will point to yet another directory, which
 
250
     *   will contain the actual data; seek to the directory and read its
 
251
     *   first member 
 
252
     */
 
253
    fseek(fp, res_info->dir_pos + direntry.OffsetToDirectory, SEEK_SET);
 
254
    if (fread(&dirhdr, sizeof(dirhdr), 1, fp) != 1
 
255
        || fread(&direntry, sizeof(direntry), 1, fp) != 1)
 
256
        return 0;
 
257
 
 
258
    /* seek to and read the first directory entry's data header */
 
259
    fseek(fp, res_info->dir_pos + direntry.OffsetToData, SEEK_SET);
 
260
    if (fread(&dataentry, sizeof(dataentry), 1, fp) != 1)
 
261
        return 0;
 
262
 
 
263
    /* set the size from the directory entry */
 
264
    *data_size = dataentry.Size;
 
265
 
 
266
    /* 
 
267
     *   compute and return the data address - note that the address is
 
268
     *   relative to the resource section's virtual address setting, so we
 
269
     *   must adjust the virtual address to determine the seek address in
 
270
     *   the file 
 
271
     */
 
272
    return dataentry.OffsetToData - res_info->base_addr + res_info->dir_pos;
 
273
}
 
274
 
 
275
/*
 
276
 *   Replace the icon at icon_idx in the EXE file with the icon in the ICO
 
277
 *   file.  Returns zero on success, non-zero on error.  
 
278
 */
 
279
static int replace_icon(FILE *fpexe, int icon_idx, FILE *fpico)
 
280
{
 
281
    res_info_t res_info;
 
282
    long  group_pos;
 
283
    long  group_size;
 
284
    int   err = 0;
 
285
    int   i;
 
286
    GRPICONDIR group_dir;
 
287
    GRPICONDIRENTRY *group_dir_entry = 0;
 
288
    ICONDIR  icon_dir;
 
289
    ICONDIRENTRY  *icon_dir_entry = 0;
 
290
    ICONDIRENTRY  *cur_ico;
 
291
 
 
292
    /* seek to the start of the executable */
 
293
    fseek(fpexe, 0, SEEK_SET);
 
294
 
 
295
    /* read the ICO directory header */
 
296
    if (fread(&icon_dir,
 
297
              sizeof(icon_dir) - sizeof(ICONDIRENTRY), 1, fpico) != 1)
 
298
    {
 
299
        err = 101;
 
300
        goto done;
 
301
    }
 
302
 
 
303
    /* read the ICO directory entries */
 
304
    icon_dir_entry = (ICONDIRENTRY *)malloc(icon_dir.idCount
 
305
                                            * sizeof(ICONDIRENTRY));
 
306
    if (icon_dir_entry == 0)
 
307
    {
 
308
        err = 102;
 
309
        goto done;
 
310
    }
 
311
    if (fread(icon_dir_entry, icon_dir.idCount * sizeof(ICONDIRENTRY),
 
312
              1, fpico) != 1)
 
313
    {
 
314
        err = 103;
 
315
        goto done;
 
316
    }
 
317
 
 
318
    /* find the resource directory in the EXE file */
 
319
    err = find_resource_dir(fpexe, &res_info);
 
320
    if (err != 0)
 
321
        goto done;
 
322
 
 
323
    /* look up the icon group resource by index */
 
324
    group_pos = find_resource(fpexe, &group_size, &res_info,
 
325
                              RT_GROUP_ICON, icon_idx, TRUE);
 
326
 
 
327
    /* seek to and read the group icon directory */
 
328
    fseek(fpexe, group_pos, SEEK_SET);
 
329
    if (fread(&group_dir, sizeof(group_dir) - sizeof(GRPICONDIRENTRY),
 
330
              1, fpexe) != 1)
 
331
    {
 
332
        err = 104;
 
333
        goto done;
 
334
    }
 
335
 
 
336
    /* read the group directory entries */
 
337
    group_dir_entry = (GRPICONDIRENTRY *)malloc(group_dir.idCount
 
338
                                                * sizeof(GRPICONDIRENTRY));
 
339
    if (group_dir_entry == 0)
 
340
    {
 
341
        err = 105;
 
342
        goto done;
 
343
    }
 
344
    if (fread(group_dir_entry, group_dir.idCount * sizeof(GRPICONDIRENTRY),
 
345
              1, fpexe) != 1)
 
346
    {
 
347
        err = 106;
 
348
        goto done;
 
349
    }
 
350
 
 
351
    /* 
 
352
     *   go through the image types in the replacement icon file; for each
 
353
     *   one, try to find the corresponding entry in the EXE file, and
 
354
     *   replace it 
 
355
     */
 
356
    for (i = 0, cur_ico = icon_dir_entry ; i < icon_dir.idCount ;
 
357
         ++i, ++cur_ico)
 
358
    {
 
359
        int j;
 
360
        GRPICONDIRENTRY *cur_grp;
 
361
        int found;
 
362
 
 
363
        /* try to find this icon type in the EXE group icon directory */
 
364
        for (found = FALSE, j = 0, cur_grp = group_dir_entry ;
 
365
             j < group_dir.idCount ; ++j, ++cur_grp)
 
366
        {
 
367
            /* see if it matches */
 
368
            if (cur_grp->bWidth == cur_ico->bWidth
 
369
                && cur_grp->bHeight == cur_ico->bHeight
 
370
                && cur_grp->bColorCount == cur_ico->bColorCount
 
371
                && cur_grp->dwBytesInRes == cur_ico->dwBytesInRes)
 
372
            {
 
373
                long  icon_pos;
 
374
                long  icon_size;
 
375
                long  rem;
 
376
 
 
377
                /* note that we found it */
 
378
                found = TRUE;
 
379
 
 
380
                /* look up this icon entry */
 
381
                icon_pos = find_resource(fpexe, &icon_size, &res_info,
 
382
                                         RT_ICON, cur_grp->nID, FALSE);
 
383
                if (icon_pos == 0)
 
384
                {
 
385
                    err = 107;
 
386
                    goto done;
 
387
                }
 
388
 
 
389
                /* seek to the icon resource in the EXE file */
 
390
                fseek(fpexe, icon_pos, SEEK_SET);
 
391
 
 
392
                /* seek to the icon image in the ICO file */
 
393
                fseek(fpico, cur_ico->dwImageOffset, SEEK_SET);
 
394
 
 
395
                /* copy data from the ICO file to the EXE file */
 
396
                for (rem = cur_ico->dwBytesInRes ; rem != 0 ; )
 
397
                {
 
398
                    size_t cur;
 
399
                    char   copybuf[512];
 
400
 
 
401
                    /* 
 
402
                     *   read up to the buffer size or the amount
 
403
                     *   remaining, whichever is less 
 
404
                     */
 
405
                    cur = sizeof(copybuf);
 
406
                    if (rem < (long)cur)
 
407
                        cur = (size_t)rem;
 
408
                    if (fread(copybuf, cur, 1, fpico) != 1)
 
409
                    {
 
410
                        err = 108;
 
411
                        goto done;
 
412
                    }
 
413
 
 
414
                    /* write it out to the EXE file */
 
415
                    if (fwrite(copybuf, cur, 1, fpexe) != 1)
 
416
                    {
 
417
                        err = 109;
 
418
                        goto done;
 
419
                    }
 
420
 
 
421
                    /* deduct the amount read from the total */
 
422
                    rem -= cur;
 
423
                }
 
424
 
 
425
                /* no need to look any further */
 
426
                break;
 
427
            }
 
428
        }
 
429
 
 
430
        /* if we didn't find it, report an error */
 
431
        if (!found)
 
432
        {
 
433
            /* 
 
434
             *   note the error, but keep looking anyway, because there
 
435
             *   may still be other resources that we can successfully
 
436
             *   use; this error is essentially just a warning that there
 
437
             *   were some icon types in the ICO file that we couldn't
 
438
             *   store into the EXE file 
 
439
             */
 
440
            err = 110;
 
441
        }
 
442
    }
 
443
 
 
444
done:
 
445
    /* free any memory we allocated */
 
446
    if (group_dir_entry != 0)
 
447
        free(group_dir_entry);
 
448
    if (icon_dir_entry != 0)
 
449
        free(icon_dir_entry);
 
450
 
 
451
    /* return the result */
 
452
    return err;
 
453
}
 
454
 
 
455
 
 
456
#endif /* MKTRX_SETICON
 
457
 
 
458
/* ------------------------------------------------------------------------ */
 
459
/*
 
460
 *   A couple of OS routines we lifted from osgen.c
 
461
 */
 
462
 
 
463
static void os_defext(char *fn, char *ext)
 
464
{
 
465
    char *p = fn + strlen(fn);
 
466
    while (p > fn)
 
467
    {
 
468
        --p;
 
469
        if (*p == '.') return;                  /* already has an extension */
 
470
        if (*p == '/' || *p == '\\' || * p== ':') break;    /* found a path */
 
471
    }
 
472
    strcat(fn, ".");                                           /* add a dot */
 
473
    strcat(fn, ext);                                   /* add the extension */
 
474
}
 
475
 
 
476
static void os_remext(char *fn)
 
477
{
 
478
    char *p = fn + strlen(fn);
 
479
    while (p > fn)
 
480
    {
 
481
        --p;
 
482
        if (*p == '.')
 
483
        {
 
484
            *p = '\0';
 
485
            return;
 
486
        }
 
487
        if (*p == '/' || *p == '\\' || *p == ':') return;
 
488
    }
 
489
}
 
490
 
 
491
static char *os_get_root_name(char *buf)
 
492
{
 
493
    char *rootname;
 
494
 
 
495
    /* scan the name for path separators */
 
496
    for (rootname = buf ; *buf != '\0' ; ++buf)
 
497
    {
 
498
        /* if this is a path separator, remember it */
 
499
        if (*buf == '/' || *buf == '\\' || *buf == ':')
 
500
        {
 
501
            /* 
 
502
             *   It's a path separators - for now, assume the root will
 
503
             *   start at the next character after this separator.  If we
 
504
             *   find another separator later, we'll forget about this one
 
505
             *   and use the later one instead.  
 
506
             */
 
507
            rootname = buf + 1;
 
508
        }
 
509
    }
 
510
 
 
511
    /* return the last root name candidate */
 
512
    return rootname;
 
513
}
 
514
 
 
515
/*
 
516
 *   Write a section header.  The section header consists of a check value
 
517
 *   that indicates that the seek position is correct, and a section type
 
518
 *   code.  
 
519
 *   
 
520
 *   The typecode must always be four bytes long.  
 
521
 */
 
522
static void write_header(FILE *fpout, const char *typecode)
 
523
{
 
524
    long comp;
 
525
    
 
526
    /* compute the check code */
 
527
    comp = ~ftell(fpout);
 
528
 
 
529
    /* write the check and the type code */
 
530
    if (fwrite(&comp, sizeof(comp), 1, fpout) != 1
 
531
        || fwrite(typecode, 4, 1, fpout) != 1)
 
532
    {
 
533
        fprintf(stderr, "error writing section header (%s)\n", typecode);
 
534
        exit(3);
 
535
    }
 
536
}
 
537
 
 
538
/*
 
539
 *   Write the end-of-section trailing header.  The trailing header
 
540
 *   consists of a check indicating its own seek location, so we can tell
 
541
 *   that the trailing header is indeed a trailing header; a type code;
 
542
 *   and the seek position of the leading data block header.
 
543
 *   
 
544
 *   The typecode must always be four bytes long.  
 
545
 */
 
546
static void write_trailing_header(FILE *fpout, long startofs,
 
547
                                  const char *typecode)
 
548
{
 
549
    long comp;
 
550
    
 
551
    /* compute the check */
 
552
    comp = ~ftell(fpout);
 
553
 
 
554
    /* write the trailing header */
 
555
    if (fwrite(&comp, sizeof(comp), 1, fpout) != 1
 
556
        || fwrite(typecode, 4, 1, fpout) != 1
 
557
        || fwrite(&startofs, sizeof(startofs), 1, fpout) != 1)
 
558
    {
 
559
        fprintf(stderr, "error writing trailing header (%s)\n", typecode);
 
560
        exit(3);
 
561
    }
 
562
}
 
563
 
 
564
/*
 
565
 *   A great big buffer for copying the files around.
 
566
 */
 
567
static char buf[30*1024];
 
568
 
 
569
static void append_file(FILE *fpout, FILE *fpin, char *typ, int put_size)
 
570
{
 
571
    long  startofs;
 
572
 
 
573
    /* remember where this starts */
 
574
    startofs = ftell(fpout);
 
575
 
 
576
    /* write the section header */
 
577
    write_header(fpout, typ);
 
578
 
 
579
    /* write the size prefix if desired */
 
580
    if (put_size)
 
581
    {
 
582
        long siz;
 
583
 
 
584
        /* seek to the end to get the size, then back to the start */
 
585
        fseek(fpin, 0L, SEEK_END);
 
586
        siz = ftell(fpin);
 
587
        fseek(fpin, 0L, SEEK_SET);
 
588
 
 
589
        /* write the size prefix */
 
590
        if (fwrite(&siz, sizeof(siz), 1, fpout) != 1)
 
591
        {
 
592
            fprintf(stderr, "error writing output file\n");
 
593
            exit(3);
 
594
        }
 
595
    }
 
596
 
 
597
    /* copy the data from the input file to the output file */
 
598
    for ( ;; )
 
599
    {
 
600
        size_t siz;
 
601
        
 
602
        if (!(siz = fread(buf, 1, sizeof(buf), fpin))) break;
 
603
        if (fwrite(buf, 1, siz, fpout) != siz)
 
604
        {
 
605
            fprintf(stderr, "error writing output file\n");
 
606
            exit(3);
 
607
        }
 
608
    }
 
609
 
 
610
    /* write the trailing descriptor block */
 
611
    write_trailing_header(fpout, startofs, typ);
 
612
}
 
613
 
 
614
 
 
615
int main(int argc, char **argv)
 
616
{
 
617
    FILE   *fptrx, *fpgam, *fpout;
 
618
    char    trxnam[_MAX_PATH];
 
619
    char    gamnam[_MAX_PATH];
 
620
    char    outnam[_MAX_PATH];
 
621
    char   *config_file;
 
622
    char   *argv0 = argv[0];
 
623
    size_t  siz;
 
624
    char   *p;
 
625
    int     curarg;
 
626
    char   *savext;
 
627
    int     use_html_exe = FALSE;
 
628
    int     use_t3_exe = FALSE;
 
629
    int     use_prot_exe = FALSE;
 
630
    int     use_win32_exe = FALSE;
 
631
    int     show_logo = TRUE;
 
632
    char   *charlib;
 
633
    char    charlibname[_MAX_PATH];
 
634
    char   *res_type = "TGAM";
 
635
#ifdef MKTRX_SETICON
 
636
    char    iconname[_MAX_PATH];
 
637
    int     set_icon = FALSE;
 
638
#endif
 
639
 
 
640
    /* presume we won't find a saved game extension option */
 
641
    savext = 0;
 
642
 
 
643
    /* presume we won't find a character map library */
 
644
    charlib = 0;
 
645
 
 
646
    /*
 
647
     *   Scan options 
 
648
     */
 
649
    for (curarg = 1 ; curarg < argc ; ++curarg)
 
650
    {
 
651
        /* if it's not an option, we're done */
 
652
        if (argv[curarg][0] != '-')
 
653
            break;
 
654
 
 
655
        /* check the option */
 
656
        if (stricmp(argv[curarg], "-savext") == 0)
 
657
        {
 
658
            /* the next argument is the saved game extension */
 
659
            ++curarg;
 
660
            if (curarg < argc)
 
661
                savext = argv[curarg];
 
662
        }
 
663
        else if (stricmp(argv[curarg], "-html") == 0)
 
664
        {
 
665
            /* note that we want to use the HTML TADS executable */
 
666
            use_html_exe = TRUE;
 
667
        }
 
668
        else if (stricmp(argv[curarg], "-prot") == 0)
 
669
        {
 
670
            /* note that we want to use the protected-mode run-time */
 
671
            use_prot_exe = TRUE;
 
672
        }
 
673
        else if (stricmp(argv[curarg], "-win32") == 0)
 
674
        {
 
675
            /* note that we want to use the 32-bit Windows run-time */
 
676
            use_win32_exe = TRUE;
 
677
        }
 
678
        else if (stricmp(argv[curarg], "-nologo") == 0)
 
679
        {
 
680
            /* hide the logo */
 
681
            show_logo = FALSE;
 
682
        }
 
683
        else if (stricmp(argv[curarg], "-t3") == 0)
 
684
        {
 
685
            /* use a T3 executable */
 
686
            use_t3_exe = TRUE;
 
687
        }
 
688
        else if (stricmp(argv[curarg], "-clib") == 0)
 
689
        {
 
690
            ++curarg;
 
691
            if (curarg < argc)
 
692
                charlib = argv[curarg];
 
693
        }
 
694
#ifdef MKTRX_SETICON
 
695
        else if (stricmp(argv[curarg], "-icon") == 0)
 
696
        {
 
697
            /* the next argument is the icon file */
 
698
            ++curarg;
 
699
            if (curarg < argc)
 
700
            {
 
701
                /* 
 
702
                 *   copy the name of the icon file and apply the .ICO
 
703
                 *   extension by default 
 
704
                 */
 
705
                strcpy(iconname, argv[curarg]);
 
706
                os_defext(iconname, "ico");
 
707
 
 
708
                /* note that we want to set the icon */
 
709
                set_icon = TRUE;
 
710
            }
 
711
        }
 
712
#endif
 
713
        else if (stricmp(argv[curarg], "-type") == 0)
 
714
        {
 
715
            /* the next argument is the type string */
 
716
            ++curarg;
 
717
            if (curarg < argc && strlen(argv[curarg]) == 4)
 
718
            {
 
719
                /* set the resource type name to this argument */
 
720
                res_type = argv[curarg];
 
721
            }
 
722
            else
 
723
                printf("usage error - "
 
724
                       "type must be exactly four characters\n");
 
725
        }
 
726
        else
 
727
        {
 
728
            /* display the usage message */
 
729
            curarg = argc;
 
730
            break;
 
731
        }
 
732
    }
 
733
 
 
734
    /*
 
735
     *   If there's a config file, get it now, and remove it from the
 
736
     *   arguments so the rest of the argument parser doesn't have to be
 
737
     *   bothered with it (which is important, since the rest of the
 
738
     *   argument parser is positional) 
 
739
     */
 
740
    if (curarg < argc && argv[curarg][0] == '@')
 
741
    {
 
742
        config_file = &argv[curarg][1];
 
743
        ++curarg;
 
744
    }
 
745
    else
 
746
        config_file = 0;
 
747
 
 
748
    /* show the banner, unless we've been asked not to do so */
 
749
    if (show_logo)
 
750
    {
 
751
        printf("maketrx v3.0.0   ");
 
752
        printf("Copyright (c) 1992, 2000 by Michael J. Roberts.\n");
 
753
    }
 
754
 
 
755
    /* 
 
756
     *   check to make sure we have enough arguments remaining - we need
 
757
     *   at least one argument for the game filename 
 
758
     */
 
759
    if (curarg >= argc)
 
760
    {
 
761
        fprintf(stderr,
 
762
                "usage:  maketrx [options] [@config] [source] game [dest]\n"
 
763
                "  config = [optional] configuration file\n"
 
764
                "  source = [optional] TADS interpreter executable\n"
 
765
                "  game   = your compiled .gam or .t3 file\n"
 
766
                "  dest   = [optional] output .exe executable file\n"
 
767
                "\n"
 
768
                "If 'source' is not specified, the program will look for "
 
769
                "the interpreter in\n"
 
770
                "the same directory as MAKETRX.EXE.  If 'dest' is not "
 
771
                "specified, the program\n"
 
772
                "will use the same filename as the 'game' with the "
 
773
                "extension replaced\n"
 
774
                "with .EXE.\n"
 
775
                "\n"
 
776
                "Options:\n"
 
777
                "   -html          Use the HTML TADS 2 run-time (HTMLT2.EXE)\n"
 
778
#ifdef MKTRX_SETICON
 
779
                "   -icon icofile  Use the icon in 'icofile' for the desktop "
 
780
                "icon for the\n"
 
781
                "                  output file (for HTML TADS only)\n"
 
782
#endif
 
783
                "   -prot          Use the 16-bit DOS protected mode run-time"
 
784
                " (TRX.EXE)\n"
 
785
                "   -win32         Use the 32-bit Windows console-mode "
 
786
                "run-time (T2R32.EXE)\n"
 
787
                "   -savext ext    Use 'ext' as the saved game extension "
 
788
                "at run-time\n"
 
789
                "   -t3            Use TADS 3 executables (T3RUN/HTMLT3)\n"
 
790
                "   -clib resfile  Add 'resfile' as a CLIB (T3 character map "
 
791
                "library) section\n"
 
792
                "                  Note: -t3 implies '-clib "
 
793
                "charmap\\cmaplib.t3r' if charmap\\cmaplib.t3r exists\n"
 
794
               );
 
795
        exit(1);
 
796
    }
 
797
 
 
798
    /* 
 
799
     *   check for the run-time executable - if we have three more
 
800
     *   arguments, they've explicitly told us which run-time executable
 
801
     *   to use; otherwise, try to find the default executable 
 
802
     */
 
803
    if (curarg + 2 >= argc)
 
804
    {
 
805
        size_t len;
 
806
        
 
807
        /* 
 
808
         *   the run-time executable was not specified - get the implicit
 
809
         *   TR.EXE location from argv[0] if possible 
 
810
         */
 
811
        strcpy(trxnam, argv0);
 
812
        p = trxnam;
 
813
        if (strlen(p) == 0)
 
814
        {
 
815
            fprintf(stderr,
 
816
                    "error: you must specify the full path to TR.EXE as the\n"
 
817
                    "first argument (run this program again with no arguments"
 
818
                    "\nfor the usage message)\n");
 
819
            exit(1);
 
820
        }
 
821
 
 
822
        /* use first argument as the game */
 
823
        strcpy(gamnam, argv[curarg]);
 
824
 
 
825
        /* if there's a .t3 suffix on the input file, assume -t3 mode */
 
826
        if ((len = strlen(gamnam)) > 3
 
827
            && stricmp(gamnam + len - 3, ".t3") == 0)
 
828
            use_t3_exe = TRUE;
 
829
 
 
830
        /* find the end of the path prefix */
 
831
        for (p += strlen(p) - 1 ; p > trxnam
 
832
             && *p != '/' && *p != '\\' && *p != ':' ; --p);
 
833
        if (*p == '/' || *p == '\\' || *p == ':') ++p;
 
834
 
 
835
        /* copy the executable name */
 
836
        if (use_t3_exe)
 
837
        {
 
838
            /* build using the appropriate T3 executable */
 
839
            if (use_html_exe)
 
840
                strcpy(p, "htmlt3.exe");
 
841
            else
 
842
                strcpy(p, "t3run.exe");
 
843
        }
 
844
        else
 
845
        {
 
846
            /* use the appropriate TADS 2 executable */
 
847
            if (use_html_exe)
 
848
                strcpy(p, "htmlt2.exe");
 
849
            else if (use_prot_exe)
 
850
                strcpy(p, "trx.exe");
 
851
            else if (use_win32_exe)
 
852
                strcpy(p, "t2r32.exe");
 
853
            else
 
854
                strcpy(p, "tr.exe");
 
855
        }
 
856
 
 
857
        /* 
 
858
         *   if there's no directory path prefix at all, and the file we're
 
859
         *   looking for doesn't exist, then we must have an argv[0] that
 
860
         *   found this program on the PATH environment variable; scan the
 
861
         *   PATH for a directory containing the file we're looking for 
 
862
         */
 
863
        if (access(trxnam, 0) && p == trxnam)
 
864
        {
 
865
            char fullname[_MAX_PATH];
 
866
            char *path;
 
867
            
 
868
            /* get the PATH setting */
 
869
            path = getenv("PATH");
 
870
 
 
871
            /* if we found it, scan it */
 
872
            while (path != 0)
 
873
            {
 
874
                size_t len;
 
875
                
 
876
                /* scan for a semicolon */
 
877
                for (p = path ; *p != ';' && *p != '\0' ; ++p) ;
 
878
 
 
879
                /* 
 
880
                 *   get the length of this element, but limit it to our
 
881
                 *   buffer size, leaving space for the filename we need to
 
882
                 *   append (our filenames are all 8.3, so we need at most 12
 
883
                 *   characters including the dot, plus one for the null
 
884
                 *   terminator) 
 
885
                 */
 
886
                len = p - path;
 
887
                if (len > _MAX_PATH - 13)
 
888
                    len = _MAX_PATH - 13;
 
889
 
 
890
                /* check this path element if it's not empty */
 
891
                if (len != 0)
 
892
                {
 
893
                    /* extract the path element */
 
894
                    memcpy(fullname, path, len);
 
895
 
 
896
                    /* add a path separator if there isn't one already */
 
897
                    if (len != 0
 
898
                        && fullname[len-1] != '\\' && fullname[len-1] != '/')
 
899
                        fullname[len++] = '\\';
 
900
                    
 
901
                    /* append the name of the file we're looking for */
 
902
                    strcpy(fullname + len, trxnam);
 
903
                    
 
904
                    /* if this is the one, use this path */
 
905
                    if (!access(fullname, 0))
 
906
                    {
 
907
                        /* use this as the full interpreter path */
 
908
                        strcpy(trxnam, fullname);
 
909
                        
 
910
                        /* no need to look any further */
 
911
                        break;
 
912
                    }
 
913
                }
 
914
                    
 
915
                /* 
 
916
                 *   this isn't the one; if we found a semicolon, move to the
 
917
                 *   next path element, otherwise we're done 
 
918
                 */
 
919
                if (*p == ';')
 
920
                    path = p + 1;
 
921
                else
 
922
                    path = 0;
 
923
            }
 
924
        }
 
925
 
 
926
        /* if no destination is specified, use game (but strip extension) */
 
927
        if (curarg + 1 >= argc)
 
928
        {
 
929
            strcpy(outnam, argv[curarg]);
 
930
            os_remext(outnam);
 
931
        }
 
932
        else
 
933
            strcpy(outnam, argv[curarg + 1]);
 
934
    }
 
935
    else
 
936
    {
 
937
        /* get TR.EXE path from explicit first argument */
 
938
        strcpy(trxnam, argv[curarg]);
 
939
        strcpy(gamnam, argv[curarg + 1]);
 
940
        strcpy(outnam, argv[curarg + 2]);
 
941
        os_defext(trxnam, "exe");
 
942
    }
 
943
 
 
944
    /* apply default extensions */
 
945
    os_defext(trxnam, "exe");
 
946
    os_defext(gamnam, use_t3_exe ? "t3" : "gam");
 
947
    os_defext(outnam, "exe");
 
948
    
 
949
    if (!(fptrx = fopen(trxnam, "rb")))
 
950
    {
 
951
        fprintf(stderr, "unable to open program file \"%s\"\n", trxnam);
 
952
        exit(2);
 
953
    }
 
954
    if (!(fpgam = fopen(gamnam, "rb")))
 
955
    {
 
956
        fprintf(stderr, "unable to open .GAM file \"%s\"\n", gamnam);
 
957
        exit(2);
 
958
    }
 
959
    if (!(fpout = fopen(outnam, "w+b")))
 
960
    {
 
961
        fprintf(stderr, "unable to open output file \"%s\"\n", outnam);
 
962
        exit(2);
 
963
    }
 
964
 
 
965
    /* 
 
966
     *   if we're in t3 mode, and no CLIB file was specified, and we can
 
967
     *   find 'cmaplib.t3r' in the same directory as the original
 
968
     *   interpreter .exe file, add cmaplib.t3r to as the implicit CLIB
 
969
     *   section 
 
970
     */
 
971
    if (use_t3_exe && charlib == 0)
 
972
    {
 
973
        /* build the name of the implicit file */
 
974
        strcpy(charlibname, trxnam);
 
975
        strcpy(os_get_root_name(charlibname), "charmap\\cmaplib.t3r");
 
976
        if (!access(charlibname, 0))
 
977
            charlib = charlibname;
 
978
    }
 
979
    
 
980
    /* Copy the all of TR.EXE original to the output file */
 
981
    for ( ;; )
 
982
    {
 
983
        if (!(siz = fread(buf, 1, sizeof(buf), fptrx))) break;
 
984
        if (fwrite(buf, 1, siz, fpout) != siz)
 
985
        {
 
986
            fprintf(stderr, "error writing output file\n");
 
987
            exit(3);
 
988
        }
 
989
    }
 
990
 
 
991
    /* write the config file, if present */
 
992
    if (config_file)
 
993
    {
 
994
        FILE *fpconfig;
 
995
 
 
996
        /* open the config file */
 
997
        fpconfig = fopen(config_file, "rb");
 
998
        if (!fpconfig)
 
999
        {
 
1000
            fprintf(stderr, "unable to open configuration file \"%s\"\n",
 
1001
                    config_file);
 
1002
            exit(2);
 
1003
        }
 
1004
 
 
1005
        /* copy the config file to the output file */
 
1006
        append_file(fpout, fpconfig, "RCFG", 1);
 
1007
        fclose(fpconfig);
 
1008
    }
 
1009
 
 
1010
    /* add the game file */
 
1011
    append_file(fpout, fpgam, res_type, 0);
 
1012
 
 
1013
    /*
 
1014
     *   If there's a save file extension, add the SAVX resource to
 
1015
     *   specify the extension 
 
1016
     */
 
1017
    if (savext != 0)
 
1018
    {
 
1019
        long startofs;
 
1020
        unsigned short len;
 
1021
 
 
1022
        /* remember the starting location */
 
1023
        startofs = ftell(fpout);
 
1024
 
 
1025
        /* write the header */
 
1026
        write_header(fpout, "SAVX");
 
1027
 
 
1028
        /* write the length of the extension, followed by the extension */
 
1029
        len = strlen(savext);
 
1030
        if (fwrite(&len, sizeof(len), 1, fpout) != 1
 
1031
            || fwrite(savext, len, 1, fpout) != 1)
 
1032
        {
 
1033
            fprintf(stderr, "error writing output file\n");
 
1034
            exit(3);
 
1035
        }
 
1036
 
 
1037
        /* write the trailing header */
 
1038
        write_trailing_header(fpout, startofs, "SAVX");
 
1039
    }
 
1040
 
 
1041
    /* if there's a character library file, add it */
 
1042
    if (charlib != 0)
 
1043
    {
 
1044
        FILE *fpchar;
 
1045
        
 
1046
        /* open the file */
 
1047
        fpchar = fopen(charlib, "rb");
 
1048
 
 
1049
        /* make sure we got it */
 
1050
        if (fpchar != 0)
 
1051
        {
 
1052
            /* append it */
 
1053
            append_file(fpout, fpchar, "CLIB", 0);
 
1054
 
 
1055
            /* done with the file */
 
1056
            fclose(fpchar);
 
1057
        }
 
1058
        else
 
1059
        {
 
1060
            fprintf(stderr, "unable to open character library (CLIB) file "
 
1061
                    "\"%s\"\n", charlib);
 
1062
        }
 
1063
    }
 
1064
 
 
1065
#ifdef MKTRX_SETICON
 
1066
    /*
 
1067
     *   If they want to set the icon, do so now 
 
1068
     */
 
1069
    if (set_icon)
 
1070
    {
 
1071
        FILE   *fpico;
 
1072
        int     err;
 
1073
 
 
1074
        /* open the icon file */
 
1075
        fpico = fopen(iconname, "rb");
 
1076
        if (fpico == 0)
 
1077
        {
 
1078
            /* report the error */
 
1079
            fprintf(stderr, "error opening icon file \"%s\"\n", iconname);
 
1080
        }
 
1081
        else
 
1082
        {
 
1083
            /* 
 
1084
             *   replace the icon - always replace the icon at index zero,
 
1085
             *   since this is the icon used to represent the application
 
1086
             *   on the desktop 
 
1087
             */
 
1088
            err = replace_icon(fpout, 0, fpico);
 
1089
            if (err != 0)
 
1090
            {
 
1091
                if (err < 6)
 
1092
                    fprintf(stderr,
 
1093
                            "error replacing icon: .EXE file is invalid\n");
 
1094
                else if (err <= 106)
 
1095
                    fprintf(stderr,
 
1096
                            "error replacing icon: .EXE file does not "
 
1097
                            "contain any icon resources\n");
 
1098
                else if (err == 107)
 
1099
                    fprintf(stderr,
 
1100
                            "error replacing icon: desktop icon is not "
 
1101
                            "present in .EXE file\n");
 
1102
                else if (err == 110)
 
1103
                    fprintf(stderr,
 
1104
                            "Error replacing icon: one or more icon formats "
 
1105
                            "in this .ICO file are not present\n"
 
1106
                            "in the .EXE file -- "
 
1107
                            "these formats could not be replaced.\n");
 
1108
                else
 
1109
                    fprintf(stderr,
 
1110
                            "error: icon could not be replaced in "
 
1111
                            ".EXE file\n");
 
1112
            }
 
1113
        }
 
1114
    }
 
1115
#endif
 
1116
 
 
1117
    /* close all the files and terminate successfully */
 
1118
    fclose(fptrx);
 
1119
    fclose(fpgam);
 
1120
    fclose(fpout);
 
1121
    exit(0);
 
1122
    return 0;
 
1123
}
 
1124
 
 
1125