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

« back to all changes in this revision

Viewing changes to tads/tads3/resfind.cpp

  • 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) 2001, 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
  resfind.cpp - find a multimedia resource in a tads 2 or tads 3 game file
 
10
Function
 
11
  Searches a compiled tads 2 game file or a compiled tads 3 image file
 
12
  for a multimedia resource of a given name.  The caller doesn't have to
 
13
  know which tads version created the file; we'll sense the file type and
 
14
  parse it accordingly.
 
15
 
 
16
  This implementation is independent of any tads 2 or tads 3 subsystem
 
17
  except osifc, which it uses for portable file I/O and portable byte
 
18
  format conversions.
 
19
Notes
 
20
  
 
21
Modified
 
22
  09/24/01 MJRoberts  - Creation
 
23
*/
 
24
 
 
25
#include <string.h>
 
26
#include <stdlib.h>
 
27
 
 
28
#include <os.h>
 
29
#include "t3std.h"
 
30
#include "resfind.h"
 
31
 
 
32
/* ------------------------------------------------------------------------ */
 
33
/* 
 
34
 *   tads 2 file signature 
 
35
 */
 
36
#define T2_SIG "TADS2 bin\012\015\032"
 
37
 
 
38
/* 
 
39
 *   tads 3 file signature 
 
40
 */
 
41
#define T3_SIG "T3-image\015\012\032"
 
42
 
 
43
 
 
44
 
 
45
/* ------------------------------------------------------------------------ */
 
46
/*
 
47
 *   Find a resource in a tads 2 game file 
 
48
 */
 
49
static int t2_find_res(osfildef *fp, const char *resname,
 
50
                       tads_resinfo *info)
 
51
{
 
52
    char buf[300];
 
53
    unsigned long startpos;
 
54
    size_t resname_len;
 
55
    int found;
 
56
 
 
57
    /* we haven't found what we're looking for yet */
 
58
    found = FALSE;
 
59
 
 
60
    /* note the length of the name we're seeking */
 
61
    resname_len = strlen(resname);
 
62
 
 
63
    /* 
 
64
     *   note the seek location of the start of the tads 2 game file stream
 
65
     *   within the file - if the game file is embedded in a larger file
 
66
     *   stream, the seek locations we find within the file are relative to
 
67
     *   this starting location 
 
68
     */
 
69
    startpos = osfpos(fp);
 
70
 
 
71
    /* 
 
72
     *   skip past the tads 2 file header (13 bytes for the signature, 7
 
73
     *   bytes for the version header, 2 bytes for the flags, 26 bytes for
 
74
     *   the timestamp) 
 
75
     */
 
76
    osfseek(fp, 13 + 7 + 2 + 26, OSFSK_CUR);
 
77
 
 
78
    /* 
 
79
     *   scan the sections in the file; stop on $EOF, and skip everything
 
80
     *   else but HTMLRES, which is the section type that 
 
81
     */
 
82
    for (;;)
 
83
    {
 
84
        unsigned long endofs;
 
85
        
 
86
        /* read the section type and next-section pointer */
 
87
        if (osfrb(fp, buf, 1)
 
88
            || osfrb(fp, buf + 1, (int)((unsigned char)buf[0] + 4)))
 
89
        {
 
90
            /* failed to read it - give up */
 
91
            return FALSE;
 
92
        }
 
93
 
 
94
        /* note the ending position of this section */
 
95
        endofs = t3rp4u(buf + 1 + (unsigned char)buf[0]);
 
96
 
 
97
        /* check the type */
 
98
        if (buf[0] == 7 && memcmp(buf+1, "HTMLRES", 7) == 0)
 
99
        {
 
100
            unsigned long entry_cnt;
 
101
            unsigned long i;
 
102
            
 
103
            /* 
 
104
             *   It's a multimedia resource block.  Read the index table
 
105
             *   header (which contains the number of entries and a reserved
 
106
             *   uint32).  
 
107
             */
 
108
            if (osfrb(fp, buf, 8))
 
109
                return FALSE;
 
110
 
 
111
            /* get the number of entries from the header */
 
112
            entry_cnt = t3rp4u(buf);
 
113
 
 
114
            /* read the entries */
 
115
            for (i = 0 ; i < entry_cnt ; ++i)
 
116
            {
 
117
                unsigned long res_ofs;
 
118
                unsigned long res_siz;
 
119
                unsigned short name_len;
 
120
 
 
121
                /* read the entry header */
 
122
                if (osfrb(fp, buf, 10))
 
123
                    return FALSE;
 
124
 
 
125
                /* parse the header */
 
126
                res_ofs = t3rp4u(buf);
 
127
                res_siz = t3rp4u(buf + 4);
 
128
                name_len = osrp2(buf + 8);
 
129
 
 
130
                /* read the entry's name */
 
131
                if (name_len > sizeof(buf) || osfrb(fp, buf, name_len))
 
132
                    return FALSE;
 
133
 
 
134
                /* 
 
135
                 *   if it matches the name we're looking for, note that we
 
136
                 *   found it 
 
137
                 */
 
138
                if (name_len == resname_len
 
139
                    && memicmp(resname, buf, name_len) == 0)
 
140
                {
 
141
                    /* 
 
142
                     *   note that we found it, and note its resource size
 
143
                     *   and offset in the return structure - but keep
 
144
                     *   scanning the rest of the directory, since we need
 
145
                     *   to know where the directory ends to know where the
 
146
                     *   actual resources begin 
 
147
                     */
 
148
                    found = TRUE;
 
149
                    info->seek_pos = res_ofs;
 
150
                    info->siz = res_siz;
 
151
                }
 
152
            }
 
153
 
 
154
            /* 
 
155
             *   if we found our resource, the current seek position is the
 
156
             *   base of the offset we found in the directory; so fix up the
 
157
             *   offset to give the actual file location 
 
158
             */
 
159
            if (found)
 
160
            {
 
161
                /* fix up the offset with the actual file location */
 
162
                info->seek_pos += osfpos(fp);
 
163
 
 
164
                /* tell the caller we found it */
 
165
                return TRUE;
 
166
            }
 
167
 
 
168
            /* we didn't find it - seek to the end of this section */
 
169
            osfseek(fp, endofs + startpos, OSFSK_SET);
 
170
        }
 
171
        else if (buf[0] == 4 && memcmp(buf+1, "$EOF", 4) == 0)
 
172
        {
 
173
            /* 
 
174
             *   that's the end of the file - we've finished without finding
 
175
             *   the resource, so return failure 
 
176
             */
 
177
            return FALSE;
 
178
        }
 
179
        else
 
180
        {
 
181
            /* 
 
182
             *   this isn't a section we're interested in - skip to the end
 
183
             *   of the section and keep going
 
184
             */
 
185
            osfseek(fp, endofs + startpos, OSFSK_SET);
 
186
        }
 
187
    }
 
188
}
 
