cfad47cfa3/t3compiler/tads3/rcmain.cpp

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
#ifdef RCSID
2
static char RCSid[] =
3
"$Header$";
4
#endif
5
6
/* 
7
 *   Copyright (c) 2000, 2002 Michael J. Roberts.  All Rights Reserved.
8
 *   
9
 *   Please see the accompanying license file, LICENSE.TXT, for information
10
 *   on using and copying this software.  
11
 */
12
/*
13
Name
14
  rcmain.cpp - T3 resource compiler main
15
Function
16
  
17
Notes
18
  
19
Modified
20
  01/03/00 MJRoberts  - Creation
21
*/
22
23
24
#include <stdlib.h>
25
#include <string.h>
26
#include <stdio.h>
27
#include <ctype.h>
28
#include <time.h>
29
30
#include "os.h"
31
#include "t3std.h"
32
#include "rcmain.h"
33
#include "vmimage.h"
34
35
36
/* 
37
 *   copy a block of bytes from the input file to the output file 
38
 */
39
static int copy_file_bytes(osfildef *fpin, osfildef *fpout, ulong siz)
40
{
41
    static char copybuf[16 * 1024];
42
    size_t cursiz;
43
44
    /* copy bytes until we run out */
45
    while (siz != 0)
46
    {
47
        /* we can copy up to one full buffer at a time */
48
        cursiz = (siz > sizeof(copybuf) ? sizeof(copybuf) : (size_t)siz);
49
50
        /* deduct the amount we're copying from the total */
51
        siz -= cursiz;
52
53
        /* read from input, copy to output */
54
        if (osfrb(fpin, copybuf, cursiz)
55
            || osfwb(fpout, copybuf, cursiz))
56
            return 1;
57
    }
58
59
    /* success */
60
    return 0;
61
}
62
63
/*
64
 *   Add resources 
65
 */
66
int CResCompMain::add_resources(const char *image_fname,
67
                                const class CRcResList *reslist,
68
                                class CRcHostIfc *hostifc,
69
                                int create_new, os_filetype_t file_type,
70
                                int link_mode)
