"Fossies" - the Fresh Open Source Software Archive 
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
1 /* Copyright (C) 2003-2005 Ghostgum Software Pty Ltd. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under licence and may not be copied,
7 modified or distributed except as expressly authorised under the terms
8 of the licence contained in the file LICENCE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostgum.com.au/ or contact Ghostsgum Software Pty Ltd,
12 218 Gallaghers Rd, Glen Waverley VIC 3150, AUSTRALIA,
13 Fax +61 3 9886 6616.
14 */
15
16 /* $Id: cmac.c,v 1.11 2005/06/10 09:39:24 ghostgum Exp $ */
17 /* Macintosh AppleSingle, AppleDouble and MacBinary file formats */
18 /* Macintosh does not use a flat file system.
19 * Each file can have a data fork and a resource fork.
20 * EPSF files have the PostScript in the data fork,
21 * optionally have a PICT preview in the resource fork.
22 * In addition, finder info gives the file type using FOURCC codes
23 * such as "EPSF" or "PICT".
24 * When files are copied to foreign file systems, the resource
25 * fork may be left behind. Alternatives to retain the resource
26 * fork are to package the finder data, data fork and resource fork
27 * in a single MacBinary or AppleSingle file,
28 * or to put the data fork in a flat file and the finder info
29 * and resource fork in an AppleDouble file.
30 */
31
32 #include "common.h"
33 #include <time.h>
34 #include "cmac.h"
35
36 static int extract_mac_data(GFile *f, LPCTSTR outname,
37 unsigned long begin, unsigned long length, unsigned long header);
38
39 DWORD
40 get_bigendian_dword(const unsigned char *buf)
41 {
42 DWORD dw;
43 dw = ((DWORD)buf[0])<<24;
44 dw += ((DWORD)buf[1])<<16;
45 dw += ((DWORD)buf[2])<<8;
46 dw += (DWORD)buf[3];
47 return dw;
48 }
49
50 WORD
51 get_bigendian_word(const unsigned char *buf)
52 {
53 WORD w;
54 w = (WORD)(buf[0]<<8);
55 w |= (WORD)buf[1];
56 return w;
57 }
58
59 /* write DWORD as DWORD */
60 void
61 put_bigendian_dword(unsigned char *dw, DWORD val)
62 {
63 dw[0] = (unsigned char)((val>>24) & 0xff);
64 dw[1] = (unsigned char)((val>>16) & 0xff);
65 dw[2] = (unsigned char)((val>>8) & 0xff);
66 dw[3] = (unsigned char)( val & 0xff);
67 }
68
69
70 /* write WORD as WORD */
71 void
72 put_bigendian_word(unsigned char *w, WORD val)
73 {
74 w[0] = (unsigned char)((val>>8) & 0xff);
75 w[1] = (unsigned char)( val & 0xff);
76 }
77
78 const unsigned char apple_single_magic[4] = {0x00, 0x05, 0x16, 0x00};
79 const unsigned char apple_double_magic[4] = {0x00, 0x05, 0x16, 0x07};
80
81 CMACFILE *get_mactype(GFile *f)
82 {
83 #define ASD_HEADER_LENGTH 26
84 #define MACBIN_HEADER_LENGTH 128
85 unsigned char data[MACBIN_HEADER_LENGTH];
86 CMAC_TYPE type = CMAC_TYPE_NONE;
87 CMACFILE *mac;
88 int i;
89 int asd_entries = 0;
90 DWORD version;
91 DWORD EntryID;
92 DWORD Offset;
93 DWORD Length;
94 FILE_POS file_length;
95 int count;
96
97 if (f == NULL)
98 return NULL;
99 file_length = gfile_get_length(f);
100 count = gfile_read(f, data, ASD_HEADER_LENGTH);
101 if (count >= ASD_HEADER_LENGTH) {
102 if (memcmp(data, apple_single_magic, 4) == 0)
103 type = CMAC_TYPE_SINGLE;
104 else if (memcmp(data, apple_double_magic, 4) == 0)
105 type = CMAC_TYPE_DOUBLE;
106 if ((type == CMAC_TYPE_SINGLE || type == CMAC_TYPE_DOUBLE)) {
107 version = get_bigendian_dword(data+4);
108 if ((version != 0x00010000) && (version != 0x00020000))
109 type = CMAC_TYPE_NONE;
110 asd_entries = get_bigendian_word(data+24);
111 }
112 else if (type == CMAC_TYPE_NONE) {
113 count += gfile_read(f, data+ASD_HEADER_LENGTH,
114 MACBIN_HEADER_LENGTH-ASD_HEADER_LENGTH);
115 if (count >= MACBIN_HEADER_LENGTH &&
116 (data[0]==0x0) &&
117 (data[1] >= 1) && (data[1] <= 63) &&
118 (data[74]==0x0) && (data[82]==0x0) &&
119 (data[65]>=' ') && (data[65]<='z') &&
120 (data[66]>=' ') && (data[66]<='z') &&
121 (data[67]>=' ') && (data[67]<='z') &&
122 (data[68]>=' ') && (data[68]<='z') &&
123 (data[69]>=' ') && (data[69]<='z') &&
124 (data[70]>=' ') && (data[70]<='z') &&
125 (data[71]>=' ') && (data[71]<='z') &&
126 (data[72]>=' ') && (data[72]<='z')) {
127 type = CMAC_TYPE_MACBIN;
128 }
129 else {
130 /* check for bare resource fork */
131 DWORD data_begin = get_bigendian_dword(data);
132 DWORD map_begin = get_bigendian_dword(data+4);
133 DWORD data_length = get_bigendian_dword(data+8);
134 DWORD map_length = get_bigendian_dword(data+12);
135 if ((data_begin == 0x100) &&
136 (data_begin + data_length == map_begin) &&
137 (map_begin + map_length == file_length))
138 type = CMAC_TYPE_RSRC;
139 }
140 }
141 }
142
143 if (type == CMAC_TYPE_NONE)
144 return NULL;
145
146 mac = (CMACFILE *)malloc(sizeof(CMACFILE));
147 if (mac == NULL)
148 return NULL;
149 memset(mac, 0, sizeof(CMACFILE));
150 mac->type = type;
151
152 /* Read Mac Binary stuff */
153 if (type == CMAC_TYPE_MACBIN) {
154 memcpy(mac->file_type, data+65, 4);
155 memcpy(mac->file_creator, data+69, 4);
156 mac->data_begin = 128;
157 mac->data_length = get_bigendian_dword(data+83);
158 mac->resource_begin =
159 (mac->data_begin + mac->data_length + 127 ) & ~127;
160 mac->resource_length = get_bigendian_dword(data+87);
161 }
162 else if (type == CMAC_TYPE_RSRC) {
163 memcpy(mac->file_type, " ", 4);
164 memcpy(mac->file_creator, " ", 4);
165 mac->resource_begin = 0;
166 mac->resource_length = file_length;
167 }
168 else {
169 /* AppleSingle or AppleDouble */
170 for (i=0; i<asd_entries; i++) {
171 count = gfile_read(f, data, 12);
172 EntryID = get_bigendian_dword(data);
173 Offset = get_bigendian_dword(data+4);
174 Length = get_bigendian_dword(data+8);
175 switch (EntryID) {
176 case 1: /* Data fork */
177 mac->data_begin = Offset;
178 mac->data_length = Length;
179 break;
180 case 2: /* Resource fork */
181 mac->resource_begin = Offset;
182 mac->resource_length = Length;
183 break;
184 case 9: /* Finder info */
185 mac->finder_begin = Offset;
186 mac->finder_length = Length;
187 break;
188 }
189 }
190 if (mac->finder_begin != 0) {
191 gfile_seek(f, mac->finder_begin, SEEK_SET);
192 count = gfile_read(f, data, min(sizeof(data), mac->finder_length));
193 if (count >= 8) {
194 memcpy(mac->file_type, data, 4);
195 memcpy(mac->file_creator, data+4, 4);
196 }
197 }
198 }
199
200 return mac;
201 }
202
203
204 typedef struct CMAC_RESOURCE_HEADER_s {
205 DWORD data_begin;
206 DWORD map_begin;
207 DWORD data_length;
208 DWORD map_length;
209 } CMAC_RESOURCE_HEADER;
210
211 typedef struct CMAC_RESOURCE_MAP_s {
212 unsigned char reshdr[16];
213 DWORD reshdl;
214 WORD filerefno;
215 WORD attributes;
216 WORD offset_type_list;
217 WORD offset_name_list;
218 WORD type_count;
219 } CMAC_RESOURCE_MAP;
220
221 typedef struct CMAC_RESOURCE_TYPE_LIST_s {
222 unsigned char type[4];
223 WORD count;
224 WORD offset_ref_list;
225 } CMAC_RESOURCE_TYPE_LIST;
226
227 typedef struct CMAC_RESOURCE_REF_LIST_s {
228 WORD id;
229 WORD offset_name;
230 unsigned char attributes;
231 DWORD offset_data; /* 3 bytes only */
232 DWORD handle;
233 } CMAC_RESOURCE_REF_LIST;
234
235
236 /* Find the location of PICT in the resource fork, if present */
237 int
238 get_pict(GFile *f, CMACFILE *mac, int debug)
239 {
240 CMAC_RESOURCE_HEADER reshdr;
241 CMAC_RESOURCE_MAP resmap;
242 CMAC_RESOURCE_TYPE_LIST typelist;
243 CMAC_RESOURCE_REF_LIST reflist;
244 DWORD res_offset;
245 DWORD map_offset;
246 DWORD type_offset;
247 DWORD ref_offset;
248 DWORD preview_offset = 0;
249 DWORD preview_length = 0;
250 unsigned char data[16];
251 char name[257];
252 int name_length;
253 int count;
254 int i, j;
255 if (mac == NULL)
256 return 1;
257 if (mac->type == CMAC_TYPE_NONE)
258 return 1;
259 if (((mac->type != CMAC_TYPE_RSRC) && (mac->resource_begin == 0))
260 || (mac->resource_length == 0))
261 return 1;
262
263 memset(&resmap, 0, sizeof(resmap));
264 memset(&typelist, 0, sizeof(typelist));
265 memset(&reflist, 0, sizeof(reflist));
266
267 res_offset = mac->resource_begin;
268 gfile_seek(f, res_offset, SEEK_SET);
269 count = gfile_read(f, data, 16);
270 if (count != 16)
271 return -1;
272 reshdr.data_begin = get_bigendian_dword(data);
273 reshdr.map_begin = get_bigendian_dword(data+4);
274 reshdr.data_length = get_bigendian_dword(data+8);
275 reshdr.map_length = get_bigendian_dword(data+12);
276 if (debug) {
277 fprintf(stdout, "resource data: %ld %ld\n",
278 reshdr.data_begin, reshdr.data_length);
279 fprintf(stdout, "resource map: %ld %ld\n",
280 reshdr.map_begin, reshdr.map_length);
281 }
282
283 map_offset = res_offset+reshdr.map_begin;
284 gfile_seek(f, map_offset, SEEK_SET);
285 count = gfile_read(f, &resmap.reshdr, 16);
286 if (count != 16)
287 return -1;
288 count = gfile_read(f, data, 14);
289 if (count != 14)
290 return -1;
291 resmap.reshdl = get_bigendian_dword(data);
292 resmap.filerefno = get_bigendian_word(data+4);
293 resmap.attributes = get_bigendian_word(data+6);
294 resmap.offset_type_list = get_bigendian_word(data+8);
295 resmap.offset_name_list = get_bigendian_word(data+10);
296 resmap.type_count = get_bigendian_word(data+12);
297
298 if (debug) {
299 fprintf(stdout, " resource handle %ld\n", resmap.reshdl);
300 fprintf(stdout, " file reference number %d\n",
301 resmap.filerefno);
302 fprintf(stdout, " attributes 0x%x\n", resmap.attributes);
303 fprintf(stdout, " offset type list %d\n", resmap.offset_type_list);
304 fprintf(stdout, " offset name list %d\n", resmap.offset_name_list);
305 fprintf(stdout, " type count %d\n", resmap.type_count);
306 }
307
308 /* Documentation says that the type list starts at
309 * map_offset + resmap.offset_type_list, but we have
310 * found that it is actually 2 bytes further on.
311 * Perhaps the type count is supposed be part of the type_list
312 */
313 type_offset = map_offset+resmap.offset_type_list;
314 for (i=0; i<=resmap.type_count; i++) {
315 gfile_seek(f, type_offset + 2 + i * 8, SEEK_SET); /* +2 KLUDGE */
316 count = gfile_read(f, &typelist.type, 4);
317 if (count != 4)
318 return -1;
319 count = gfile_read(f, data, 4);
320 if (count != 4)
321 return -1;
322 typelist.count = get_bigendian_word(data);
323 typelist.offset_ref_list = get_bigendian_word(data+2);
324 if (debug)
325 fprintf(stdout, "type %d %c%c%c%c count=%d offset=%d\n", i,
326 typelist.type[0], typelist.type[1],
327 typelist.type[2], typelist.type[3],
328 typelist.count, typelist.offset_ref_list);
329 ref_offset = type_offset + typelist.offset_ref_list;
330 for (j=0; j<=typelist.count; j++) {
331 gfile_seek(f, ref_offset + j * 12, SEEK_SET);
332 count = gfile_read(f, data, 12);
333 if (count != 12)
334 return -1;
335 reflist.id = get_bigendian_word(data);
336 reflist.offset_name = get_bigendian_word(data+2);
337 reflist.attributes = data[4];
338 reflist.offset_data = ((DWORD)data[5])<<16;
339 reflist.offset_data += ((DWORD)data[6])<<8;
340 reflist.offset_data += ((DWORD)data[7]);
341 reflist.handle = get_bigendian_dword(data+8);
342 if (debug) {
343 fprintf(stdout, " reflist %d id=%d name=%d attributes=0x%x data=%ld 0x%lx\n",
344 j, reflist.id, reflist.offset_name, reflist.attributes,
345 reflist.offset_data, reflist.offset_data);
346 gfile_seek(f, res_offset + reshdr.data_begin +
347 reflist.offset_data, SEEK_SET);
348 count = gfile_read(f, data, 4);
349 if (count != 4)
350 return -1;
351 fprintf(stdout, " length=%ld 0x%lx\n",
352 get_bigendian_dword(data), get_bigendian_dword(data));
353 }
354
355
356 if ((resmap.offset_name_list < reshdr.map_length) &&
357 (resmap.offset_name_list != 0xffff) &&
358 (reflist.offset_name != 0xffff)) {
359 gfile_seek(f, map_offset + resmap.offset_name_list +
360 reflist.offset_name, SEEK_SET);
361 count = gfile_read(f, data, 1);
362 if (count != 1)
363 return -1;
364 name_length = data[0];
365 if (name_length <= 256) {
366 count = gfile_read(f, name, name_length);
367 if (count != name_length)
368 return -1;
369 name[name_length] = '\0';
370 if (debug)
371 fprintf(stdout, " name=%s\n", name);
372 }
373 }
374 if ((memcmp(typelist.type, "PICT", 4) == 0) &&
375 (reflist.id == 256)) {
376 /* This is the PICT preview for an EPS files */
377 preview_offset =
378 res_offset + reshdr.data_begin + reflist.offset_data;
379 }
380 }
381 }
382
383 if (preview_offset != 0) {
384 gfile_seek(f, preview_offset, SEEK_SET);
385 gfile_read(f, data, 4);
386 preview_length = get_bigendian_dword(data);
387 if (preview_length != 0) {
388 mac->pict_begin = preview_offset + 4;
389 mac->pict_length = preview_length;
390 }
391 }
392 return 0;
393 }
394
395
396 /* Extract EPSF from data resource fork to a file */
397 /* Returns 0 on success, negative on failure */
398 static int
399 extract_mac_data(GFile *f, LPCTSTR outname,
400 unsigned long begin, unsigned long length, unsigned long header)
401 {
402 unsigned long len;
403 unsigned int count;
404 char *buffer;
405 GFile *outfile;
406 int code = 0;
407 if ((begin == 0) || (length == 0))
408 return -1;
409
410 if (*outname == '\0')
411 return -1;
412
413 /* create buffer for file copy */
414 buffer = (char *)malloc(COPY_BUF_SIZE);
415 if (buffer == (char *)NULL)
416 return -1;
417
418 outfile = gfile_open(outname, gfile_modeWrite | gfile_modeCreate);
419 if (outfile == (GFile *)NULL) {
420 free(buffer);
421 return -1;
422 }
423
424 /* PICT files when stored separately start with 512 nulls */
425 memset(buffer, 0, COPY_BUF_SIZE);
426 if (header && header < COPY_BUF_SIZE)
427 gfile_write(outfile, buffer, header);
428
429 gfile_seek(f, begin, gfile_begin); /* seek to EPSF or PICT */
430 len = length;
431 while ( (count = (unsigned int)min(len,COPY_BUF_SIZE)) != 0 ) {
432 count = (int)gfile_read(f, buffer, count);
433 gfile_write(outfile, buffer, count);
434 if (count == 0) {
435 len = 0;
436 code = -1;
437 }
438 else
439 len -= count;
440 }
441 free(buffer);
442 gfile_close(outfile);
443
444 return code;
445 }
446
447 /* Extract PICT from resource fork to a file */
448 /* Returns 0 on success, negative on failure */
449 int
450 extract_mac_pict(GFile *f, CMACFILE *mac, LPCTSTR outname)
451 {
452 if ((f == NULL) || (mac == NULL))
453 return -1;
454 /* PICT files when stored separately start with 512 nulls */
455 return extract_mac_data(f, outname,
456 mac->pict_begin, mac->pict_length, 512);
457 }
458
459 /* Extract EPSF from data fork to a file */
460 /* Returns 0 on success, negative on failure */
461 int
462 extract_mac_epsf(GFile *f, CMACFILE *mac, LPCTSTR outname)
463 {
464 if ((f == NULL) || (mac == NULL))
465 return -1;
466 return extract_mac_data(f, outname,
467 mac->data_begin, mac->data_length, 0);
468 }
469
470 /* Write resources containing a single PICT with id=256 to file.
471 * Return number of bytes written if OK, -ve if not OK.
472 * If f==NULL, return number of bytes required for resources.
473 */
474 int
475 write_resource_pict(GFile *f, LPCTSTR pictname)
476 {
477 GFile *pictfile;
478 unsigned long pict_length;
479 unsigned char data[256];
480 unsigned long data_offset = 256;
481 unsigned long map_length = 58;
482 unsigned long pict_offset = 0; /* at start of resource data */
483 unsigned resource_length;
484 unsigned long len;
485 unsigned int count;
486 int code = 0;
487
488 pictfile = gfile_open(pictname, gfile_modeRead);
489 if (pictfile == NULL)
490 return -1;
491 pict_length = gfile_get_length(pictfile) - 512;
492 if ((long)pict_length < 0) {
493 gfile_close(pictfile);
494 return -1;
495 }
496 resource_length = data_offset + 4 + pict_length + map_length;
497 if (f == NULL) {
498 gfile_close(pictfile);
499 return resource_length;
500 }
501
502 /* resource header */
503 memset(data, 0, sizeof(data));
504 put_bigendian_dword(data, data_offset); /* data offset */
505 put_bigendian_dword(data+4, data_offset + 4 + pict_length); /* map offset */
506 put_bigendian_dword(data+8, pict_length + 4); /* data length */
507 put_bigendian_dword(data+12, map_length); /* map length */
508 gfile_write(f, data, data_offset);
509
510 /* pict file */
511 put_bigendian_dword(data, pict_length);
512 gfile_write(f, data, 4);
513 len = pict_length;
514 gfile_seek(pictfile, 512, SEEK_SET);
515 while ( (count = (unsigned int)min(len,sizeof(data))) != 0 ) {
516 count = (int)gfile_read(pictfile, data, count);
517 gfile_write(f, data, count);
518 if (count == 0) {
519 len = 0;
520 code = -1;
521 }
522 else
523 len -= count;
524 }
525 gfile_close(pictfile);
526 if (code < 0)
527 return code;
528
529 /* resource map */
530 memset(data, 0, sizeof(data));
531 put_bigendian_dword(data+16, 0); /* resource handle */
532 put_bigendian_word(data+20, 0); /* file reference number */
533 put_bigendian_word(data+22, 0); /* attributes */
534 put_bigendian_word(data+24, 28); /* offset to type list */
535 put_bigendian_word(data+26, 50); /* offset to name list */
536 gfile_write(f, data, 28);
537
538 /* type list */
539 memset(data, 0, sizeof(data));
540 put_bigendian_word(data, 0); /* number of types */
541 memcpy(data+2, "PICT", 4);
542 put_bigendian_word(data+6, 0); /* type count */
543 put_bigendian_word(data+8, 10); /* offset to ref list */
544 gfile_write(f, data, 10);
545
546 /* reference list */
547 memset(data, 0, sizeof(data));
548 put_bigendian_word(data, 256); /* resource id for EPSF preview */
549 put_bigendian_word(data+2, 0); /* offset to name */
550 data[4] = '\0'; /* attributes */
551 data[5] = (unsigned char)((pict_offset>>16) & 0xff);
552 data[6] = (unsigned char)((pict_offset>>8) & 0xff);
553 data[7] = (unsigned char)( pict_offset & 0xff);
554 put_bigendian_dword(data+8, 0); /* handle */
555 gfile_write(f, data, 12);
556
557 /* name list */
558 memset(data, 0, sizeof(data));
559 data[0] = 7;
560 memcpy(data+1, "Preview", 7);
561 gfile_write(f, data, 8);
562
563 return resource_length;
564 }
565
566 /* Write an AppleDouble file containing a single PICT with id=256 to file. */
567 int
568 write_appledouble(GFile *f, LPCTSTR pictname)
569 {
570 unsigned char data[256];
571 unsigned long resource_length;
572
573 /* get length of resources */
574 resource_length = write_resource_pict(NULL, pictname);
575
576 memset(data, 0, sizeof(data));
577 memcpy(data, apple_double_magic, 4); /* magic signature */
578 put_bigendian_dword(data+4, 0x00020000); /* version */
579 /* 16 bytes filler */
580 put_bigendian_word(data+24, 2); /* 2 entries, finder and resource */
581 /* finder descriptor */
582 put_bigendian_dword(data+26, 9); /* finder id */
583 put_bigendian_dword(data+30, 50); /* offset */
584 put_bigendian_dword(data+34, 32); /* length */
585 /* resource descriptor */
586 put_bigendian_dword(data+38, 2); /* resource fork id */
587 put_bigendian_dword(data+42, 82); /* offset */
588 put_bigendian_dword(data+46, resource_length); /* length */
589 /* finder info */
590 memcpy(data+50, "EPSF", 4); /* file type */
591 memcpy(data+54, "MSWD", 4); /* file creator */
592 data[58] = 1; /* ??? need to check finder info */
593 gfile_write(f, data, 82);
594
595 /* Now copy the resource fork */
596 if (write_resource_pict(f, pictname) <= 0)
597 return -1;
598 return 0;
599 }
600
601 /* Write an AppleSingle file with a data fork containing EPSF
602 * and a resource fork containing a preview as a single PICT
603 * with id=256.
604 */
605 int
606 write_applesingle(GFile *f, LPCTSTR epsname, LPCTSTR pictname)
607 {
608 unsigned long resource_length;
609 unsigned long data_length;
610 unsigned char data[256];
611 unsigned char *buffer;
612 GFile *epsfile;
613 unsigned long len;
614 unsigned int count;
615 int code = 0;
616
617 buffer = (unsigned char *)malloc(COPY_BUF_SIZE);
618 if (buffer == (unsigned char *)NULL)
619 return -1;
620
621 /* get length of data and resources */
622 epsfile = gfile_open(epsname, gfile_modeRead);
623 if (epsname == NULL) {
624 free(buffer);
625 return -1;
626 }
627 data_length = gfile_get_length(epsfile);
628 resource_length = write_resource_pict(NULL, pictname);
629
630 memset(data, 0, sizeof(data));
631 memcpy(data, apple_single_magic, 4); /* magic signature */
632 put_bigendian_dword(data+4, 0x00020000); /* version */
633 /* 16 bytes filler */
634 put_bigendian_word(data+24, 3); /* 3 entries, finder, data and resource */
635 /* finder descriptor */
636 put_bigendian_dword(data+26, 9); /* finder id */
637 put_bigendian_dword(data+30, 62); /* offset */
638 put_bigendian_dword(data+34, 32); /* length */
639 /* data descriptor */
640 put_bigendian_dword(data+38, 1); /* data fork id */
641 put_bigendian_dword(data+42, 94); /* offset */
642 put_bigendian_dword(data+46, data_length); /* length */
643 /* resource descriptor */
644 put_bigendian_dword(data+50, 2); /* resource fork id */
645 put_bigendian_dword(data+54, 94+data_length); /* offset */
646 put_bigendian_dword(data+58, resource_length); /* length */
647 /* finder info */
648 memcpy(data+62, "EPSF", 4); /* file type */
649 memcpy(data+66, "MSWD", 4); /* file creator */
650 data[70] = 1; /* ??? need to check finder info */
651 gfile_write(f, data, 94);
652
653 /* Copy data fork */
654 len = data_length;
655 while ( (count = (unsigned int)min(len,COPY_BUF_SIZE)) != 0 ) {
656 count = (int)gfile_read(epsfile, buffer, count);
657 gfile_write(f, buffer, count);
658 if (count == 0) {
659 len = 0;
660 code = -1;
661 }
662 else
663 len -= count;
664 }
665 gfile_close(epsfile);
666 free(buffer);
667 if (code < 0)
668 return code;
669
670 /* Now copy the resource fork */
671 if (write_resource_pict(f, pictname) <= 0)
672 return -1;
673 return 0;
674 }
675
676
677 /* Write a MacBinary file with a data fork containing EPSF
678 * and a resource fork containing a preview as a single PICT
679 * with id=256.
680 */
681 int
682 write_macbin(GFile *f, const char *name, LPCTSTR epsname, LPCTSTR pictname)
683 {
684 unsigned char *buffer;
685 unsigned char data[128];
686 const char *macname;
687 int macname_length;
688 unsigned long data_length;
689 unsigned long resource_length;
690 unsigned long len;
691 unsigned int count;
692 int code = 0;
693 GFile *epsfile;
694 time_t now = time(NULL);
695 now += 2082844800LU; /* convert from Unix to Mac time */
696 macname = name;
697 if (name[0] == '\0') /* we are writing to stdout */
698 macname = "Unknown";
699
700 buffer = (unsigned char *)malloc(COPY_BUF_SIZE);
701 if (buffer == (unsigned char *)NULL)
702 return -1;
703
704 /* get length of data and resources */
705 epsfile = gfile_open(epsname, gfile_modeRead);
706 if (epsname == NULL) {
707 free(buffer);
708 return -1;
709 }
710 data_length = gfile_get_length(epsfile);
711 resource_length = write_resource_pict(NULL, pictname);
712
713 /* MacBinary I header */
714 memset(data, 0, sizeof(data));
715 data[0] = 0; /* version */
716 macname_length = min(63, (int)strlen(macname));
717 data[1] = (unsigned char)macname_length;
718 memcpy(data+2, macname, macname_length);
719 memcpy(data+65, "EPSF", 4); /* file type */
720 memcpy(data+69, "MSWD", 4); /* file creator */
721 data[73] = 1; /* finder flags */
722 put_bigendian_dword(data+83, data_length);
723 put_bigendian_dword(data+87, resource_length);
724 put_bigendian_dword(data+91, (DWORD)now); /* creation date */
725 put_bigendian_dword(data+95, (DWORD)now); /* last modified date */
726 gfile_write(f, data, 128);
727
728 /* copy data fork */
729 len = data_length;
730 while ( (count = (unsigned int)min(len,COPY_BUF_SIZE)) != 0 ) {
731 count = (int)gfile_read(epsfile, buffer, count);
732 gfile_write(f, buffer, count);
733 if (count == 0) {
734 len = 0;
735 code = -1;
736 }
737 else
738 len -= count;
739 }
740 gfile_close(epsfile);
741 free(buffer);
742 if (code < 0)
743 return code;
744
745 /* Pad to 128 byte boundary */
746 memset(data, 0, sizeof(data));
747 count = data_length & 127;
748 if (count)
749 gfile_write(f, data, 128-count);
750
751 /* Now copy the resource fork */
752 if (write_resource_pict(f, pictname) <= 0)
753 return -1;
754
755 /* Pad to 128 byte boundary */
756 count = resource_length & 127;
757 if (count)
758 gfile_write(f, data, 128-count);
759
760 return 0;
761 }
762
763
764 /* Returns -1 for error, 0 for Mac and 1 for non-Mac */
765 /* If Macintosh format and verbose, print some details to stdout */
766 int dump_macfile(LPCTSTR filename, int verbose)
767 {
768 CMACFILE *mac;
769 const char *p;
770 GFile *f = gfile_open(filename, gfile_modeRead);
771 if (f == NULL)
772 return -1;
773 mac = get_mactype(f);
774 if (mac)
775 get_pict(f, mac, (verbose > 1));
776
777 gfile_close(f);
778
779 if (mac == NULL)
780 return 1;
781 if (verbose) {
782 switch (mac->type) {
783 case CMAC_TYPE_SINGLE:
784 p = "AppleSingle";
785 break;
786 case CMAC_TYPE_DOUBLE:
787 p = "AppleDouble";
788 break;
789 case CMAC_TYPE_MACBIN:
790 p = "MacBinary";
791 break;
792 case CMAC_TYPE_RSRC:
793 p = "Resource";
794 break;
795 default:
796 p = "Unknown";
797 }
798 fprintf(stdout, "Macintosh Binary Format: %s\n", p);
799 fprintf(stdout, " File Type: %c%c%c%c\n",
800 mac->file_type[0], mac->file_type[1],
801 mac->file_type[2], mac->file_type[3]);
802 fprintf(stdout, " File Creator: %c%c%c%c\n",
803 mac->file_creator[0], mac->file_creator[1],
804 mac->file_creator[2], mac->file_creator[3]);
805 fprintf(stdout, " Finder Info: %ld %ld\n",
806 mac->finder_begin, mac->finder_length);
807 fprintf(stdout, " Data Fork: %ld %ld\n",
808 mac->data_begin, mac->data_length);
809 fprintf(stdout, " Resource Fork: %ld %ld\n",
810 mac->resource_begin, mac->resource_length);
811 fprintf(stdout, " PICT: %ld %ld, 0x%lx 0x%lx\n",
812 mac->pict_begin, mac->pict_length,
813 mac->pict_begin, mac->pict_length);
814 }
815 free(mac);
816 return 0;
817 }
818
819 #ifdef STANDALONE
820 /* To compile standalone for Windows,
821 * cl -D_Windows -D__WIN32__ -DSTANDALONE -Isrc -Isrcwin
822 * src/cmac.c obj/wfile.obj obj/calloc.obj
823 */
824
825
826 int debug;
827
828
829 void usage(void)
830 {
831 fprintf(stdout, "Usage:\n\
832 File info: cmac -i input.eps\n\
833 Extract data fork: cmac -d input output\n\
834 Extract PICT: cmac -x input.eps output.pict\n\
835 Write AppleSingle: cmac -1 input.eps input.pict output.eps\n\
836 Write AppleDouble: cmac -2 input.pict ._output.eps\n\
837 Write MacBinary: cmac -b input.eps input.pict output.eps\n\
838 ");
839 }
840
841 int main(int argc, char *argv[])
842 {
843 GFile *f;
844 CMACFILE *mac;
845 int code = 0;
846 if (argc < 2) {
847 usage();
848 return 1;
849 }
850
851 if (argv[1][0] != '-') {
852 usage();
853 return 1;
854 }
855 switch (argv[1][1]) {
856 case 'i':
857 if (argc != 3) {
858 usage();
859 return 1;
860 }
861 if (dump_macfile(argv[2], 2) == 1)
862 fprintf(stdout, "Not a Macintosh Resource, MacBinary, AppleSingle or AppleDouble file";
863 break;
864 case 'd':
865 /* Extract data fork */
866 if (argc != 4) {
867 usage();
868 return 1;
869 }
870 f = gfile_open(argv[2], gfile_modeRead);
871 if (f == NULL) {
872 fprintf(stdout, "Failed to open file \042%s\042\n", argv[1]);
873 return -1;
874 }
875 mac = get_mactype(f);
876 if (mac == NULL) {
877 fprintf(stdout, "Not a Mac file with resource fork\n");
878 code = 1;
879 }
880 if (code == 0) {
881 code = extract_mac_data(f, argv[3],
882 mac->data_begin, mac->data_length, 0);
883 if (code)
884 fprintf(stdout, "Failed to extract data fork.\n");
885 else
886 fprintf(stdout, "Success\n");
887 }
888 gfile_close(f);
889 break;
890 case 'x':
891 /* Extract PICT preview */
892 if (argc != 4) {
893 usage();
894 return 1;
895 }
896 f = gfile_open(argv[2], gfile_modeRead);
897 if (f == NULL) {
898 fprintf(stdout, "Failed to open file \042%s\042\n", argv[1]);
899 return -1;
900 }
901 mac = get_mactype(f);
902 if (mac == NULL) {
903 fprintf(stdout, "Not a Mac file with resource fork\n");
904 code = 1;
905 }
906 if (get_pict(f, mac, FALSE) == 0) {
907 if (extract_mac_pict(f, mac, argv[3]) != 0)
908 fprintf(stdout, "Failed to find PICT id=256 or write file\n");
909 else
910 fprintf(stdout, "Success\n");
911
912 }
913 else {
914 fprintf(stdout, "Resource fork didn't contain PICT preview\n");
915 }
916 gfile_close(f);
917 break;
918 case 'm':
919 /* Create MacBinary */
920 if (argc != 5) {
921 usage();
922 return 1;
923 }
924 f = gfile_open(argv[4], gfile_modeWrite | gfile_modeCreate);
925 if (f == NULL) {
926 fprintf(stdout, "Failed to create file \042%s\042\n", argv[3]);
927 return -1;
928 }
929 code = write_macbin(f, argv[2], argv[2], argv[3]);
930 if (code != 0)
931 fprintf(stdout, "Failed to write MacBinary\n");
932 else
933 fprintf(stdout, "Success at writing MacBinary\n");
934 gfile_close(f);
935 break;
936 case '1':
937 /* Create AppleSingle */
938 if (argc != 5) {
939 usage();
940 return 1;
941 }
942 f = gfile_open(argv[4], gfile_modeWrite | gfile_modeCreate);
943 if (f == NULL) {
944 fprintf(stdout, "Failed to create file \042%s\042\n", argv[3]);
945 return -1;
946 }
947 code = write_applesingle(f, argv[2], argv[3]);
948 if (code != 0)
949 fprintf(stdout, "Failed to write AppleSingle\n");
950 else
951 fprintf(stdout, "Success at writing AppleSingle\n");
952 gfile_close(f);
953 break;
954 case '2':
955 /* Create AppleDouble */
956 if (argc != 4) {
957 usage();
958 return 1;
959 }
960 f = gfile_open(argv[3], gfile_modeWrite | gfile_modeCreate);
961 if (f == NULL) {
962 fprintf(stdout, "Failed to create file \042%s\042\n", argv[3]);
963 return -1;
964 }
965 code = write_appledouble(f, argv[2]);
966 if (code != 0)
967 fprintf(stdout, "Failed to write AppleDouble\n");
968 else
969 fprintf(stdout, "Success at writing AppleDouble\n");
970 gfile_close(f);
971 break;
972 default:
973 usage();
974 return 1;
975 }
976
977 return code;
978 }
979 #endif