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
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
);
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
49 int libmpq_archive_open(mpq_archive
*mpq_a
, unsigned char *mpq_filename
) {
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
) {
67 /* fill the structures with informations */
68 strcpy((char *)mpq_a
->filename
, (char *)mpq_filename
);
69 libmpq_init_buffer(mpq_a
);
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
78 mpq_a
->header
->id
= 0;
80 _lseeki64(mpq_a
->fd
, mpq_a
->mpqpos
, SEEK_SET
);
82 lseek64(mpq_a
->fd
, mpq_a
->mpqpos
, SEEK_SET
);
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
) {
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) {
108 /* move to the next possible offset */
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;
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. */
151 /* Check if file descriptor is valid. */
152 if ((_close(mpq_a
->fd
)) == 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
;
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
++) {
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
;
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
;
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;
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]);
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
;
239 case LIBMPQ_FILE_COMPRESSED_SIZE
:
241 case LIBMPQ_FILE_UNCOMPRESSED_SIZE
:
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
;
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
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) {
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
);
276 * This function returns the number to the given
279 int libmpq_file_number(mpq_archive
*mpq_a
, const char *name
) {
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 */
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
) {
304 char tempfile
[PATH_MAX
];
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
;
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 */
326 /* if a file was found return 0 */
328 return LIBMPQ_TOOLS_SUCCESS
;
330 return LIBMPQ_EFILE_NOT_FOUND
;
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;
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
) {
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]);
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
));
392 return LIBMPQ_EALLOCMEM
;
395 /* initialize file structure */
396 memset(mpq_f
, 0, sizeof(mpq_file
));
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) {
423 mpq_f
->accessed
= TRUE
;
424 mpq_f
->filepos
+= transferred
;
427 transferred
= _write(mpq_f
->fd
, buffer
, transferred
);
428 if (transferred
== 0) {
435 /* freeing the file structure */
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
]) {
454 int result
= LIBMPQ_TOOLS_SUCCESS
;
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 *));
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)) {
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);
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
);
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 *));
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. */
528 result
= LIBMPQ_CONF_EFILE_NOT_FOUND
;
536 * This function frees the allocated memory for the listfile.
538 int libmpq_listfile_close(mpq_archive
*mpq_a
) {
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
);
552 int libmpq_file_getdata(mpq_archive
*mpq_a
, mpq_hash mpq_h
, const int number
, unsigned char *dest
) {
553 int blockindex
= number
; //-1;
555 mpq_file
*mpq_f
= NULL
;
556 mpq_block
*mpq_b
= NULL
;
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]);
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
));
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
)
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 */
625 return success
?LIBMPQ_TOOLS_SUCCESS
:LIBMPQ_EFILE_CORRUPT
;