189
 
 
190
/* ------------------------------------------------------------------------ */
 
191
/*
 
192
 *   Find a resource in a T3 image file
 
193
 */
 
194
static int t3_find_res(osfildef *fp, const char *resname,
 
195
                       tads_resinfo *info)
 
196
{
 
197
    char buf[256];
 
198
    size_t resname_len;
 
199
    
 
200
    /* note the length of the name we're seeking */
 
201
    resname_len = strlen(resname);
 
202
 
 
203
    /* 
 
204
     *   skip the file header - 11 bytes for the signature, 2 bytes for the
 
205
     *   format version, 32 reserved bytes, and 24 bytes for the timestamp 
 
206
     */
 
207
    osfseek(fp, 11 + 2 + 32 + 24, OSFSK_CUR);
 
208
 
 
209
    /* scan the data blocks */
 
210
    for (;;)
 
211
    {
 
212
        unsigned long siz;
 
213
        
 
214
        /* read the block header */
 
215
        if (osfrb(fp, buf, 10))
 
216
            return FALSE;
 
217
 
 
218
        /* get the block size */
 
219
        siz = t3rp4u(buf + 4);
 
220
 
 
221
        /* check the type */
 
222
        if (memcmp(buf, "MRES", 4) == 0)
 
223
        {
 
224
            unsigned long base_ofs;
 
225
            unsigned int entry_cnt;
 
226
            unsigned int i;
 
227
 
 
228
            /* 
 
229
             *   remember the current seek position - the data seek location
 
230
             *   for each index entry is given as an offset from this
 
231
             *   location 
 
232
             */
 
233
            base_ofs = osfpos(fp);
 
234
 
 
235
            /* read the number of entries */
 
236
            if (osfrb(fp, buf, 2))
 
237
                return FALSE;
 
238
 
 
239
            /* parse the entry count */
 
240
            entry_cnt = osrp2(buf);
 
241
 
 
242
            /* read the entries */
 
243
            for (i = 0 ; i < entry_cnt ; ++i)
 
244
            {
 
245
                unsigned long entry_ofs;
 
246
                unsigned long entry_siz;
 
247
                unsigned int entry_name_len;
 
248
                char *p;
 
249
                size_t rem;
 
250
 
 
251
                /* read this index entry's header */
 
252
                if (osfrb(fp, buf, 9))
 
253
                    return FALSE;
 
254
 
 
255
                /* parse the header */
 
256
                entry_ofs = t3rp4u(buf);
 
257
                entry_siz = t3rp4u(buf + 4);
 
258
                entry_name_len = (unsigned char)buf[8];
 
259
 
 
260
                /* read the entry's name */
 
261
                if (osfrb(fp, buf, entry_name_len))
 
262
                    return FALSE;
 
263
 
 
264
                /* XOR the bytes of the name with 0xFF */
 
265
                for (p = buf, rem = entry_name_len ; rem != 0 ; ++p, --rem)
 
266
                    *p ^= 0xFF;
 
267
 
 
268
                /* if this is the one we're looking for, return it */
 
269
                if (entry_name_len == resname_len
 
270
                    && memicmp(resname, buf, resname_len) == 0)
 
271
                {
 
272
                    /* 
 
273
                     *   fill in the return information - note that the
 
274
                     *   entry offset given in the header is an offset from
 
275
                     *   data block's starting location, so fix this up to
 
276
                     *   an absolute seek location for the return value
 
277
                     */
 
278
                    info->seek_pos = base_ofs + entry_ofs;
 
279
                    info->siz = entry_siz;
 
280
 
 
281
                    /* return success */
 
282
                    return TRUE;
 
283
                }
 
284
            }
 
285
        }
 
286
        else if (memcmp(buf, "EOF ", 4) == 0)
 
287
        {
 
288
            /* 
 
289
             *   end of file - we've finished without finding the resource,
 
290
             *   so return failure 
 
291
             */
 
292
            return FALSE;
 
293
        }
 
294
        else
 
295
        {
 
296
            /* 
 
297
             *   we don't care about anything else - just skip this block
 
298
             *   and keep going; to skip the block, simply seek ahead by the
 
299
             *   size of the block's contents as given in the block header 
 
300
             */
 
301
            osfseek(fp, siz, OSFSK_CUR);
 
302
        }
 
303
    }
 
304
}
 
