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.
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.
28 #include "ByteArray.h"
30 #include <sys/types.h>
37 #include "endianness.h"
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
) {
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
) {
65 *encbuf
= *clear
+ clibpasswencstring
[adx
];
67 adx
++; encbuf
++; clear
++;
68 if (adx
> 10) adx
= 0;
72 static void clib_decrypt_text(char *toenc
) {
75 *toenc
-= clibpasswencstring
[adx
];
78 if (adx
> 10) adx
= 0;
82 static void fgetnulltermstring(char *sss
, struct ByteArray
*ddd
, int bufsize
) {
84 off_t l
= ByteArray_get_length(ddd
);
86 if (b
< bufsize
- 1) b
++;
87 if(ByteArray_get_position(ddd
) >= l
)
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
) {
104 res
.i
= ByteArray_readUnsignedInt(ba
);
107 res
.c
[i
] -= get_pseudo_rand();
111 static void fread_data_intarray_enc(struct ByteArray
*ba
, unsigned* dest
, size_t count
) {
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
) {
119 for(; i
< count
; i
++)
120 dest
[i
] = ByteArray_readInt(ba
);
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
;
128 for (; i
< dataSize
* dataCount
; i
++)
129 dataChar
[i
] -= get_pseudo_rand();
133 static void fgetstring_enc(char *sss
, struct ByteArray
*ooo
, int maxLength
) {
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
) {
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
)
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);
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
) {
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
)
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
);
196 static int read_new_new_format_clib(struct MultiFileLibNew
* mfl
, struct ByteArray
* wout
) {
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
);
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
);
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
);
234 for (aa
= 0; aa
< mfl
->num_files
; aa
++)
235 clib_decrypt_text(mfl
->filenames
[aa
]);
240 void AgsFile_close(struct AgsFile
*f
) {
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
) {
258 void AgsFile_setFileCount(struct AgsFile
*f
, size_t count
) {
259 f
->mflib
.num_files
= count
;
262 static off_t
filelength(int fd
) {
268 int AgsFile_setFile(struct AgsFile
*f
, size_t index
, char* fn
) {
269 strncpy(f
->mflib
.filenames
[index
], fn
, 100);
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
);
276 f
->mflib
.length
[index
] = fl
;
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);
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
) {
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
) {
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
;
329 if(filesize
== -1L) {
332 ssize_t r
= read(f
, readbuf
, sizeof readbuf
);
333 if(r
< 0) { ret
=0; goto end
; }
336 if(write(fd
, readbuf
, r
) != r
) { ret
=0; goto end
; }
339 size_t togo
= filesize
> sizeof(readbuf
) ? sizeof(readbuf
) : filesize
;
340 if((size_t) read(f
, readbuf
, togo
) != togo
) { ret
= 0; goto end
; }
345 wret
= write(fd
, readbuf
+ nwritten
, togo
);
346 if(wret
<= 0) { ret
= 0; goto end
; }
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);
363 if(myversion
>= 10) WH_EWRITE(fd
, &version
, 1);
366 if(myversion
== 30) {
367 write_int(fd
, 0); /*reservedFlags*/
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;
375 if(l
!= (size_t) write(fd
, f
->mflib
.data_filenames
[i
], l
))
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;
386 if(l
!= (size_t) write(fd
, f
->mflib
.filenames
[i
], l
))
388 /* write another 0 byte as libuid */
389 WH_EWRITE(fd
, &version
, 1);
392 write_ull(fd
, f
->mflib
.offset
[i
]);
395 write_ull(fd
, f
->mflib
.length
[i
]);
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
))
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
) {
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;
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 */
447 size_t stubsize
= -1L;
448 if(!copy_into_file(fd
, f
->dir
, f
->exestub_fn
, &stubsize
))
450 header_offset
= stubsize
;
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
))
458 write_footer(fd
, header_offset
, f
->libversion
>= 30);
463 static int prep_multifiles(struct AgsFile
*f
) {
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
++) {
470 snprintf(fntmp
, sizeof fntmp
, "%s%s%s",
471 f
->dir
? f
->dir
: "",
472 f
->dir
? PSEP_STR
: "",
473 f
->mflib
.data_filenames
[aa
]);
476 if(!ByteArray_open_file(ba
, fntmp
)) {
477 fprintf(stderr
, "error opening mfl datafile %s\n", fntmp
);
480 ByteArray_set_endian(ba
, BAE_LITTLE
); // all ints etc are saved in little endian.
485 static int csetlib(struct AgsFile
* f
, char *filename
) {
490 int passwmodifier
= 0;
494 struct ByteArray
*ba
= &f
->f
[0];
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 */
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
509 ByteArray_set_position(ba
, ba_len
- 12);
510 ByteArray_readMultiByte(ba
, clbuff
, 12);
512 if (strncmp(clbuff
, clibendfilesig
, 12) != 0)
514 // it's an appended-to-end-of-exe thing.
515 // there's a new format using a 64bit offset, we need to try
519 ByteArray_set_position(ba
, ba_len
- 20);
520 off64
= ByteArray_readUnsignedLongLong(ba
);
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");
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");
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:
551 // unsupported version
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
))
567 } else if (f
->libversion
== 20) {
568 if (read_new_new_format_clib(&f
->mflib
, ba
))
571 struct MultiFileLib mflibOld_b
, *mflibOld
= &mflibOld_b
;
573 if (read_new_format_clib(mflibOld
, ba
, f
->libversion
))
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;
637 static int checkDataIndex(struct AgsFile
*f
, size_t index
) {
638 if (index
>= AgsFile_getDataFileCount(f
)) return 0;
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;
691 size_t written
= 0, l
= len
;
692 off_t save_pos
= ByteArray_get_position(&f
->f
[multifileno
]);
693 AgsFile_seek(f
, multifileno
, start
);
695 size_t togo
= l
- written
;
696 if(togo
> sizeof(buf
)) togo
= sizeof(buf
);
698 ssize_t ret
= AgsFile_read(f
, multifileno
, buf
, togo
);
700 ret
= fwrite(buf
, 1, togo
, fo
);
702 fprintf(stderr
, "short write\n");
708 AgsFile_seek(f
, multifileno
, save_pos
);
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
);
722 int AgsFile_open(struct AgsFile
*buf
) {
723 int ret
= csetlib(buf
, buf
->fn
);