hbmap: fix iterator truncation when size_t < 32bit
[rofl0r-agsutils.git] / Clib32.c
blob0caa6062b58a254f8ca26a38db7c8aeba653f0fd
1 /* CLIB32 - DJGPP implemention of the CLIB reader.
2 (c) 1998-99 Chris Jones, (c) 2012 rofl0r
4 22/12/02 - Shawn's Linux changes approved and integrated - CJ
6 v1.2 (Apr'01) added support for new multi-file CLIB version 10 files
7 v1.1 (Jul'99) added support for appended-to-exe data files
9 Adventure Game Studio source code Copyright 1999-2011 Chris Jones.
10 All rights reserved.
12 The AGS Editor Source Code is provided under the Artistic License 2.0
13 http://www.opensource.org/licenses/artistic-license-2.0.php
15 You MAY NOT compile your own builds of the engine without making it EXPLICITLY
16 CLEAR that the code has been altered from the Standard Version.
18 v1.3 rofl0r: code cleaned up for usage in agsutils.
19 v1.4 rofl0r: added writer and reader interfaces, as well as the entire writer code.
23 #include <assert.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stddef.h>
28 #include "ByteArray.h"
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <stdint.h>
34 #include <stddef.h>
35 #include <limits.h>
36 #include "Clib32.h"
37 #include "endianness.h"
39 #ifndef _WIN32
40 #define O_BINARY 0
41 #define PSEP_STR "/"
42 #else
43 #define PSEP_STR "\\"
44 #endif
45 #define PSEP PSEP_STR[0]
47 #define RAND_SEED_SALT 9338638
49 static char *clibendfilesig = "CLIB\x1\x2\x3\x4SIGE";
50 static char *clibpasswencstring = "My\x1\xde\x4Jibzle";
51 static int _last_rand;
53 static void init_pseudo_rand_gen(int seed) {
54 _last_rand = seed;
57 static int get_pseudo_rand() {
58 return( ((_last_rand = _last_rand * 214013L
59 + 2531011L) >> 16) & 0x7fff );
62 static void clib_encrypt_text(unsigned char *clear, unsigned char *encbuf) {
63 unsigned adx = 0;
64 while(1) {
65 *encbuf = *clear + clibpasswencstring[adx];
66 if(!*clear) break;
67 adx++; encbuf++; clear++;
68 if (adx > 10) adx = 0;
72 static void clib_decrypt_text(char *toenc) {
73 unsigned adx = 0;
74 while (1) {
75 *toenc -= clibpasswencstring[adx];
76 if (!*toenc) break;
77 adx++; toenc++;
78 if (adx > 10) adx = 0;
82 static void fgetnulltermstring(char *sss, struct ByteArray *ddd, int bufsize) {
83 int b = -1;
84 off_t l = ByteArray_get_length(ddd);
85 do {
86 if (b < bufsize - 1) b++;
87 if(ByteArray_get_position(ddd) >= l)
88 return;
89 sss[b] = ByteArray_readByte(ddd);
90 } while (sss[b] != 0);
93 long last_opened_size;
95 static int fread_data_enc_byte(struct ByteArray *ba) {
96 return ByteArray_readUnsignedByte(ba) - get_pseudo_rand();
99 static uint32_t fread_data_enc_int(struct ByteArray *ba) {
100 union {
101 uint32_t i;
102 unsigned char c[4];
103 } res;
104 res.i = ByteArray_readUnsignedInt(ba);
105 size_t i = 0;
106 for(; i < 4; i++)
107 res.c[i] -= get_pseudo_rand();
108 return res.i;
111 static void fread_data_intarray_enc(struct ByteArray *ba, unsigned* dest, size_t count) {
112 size_t i = 0;
113 for(; i < count; i++)
114 dest[i] = fread_data_enc_int(ba);
117 static void fread_data_intarray(struct ByteArray *ba, unsigned* dest, size_t count) {
118 size_t i = 0;
119 for(; i < count; i++)
120 dest[i] = ByteArray_readInt(ba);
123 #if 0
124 static void fread_data_enc(void *data, size_t dataSize, size_t dataCount, struct ByteArray *ooo) {
125 ByteArray_readMultiByte(ooo, (char*)data, dataSize * dataCount);
126 unsigned char *dataChar = (unsigned char*)data;
127 size_t i = 0;
128 for (; i < dataSize * dataCount; i++)
129 dataChar[i] -= get_pseudo_rand();
131 #endif
133 static void fgetstring_enc(char *sss, struct ByteArray *ooo, int maxLength) {
134 int i = 0;
135 while ((i == 0) || (sss[i - 1] != 0)) {
136 sss[i] = ByteArray_readByte(ooo) - get_pseudo_rand();
137 if (i < maxLength - 1) i++;
141 static int read_v30_clib(struct MultiFileLibNew * mfl, struct ByteArray * wout) {
142 size_t aa;
143 /* int reservedFlags = */ ByteArray_readInt(wout);
145 mfl->num_data_files = ByteArray_readInt(wout);
146 assert(mfl->num_data_files <= MAXMULTIFILES);
147 for (aa = 0; aa < mfl->num_data_files; aa++)
148 fgetnulltermstring(mfl->data_filenames[aa], wout, 50);
149 mfl->num_files = ByteArray_readInt(wout);
151 if (mfl->num_files > MAX_FILES)
152 return -1;
154 for (aa = 0; aa < mfl->num_files; aa++) {
155 fgetnulltermstring(mfl->filenames[aa], wout, 100);
156 mfl->file_datafile[aa] = ByteArray_readUnsignedByte(wout); /* "LibUid" */
157 unsigned long long tmp;
158 tmp = ByteArray_readUnsignedLongLong(wout);
159 mfl->offset[aa] = tmp;
160 tmp = ByteArray_readUnsignedLongLong(wout);
161 mfl->length[aa] = tmp;
164 // for(aa = 0; aa < mfl->num_files; aa++)
165 // mfl->file_datafile[aa] = fread_data_enc_byte(wout);
166 return 0;
169 static int getw_enc(struct ByteArray *ooo) {
170 return fread_data_enc_int(ooo);
173 static int read_new_new_enc_format_clib(struct MultiFileLibNew * mfl, struct ByteArray * wout) {
174 size_t aa;
175 int randSeed = ByteArray_readInt(wout);
176 init_pseudo_rand_gen(randSeed + RAND_SEED_SALT);
177 mfl->num_data_files = getw_enc(wout);
178 for (aa = 0; aa < mfl->num_data_files; aa++)
179 fgetstring_enc(mfl->data_filenames[aa], wout, 50);
180 mfl->num_files = getw_enc(wout);
182 if (mfl->num_files > MAX_FILES)
183 return -1;
185 for (aa = 0; aa < mfl->num_files; aa++)
186 fgetstring_enc(mfl->filenames[aa], wout, 100);
187 for (aa = 0; aa < mfl->num_files; aa++)
188 mfl->offset[aa] = fread_data_enc_int(wout);
189 for (aa = 0; aa < mfl->num_files; aa++)
190 mfl->length[aa] = fread_data_enc_int(wout);
191 for (aa = 0; aa < mfl->num_files; aa++)
192 mfl->file_datafile[aa] = fread_data_enc_byte(wout);
193 return 0;
196 static int read_new_new_format_clib(struct MultiFileLibNew* mfl, struct ByteArray * wout) {
197 size_t aa;
198 mfl->num_data_files = ByteArray_readInt(wout);
199 for (aa = 0; aa < mfl->num_data_files; aa++)
200 fgetnulltermstring(mfl->data_filenames[aa], wout, 50);
201 mfl->num_files = ByteArray_readInt(wout);
203 if (mfl->num_files > MAX_FILES) return -1;
205 for (aa = 0; aa < mfl->num_files; aa++) {
206 short nameLength = ByteArray_readShort(wout);
207 nameLength /= 5;
208 ByteArray_readMultiByte(wout, mfl->filenames[aa], nameLength);
209 clib_decrypt_text(mfl->filenames[aa]);
211 for (aa = 0; aa < mfl->num_files; aa++)
212 mfl->offset[aa] = ByteArray_readInt(wout);
213 for (aa = 0; aa < mfl->num_files; aa++)
214 mfl->length[aa] = ByteArray_readInt(wout);
215 ByteArray_readMultiByte(wout, mfl->file_datafile, mfl->num_files);
216 return 0;
219 static int read_new_format_clib(struct MultiFileLib * mfl, struct ByteArray * wout, int libver) {
220 mfl->num_data_files = ByteArray_readInt(wout);
221 ByteArray_readMultiByte(wout, (char*) mfl->data_filenames, 20U * mfl->num_data_files);
222 mfl->num_files = ByteArray_readInt(wout);
224 if (mfl->num_files > MAX_FILES) return -1;
226 ByteArray_readMultiByte(wout, (char*) mfl->filenames, 25U * mfl->num_files);
228 fread_data_intarray(wout, mfl->offset, mfl->num_files);
229 fread_data_intarray(wout, mfl->length, mfl->num_files);
230 ByteArray_readMultiByte(wout, mfl->file_datafile, mfl->num_files);
232 if (libver >= 11) {
233 size_t aa;
234 for (aa = 0; aa < mfl->num_files; aa++)
235 clib_decrypt_text(mfl->filenames[aa]);
237 return 0;
240 void AgsFile_close(struct AgsFile *f) {
241 unsigned i;
242 for (i=0; i < AgsFile_getDataFileCount(f); i++)
243 ByteArray_close_file(&f->f[i]);
246 int AgsFile_getVersion(struct AgsFile *f) {
247 return f->libversion;
250 void AgsFile_setVersion(struct AgsFile *f, int version) {
251 f->libversion = version;
254 void AgsFile_setSourceDir(struct AgsFile *f, char* sourcedir) {
255 f->dir = sourcedir;
258 void AgsFile_setFileCount(struct AgsFile *f, size_t count) {
259 f->mflib.num_files = count;
262 static off_t filelength(int fd) {
263 struct stat st;
264 fstat(fd, &st);
265 return st.st_size;
268 int AgsFile_setFile(struct AgsFile *f, size_t index, char* fn) {
269 strncpy(f->mflib.filenames[index], fn, 100);
270 char fnbuf[512];
271 snprintf(fnbuf, sizeof(fnbuf), "%s%c%s", f->dir, PSEP, f->mflib.filenames[index]);
272 int fd = open(fnbuf, O_RDONLY|O_BINARY);
273 if(fd == -1) return 0;
274 off_t fl = filelength(fd);
275 close(fd);
276 f->mflib.length[index] = fl;
277 return 1;
280 int AgsFile_setDataFile(struct AgsFile *f, size_t index, char* fn) {
281 size_t l = strlen(fn);
282 if(l >= 20) return 0;
283 strncpy(f->mflib.data_filenames[index], fn, 20);
284 return 1;
287 void AgsFile_setDataFileCount(struct AgsFile *f, size_t count) {
288 f->mflib.num_data_files = count;
291 static void write_ull(int fd, unsigned long long val) {
292 unsigned long long le = end_htole64(val);
293 write(fd, &le, sizeof(le));
296 static int get_int_le(int val) {
297 return end_htole32(val);
300 static void write_int(int fd, int val) {
301 int le = get_int_le(val);
302 write(fd, &le, sizeof(le));
305 static short get_short_le(short val) {
306 return end_htole16(val);
309 static void write_short(int fd, short val) {
310 short le = get_short_le(val);
311 write(fd, &le, sizeof(le));
314 static void write_int_array(int fd, int* arr, size_t len) {
315 size_t i = 0;
316 for(; i < len; i++)
317 write_int(fd, arr[i]);
320 /* if *bytes == -1L, read entire file, and store the total amount read there */
321 static int copy_into_file(int fd, const char *dir, const char *fn, size_t *bytes) {
322 char fnbuf[512];
323 snprintf(fnbuf, sizeof(fnbuf), "%s%c%s", dir, PSEP, fn);
324 int f = open(fnbuf, O_RDONLY|O_BINARY);
325 if(f == -1) return 0;
326 size_t filesize = *bytes;
327 char readbuf[4096];
328 int ret = 1;
329 if(filesize == -1L) {
330 *bytes = 0;
331 while(1) {
332 ssize_t r = read(f, readbuf, sizeof readbuf);
333 if(r < 0) { ret=0; goto end; }
334 if(r == 0) break;
335 *bytes += r;
336 if(write(fd, readbuf, r) != r) { ret=0; goto end; }
338 } else do {
339 size_t togo = filesize > sizeof(readbuf) ? sizeof(readbuf) : filesize;
340 if((size_t) read(f, readbuf, togo) != togo) { ret = 0; goto end; }
341 filesize -= togo;
342 ssize_t wret;
343 size_t nwritten = 0;
344 while(togo) {
345 wret = write(fd, readbuf + nwritten, togo);
346 if(wret <= 0) { ret = 0; goto end; }
347 nwritten += wret;
348 togo -= wret;
350 } while(filesize);
351 end:
352 close(f);
353 return ret;
356 #define WH_EWRITE(X,Y,Z) do {if(Z != write(X, Y, Z)) return 0;} while(0)
357 static size_t write_header(struct AgsFile *f, int fd) {
358 int myversion = f->libversion >= 30 ? 30 : 20; //f->libversion;
359 unsigned char version = myversion;
360 WH_EWRITE(fd, "CLIB\x1a", 5);
361 WH_EWRITE(fd, &version, 1);
362 version = 0;
363 if(myversion >= 10) WH_EWRITE(fd, &version, 1);
364 size_t written = 7;
365 size_t i,l;
366 if(myversion == 30) {
367 write_int(fd, 0); /*reservedFlags*/
368 written += 4;
370 write_int(fd, f->mflib.num_data_files);
371 written += sizeof(int);
372 for(i = 0; i < f->mflib.num_data_files; i++) {
373 l = strlen(f->mflib.data_filenames[i]) + 1;
374 written += l;
375 if(l != (size_t) write(fd, f->mflib.data_filenames[i], l))
376 return 0;
379 write_int(fd, f->mflib.num_files);
380 written += sizeof(int);
382 if(myversion == 30) {
383 for(i = 0; i < f->mflib.num_files; i++) {
384 l = strlen(f->mflib.filenames[i]) + 1;
385 written += l;
386 if(l != (size_t) write(fd, f->mflib.filenames[i], l))
387 return 0;
388 /* write another 0 byte as libuid */
389 WH_EWRITE(fd, &version, 1);
390 ++written;
392 write_ull(fd, f->mflib.offset[i]);
393 written += 8;
395 write_ull(fd, f->mflib.length[i]);
396 written += 8;
398 return written;
401 unsigned char encbuf[100];
402 for(i = 0; i < f->mflib.num_files; i++) {
403 l = strlen(f->mflib.filenames[i]) + 1;
404 write_short(fd, l * 5);
405 clib_encrypt_text((unsigned char*)f->mflib.filenames[i], encbuf);
406 if(l != (size_t) write(fd, encbuf, l)) return 0;
407 written += sizeof(short) + l;
409 l = f->mflib.num_files;
410 for(i = 0; i < l; ++i)
411 write_int(fd, f->mflib.offset[i]);
412 written += sizeof(int) * l;
414 for(i = 0; i < l; ++i)
415 write_int(fd, f->mflib.length[i]);
416 written += sizeof(int) * l;
418 if(l != (size_t) write(fd, f->mflib.file_datafile, l))
419 return 0;
420 written += l;
421 return written;
424 static void write_footer(int fd, unsigned offset, int v30) {
425 write_int(fd, offset);
426 if(v30) write_int(fd, 0);
427 write(fd, clibendfilesig, 12);
430 void AgsFile_setExeStub(struct AgsFile *f, const char *fn) {
431 f->exestub_fn = fn;
434 int AgsFile_write(struct AgsFile *f) {
435 int fd = open(f->fn, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY, 0660);
436 if(fd == -1) return 0;
437 size_t i;
438 unsigned long long off;
439 if(!(off = write_header(f, fd))) return 0;
440 for(i = 0; i < f->mflib.num_files; i++) {
441 f->mflib.offset[i] = off;
442 off += f->mflib.length[i];
444 lseek(fd, 0, SEEK_SET);
445 unsigned header_offset = 0; /* we can safely assume the exe stub fits into 4 GB */
446 if(f->exestub_fn) {
447 size_t stubsize = -1L;
448 if(!copy_into_file(fd, f->dir, f->exestub_fn, &stubsize))
449 return 0;
450 header_offset = stubsize;
452 write_header(f, fd);
453 for(i = 0; i < f->mflib.num_files; i++) {
454 size_t fs = f->mflib.length[i];
455 if(!copy_into_file(fd, f->dir, f->mflib.filenames[i], &fs))
456 return 0;
458 write_footer(fd, header_offset, f->libversion >= 30);
459 close(fd);
460 return 1;
463 static int prep_multifiles(struct AgsFile *f) {
464 unsigned aa;
465 struct ByteArray *ba;
466 /* open each datafile as a byte array */
467 assert(MAXMULTIFILES >= f->mflib.num_data_files);
468 for (aa = 1; aa < f->mflib.num_data_files; aa++) {
469 char fntmp[512];
470 snprintf(fntmp, sizeof fntmp, "%s%s%s",
471 f->dir ? f->dir : "",
472 f->dir ? PSEP_STR : "",
473 f->mflib.data_filenames[aa]);
474 ba = &f->f[aa];
475 ByteArray_ctor(ba);
476 if(!ByteArray_open_file(ba, fntmp)) {
477 fprintf(stderr, "error opening mfl datafile %s\n", fntmp);
478 return -1;
480 ByteArray_set_endian(ba, BAE_LITTLE); // all ints etc are saved in little endian.
482 return 0;
485 static int csetlib(struct AgsFile* f, char *filename) {
486 char clbuff[20];
487 if (!filename)
488 return -11;
490 int passwmodifier = 0;
491 size_t aa;
492 size_t cc, l;
494 struct ByteArray *ba = &f->f[0];
495 ByteArray_ctor(ba);
496 if(!ByteArray_open_file(ba, filename)) return -1;
497 ByteArray_set_endian(ba, BAE_LITTLE); // all ints etc are saved in little endian.
498 off_t ba_len = ByteArray_get_length(ba);
499 ByteArray_readMultiByte(ba, clbuff, 5);
501 uint32_t absoffs = 0; /* we read 4 bytes- so our datatype
502 must be 4 bytes as well, since we use a pointer to it */
504 f->pack_off = 0;
505 if (strncmp(clbuff, "CLIB", 4) != 0) {
506 /* CLIB signature not found at the beginning of file
507 so it's probably an exe and we need to lookup the tail
508 signature. */
509 ByteArray_set_position(ba, ba_len - 12);
510 ByteArray_readMultiByte(ba, clbuff, 12);
512 if (strncmp(clbuff, clibendfilesig, 12) != 0)
513 return -2;
514 // it's an appended-to-end-of-exe thing.
515 // there's a new format using a 64bit offset, we need to try
516 // that first.
518 uint64_t off64;
519 ByteArray_set_position(ba, ba_len - 20);
520 off64 = ByteArray_readUnsignedLongLong(ba);
521 if(off64 < ba_len &&
522 ByteArray_set_position(ba, off64) &&
523 ByteArray_readMultiByte(ba, clbuff, 5) &&
524 !strncmp(clbuff, "CLIB", 4)) {
525 if(off64 > 0xffffffff) {
526 fprintf(stderr, "error: gamepacks > 4GB not supported at this time\n");
527 return -21;
529 absoffs = off64;
530 } else {
531 ByteArray_set_position(ba, ba_len - 16);
532 absoffs = ByteArray_readUnsignedInt(ba);
535 ByteArray_set_position(ba, absoffs);
536 ByteArray_readMultiByte(ba, clbuff, 5);
537 if(strncmp(clbuff, "CLIB", 4)) {
538 fprintf(stderr, "error: ags clib header signature not found\n");
539 return -22;
542 f->pack_off = absoffs;
545 f->libversion = ByteArray_readUnsignedByte(ba);
546 switch (f->libversion) {
547 /* enum MFLVersion (kMFLVersion_MultiV21 ...) in newer AGS */
548 case 6: case 10: case 11: case 15: case 20: case 21: case 30:
549 break;
550 default:
551 // unsupported version
552 return -3;
555 // remove slashes so that the lib name fits in the buffer
556 while (filename[0] == '\\' || filename[0] == '/') filename++;
558 if (f->libversion >= 10) {
559 if (ByteArray_readUnsignedByte(ba) != 0)
560 return -4; // not first datafile in chain
562 if (f->libversion >= 30) {
563 if (read_v30_clib(&f->mflib, ba)) return -5;
564 } else if (f->libversion >= 21) {
565 if (read_new_new_enc_format_clib(&f->mflib, ba))
566 return -5;
567 } else if (f->libversion == 20) {
568 if (read_new_new_format_clib(&f->mflib, ba))
569 return -5;
570 } else {
571 struct MultiFileLib mflibOld_b, *mflibOld = &mflibOld_b;
573 if (read_new_format_clib(mflibOld, ba, f->libversion))
574 return -5;
575 // convert to newer format
576 f->mflib.num_files = mflibOld->num_files;
577 f->mflib.num_data_files = mflibOld->num_data_files;
578 for(aa = 0; aa < f->mflib.num_files; ++aa)
579 f->mflib.offset[aa] = mflibOld->offset[aa];
580 for(aa = 0; aa < f->mflib.num_files; ++aa)
581 f->mflib.length[aa] = mflibOld->length[aa];
582 memcpy(f->mflib.file_datafile, mflibOld->file_datafile, sizeof(char) * f->mflib.num_files);
583 assert(MAXMULTIFILES >= f->mflib.num_data_files);
584 for (aa = 0; aa < f->mflib.num_data_files; aa++)
585 strcpy(f->mflib.data_filenames[aa], mflibOld->data_filenames[aa]);
586 for (aa = 0; aa < f->mflib.num_files; aa++)
587 strcpy(f->mflib.filenames[aa], mflibOld->filenames[aa]);
590 strcpy(f->mflib.data_filenames[0], filename);
591 for (aa = 0; aa < f->mflib.num_files; aa++) {
592 // correct offsets for EXE file
593 if (f->mflib.file_datafile[aa] == 0)
594 f->mflib.offset[aa] += absoffs;
596 return prep_multifiles(f);
599 passwmodifier = ByteArray_readUnsignedByte(ba);
600 ByteArray_readUnsignedByte(ba); // unused byte
601 f->mflib.num_data_files = 1;
602 strcpy(f->mflib.data_filenames[0], filename);
604 short tempshort = ByteArray_readShort(ba);
605 f->mflib.num_files = tempshort;
607 if (f->mflib.num_files > MAX_FILES) return -4;
609 ByteArray_readMultiByte(ba, clbuff, 13); // skip password dooberry
610 for (aa = 0; aa < f->mflib.num_files; aa++) {
611 ByteArray_readMultiByte(ba, f->mflib.filenames[aa], 13);
612 l = strlen(f->mflib.filenames[aa]);
613 for (cc = 0; cc < l; cc++)
614 f->mflib.filenames[aa][cc] -= passwmodifier;
616 for(cc = 0; cc < f->mflib.num_files; cc++)
617 f->mflib.length[cc] = ByteArray_readUnsignedInt(ba);
619 ByteArray_set_position_rel(ba, 2 * f->mflib.num_files); // skip flags & ratio
621 f->mflib.offset[0] = ByteArray_get_position(ba);
623 for (aa = 1; aa < f->mflib.num_files; aa++) {
624 f->mflib.offset[aa] = f->mflib.offset[aa - 1] + f->mflib.length[aa - 1];
625 f->mflib.file_datafile[aa] = 0;
627 f->mflib.file_datafile[0] = 0;
629 return prep_multifiles(f);
632 static int checkIndex(struct AgsFile *f, size_t index) {
633 if (index >= AgsFile_getFileCount(f)) return 0;
634 return 1;
637 static int checkDataIndex(struct AgsFile *f, size_t index) {
638 if (index >= AgsFile_getDataFileCount(f)) return 0;
639 return 1;
642 size_t AgsFile_getFileCount(struct AgsFile *f) {
643 return f->mflib.num_files;
646 size_t AgsFile_getDataFileCount(struct AgsFile *f) {
647 return f->mflib.num_data_files;
650 int AgsFile_getFileNumber(struct AgsFile *f, size_t index) {
651 if (!checkIndex(f, index)) return -1;
652 return f->mflib.file_datafile[index];
655 void AgsFile_setFileNumber(struct AgsFile *f, size_t index, int number) {
656 *(unsigned char*)(&f->mflib.file_datafile[index]) = number;
659 char *AgsFile_getFileName(struct AgsFile *f, size_t index) {
660 if (!checkIndex(f, index)) return 0;
661 return f->mflib.filenames[index];
664 char *AgsFile_getDataFileName(struct AgsFile *f, size_t index) {
665 if (!checkDataIndex(f, index)) return 0;
666 return f->mflib.data_filenames[index];
669 size_t AgsFile_getFileSize(struct AgsFile *f, size_t index) {
670 if (!checkIndex(f, index)) return 0;
671 return f->mflib.length[index];
674 size_t AgsFile_getOffset(struct AgsFile *f, size_t index) {
675 if (!checkIndex(f, index)) return 0;
676 return f->mflib.offset[index];
679 static int AgsFile_seek(struct AgsFile *f, int multifileno, off_t pos) {
680 return ByteArray_set_position(&f->f[multifileno], pos);
683 static ssize_t AgsFile_read(struct AgsFile *f, int multifileno, void* buf, size_t count) {
684 return ByteArray_readMultiByte(&f->f[multifileno], buf, count);
687 int AgsFile_extract(struct AgsFile* f, int multifileno, off_t start, size_t len, const char* outfn) {
688 FILE *fo = fopen(outfn, "wb");
689 if(fo == 0) return 0;
690 char buf[4096];
691 size_t written = 0, l = len;
692 off_t save_pos = ByteArray_get_position(&f->f[multifileno]);
693 AgsFile_seek(f, multifileno, start);
694 while(written < l) {
695 size_t togo = l - written;
696 if(togo > sizeof(buf)) togo = sizeof(buf);
697 if(togo == 0) break;
698 ssize_t ret = AgsFile_read(f, multifileno, buf, togo);
699 if(ret <= 0) break;
700 ret = fwrite(buf, 1, togo, fo);
701 if(ret != togo) {
702 fprintf(stderr, "short write\n");
703 break;
705 written += togo;
707 fclose(fo);
708 AgsFile_seek(f, multifileno, save_pos);
709 return written == l;
712 int AgsFile_dump(struct AgsFile* f, size_t index, const char* outfn) {
713 if (!checkIndex(f, index)) return 0;
714 return AgsFile_extract(f, AgsFile_getFileNumber(f, index), AgsFile_getOffset(f, index), AgsFile_getFileSize(f, index), outfn);
717 void AgsFile_init(struct AgsFile *buf, char* filename) {
718 memset(buf, 0, sizeof *buf);
719 buf->fn = filename;
722 int AgsFile_open(struct AgsFile *buf) {
723 int ret = csetlib(buf, buf->fn);
725 return ret == 0;