agstract: create dir if possible
[rofl0r-agsutils.git] / Clib32.c
blobd8ed19ea09200f70bc9a3062dd35b5c6450092b1
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 <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stddef.h>
27 #include "ByteArray.h"
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <stdint.h>
33 #include <stddef.h>
34 #include "Clib32.h"
35 #include "endianness.h"
37 #define RAND_SEED_SALT 9338638
39 static char *clibendfilesig = "CLIB\x1\x2\x3\x4SIGE";
40 static char *clibpasswencstring = "My\x1\xde\x4Jibzle";
41 static int _last_rand;
43 static void init_pseudo_rand_gen(int seed) {
44 _last_rand = seed;
47 static int get_pseudo_rand() {
48 return( ((_last_rand = _last_rand * 214013L
49 + 2531011L) >> 16) & 0x7fff );
52 static void clib_encrypt_text(unsigned char *clear, unsigned char *encbuf) {
53 unsigned adx = 0;
54 while(1) {
55 *encbuf = *clear + clibpasswencstring[adx];
56 if(!*clear) break;
57 adx++; encbuf++; clear++;
58 if (adx > 10) adx = 0;
62 static void clib_decrypt_text(char *toenc) {
63 unsigned adx = 0;
64 while (1) {
65 *toenc -= clibpasswencstring[adx];
66 if (!*toenc) break;
67 adx++; toenc++;
68 if (adx > 10) adx = 0;
72 static void fgetnulltermstring(char *sss, struct ByteArray *ddd, int bufsize) {
73 int b = -1;
74 off_t l = ByteArray_get_length(ddd);
75 do {
76 if (b < bufsize - 1) b++;
77 if(ByteArray_get_position(ddd) >= l)
78 return;
79 sss[b] = ByteArray_readByte(ddd);
80 } while (sss[b] != 0);
83 long last_opened_size;
85 static int fread_data_enc_byte(struct ByteArray *ba) {
86 return ByteArray_readUnsignedByte(ba) - get_pseudo_rand();
89 static uint32_t fread_data_enc_int(struct ByteArray *ba) {
90 union {
91 uint32_t i;
92 unsigned char c[4];
93 } res;
94 res.i = ByteArray_readUnsignedInt(ba);
95 size_t i = 0;
96 for(; i < 4; i++)
97 res.c[i] -= get_pseudo_rand();
98 return res.i;
101 static void fread_data_intarray_enc(struct ByteArray *ba, unsigned* dest, size_t count) {
102 size_t i = 0;
103 for(; i < count; i++)
104 dest[i] = fread_data_enc_int(ba);
107 static void fread_data_intarray(struct ByteArray *ba, unsigned* dest, size_t count) {
108 size_t i = 0;
109 for(; i < count; i++)
110 dest[i] = ByteArray_readInt(ba);
113 #if 0
114 static void fread_data_enc(void *data, size_t dataSize, size_t dataCount, struct ByteArray *ooo) {
115 ByteArray_readMultiByte(ooo, (char*)data, dataSize * dataCount);
116 unsigned char *dataChar = (unsigned char*)data;
117 size_t i = 0;
118 for (; i < dataSize * dataCount; i++)
119 dataChar[i] -= get_pseudo_rand();
121 #endif
123 static void fgetstring_enc(char *sss, struct ByteArray *ooo, int maxLength) {
124 int i = 0;
125 while ((i == 0) || (sss[i - 1] != 0)) {
126 sss[i] = ByteArray_readByte(ooo) - get_pseudo_rand();
127 if (i < maxLength - 1) i++;
131 static int getw_enc(struct ByteArray *ooo) {
132 return fread_data_enc_int(ooo);
135 static int read_new_new_enc_format_clib(struct MultiFileLibNew * mfl, struct ByteArray * wout) {
136 size_t aa;
137 int randSeed = ByteArray_readInt(wout);
138 init_pseudo_rand_gen(randSeed + RAND_SEED_SALT);
139 mfl->num_data_files = getw_enc(wout);
140 for (aa = 0; aa < mfl->num_data_files; aa++)
141 fgetstring_enc(mfl->data_filenames[aa], wout, 50);
142 mfl->num_files = getw_enc(wout);
144 if (mfl->num_files > MAX_FILES)
145 return -1;
147 for (aa = 0; aa < mfl->num_files; aa++)
148 fgetstring_enc(mfl->filenames[aa], wout, 100);
150 fread_data_intarray_enc(wout, mfl->offset, mfl->num_files);
151 fread_data_intarray_enc(wout, mfl->length, mfl->num_files);
152 for(aa = 0; aa < mfl->num_files; aa++)
153 mfl->file_datafile[aa] = fread_data_enc_byte(wout);
154 return 0;
157 static int read_new_new_format_clib(struct MultiFileLibNew* mfl, struct ByteArray * wout) {
158 size_t aa;
159 mfl->num_data_files = ByteArray_readInt(wout);
160 for (aa = 0; aa < mfl->num_data_files; aa++)
161 fgetnulltermstring(mfl->data_filenames[aa], wout, 50);
162 mfl->num_files = ByteArray_readInt(wout);
164 if (mfl->num_files > MAX_FILES) return -1;
166 for (aa = 0; aa < mfl->num_files; aa++) {
167 short nameLength = ByteArray_readShort(wout);
168 nameLength /= 5;
169 ByteArray_readMultiByte(wout, mfl->filenames[aa], nameLength);
170 clib_decrypt_text(mfl->filenames[aa]);
172 fread_data_intarray(wout, mfl->offset, mfl->num_files);
173 fread_data_intarray(wout, mfl->length, mfl->num_files);
174 ByteArray_readMultiByte(wout, mfl->file_datafile, mfl->num_files);
175 return 0;
178 static int read_new_format_clib(struct MultiFileLib * mfl, struct ByteArray * wout, int libver) {
179 mfl->num_data_files = ByteArray_readInt(wout);
180 ByteArray_readMultiByte(wout, (char*) mfl->data_filenames, 20U * mfl->num_data_files);
181 mfl->num_files = ByteArray_readInt(wout);
183 if (mfl->num_files > MAX_FILES) return -1;
185 ByteArray_readMultiByte(wout, (char*) mfl->filenames, 25U * mfl->num_files);
187 fread_data_intarray(wout, mfl->offset, mfl->num_files);
188 fread_data_intarray(wout, mfl->length, mfl->num_files);
189 ByteArray_readMultiByte(wout, mfl->file_datafile, mfl->num_files);
191 if (libver >= 11) {
192 size_t aa;
193 for (aa = 0; aa < mfl->num_files; aa++)
194 clib_decrypt_text(mfl->filenames[aa]);
196 return 0;
199 void AgsFile_close(struct AgsFile *f) {
200 unsigned i;
201 for (i=0; i < AgsFile_getFileCount(f); i++)
202 ByteArray_close_file(&f->f[i]);
205 int AgsFile_getVersion(struct AgsFile *f) {
206 return f->libversion;
209 void AgsFile_setVersion(struct AgsFile *f, int version) {
210 f->libversion = version;
213 void AgsFile_setSourceDir(struct AgsFile *f, char* sourcedir) {
214 f->dir = sourcedir;
217 void AgsFile_setFileCount(struct AgsFile *f, size_t count) {
218 f->mflib.num_files = count;
221 static off_t filelength(int fd) {
222 struct stat st;
223 fstat(fd, &st);
224 return st.st_size;
227 int AgsFile_setFile(struct AgsFile *f, size_t index, char* fn) {
228 strncpy(f->mflib.filenames[index], fn, 100);
229 char fnbuf[512];
230 snprintf(fnbuf, sizeof(fnbuf), "%s/%s", f->dir, f->mflib.filenames[index]);
231 int fd = open(fnbuf, O_RDONLY);
232 if(fd == -1) return 0;
233 off_t fl = filelength(fd);
234 close(fd);
235 f->mflib.length[index] = fl;
236 return 1;
239 int AgsFile_setDataFile(struct AgsFile *f, size_t index, char* fn) {
240 size_t l = strlen(fn);
241 if(l >= 20) return 0;
242 strncpy(f->mflib.data_filenames[index], fn, 20);
243 return 1;
246 void AgsFile_setDataFileCount(struct AgsFile *f, size_t count) {
247 f->mflib.num_data_files = count;
251 static int get_int_le(int val) {
252 #ifdef IS_LITTLE_ENDIAN
253 return val;
254 #else
255 return byteswap32(val);
256 #endif
259 static void write_int(int fd, int val) {
260 int le = get_int_le(val);
261 write(fd, &le, sizeof(le));
264 static short get_short_le(short val) {
265 #ifdef IS_LITTLE_ENDIAN
266 return val;
267 #else
268 return byteswap16(val);
269 #endif
272 static void write_short(int fd, short val) {
273 short le = get_short_le(val);
274 write(fd, &le, sizeof(le));
277 static void write_int_array(int fd, int* arr, size_t len) {
278 size_t i = 0;
279 for(; i < len; i++)
280 write_int(fd, arr[i]);
283 static int copy_into_file(int fd, char *dir, char *fn, size_t filesize) {
284 char fnbuf[512];
285 snprintf(fnbuf, sizeof(fnbuf), "%s/%s", dir, fn);
286 int f = open(fnbuf, O_RDONLY);
287 if(f == -1) return 0;
288 char readbuf[4096];
289 int ret = 1;
290 do {
291 size_t togo = filesize > sizeof(readbuf) ? sizeof(readbuf) : filesize;
292 if((size_t) read(f, readbuf, togo) != togo) { ret = 0; goto end; }
293 if((size_t) write(fd, readbuf, togo) != togo) { ret = 0; goto end; }
294 filesize -= togo;
295 } while(filesize);
296 end:
297 close(f);
298 return ret;
301 static size_t write_header(struct AgsFile *f, int fd) {
302 int myversion = 20; //f->libversion;
303 unsigned char version = myversion;
304 write(fd, "CLIB", 5);
305 write(fd, &version, 1);
306 version = 0;
307 if(myversion >= 10) write(fd, &version, 1);
308 size_t written = 7;
309 size_t i,l;
310 write_int(fd, f->mflib.num_data_files);
311 written += sizeof(int);
312 for(i = 0; i < f->mflib.num_data_files; i++) {
313 l = strlen(f->mflib.data_filenames[i]) + 1;
314 written += l;
315 if(l != (size_t) write(fd, f->mflib.data_filenames[i], l))
316 return 0;
319 write_int(fd, f->mflib.num_files);
320 written += sizeof(int);
321 unsigned char encbuf[100];
322 for(i = 0; i < f->mflib.num_files; i++) {
323 l = strlen(f->mflib.filenames[i]) + 1;
324 write_short(fd, l * 5);
325 clib_encrypt_text((unsigned char*)f->mflib.filenames[i], encbuf);
326 if(l != (size_t) write(fd, encbuf, l)) return 0;
327 written += sizeof(short) + l;
329 l = f->mflib.num_files;
330 write_int_array(fd, (int*) f->mflib.offset, l);
331 written += sizeof(int) * l;
332 write_int_array(fd, (int*) f->mflib.length, l);
333 written += sizeof(int) * l;
334 if(l != (size_t) write(fd, f->mflib.file_datafile, l))
335 return 0;
336 written += l;
337 return written;
340 static void write_footer(int fd) {
341 write_int(fd, 0);
342 write(fd, clibendfilesig, 12);
346 int AgsFile_write(struct AgsFile *f) {
347 int fd = open(f->fn, O_CREAT | O_WRONLY | O_TRUNC, 0660);
348 if(fd == -1) return 0;
349 size_t i, off;
350 if(!(off = write_header(f, fd))) return 0;
351 lseek(fd, 0, SEEK_SET);
352 for(i = 0; i < f->mflib.num_files; i++) {
353 f->mflib.offset[i] = off;
354 off += f->mflib.length[i];
356 write_header(f, fd);
357 for(i = 0; i < f->mflib.num_files; i++) {
358 if(!copy_into_file(fd, f->dir, f->mflib.filenames[i], f->mflib.length[i]))
359 return 0;
361 write_footer(fd);
362 close(fd);
363 return 1;
366 static int prep_multifiles(struct AgsFile *f) {
367 unsigned aa;
368 struct ByteArray *ba;
369 /* open each datafile as a byte array */
370 for (aa = 1; aa < f->mflib.num_data_files; aa++) {
371 ba = &f->f[aa];
372 ByteArray_ctor(ba);
373 if(!ByteArray_open_file(ba, f->mflib.data_filenames[aa])) return -1;
374 ByteArray_set_endian(ba, BAE_LITTLE); // all ints etc are saved in little endian.
376 return 0;
379 static int csetlib(struct AgsFile* f, char *filename) {
380 char clbuff[20];
381 if (!filename)
382 return -11;
384 int passwmodifier = 0;
385 size_t aa;
386 size_t cc, l;
388 struct ByteArray *ba = &f->f[0];
389 ByteArray_ctor(ba);
390 if(!ByteArray_open_file(ba, filename)) return -1;
391 ByteArray_set_endian(ba, BAE_LITTLE); // all ints etc are saved in little endian.
392 off_t ba_len = ByteArray_get_length(ba);
393 ByteArray_readMultiByte(ba, clbuff, 5);
395 uint32_t absoffs = 0; /* we read 4 bytes- so our datatype
396 must be 4 bytes as well, since we use a pointer to it */
398 f->pack_off = 0;
399 if (strncmp(clbuff, "CLIB", 4) != 0) {
400 ByteArray_set_position(ba, ba_len - 12);
401 ByteArray_readMultiByte(ba, clbuff, 12);
403 if (strncmp(clbuff, clibendfilesig, 12) != 0)
404 return -2;
405 // it's an appended-to-end-of-exe thing
406 ByteArray_set_position(ba, ba_len - 16);
407 absoffs = ByteArray_readUnsignedInt(ba);
408 ByteArray_set_position(ba, absoffs + 5);
409 f->pack_off = absoffs;
412 f->libversion = ByteArray_readUnsignedByte(ba);
413 switch (f->libversion) {
414 case 6: case 10: case 11: case 15: case 20: case 21:
415 break;
416 default:
417 // unsupported version
418 return -3;
421 // remove slashes so that the lib name fits in the buffer
422 while (filename[0] == '\\' || filename[0] == '/') filename++;
424 if (f->libversion >= 10) {
425 if (ByteArray_readUnsignedByte(ba) != 0)
426 return -4; // not first datafile in chain
428 if (f->libversion >= 21) {
429 if (read_new_new_enc_format_clib(&f->mflib, ba))
430 return -5;
432 else if (f->libversion == 20) {
433 if (read_new_new_format_clib(&f->mflib, ba))
434 return -5;
435 } else {
436 struct MultiFileLib mflibOld_b, *mflibOld = &mflibOld_b;
438 if (read_new_format_clib(mflibOld, ba, f->libversion))
439 return -5;
440 // convert to newer format
441 f->mflib.num_files = mflibOld->num_files;
442 f->mflib.num_data_files = mflibOld->num_data_files;
443 memcpy(f->mflib.offset, mflibOld->offset, sizeof(int) * f->mflib.num_files);
444 memcpy(f->mflib.length, mflibOld->length, sizeof(int) * f->mflib.num_files);
445 memcpy(f->mflib.file_datafile, mflibOld->file_datafile, sizeof(char) * f->mflib.num_files);
446 for (aa = 0; aa < f->mflib.num_data_files; aa++)
447 strcpy(f->mflib.data_filenames[aa], mflibOld->data_filenames[aa]);
448 for (aa = 0; aa < f->mflib.num_files; aa++)
449 strcpy(f->mflib.filenames[aa], mflibOld->filenames[aa]);
452 strcpy(f->mflib.data_filenames[0], filename);
453 for (aa = 0; aa < f->mflib.num_files; aa++) {
454 // correct offsetes for EXE file
455 if (f->mflib.file_datafile[aa] == 0)
456 f->mflib.offset[aa] += absoffs;
458 return prep_multifiles(f);
461 passwmodifier = ByteArray_readUnsignedByte(ba);
462 ByteArray_readUnsignedByte(ba); // unused byte
463 f->mflib.num_data_files = 1;
464 strcpy(f->mflib.data_filenames[0], filename);
466 short tempshort = ByteArray_readShort(ba);
467 f->mflib.num_files = tempshort;
469 if (f->mflib.num_files > MAX_FILES) return -4;
471 ByteArray_readMultiByte(ba, clbuff, 13); // skip password dooberry
472 for (aa = 0; aa < f->mflib.num_files; aa++) {
473 ByteArray_readMultiByte(ba, f->mflib.filenames[aa], 13);
474 l = strlen(f->mflib.filenames[aa]);
475 for (cc = 0; cc < l; cc++)
476 f->mflib.filenames[aa][cc] -= passwmodifier;
478 for(cc = 0; cc < f->mflib.num_files; cc++)
479 f->mflib.length[cc] = ByteArray_readUnsignedInt(ba);
481 ByteArray_set_position_rel(ba, 2 * f->mflib.num_files); // skip flags & ratio
483 f->mflib.offset[0] = ByteArray_get_position(ba);
485 for (aa = 1; aa < f->mflib.num_files; aa++) {
486 f->mflib.offset[aa] = f->mflib.offset[aa - 1] + f->mflib.length[aa - 1];
487 f->mflib.file_datafile[aa] = 0;
489 f->mflib.file_datafile[0] = 0;
491 return prep_multifiles(f);
494 static int checkIndex(struct AgsFile *f, size_t index) {
495 if (index >= AgsFile_getFileCount(f)) return 0;
496 return 1;
499 static int checkDataIndex(struct AgsFile *f, size_t index) {
500 if (index >= AgsFile_getDataFileCount(f)) return 0;
501 return 1;
504 size_t AgsFile_getFileCount(struct AgsFile *f) {
505 return f->mflib.num_files;
508 size_t AgsFile_getDataFileCount(struct AgsFile *f) {
509 return f->mflib.num_data_files;
512 int AgsFile_getFileNumber(struct AgsFile *f, size_t index) {
513 if (!checkIndex(f, index)) return -1;
514 return f->mflib.file_datafile[index];
517 void AgsFile_setFileNumber(struct AgsFile *f, size_t index, int number) {
518 *(unsigned char*)(&f->mflib.file_datafile[index]) = number;
521 char *AgsFile_getFileName(struct AgsFile *f, size_t index) {
522 if (!checkIndex(f, index)) return 0;
523 return f->mflib.filenames[index];
526 char *AgsFile_getDataFileName(struct AgsFile *f, size_t index) {
527 if (!checkDataIndex(f, index)) return 0;
528 return f->mflib.data_filenames[index];
531 size_t AgsFile_getFileSize(struct AgsFile *f, size_t index) {
532 if (!checkIndex(f, index)) return 0;
533 return f->mflib.length[index];
536 size_t AgsFile_getOffset(struct AgsFile *f, size_t index) {
537 if (!checkIndex(f, index)) return 0;
538 return f->mflib.offset[index];
541 static int AgsFile_seek(struct AgsFile *f, int multifileno, off_t pos) {
542 return ByteArray_set_position(&f->f[multifileno], pos);
545 static ssize_t AgsFile_read(struct AgsFile *f, int multifileno, void* buf, size_t count) {
546 return ByteArray_readMultiByte(&f->f[multifileno], buf, count);
549 int AgsFile_extract(struct AgsFile* f, int multifileno, off_t start, size_t len, const char* outfn) {
550 int fd = open(outfn, O_WRONLY | O_CREAT | O_TRUNC, 0660);
551 if(fd == -1) return 0;
552 char buf[4096];
553 size_t written = 0, l = len;
554 off_t save_pos = ByteArray_get_position(&f->f[multifileno]);
555 AgsFile_seek(f, multifileno, start);
556 while(written < l) {
557 size_t togo = l - written;
558 if(togo > sizeof(buf)) togo = sizeof(buf);
559 if(togo == 0) break;
560 ssize_t ret = AgsFile_read(f, multifileno, buf, togo);
561 if(ret <= 0) break;
562 write(fd, buf, togo);
563 written += togo;
565 close(fd);
566 AgsFile_seek(f, multifileno, save_pos);
567 return written == l;
570 int AgsFile_dump(struct AgsFile* f, size_t index, const char* outfn) {
571 if (!checkIndex(f, index)) return 0;
572 return AgsFile_extract(f, AgsFile_getFileNumber(f, index), AgsFile_getOffset(f, index), AgsFile_getFileSize(f, index), outfn);
575 void AgsFile_init(struct AgsFile *buf, char* filename) {
576 buf->fn = filename;
579 int AgsFile_open(struct AgsFile *buf) {
580 int ret = csetlib(buf, buf->fn);
582 return ret == 0;