71
{
72
    osfildef *fp = 0;
73
    osfildef *resfp = 0;
74
    char buf[256 + 256 + 128];
75
    long mres_seek;
76
    long mres_size;
77
    CRcResEntry *entry;
78
    long ofs;
79
    long contents_siz;
80
81
    /* 
82
     *   if the file doesn't exist, and we're not creating a new file,
83
     *   it's an error 
84
     */
85
    if (osfacc(image_fname) && !create_new)
86
    {
87
        /* we can't create a new file - display an error and give up */
88
        disp_error(hostifc, "image file \"%.*s\" does not exist",
89
                   (int)OSFNMAX, image_fname);
90
        goto ret_error;
91
    }
92
93
    /* if we're creating a new file, write the header */
94
    if (create_new)
95
    {
96
        time_t timer;
97
        struct tm *tblock;
98
            
99
        /* create a new image file */
100
        fp = osfopwb(image_fname, file_type);
101
        if (fp == 0)
102
        {
103
            disp_error(hostifc, "can't create file \"%.*s\"",
104
                       (int)OSFNMAX, image_fname);
105
            goto ret_error;
106
        }
107
108
        /* set the version ID in the header */
109
        oswp2(buf, 1);
110
111
        /* set the reserved bytes in the header */
112
        memset(buf + 2, 0, 32);
113
114
        /* set the timestamp in the header */
115
        timer = time(NULL);
116
        tblock = localtime(&timer);
117
        memcpy(buf + 2 + 32, asctime(tblock), 24);
118
119
        /* set up an EOF block */
120
        memcpy(buf + 2 + 32 + 24, "EOF ", 4);
121
        memset(buf + 2 + 32 + 24 + 4, 0, 4);
122
        oswp2(buf + 2 + 32 + 24 + 4 + 4, VMIMAGE_DBF_MANDATORY);
123
124
        /* write the header and the EOF block */
125
        if (osfwb(fp, VMIMAGE_SIG, sizeof(VMIMAGE_SIG)-1)
126
            || osfwb(fp, buf, 2 + 32 + 24 + 10))
127
        {
128
            disp_error(hostifc, "%.*s: error writing new file header",
129
                       (int)OSFNMAX, image_fname);
130
            goto ret_error;
131
        }
132
133
        /* done with the file - close it for now */
134
        osfcls(fp);
135
        fp = 0;
136
    }
137
138
    /* 
139
     *   open the file for reading and writing, since we'll need to read
140
     *   through the current contents then write our new data after the
141
     *   end of the existing file 
142
     */
143
    fp = osfoprwb(image_fname, file_type);
144
    if (fp == 0)
145
    {
146
        /* display an error and give up */
147
        disp_error(hostifc, "can't open file \"%.*s\"",
148
                   (int)OSFNMAX, image_fname);
149
        goto ret_error;
150
    }
151
152
    /* read and verify the header */
153
    if (osfrb(fp, buf, sizeof(VMIMAGE_SIG)-1 + 2 + 32 + 24)
154
        || memcmp(buf, VMIMAGE_SIG, sizeof(VMIMAGE_SIG)-1) != 0)
155
    {
156
        disp_error(hostifc, "%.*s: invalid image file header",
157
                   (int)OSFNMAX, image_fname);
158
        goto ret_error;
159
    }
160
161
    /* read and skip the blocks until we reach the "EOF" block */
162
    for (;;)
163
    {
164
        long block_siz;
165
166
        /* read the next block */
167
        if (osfrb(fp, buf, 10))
168
        {
169
            disp_error(hostifc, "%.*s: unexpected end of file",
170
                       (int)OSFNMAX, image_fname);
171
            goto ret_error;
172
        }
173
174
        /* if it's EOF, we're done */
175
        if (memcmp(buf, "EOF ", 4) == 0)
176
            break;
177
            
178
        /* read the size of this block */
179
        block_siz = t3rp4u(buf + 4);
180
181
        /* skip past this block */
182
        osfseek(fp, block_siz, OSFSK_CUR);
183
    }
184
185
    /* 
186
     *   we've found the EOF block - make sure we're at the end of the
187
     *   file 
188
     */
189
    mres_seek = osfpos(fp);
190
    osfseek(fp, 0, OSFSK_END);
191
    if (mres_seek != osfpos(fp))
192
    {
193
        disp_error(hostifc, "%.*s: extra data after end of file",
194
                   (int)OSFNMAX, image_fname);
195
        goto ret_error;
196
    }
197
198
    /* 
199
     *   seek back to the start of the EOF block, so that we can overwrite
200
     *   it with a new MRES block 
201
     */
202
    mres_seek -= 10;
203
    osfseek(fp, mres_seek, OSFSK_SET);
204
205
    /* 
206
     *   Prepare and write the MRES block header, plus the resource entry
207
     *   count.  If we're in "link mode," write an MREL header instead.  
208
     */
209
    memcpy(buf, link_mode ? "MREL" : "MRES", 4);
210
    memset(buf + 4, 0, 6);
211
    oswp2(buf + 10, reslist->get_count());
212
    if (osfwb(fp, buf, 10 + 2))
213
    {
214
        disp_error(hostifc, "%.*s: error writing resource block header",
215
                   (int)OSFNMAX, image_fname);
216
        goto ret_error;
217
    }
218
219
    /* 
220
     *   First, figure out how much space the table of contents itself
221
     *   will take up.  We need this information so we will know where the
222
     *   first resource's binary data stream begins.  Note that the
223
     *   contents offset starts at 2, since the table entry count (a
224
     *   UINT2) comes before the table's first entry.  
225
     */
226
    for (contents_siz = 2, entry = reslist->get_head() ; entry != 0 ;
227
         entry = entry->get_next())
228
    {
229
        /* 
230
         *   each entry in the table of contents requires a UINT4 (offset
231
         *   of the data), UINT4 (size of the data), UBYTE (name length),
232
         *   and the bytes for the name itself 
233
         */
234
        contents_siz += 4 + 4 + 1 + strlen(entry->get_url());
235
    }
236
237
    /* build the table of contents */
238
    for (ofs = contents_siz, entry = reslist->get_head() ; entry != 0 ;
239
         entry = entry->get_next())
240
    {
241
        long res_size;
242
        size_t url_len;
243
        char *p;
244
        size_t rem;
245
246
        /* if the entry name is too long, it's an error */
247
        url_len = strlen(entry->get_url());
248
        if (url_len > 255)
249
        {
250
            disp_error(hostifc,
251
                       "%.*s: resource name \"%.*s\" for file \"%.*s\" "
252
                       "is too long",
253
                       (int)OSFNMAX, image_fname,
254
                       (int)OSFNMAX, entry->get_url(),
255
                       (int)OSFNMAX, entry->get_fname());
256
            goto ret_error;
257
        }
258
259
        /* 
260
         *   if we're in "link mode", the table of contents entry consists of
261
         *   simply the resource name plus the linked filename - we don't
262
         *   care about the contents of the local file in this case 
263
         */
264
        if (link_mode)
265
        {
266
            size_t flen;
267
            
268
            /* make sure the local filename isn't too long, either */
269
            flen = strlen(entry->get_fname());
270
            if (flen > 255)
271
            {
272
                disp_error(hostifc,
273
                           "%.*s: filename \"%.*s\" for resource link "
274
                           "\"%.*s\" is too long",
275
                           (int)OSFNMAX, image_fname,
276
                           (int)OSFNMAX, entry->get_fname(),
277
                           (int)OSFNMAX, entry->get_url());
278
                goto ret_error;
279
            }
280
281
            /* 
282
             *   build the block: resource name len, resource name, filename
283
             *   len, filenam 
284
             */
285
            buf[0] = (uchar)url_len;
286
            memcpy(buf + 1, entry->get_url(), url_len);
287
            buf[1 + url_len] = (uchar)flen;
288
            memcpy(buf + 1 + url_len + 1, entry->get_fname(), flen);
289
290
            /* write the block */
291
            if (osfwb(fp, buf, 1 + url_len + 1 + flen))
292
            {
293
                disp_error(hostifc, "%.*s: error writing contents entry for "
294
                           "resource link \"%.*s\"",
295
                           (int)OSFNMAX, image_fname,
296
                           (int)OSFNMAX, entry->get_url());
297
                goto ret_error;
298
            }
299
300
            /* that's all for a link mode entry */
301
            continue;
302
        }
303
        
304
        /* open this resource file */
305
        resfp = osfoprb(entry->get_fname(), OSFTBIN);
306
        if (resfp == 0)
307
        {
308
            disp_error(hostifc, "%.*s: cannot open resource file \"%.*s\"",
309
                       (int)OSFNMAX, image_fname,
310
                       (int)OSFNMAX, entry->get_fname());
311
            goto ret_error;
312
        }
313
314
        /* 
315
         *   seek to the end of the resource file so we can determine its
316
         *   size 
317
         */
318
        osfseek(resfp, 0, OSFSK_END);
319
        res_size = osfpos(resfp);
320
321
        /* build this table entry */
322
        oswp4(buf, ofs);
323
        oswp4(buf + 4, res_size);
324
        buf[8] = (char)url_len;
325
        memcpy(buf + 9, entry->get_url(), url_len);
326
327
        /* mask the resource name by xor'ing each byte with 0xff */
328
        for (p = buf + 9, rem = url_len ; rem != 0 ; --rem, ++p)
329
            *p ^= 0xFF;
330
331
        /* write the entry */
332
        if (osfwb(fp, buf, 9 + url_len))
333
        {
334
            disp_error(hostifc, "%.*s: error writing contents entry for "
335
                       "resource \"%.*s\"",
336
                       (int)OSFNMAX, image_fname,
337
                       (int)OSFNMAX, entry->get_url());
338
            goto ret_error;
339
        }
340
341
        /* add the resource's size into the offset so far */
342
        ofs += res_size;
343
344
        /* we're done with this file for now */
345
        osfcls(resfp);
346
        resfp = 0;
347
    }
348
349
    /* now copy the resources themselves, if we're not in link-only mode */
350
    if (!link_mode)
351
    {
352
        /* run through our resource list */
353
        for (entry = reslist->get_head() ; entry != 0 ;
354
             entry = entry->get_next())
355
        {
356
            long res_size;
357
            char msg[OSFNMAX*2 + 20];
358
359
            /* show what we're doing */
360
            sprintf(msg, "+ %.*s (%.*s)",
361
                    (int)OSFNMAX, entry->get_fname(),
362
                    (int)OSFNMAX, entry->get_url());
363
            hostifc->display_status(msg);
364
365
            /* open this resource file */
366
            resfp = osfoprb(entry->get_fname(), OSFTBIN);
367
            if (resfp == 0)
368
            {
369
                disp_error(hostifc,
370
                           "%.*s: cannot open resource file \"%.*s\"",
371
                           (int)OSFNMAX, image_fname,
372
                           (int)OSFNMAX, entry->get_fname());
373
                goto ret_error;
374
            }
375
376
            /* get the size of the file */
377
            osfseek(resfp, 0, OSFSK_END);
378
            res_size = osfpos(resfp);
379
            osfseek(resfp, 0, OSFSK_SET);
380
381
            /* copy the resource file's contents into the image file */
382
            if (copy_file_bytes(resfp, fp, res_size))
383
            {
384
                disp_error(hostifc, "%.*s: error copying resource file \"%.*s\"",
385
                           (int)OSFNMAX, image_fname,
386
                           (int)OSFNMAX, entry->get_fname());
387
                goto ret_error;
388
            }
389
390
            /* we're done with this file for now */
391
            osfcls(resfp);
392
            resfp = 0;
393
        }
394
    }
395
396
    /* 
397
     *   calculate the size of the MRES/MREL data (excluding the 10-byte
398
     *   standard block header) 
399
     */
400
    mres_size = osfpos(fp) - mres_seek - 10;
401
402
    /* go back and fix up the MRES/MREL block header with the block size */
403
    osfseek(fp, mres_seek + 4, OSFSK_SET);
404
    oswp4(buf, mres_size);
405
    if (osfwb(fp, buf, 4))
406
    {
407
        disp_error(hostifc, "%.*s: error writing header size",
408
                   (int)OSFNMAX, image_fname);
409
        goto ret_error;
410
    }
411
412
    /* seek back to the end of the file */
413
    osfseek(fp, 0, OSFSK_END);
414
415
    /* set up an EOF block */
416
    memcpy(buf, "EOF ", 4);
417
    memset(buf + 4, 0, 4);
418
    oswp2(buf + 8, VMIMAGE_DBF_MANDATORY);
419
420
    /* write the EOF block */
421
    if (osfwb(fp, buf, 10))
422
    {
423
        disp_error(hostifc, "%.*s: error writing end-of-file block",
424
                   (int)OSFNMAX, image_fname);
425
        goto ret_error;
426
    }
427
428
    /* done with the file */
429
    osfcls(fp);
430
    fp = 0;
431
432
    /* success */
433
    return 0;
434
435
ret_error:
436
    /* close any files we opened */
437
    if (fp != 0)
438
        osfcls(fp);
439
    if (resfp != 0)
440
        osfcls(resfp);
441
442
    /* return an error indication */
443
    return 1;
444
}
445
446
/*
447
 *   Format and display an error message 
448
 */
