📄 common.cpp
字号:
lseek64(mpq_a->fd, mpq_a->header->hashtablepos, SEEK_SET);
#endif
rb = _read(mpq_a->fd, mpq_a->hashtable, bytes);
if (rb != bytes) {
return LIBMPQ_EFILE_CORRUPT;
}
/* Decrypt hash table and check if it is correctly decrypted */
mpq_hash *mpq_h_end = mpq_a->hashtable + mpq_a->header->hashtablesize;
mpq_hash *mpq_h = NULL;
libmpq_decrypt_hashtable(mpq_a, (unsigned char *)"(hash table)");
/* Check hash table if is correctly decrypted */
for (mpq_h = mpq_a->hashtable; mpq_h < mpq_h_end; mpq_h++) {
if (mpq_h->locale != 0xFFFFFFFF && (mpq_h->locale & 0xFFFF0000) != 0) {
return LIBMPQ_EFILE_FORMAT;
}
/* Remember the highest block table entry */
if (mpq_h->blockindex < LIBMPQ_HASH_ENTRY_DELETED && mpq_h->blockindex > 0) {
mpq_a->maxblockindex = mpq_h->blockindex;
}
}
return LIBMPQ_TOOLS_SUCCESS;
}
/*
* This functions fills the mpq_block structure with the
* blocktable found in the MPQ file. The blocktable will
* be decrypted for later use.
*
* NOTICE: Some MPQs have decrypted block table, e.g.
* cracked Diablo versions.
*/
int libmpq_read_blocktable(mpq_archive *mpq_a) {
unsigned int bytes = 0;
int rb = 0;
/*
* Allocate memory. Note that the block table should be as large as the
* hash table. (for later file additions)
*/
mpq_a->blocktable = (mpq_block *)malloc(sizeof(mpq_block) * mpq_a->header->hashtablesize);
mpq_a->blockbuf = (unsigned char *)malloc(mpq_a->blocksize);
if (!mpq_a->blocktable || !mpq_a->blockbuf) {
return LIBMPQ_EALLOCMEM;
}
/* Read the block table into the buffer */
bytes = mpq_a->header->blocktablesize * sizeof(mpq_block);
memset(mpq_a->blocktable, 0, mpq_a->header->blocktablesize * sizeof(mpq_block));
#ifdef WIN32
_lseeki64(mpq_a->fd, mpq_a->header->blocktablepos, SEEK_SET);
#else
lseek64(mpq_a->fd, mpq_a->header->blocktablepos, SEEK_SET);
#endif
rb = _read(mpq_a->fd, mpq_a->blocktable, bytes);
if (rb != bytes) {
return LIBMPQ_EFILE_CORRUPT;
}
/*
* Decrypt block table. Some MPQs don't have encrypted block table,
* e.g. cracked Diablo version. We have to check if block table is
* already decrypted
*/
mpq_block *mpq_b_end = mpq_a->blocktable + mpq_a->maxblockindex + 1;
mpq_block *mpq_b = NULL;
unsigned int archivesize = mpq_a->header->archivesize + mpq_a->mpqpos;
if (mpq_a->header->offset != mpq_a->blocktable->filepos) {
libmpq_decrypt_blocktable(mpq_a, (unsigned char *)"(block table)");
}
for (mpq_b = mpq_a->blocktable; mpq_b < mpq_b_end; mpq_b++) {
if (mpq_b->filepos > archivesize || mpq_b->csize > archivesize) {
if ((mpq_a->flags & LIBMPQ_FLAG_PROTECTED) == 0) {
return LIBMPQ_EFILE_FORMAT;
}
}
mpq_b->filepos += mpq_a->mpqpos;
}
return LIBMPQ_TOOLS_SUCCESS;
}
int libmpq_file_read_block(mpq_archive *mpq_a, mpq_file *mpq_f, unsigned int blockpos, char *buffer, unsigned int blockbytes) {
unsigned char *tempbuf = NULL; /* Buffer for reading compressed data from the file */
unsigned int readpos; /* Reading position from the file */
unsigned int toread = 0; /* Number of bytes to read */
unsigned int blocknum; /* Block number (needed for decrypt) */
unsigned int bytesread = 0; /* Total number of bytes read */
unsigned int nblocks; /* Number of blocks to load */
unsigned int i;
/* Test parameters. Block position and block size must be block-aligned, block size nonzero */
if ((blockpos & (mpq_a->blocksize - 1)) || blockbytes == 0) {
return 0;
}
/* Check the end of file */
if ((blockpos + blockbytes) > mpq_f->mpq_b->fsize) {
blockbytes = mpq_f->mpq_b->fsize - blockpos;
}
blocknum = blockpos / mpq_a->blocksize;
nblocks = blockbytes / mpq_a->blocksize;
if (blockbytes % mpq_a->blocksize) {
nblocks++;
}
/* If file has variable block positions, we have to load them */
if ((mpq_f->mpq_b->flags & LIBMPQ_FILE_COMPRESSED) && mpq_f->blockposloaded == FALSE) {
unsigned int nread;
if (mpq_f->mpq_b->filepos != mpq_a->filepos) {
#ifdef WIN32
_lseeki64(mpq_a->fd, mpq_f->mpq_b->filepos, SEEK_SET);
#else
lseek64(mpq_a->fd, mpq_f->mpq_b->filepos, SEEK_SET);
#endif
}
/* Read block positions from begin of file. */
nread = (mpq_f->nblocks + 1) * sizeof(int);
nread = _read(mpq_a->fd, mpq_f->blockpos, nread);
/*
* If the archive is protected some way, perform additional check
* Sometimes, the file appears not to be encrypted, but it is.
*/
/*if (mpq_f->blockpos[0] != nread) {
mpq_f->mpq_b->flags |= LIBMPQ_FILE_ENCRYPTED;
}*/
if ((mpq_f->mpq_b->flags & LIBMPQ_FILE_HAS_METADATA) == 0) {
if (mpq_f->blockpos[0] != nread) {
mpq_f->mpq_b->flags |= LIBMPQ_FILE_ENCRYPTED;
}
}
/* Decrypt loaded block positions if necessary */
if (mpq_f->mpq_b->flags & LIBMPQ_FILE_ENCRYPTED) {
/* If we don't know the file seed, try to find it. */
if (mpq_f->seed == 0) {
mpq_f->seed = libmpq_detect_fileseed(mpq_a, mpq_f->blockpos, nread);
}
/* If we don't know the file seed, sorry but we cannot extract the file. */
if (mpq_f->seed == 0) {
return 0;
}
/* Decrypt block positions */
libmpq_decrypt_block(mpq_a, mpq_f->blockpos, nread, mpq_f->seed - 1);
/*
* Check if the block positions are correctly decrypted
* I don't know why, but sometimes it will result invalid
* block positions on some files.
*/
if (mpq_f->blockpos[0] != nread) {
/* Try once again to detect file seed and decrypt the blocks */
#ifdef WIN32
_lseeki64(mpq_a->fd, mpq_f->mpq_b->filepos, SEEK_SET);
#else
lseek64(mpq_a->fd, mpq_f->mpq_b->filepos, SEEK_SET);
#endif
nread = _read(mpq_a->fd, mpq_f->blockpos, (mpq_f->nblocks + 1) * sizeof(int));
mpq_f->seed = libmpq_detect_fileseed(mpq_a, mpq_f->blockpos, nread);
libmpq_decrypt_block(mpq_a, mpq_f->blockpos, nread, mpq_f->seed - 1);
/* Check if the block positions are correctly decrypted. */
if (mpq_f->blockpos[0] != nread) {
return 0;
}
}
}
/* Update mpq_f's variables */
mpq_f->blockposloaded = TRUE;
mpq_a->filepos = mpq_f->mpq_b->filepos + nread;
}
/* Get file position and number of bytes to read */
readpos = blockpos;
toread = blockbytes;
if (mpq_f->mpq_b->flags & LIBMPQ_FILE_COMPRESSED) {
readpos = mpq_f->blockpos[blocknum];
toread = mpq_f->blockpos[blocknum + nblocks] - readpos;
}
readpos += mpq_f->mpq_b->filepos;
/* Get work buffer for store read data */
if ((tempbuf = (unsigned char *)malloc(toread)) == NULL) {
/* Hmmm... We should add a better error handling here :) */
return 0;
}
/* Set file pointer, if necessary. */
if (mpq_a->filepos != readpos) {
#ifdef WIN32
mpq_a->filepos = _lseeki64(mpq_a->fd, readpos, SEEK_SET);
#else
mpq_a->filepos = lseek64(mpq_a->fd, readpos, SEEK_SET);
#endif
}
/* 15018F87 - Read all requested blocks. */
bytesread = _read(mpq_a->fd, tempbuf, toread);
mpq_a->filepos = readpos + bytesread;
/* Block processing part. */
unsigned int blockstart = 0; /* Index of block start in work buffer. */
unsigned int blocksize = min(blockbytes, mpq_a->blocksize);
unsigned int index = blocknum; /* Current block index. */
bytesread = 0; /* Clear read byte counter */
/* Walk through all blocks. */
for (i = 0; i < nblocks; i++, index++) {
int outlength = mpq_a->blocksize;
/* Get current block length */
if (mpq_f->mpq_b->flags & LIBMPQ_FILE_COMPRESSED) {
blocksize = mpq_f->blockpos[index + 1] - mpq_f->blockpos[index];
}
/* If block is encrypted, we have to decrypt it. */
if (mpq_f->mpq_b->flags & LIBMPQ_FILE_ENCRYPTED) {
if (mpq_f->seed == 0) {
return 0;
}
libmpq_decrypt_block(mpq_a, (unsigned int *)&tempbuf[blockstart], blocksize, mpq_f->seed + index);
}
/*
* If the block is really compressed, recompress it.
* WARNING: Some block may not be compressed, it can
* only be determined by comparing uncompressed and
* compressed size!
*/
if (blocksize < blockbytes) {
/* Is the file compressed with PKWARE Data Compression Library? */
if (mpq_f->mpq_b->flags & LIBMPQ_FILE_COMPRESS_PKWARE) {
libmpq_pkzip_decompress(buffer, &outlength, (char *)&tempbuf[blockstart], blocksize);
}
/*
* Is it a file compressed by Blizzard's multiple compression ?
* Note that Storm.dll v 1.0.9 distributed with Warcraft III
* passes the full path name of the opened archive as the new
* last parameter.
*/
if (mpq_f->mpq_b->flags & LIBMPQ_FILE_COMPRESS_MULTI) {
libmpq_multi_decompress(buffer, &outlength, (char *)&tempbuf[blockstart], blocksize);
}
bytesread += outlength;
buffer += outlength;
} else {
memcpy(buffer, tempbuf, blocksize);
bytesread += blocksize;
buffer += blocksize;
}
blockstart += blocksize;
}
/* Delete input buffer, if necessary. */
free(tempbuf);
return bytesread;
}
int libmpq_file_read_file(mpq_archive *mpq_a, mpq_file *mpq_f, unsigned int filepos, char *buffer, unsigned int toread) {
unsigned int bytesread = 0; /* Number of bytes read from the file */
unsigned int blockpos; /* Position in the file aligned to the whole blocks */
unsigned int loaded = 0;
/* File position is greater or equal to file size? */
if (filepos >= mpq_f->mpq_b->fsize) {
return 0;
}
/* If to few bytes in the file remaining, cut them */
if ((mpq_f->mpq_b->fsize - filepos) < toread) {
toread = (mpq_f->mpq_b->fsize - filepos);
}
/* Block position in the file */
blockpos = filepos & ~(mpq_a->blocksize - 1);
/*
* Load the first block, if noncomplete. It may be loaded in the cache buffer.
* We have to check if this block is loaded. If not, load it.
*/
if ((filepos % mpq_a->blocksize) != 0) {
/* Number of bytes remaining in the buffer */
unsigned int tocopy;
unsigned int loaded = mpq_a->blocksize;
/* Check if data are loaded in the cache */
if (mpq_f->accessed == FALSE || blockpos != mpq_a->blockpos) {
/* Load one MPQ block into archive buffer */
loaded = libmpq_file_read_block(mpq_a, mpq_f, blockpos, (char *)mpq_a->blockbuf, mpq_a->blocksize);
if (loaded == 0) {
return 0;
}
/* Save lastly accessed file and block position for later use */
mpq_f->accessed = TRUE;
mpq_a->blockpos = blockpos;
mpq_a->bufpos = filepos % mpq_a->blocksize;
}
tocopy = loaded - mpq_a->bufpos;
if (tocopy > toread) {
tocopy = toread;
}
/* Copy data from block buffer into target buffer */
memcpy(buffer, mpq_a->blockbuf + mpq_a->bufpos, tocopy);
/* Update pointers */
toread -= tocopy;
bytesread += tocopy;
buffer += tocopy;
blockpos += mpq_a->blocksize;
mpq_a->bufpos += tocopy;
/* If all, return. */
if (toread == 0) {
return bytesread;
}
}
/* Load the whole ("middle") blocks only if there are more or equal one block */
if (toread > mpq_a->blocksize) {
unsigned int blockbytes = toread & ~(mpq_a->blocksize - 1);
loaded = libmpq_file_read_block(mpq_a, mpq_f, blockpos, buffer, blockbytes);
if (loaded == 0) {
return 0;
}
/* Update pointers */
toread -= loaded;
bytesread += loaded;
buffer += loaded;
blockpos += loaded;
/* If all, return. */
if (toread == 0) {
return bytesread;
}
}
/* Load the terminating block */
if (toread > 0) {
unsigned int tocopy = mpq_a->blocksize;
/* Check if data are loaded in the cache */
if (mpq_f->accessed == FALSE || blockpos != mpq_a->blockpos) {
/* Load one MPQ block into archive buffer */
tocopy = libmpq_file_read_block(mpq_a, mpq_f, blockpos, (char *)mpq_a->blockbuf, mpq_a->blocksize);
if (tocopy == 0) {
return 0;
}
/* Save lastly accessed file and block position for later use */
mpq_f->accessed = TRUE;
mpq_a->blockpos = blockpos;
}
mpq_a->bufpos = 0;
/* Check number of bytes read */
if (tocopy > toread) {
tocopy = toread;
}
memcpy(buffer, mpq_a->blockbuf, tocopy);
bytesread += tocopy;
mpq_a->bufpos = tocopy;
}
/* Return what we've read */
return bytesread;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -