// Adds a name into the list of all names. For each locale in the MPQ, // one entry will be created // If the file name is already there, does nothing. static int SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFileName) { TMPQHeader * pHeader = ha->pHeader; TFileEntry * pFileEntry; TMPQHash * pFirstHash; TMPQHash * pHash; bool bNameEntryCreated = false; // If we have HET table, use that one if(ha->pHetTable != NULL) { pFileEntry = GetFileEntryAny(ha, szFileName); if(pFileEntry != NULL) { // Allocate file name for the file entry AllocateFileName(pFileEntry, szFileName); bNameEntryCreated = true; } return ERROR_SUCCESS; } // If we have hash table, we use it if(bNameEntryCreated == false && ha->pHashTable != NULL) { // Look for the first hash table entry for the file pFirstHash = pHash = GetFirstHashEntry(ha, szFileName); // Go while we found something while(pHash != NULL) { // Is it a valid file table index ? if(pHash->dwBlockIndex < pHeader->dwBlockTableSize) { // Allocate file name for the file entry AllocateFileName(ha->pFileTable + pHash->dwBlockIndex, szFileName); bNameEntryCreated = true; } // Now find the next language version of the file pHash = GetNextHashEntry(ha, pFirstHash, pHash); } } return ERROR_CAN_NOT_COMPLETE; }
static int CreatePseudoFileName(void * hFile, TFileEntry * pFileEntry, char * szFileName) { TMPQFile * hf = (TMPQFile *)hFile; /* MPQ File handle */ uint32_t FirstBytes[2] = {0, 0}; /* The first 4 bytes of the file */ size_t dwBytesRead = 0; size_t dwFilePos; /* Saved file position */ /* Read the first 2 uint32_ts bytes from the file */ dwFilePos = SFileSetFilePointer(hFile, 0, NULL, SEEK_CUR); SFileReadFile(hFile, FirstBytes, sizeof(FirstBytes), &dwBytesRead); SFileSetFilePointer(hFile, dwFilePos, NULL, SEEK_SET); /* If we read at least 8 bytes */ if(dwBytesRead == sizeof(FirstBytes)) { size_t i; /* Make sure that the array is properly BSWAP-ed */ BSWAP_ARRAY32_UNSIGNED(FirstBytes, sizeof(FirstBytes)); /* Try to guess file extension from those 2 ints */ for(i = 0; data2ext[i].szExt != NULL; i++) { if((FirstBytes[0] & data2ext[i].dwOffset00Mask) == data2ext[i].dwOffset00Data && (FirstBytes[1] & data2ext[i].dwOffset04Mask) == data2ext[i].dwOffset04Data) { char szPseudoName[20] = ""; /* Format the pseudo-name */ sprintf(szPseudoName, "File%08u.%s", (unsigned int)(pFileEntry - hf->ha->pFileTable), data2ext[i].szExt); /* Save the pseudo-name in the file entry as well */ AllocateFileName(hf->ha, pFileEntry, szPseudoName); /* If the caller wants to copy the file name, do it */ if(szFileName != NULL) strcpy(szFileName, szPseudoName); return ERROR_SUCCESS; } } } return ERROR_CAN_NOT_COMPLETE; }
void LoadFileTimesAndSizes( BOOL fUseSuckDATFile ) { CHAR cPath[MAX_PATH]; FILEHEADER fileheader; int iCount; // // Initialize the tree root // fnRoot = AllocateFileName(); fnRoot->fnParent.mem_ptr = NULL; fnRoot->fnChild.mem_ptr = NULL; fnRoot->fnSibling.mem_ptr = NULL; strcpy( fnRoot->cFileName, "<ROOT>" ); // Look for SUCK.DAT if ( fUseSuckDATFile ) { SuckDATFile = fopen( SUCK_DAT_FILE, "rb" ); } else { SuckDATFile == NULL; } if ( SuckDATFile != NULL ) { // // If file exists, then load the data from it. // printf("Loading Previous Statistics...\n"); iCount = fread( &fileheader, sizeof(fileheader), 1, SuckDATFile ); if ( iCount != 1 ) { printf("Error reading SUCK.DAT file, remove and restart\n"); exit(1); } EnumDATFileData( fnRoot, fileheader.fnRoot.disk_ptr ); fclose( SuckDATFile ); } }
static int CreatePseudoFileName(HANDLE hFile, TFileEntry * pFileEntry, char * szFileName) { TMPQFile * hf = (TMPQFile *)hFile; // MPQ File handle DWORD FirstBytes[2] = {0, 0}; // The first 4 bytes of the file DWORD dwBytesRead = 0; DWORD dwFilePos; // Saved file position // Read the first 2 DWORDs bytes from the file dwFilePos = SFileSetFilePointer(hFile, 0, NULL, FILE_CURRENT); SFileReadFile(hFile, FirstBytes, sizeof(FirstBytes), &dwBytesRead, NULL); SFileSetFilePointer(hFile, dwFilePos, NULL, FILE_BEGIN); // If we read at least 8 bytes if(dwBytesRead == sizeof(FirstBytes)) { // Make sure that the array is properly BSWAP-ed BSWAP_ARRAY32_UNSIGNED(FirstBytes, sizeof(FirstBytes)); // Try to guess file extension from those 2 DWORDs for(size_t i = 0; data2ext[i].szExt != NULL; i++) { if((FirstBytes[0] & data2ext[i].dwOffset00Mask) == data2ext[i].dwOffset00Data && (FirstBytes[1] & data2ext[i].dwOffset04Mask) == data2ext[i].dwOffset04Data) { char szPseudoName[20] = ""; // Format the pseudo-name sprintf(szPseudoName, "File%08u.%s", (unsigned int)(pFileEntry - hf->ha->pFileTable), data2ext[i].szExt); // Save the pseudo-name in the file entry as well AllocateFileName(hf->ha, pFileEntry, szPseudoName); // If the caller wants to copy the file name, do it if(szFileName != NULL) strcpy(szFileName, szPseudoName); return ERROR_SUCCESS; } } } return ERROR_CAN_NOT_COMPLETE; }
bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * phFile) { TMPQArchive * ha = (TMPQArchive *)hMpq; TFileEntry * pFileEntry = NULL; TMPQFile * hf = NULL; DWORD dwFileIndex = 0; bool bOpenByIndex = false; int nError = ERROR_SUCCESS; // Don't accept NULL pointer to file handle if(phFile == NULL) nError = ERROR_INVALID_PARAMETER; // Prepare the file opening if(nError == ERROR_SUCCESS) { switch(dwSearchScope) { case SFILE_OPEN_FROM_MPQ: case SFILE_OPEN_BASE_FILE: if(!IsValidMpqHandle(ha)) { nError = ERROR_INVALID_HANDLE; break; } if(szFileName == NULL || *szFileName == 0) { nError = ERROR_INVALID_PARAMETER; break; } // Check the pseudo-file name if(IsPseudoFileName(szFileName, &dwFileIndex)) { pFileEntry = GetFileEntryByIndex(ha, dwFileIndex); bOpenByIndex = true; if(pFileEntry == NULL) nError = ERROR_FILE_NOT_FOUND; } else { // If this MPQ is a patched archive, open the file as patched if(ha->haPatch == NULL || dwSearchScope == SFILE_OPEN_BASE_FILE) { // Otherwise, open the file from *this* MPQ pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale); if(pFileEntry == NULL) nError = ERROR_FILE_NOT_FOUND; } else { return OpenPatchedFile(hMpq, szFileName, 0, phFile); } } break; case SFILE_OPEN_ANY_LOCALE: // This open option is reserved for opening MPQ internal listfile. // No argument validation. Tries to open file with neutral locale first, // then any other available. pFileEntry = GetFileEntryAny(ha, szFileName); if(pFileEntry == NULL) nError = ERROR_FILE_NOT_FOUND; break; case SFILE_OPEN_LOCAL_FILE: if(szFileName == NULL || *szFileName == 0) { nError = ERROR_INVALID_PARAMETER; break; } return OpenLocalFile(szFileName, phFile); default: // Don't accept any other value nError = ERROR_INVALID_PARAMETER; break; } // Quick return if something failed if(nError != ERROR_SUCCESS) { SetLastError(nError); return false; } } // Test if the file was not already deleted. if(nError == ERROR_SUCCESS) { if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0) nError = ERROR_FILE_NOT_FOUND; if(pFileEntry->dwFlags & ~MPQ_FILE_VALID_FLAGS) nError = ERROR_NOT_SUPPORTED; } // Allocate file handle if(nError == ERROR_SUCCESS) { if((hf = STORM_ALLOC(TMPQFile, 1)) == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; } // Initialize file handle if(nError == ERROR_SUCCESS) { memset(hf, 0, sizeof(TMPQFile)); hf->pFileEntry = pFileEntry; hf->dwMagic = ID_MPQ_FILE; hf->ha = ha; hf->MpqFilePos = pFileEntry->ByteOffset; hf->RawFilePos = ha->MpqPos + hf->MpqFilePos; hf->dwDataSize = pFileEntry->dwFileSize; // If the MPQ has sector CRC enabled, enable if for the file if(ha->dwFlags & MPQ_FLAG_CHECK_SECTOR_CRC) hf->bCheckSectorCRCs = true; // If we know the real file name, copy it to the file entry if(bOpenByIndex == false) { // If there is no file name yet, allocate it AllocateFileName(pFileEntry, szFileName); // If the file is encrypted, we should detect the file key if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) { hf->dwFileKey = DecryptFileKey(szFileName, pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags); } } else { // Try to auto-detect the file name if(!SFileGetFileName(hf, NULL)) nError = GetLastError(); } } // If the file is actually a patch file, we have to load the patch file header if(nError == ERROR_SUCCESS && pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) { assert(hf->pPatchInfo == NULL); nError = AllocatePatchInfo(hf, true); } // Cleanup if(nError != ERROR_SUCCESS) { SetLastError(nError); FreeMPQFile(hf); } *phFile = hf; return (nError == ERROR_SUCCESS); }
bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * PtrFile) { TMPQArchive * ha = IsValidMpqHandle(hMpq); TFileEntry * pFileEntry = NULL; TMPQFile * hf = NULL; DWORD dwHashIndex = HASH_ENTRY_FREE; DWORD dwFileIndex = 0; bool bOpenByIndex = false; int nError = ERROR_SUCCESS; // Don't accept NULL pointer to file handle if(szFileName == NULL || *szFileName == 0) nError = ERROR_INVALID_PARAMETER; // When opening a file from MPQ, the handle must be valid if(dwSearchScope != SFILE_OPEN_LOCAL_FILE && ha == NULL) nError = ERROR_INVALID_HANDLE; // When not checking for existence, the pointer to file handle must be valid if(dwSearchScope != SFILE_OPEN_CHECK_EXISTS && PtrFile == NULL) nError = ERROR_INVALID_PARAMETER; // Prepare the file opening if(nError == ERROR_SUCCESS) { switch(dwSearchScope) { case SFILE_OPEN_FROM_MPQ: case SFILE_OPEN_BASE_FILE: case SFILE_OPEN_CHECK_EXISTS: // Check the pseudo-file name if((bOpenByIndex = IsPseudoFileName(szFileName, &dwFileIndex)) == true) { // Get the file entry for the file if(dwFileIndex > ha->dwFileTableSize) break; pFileEntry = ha->pFileTable + dwFileIndex; } else { // If this MPQ has no patches, open the file from this MPQ directly if(ha->haPatch == NULL || dwSearchScope == SFILE_OPEN_BASE_FILE) { pFileEntry = GetFileEntryLocale2(ha, szFileName, lcFileLocale, &dwHashIndex); } // If this MPQ is a patched archive, open the file as patched else { return OpenPatchedFile(hMpq, szFileName, PtrFile); } } break; case SFILE_OPEN_ANY_LOCALE: // This open option is reserved for opening MPQ internal listfile. // No argument validation. Tries to open file with neutral locale first, // then any other available. pFileEntry = GetFileEntryLocale2(ha, szFileName, 0, &dwHashIndex); break; case SFILE_OPEN_LOCAL_FILE: // Open a local file return OpenLocalFile(szFileName, PtrFile); default: // Don't accept any other value nError = ERROR_INVALID_PARAMETER; break; } } // Check whether the file really exists in the MPQ if(nError == ERROR_SUCCESS) { if(pFileEntry == NULL || (pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0) nError = ERROR_FILE_NOT_FOUND; // Ignore unknown loading flags (example: MPQ_2016_v1_WME4_4.w3x) // if(pFileEntry != NULL && pFileEntry->dwFlags & ~MPQ_FILE_VALID_FLAGS) // nError = ERROR_NOT_SUPPORTED; } // Did the caller just wanted to know if the file exists? if(nError == ERROR_SUCCESS && dwSearchScope != SFILE_OPEN_CHECK_EXISTS) { // Allocate file handle hf = CreateFileHandle(ha, pFileEntry); if(hf != NULL) { // Get the hash index for the file if(ha->pHashTable != NULL && dwHashIndex == HASH_ENTRY_FREE) dwHashIndex = FindHashIndex(ha, dwFileIndex); if(dwHashIndex != HASH_ENTRY_FREE) hf->pHashEntry = ha->pHashTable + dwHashIndex; hf->dwHashIndex = dwHashIndex; // If the MPQ has sector CRC enabled, enable if for the file if(ha->dwFlags & MPQ_FLAG_CHECK_SECTOR_CRC) hf->bCheckSectorCRCs = true; // If we know the real file name, copy it to the file entry if(bOpenByIndex == false) { // If there is no file name yet, allocate it AllocateFileName(ha, pFileEntry, szFileName); // If the file is encrypted, we should detect the file key if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) { hf->dwFileKey = DecryptFileKey(szFileName, pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags); } } } else { nError = ERROR_NOT_ENOUGH_MEMORY; } } // Give the file entry if(PtrFile != NULL) PtrFile[0] = hf; // Return error code if(nError != ERROR_SUCCESS) SetLastError(nError); return (nError == ERROR_SUCCESS); }
void EnumDATFileData( FILENAME *fnParent, DWORD dwDiskPtr ) { FILENAME fnDiskName; FILENAME *fnChild = &fnDiskName; FILENAME *fnCurrent; int iSeek; int iCount; // // Read in this level from the DAT file // while ( dwDiskPtr != 0 ) { // Seek to this entry iSeek = fseek( SuckDATFile, dwDiskPtr, SEEK_SET ); if ( iSeek != 0 ) { printf("SUCK.DAT seek error, remove and restart\n"); exit(3); } // Read in this entry iCount = fread( (void *)fnChild, sizeof(FILENAME), 1, SuckDATFile ); if ( iCount != 1 ) { printf("SUCK.DAT read error, remove and restart\n"); exit(4); } #ifdef SEE_EM printf("Reading record [%s], at %08lX Child %08lX Sib %08lX\n", fnChild->cFileName, dwDiskPtr, fnChild->fnChild.disk_ptr, fnChild->fnSibling.disk_ptr ); printf("Size = %d\n", fnChild->dwFileSizeLow ); #endif // // Add this file node to the tree // fnCurrent = AllocateFileName(); fnCurrent->dwStatus = fnChild->dwStatus; fnCurrent->dwCopy = 0; fnCurrent->fnParent.mem_ptr = fnParent; fnCurrent->fnChild.mem_ptr = NULL; fnCurrent->fnSibling.mem_ptr = fnParent->fnChild.mem_ptr; fnCurrent->dwFileSizeLow = 0; fnCurrent->dwFileSizeHigh = 0; fnCurrent->ftFileTime = ftZero; fnCurrent->dwDATFileSizeLow = fnChild->dwFileSizeLow; fnCurrent->dwDATFileSizeHigh = fnChild->dwFileSizeHigh; fnCurrent->ftDATFileTime = fnChild->ftFileTime; fnCurrent->dwFileNameLen = fnChild->dwFileNameLen; strcpy( fnCurrent->cFileName, fnChild->cFileName ); fnParent->fnChild.mem_ptr = fnCurrent; if ( (fnCurrent->dwStatus & DIRECTORY) == DIRECTORY ) { nDirectories++; // // Load this directories children // EnumDATFileData( fnCurrent, fnChild->fnChild.disk_ptr ); } else { fnCurrent->dwStatus = 0; nFiles++; } // Move to next sibling at this level dwDiskPtr = fnChild->fnSibling.disk_ptr; } }
VOID AddFile( FILENAME *fn, LPWIN32_FIND_DATA lpwfd, DWORD mask ) { CHAR *pdest; CHAR *psrc; INT count; INT maximum; FILENAME *fnCurrent; FILENAME *fnChild; DWORD dwFileNameLen; CHAR NewName[MAX_FILENAME_LENGTH+1]; FILENAME *fnChildOriginally; if ( *lpwfd->cFileName == '.' ) { return; } dwFileNameLen = strlen(lpwfd->cFileName); if (dwFileNameLen > MAX_FILENAME_LENGTH) { fprintf(stderr, "File name %s too long (%u > %u), complain to BobDay\n", lpwfd->cFileName, dwFileNameLen, MAX_FILENAME_LENGTH); return; } strcpy( NewName, lpwfd->cFileName ); fnChild = fn->fnChild.mem_ptr; fnChildOriginally = fnChild; while ( fnChild ) { if ( fnChild->dwFileNameLen == dwFileNameLen && !strcmp(NewName, fnChild->cFileName) ) { fnChild->dwStatus |= mask; // Atomic instruction if ( fnChild->ftFileTime.dwLowDateTime == ftZero.dwLowDateTime && fnChild->ftFileTime.dwHighDateTime == ftZero.dwHighDateTime ) { EnterCriticalSection( &cs ); fnChild->dwFileSizeLow = lpwfd->nFileSizeLow; fnChild->dwFileSizeHigh = lpwfd->nFileSizeHigh; fnChild->ftFileTime = lpwfd->ftLastWriteTime; LeaveCriticalSection( &cs ); } nDuplicates++; return; } fnChild = fnChild->fnSibling.mem_ptr; } // Probably not there... Enter the critical section now to prove it EnterCriticalSection( &cs ); // Most common case, nobody has changed this directory at all. if ( fn->fnChild.mem_ptr != fnChildOriginally ) { // Otherwise, make another scan inside the critical section. fnChild = fn->fnChild.mem_ptr; while ( fnChild ) { if ( fnChild->dwFileNameLen == dwFileNameLen && !strcmp(NewName, fnChild->cFileName) ) { fnChild->dwStatus |= mask; // Atomic instruction nDuplicates++; LeaveCriticalSection( &cs ); return; } fnChild = fnChild->fnSibling.mem_ptr; } } fnCurrent = AllocateFileName(); strcpy( fnCurrent->cFileName, NewName ); fnCurrent->dwFileNameLen = dwFileNameLen; fnCurrent->dwFileSizeLow = lpwfd->nFileSizeLow; fnCurrent->dwFileSizeHigh = lpwfd->nFileSizeHigh; fnCurrent->ftFileTime = lpwfd->ftLastWriteTime; fnCurrent->dwDATFileSizeLow = 0; fnCurrent->dwDATFileSizeHigh = 0; fnCurrent->ftDATFileTime = ftZero; fnCurrent->dwCopy = 0; fnCurrent->fnParent.mem_ptr = fn; fnCurrent->fnChild.mem_ptr = NULL; fnCurrent->fnSibling.mem_ptr = fn->fnChild.mem_ptr; fn->fnChild.mem_ptr = fnCurrent; if ( lpwfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { fnCurrent->dwStatus = DIRECTORY; nDirectories++; } else { fnCurrent->dwStatus = 0; nFiles++; } fnCurrent->dwStatus |= mask; #ifdef SEE_EM { char text[MAX_FILENAME_LENGTH+1]; memcpy( text, fnCurrent->cFileName, MAX_FILENAME_LENGTH ); text[MAX_FILENAME_LENGTH] = '\0'; if ( fnCurrent->dwStatus & DIRECTORY ) { printf("Munged DirName = %08lX:[%s]\n", fnCurrent, text ); } else { printf("Munged FileName = %08lX:[%s]\n", fnCurrent, text ); } } #endif LeaveCriticalSection( &cs ); }