449
void CResCompMain::disp_error(class CRcHostIfc *hostifc,
450
                              const char *msg, ...)
451
{
452
    char buf[1024];
453
    va_list argp;
454
    
455
    /* format the message into our buffer */
456
    va_start(argp, msg);
457
    vsprintf(buf, msg, argp);
458
    va_end(argp);
459
460
    /* display the formatted message */
461
    hostifc->display_error(buf);
462
}
463
464
/* ------------------------------------------------------------------------ */
465
/*
466
 *   Add a file or directory to a resource list 
467
 */
468
void CRcResList::add_file(const char *fname, const char *alias,
469
                          int recurse)
470
{
471
    char url[OSFNMAX];
472
    char search_file[OSFNMAX];
473
    int is_dir;
474
    void *search_ctx;
475
    
476
    /* 
477
     *   if no alias was specified, convert the filename to a URL and use
478
     *   that as the resource name; otherwise, use the alias without
479
     *   changes 
480
     */
481
    if (alias == 0)
482
    {
483
        os_cvt_dir_url(url, sizeof(url), fname, FALSE);
484
        alias = url;
485
    }
486
487
    /* 
488
     *   if this is a directory, add one entry for each item in the
489
     *   directory 
490
     */
491
    search_ctx = os_find_first_file("", fname,
492
                                    search_file, sizeof(search_file),
493
                                    &is_dir, 0, 0);
494
    if (search_ctx != 0 && is_dir)
495
    {
496
        char fullname[OSFNMAX];
497
        
498
        /* cancel the search - we only needed the one matching file */
499
        os_find_close(search_ctx);
500
501
        /* 
502
         *   search through the contents of the directory, and add each
503
         *   entry 
504
         */
505
        search_ctx = os_find_first_file(fname, 0,
506
                                        search_file, sizeof(search_file),
507
                                        &is_dir, fullname, sizeof(fullname));
508
        while (search_ctx != 0)
509
        {
510
            char full_url[OSFNMAX];
511
            size_t len;
512
            
513
            /* 
514
             *   build the full alias for this file path -- start with the
515
             *   the alias for the directory itself 
516
             */
517
            len = strlen(alias);
518
            memcpy(full_url, alias, len);
519
520
            /* 
521
             *   add a slash to separate the filename from the directory
522
             *   prefix, if the directory path alias doesn't already end
523
             *   in a slash 
524
             */
525
            if (len != 0 && full_url[len - 1] != '/')
526
                full_url[len++] = '/';
527
528
            /* add this file name */
529
            strcpy(full_url + len, search_file);
530
531
            /* check whether we found a file or directory */
532
            if (is_dir)
533
            {
534
                os_specfile_t spec_type;
535
                
536
                /* check for a special file */
537
                spec_type = os_is_special_file(search_file);
538
                
539
                /* 
540
                 *   It's a directory - if we're allowed to recurse, add
541
                 *   all of the directory's contents; otherwise simply
542
                 *   ignore it.
543
                 */
544
                if (recurse
545
                    && spec_type != OS_SPECFILE_SELF
546
                    && spec_type != OS_SPECFILE_PARENT)
547
                {
548
                    /* add the subdirectory with a recursive call */
549
                    add_file(fullname, full_url, TRUE);
550
                }
551
            }
552
            else
553
            {
554
                /* add a new entry for this file */
555
                add_element(new CRcResEntry(fullname, full_url));
556
            }
557
558
            /* get the next file */
559
            search_ctx = os_find_next_file(search_ctx,
560
                                           search_file, sizeof(search_file),
561
                                           &is_dir,
562
                                           fullname, sizeof(fullname));
563
        }
564
    }
565
    else
566
    {
567
        /* if the search succeeded, close it */
568
        if (search_ctx != 0)
569
            os_find_close(search_ctx);
570
        
571
        /* it's not a directory - simply add an entry for the file */
572
        add_element(new CRcResEntry(fname, alias));
573
    }
574
}
575