305
 
 
306
/* ------------------------------------------------------------------------ */
 
307
/*
 
308
 *   Find a multimedia resource with the given name in the given file.  The
 
309
 *   file must be positioned at the start of the tads game file when we're
 
310
 *   invoked - this allows searching for a resource within a game file that
 
311
 *   is embedded in a larger file stream, since we don't care where within
 
312
 *   the osfildef stream the tads game file starts.
 
313
 *   
 
314
 *   Fills in the resource information structure with the seek offset and
 
315
 *   size of the resource in the file and returns true if the resource is
 
316
 *   found; returns false if a resource with the given name doesn't exist in
 
317
 *   the file.  
 
318
 */
 
319
int tads_find_resource_fp(osfildef *fp, const char *resname,
 
320
                          tads_resinfo *info)
 
321
{
 
322
    char buf[12];
 
323
 
 
324
    /* read the signature */
 
325
    if (!osfrb(fp, buf, 12))
 
326
    {
 
327
        /* seek back to the start of the header */
 
328
        osfseek(fp, -12, OSFSK_CUR);
 
329
        
 
330
        /* check which signature we have */
 
331
        if (memcmp(buf, T2_SIG, strlen(T2_SIG)) == 0)
 
332
        {
 
333
            /* it's a tads 2 game file - read it accordingly */
 
334
            return t2_find_res(fp, resname, info);
 
335
        }
 
336
        else if (memcmp(buf, T3_SIG, strlen(T3_SIG)) == 0)
 
337
        {
 
338
            /* it's a t3 image file - read it accordingly */
 
339
            return t3_find_res(fp, resname, info);
 
340
        }
 
341
    }
 
342
 
 
343
    /* 
 
344
     *   if we get here, it means either that we couldn't read the
 
345
     *   signature, or that we didn't recognize the signature - in either
 
346
     *   case, we can't parse the file at all, so we can't find the resource 
 
347
     */
 
348
    return FALSE;
 
349
}
 
