improve agssemble performance 3x
[rofl0r-agsutils.git] / Clib32.c
blob7b60d128e0a2ffcc8f5777c78e9b9d6d9e06ab69
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 "Clib32.h"
36 #include "endianness.h"
38 #define RAND_SEED_SALT 9338638
40 static char *clibendfilesig = "CLIB\x1\x2\x3\x4SIGE";
41 static char *clibpasswencstring = "My\x1\xde\x4Jibzle";
42 static int _last_rand;
44 static void init_pseudo_rand_gen(int seed) {
45 _last_rand = seed;
48 static int get_pseudo_rand() {
49 return( ((_last_rand = _last_rand * 214013L
50 + 2531011L) >> 16) & 0x7fff );
53 static void clib_encrypt_text(unsigned char *clear, unsigned char *encbuf) {
54 unsigned adx = 0;
55 while(1) {
56 *encbuf = *clear + clibpasswencstring[adx];
57 if(!*clear) break;
58 adx++; encbuf++; clear++;
59 if (adx > 10) adx = 0;
63 static void clib_decrypt_text(char *toenc) {
64 unsigned adx = 0;
65 while (1) {
66 *toenc -= clibpasswencstring[adx];
67 if (!*toenc) break;
68 adx++; toenc++;
69 if (adx > 10) adx = 0;
73 static void fgetnulltermstring(char *sss, struct ByteArray *ddd, int bufsize) {
74 int b = -1;
75 off_t l = ByteArray_get_length(ddd);
76 do {
77 if (b < bufsize - 1) b++;
78 if(ByteArray_get_position(ddd) >= l)
79 return;
80 sss[b] = ByteArray_readByte(ddd);
81 } while (sss[b] != 0);
84 long last_opened_size;
86 static int fread_data_enc_byte(struct ByteArray *ba) {
87 return ByteArray_readUnsignedByte(ba) - get_pseudo_rand();
90 static uint32_t fread_data_enc_int(struct ByteArray *ba) {
91 union {
92 uint32_t i;
93 unsigned char c[4];
94 } res;
95 res.i = ByteArray_readUnsignedInt(ba);
96 size_t i = 0;
97 for(; i < 4; i++)
98 res.c[i] -= get_pseudo_rand();
99 return res.i;
102 static void fread_data_intarray_enc(struct ByteArray *ba, unsigned* dest, size_t count) {
103 size_t i = 0;
104 for(; i < count; i++)
105 dest[i] = fread_data_enc_int(ba);
108 static void fread_data_intarray(struct ByteArray *ba, unsigned* dest, size_t count) {
109 size_t i = 0;
110 for(; i < count; i++)
111 dest[i] = ByteArray_readInt(ba);
114 #if 0
115 static void fread_data_enc(void *data, size_t dataSize, size_t dataCount, struct ByteArray *ooo) {
116 ByteArray_readMultiByte(ooo, (char*)data, dataSize * dataCount);
117 unsigned char *dataChar = (unsigned char*)data;
118 size_t i = 0;
119 for (; i < dataSize * dataCount; i++)
120 dataChar[i] -= get_pseudo_rand();
122 #endif
124 static void fgetstring_enc(char *sss, struct ByteArray *ooo, int maxLength) {
125 int i = 0;
126 while ((i == 0) || (sss[i - 1] != 0)) {
127 sss[i] = ByteArray_readByte(ooo) - get_pseudo_rand();
128 if (i < maxLength - 1) i++;
132 static int getw_enc(struct ByteArray *ooo) {
133 return fread_data_enc_int(ooo);
136 static int read_new_new_enc_format_clib(struct MultiFileLibNew * mfl, struct ByteArray * wout) {
137 size_t aa;
138 int randSeed = ByteArray_readInt(wout);
139 init_pseudo_rand_gen(randSeed + RAND_SEED_SALT);
140 mfl->num_data_files = getw_enc(wout);
141 for (aa = 0; aa < mfl->num_data_files; aa++)
142 fgetstring_enc(mfl->data_filenames[aa], wout, 50);
143 mfl->num_files = getw_enc(wout);
145 if (mfl->num_files > MAX_FILES)
146 return -1;
148 for (aa = 0; aa < mfl->num_files; aa++)
149 fgetstring_enc(mfl->filenames[aa], wout, 100);
151 fread_data_intarray_enc(wout, mfl->offset, mfl->num_files);
152 fread_data_intarray_enc(wout, mfl->length, mfl->num_files);
153 for(aa = 0; aa < mfl->num_files; aa++)
154 mfl->file_datafile[aa] = fread_data_enc_byte(wout);
155 return 0;
158 static int read_new_new_format_clib(struct MultiFileLibNew* mfl, struct ByteArray * wout) {
159 size_t aa;
160 mfl->num_data_files = ByteArray_readInt(wout);
161 for (aa = 0; aa < mfl->num_data_files; aa++)
162 fgetnulltermstring(mfl->data_filenames[aa], wout, 50);
163 mfl->num_files = ByteArray_readInt(wout);
165 if (mfl->num_files > MAX_FILES) return -1;
167 for (aa = 0; aa < mfl->num_files; aa++) {
168 short nameLength = ByteArray_readShort(wout);
169 nameLength /= 5;
170 ByteArray_readMultiByte(wout, mfl->filenames[aa], nameLength);
171 clib_decrypt_text(mfl->filenames[aa]);
173 fread_data_intarray(wout, mfl->offset, mfl->num_files);
174 fread_data_intarray(wout, mfl->length, mfl->num_files);
175 ByteArray_readMultiByte(wout, mfl->file_datafile, mfl->num_files);
176 return 0;
179 static int read_new_format_clib(struct MultiFileLib * mfl, struct ByteArray * wout, int libver) {
180 mfl->num_data_files = ByteArray_readInt(wout);
181 ByteArray_readMultiByte(wout, (char*) mfl->data_filenames, 20U * mfl->num_data_files);
182 mfl->num_files = ByteArray_readInt(wout);
184 if (mfl->num_files > MAX_FILES) return -1;
186 ByteArray_readMultiByte(wout, (char*) mfl->filenames, 25U * mfl->num_files);
188 fread_data_intarray(wout, mfl->offset, mfl->num_files);
189 fread_data_intarray(wout, mfl->length, mfl->num_files);
190 ByteArray_readMultiByte(wout, mfl->file_datafile, mfl->num_files);
192 if (libver >= 11) {
193 size_t aa;
194 for (aa = 0; aa < mfl->num_files; aa++)
195 clib_decrypt_text(mfl->filenames[aa]);
197 return 0;
200 void AgsFile_close(struct AgsFile *f) {
201 unsigned i;
202 for (i=0; i < AgsFile_getDataFileCount(f); i++)
203 ByteArray_close_file(&f->f[i]);
206 int AgsFile_getVersion(struct AgsFile *f) {
207 return f->libversion;
210 void AgsFile_setVersion(struct AgsFile *f, int version) {
211 f->libversion = version;
214 void AgsFile_setSourceDir(struct AgsFile *f, char* sourcedir) {
215 f->dir = sourcedir;
218 void AgsFile_setFileCount(struct AgsFile *f, size_t count) {
219 f->mflib.num_files = count;
222 static off_t filelength(int fd) {
223 struct stat st;
224 fstat(fd, &st);
225 return st.st_size;
228 int AgsFile_setFile(struct AgsFile *f, size_t index, char* fn) {
229 strncpy(f->mflib.filenames[index], fn, 100);
230 char fnbuf[512];
231 snprintf(fnbuf, sizeof(fnbuf), "%s/%s", f->dir, f->mflib.filenames[index]);
232 int fd = open(fnbuf, O_RDONLY);
233 if(fd == -1) return 0;
234 off_t fl = filelength(fd);
235 close(fd);
236 f->mflib.length[index] = fl;
237 return 1;
240 int AgsFile_setDataFile(struct AgsFile *f, size_t index, char* fn) {
241 size_t l = strlen(fn);
242 if(l >= 20) return 0;
243 strncpy(f->mflib.data_filenames[index], fn, 20);
244 return 1;
247 void AgsFile_setDataFileCount(struct AgsFile *f, size_t count) {
248 f->mflib.num_data_files = count;
252 static int get_int_le(int val) {
253 #ifdef IS_LITTLE_ENDIAN
254 return val;
255 #else
256 return byteswap32(val);
257 #endif
260 static void write_int(int fd, int val) {
261 int le = get_int_le(val);
262 write(fd, &le, sizeof(le));
265 static short get_short_le(short val) {
266 #ifdef IS_LITTLE_ENDIAN
267 return val;
268 #else
269 return byteswap16(val);
270 #endif
273 static void write_short(int fd, short val) {
274 short le = get_short_le(val);
275 write(fd, &le, sizeof(le));
278 static void write_int_array(int fd, int* arr, size_t len) {
279 size_t i = 0;
280 for(; i < len; i++)
281 write_int(fd, arr[i]);
284 static int copy_into_file(int fd, char *dir, char *fn, size_t filesize) {
285 char fnbuf[512];
286 snprintf(fnbuf, sizeof(fnbuf), "%s/%s", dir, fn);
287 int f = open(fnbuf, O_RDONLY);
288 if(f == -1) return 0;
289 char readbuf[4096];
290 int ret = 1;
291 do {
292 size_t togo = filesize > sizeof(readbuf) ? sizeof(readbuf) : filesize;
293 if((size_t) read(f, readbuf, togo) != togo) { ret = 0; goto end; }
294 filesize -= togo;
295 ssize_t wret;
296 size_t nwritten = 0;
297 while(togo) {
298 wret = write(fd, readbuf + nwritten, togo);
299 if(wret <= 0) { ret = 0; goto end; }
300 nwritten += wret;
301 togo -= wret;
303 } while(filesize);
304 end:
305 close(f);
306 return ret;
309 #define WH_EWRITE(X,Y,Z) do {if(Z != write(X, Y, Z)) return 0;} while(0)
310 static size_t write_header(struct AgsFile *f, int fd) {
311 int myversion = 20; //f->libversion;
312 unsigned char version = myversion;
313 WH_EWRITE(fd, "CLIB\x1a", 5);
314 WH_EWRITE(fd, &version, 1);
315 version = 0;
316 if(myversion >= 10) WH_EWRITE(fd, &version, 1);
317 size_t written = 7;
318 size_t i,l;
319 write_int(fd, f->mflib.num_data_files);
320 written += sizeof(int);
321 for(i = 0; i < f->mflib.num_data_files; i++) {
322 l = strlen(f->mflib.data_filenames[i]) + 1;
323 written += l;
324 if(l != (size_t) write(fd, f->mflib.data_filenames[i], l))
325 return 0;
328 write_int(fd, f->mflib.num_files);
329 written += sizeof(int);
330 unsigned char encbuf[100];
331 for(i = 0; i < f->mflib.num_files; i++) {
332 l = strlen(f->mflib.filenames[i]) + 1;
333 write_short(fd, l * 5);
334 clib_encrypt_text((unsigned char*)f->mflib.filenames[i], encbuf);
335 if(l != (size_t) write(fd, encbuf, l)) return 0;
336 written += sizeof(short) + l;
338 l = f->mflib.num_files;
339 write_int_array(fd, (int*) f->mflib.offset, l);
340 written += sizeof(int) * l;
341 write_int_array(fd, (int*) f->mflib.length, l);
342 written += sizeof(int) * l;
343 if(l != (size_t) write(fd, f->mflib.file_datafile, l))
344 return 0;
345 written += l;
346 return written;
349 static void write_footer(int fd) {
350 write_int(fd, 0);
351 write(fd, clibendfilesig, 12);
355 int AgsFile_write(struct AgsFile *f) {
356 int fd = open(f->fn, O_CREAT | O_WRONLY | O_TRUNC, 0660);
357 if(fd == -1) return 0;
358 size_t i, off;
359 if(!(off = write_header(f, fd))) return 0;
360 lseek(fd, 0, SEEK_SET);
361 for(i = 0; i < f->mflib.num_files; i++) {
362 f->mflib.offset[i] = off;
363 off += f->mflib.length[i];
365 write_header(f, fd);
366 for(i = 0; i < f->mflib.num_files; i++) {
367 if(!copy_into_file(fd, f->dir, f->mflib.filenames[i], f->mflib.length[i]))
368 return 0;
370 write_footer(fd);
371 close(fd);
372 return 1;
375 static int prep_multifiles(struct AgsFile *f) {
376 unsigned aa;
377 struct ByteArray *ba;
378 /* open each datafile as a byte array */
379 assert(MAXMULTIFILES >= f->mflib.num_data_files);
380 for (aa = 1; aa < f->mflib.num_data_files; aa++) {
381 ba = &f->f[aa];
382 ByteArray_ctor(ba);
383 if(!ByteArray_open_file(ba, f->mflib.data_filenames[aa])) return -1;
384 ByteArray_set_endian(ba, BAE_LITTLE); // all ints etc are saved in little endian.
386 return 0;
389 static int csetlib(struct AgsFile* f, char *filename) {
390 char clbuff[20];
391 if (!filename)
392 return -11;
394 int passwmodifier = 0;
395 size_t aa;
396 size_t cc, l;
398 struct ByteArray *ba = &f->f[0];
399 ByteArray_ctor(ba);
400 if(!ByteArray_open_file(ba, filename)) return -1;
401 ByteArray_set_endian(ba, BAE_LITTLE); // all ints etc are saved in little endian.
402 off_t ba_len = ByteArray_get_length(ba);
403 ByteArray_readMultiByte(ba, clbuff, 5);
405 uint32_t absoffs = 0; /* we read 4 bytes- so our datatype
406 must be 4 bytes as well, since we use a pointer to it */
408 f->pack_off = 0;
409 if (strncmp(clbuff, "CLIB", 4) != 0) {
410 ByteArray_set_position(ba, ba_len - 12);
411 ByteArray_readMultiByte(ba, clbuff, 12);
413 if (strncmp(clbuff, clibendfilesig, 12) != 0)
414 return -2;
415 // it's an appended-to-end-of-exe thing
416 ByteArray_set_position(ba, ba_len - 16);
417 absoffs = ByteArray_readUnsignedInt(ba);
418 ByteArray_set_position(ba, absoffs + 5);
419 f->pack_off = absoffs;
422 f->libversion = ByteArray_readUnsignedByte(ba);
423 switch (f->libversion) {
424 /* enum MFLVersion (kMFLVersion_MultiV21 ...) in newer AGS */
425 case 6: case 10: case 11: case 15: case 20: case 21:
426 break;
427 default:
428 // unsupported version
429 return -3;
432 // remove slashes so that the lib name fits in the buffer
433 while (filename[0] == '\\' || filename[0] == '/') filename++;
435 if (f->libversion >= 10) {
436 if (ByteArray_readUnsignedByte(ba) != 0)
437 return -4; // not first datafile in chain
439 if (f->libversion >= 21) {
440 if (read_new_new_enc_format_clib(&f->mflib, ba))
441 return -5;
443 else if (f->libversion == 20) {
444 if (read_new_new_format_clib(&f->mflib, ba))
445 return -5;
446 } else {
447 struct MultiFileLib mflibOld_b, *mflibOld = &mflibOld_b;
449 if (read_new_format_clib(mflibOld, ba, f->libversion))
450 return -5;
451 // convert to newer format
452 f->mflib.num_files = mflibOld->num_files;
453 f->mflib.num_data_files = mflibOld->num_data_files;
454 memcpy(f->mflib.offset, mflibOld->offset, sizeof(int) * f->mflib.num_files);
455 memcpy(f->mflib.length, mflibOld->length, sizeof(int) * f->mflib.num_files);
456 memcpy(f->mflib.file_datafile, mflibOld->file_datafile, sizeof(char) * f->mflib.num_files);
457 assert(MAXMULTIFILES >= f->mflib.num_data_files);
458 for (aa = 0; aa < f->mflib.num_data_files; aa++)
459 strcpy(f->mflib.data_filenames[aa], mflibOld->data_filenames[aa]);
460 for (aa = 0; aa < f->mflib.num_files; aa++)
461 strcpy(f->mflib.filenames[aa], mflibOld->filenames[aa]);
464 strcpy(f->mflib.data_filenames[0], filename);
465 for (aa = 0; aa < f->mflib.num_files; aa++) {
466 // correct offsetes for EXE file
467 if (f->mflib.file_datafile[aa] == 0)
468 f->mflib.offset[aa] += absoffs;
470 return prep_multifiles(f);
473 passwmodifier = ByteArray_readUnsignedByte(ba);
474 ByteArray_readUnsignedByte(ba); // unused byte
475 f->mflib.num_data_files = 1;
476 strcpy(f->mflib.data_filenames[0], filename);
478 short tempshort = ByteArray_readShort(ba);
479 f->mflib.num_files = tempshort;
481 if (f->mflib.num_files > MAX_FILES) return -4;
483 ByteArray_readMultiByte(ba, clbuff, 13); // skip password dooberry
484 for (aa = 0; aa < f->mflib.num_files; aa++) {
485 ByteArray_readMultiByte(ba, f->mflib.filenames[aa], 13);
486 l = strlen(f->mflib.filenames[aa]);
487 for (cc = 0; cc < l; cc++)
488 f->mflib.filenames[aa][cc] -= passwmodifier;
490 for(cc = 0; cc < f->mflib.num_files; cc++)
491 f->mflib.length[cc] = ByteArray_readUnsignedInt(ba);
493 ByteArray_set_position_rel(ba, 2 * f->mflib.num_files); // skip flags & ratio
495 f->mflib.offset[0] = ByteArray_get_position(ba);
497 for (aa = 1; aa < f->mflib.num_files; aa++) {
498 f->mflib.offset[aa] = f->mflib.offset[aa - 1] + f->mflib.length[aa - 1];
499 f->mflib.file_datafile[aa] = 0;
501 f->mflib.file_datafile[0] = 0;
503 return prep_multifiles(f);
506 static int checkIndex(struct AgsFile *f, size_t index) {
507 if (index >= AgsFile_getFileCount(f)) return 0;
508 return 1;
511 static int checkDataIndex(struct AgsFile *f, size_t index) {
512 if (index >= AgsFile_getDataFileCount(f)) return 0;
513 return 1;
516 size_t AgsFile_getFileCount(struct AgsFile *f) {
517 return f->mflib.num_files;
520 size_t AgsFile_getDataFileCount(struct AgsFile *f) {
521 return f->mflib.num_data_files;
524 int AgsFile_getFileNumber(struct AgsFile *f, size_t index) {
525 if (!checkIndex(f, index)) return -1;
526 return f->mflib.file_datafile[index];
529 void AgsFile_setFileNumber(struct AgsFile *f, size_t index, int number) {
530 *(unsigned char*)(&f->mflib.file_datafile[index]) = number;
533 char *AgsFile_getFileName(struct AgsFile *f, size_t index) {
534 if (!checkIndex(f, index)) return 0;
535 return f->mflib.filenames[index];
538 char *AgsFile_getDataFileName(struct AgsFile *f, size_t index) {
539 if (!checkDataIndex(f, index)) return 0;
540 return f->mflib.data_filenames[index];
543 size_t AgsFile_getFileSize(struct AgsFile *f, size_t index) {
544 if (!checkIndex(f, index)) return 0;
545 return f->mflib.length[index];
548 size_t AgsFile_getOffset(struct AgsFile *f, size_t index) {
549 if (!checkIndex(f, index)) return 0;
550 return f->mflib.offset[index];
553 static int AgsFile_seek(struct AgsFile *f, int multifileno, off_t pos) {
554 return ByteArray_set_position(&f->f[multifileno], pos);
557 static ssize_t AgsFile_read(struct AgsFile *f, int multifileno, void* buf, size_t count) {
558 return ByteArray_readMultiByte(&f->f[multifileno], buf, count);
561 int AgsFile_extract(struct AgsFile* f, int multifileno, off_t start, size_t len, const char* outfn) {
562 int fd = open(outfn, O_WRONLY | O_CREAT | O_TRUNC, 0660);
563 if(fd == -1) return 0;
564 char buf[4096];
565 size_t written = 0, l = len;
566 off_t save_pos = ByteArray_get_position(&f->f[multifileno]);
567 AgsFile_seek(f, multifileno, start);
568 while(written < l) {
569 size_t togo = l - written;
570 if(togo > sizeof(buf)) togo = sizeof(buf);
571 if(togo == 0) break;
572 ssize_t ret = AgsFile_read(f, multifileno, buf, togo);
573 if(ret <= 0) break;
574 ret = write(fd, buf, togo);
575 if(ret == -1) {
576 perror("write");
577 break;
578 } else if(ret != togo) {
579 dprintf(2, "short write\n");
580 break;
582 written += togo;
584 close(fd);
585 AgsFile_seek(f, multifileno, save_pos);
586 return written == l;
589 int AgsFile_dump(struct AgsFile* f, size_t index, const char* outfn) {
590 if (!checkIndex(f, index)) return 0;
591 return AgsFile_extract(f, AgsFile_getFileNumber(f, index), AgsFile_getOffset(f, index), AgsFile_getFileSize(f, index), outfn);
594 void AgsFile_init(struct AgsFile *buf, char* filename) {
595 buf->fn = filename;
598 int AgsFile_open(struct AgsFile *buf) {
599 int ret = csetlib(buf, buf->fn);
601 return ret == 0;