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"
39 #define RAND_SEED_SALT 9338638
41 static char *clibendfilesig
= "CLIB\x1\x2\x3\x4SIGE";
42 static char *clibpasswencstring
= "My\x1\xde\x4Jibzle";
43 static int _last_rand
;
45 static void init_pseudo_rand_gen(int seed
) {
49 static int get_pseudo_rand() {
50 return( ((_last_rand
= _last_rand
* 214013L
51 + 2531011L) >> 16) & 0x7fff );
54 static void clib_encrypt_text(unsigned char *clear
, unsigned char *encbuf
) {
57 *encbuf
= *clear
+ clibpasswencstring
[adx
];
59 adx
++; encbuf
++; clear
++;
60 if (adx
> 10) adx
= 0;
64 static void clib_decrypt_text(char *toenc
) {
67 *toenc
-= clibpasswencstring
[adx
];
70 if (adx
> 10) adx
= 0;
74 static void fgetnulltermstring(char *sss
, struct ByteArray
*ddd
, int bufsize
) {
76 off_t l
= ByteArray_get_length(ddd
);
78 if (b
< bufsize
- 1) b
++;
79 if(ByteArray_get_position(ddd
) >= l
)
81 sss
[b
] = ByteArray_readByte(ddd
);
82 } while (sss
[b
] != 0);
85 long last_opened_size
;
87 static int fread_data_enc_byte(struct ByteArray
*ba
) {
88 return ByteArray_readUnsignedByte(ba
) - get_pseudo_rand();
91 static uint32_t fread_data_enc_int(struct ByteArray
*ba
) {
96 res
.i
= ByteArray_readUnsignedInt(ba
);
99 res
.c
[i
] -= get_pseudo_rand();
103 static void fread_data_intarray_enc(struct ByteArray
*ba
, unsigned* dest
, size_t count
) {
105 for(; i
< count
; i
++)
106 dest
[i
] = fread_data_enc_int(ba
);
109 static void fread_data_intarray(struct ByteArray
*ba
, unsigned* dest
, size_t count
) {
111 for(; i
< count
; i
++)
112 dest
[i
] = ByteArray_readInt(ba
);
116 static void fread_data_enc(void *data
, size_t dataSize
, size_t dataCount
, struct ByteArray
*ooo
) {
117 ByteArray_readMultiByte(ooo
, (char*)data
, dataSize
* dataCount
);
118 unsigned char *dataChar
= (unsigned char*)data
;
120 for (; i
< dataSize
* dataCount
; i
++)
121 dataChar
[i
] -= get_pseudo_rand();
125 static void fgetstring_enc(char *sss
, struct ByteArray
*ooo
, int maxLength
) {
127 while ((i
== 0) || (sss
[i
- 1] != 0)) {
128 sss
[i
] = ByteArray_readByte(ooo
) - get_pseudo_rand();
129 if (i
< maxLength
- 1) i
++;
133 static int read_v30_clib(struct MultiFileLibNew
* mfl
, struct ByteArray
* wout
) {
135 /* int reservedFlags = */ ByteArray_readInt(wout
);
137 mfl
->num_data_files
= ByteArray_readInt(wout
);
138 assert(mfl
->num_data_files
<= MAXMULTIFILES
);
139 for (aa
= 0; aa
< mfl
->num_data_files
; aa
++)
140 fgetnulltermstring(mfl
->data_filenames
[aa
], wout
, 50);
141 mfl
->num_files
= ByteArray_readInt(wout
);
143 if (mfl
->num_files
> MAX_FILES
)
146 for (aa
= 0; aa
< mfl
->num_files
; aa
++) {
147 fgetnulltermstring(mfl
->filenames
[aa
], wout
, 100);
148 ByteArray_readUnsignedByte(wout
); /* "LibUid" */
149 unsigned long long tmp
;
150 tmp
= ByteArray_readUnsignedLongLong(wout
);
151 assert(tmp
< UINT_MAX
);
152 mfl
->offset
[aa
] = tmp
&0xffffffff;
153 tmp
= ByteArray_readUnsignedLongLong(wout
);
154 assert(tmp
< UINT_MAX
);
155 mfl
->length
[aa
] = tmp
&0xffffffff;
158 // for(aa = 0; aa < mfl->num_files; aa++)
159 // mfl->file_datafile[aa] = fread_data_enc_byte(wout);
163 static int getw_enc(struct ByteArray
*ooo
) {
164 return fread_data_enc_int(ooo
);
167 static int read_new_new_enc_format_clib(struct MultiFileLibNew
* mfl
, struct ByteArray
* wout
) {
169 int randSeed
= ByteArray_readInt(wout
);
170 init_pseudo_rand_gen(randSeed
+ RAND_SEED_SALT
);
171 mfl
->num_data_files
= getw_enc(wout
);
172 for (aa
= 0; aa
< mfl
->num_data_files
; aa
++)
173 fgetstring_enc(mfl
->data_filenames
[aa
], wout
, 50);
174 mfl
->num_files
= getw_enc(wout
);
176 if (mfl
->num_files
> MAX_FILES
)
179 for (aa
= 0; aa
< mfl
->num_files
; aa
++)
180 fgetstring_enc(mfl
->filenames
[aa
], wout
, 100);
182 fread_data_intarray_enc(wout
, mfl
->offset
, mfl
->num_files
);
183 fread_data_intarray_enc(wout
, mfl
->length
, mfl
->num_files
);
184 for(aa
= 0; aa
< mfl
->num_files
; aa
++)
185 mfl
->file_datafile
[aa
] = fread_data_enc_byte(wout
);
189 static int read_new_new_format_clib(struct MultiFileLibNew
* mfl
, struct ByteArray
* wout
) {
191 mfl
->num_data_files
= ByteArray_readInt(wout
);
192 for (aa
= 0; aa
< mfl
->num_data_files
; aa
++)
193 fgetnulltermstring(mfl
->data_filenames
[aa
], wout
, 50);
194 mfl
->num_files
= ByteArray_readInt(wout
);
196 if (mfl
->num_files
> MAX_FILES
) return -1;
198 for (aa
= 0; aa
< mfl
->num_files
; aa
++) {
199 short nameLength
= ByteArray_readShort(wout
);
201 ByteArray_readMultiByte(wout
, mfl
->filenames
[aa
], nameLength
);
202 clib_decrypt_text(mfl
->filenames
[aa
]);
204 fread_data_intarray(wout
, mfl
->offset
, mfl
->num_files
);
205 fread_data_intarray(wout
, mfl
->length
, mfl
->num_files
);
206 ByteArray_readMultiByte(wout
, mfl
->file_datafile
, mfl
->num_files
);
210 static int read_new_format_clib(struct MultiFileLib
* mfl
, struct ByteArray
* wout
, int libver
) {
211 mfl
->num_data_files
= ByteArray_readInt(wout
);
212 ByteArray_readMultiByte(wout
, (char*) mfl
->data_filenames
, 20U * mfl
->num_data_files
);
213 mfl
->num_files
= ByteArray_readInt(wout
);
215 if (mfl
->num_files
> MAX_FILES
) return -1;
217 ByteArray_readMultiByte(wout
, (char*) mfl
->filenames
, 25U * mfl
->num_files
);
219 fread_data_intarray(wout
, mfl
->offset
, mfl
->num_files
);
220 fread_data_intarray(wout
, mfl
->length
, mfl
->num_files
);
221 ByteArray_readMultiByte(wout
, mfl
->file_datafile
, mfl
->num_files
);
225 for (aa
= 0; aa
< mfl
->num_files
; aa
++)
226 clib_decrypt_text(mfl
->filenames
[aa
]);
231 void AgsFile_close(struct AgsFile
*f
) {
233 for (i
=0; i
< AgsFile_getDataFileCount(f
); i
++)
234 ByteArray_close_file(&f
->f
[i
]);
237 int AgsFile_getVersion(struct AgsFile
*f
) {
238 return f
->libversion
;
241 void AgsFile_setVersion(struct AgsFile
*f
, int version
) {
242 f
->libversion
= version
;
245 void AgsFile_setSourceDir(struct AgsFile
*f
, char* sourcedir
) {
249 void AgsFile_setFileCount(struct AgsFile
*f
, size_t count
) {
250 f
->mflib
.num_files
= count
;
253 static off_t
filelength(int fd
) {
259 int AgsFile_setFile(struct AgsFile
*f
, size_t index
, char* fn
) {
260 strncpy(f
->mflib
.filenames
[index
], fn
, 100);
262 snprintf(fnbuf
, sizeof(fnbuf
), "%s/%s", f
->dir
, f
->mflib
.filenames
[index
]);
263 int fd
= open(fnbuf
, O_RDONLY
);
264 if(fd
== -1) return 0;
265 off_t fl
= filelength(fd
);
267 f
->mflib
.length
[index
] = fl
;
271 int AgsFile_setDataFile(struct AgsFile
*f
, size_t index
, char* fn
) {
272 size_t l
= strlen(fn
);
273 if(l
>= 20) return 0;
274 strncpy(f
->mflib
.data_filenames
[index
], fn
, 20);
278 void AgsFile_setDataFileCount(struct AgsFile
*f
, size_t count
) {
279 f
->mflib
.num_data_files
= count
;
283 static int get_int_le(int val
) {
284 #ifdef IS_LITTLE_ENDIAN
287 return byteswap32(val
);
291 static void write_int(int fd
, int val
) {
292 int le
= get_int_le(val
);
293 write(fd
, &le
, sizeof(le
));
296 static short get_short_le(short val
) {
297 #ifdef IS_LITTLE_ENDIAN
300 return byteswap16(val
);
304 static void write_short(int fd
, short val
) {
305 short le
= get_short_le(val
);
306 write(fd
, &le
, sizeof(le
));
309 static void write_int_array(int fd
, int* arr
, size_t len
) {
312 write_int(fd
, arr
[i
]);
315 static int copy_into_file(int fd
, char *dir
, char *fn
, size_t filesize
) {
317 snprintf(fnbuf
, sizeof(fnbuf
), "%s/%s", dir
, fn
);
318 int f
= open(fnbuf
, O_RDONLY
);
319 if(f
== -1) return 0;
323 size_t togo
= filesize
> sizeof(readbuf
) ? sizeof(readbuf
) : filesize
;
324 if((size_t) read(f
, readbuf
, togo
) != togo
) { ret
= 0; goto end
; }
329 wret
= write(fd
, readbuf
+ nwritten
, togo
);
330 if(wret
<= 0) { ret
= 0; goto end
; }
340 #define WH_EWRITE(X,Y,Z) do {if(Z != write(X, Y, Z)) return 0;} while(0)
341 static size_t write_header(struct AgsFile
*f
, int fd
) {
342 int myversion
= 20; //f->libversion;
343 unsigned char version
= myversion
;
344 WH_EWRITE(fd
, "CLIB\x1a", 5);
345 WH_EWRITE(fd
, &version
, 1);
347 if(myversion
>= 10) WH_EWRITE(fd
, &version
, 1);
350 write_int(fd
, f
->mflib
.num_data_files
);
351 written
+= sizeof(int);
352 for(i
= 0; i
< f
->mflib
.num_data_files
; i
++) {
353 l
= strlen(f
->mflib
.data_filenames
[i
]) + 1;
355 if(l
!= (size_t) write(fd
, f
->mflib
.data_filenames
[i
], l
))
359 write_int(fd
, f
->mflib
.num_files
);
360 written
+= sizeof(int);
361 unsigned char encbuf
[100];
362 for(i
= 0; i
< f
->mflib
.num_files
; i
++) {
363 l
= strlen(f
->mflib
.filenames
[i
]) + 1;
364 write_short(fd
, l
* 5);
365 clib_encrypt_text((unsigned char*)f
->mflib
.filenames
[i
], encbuf
);
366 if(l
!= (size_t) write(fd
, encbuf
, l
)) return 0;
367 written
+= sizeof(short) + l
;
369 l
= f
->mflib
.num_files
;
370 write_int_array(fd
, (int*) f
->mflib
.offset
, l
);
371 written
+= sizeof(int) * l
;
372 write_int_array(fd
, (int*) f
->mflib
.length
, l
);
373 written
+= sizeof(int) * l
;
374 if(l
!= (size_t) write(fd
, f
->mflib
.file_datafile
, l
))
380 static void write_footer(int fd
) {
382 write(fd
, clibendfilesig
, 12);
386 int AgsFile_write(struct AgsFile
*f
) {
387 int fd
= open(f
->fn
, O_CREAT
| O_WRONLY
| O_TRUNC
, 0660);
388 if(fd
== -1) return 0;
390 if(!(off
= write_header(f
, fd
))) return 0;
391 lseek(fd
, 0, SEEK_SET
);
392 for(i
= 0; i
< f
->mflib
.num_files
; i
++) {
393 f
->mflib
.offset
[i
] = off
;
394 off
+= f
->mflib
.length
[i
];
397 for(i
= 0; i
< f
->mflib
.num_files
; i
++) {
398 if(!copy_into_file(fd
, f
->dir
, f
->mflib
.filenames
[i
], f
->mflib
.length
[i
]))
406 static int prep_multifiles(struct AgsFile
*f
) {
408 struct ByteArray
*ba
;
409 /* open each datafile as a byte array */
410 assert(MAXMULTIFILES
>= f
->mflib
.num_data_files
);
411 for (aa
= 1; aa
< f
->mflib
.num_data_files
; aa
++) {
414 if(!ByteArray_open_file(ba
, f
->mflib
.data_filenames
[aa
])) return -1;
415 ByteArray_set_endian(ba
, BAE_LITTLE
); // all ints etc are saved in little endian.
420 static int csetlib(struct AgsFile
* f
, char *filename
) {
425 int passwmodifier
= 0;
429 struct ByteArray
*ba
= &f
->f
[0];
431 if(!ByteArray_open_file(ba
, filename
)) return -1;
432 ByteArray_set_endian(ba
, BAE_LITTLE
); // all ints etc are saved in little endian.
433 off_t ba_len
= ByteArray_get_length(ba
);
434 ByteArray_readMultiByte(ba
, clbuff
, 5);
436 uint32_t absoffs
= 0; /* we read 4 bytes- so our datatype
437 must be 4 bytes as well, since we use a pointer to it */
440 if (strncmp(clbuff
, "CLIB", 4) != 0) {
441 ByteArray_set_position(ba
, ba_len
- 12);
442 ByteArray_readMultiByte(ba
, clbuff
, 12);
444 if (strncmp(clbuff
, clibendfilesig
, 12) != 0)
446 // it's an appended-to-end-of-exe thing
447 ByteArray_set_position(ba
, ba_len
- 16);
448 absoffs
= ByteArray_readUnsignedInt(ba
);
449 ByteArray_set_position(ba
, absoffs
+ 5);
450 f
->pack_off
= absoffs
;
453 f
->libversion
= ByteArray_readUnsignedByte(ba
);
454 switch (f
->libversion
) {
455 /* enum MFLVersion (kMFLVersion_MultiV21 ...) in newer AGS */
456 case 6: case 10: case 11: case 15: case 20: case 21: case 30:
459 // unsupported version
463 // remove slashes so that the lib name fits in the buffer
464 while (filename
[0] == '\\' || filename
[0] == '/') filename
++;
466 if (f
->libversion
>= 10) {
467 if (ByteArray_readUnsignedByte(ba
) != 0)
468 return -4; // not first datafile in chain
470 if (f
->libversion
>= 30) {
471 if (read_v30_clib(&f
->mflib
, ba
)) return -5;
472 } else if (f
->libversion
>= 21) {
473 if (read_new_new_enc_format_clib(&f
->mflib
, ba
))
475 } else if (f
->libversion
== 20) {
476 if (read_new_new_format_clib(&f
->mflib
, ba
))
479 struct MultiFileLib mflibOld_b
, *mflibOld
= &mflibOld_b
;
481 if (read_new_format_clib(mflibOld
, ba
, f
->libversion
))
483 // convert to newer format
484 f
->mflib
.num_files
= mflibOld
->num_files
;
485 f
->mflib
.num_data_files
= mflibOld
->num_data_files
;
486 memcpy(f
->mflib
.offset
, mflibOld
->offset
, sizeof(int) * f
->mflib
.num_files
);
487 memcpy(f
->mflib
.length
, mflibOld
->length
, sizeof(int) * f
->mflib
.num_files
);
488 memcpy(f
->mflib
.file_datafile
, mflibOld
->file_datafile
, sizeof(char) * f
->mflib
.num_files
);
489 assert(MAXMULTIFILES
>= f
->mflib
.num_data_files
);
490 for (aa
= 0; aa
< f
->mflib
.num_data_files
; aa
++)
491 strcpy(f
->mflib
.data_filenames
[aa
], mflibOld
->data_filenames
[aa
]);
492 for (aa
= 0; aa
< f
->mflib
.num_files
; aa
++)
493 strcpy(f
->mflib
.filenames
[aa
], mflibOld
->filenames
[aa
]);
496 strcpy(f
->mflib
.data_filenames
[0], filename
);
497 for (aa
= 0; aa
< f
->mflib
.num_files
; aa
++) {
498 // correct offsetes for EXE file
499 if (f
->mflib
.file_datafile
[aa
] == 0)
500 f
->mflib
.offset
[aa
] += absoffs
;
502 return prep_multifiles(f
);
505 passwmodifier
= ByteArray_readUnsignedByte(ba
);
506 ByteArray_readUnsignedByte(ba
); // unused byte
507 f
->mflib
.num_data_files
= 1;
508 strcpy(f
->mflib
.data_filenames
[0], filename
);
510 short tempshort
= ByteArray_readShort(ba
);
511 f
->mflib
.num_files
= tempshort
;
513 if (f
->mflib
.num_files
> MAX_FILES
) return -4;
515 ByteArray_readMultiByte(ba
, clbuff
, 13); // skip password dooberry
516 for (aa
= 0; aa
< f
->mflib
.num_files
; aa
++) {
517 ByteArray_readMultiByte(ba
, f
->mflib
.filenames
[aa
], 13);
518 l
= strlen(f
->mflib
.filenames
[aa
]);
519 for (cc
= 0; cc
< l
; cc
++)
520 f
->mflib
.filenames
[aa
][cc
] -= passwmodifier
;
522 for(cc
= 0; cc
< f
->mflib
.num_files
; cc
++)
523 f
->mflib
.length
[cc
] = ByteArray_readUnsignedInt(ba
);
525 ByteArray_set_position_rel(ba
, 2 * f
->mflib
.num_files
); // skip flags & ratio
527 f
->mflib
.offset
[0] = ByteArray_get_position(ba
);
529 for (aa
= 1; aa
< f
->mflib
.num_files
; aa
++) {
530 f
->mflib
.offset
[aa
] = f
->mflib
.offset
[aa
- 1] + f
->mflib
.length
[aa
- 1];
531 f
->mflib
.file_datafile
[aa
] = 0;
533 f
->mflib
.file_datafile
[0] = 0;
535 return prep_multifiles(f
);
538 static int checkIndex(struct AgsFile
*f
, size_t index
) {
539 if (index
>= AgsFile_getFileCount(f
)) return 0;
543 static int checkDataIndex(struct AgsFile
*f
, size_t index
) {
544 if (index
>= AgsFile_getDataFileCount(f
)) return 0;
548 size_t AgsFile_getFileCount(struct AgsFile
*f
) {
549 return f
->mflib
.num_files
;
552 size_t AgsFile_getDataFileCount(struct AgsFile
*f
) {
553 return f
->mflib
.num_data_files
;
556 int AgsFile_getFileNumber(struct AgsFile
*f
, size_t index
) {
557 if (!checkIndex(f
, index
)) return -1;
558 return f
->mflib
.file_datafile
[index
];
561 void AgsFile_setFileNumber(struct AgsFile
*f
, size_t index
, int number
) {
562 *(unsigned char*)(&f
->mflib
.file_datafile
[index
]) = number
;
565 char *AgsFile_getFileName(struct AgsFile
*f
, size_t index
) {
566 if (!checkIndex(f
, index
)) return 0;
567 return f
->mflib
.filenames
[index
];
570 char *AgsFile_getDataFileName(struct AgsFile
*f
, size_t index
) {
571 if (!checkDataIndex(f
, index
)) return 0;
572 return f
->mflib
.data_filenames
[index
];
575 size_t AgsFile_getFileSize(struct AgsFile
*f
, size_t index
) {
576 if (!checkIndex(f
, index
)) return 0;
577 return f
->mflib
.length
[index
];
580 size_t AgsFile_getOffset(struct AgsFile
*f
, size_t index
) {
581 if (!checkIndex(f
, index
)) return 0;
582 return f
->mflib
.offset
[index
];
585 static int AgsFile_seek(struct AgsFile
*f
, int multifileno
, off_t pos
) {
586 return ByteArray_set_position(&f
->f
[multifileno
], pos
);
589 static ssize_t
AgsFile_read(struct AgsFile
*f
, int multifileno
, void* buf
, size_t count
) {
590 return ByteArray_readMultiByte(&f
->f
[multifileno
], buf
, count
);
593 int AgsFile_extract(struct AgsFile
* f
, int multifileno
, off_t start
, size_t len
, const char* outfn
) {
594 int fd
= open(outfn
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0660);
595 if(fd
== -1) return 0;
597 size_t written
= 0, l
= len
;
598 off_t save_pos
= ByteArray_get_position(&f
->f
[multifileno
]);
599 AgsFile_seek(f
, multifileno
, start
);
601 size_t togo
= l
- written
;
602 if(togo
> sizeof(buf
)) togo
= sizeof(buf
);
604 ssize_t ret
= AgsFile_read(f
, multifileno
, buf
, togo
);
606 ret
= write(fd
, buf
, togo
);
610 } else if(ret
!= togo
) {
611 dprintf(2, "short write\n");
617 AgsFile_seek(f
, multifileno
, save_pos
);
621 int AgsFile_dump(struct AgsFile
* f
, size_t index
, const char* outfn
) {
622 if (!checkIndex(f
, index
)) return 0;
623 return AgsFile_extract(f
, AgsFile_getFileNumber(f
, index
), AgsFile_getOffset(f
, index
), AgsFile_getFileSize(f
, index
), outfn
);
626 void AgsFile_init(struct AgsFile
*buf
, char* filename
) {
630 int AgsFile_open(struct AgsFile
*buf
) {
631 int ret
= csetlib(buf
, buf
->fn
);