350
 
 
351
/*
 
352
 *   Find a resource in a file, given the filename. 
 
353
 *   
 
354
 *   Fills in the resource information structure with the seek offset and
 
355
 *   size of the resource in the file and returns true if the resource is
 
356
 *   found; returns false if a resource with the given name doesn't exist in
 
357
 *   the file.  
 
358
 */
 
359
int tads_find_resource(const char *fname, const char *resname,
 
360
                       tads_resinfo *info)
 
361
{
 
362
    osfildef *fp;
 
363
    int found;
 
364
 
 
365
    /* open the file */
 
366
    if ((fp = osfoprb(fname, OSFTGAME)) == 0
 
367
        && (fp = osfoprb(fname, OSFTT3IMG)) == 0)
 
368
    {
 
369
        /* we couldn't open the file, so there's no resource to be found */
 
370
        return FALSE;
 
371
    }
 
372
 
 
373
    /* find the resource in the file */
 
374
    found = tads_find_resource_fp(fp, resname, info);
 
375
 
 
376
    /* we're done with the file - close it */
 
377
    osfcls(fp);
 
378
 
 
379
    /* return our found or not-found indication */
 
380
    return found;
 
381
}
 
382
 
 
383
/* ------------------------------------------------------------------------ */
 
384
/*
 
385
 *   Testing main - looks for a given resource in a given file, and copies
 
386
 *   it to standard output if found.  
 
387
 */
 
388
#ifdef TEST
 
389
 
 
390
int main(int argc, char **argv)
 
391
{
 
392
    osfildef *fp;
 
393
    const char *fname;
 
394
    const char *resname;
 
395
    tads_resinfo res_info;
 
396
    unsigned long rem;
 
397
    
 
398
    /* check usage */
 
399
    if (argc != 3)
 
400
    {
 
401
        fprintf(stderr, "usage: resfind <filename> <resname>\n");
 
402
        exit(2);
 
403
    }
 
404
 
 
405
    /* get the arguments */
 
406
    fname = argv[1];
 
407
    resname = argv[2];
 
408
 
 
409
    /* open the file */
 
410
    if ((fp = osfoprb(fname, OSFTGAME)) == 0
 
411
        && (fp = osfoprb(fname, OSFTT3IMG)) == 0)
 
412
    {
 
413
        fprintf(stderr, "unable to open file \"%s\"\n", fname);
 
414
        exit(2);
 
415
    }
 
416
 
 
417
    /* find the resource */
 
418
    if (!tads_find_resource_fp(fp, resname, &res_info))
 
419
    {
 
420
        fprintf(stderr, "unable to find resource \"%s\"\n", resname);
 
421
        osfcls(fp);
 
422
        exit(1);
 
423
    }
 
424
 
 
425
    /* seek to the resource */
 
426
    osfseek(fp, res_info.seek_pos, OSFSK_SET);
 
427
 
 
428
    /* copy the resource to standard output */
 
429
    for (rem = res_info.siz ; rem != 0 ; )
 
430
    {
 
431
        char buf[1024];
 
432
        size_t cur;
 
433
 
 
434
        /* 
 
435
         *   read up to the buffer size or up to the resource size
 
436
         *   remaining, whichever is smaller 
 
437
         */
 
438
        cur = sizeof(buf);
 
439
        if (cur > rem)
 
440
            cur = (size_t)rem;
 
441
 
 
442
        /* read the chunk */
 
443
        if (osfrb(fp, buf, cur))
 
444
        {
 
445
            fprintf(stderr, "error reading %u bytes from file\n", cur);
 
446
            osfcls(fp);
 
447
            exit(2);
 
448
        }
 
449
 
 
450
        /* copy the chunk to standard output */
 
451
        fwrite(buf, cur, 1, stdout);
 
452
 
 
453
        /* deduct the amount we just read from the size remaining */
 
454
        rem -= cur;
 
455
    }
 
456
 
 
457
    /* done with the file - close it */
 
458
    osfcls(fp);
 
459
 
 
460
    /* success */
 
461
    exit(0);
 
462
    return 0;
 
463
}
 
464
 
 
465
 
 
466
#endif /* TEST */