/*****************************************************************************/ /* CascOpenFile.cpp Copyright (c) Ladislav Zezula 2014 */ /*---------------------------------------------------------------------------*/ /* System-dependent directory functions for CascLib */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 01.05.14 1.00 Lad The first version of CascOpenFile.cpp */ /*****************************************************************************/ #define __CASCLIB_SELF__ #include "CascLib.h" #include "CascCommon.h" //----------------------------------------------------------------------------- // TCascFile class functions TCascFile::TCascFile(TCascStorage * ahs, PCASC_CKEY_ENTRY apCKeyEntry) { // Reference the storage handle if((hs = ahs) != NULL) hs->AddRef(); ClassName = CASC_MAGIC_FILE; FilePointer = 0; pCKeyEntry = apCKeyEntry; SpanCount = (pCKeyEntry->SpanCount != 0) ? pCKeyEntry->SpanCount : 1; bVerifyIntegrity = false; bDownloadFileIf = false; bCloseFileStream = false; bFreeCKeyEntries = false; // Allocate the array of file spans if((pFileSpan = CASC_ALLOC_ZERO(SpanCount)) != NULL) { InitFileSpans(pFileSpan, SpanCount); InitCacheStrategy(); } } TCascFile::~TCascFile() { // Free all stuff related to file spans if(pFileSpan != NULL) { PCASC_FILE_SPAN pSpanPtr = pFileSpan; for(DWORD i = 0; i < SpanCount; i++, pSpanPtr++) { // Close the span file stream if this is a local file if(bCloseFileStream) FileStream_Close(pSpanPtr->pStream); pSpanPtr->pStream = NULL; // Free the span frames CASC_FREE(pSpanPtr->pFrames); } CASC_FREE(pFileSpan); } // Free the CKey entries, if needed if(pCKeyEntry && bFreeCKeyEntries) delete [] pCKeyEntry; pCKeyEntry = NULL; // Free the file cache CASC_FREE(pbFileCache); // Close (dereference) the archive handle if(hs != NULL) hs = hs->Release(); ClassName = 0; } DWORD TCascFile::OpenFileSpans(LPCTSTR szSpanList) { TFileStream * pStream; ULONGLONG FileSize = 0; DWORD dwErrCode = ERROR_SUCCESS; for(DWORD i = 0; i < SpanCount; i++) { // Open the file span pFileSpan[i].pStream = pStream = FileStream_OpenFile(szSpanList, BASE_PROVIDER_FILE | STREAM_PROVIDER_FLAT); if(pFileSpan[i].pStream == NULL) { dwErrCode = GetCascError(); break; } // If succeeded, we assign the span to the FileStream_GetSize(pStream, &FileSize); if((FileSize >> 0x1E) != 0) { dwErrCode = ERROR_NOT_SUPPORTED; break; } pCKeyEntry[i].EncodedSize = (DWORD)FileSize; } // Free the so-far-opened files if(dwErrCode != ERROR_SUCCESS) { for(DWORD i = 0; i < SpanCount; i++) { if(pFileSpan[i].pStream != NULL) FileStream_Close(pFileSpan[i].pStream); pFileSpan[i].pStream = NULL; } } return dwErrCode; } void TCascFile::InitFileSpans(PCASC_FILE_SPAN pSpans, DWORD dwSpanCount) { ULONGLONG FileOffsetBits = 30; ULONGLONG FileOffsetMask = 0; ULONGLONG FileOffset = 0; // Initialize the file sizes. Note that if any of the spans has invalid size, // the entire file size will be set to CASC_INVALID_SIZE64. GetFileSpanInfo(pCKeyEntry, &ContentSize, &EncodedSize); // Resolve the file offset bits and file offset mask if(hs != NULL) FileOffsetBits = hs->FileOffsetBits; FileOffsetMask = ((ULONGLONG)1 << FileOffsetBits) - 1; // Add all span sizes for(DWORD i = 0; i < dwSpanCount; i++, pSpans++) { // Put the archive index and archive offset pSpans->ArchiveIndex = (DWORD)(pCKeyEntry[i].StorageOffset >> FileOffsetBits); pSpans->ArchiveOffs = (DWORD)(pCKeyEntry[i].StorageOffset & FileOffsetMask); // Add to the total encoded size if(ContentSize != CASC_INVALID_SIZE64) { pSpans->StartOffset = FileOffset; FileOffset = FileOffset + pCKeyEntry[i].ContentSize; pSpans->EndOffset = FileOffset; } } } void TCascFile::InitCacheStrategy() { CacheStrategy = CascCacheLastFrame; FileCacheStart = FileCacheEnd = 0; pbFileCache = NULL; } //----------------------------------------------------------------------------- // Local functions static size_t GetSpanFileCount(LPTSTR szSpanList) { LPTSTR szSpanPtr = szSpanList; size_t nSpanCount = 1; while(szSpanPtr[0] != 0) { // End of a file? if(szSpanPtr[0] == ';' && szSpanPtr[1] != 0) { szSpanPtr[0] = 0; nSpanCount++; } szSpanPtr++; } // Place an additional zero to make the list terminated by double EOS szSpanPtr[1] = 0; return nSpanCount; } PCASC_CKEY_ENTRY FindCKeyEntry_CKey(TCascStorage * hs, LPBYTE pbCKey, PDWORD PtrIndex) { return (PCASC_CKEY_ENTRY)hs->CKeyMap.FindObject(pbCKey, PtrIndex); } PCASC_CKEY_ENTRY FindCKeyEntry_EKey(TCascStorage * hs, LPBYTE pbEKey, PDWORD PtrIndex) { return (PCASC_CKEY_ENTRY)hs->EKeyMap.FindObject(pbEKey, PtrIndex); } bool OpenFileByCKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD dwOpenFlags, HANDLE * PtrFileHandle) { TCascFile * hf = NULL; DWORD dwErrCode = ERROR_FILE_NOT_FOUND; // If the CKey entry is NULL, we consider the file non-existant if(pCKeyEntry != NULL) { // Create the file handle structure if((hf = new TCascFile(hs, pCKeyEntry)) != NULL) { hf->bVerifyIntegrity = (dwOpenFlags & CASC_STRICT_DATA_CHECK) ? true : false; hf->bDownloadFileIf = (hs->dwFeatures & CASC_FEATURE_ONLINE) ? true : false; hf->bOvercomeEncrypted = (dwOpenFlags & CASC_OVERCOME_ENCRYPTED) ? true : false; dwErrCode = ERROR_SUCCESS; } else { dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } } // Give the output parameter, no matter what PtrFileHandle[0] = (HANDLE)hf; // Handle last error if(dwErrCode != ERROR_SUCCESS) SetCascError(dwErrCode); return (dwErrCode == ERROR_SUCCESS); } bool OpenLocalFile(LPCTSTR szFileName, DWORD dwOpenFlags, HANDLE * PtrFileHandle) { PCASC_CKEY_ENTRY pCKeyEntry; TCascFile * hf = NULL; LPTSTR szSpanList; size_t nSpanCount; DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY; // Create a copy of the file name. It is actually a file name list, // separated by comma (for supporting multi-span files) if((szSpanList = CascNewStr(szFileName, 1)) != NULL) { // Calculate the span count if((nSpanCount = GetSpanFileCount(szSpanList)) != 0 || nSpanCount > 0xFF) { // Allocate CKey array for the file. Each entry describes one file span if((pCKeyEntry = new CASC_CKEY_ENTRY[nSpanCount]) != NULL) { // Prepare the span count to the first item pCKeyEntry->SpanCount = (BYTE)nSpanCount; // Prepare the archive offset in each CKey entry for(size_t i = 0; i < nSpanCount; i++) pCKeyEntry[i].StorageOffset = 0; // Create an instance of the TCascFile if((hf = new TCascFile(NULL, pCKeyEntry)) != NULL) { // Prepare the structure hf->bVerifyIntegrity = (dwOpenFlags & CASC_STRICT_DATA_CHECK) ? true : false; hf->bOvercomeEncrypted = (dwOpenFlags & CASC_OVERCOME_ENCRYPTED) ? true : false; hf->bCloseFileStream = true; // Open all local file spans dwErrCode = hf->OpenFileSpans(szSpanList); if(dwErrCode != ERROR_SUCCESS) { delete hf; hf = NULL; } } } } else { dwErrCode = ERROR_INVALID_PARAMETER; } delete [] szSpanList; } // Give the output parameter, no matter what PtrFileHandle[0] = (HANDLE)hf; // Handle last error if(dwErrCode != ERROR_SUCCESS) SetCascError(dwErrCode); return (dwErrCode == ERROR_SUCCESS); } bool SetCacheStrategy(HANDLE hFile, CSTRTG CacheStrategy) { TCascFile * hf; // Validate the file handle if((hf = TCascFile::IsValid(hFile)) != NULL) { // The cache must not be initialized yet if(hf->pbFileCache == NULL) { hf->CacheStrategy = CacheStrategy; return true; } } // Failed. This should never happen assert(false); return false; } //----------------------------------------------------------------------------- // Public functions bool WINAPI CascOpenFile(HANDLE hStorage, const void * pvFileName, DWORD dwLocaleFlags, DWORD dwOpenFlags, HANDLE * PtrFileHandle) { PCASC_CKEY_ENTRY pCKeyEntry = NULL; TCascStorage * hs; const char * szFileName; DWORD FileDataId = CASC_INVALID_ID; BYTE CKeyEKeyBuffer[MD5_HASH_SIZE]; DWORD dwErrCode = ERROR_SUCCESS; // This parameter is not used CASCLIB_UNUSED(dwLocaleFlags); // Validate the storage handle hs = TCascStorage::IsValid(hStorage); if(hs == NULL) { SetCascError(ERROR_INVALID_HANDLE); return false; } // Validate the other parameters if(PtrFileHandle == NULL) { SetCascError(ERROR_INVALID_PARAMETER); return false; } // Retrieve the CKey/EKey from the file name in different modes switch(dwOpenFlags & CASC_OPEN_TYPE_MASK) { case CASC_OPEN_BY_NAME: // The 'pvFileName' must be zero terminated ANSI file name szFileName = (const char *)pvFileName; if(szFileName == NULL || szFileName[0] == 0) { SetCascError(ERROR_INVALID_PARAMETER); return false; } // The first chance: Try to find the file by name (using the root handler) pCKeyEntry = hs->pRootHandler->GetFile(hs, szFileName); if(pCKeyEntry != NULL) break; // Second chance: If the file name is actually a file data id, we convert it to file data ID if(IsFileDataIdName(szFileName, FileDataId)) { pCKeyEntry = hs->pRootHandler->GetFile(hs, FileDataId); if(pCKeyEntry != NULL) break; } // Third chance: If the file name is a string representation of CKey/EKey, we try to query for CKey if(IsFileCKeyEKeyName(szFileName, CKeyEKeyBuffer)) { pCKeyEntry = FindCKeyEntry_CKey(hs, CKeyEKeyBuffer); if(pCKeyEntry != NULL) break; pCKeyEntry = FindCKeyEntry_EKey(hs, CKeyEKeyBuffer); if(pCKeyEntry != NULL) break; } SetCascError(ERROR_FILE_NOT_FOUND); return false; case CASC_OPEN_BY_CKEY: // The 'pvFileName' must be a pointer to 16-byte CKey or EKey if(pvFileName == NULL) { SetCascError(ERROR_INVALID_PARAMETER); return false; } // Search the CKey map in order to find the CKey entry pCKeyEntry = FindCKeyEntry_CKey(hs, (LPBYTE)pvFileName); break; case CASC_OPEN_BY_EKEY: // The 'pvFileName' must be a pointer to 16-byte CKey or EKey if(pvFileName == NULL) { SetCascError(ERROR_INVALID_PARAMETER); return false; } // Search the CKey map in order to find the CKey entry pCKeyEntry = FindCKeyEntry_EKey(hs, (LPBYTE)pvFileName); break; case CASC_OPEN_BY_FILEID: // Retrieve the file CKey/EKey pCKeyEntry = hs->pRootHandler->GetFile(hs, CASC_FILE_DATA_ID_FROM_STRING(pvFileName)); break; default: // Unknown open mode dwErrCode = ERROR_INVALID_PARAMETER; break; } // Check opening unique file if(dwOpenFlags & CASC_OPEN_CKEY_ONCE) { // Was the file already open since CascOpenStorage? if(pCKeyEntry->Flags & CASC_CE_OPEN_CKEY_ONCE) { SetCascError(ERROR_CKEY_ALREADY_OPENED); return false; } else { pCKeyEntry->Flags |= CASC_CE_OPEN_CKEY_ONCE; } } // Perform the open operation return OpenFileByCKeyEntry(hs, pCKeyEntry, dwOpenFlags, PtrFileHandle); } bool WINAPI CascOpenLocalFile(LPCTSTR szFileName, DWORD dwOpenFlags, HANDLE * PtrFileHandle) { // Verify parameters if(szFileName == NULL || szFileName[0] == 0 || PtrFileHandle == NULL) { SetCascError(ERROR_INVALID_PARAMETER); return false; } return OpenLocalFile(szFileName, dwOpenFlags, PtrFileHandle); } bool WINAPI CascCloseFile(HANDLE hFile) { TCascFile * hf; hf = TCascFile::IsValid(hFile); if(hf != NULL) { delete hf; return true; } SetCascError(ERROR_INVALID_HANDLE); return false; }