comparison lib64gfx.c @ 407:59244a7ae37f

Move c64 utilities to the engine lib, as we benefit from a common framework.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 03 Nov 2012 02:19:51 +0200
parents
children e4b2f689aff6
comparison
equal deleted inserted replaced
406:a0160ffdf7e5 407:59244a7ae37f
1 /*
2 * Functions for reading and converting various restricted
3 * C64/etc and/or indexed/paletted graphics formats.
4 * Programmed and designed by Matti 'ccr' Hamalainen
5 * (C) Copyright 2012 Tecnic Software productions (TNSP)
6 *
7 * Please read file 'COPYING' for information on license and distribution.
8 */
9 #include "lib64gfx.h"
10 #include <errno.h>
11
12 const char *dmC64ImageTypeNames[DM_C64IFMT_LAST_TYPE] =
13 {
14 "hires",
15 "multicolor",
16 "hires interlace",
17 "mc interlace",
18 "hires fli",
19 "mc fli",
20 };
21
22
23 // Based on Pepto's palette, stolen from VICE
24 DMColor dmC64Palette[C64_NCOLORS] =
25 {
26 { 0x00, 0x00, 0x00, 0 },
27 { 0xFF, 0xFF, 0xFF, 0 },
28 { 0x68, 0x37, 0x2B, 0 },
29 { 0x70, 0xA4, 0xB2, 0 },
30 { 0x6F, 0x3D, 0x86, 0 },
31 { 0x58, 0x8D, 0x43, 0 },
32 { 0x35, 0x28, 0x79, 0 },
33 { 0xB8, 0xC7, 0x6F, 0 },
34 { 0x6F, 0x4F, 0x25, 0 },
35 { 0x43, 0x39, 0x00, 0 },
36 { 0x9A, 0x67, 0x59, 0 },
37 { 0x44, 0x44, 0x44, 0 },
38 { 0x6C, 0x6C, 0x6C, 0 },
39 { 0x9A, 0xD2, 0x84, 0 },
40 { 0x6C, 0x5E, 0xB5, 0 },
41 { 0x95, 0x95, 0x95, 0 },
42 };
43
44
45 const size_t dmC64DefaultSizes[DT_LAST] =
46 {
47 C64_SCR_COLOR_SIZE,
48 C64_SCR_BITMAP_SIZE,
49 C64_SCR_SCREEN_SIZE,
50 1,
51 C64_SCR_EXTRADATA,
52 };
53
54
55
56 DMImage * dmImageAlloc(int width, int height)
57 {
58 DMImage *img = dmCalloc(1, sizeof(DMImage));
59 if (img == NULL)
60 return NULL;
61
62 img->width = img->pitch = width;
63 img->height = height;
64 img->data = dmMalloc(width * height * sizeof(uint8_t));
65 if (img->data == NULL)
66 {
67 dmFree(img);
68 return NULL;
69 }
70
71 return img;
72 }
73
74
75 void dmImageFree(DMImage *img)
76 {
77 if (img != NULL)
78 {
79 if (!img->constpal)
80 {
81 dmFree(img->pal);
82 }
83 dmFree(img->data);
84 dmFree(img);
85 }
86 }
87
88
89 int dmImageGetBytesPerPixel(int format)
90 {
91 switch (format)
92 {
93 case DM_IFMT_PALETTE : return 1;
94 case DM_IFMT_RGB : return 3;
95 case DM_IFMT_RGBA : return 4;
96 case DM_IFMT_RGB_PLANE : return 3;
97 default: return 0;
98 }
99 }
100
101
102 int dmC64ConvertCSData(DMImage *img,
103 int xoffs, int yoffs, const uint8_t *buf,
104 int width, int height, BOOL multicolor, int *colors)
105 {
106 int yc, widthpx = width * 8;
107 uint8_t *dp;
108
109 if (img == NULL)
110 return -1;
111 if (xoffs < 0 || yoffs < 0)
112 return -2;
113 if (xoffs > img->width - widthpx ||
114 yoffs > img->height - height)
115 return -3;
116
117 dp = img->data + (yoffs * img->pitch) + xoffs;
118
119 if (multicolor)
120 {
121 for (yc = 0; yc < height; yc++)
122 {
123 const int offs = yc * width;
124 int xc;
125 uint8_t *d = dp;
126
127 for (xc = 0; xc < widthpx / 2; xc++)
128 {
129 const int b = buf[offs + (xc / 4)];
130 const int v = 6 - ((xc * 2) & 6);
131 const uint8_t c = colors[(b >> v) & 3];
132
133 *d++ = c;
134 *d++ = c;
135 }
136
137 dp += img->pitch;
138 }
139 }
140 else
141 {
142 for (yc = 0; yc < height; yc++)
143 {
144 const int offs = yc * width;
145 int xc;
146 uint8_t *d = dp;
147
148 for (xc = 0; xc < widthpx; xc++)
149 {
150 const int b = buf[offs + (xc / 8)];
151 const int v = 7 - (xc & 7);
152 const uint8_t c = colors[(b >> v) & 1];
153
154 *d++ = c;
155 }
156
157 dp += img->pitch;
158 }
159 }
160
161 return 0;
162 }
163
164
165 static int fmtProbeDrazPaint(const uint8_t *buf, const size_t len)
166 {
167 if (len == 10051 && buf[0] == 0x00 && buf[1] == 0x58)
168 return DM_PROBE_SCORE_GOOD;
169
170 return DM_PROBE_SCORE_FALSE;
171 }
172
173
174 static int fmtProbeDrazPaint20Packed(const uint8_t *buf, const size_t len)
175 {
176 const char *ident = (const char *) buf + 2;
177 if (len > 22 && buf[0] == 0x00 && buf[1] == 0x58 &&
178 strncmp(ident, "DRAZPAINT ", 10) == 0 &&
179 ident[11] == '.' && (
180 (ident[10] == '1' && ident[12] == '4') ||
181 (ident[10] == '2' && ident[12] == '0')
182 ))
183 return DM_PROBE_SCORE_MAX;
184
185 return DM_PROBE_SCORE_FALSE;
186 }
187
188
189 static int fmtDecodeDrazPaintPacked(DMC64Image *img, const uint8_t *buf, const size_t len, const DMC64ImageFormat *fmt)
190 {
191 int res;
192 uint8_t rleMarker;
193 uint8_t *mem, *dst, *dstEnd;
194 const uint8_t *src, *srcEnd;
195
196 if ((mem = dmMalloc(C64_RAM_SIZE)) == NULL)
197 return -1;
198
199 rleMarker = *(buf + 0x0d);
200 src = buf + 0x0e;
201 srcEnd = buf + len;
202 dst = mem;
203 dstEnd = mem + C64_RAM_SIZE;
204
205 while (src <= srcEnd && dst <= dstEnd)
206 {
207 int c = *src++;
208 if (c == rleMarker && src + 2 <= srcEnd)
209 {
210 int cnt = *src++;
211 c = *src++;
212 while (cnt-- && dst <= dstEnd)
213 *dst++ = c;
214 }
215 else
216 *dst++ = c;
217 }
218
219 res = dmC64DecodeGenericBMP(img, mem, dst - mem + 1, fmt);
220
221 dmFree(mem);
222
223 return res;
224 }
225
226
227 static int fmtProbeDrazLace10(const uint8_t *buf, const size_t len)
228 {
229 if (len == 18242 && buf[0] == 0x00 && buf[1] == 0x58)
230 return DM_PROBE_SCORE_GOOD;
231 return DM_PROBE_SCORE_FALSE;
232 }
233
234
235 static int fmtProbeDrazLace10Packed(const uint8_t *buf, const size_t len)
236 {
237 const char *ident = (const char *) buf + 2;
238 if (len > 22 && buf[0] == 0x00 && buf[1] == 0x58 &&
239 strncmp(ident, "DRAZLACE! 1.0", 13) == 0)
240 return DM_PROBE_SCORE_MAX;
241
242 return DM_PROBE_SCORE_FALSE;
243 }
244
245
246 static BOOL fmtDrazLaceSetLaceType(DMC64Image *img, const struct _DMDecodeOp *op, const uint8_t *buf, const size_t len)
247 {
248 img->laceType = buf[op->offs] ? DM_C64ILACE_RES : DM_C64ILACE_COLOR;
249 img->laceBank2 = 0;
250 return TRUE;
251 }
252
253
254 #define AMICA_DM_PROBE_SIZE 1024
255 static int fmtProbeAmicaPaintPacked(const uint8_t *buf, const size_t len)
256 {
257 int i, n;
258 if (len < AMICA_DM_PROBE_SIZE || buf[0] != 0x00 || buf[1] != 0x40)
259 return DM_PROBE_SCORE_FALSE;
260
261 for (n = 0, i = 2; i < AMICA_DM_PROBE_SIZE; i++)
262 if (buf[i] == 0xC2) n++;
263
264 if (n > 5)
265 return DM_PROBE_SCORE_GOOD;
266 if (n > 3)
267 return DM_PROBE_SCORE_AVG;
268
269 return DM_PROBE_SCORE_MAYBE;
270 }
271
272
273 static int fmtDecodeAmicaPaintPacked(DMC64Image *img, const uint8_t *buf, const size_t len, const DMC64ImageFormat *fmt)
274 {
275 int res;
276 uint8_t *mem, *dst, *dstEnd;
277 const uint8_t *src, *srcEnd;
278
279 if ((mem = dmMalloc(C64_RAM_SIZE)) == NULL)
280 return -1;
281
282 src = buf;
283 srcEnd = buf + len;
284 dst = mem;
285 dstEnd = mem + C64_RAM_SIZE;
286
287 while (src <= srcEnd && dst <= dstEnd)
288 {
289 int c = *src++;
290 if (c == 0xC2 && src + 2 <= srcEnd)
291 {
292 int cnt = *src++;
293 c = *src++;
294 while (cnt-- && dst <= dstEnd)
295 *dst++ = c;
296 }
297 else
298 *dst++ = c;
299 }
300
301 res = dmC64DecodeGenericBMP(img, mem, dst - mem + 1, fmt);
302
303 dmFree(mem);
304
305 return res;
306 }
307
308
309 static int fmtProbeKoalaPaint(const uint8_t *buf, const size_t len)
310 {
311 if (len == 10003 && buf[0] == 0x00 && buf[1] == 0x60)
312 return DM_PROBE_SCORE_AVG;
313 return DM_PROBE_SCORE_FALSE;
314 }
315
316
317 static int fmtProbeTruePaint(const uint8_t *buf, const size_t len)
318 {
319 if (len == 19434 && buf[0] == 0x00 && buf[1] == 0x9c)
320 return DM_PROBE_SCORE_GOOD;
321 return DM_PROBE_SCORE_FALSE;
322 }
323
324
325 static BOOL fmtTruePaintSetLaceType(DMC64Image *img, const struct _DMDecodeOp *op, const uint8_t *buf, const size_t len)
326 {
327 img->laceType = DM_C64ILACE_RES;
328 img->laceBank2 = 1;
329 return TRUE;
330 }
331
332
333 DMC64ImageFormat dmC64ImageFormats[] =
334 {
335 {
336 DM_C64IFMT_MC, ".drp", "DrazPaint 2.0 (packed)",
337 fmtProbeDrazPaint20Packed, fmtDecodeDrazPaintPacked, NULL,
338 4,
339 {
340 { DT_COLOR_RAM, 0x0000, 0, 0, NULL },
341 { DT_BITMAP, 0x0800, 0, 0, NULL },
342 { DT_SCREEN_RAM, 0x0400, 0, 0, NULL },
343 { DT_BGCOLOR, 0x2740, 0, 0, NULL },
344 }
345 },
346
347 {
348 DM_C64IFMT_MC_ILACE, ".dlp", "DrazLace 1.0 (packed)",
349 fmtProbeDrazLace10Packed, fmtDecodeDrazPaintPacked, NULL,
350 6,
351 {
352 { DT_COLOR_RAM, 0x0000, 0, 0, NULL },
353 { DT_BITMAP, 0x0800, 0, 0, NULL },
354 { DT_SCREEN_RAM, 0x0400, 0, 0, NULL },
355 { DT_BGCOLOR, 0x2740, 0, 0, NULL },
356 { DT_BITMAP, 0x2800, 1, 0, NULL },
357 { DT_FUNCTION, 0x2742, 0, 1, fmtDrazLaceSetLaceType },
358 }
359 },
360
361 {
362 DM_C64IFMT_MC, ".drp", "DrazPaint (unpacked)",
363 fmtProbeDrazPaint, NULL, NULL,
364 4,
365 {
366 { DT_COLOR_RAM, 0x0000, 0, 0, NULL },
367 { DT_BITMAP, 0x0800, 0, 0, NULL },
368 { DT_SCREEN_RAM, 0x0400, 0, 0, NULL },
369 { DT_BGCOLOR, 0x2740, 0, 0, NULL },
370 }
371 },
372
373 {
374 DM_C64IFMT_MC_ILACE, ".drl", "DrazLace 1.0 (unpacked)",
375 fmtProbeDrazLace10, NULL, NULL,
376 6,
377 {
378 { DT_COLOR_RAM, 0x0000, 0, 0, NULL },
379 { DT_BITMAP, 0x0800, 0, 0, NULL },
380 { DT_SCREEN_RAM, 0x0400, 0, 0, NULL },
381 { DT_BGCOLOR, 0x2740, 0, 0, NULL },
382 { DT_BITMAP, 0x2800, 1, 0, NULL },
383 { DT_FUNCTION, 0x2742, 0, 1, fmtDrazLaceSetLaceType },
384 }
385 },
386
387 {
388 DM_C64IFMT_MC_ILACE, ".mci", "Truepaint (unpacked)",
389 fmtProbeTruePaint, NULL, NULL,
390 6,
391 {
392 { DT_SCREEN_RAM, 0x0000, 0, 0, NULL },
393 { DT_BGCOLOR, 0x03e8, 0, 0, NULL },
394 { DT_BITMAP, 0x0400, 0, 0, NULL },
395 { DT_BITMAP, 0x2400, 1, 0, NULL },
396 { DT_SCREEN_RAM, 0x4400, 1, 0, NULL },
397 { DT_COLOR_RAM, 0x4800, 0, 0, NULL },
398 { DT_FUNCTION, 0x0000, 0, 0, fmtTruePaintSetLaceType },
399 }
400 },
401
402 {
403 DM_C64IFMT_MC, ".kla", "Koala Paint (unpacked)",
404 fmtProbeKoalaPaint, NULL, NULL,
405 4,
406 {
407 { DT_COLOR_RAM, 0x2328, 0, 0, NULL },
408 { DT_BITMAP, 0x0000, 0, 0, NULL },
409 { DT_SCREEN_RAM, 0x1f40, 0, 0, NULL },
410 { DT_BGCOLOR, 0x2710, 0, 0, NULL },
411 }
412 },
413
414 {
415 DM_C64IFMT_MC, ".ami", "Amica Paint (packed)",
416 fmtProbeAmicaPaintPacked, fmtDecodeAmicaPaintPacked, NULL,
417 4,
418 {
419 { DT_COLOR_RAM, 0x2328, 0, 0, NULL },
420 { DT_BITMAP, 0x0000, 0, 0, NULL },
421 { DT_SCREEN_RAM, 0x1f40, 0, 0, NULL },
422 { DT_BGCOLOR, 0x2710, 0, 0, NULL },
423 }
424 },
425
426 };
427
428 const int ndmC64ImageFormats = sizeof(dmC64ImageFormats) / sizeof(dmC64ImageFormats[0]);
429
430
431 int dmC64ProbeGeneric(const uint8_t *buf, const size_t len,
432 DMC64ImageFormat **pfmt)
433 {
434 int i, scoreMax = 0, scoreIndex = -1;
435
436 for (i = 0; i < ndmC64ImageFormats; i++)
437 {
438 DMC64ImageFormat *fmt = &dmC64ImageFormats[i];
439 int score = fmt->probe(buf, len);
440 if (score > scoreMax)
441 {
442 scoreMax = score;
443 scoreIndex = i;
444 }
445 }
446
447 if (scoreIndex >= 0)
448 {
449 *pfmt = &dmC64ImageFormats[scoreIndex];
450 return scoreMax;
451 }
452 else
453 return 0;
454 }
455
456
457 int dmC64DecodeGenericBMP(DMC64Image *img, const uint8_t *buf,
458 const size_t len, const DMC64ImageFormat *fmt)
459 {
460 int i;
461
462 memset(img, 0, sizeof(*img));
463 img->type = fmt->type;
464
465 for (i = 0; i < fmt->ndecodeOps; i++)
466 {
467 const DMDecodeOp *op = &fmt->decodeOps[i];
468 const uint8_t *src;
469 size_t size;
470
471 if (op->bank < 0 || op->bank >= C64_SCR_MAX_BANK)
472 {
473 dmError("Invalid bank %d definition in generic decode operator %d @ #%d.\n",
474 op->bank, op->type, i);
475 return -1;
476 }
477
478 if (op->type < 0 || op->type >= DT_LAST)
479 {
480 dmError("Invalid decode operator type %d @ #%d.\n",
481 op->type, i);
482 return -1;
483 }
484
485 size = (op->size == 0) ? dmC64DefaultSizes[op->type] : op->size;
486
487 if (op->offs + size > len)
488 {
489 dmError("Decode out of bounds, op #%d type=%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x\n",
490 i, op->type, op->offs, op->offs, op->bank, size, size, len, len);
491 return -2;
492 }
493
494 src = buf + op->offs;
495
496 switch (op->type)
497 {
498 case DT_COLOR_RAM: memcpy(img->color[op->bank], src, size); break;
499 case DT_BITMAP: memcpy(img->bitmap[op->bank], src, size); break;
500 case DT_SCREEN_RAM: memcpy(img->screen[op->bank], src, size); break;
501 case DT_BGCOLOR: img->bgcolor = *src; break;
502 case DT_EXTRADATA: memcpy(img->extradata, src, size); break;
503 case DT_FUNCTION:
504 if (op->function == NULL)
505 {
506 dmError("Decode op is a function, but function ptr is NULL: op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x\n",
507 i, op->offs, op->offs, op->bank, size, size, len, len);
508 return -6;
509 }
510 if (!op->function(img, op, buf, len))
511 {
512 dmError("Decode op custom function failed: op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x\n",
513 i, op->offs, op->offs, op->bank, size, size, len, len);
514 return -5;
515 }
516 break;
517 }
518 }
519
520 return 0;
521 }
522
523
524 static int dmC64ConvertHiResBMP(DMImage *screen, const DMC64Image *img)
525 {
526 int yc;
527 uint8_t *dp = screen->data;
528
529 for (yc = 0; yc < C64_SCR_HEIGHT; yc++)
530 {
531 uint8_t *d = dp;
532 const int y = yc / 8, yb = yc & 7;
533 const int scroffsy = y * C64_SCR_CH_WIDTH;
534 const int bmoffsy = y * C64_SCR_WIDTH;
535 int xc;
536
537 for (xc = 0; xc < C64_SCR_WIDTH; xc++)
538 {
539 const int x = xc / 8;
540 const int scroffs = scroffsy + x;
541 const int b = img->bitmap[0][bmoffsy + (x * 8) + yb];
542 const int v = 7 - (xc & 7);
543 uint8_t c;
544
545 if ((b >> v) & 1)
546 c = img->screen[0][scroffs] & 15;
547 else
548 c = img->screen[0][scroffs] >> 4;
549
550 *d++ = c;
551 }
552
553 dp += screen->pitch;
554 }
555
556 return 0;
557 }
558
559
560 static int dmC64ConvertMultiColorBMP(DMImage *screen, const DMC64Image *img)
561 {
562 int yc;
563 uint8_t *dp = screen->data;
564
565 for (yc = 0; yc < C64_SCR_HEIGHT; yc++)
566 {
567 uint8_t *d = dp;
568 const int y = yc / 8, yb = yc & 7;
569 const int scroffsy = y * C64_SCR_CH_WIDTH;
570 const int bmoffsy = y * C64_SCR_WIDTH;
571 int xc;
572
573 for (xc = 0; xc < C64_SCR_WIDTH / 2; xc++)
574 {
575 const int x = xc / 4;
576 const int scroffs = scroffsy + x;
577 const int b = img->bitmap[0][bmoffsy + (x * 8) + yb];
578 const int v = 6 - ((xc * 2) & 6);
579 uint8_t c;
580
581 switch ((b >> v) & 3)
582 {
583 case 0: c = img->bgcolor; break;
584 case 1: c = img->screen[0][scroffs] >> 4; break;
585 case 2: c = img->screen[0][scroffs] & 15; break;
586 case 3: c = img->color[0][scroffs] & 15; break;
587 }
588
589 *d++ = c;
590 *d++ = c;
591 }
592
593 dp += screen->pitch;
594 }
595
596 return 0;
597 }
598
599
600 static int dmC64ConvertLaceMultiColorBMP(DMImage *screen, const DMC64Image *img)
601 {
602 int yc;
603 uint8_t *dp = screen->data;
604
605 for (yc = 0; yc < C64_SCR_HEIGHT; yc++)
606 {
607 uint8_t *d = dp;
608 const int y = yc / 8, yb = yc & 7;
609 const int scroffsy = y * C64_SCR_CH_WIDTH;
610 const int bmoffsy = y * C64_SCR_WIDTH;
611 int xc;
612
613 for (xc = 0; xc < C64_SCR_WIDTH / 2; xc++)
614 {
615 const int x = xc / 4;
616 const int scroffs = scroffsy + x;
617 const int bmoffs = bmoffsy + (x * 8) + yb;
618 const int v = 6 - ((xc * 2) & 6);
619 const int b1 = (img->bitmap[0][bmoffs] >> v) & 3;
620 const int b2 = (img->bitmap[1][bmoffs] >> v) & 3;
621 uint8_t c1, c2;
622
623 switch (b1)
624 {
625 case 0: c1 = img->bgcolor; break;
626 case 1: c1 = img->screen[0][scroffs] >> 4; break;
627 case 2: c1 = img->screen[0][scroffs] & 15; break;
628 case 3: c1 = img->color[0][scroffs] & 15; break;
629 }
630
631 switch (b2)
632 {
633 case 0: c2 = img->bgcolor; break;
634 case 1: c2 = img->screen[img->laceBank2][scroffs] >> 4; break;
635 case 2: c2 = img->screen[img->laceBank2][scroffs] & 15; break;
636 case 3: c2 = img->color[img->laceBank2][scroffs] & 15; break;
637 }
638
639 *d++ = c1;
640 *d++ = c2;
641 }
642
643 dp += screen->pitch;
644 }
645
646 return 0;
647 }
648
649
650 int dmC64ConvertGenericBMP2Image(DMImage *dst, const DMC64Image *src)
651 {
652 switch (src->type)
653 {
654 case DM_C64IFMT_HIRES:
655 return dmC64ConvertHiResBMP(dst, src);
656
657 case DM_C64IFMT_MC:
658 return dmC64ConvertMultiColorBMP(dst, src);
659
660 case DM_C64IFMT_MC_ILACE:
661 return dmC64ConvertLaceMultiColorBMP(dst, src);
662
663 default:
664 return -1;
665 }
666 }
667
668
669 #define BUF_SIZE_INITIAL (16*1024)
670 #define BUF_SIZE_GROW (2*1024)
671
672 int dmReadDataFile(const char *filename, uint8_t **pbuf, size_t *pbufSize)
673 {
674 FILE *f;
675 uint8_t *dataBuf = NULL, *dataPtr;
676 size_t bufSize, readSize, dataSize;
677
678 if (filename == NULL)
679 f = stdin;
680 else
681 if ((f = fopen(filename, "rb")) == NULL)
682 {
683 int err = errno;
684 dmError("Could not open input file '%s': %d, %s\n",
685 filename, err, strerror(err));
686 return -1;
687 }
688
689 readSize = bufSize = BUF_SIZE_INITIAL;
690 if ((dataBuf = dmMalloc(bufSize)) == NULL)
691 {
692 fclose(f);
693 dmError("Error allocating memory for data, %d bytes.\n", bufSize);
694 return -4;
695 }
696
697 dataPtr = dataBuf;
698 dataSize = 0;
699
700 while (!feof(f) && !ferror(f))
701 {
702 size_t read = fread(dataPtr, 1, readSize, f);
703
704 dataSize += read;
705 dataPtr += read;
706
707 if (read == readSize && !feof(f))
708 {
709 readSize = BUF_SIZE_GROW;
710 bufSize += BUF_SIZE_GROW;
711 if ((dataBuf = dmRealloc(dataBuf, bufSize)) == NULL)
712 {
713 dmError("Error reallocating memory for data, %d bytes.\n", bufSize);
714 return -4;
715 }
716 }
717 else
718 break;
719 }
720
721 fclose(f);
722
723 *pbuf = dataBuf;
724 *pbufSize = dataSize;
725
726 return 0;
727 }