Fixed extracting common.MPQ under *nix
[auctionmangos.git] / contrib / extractor / libmpq / mpq.cpp
blob0761d81caae8fdcdafeb2bb65f52c236d9db3490
1 /*
2 * mpq.c -- functions for developers using libmpq.
4 * Copyright (C) 2003 Maik Broemme <mbroemme@plusserver.de>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 * $Id: mpq.c,v 1.6 2004/02/12 00:49:00 mbroemme Exp $
22 #define _CRT_SECURE_NO_DEPRECATE
24 #include <stdlib.h>
25 #include <sys/stat.h>
26 //#include <unistd.h>
27 //#include <io.h>
28 #include <fcntl.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include "mpq.h"
32 #include "common.h"
35 * This function returns version information.
36 * format: MAJOR.MINOR.PATCH
38 char *libmpq_version() {
39 static char version[10];
40 sprintf(version, "%i.%i.%i", LIBMPQ_MAJOR_VERSION, LIBMPQ_MINOR_VERSION, LIBMPQ_PATCH_VERSION);
41 return version;
45 * This function reads a file and verify if it is a legit MPQ archive
46 * or not. Then it fills the mpq_header structure and reads the hash
47 * table.
49 int libmpq_archive_open(mpq_archive *mpq_a, unsigned char *mpq_filename) {
50 int fd = 0;
51 int rb = 0;
52 int ncnt = FALSE;
53 struct stat fileinfo;
55 /* allocate memory */
56 mpq_a->mpq_l = (mpq_list *)malloc(sizeof(mpq_list));
57 memset(mpq_a->mpq_l, 0, sizeof(mpq_list));
58 mpq_a->header = (mpq_header *)malloc(sizeof(mpq_header));
59 memset(mpq_a->header, 0, sizeof(mpq_header));
61 /* Check if file exists and is readable */
62 fd = _open((char *)mpq_filename, O_RDONLY | O_BINARY | O_LARGEFILE);
63 if (fd == LIBMPQ_EFILE) {
64 return LIBMPQ_EFILE;
67 /* fill the structures with informations */
68 strcpy((char *)mpq_a->filename, (char *)mpq_filename);
69 libmpq_init_buffer(mpq_a);
70 mpq_a->fd = fd;
71 mpq_a->header->id = 0;
72 mpq_a->maxblockindex = 0;
73 mpq_a->mpq_l->mpq_files = NULL;
75 mpq_a->mpqpos = 0; //k
77 while (!ncnt) {
78 mpq_a->header->id = 0;
79 #ifdef WIN32
80 _lseeki64(mpq_a->fd, mpq_a->mpqpos, SEEK_SET);
81 #else
82 lseek64(mpq_a->fd, mpq_a->mpqpos, SEEK_SET);
83 #endif
84 rb = _read(mpq_a->fd, mpq_a->header, sizeof(mpq_header));
86 /* if different number of bytes read, break the loop */
87 if (rb != sizeof(mpq_header)) {
88 return LIBMPQ_EFILE_FORMAT;
91 /* special offset for protected MPQs */
92 if (mpq_a->header->offset == LIBMPQ_HEADER_W3M) {
93 mpq_a->flags |= LIBMPQ_FLAG_PROTECTED;
94 mpq_a->header->offset = sizeof(mpq_header);
97 /* if valid signature has been found, break the loop */
98 if (mpq_a->header->id == LIBMPQ_ID_MPQ) {
99 ncnt = true;
101 /*if (mpq_a->header->id == LIBMPQ_ID_MPQ &&
102 mpq_a->header->offset == sizeof(mpq_header) &&
103 mpq_a->header->hashtablepos < mpq_a->header->archivesize &&
104 mpq_a->header->blocktablepos < mpq_a->header->archivesize) {
105 ncnt = TRUE;
108 /* move to the next possible offset */
109 if (!ncnt) {
110 mpq_a->mpqpos += 0x200;
114 /* get the right positions of the hash table and the block table. */
115 mpq_a->blocksize = (0x200 << mpq_a->header->blocksize);
116 fstat(mpq_a->fd, &fileinfo);
118 /* Normal MPQs must have position of */
119 /*if (mpq_a->header->hashtablepos + mpq_a->mpqpos < fileinfo.st_size &&
120 mpq_a->header->blocktablepos + mpq_a->mpqpos < fileinfo.st_size) {
121 mpq_a->header->hashtablepos += mpq_a->mpqpos;
122 mpq_a->header->blocktablepos += mpq_a->mpqpos;
123 } else {
124 return LIBMPQ_EFILE_FORMAT;
127 /* Try to read and decrypt the hashtable */
128 if (libmpq_read_hashtable(mpq_a) != 0) {
129 return LIBMPQ_EHASHTABLE;
132 /* Try to read and decrypt the blocktable */
133 if (libmpq_read_blocktable(mpq_a) != 0) {
134 return LIBMPQ_EBLOCKTABLE;
137 return LIBMPQ_TOOLS_SUCCESS;
141 * This function closes the file descriptor opened by
142 * mpq_open_archive(); and frees the decryption buffer.
144 int libmpq_archive_close(mpq_archive *mpq_a) {
145 memset(mpq_a->buf, 0, sizeof(mpq_a->buf));
147 /* free the allocated memory. */
148 free(mpq_a->header);
149 free(mpq_a->mpq_l);
151 /* Check if file descriptor is valid. */
152 if ((_close(mpq_a->fd)) == LIBMPQ_EFILE) {
153 return LIBMPQ_EFILE;
156 return LIBMPQ_TOOLS_SUCCESS;
160 * This function returns the value for the given infotype.
161 * If an error occurs something < 0 is returned.
163 int libmpq_archive_info(mpq_archive *mpq_a, unsigned int infotype) {
164 unsigned int filecount = 0;
165 unsigned int fsize = 0;
166 unsigned int csize = 0;
167 mpq_block *mpq_b_end = mpq_a->blocktable + mpq_a->header->blocktablesize;
168 mpq_block *mpq_b = NULL;
170 switch (infotype) {
171 case LIBMPQ_MPQ_ARCHIVE_SIZE:
172 return mpq_a->header->archivesize;
173 case LIBMPQ_MPQ_HASHTABLE_SIZE:
174 return mpq_a->header->hashtablesize;
175 case LIBMPQ_MPQ_BLOCKTABLE_SIZE:
176 return mpq_a->header->blocktablesize;
177 case LIBMPQ_MPQ_BLOCKSIZE:
178 return mpq_a->blocksize;
179 case LIBMPQ_MPQ_NUMFILES:
180 for (mpq_b = mpq_a->blocktable; mpq_b < mpq_b_end; mpq_b++) {
181 filecount++;
183 return filecount;
184 case LIBMPQ_MPQ_COMPRESSED_SIZE:
185 for (mpq_b = mpq_a->blocktable; mpq_b < mpq_b_end; mpq_b++) {
186 csize += mpq_b->csize;
188 return csize;
189 case LIBMPQ_MPQ_UNCOMPRESSED_SIZE:
190 for (mpq_b = mpq_a->blocktable; mpq_b < mpq_b_end; mpq_b++) {
191 fsize += mpq_b->fsize;
193 return fsize;
194 default:
195 return LIBMPQ_TOOLS_SUCCESS;
200 * This function returns some useful file information.
202 int libmpq_file_info(mpq_archive *mpq_a, unsigned int infotype, const int number) {
203 int blockindex = number; //-1;
204 int i = 0;
205 mpq_block *mpq_b = NULL;
206 mpq_hash *mpq_h = NULL;
208 /* check if given number is not out of range */
209 if (number < 1 || number > mpq_a->header->blocktablesize) {
210 return LIBMPQ_EINV_RANGE;
213 /* search for correct hashtable */
214 /*for (i = 0; i < mpq_a->header->hashtablesize; i++) {
215 if ((number - 1) == (mpq_a->hashtable[i]).blockindex) {
216 blockindex = (mpq_a->hashtable[i]).blockindex;
217 mpq_h = &(mpq_a->hashtable[i]);
218 break;
222 /* check if file was found */
223 /*if (blockindex == -1 || blockindex > mpq_a->header->blocktablesize) {
224 return LIBMPQ_EFILE_NOT_FOUND;
227 /* check if sizes are correct */
228 mpq_b = mpq_a->blocktable + blockindex;
229 if (mpq_b->filepos > (mpq_a->header->archivesize + mpq_a->mpqpos) || mpq_b->csize > mpq_a->header->archivesize) {
230 return LIBMPQ_EFILE_CORRUPT;
233 /* check if file exists */
234 if ((mpq_b->flags & LIBMPQ_FILE_EXISTS) == 0) {
235 return LIBMPQ_EFILE_NOT_FOUND;
238 switch (infotype) {
239 case LIBMPQ_FILE_COMPRESSED_SIZE:
240 return mpq_b->csize;
241 case LIBMPQ_FILE_UNCOMPRESSED_SIZE:
242 return mpq_b->fsize;
243 case LIBMPQ_FILE_COMPRESSION_TYPE:
244 if (mpq_b->flags & LIBMPQ_FILE_COMPRESS_PKWARE) {
245 return LIBMPQ_FILE_COMPRESS_PKWARE;
247 if (mpq_b->flags & LIBMPQ_FILE_COMPRESS_MULTI) {
248 return LIBMPQ_FILE_COMPRESS_MULTI;
250 default:
251 return LIBMPQ_TOOLS_SUCCESS;
256 * This function searches the listfile for the filename.
257 * On success it returns the filename, otherwiese a name
258 * like file000001.xxx and if number is out of range it
259 * returns NULL.
261 char *libmpq_file_name(mpq_archive *mpq_a, const int number) {
262 static char tempfile[PATH_MAX];
264 /* check if we are in the range of available files. */
265 if (number > libmpq_archive_info(mpq_a, LIBMPQ_MPQ_NUMFILES) || number < 1) {
266 return NULL;
269 /* this is safe because we built a fallback filelist, if something was wrong. */
270 sprintf(tempfile, (char *)mpq_a->mpq_l->mpq_files[number - 1], number);
272 return tempfile;
276 * This function returns the number to the given
277 * filename.
279 int libmpq_file_number(mpq_archive *mpq_a, const char *name) {
280 int i;
281 char tempfile[PATH_MAX];
283 for (i = 0; mpq_a->mpq_l->mpq_files[i]; i++) {
284 sprintf(tempfile, (char *)mpq_a->mpq_l->mpq_files[i], i + 1);
285 if (strncmp(tempfile, name, strlen(name)) == 0) {
287 /* if file found return the number */
288 return i + 1;
292 /* if no matching entry found return LIBMPQ_EFILE_NOT_FOUND */
293 return LIBMPQ_EFILE_NOT_FOUND;
297 * This function verifies if a given file (by number
298 * or name) is in the opened mpq archive. On success
299 * it returns 0, otherwise LIBMPQ_EFILE_NOT_FOUND.
301 int libmpq_file_check(mpq_archive *mpq_a, void *file, int type) {
302 int found = 0;
303 int i;
304 char tempfile[PATH_MAX];
306 switch (type) {
307 case LIBMPQ_FILE_TYPE_INT:
309 /* check if we are in the range of available files. */
310 if (*(int *)file > libmpq_archive_info(mpq_a, LIBMPQ_MPQ_NUMFILES) || *(int *)file < 1) {
311 return LIBMPQ_EFILE_NOT_FOUND;
312 } else {
313 return LIBMPQ_TOOLS_SUCCESS;
315 case LIBMPQ_FILE_TYPE_CHAR:
316 for (i = 0; mpq_a->mpq_l->mpq_files[i]; i++) {
317 sprintf(tempfile, (char *)mpq_a->mpq_l->mpq_files[i], i);
318 if (strncmp(tempfile, (char *)file, strlen((char *)file)) == 0) {
320 /* if file found break */
321 found = 1;
322 break;
326 /* if a file was found return 0 */
327 if (found == 1) {
328 return LIBMPQ_TOOLS_SUCCESS;
329 } else {
330 return LIBMPQ_EFILE_NOT_FOUND;
332 default:
333 return LIBMPQ_TOOLS_SUCCESS;
338 * This function extracts a file from a MPQ archive
339 * by the given number.
341 int libmpq_file_extract(mpq_archive *mpq_a, const int number, const char *filename) {
342 int blockindex = number; //-1;
343 int fd = 0;
344 int i = 0;
345 char buffer[0x1000];
346 //char tempfile[PATH_MAX];
347 unsigned int transferred = 1;
348 mpq_file *mpq_f = NULL;
349 mpq_block *mpq_b = NULL;
350 mpq_hash *mpq_h = NULL;
352 /* if (number < 1 || number > mpq_a->header->blocktablesize) {
353 return LIBMPQ_EINV_RANGE;
356 sprintf(tempfile, libmpq_file_name(mpq_a, number));
358 /* check if mpq_f->filename could be written here. */
359 fd = _open(filename, O_RDWR|O_CREAT|O_TRUNC, 0644);
360 if (fd == LIBMPQ_EFILE) {
361 return LIBMPQ_EFILE;
364 /* search for correct hashtable */
365 /*for (i = 0; i < mpq_a->header->hashtablesize; i++) {
366 if ((number - 1) == (mpq_a->hashtable[i]).blockindex) {
367 blockindex = (mpq_a->hashtable[i]).blockindex;
368 mpq_h = &(mpq_a->hashtable[i]);
369 break;
373 /* check if file was found */
374 if (blockindex == -1 || blockindex > mpq_a->header->blocktablesize) {
375 return LIBMPQ_EFILE_NOT_FOUND;
378 /* check if sizes are correct */
379 mpq_b = mpq_a->blocktable + blockindex;
380 if (mpq_b->filepos > (mpq_a->header->archivesize + mpq_a->mpqpos) || mpq_b->csize > mpq_a->header->archivesize) {
381 return LIBMPQ_EFILE_CORRUPT;
384 /* check if file exists */
385 if ((mpq_b->flags & LIBMPQ_FILE_EXISTS) == 0) {
386 return LIBMPQ_EFILE_NOT_FOUND;
389 /* allocate memory for file structure */
390 mpq_f = (mpq_file *)malloc(sizeof(mpq_file));
391 if (!mpq_f) {
392 return LIBMPQ_EALLOCMEM;
395 /* initialize file structure */
396 memset(mpq_f, 0, sizeof(mpq_file));
397 mpq_f->fd = fd;
398 mpq_f->mpq_b = mpq_b;
399 mpq_f->nblocks = (mpq_f->mpq_b->fsize + mpq_a->blocksize - 1) / mpq_a->blocksize;
400 mpq_f->mpq_h = mpq_h;
401 mpq_f->accessed = FALSE;
402 mpq_f->blockposloaded = FALSE;
403 sprintf((char *)mpq_f->filename, filename);
405 /* allocate buffers for decompression. */
406 if (mpq_f->mpq_b->flags & LIBMPQ_FILE_COMPRESSED) {
409 * Allocate buffer for block positions. At the begin of file are stored
410 * unsigned ints holding positions of each block relative from begin of
411 * file in the archive.
413 if ((mpq_f->blockpos = (unsigned int *)malloc(sizeof(int) * mpq_f->nblocks + 1)) == NULL) {
414 return LIBMPQ_EALLOCMEM;
418 while (transferred > 0) {
419 transferred = libmpq_file_read_file(mpq_a, mpq_f, mpq_f->filepos, buffer, sizeof(buffer));
420 if (transferred == 0) {
421 break;
422 } else {
423 mpq_f->accessed = TRUE;
424 mpq_f->filepos += transferred;
427 transferred = _write(mpq_f->fd, buffer, transferred);
428 if (transferred == 0) {
429 break;
433 _close(fd);
435 /* freeing the file structure */
436 free(mpq_f);
437 return LIBMPQ_TOOLS_SUCCESS;
441 * This function tries to get the filenames for the hashes. It uses
442 * an internal listfile database and gets the correct listfile from
443 * some specific archive informations.
446 int libmpq_listfile_open(mpq_archive *mpq_a, char file[PATH_MAX]) {
447 FILE *fp;
448 //char **filelist;
449 int i = 0;
450 //int fl_count;
451 //int fl_size;
452 int fl_count_fb;
453 int fl_size_fb;
454 int result = LIBMPQ_TOOLS_SUCCESS;
455 struct stat statbuf;
457 /* get file status */
458 if (stat(file, &statbuf) < 0) {
459 result = LIBMPQ_CONF_EFILE_NOT_FOUND;
462 /* check if file is a filename or directory */
463 /*if (S_ISDIR(statbuf.st_mode)) {
465 // allocate memory for the file list
466 filelist = (char **)malloc(LIBMPQ_CONF_FL_INCREMENT * sizeof(char *));
467 fl_count = 0;
468 fl_size = LIBMPQ_CONF_FL_INCREMENT;
470 // check if it is a valid listfile
471 if (libmpq_detect_listfile_rec(file, &filelist, &fl_count, &fl_size)) {
472 filelist == NULL;
475 filelist[fl_count] = NULL;
477 // return if no listfile was found
478 if (filelist == NULL) {
479 result = LIBMPQ_CONF_EFILE_NOT_FOUND;
482 for (i = 0; filelist[i]; i++) {
483 if ((fp = fopen(filelist[i], "r")) != NULL ) {
484 result = libmpq_read_listfile(mpq_a, fp);
485 fclose(fp);
489 // freeing the listfile struct
490 libmpq_free_listfile(filelist);
493 /* if file is a regular file use it */
494 //if (S_ISREG(statbuf.st_mode)) {
496 /* if specific listfile was forced. */
497 if ((fp = fopen(file, "r")) != NULL ) {
498 result = libmpq_read_listfile(mpq_a, fp);
499 fclose(fp);
500 } else {
501 result = LIBMPQ_CONF_EFILE_OPEN;
505 /* if error occured we need to create a fallback filelist. */
506 if (mpq_a->mpq_l->mpq_files == NULL) {
508 /* allocate memory for the file list */
509 mpq_a->mpq_l->mpq_files = (unsigned char **)malloc(LIBMPQ_CONF_FL_INCREMENT * sizeof(char *));
510 fl_count_fb = 0;
511 fl_size_fb = LIBMPQ_CONF_FL_INCREMENT;
513 for (i = 0; i < libmpq_archive_info(mpq_a, LIBMPQ_MPQ_NUMFILES); i++) {
515 /* set the next filelist entry to a copy of the file */
516 mpq_a->mpq_l->mpq_files[fl_count_fb++] = (unsigned char *)_strdup("file%06lu.xxx");
518 /* increase the array size */
519 if (fl_count_fb == fl_size_fb) {
520 mpq_a->mpq_l->mpq_files = (unsigned char **)realloc(mpq_a->mpq_l->mpq_files, (fl_size_fb + LIBMPQ_CONF_FL_INCREMENT) * sizeof(char *));
521 fl_size_fb += LIBMPQ_CONF_FL_INCREMENT;
524 mpq_a->mpq_l->mpq_files[fl_count_fb] = NULL;
526 /* if no error occurs and no listfile was assigned, we think there was no matching listfile. */
527 if (result == 0) {
528 result = LIBMPQ_CONF_EFILE_NOT_FOUND;
532 return result;
536 * This function frees the allocated memory for the listfile.
538 int libmpq_listfile_close(mpq_archive *mpq_a) {
539 int i = 0;
541 /* safety check if we really have a filelist. */
542 if (mpq_a->mpq_l->mpq_files != NULL) {
543 /* freeing the filelist */
544 while (mpq_a->mpq_l->mpq_files[i]) {
545 free(mpq_a->mpq_l->mpq_files[i++]);
547 free(mpq_a->mpq_l->mpq_files);
549 return 0;
552 int libmpq_file_getdata(mpq_archive *mpq_a, mpq_hash mpq_h, const int number, unsigned char *dest) {
553 int blockindex = number; //-1;
554 int i = 0;
555 mpq_file *mpq_f = NULL;
556 mpq_block *mpq_b = NULL;
557 int success = 0;
559 /*if (number < 1 || number > mpq_a->header->blocktablesize) {
560 return LIBMPQ_EINV_RANGE;
563 /* search for correct hashtable */
564 /*for (i = 0; i < mpq_a->header->hashtablesize; i++) {
565 if ((number - 1) == (mpq_a->hashtable[i]).blockindex) {
566 blockindex = (mpq_a->hashtable[i]).blockindex;
567 mpq_h = &(mpq_a->hashtable[i]);
568 break;
572 /* check if file was found */
573 if (blockindex == -1 || blockindex > mpq_a->header->blocktablesize) {
574 return LIBMPQ_EFILE_NOT_FOUND;
577 /* check if sizes are correct */
578 mpq_b = mpq_a->blocktable + blockindex;
579 if (mpq_b->filepos > (mpq_a->header->archivesize + mpq_a->mpqpos) || mpq_b->csize > mpq_a->header->archivesize) {
580 return LIBMPQ_EFILE_CORRUPT;
583 /* check if file exists */
584 if ((mpq_b->flags & LIBMPQ_FILE_EXISTS) == 0) {
585 return LIBMPQ_EFILE_NOT_FOUND;
588 /* allocate memory for file structure */
589 mpq_f = (mpq_file*)malloc(sizeof(mpq_file));
590 if (!mpq_f) {
591 return LIBMPQ_EALLOCMEM;
594 /* initialize file structure */
595 memset(mpq_f, 0, sizeof(mpq_file));
596 mpq_f->mpq_b = mpq_b;
597 mpq_f->nblocks = (mpq_f->mpq_b->fsize + mpq_a->blocksize - 1) / mpq_a->blocksize;
598 mpq_f->mpq_h = &mpq_h;
599 mpq_f->accessed = FALSE;
600 mpq_f->blockposloaded = FALSE;
602 /* allocate buffers for decompression. */
603 if (mpq_f->mpq_b->flags & LIBMPQ_FILE_COMPRESSED) {
606 * Allocate buffer for block positions. At the begin of file are stored
607 * unsigned ints holding positions of each block relative from begin of
608 * file in the archive.
610 if ((mpq_f->blockpos = (unsigned int*)malloc(sizeof(int) * (mpq_f->nblocks + 1))) == NULL) {
611 return LIBMPQ_EALLOCMEM;
615 if(libmpq_file_read_file(mpq_a, mpq_f, 0, (char*)dest, mpq_b->fsize) == mpq_b->fsize)
616 success = 1;
618 if (mpq_f->mpq_b->flags & LIBMPQ_FILE_COMPRESSED) {
619 // Free buffer for block positions
621 free(mpq_f->blockpos);
623 /* freeing the file structure */
624 free(mpq_f);
625 return success?LIBMPQ_TOOLS_SUCCESS:LIBMPQ_EFILE_CORRUPT;