Makefile: don't link all objs to all programs
[rofl0r-agsutils.git] / Clib32.c
blobb29971f6ee0afb5f7b68fe6dc1848ef237fc171e
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 #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) {
46 _last_rand = 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) {
55 unsigned adx = 0;
56 while(1) {
57 *encbuf = *clear + clibpasswencstring[adx];
58 if(!*clear) break;
59 adx++; encbuf++; clear++;
60 if (adx > 10) adx = 0;
64 static void clib_decrypt_text(char *toenc) {
65 unsigned adx = 0;
66 while (1) {
67 *toenc -= clibpasswencstring[adx];
68 if (!*toenc) break;
69 adx++; toenc++;
70 if (adx > 10) adx = 0;
74 static void fgetnulltermstring(char *sss, struct ByteArray *ddd, int bufsize) {
75 int b = -1;
76 off_t l = ByteArray_get_length(ddd);
77 do {
78 if (b < bufsize - 1) b++;
79 if(ByteArray_get_position(ddd) >= l)
80 return;
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) {
92 union {
93 uint32_t i;
94 unsigned char c[4];
95 } res;
96 res.i = ByteArray_readUnsignedInt(ba);
97 size_t i = 0;
98 for(; i < 4; i++)
99 res.c[i] -= get_pseudo_rand();
100 return res.i;
103 static void fread_data_intarray_enc(struct ByteArray *ba, unsigned* dest, size_t count) {
104 size_t i = 0;
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) {
110 size_t i = 0;
111 for(; i < count; i++)
112 dest[i] = ByteArray_readInt(ba);
115 #if 0
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;
119 size_t i = 0;
120 for (; i < dataSize * dataCount; i++)
121 dataChar[i] -= get_pseudo_rand();
123 #endif
125 static void fgetstring_enc(char *sss, struct ByteArray *ooo, int maxLength) {
126 int i = 0;
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) {
134 size_t aa;
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)
144 return -1;
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);
160 return 0;
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) {
168 size_t aa;
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)
177 return -1;
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);
186 return 0;
189 static int read_new_new_format_clib(struct MultiFileLibNew* mfl, struct ByteArray * wout) {
190 size_t aa;
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);
200 nameLength /= 5;
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);
207 return 0;
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);
223 if (libver >= 11) {
224 size_t aa;
225 for (aa = 0; aa < mfl->num_files; aa++)
226 clib_decrypt_text(mfl->filenames[aa]);
228 return 0;
231 void AgsFile_close(struct AgsFile *f) {
232 unsigned i;
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) {
246 f->dir = sourcedir;
249 void AgsFile_setFileCount(struct AgsFile *f, size_t count) {
250 f->mflib.num_files = count;
253 static off_t filelength(int fd) {
254 struct stat st;
255 fstat(fd, &st);
256 return st.st_size;
259 int AgsFile_setFile(struct AgsFile *f, size_t index, char* fn) {
260 strncpy(f->mflib.filenames[index], fn, 100);
261 char fnbuf[512];
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);
266 close(fd);
267 f->mflib.length[index] = fl;
268 return 1;
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);
275 return 1;
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
285 return val;
286 #else
287 return byteswap32(val);
288 #endif
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
298 return val;
299 #else
300 return byteswap16(val);
301 #endif
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) {
310 size_t i = 0;
311 for(; i < len; i++)
312 write_int(fd, arr[i]);
315 static int copy_into_file(int fd, char *dir, char *fn, size_t filesize) {
316 char fnbuf[512];
317 snprintf(fnbuf, sizeof(fnbuf), "%s/%s", dir, fn);
318 int f = open(fnbuf, O_RDONLY);
319 if(f == -1) return 0;
320 char readbuf[4096];
321 int ret = 1;
322 do {
323 size_t togo = filesize > sizeof(readbuf) ? sizeof(readbuf) : filesize;
324 if((size_t) read(f, readbuf, togo) != togo) { ret = 0; goto end; }
325 filesize -= togo;
326 ssize_t wret;
327 size_t nwritten = 0;
328 while(togo) {
329 wret = write(fd, readbuf + nwritten, togo);
330 if(wret <= 0) { ret = 0; goto end; }
331 nwritten += wret;
332 togo -= wret;
334 } while(filesize);
335 end:
336 close(f);
337 return ret;
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);
346 version = 0;
347 if(myversion >= 10) WH_EWRITE(fd, &version, 1);
348 size_t written = 7;
349 size_t i,l;
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;
354 written += l;
355 if(l != (size_t) write(fd, f->mflib.data_filenames[i], l))
356 return 0;
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))
375 return 0;
376 written += l;
377 return written;
380 static void write_footer(int fd) {
381 write_int(fd, 0);
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;
389 size_t i, off;
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];
396 write_header(f, fd);
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]))
399 return 0;
401 write_footer(fd);
402 close(fd);
403 return 1;
406 static int prep_multifiles(struct AgsFile *f) {
407 unsigned aa;
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++) {
412 ba = &f->f[aa];
413 ByteArray_ctor(ba);
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.
417 return 0;
420 static int csetlib(struct AgsFile* f, char *filename) {
421 char clbuff[20];
422 if (!filename)
423 return -11;
425 int passwmodifier = 0;
426 size_t aa;
427 size_t cc, l;
429 struct ByteArray *ba = &f->f[0];
430 ByteArray_ctor(ba);
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 */
439 f->pack_off = 0;
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)
445 return -2;
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:
457 break;
458 default:
459 // unsupported version
460 return -3;
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))
474 return -5;
475 } else if (f->libversion == 20) {
476 if (read_new_new_format_clib(&f->mflib, ba))
477 return -5;
478 } else {
479 struct MultiFileLib mflibOld_b, *mflibOld = &mflibOld_b;
481 if (read_new_format_clib(mflibOld, ba, f->libversion))
482 return -5;
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;
540 return 1;
543 static int checkDataIndex(struct AgsFile *f, size_t index) {
544 if (index >= AgsFile_getDataFileCount(f)) return 0;
545 return 1;
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;
596 char buf[4096];
597 size_t written = 0, l = len;
598 off_t save_pos = ByteArray_get_position(&f->f[multifileno]);
599 AgsFile_seek(f, multifileno, start);
600 while(written < l) {
601 size_t togo = l - written;
602 if(togo > sizeof(buf)) togo = sizeof(buf);
603 if(togo == 0) break;
604 ssize_t ret = AgsFile_read(f, multifileno, buf, togo);
605 if(ret <= 0) break;
606 ret = write(fd, buf, togo);
607 if(ret == -1) {
608 perror("write");
609 break;
610 } else if(ret != togo) {
611 dprintf(2, "short write\n");
612 break;
614 written += togo;
616 close(fd);
617 AgsFile_seek(f, multifileno, save_pos);
618 return written == l;
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) {
627 buf->fn = filename;
630 int AgsFile_open(struct AgsFile *buf) {
631 int ret = csetlib(buf, buf->fn);
633 return ret == 0;