root/trunk/contrib/extractor/libmpq/common.cpp @ 149

Revision 2, 24.3 kB (checked in by yumileroy, 17 years ago)

[svn] * Proper SVN structure

Original author: Neo2003
Date: 2008-10-02 16:23:55-05:00

Line 
1/*
2 *  common.c -- shared functions used by mpq-tools.
3 *
4 *  Copyright (C) 2003 Maik Broemme <mbroemme@plusserver.de>
5 *
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.
10 *
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.
15 *
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.
19 *
20 *  $Id: common.c,v 1.12 2004/02/12 00:42:54 mbroemme Exp $
21 */
22#define _CRT_SECURE_NO_DEPRECATE
23//#include <dirent.h>
24#include <sys/stat.h>
25//#include <unistd.h>
26#include <stdlib.h>
27#include <string.h>
28#include <stdio.h>
29#include "mpq.h"
30#include "common.h"
31#include <ctype.h>
32
33/*
34 *  This function decrypts a MPQ block.
35 */
36int libmpq_decrypt_block(mpq_archive *mpq_a, unsigned int *block, unsigned int length, unsigned int seed1) {
37        unsigned int seed2 = 0xEEEEEEEE;
38        unsigned int ch;
39
40        /* Round to unsigned int's */
41        length >>= 2;
42        while (length-- > 0) {
43                seed2    += mpq_a->buf[0x400 + (seed1 & 0xFF)];
44                ch        = *block ^ (seed1 + seed2);
45                seed1     = ((~seed1 << 0x15) + 0x11111111) | (seed1 >> 0x0B);
46                seed2     = ch + seed2 + (seed2 << 5) + 3;
47                *block++  = ch;
48        }
49        return LIBMPQ_TOOLS_SUCCESS;
50}
51
52/*
53 *  This function decrypts the hashtable for the
54 *  file informations.
55 */
56int libmpq_decrypt_hashtable(mpq_archive *mpq_a, unsigned char *pbKey) {
57        unsigned int seed1 = 0x7FED7FED;
58        unsigned int seed2 = 0xEEEEEEEE;
59        unsigned int ch;                        /* One key character */
60        unsigned int *pdwTable = (unsigned int *)(mpq_a->hashtable);
61        unsigned int length = mpq_a->header->hashtablesize * 4;
62
63        /* Prepare seeds */
64        while (*pbKey != 0) {
65                ch = toupper(*pbKey++);
66                seed1 = mpq_a->buf[0x300 + ch] ^ (seed1 + seed2);
67                seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3;
68        }
69
70        /* Decrypt it */
71        seed2 = 0xEEEEEEEE;
72        while (length-- > 0) {
73                seed2 += mpq_a->buf[0x400 + (seed1 & 0xFF)];
74                ch     = *pdwTable ^ (seed1 + seed2);
75                seed1  = ((~seed1 << 0x15) + 0x11111111) | (seed1 >> 0x0B);
76                seed2  = ch + seed2 + (seed2 << 5) + 3;
77                *pdwTable++ = ch;
78        }
79        return LIBMPQ_TOOLS_SUCCESS;
80}
81
82/*
83 *  This function decrypts the blocktable.
84 */
85int libmpq_decrypt_blocktable(mpq_archive *mpq_a, unsigned char *pbKey) {
86        unsigned int seed1 = 0x7FED7FED;
87        unsigned int seed2 = 0xEEEEEEEE;
88        unsigned int ch;                        /* One key character */
89        unsigned int *pdwTable = (unsigned int *)(mpq_a->blocktable);
90        unsigned int length = mpq_a->header->blocktablesize * 4;
91
92        /* Prepare seeds */
93        while(*pbKey != 0) {
94                ch = toupper(*pbKey++);
95                seed1 = mpq_a->buf[0x300 + ch] ^ (seed1 + seed2);
96                seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3;
97        }         
98
99        /* Decrypt it */
100        seed2 = 0xEEEEEEEE;
101        while(length-- > 0) {
102                seed2 += mpq_a->buf[0x400 + (seed1 & 0xFF)];
103                ch     = *pdwTable ^ (seed1 + seed2);
104                seed1  = ((~seed1 << 0x15) + 0x11111111) | (seed1 >> 0x0B);
105                seed2  = ch + seed2 + (seed2 << 5) + 3;
106                *pdwTable++ = ch;
107        }
108        return LIBMPQ_TOOLS_SUCCESS;
109}
110
111int libmpq_read_listfile(mpq_archive *mpq_a, FILE *fp) {
112        int mpq_size;
113        int mpq_ht_size;
114        int mpq_bt_size;
115        int mpq_blocksize;
116        int mpq_files;
117        int mpq_csize;
118        int mpq_fsize;
119        int entries;
120        char listdb_version[10];
121        char libmpq_version[10];
122        int listdb_temp_version = 0;
123        int libmpq_temp_version = 0;
124
125        /* first check header and version */
126        if (libmpq_conf_get_value(fp, "LIBMPQ_VERSION", mpq_a->mpq_l->mpq_version, LIBMPQ_CONF_TYPE_CHAR, sizeof(mpq_a->mpq_l->mpq_version))) {
127                return LIBMPQ_CONF_EFILE_CORRUPT;
128        } else {
129
130                /* copy to temp buffer for removing . characters */
131                sprintf(listdb_version, (char *)mpq_a->mpq_l->mpq_version);
132
133                /* remove . characters from listfile version */
134                libmpq_conf_delete_char(listdb_version, ".");
135
136                /* get libmpq version */
137                sprintf(libmpq_version, "%i%i%i",LIBMPQ_MAJOR_VERSION, LIBMPQ_MINOR_VERSION, LIBMPQ_PATCH_VERSION);
138
139                /* convert to number */
140                listdb_temp_version = atoi(listdb_version);
141                libmpq_temp_version = atoi(libmpq_version);
142
143                /* check if installed libmpq version is valid for listfile version */
144                if ((libmpq_temp_version < listdb_temp_version) || (libmpq_temp_version == 0) || (listdb_temp_version == 0)) {
145                        return LIBMPQ_CONF_EFILE_VERSION;
146                }
147        }
148
149        /* check listfile header, the following entries must be set */
150        if (libmpq_conf_get_value(fp, "MPQ_SIZE", &mpq_size, LIBMPQ_CONF_TYPE_INT, 0)) {
151                return LIBMPQ_CONF_EFILE_CORRUPT;
152        }
153        if (libmpq_conf_get_value(fp, "MPQ_HASHTABLE_SIZE", &mpq_ht_size, LIBMPQ_CONF_TYPE_INT, 0)) {
154                return LIBMPQ_CONF_EFILE_CORRUPT;
155        }
156        if (libmpq_conf_get_value(fp, "MPQ_BLOCKTABLE_SIZE", &mpq_bt_size, LIBMPQ_CONF_TYPE_INT, 0)) {
157                return LIBMPQ_CONF_EFILE_CORRUPT;
158        }
159        if (libmpq_conf_get_value(fp, "MPQ_BLOCKSIZE", &mpq_blocksize, LIBMPQ_CONF_TYPE_INT, 0)) {
160                return LIBMPQ_CONF_EFILE_CORRUPT;
161        }
162        if (libmpq_conf_get_value(fp, "MPQ_FILES", &mpq_files, LIBMPQ_CONF_TYPE_INT, 0)) {
163                return LIBMPQ_CONF_EFILE_CORRUPT;
164        }
165        if (libmpq_conf_get_value(fp, "MPQ_COMPRESSED_SIZE", &mpq_csize, LIBMPQ_CONF_TYPE_INT, 0)) {
166                return LIBMPQ_CONF_EFILE_CORRUPT;
167        }
168        if (libmpq_conf_get_value(fp, "MPQ_UNCOMPRESSED_SIZE", &mpq_fsize, LIBMPQ_CONF_TYPE_INT, 0)) {
169                return LIBMPQ_CONF_EFILE_CORRUPT;
170        }
171        if (libmpq_conf_get_value(fp, "MPQ_NAME", mpq_a->mpq_l->mpq_name, LIBMPQ_CONF_TYPE_CHAR, PATH_MAX)) {
172                return LIBMPQ_CONF_EFILE_CORRUPT;
173        }
174        if (libmpq_conf_get_value(fp, "MPQ_TYPE", mpq_a->mpq_l->mpq_type, LIBMPQ_CONF_TYPE_CHAR, sizeof(mpq_a->mpq_l->mpq_type))) {
175                return LIBMPQ_CONF_EFILE_CORRUPT;
176        }
177
178        /* these are optional parameters, if they are empty we set the struct members empty */
179        libmpq_conf_get_value(fp, "MPQ_GAME", mpq_a->mpq_l->mpq_game, LIBMPQ_CONF_TYPE_CHAR, sizeof(mpq_a->mpq_l->mpq_game));
180        libmpq_conf_get_value(fp, "MPQ_GAME_VERSION", mpq_a->mpq_l->mpq_game_version, LIBMPQ_CONF_TYPE_CHAR, sizeof(mpq_a->mpq_l->mpq_game_version));
181
182        /* check if we found a valid listfile for the given archive */
183        if (mpq_a->header->hashtablesize == mpq_ht_size && mpq_a->header->blocktablesize == mpq_bt_size && mpq_a->blocksize == mpq_blocksize && libmpq_archive_info(mpq_a, LIBMPQ_MPQ_ARCHIVE_SIZE) == mpq_size && libmpq_archive_info(mpq_a, LIBMPQ_MPQ_NUMFILES) == mpq_files && libmpq_archive_info(mpq_a, LIBMPQ_MPQ_COMPRESSED_SIZE) == mpq_csize && libmpq_archive_info(mpq_a, LIBMPQ_MPQ_UNCOMPRESSED_SIZE) == mpq_fsize) {
184
185                /* check if the filelist is correct */
186                if (!libmpq_conf_get_array(fp, "FILE_NAMES", (char ***)&mpq_a->mpq_l->mpq_files, &entries)) {
187
188                        /* we have a corrupt filelist, so return */
189                        return LIBMPQ_CONF_EFILE_LIST_CORRUPT;
190                } else {
191
192                        /* now check if filelist entries matches number of files in the archive. */
193                        if (entries != libmpq_archive_info(mpq_a, LIBMPQ_MPQ_NUMFILES)) {
194                                libmpq_free_listfile((char **)mpq_a->mpq_l->mpq_files);
195                                mpq_a->mpq_l->mpq_files = NULL;
196                                return LIBMPQ_CONF_EFILE_LIST_CORRUPT;
197                        }
198                }
199        }
200
201        return LIBMPQ_TOOLS_SUCCESS;
202}
203
204/*
205 *  This function frees up the space reserved by libmpq_get_listfile()
206 */
207int libmpq_free_listfile(char **filelist) {
208        int i = 0;
209        while (filelist[i]) {
210                free(filelist[i++]);
211        }
212        free(filelist);
213
214        return LIBMPQ_TOOLS_SUCCESS;
215}
216
217/*
218 *  This function reads the directory and the subdirectories
219 *  of the listfile database and adds a entry to the lisfile
220 *  array.
221 */
222/*int libmpq_detect_listfile_rec(char path[PATH_MAX], char ***filelist, int *fl_count, int *fl_size) {
223        char nextpath[PATH_MAX];
224        DIR *dp = opendir(path);
225        FILE *fp;
226        struct dirent *entry;
227        struct stat statbuf;
228        char buf[LIBMPQ_CONF_BUFSIZE];
229
230        if (dp == NULL) {
231                return LIBMPQ_CONF_EOPEN_DIR;
232        } else {
233                while ((entry = readdir(dp)) != NULL) {
234                        if (strncmp(entry->d_name, ".", 1) == 0 || strncmp(entry->d_name, "..", 2) == 0) {
235                                continue;
236                        }
237                        if (strnlen(path, PATH_MAX) + strnlen(entry->d_name, PATH_MAX) + 2 > sizeof nextpath) {
238                                continue;
239                        }
240
241                        snprintf(nextpath, PATH_MAX, "%s/%s", path, entry->d_name);
242
243                        // check if file extension matches listdb file extension
244                        if (strncmp(&entry->d_name[strlen(entry->d_name) - strlen(LIBMPQ_CONF_EXT)], LIBMPQ_CONF_EXT, strlen(LIBMPQ_CONF_EXT)) == 0) {
245
246                                // check if it is really a listdb file
247                                if ((fp = fopen(nextpath, "r")) != NULL ) {
248                                        while (fgets(buf, LIBMPQ_CONF_BUFSIZE, fp) != NULL) {
249                                                char *line;
250
251                                                buf[strlen(buf) - 1] = '\0';
252
253                                                // skip whitespace
254                                                for (line = buf; isspace(*line); line++) {
255                                                        continue;
256                                                }
257
258                                                // skip empty line
259                                                if (line[0] == '\0') {
260                                                        continue;
261                                                }
262
263                                                // skip comments
264                                                if (line[0] == '#') {
265                                                        continue;
266                                                }
267
268                                                //search for listdb header; dirty but works :)
269                                                if (!strncasecmp(line, LIBMPQ_CONF_HEADER, strlen(LIBMPQ_CONF_HEADER))) {
270
271                                                        // set the next filelist entry to a copy of the file path
272                                                        (*filelist)[(*fl_count)++] = strdup(nextpath);
273
274                                                        // increase the array size
275                                                        if ((*fl_count) == (*fl_size)) {
276                                                                (*filelist) = realloc((*filelist), ((*fl_size) + LIBMPQ_CONF_FL_INCREMENT) * sizeof(char *));
277                                                                (*fl_size) += LIBMPQ_CONF_FL_INCREMENT;
278                                                        }
279
280                                                        // header found so we could stop reading the file.
281                                                        break;
282                                                }
283                                        }
284                                        fclose(fp);
285                                }
286                        }
287
288                        if (stat(nextpath, &statbuf) < 0) {
289                                continue;
290                        }
291
292                        // if entry ia a subdirectory, read it
293                        if (S_ISDIR(statbuf.st_mode)) {
294                                libmpq_detect_listfile_rec(nextpath, filelist, fl_count, fl_size);
295                        }
296                }
297                closedir(dp);
298        }
299
300        return LIBMPQ_TOOLS_SUCCESS;
301}
302*/
303
304/*
305 *  This functions tries to get file decryption key. The trick comes from block
306 *  positions which are stored at the begin of each compressed file. We know the
307 *  file size, that means we know number of blocks that means we know the first
308 *  int value in block position. And if we know encrypted and decrypted value,
309 *  we can find the decryption key.
310 */
311int libmpq_detect_fileseed(mpq_archive *mpq_a, unsigned int *block, unsigned int decrypted) {
312        unsigned int saveseed1;
313        unsigned int temp = *block ^ decrypted;         /* temp = seed1 + seed2 */
314        int i = 0;
315        temp -= 0xEEEEEEEE;                             /* temp = seed1 + mpq_a->buf[0x400 + (seed1 & 0xFF)] */
316
317        for (i = 0; i < 0x100; i++) {                   /* Try all 255 possibilities */
318                unsigned int seed1;
319                unsigned int seed2 = 0xEEEEEEEE;
320                unsigned int ch;
321
322                /* Try the first unsigned int's (We exactly know the value) */
323                seed1  = temp - mpq_a->buf[0x400 + i];
324                seed2 += mpq_a->buf[0x400 + (seed1 & 0xFF)];
325                ch     = block[0] ^ (seed1 + seed2);
326
327                if (ch != decrypted) {
328                        continue;
329                }
330
331                /* Add 1 because we are decrypting block positions */
332                saveseed1 = seed1 + 1;
333
334                /*
335                 *  If OK, continue and test the second value. We don't know exactly the value,
336                 *  but we know that the second one has lower 16 bits set to zero
337                 *  (no compressed block is larger than 0xFFFF bytes)
338                 */
339                seed1  = ((~seed1 << 0x15) + 0x11111111) | (seed1 >> 0x0B);
340                seed2  = ch + seed2 + (seed2 << 5) + 3;
341                seed2 += mpq_a->buf[0x400 + (seed1 & 0xFF)];
342                ch     = block[1] ^ (seed1 + seed2);
343                if ((ch & 0xFFFF0000) == 0) {
344                        return saveseed1;
345                }
346        }
347        return LIBMPQ_TOOLS_SUCCESS;
348}
349
350/*
351 *  This function initialize the decryption buffer
352 */
353int libmpq_init_buffer(mpq_archive *mpq_a) {
354        unsigned int seed   = 0x00100001;
355        unsigned int index1 = 0;
356        unsigned int index2 = 0;
357        int i;
358
359        memset(mpq_a->buf, 0, sizeof(mpq_a->buf));
360
361        /* Initialize the decryption buffer. */
362        for (index1 = 0; index1 < 0x100; index1++) {
363                for(index2 = index1, i = 0; i < 5; i++, index2 += 0x100) {
364                        unsigned int temp1, temp2;
365                        seed  = (seed * 125 + 3) % 0x2AAAAB;
366                        temp1 = (seed & 0xFFFF) << 0x10;
367
368                        seed  = (seed * 125 + 3) % 0x2AAAAB;
369                        temp2 = (seed & 0xFFFF);
370
371                        mpq_a->buf[index2] = (temp1 | temp2);
372                }
373        }
374        return LIBMPQ_TOOLS_SUCCESS;
375}
376
377/*
378 *  This functions fills the mpq_hash structure with the
379 *  hashtable found in the MPQ file. The hashtable will
380 *  be decrypted for later use.
381 */
382int libmpq_read_hashtable(mpq_archive *mpq_a) {
383        unsigned int bytes = 0;
384        int rb = 0;
385
386        /*
387         *  Allocate memory. Note that the block table should be as large as the
388         *  hash table. (for later file additions)
389         */
390        mpq_a->hashtable = (mpq_hash *)malloc(sizeof(mpq_hash) * mpq_a->header->hashtablesize);
391
392        if (!mpq_a->hashtable) {
393                return LIBMPQ_EALLOCMEM;
394        }
395
396        /* Read the hash table into the buffer */
397        bytes = mpq_a->header->hashtablesize * sizeof(mpq_hash);
398
399        #ifdef WIN32
400                _lseeki64(mpq_a->fd, mpq_a->header->hashtablepos, SEEK_SET);
401        #else
402                lseek64(mpq_a->fd, mpq_a->header->hashtablepos, SEEK_SET);
403        #endif
404
405        rb = _read(mpq_a->fd, mpq_a->hashtable, bytes);
406        if (rb != bytes) {
407                return LIBMPQ_EFILE_CORRUPT;
408        }
409
410        /* Decrypt hash table and check if it is correctly decrypted */
411        mpq_hash *mpq_h_end = mpq_a->hashtable + mpq_a->header->hashtablesize;
412        mpq_hash *mpq_h     = NULL;
413
414        libmpq_decrypt_hashtable(mpq_a, (unsigned char *)"(hash table)");
415
416        /* Check hash table if is correctly decrypted */
417        for (mpq_h = mpq_a->hashtable; mpq_h < mpq_h_end; mpq_h++) {
418                if (mpq_h->locale != 0xFFFFFFFF && (mpq_h->locale & 0xFFFF0000) != 0) {
419                        return LIBMPQ_EFILE_FORMAT;
420                }
421
422                /* Remember the highest block table entry */
423                if (mpq_h->blockindex < LIBMPQ_HASH_ENTRY_DELETED && mpq_h->blockindex > 0) {
424                        mpq_a->maxblockindex = mpq_h->blockindex;
425                }
426        }
427
428        return LIBMPQ_TOOLS_SUCCESS;
429}
430
431/*
432 *  This functions fills the mpq_block structure with the
433 *  blocktable found in the MPQ file. The blocktable will
434 *  be decrypted for later use.
435 *
436 *  NOTICE: Some MPQs have decrypted block table, e.g.
437 *          cracked Diablo versions.
438 */
439int libmpq_read_blocktable(mpq_archive *mpq_a) {
440        unsigned int bytes = 0;
441        int rb = 0;
442
443        /*
444         *  Allocate memory. Note that the block table should be as large as the
445         *  hash table. (for later file additions)
446         */
447        mpq_a->blocktable = (mpq_block *)malloc(sizeof(mpq_block) * mpq_a->header->hashtablesize);
448        mpq_a->blockbuf   = (unsigned char *)malloc(mpq_a->blocksize);
449
450        if (!mpq_a->blocktable || !mpq_a->blockbuf) {
451                return LIBMPQ_EALLOCMEM;
452        }
453
454        /* Read the block table into the buffer */
455        bytes = mpq_a->header->blocktablesize * sizeof(mpq_block);
456        memset(mpq_a->blocktable, 0, mpq_a->header->blocktablesize * sizeof(mpq_block));
457
458        #ifdef WIN32
459                _lseeki64(mpq_a->fd, mpq_a->header->blocktablepos, SEEK_SET);
460        #else
461                lseek64(mpq_a->fd, mpq_a->header->blocktablepos, SEEK_SET);
462        #endif 
463
464        rb = _read(mpq_a->fd, mpq_a->blocktable, bytes);
465        if (rb != bytes) {
466                return LIBMPQ_EFILE_CORRUPT;
467        }
468
469        /*
470         *  Decrypt block table. Some MPQs don't have encrypted block table,
471         *  e.g. cracked Diablo version. We have to check if block table is
472         *  already decrypted
473         */
474        mpq_block *mpq_b_end     = mpq_a->blocktable + mpq_a->maxblockindex + 1;
475        mpq_block *mpq_b         = NULL;
476        unsigned int archivesize = mpq_a->header->archivesize + mpq_a->mpqpos;
477
478        if (mpq_a->header->offset != mpq_a->blocktable->filepos) {
479                libmpq_decrypt_blocktable(mpq_a, (unsigned char *)"(block table)");
480        }
481        for (mpq_b = mpq_a->blocktable; mpq_b < mpq_b_end; mpq_b++) {
482                if (mpq_b->filepos > archivesize || mpq_b->csize > archivesize) {
483                        if ((mpq_a->flags & LIBMPQ_FLAG_PROTECTED) == 0) {
484                                return LIBMPQ_EFILE_FORMAT;
485                        }
486                }
487                mpq_b->filepos += mpq_a->mpqpos;
488        }
489
490        return LIBMPQ_TOOLS_SUCCESS;
491}
492
493int libmpq_file_read_block(mpq_archive *mpq_a, mpq_file *mpq_f, unsigned int blockpos, char *buffer, unsigned int blockbytes) {
494        unsigned char *tempbuf = NULL;                  /* Buffer for reading compressed data from the file */
495        unsigned int readpos;                           /* Reading position from the file */
496        unsigned int toread = 0;                        /* Number of bytes to read */
497        unsigned int blocknum;                          /* Block number (needed for decrypt) */
498        unsigned int bytesread = 0;                     /* Total number of bytes read */
499        unsigned int nblocks;                           /* Number of blocks to load */
500        unsigned int i;
501
502        /* Test parameters. Block position and block size must be block-aligned, block size nonzero */
503        if ((blockpos & (mpq_a->blocksize - 1)) || blockbytes == 0) {
504                return 0;
505        }
506
507        /* Check the end of file */
508        if ((blockpos + blockbytes) > mpq_f->mpq_b->fsize) {
509                blockbytes = mpq_f->mpq_b->fsize - blockpos;
510        }
511        blocknum = blockpos   / mpq_a->blocksize;
512        nblocks  = blockbytes / mpq_a->blocksize;
513        if (blockbytes % mpq_a->blocksize) {
514                nblocks++;
515        }
516
517        /* If file has variable block positions, we have to load them */
518        if ((mpq_f->mpq_b->flags & LIBMPQ_FILE_COMPRESSED) && mpq_f->blockposloaded == FALSE) {
519                unsigned int nread;
520
521                if (mpq_f->mpq_b->filepos != mpq_a->filepos) {
522                #ifdef WIN32
523                        _lseeki64(mpq_a->fd, mpq_f->mpq_b->filepos, SEEK_SET);
524                #else
525                        lseek64(mpq_a->fd, mpq_f->mpq_b->filepos, SEEK_SET);
526
527                #endif
528                }
529
530                /* Read block positions from begin of file. */
531                nread = (mpq_f->nblocks + 1) * sizeof(int);
532                nread = _read(mpq_a->fd, mpq_f->blockpos, nread);
533
534                /*
535                 *  If the archive is protected some way, perform additional check
536                 *  Sometimes, the file appears not to be encrypted, but it is.
537                 */
538                /*if (mpq_f->blockpos[0] != nread) {
539                        mpq_f->mpq_b->flags |= LIBMPQ_FILE_ENCRYPTED;
540                }*/
541
542        if ((mpq_f->mpq_b->flags & LIBMPQ_FILE_HAS_METADATA) == 0) {
543            if (mpq_f->blockpos[0] != nread) {
544                            mpq_f->mpq_b->flags |= LIBMPQ_FILE_ENCRYPTED;
545                    }
546        }
547
548                /* Decrypt loaded block positions if necessary */
549                if (mpq_f->mpq_b->flags & LIBMPQ_FILE_ENCRYPTED) {
550
551                        /* If we don't know the file seed, try to find it. */
552                        if (mpq_f->seed == 0) {
553                                mpq_f->seed = libmpq_detect_fileseed(mpq_a, mpq_f->blockpos, nread);
554                        }
555
556                        /* If we don't know the file seed, sorry but we cannot extract the file. */
557                        if (mpq_f->seed == 0) {
558                                return 0;
559                        }
560
561                        /* Decrypt block positions */
562                        libmpq_decrypt_block(mpq_a, mpq_f->blockpos, nread, mpq_f->seed - 1);
563
564                        /*
565                         *  Check if the block positions are correctly decrypted
566                         *  I don't know why, but sometimes it will result invalid
567                         *  block positions on some files.
568                         */
569                        if (mpq_f->blockpos[0] != nread) {
570
571                                /* Try once again to detect file seed and decrypt the blocks */
572
573                                #ifdef WIN32
574                                        _lseeki64(mpq_a->fd, mpq_f->mpq_b->filepos, SEEK_SET);
575                                #else
576                                        lseek64(mpq_a->fd, mpq_f->mpq_b->filepos, SEEK_SET);
577                                #endif                         
578
579                                nread = _read(mpq_a->fd, mpq_f->blockpos, (mpq_f->nblocks + 1) * sizeof(int));
580                                mpq_f->seed = libmpq_detect_fileseed(mpq_a, mpq_f->blockpos, nread);
581                                libmpq_decrypt_block(mpq_a, mpq_f->blockpos, nread, mpq_f->seed - 1);
582
583                                /* Check if the block positions are correctly decrypted. */
584                                if (mpq_f->blockpos[0] != nread) {
585                                        return 0;
586                                }
587                        }
588                }
589
590                /* Update mpq_f's variables */
591                mpq_f->blockposloaded = TRUE;
592                mpq_a->filepos        = mpq_f->mpq_b->filepos + nread;
593        }
594
595        /* Get file position and number of bytes to read */
596        readpos = blockpos;
597        toread  = blockbytes;
598
599        if (mpq_f->mpq_b->flags & LIBMPQ_FILE_COMPRESSED) {
600                readpos = mpq_f->blockpos[blocknum];
601                toread  = mpq_f->blockpos[blocknum + nblocks] - readpos;
602        }
603
604        readpos += mpq_f->mpq_b->filepos;
605
606        /* Get work buffer for store read data */
607    if ((tempbuf = (unsigned char *)malloc(toread)) == NULL) {
608        /* Hmmm... We should add a better error handling here :) */
609        return 0;
610    }
611
612        /* Set file pointer, if necessary. */
613        if (mpq_a->filepos != readpos) {
614
615                #ifdef WIN32
616                        mpq_a->filepos = _lseeki64(mpq_a->fd, readpos, SEEK_SET);
617                #else
618                        mpq_a->filepos = lseek64(mpq_a->fd, readpos, SEEK_SET);
619                #endif 
620
621        }
622
623        /* 15018F87 - Read all requested blocks. */
624        bytesread = _read(mpq_a->fd, tempbuf, toread);
625        mpq_a->filepos = readpos + bytesread;
626
627        /* Block processing part. */
628        unsigned int blockstart = 0;                    /* Index of block start in work buffer. */
629        unsigned int blocksize  = min(blockbytes, mpq_a->blocksize);
630        unsigned int index      = blocknum;             /* Current block index. */
631        bytesread = 0;                                  /* Clear read byte counter */
632
633        /* Walk through all blocks. */
634        for (i = 0; i < nblocks; i++, index++) {
635                int outlength = mpq_a->blocksize;
636
637                /* Get current block length */
638                if (mpq_f->mpq_b->flags & LIBMPQ_FILE_COMPRESSED) {
639                        blocksize = mpq_f->blockpos[index + 1] - mpq_f->blockpos[index];
640                }
641
642                /* If block is encrypted, we have to decrypt it. */
643                if (mpq_f->mpq_b->flags & LIBMPQ_FILE_ENCRYPTED) {
644                        if (mpq_f->seed == 0) {
645                                return 0;
646                        }
647                        libmpq_decrypt_block(mpq_a, (unsigned int *)&tempbuf[blockstart], blocksize, mpq_f->seed + index);
648                }
649
650                /*
651                 *  If the block is really compressed, recompress it.
652                 *  WARNING: Some block may not be compressed, it can
653                 *  only be determined by comparing uncompressed and
654                 *  compressed size!
655                 */
656                if (blocksize < blockbytes) {
657
658                        /* Is the file compressed with PKWARE Data Compression Library? */
659                        if (mpq_f->mpq_b->flags & LIBMPQ_FILE_COMPRESS_PKWARE) {
660                                libmpq_pkzip_decompress(buffer, &outlength, (char *)&tempbuf[blockstart], blocksize);
661                        }
662
663                        /*
664                         *  Is it a file compressed by Blizzard's multiple compression ?
665                         *  Note that Storm.dll v 1.0.9 distributed with Warcraft III
666                         *  passes the full path name of the opened archive as the new
667                         *  last parameter.
668                         */
669                        if (mpq_f->mpq_b->flags & LIBMPQ_FILE_COMPRESS_MULTI) {
670                                libmpq_multi_decompress(buffer, &outlength, (char *)&tempbuf[blockstart], blocksize);
671                        }
672                        bytesread += outlength;
673                        buffer    += outlength;
674                } else {
675                        memcpy(buffer, tempbuf, blocksize);
676                        bytesread += blocksize;
677                        buffer    += blocksize;
678                }
679                blockstart += blocksize;
680        }
681
682        /* Delete input buffer, if necessary. */
683    free(tempbuf);
684
685    return bytesread;
686}
687
688int libmpq_file_read_file(mpq_archive *mpq_a, mpq_file *mpq_f, unsigned int filepos, char *buffer, unsigned int toread) {
689        unsigned int bytesread = 0;                     /* Number of bytes read from the file */
690        unsigned int blockpos;                          /* Position in the file aligned to the whole blocks */
691        unsigned int loaded = 0;
692
693        /* File position is greater or equal to file size? */
694        if (filepos >= mpq_f->mpq_b->fsize) {
695                return 0;
696        }
697
698        /* If to few bytes in the file remaining, cut them */
699        if ((mpq_f->mpq_b->fsize - filepos) < toread) {
700                toread = (mpq_f->mpq_b->fsize - filepos);
701        }
702
703        /* Block position in the file */
704        blockpos = filepos & ~(mpq_a->blocksize - 1);
705
706        /*
707         *  Load the first block, if noncomplete. It may be loaded in the cache buffer.
708         *  We have to check if this block is loaded. If not, load it.
709         */
710        if ((filepos % mpq_a->blocksize) != 0) {
711                /* Number of bytes remaining in the buffer */
712                unsigned int tocopy;
713                unsigned int loaded = mpq_a->blocksize;
714
715                /* Check if data are loaded in the cache */
716                if (mpq_f->accessed == FALSE || blockpos != mpq_a->blockpos) {   
717
718                        /* Load one MPQ block into archive buffer */
719                        loaded = libmpq_file_read_block(mpq_a, mpq_f, blockpos, (char *)mpq_a->blockbuf, mpq_a->blocksize);
720                        if (loaded == 0) {
721                                return 0;
722                        }
723
724                        /* Save lastly accessed file and block position for later use */
725                        mpq_f->accessed = TRUE;
726                        mpq_a->blockpos = blockpos;
727                        mpq_a->bufpos   = filepos % mpq_a->blocksize;
728                }
729                tocopy = loaded - mpq_a->bufpos;
730                if (tocopy > toread) {
731                        tocopy = toread;
732                }
733
734                /* Copy data from block buffer into target buffer */
735                memcpy(buffer, mpq_a->blockbuf + mpq_a->bufpos, tocopy);
736
737                /* Update pointers */
738                toread        -= tocopy;
739                bytesread     += tocopy;
740                buffer        += tocopy;
741                blockpos      += mpq_a->blocksize;
742                mpq_a->bufpos += tocopy;
743
744                /* If all, return. */
745                if (toread == 0) {
746                        return bytesread;
747                }
748        }
749
750        /* Load the whole ("middle") blocks only if there are more or equal one block */
751        if (toread > mpq_a->blocksize) {
752                unsigned int blockbytes = toread & ~(mpq_a->blocksize - 1);
753                loaded = libmpq_file_read_block(mpq_a, mpq_f, blockpos, buffer, blockbytes);
754                if (loaded == 0) {
755                        return 0;
756                }
757
758                /* Update pointers */
759                toread    -= loaded;
760                bytesread += loaded;
761                buffer    += loaded;
762                blockpos  += loaded;
763
764                /* If all, return. */
765                if (toread == 0) {
766                        return bytesread;
767                }
768        }
769
770        /* Load the terminating block */
771        if (toread > 0) {
772                unsigned int tocopy = mpq_a->blocksize;
773
774                /* Check if data are loaded in the cache */
775                if (mpq_f->accessed == FALSE || blockpos != mpq_a->blockpos) {
776
777                        /* Load one MPQ block into archive buffer */
778                        tocopy = libmpq_file_read_block(mpq_a, mpq_f, blockpos, (char *)mpq_a->blockbuf, mpq_a->blocksize);
779                        if (tocopy == 0) {
780                                return 0;
781                        }
782
783                        /* Save lastly accessed file and block position for later use */
784                        mpq_f->accessed = TRUE;
785                        mpq_a->blockpos = blockpos;
786                }
787                mpq_a->bufpos  = 0;
788
789                /* Check number of bytes read */
790                if (tocopy > toread) {
791                        tocopy = toread;
792                }
793
794                memcpy(buffer, mpq_a->blockbuf, tocopy);
795                bytesread     += tocopy;
796                mpq_a->bufpos  = tocopy;
797        }
798
799        /* Return what we've read */
800        return bytesread;
801}
Note: See TracBrowser for help on using the browser.