/*++ Copyright (c) 1993 Microsoft Corporation Module Name: decomp.c Abstract: File decompression support routines. Author: Ted Miller (tedm) 1-Feb-1995 Revision History: --*/ #include "precomp.h" #pragma hdrstop #include struct LZINFO; typedef struct LZINFO *PLZINFO; #include #include typedef struct _SFD_INFO { unsigned FileCount; PCTSTR TargetFile; BOOL GotTimestamp; FILETIME FileTime; } SFD_INFO, *PSFD_INFO; UINT pGetCompressInfoCB( IN PVOID Context, IN UINT Notification, IN UINT_PTR Param1, IN UINT_PTR Param2 ); UINT pSingleFileDecompCB( IN PVOID Context, IN UINT Notification, IN UINT_PTR Param1, IN UINT_PTR Param2 ); // // OldMyMalloc/OldMyFree used by SetupGetFileCompressionInfo // for app-compat // PVOID OldMyMalloc( IN DWORD Size ); VOID OldMyFree( IN PVOID Block ); PTSTR SetupGenerateCompressedName( IN PCTSTR Filename ) /*++ Routine Description: Given a filename, generate the compressed form of the name. The compressed form is generated as follows: Look backwards for a dot. If there is no dot, append "._" to the name. If there is a dot followed by 0, 1, or 2 charcaters, append "_". Otherwise there is a 3-character or greater extension and we replace the last character with "_". Arguments: Filename - supplies filename whose compressed form is desired. Return Value: Pointer to buffer containing nul-terminated compressed-form filename. The caller must free this buffer via MyFree(). --*/ { PTSTR CompressedName,p,q; UINT u; // // The maximum length of the compressed filename is the length of the // original name plus 2 (for ._). // if(CompressedName = MyMalloc((lstrlen(Filename)+3)*sizeof(TCHAR))) { lstrcpy(CompressedName,Filename); p = _tcsrchr(CompressedName,TEXT('.')); q = _tcsrchr(CompressedName,TEXT('\\')); if(q < p) { // // If there are 0, 1, or 2 characters after the dot, just append // the underscore. p points to the dot so include that in the length. // u = lstrlen(p); if(u < 4) { lstrcat(CompressedName,TEXT("_")); } else { // // There are at least 3 characters in the extension. // Replace the final one with an underscore. // p[u-1] = TEXT('_'); } } else { // // No dot, just add ._. // lstrcat(CompressedName,TEXT("._")); } } return(CompressedName); } DWORD pSetupAttemptLocate( IN PCTSTR FileName, OUT PBOOL Found, OUT PWIN32_FIND_DATA FindData ) /*++ Routine Description: Attempt to locate a source file via FindFirstFile(). Errors of the 'file not found' type are not considered errors and result in NO_ERROR. Any non-NO_ERROR return indicates that we could not determine whether the file is present or not because of some hardware or system problem, etc. Arguments: FileName - supplies filename of the file to be located. Found - receives a value indicating whether the file was found. This value is only valid when the function returns NO_ERROR. FindData - if found, returns win32 find data for the file. Return Value: Win32 error code indicating the outcome. If NO_ERROR, check the Found return value to see whether the file was found. --*/ { DWORD d; if(*Found = FileExists(FileName,FindData)) { d = NO_ERROR; } else { // // We didn't find the file. See whether that was because // the file wasn't there or because some other error occured. // d = GetLastError(); if((d == ERROR_NO_MORE_FILES) || (d == ERROR_FILE_NOT_FOUND) || (d == ERROR_PATH_NOT_FOUND) || (d == ERROR_BAD_NETPATH)) { d = NO_ERROR; } } return(d); } DWORD SetupDetermineSourceFileName( IN PCTSTR FileName, OUT PBOOL UsedCompressedName, OUT PTSTR *FileNameLocated, OUT PWIN32_FIND_DATA FindData ) /*++ Routine Description: Attempt to locate a source file whose name can be compressed or uncompressed. The order of attempt is - the name as given (should be the uncompressed name) - the compressed form, using _ as the compression char - the compressed form, using $ as the compression char Arguments: FileName - supplies filename of the file to be located. UsedCompressedName - receives a boolean indicating whether the filename we located seems to indicate that the file is compressed. FileNameLocated - receives a pointer to the filename actually located. The caller must free with MyFree(). FindData - if found, returns win32 find data for the file. Return Value: Win32 error code indicating the outcome. ERROR_FILE_NOT_FOUND - normal code indicating everything is ok but we can't find the file NO_ERROR - file was located; check UsedCompressedName and FileNameOpened. Others - something is wrong with the hardware or system. --*/ { DWORD d; PTSTR TryName; BOOL Found; TryName = DuplicateString(FileName); if(!TryName) { return(ERROR_NOT_ENOUGH_MEMORY); } *UsedCompressedName = FALSE; *FileNameLocated = TryName; d = pSetupAttemptLocate(TryName,&Found,FindData); if(d != NO_ERROR) { MyFree(TryName); *FileNameLocated = NULL; return(d); } if(Found) { return(NO_ERROR); } MyFree(TryName); *UsedCompressedName = TRUE; *FileNameLocated = NULL; TryName = SetupGenerateCompressedName(FileName); if(!TryName) { return(ERROR_NOT_ENOUGH_MEMORY); } *FileNameLocated = TryName; d = pSetupAttemptLocate(TryName,&Found,FindData); if(d != NO_ERROR) { MyFree(TryName); *FileNameLocated = NULL; return(d); } if(Found) { return(NO_ERROR); } MYASSERT(TryName[lstrlen(TryName)-1] == TEXT('_')); TryName[lstrlen(TryName)-1] = TEXT('$'); d = pSetupAttemptLocate(TryName,&Found,FindData); if((d != NO_ERROR) || !Found) { *FileNameLocated = NULL; MyFree(TryName); } return(Found ? NO_ERROR : ERROR_FILE_NOT_FOUND); } BOOL pSetupDoesFileMatch( IN PCTSTR InputName, IN PCTSTR CompareName, OUT PBOOL UsedCompressedName, OUT PTSTR *FileNameLocated ) /*++ Routine Description: determine if the specified input file matches the name to compare it with. We try the undecorated name as well as the compressed versions of the file name. The order of attempt is - the name as given (should be the uncompressed name) - the compressed form, using _ as the compression char - the compressed form, using $ as the compression char Arguments: FileName - supplies filename we're looking at. CompareName -supplies the filename we're comparing against UsedCompressedName - receives a boolean indicating whether the filename we located seems to indicate that the file is compressed. FileNameLocated - receives a pointer to the filename actually located. The caller must free with MyFree(). Return Value: Win32 error code indicating the outcome. ERROR_FILE_NOT_FOUND - normal code indicating everything is ok but we can't find the file NO_ERROR - file was located; check UsedCompressedName and FileNameOpened. Others - something is wrong with the hardware or system. --*/ { DWORD d; PTSTR TryName,TargetName,src,dst; BOOL Found; TryName = DuplicateString(InputName); if(!TryName) { return(FALSE); } TargetName = DuplicateString(CompareName); if(!TargetName) { MyFree(TryName); return(FALSE); } dst = _tcsrchr(TryName,TEXT('.')); if (dst) { *dst = 0; } src = _tcsrchr(TargetName,TEXT('.')); if (src) { *src = 0; } if (lstrcmpi(TargetName,TryName)) { // the "surnames" do not match, so none of the other comparisons will work. MyFree(TryName); MyFree(TargetName); return(FALSE); } if (dst) { *dst = TEXT('.'); } if (src) { *src = TEXT('.'); } *UsedCompressedName = FALSE; *FileNameLocated = TryName; if (!lstrcmpi(TryName,TargetName)) { // we matched MyFree(TargetName); return(TRUE); } MyFree(TryName); *UsedCompressedName = TRUE; TryName = SetupGenerateCompressedName(TargetName); if(!TryName) { return(ERROR_NOT_ENOUGH_MEMORY); } *FileNameLocated = TryName; if (!lstrcmpi(TryName,InputName)) { // we matched MyFree(TargetName); return(TRUE); } MYASSERT(TryName[lstrlen(TryName)-1] == TEXT('_')); TryName[lstrlen(TryName)-1] = TEXT('$'); if (!lstrcmpi(TryName,InputName)) { // we matched MyFree(TargetName); return(TRUE); } // // no match // MyFree(TargetName); MyFree(TryName); return(FALSE); } DWORD pSetupDecompressWinLzFile( IN PTSTR SourceFileName, IN PTSTR TargetFileName ) /*++ Routine Description: Determine whether a file is compressed, and retreive additional information about it. Arguments: SourceFileName - supplies filename of the file to be checked. This filename is used as a base; if not found then we look for the 2 compressed forms (ie, foo.ex_, foo.ex$) as well. ActualSourceFileName - receives a pointer to the filename that was actually located. Caller can free with MyFree(). Valid only if the return code from this routine is NO_ERROR. SourceFileSize - receives the size of the located file in its current (ie, compressed) form. Valid only if this routine returns NO_ERROR. TargetFileSize - receives the uncompressed size of the file. If the file is not compressed this will be the same as SourceFileSize. Valid only if this routine returns NO_ERROR. CompressionType - receives a value indicating the compression type. Valid only if this routine returns NO_ERROR. Return Value: Win32 error code indicating the outcome. ERROR_FILE_NOT_FOUND - normal code indicating everything is ok but we can't find the file NO_ERROR - file was located and output params are filled in. Others - something is wrong with the hardware or system. --*/ { INT hSrc,hDst; OFSTRUCT ofSrc,ofDst; LONG l; DWORD d; FILETIME CreateTime,AccessTime,WriteTime; // // Get the timestamp of the source. // d = GetSetFileTimestamp( SourceFileName, &CreateTime, &AccessTime, &WriteTime, FALSE ); if(d != NO_ERROR) { return(d); } hSrc = LZOpenFile(SourceFileName,&ofSrc,OF_READ|OF_SHARE_DENY_WRITE); if(hSrc >= 0) { hDst = LZOpenFile(TargetFileName,&ofSrc,OF_CREATE|OF_WRITE|OF_SHARE_EXCLUSIVE); if(hDst >= 0) { l = LZCopy(hSrc,hDst); if(l >= 0) { l = 0; // // Set the timestamp of the target. The file is already there // so just ignore errors. // GetSetFileTimestamp( TargetFileName, &CreateTime, &AccessTime, &WriteTime, TRUE ); } LZClose(hDst); } else { l = hDst; } LZClose(hSrc); } else { l = hSrc; } // // lz error to win32 error // switch(l) { case 0: return(NO_ERROR); case LZERROR_BADINHANDLE: case LZERROR_READ: return(ERROR_READ_FAULT); case LZERROR_BADOUTHANDLE: case LZERROR_WRITE: return(ERROR_WRITE_FAULT); case LZERROR_GLOBALLOC: case LZERROR_GLOBLOCK: return(ERROR_NOT_ENOUGH_MEMORY); case LZERROR_BADVALUE: case LZERROR_UNKNOWNALG: return(ERROR_INVALID_DATA); default: return(ERROR_INVALID_FUNCTION); } } DWORD SetupInternalGetFileCompressionInfo( IN PCTSTR SourceFileName, OUT PTSTR *ActualSourceFileName, OUT PWIN32_FIND_DATA SourceFindData, OUT PDWORD TargetFileSize, OUT PUINT CompressionType ) /*++ Routine Description: Determine whether a file is compressed, and retreive additional information about it. Arguments: SourceFileName - supplies filename of the file to be checked. This filename is used as a base; if not found then we look for the 2 compressed forms (ie, foo.ex_, foo.ex$) as well. ActualSourceFileName - receives a pointer to the filename that was actually located. Caller can free with MyFree(). Valid only if the return code from this routine is NO_ERROR. SourceFindData - receives win32 find data for the located file in its current (ie, compressed) form. Valid only if this routine returns NO_ERROR. TargetFileSize - receives the uncompressed size of the file. If the file is not compressed this will be the same as SourceFileSize. Valid only if this routine returns NO_ERROR. CompressionType - receives a value indicating the compression type. Valid only if this routine returns NO_ERROR. Return Value: Win32 error code indicating the outcome. ERROR_FILE_NOT_FOUND - normal code indicating everything is ok but we can't find the file NO_ERROR - file was located and output params are filled in. Others - something is wrong with the hardware or system. --*/ { DWORD d; DWORD caberr = NO_ERROR; BOOL b; HANDLE hFile,hMapping; DWORD size; FH UNALIGNED *LZHeader; d = SetupDetermineSourceFileName( SourceFileName, &b, ActualSourceFileName, SourceFindData ); if(d != NO_ERROR) { return(d); } // // If the file is 0-length it isn't compressed; // trying to map it in below will fail in this case. // if(SourceFindData->nFileSizeLow) { // // See if it's a diamond file. // d = DiamondProcessCabinet( *ActualSourceFileName, 0, pGetCompressInfoCB, &size, TRUE ); if(d == NO_ERROR) { *TargetFileSize = size; *CompressionType = FILE_COMPRESSION_MSZIP; return(NO_ERROR); } else if (d != ERROR_INVALID_DATA) { // // general problems not specific to the file format itself // however if this might be a plain file, ignore it // size_t len1 = lstrlen(SourceFileName); size_t len2 = lstrlen(*ActualSourceFileName); TCHAR c1 = *CharPrev(SourceFileName,SourceFileName+len1); TCHAR c2 = *CharPrev(*ActualSourceFileName,*ActualSourceFileName+len2); if(((c2 == TEXT('_')) || (c2 == TEXT('$'))) && ((len1 != len2) || (c1 != c2))) { // // ActualSourceFileName ends in '_' or '$' and is a modification of SourceFileName // don't let us try and parse this as a plain file // caberr = d; } } // // See if it's a WINLZ file. // d = pSetupOpenAndMapFileForRead( *ActualSourceFileName, &SourceFindData->nFileSizeLow, &hFile, &hMapping, (PVOID *)&LZHeader ); if(d != NO_ERROR) { MyFree(*ActualSourceFileName); return(d); } b = FALSE; try { if((SourceFindData->nFileSizeLow >= HEADER_LEN) && !memcmp(LZHeader->rgbyteMagic,COMP_SIG,COMP_SIG_LEN) && RecognizeCompAlg(LZHeader->byteAlgorithm)) { *TargetFileSize = LZHeader->cbulUncompSize; b = TRUE; } } except(EXCEPTION_EXECUTE_HANDLER) { ; } pSetupUnmapAndCloseFile(hFile,hMapping,LZHeader); if(b) { *CompressionType = FILE_COMPRESSION_WINLZA; return(NO_ERROR); } if(caberr) { // // looks like a compressed file and DiamondProcessCabinet // returned a suspicious error // return(caberr); } } // // File is not compressed. // *CompressionType = FILE_COMPRESSION_NONE; *TargetFileSize = SourceFindData->nFileSizeLow; return(NO_ERROR); } UINT pGetCompressInfoCB( IN PVOID Context, IN UINT Notification, IN UINT_PTR Param1, IN UINT_PTR Param2 ) { PFILE_IN_CABINET_INFO FileInfo; DWORD rc; switch(Notification) { case SPFILENOTIFY_CABINETINFO: // // We don't do anything with this. // rc = NO_ERROR; break; case SPFILENOTIFY_FILEINCABINET: // // New file within a cabinet. // // We don't ever want to copy the file. Save size info // and abort. // FileInfo = (PFILE_IN_CABINET_INFO)Param1; *((PDWORD)Context) = FileInfo->FileSize; FileInfo->Win32Error = NO_ERROR; rc = FILEOP_ABORT; SetLastError(NO_ERROR); break; //case SPFILENOTIFY_FILEEXTRACTED: //case SPFILENOTIFY_NEEDNEWCABINET: default: // // We should never get these. // MYASSERT(0); rc = ERROR_INVALID_FUNCTION; break; } return(rc); } #ifdef UNICODE // // ANSI version // DWORD SetupGetFileCompressionInfoA( IN PCSTR SourceFileName, OUT PSTR *ActualSourceFileName, OUT PDWORD SourceFileSize, OUT PDWORD TargetFileSize, OUT PUINT CompressionType ) { WIN32_FIND_DATA FindData; DWORD d; PCWSTR source; PWSTR actualsource = NULL; PSTR actualsourceansi = NULL; PSTR la_actualsourceansi = NULL; DWORD targetsize; UINT type; d = pSetupCaptureAndConvertAnsiArg(SourceFileName,&source); if(d != NO_ERROR) { return(d); } d = SetupInternalGetFileCompressionInfo(source,&actualsource,&FindData,&targetsize,&type); if(d == NO_ERROR) { MYASSERT(actualsource); if((actualsourceansi = pSetupUnicodeToAnsi(actualsource))==NULL) { d = ERROR_NOT_ENOUGH_MEMORY; goto clean0; } if((la_actualsourceansi = (PSTR)OldMyMalloc(1+strlen(actualsourceansi)))==NULL) { d = ERROR_NOT_ENOUGH_MEMORY; goto clean0; } strcpy(la_actualsourceansi,actualsourceansi); try { *SourceFileSize = FindData.nFileSizeLow; *ActualSourceFileName = la_actualsourceansi; // free using LocalFree *TargetFileSize = targetsize; *CompressionType = type; la_actualsourceansi = NULL; } except(EXCEPTION_EXECUTE_HANDLER) { d = ERROR_INVALID_PARAMETER; } } clean0: if(actualsource) { MyFree(actualsource); } if(actualsourceansi) { MyFree(actualsourceansi); } if(la_actualsourceansi) { OldMyFree(la_actualsourceansi); } MyFree(source); return(d); } #else // // Unicode stub // DWORD SetupGetFileCompressionInfoW( IN PCWSTR SourceFileName, OUT PWSTR *ActualSourceFileName, OUT PDWORD SourceFileSize, OUT PDWORD TargetFileSize, OUT PUINT CompressionType ) { UNREFERENCED_PARAMETER(SourceFileName); UNREFERENCED_PARAMETER(ActualSourceFileName); UNREFERENCED_PARAMETER(SourceFileSize); UNREFERENCED_PARAMETER(TargetFileSize); UNREFERENCED_PARAMETER(CompressionType); return(ERROR_CALL_NOT_IMPLEMENTED); } #endif DWORD SetupGetFileCompressionInfo( IN PCTSTR SourceFileName, OUT PTSTR *ActualSourceFileName, OUT PDWORD SourceFileSize, OUT PDWORD TargetFileSize, OUT PUINT CompressionType ) /*++ Routine Description: Here for App-Compat only Replaced by SetupGetFileCompressionInfoEx Return pointer is allocated by OldMyMalloc, it can be freed by (*cough*) OldMyFree (exported as MyFree) This is because there are apps out there that use this, and run-time link to setupapi!MyFree to release memory !!!! DO NOT USE THIS API !!!! Arguments: SourceFileName - supplies filename of the file to be checked. This filename is used as a base; if not found then we look for the 2 compressed forms (ie, foo.ex_, foo.ex$) as well. ActualSourceFileName - receives a pointer to the filename that was actually located. Caller can free with exported MyFree(). Valid only if the return code from this routine is NO_ERROR. SourceFileSize - receives the size of the located file in its current (ie, compressed) form. Valid only if this routine returns NO_ERROR. TargetFileSize - receives the uncompressed size of the file. If the file is not compressed this will be the same as SourceFileSize. Valid only if this routine returns NO_ERROR. CompressionType - receives a value indicating the compression type. Valid only if this routine returns NO_ERROR. Return Value: Win32 error code indicating the outcome. ERROR_FILE_NOT_FOUND - normal code indicating everything is ok but we can't find the file NO_ERROR - file was located and output params are filled in. Others - something is wrong with the hardware or system. --*/ { WIN32_FIND_DATA FindData; DWORD d; PCTSTR source; PTSTR actualsource = NULL; PTSTR la_actualsource = NULL; DWORD targetsize; UINT type; d = CaptureStringArg(SourceFileName,&source); if(d != NO_ERROR) { return(d); } d = SetupInternalGetFileCompressionInfo(source,&actualsource,&FindData,&targetsize,&type); if(d == NO_ERROR) { MYASSERT(actualsource); la_actualsource = (PTSTR)OldMyMalloc(sizeof(TCHAR)*(1+lstrlen(actualsource))); if (la_actualsource == NULL) { MyFree(actualsource); MyFree(source); return ERROR_NOT_ENOUGH_MEMORY; } lstrcpy(la_actualsource,actualsource); try { *SourceFileSize = FindData.nFileSizeLow; *ActualSourceFileName = la_actualsource; // free using LocalFree *TargetFileSize = targetsize; *CompressionType = type; } except(EXCEPTION_EXECUTE_HANDLER) { d = ERROR_INVALID_PARAMETER; } if(d != NO_ERROR) { OldMyFree(la_actualsource); } MyFree(actualsource); } MyFree(source); return(d); } #ifdef UNICODE // // ANSI version // BOOL WINAPI SetupGetFileCompressionInfoExA( IN PCSTR SourceFileName, IN PSTR ActualSourceFileNameBuffer, IN DWORD ActualSourceFileNameBufferLen, OUT PDWORD RequiredBufferLen, OPTIONAL OUT PDWORD SourceFileSize, OUT PDWORD TargetFileSize, OUT PUINT CompressionType ) { WIN32_FIND_DATA FindData; DWORD d; PCWSTR source; PWSTR actualsource = NULL; PSTR actualsourceansi = NULL; DWORD targetsize; DWORD reqbufsize; UINT type; d = pSetupCaptureAndConvertAnsiArg(SourceFileName,&source); if(d != NO_ERROR) { SetLastError(d); return (d==NO_ERROR); } d = SetupInternalGetFileCompressionInfo(source,&actualsource,&FindData,&targetsize,&type); if(d == NO_ERROR) { MYASSERT(actualsource); actualsourceansi = pSetupUnicodeToAnsi(actualsource); if(actualsourceansi != NULL) { try { reqbufsize = strlen(actualsourceansi)+1; if (RequiredBufferLen) { *RequiredBufferLen = reqbufsize; } if(ActualSourceFileNameBuffer) { if((ActualSourceFileNameBufferLen < reqbufsize)) { d = ERROR_INSUFFICIENT_BUFFER; } else { strcpy(ActualSourceFileNameBuffer,actualsourceansi); } } else if(ActualSourceFileNameBufferLen) { d = ERROR_INVALID_USER_BUFFER; } *SourceFileSize = FindData.nFileSizeLow; *TargetFileSize = targetsize; *CompressionType = type; } except(EXCEPTION_EXECUTE_HANDLER) { d = ERROR_INVALID_PARAMETER; } } else { d = ERROR_NOT_ENOUGH_MEMORY; } } if(actualsource) { MyFree(actualsource); } if(actualsourceansi) { MyFree(actualsourceansi); } MyFree(source); SetLastError(d); return (d==NO_ERROR); } #else // // Unicode stub // BOOL WINAPI SetupGetFileCompressionInfoExW( IN PCWSTR SourceFileName, IN PWSTR ActualSourceFileNameBuffer, IN DWORD ActualSourceFileNameBufferLen, OUT PDWORD RequiredBufferLen, OPTIONAL OUT PDWORD SourceFileSize, OUT PDWORD TargetFileSize, OUT PUINT CompressionType ) { UNREFERENCED_PARAMETER(SourceFileName); UNREFERENCED_PARAMETER(ActualSourceFileNameBuffer); UNREFERENCED_PARAMETER(ActualSourceFileNameBufferLen); UNREFERENCED_PARAMETER(RequiredBufferLen); UNREFERENCED_PARAMETER(SourceFileSize); UNREFERENCED_PARAMETER(TargetFileSize); UNREFERENCED_PARAMETER(CompressionType); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } #endif BOOL WINAPI SetupGetFileCompressionInfoEx( IN PCTSTR SourceFileName, IN PTSTR ActualSourceFileNameBuffer, IN DWORD ActualSourceFileNameBufferLen, OUT PDWORD RequiredBufferLen, OPTIONAL OUT PDWORD SourceFileSize, OUT PDWORD TargetFileSize, OUT PUINT CompressionType ) /*++ Routine Description: Determine whether a file is compressed, and retreive additional information about it. This is the replacement for the very broken SetupGetFileCompressionInfo Caller must pass in a buffer If the buffer is NULL, return size and all other parameters filled out (unless some other error occurred) note however, you would typically call this with a buffer size of MAX_PATH. Arguments: SourceFileName - supplies filename of the file to be checked. This filename is used as a base; if not found then we look for the 2 compressed forms (ie, foo.ex_, foo.ex$) as well. ActualSourceFileNameBuffer - if not NULL, receives actual filename Valid only if the return code from this routine is NO_ERROR. ActualSourceFileNameBufferLen - pass in length (characters) of ActualSourceFileNameBuffer. must be 0 if ActualSourceFileNameBuffer is NULL. RequiredBufferLen - if not NULL, filled with length of actual filename including terminating NULL. Valid only if the return code from this routine is NO_ERROR or ERROR_INSUFFICIENT_BUFFER. SourceFileSize - receives the size of the located file in its current (ie, compressed) form. Valid only if this routine returns NO_ERROR or ERROR_INSUFFICIENT_BUFFER. TargetFileSize - receives the uncompressed size of the file. If the file is not compressed this will be the same as SourceFileSize. Valid only if this routine returns NO_ERROR or ERROR_INSUFFICIENT_BUFFER. CompressionType - receives a value indicating the compression type. Valid only if this routine returns NO_ERROR or ERROR_INSUFFICIENT_BUFFER. Return Value: TRUE indicating success (NO_ERROR) FALSE indicating failure GetLastError() provides Win32 error code indicating the outcome. ERROR_FILE_NOT_FOUND - normal code indicating everything is ok but we can't find the file NO_ERROR - file was located and all output params are filled in including ActualSourceFileNameBuffer. also returned if ActualSourceFileNameBuffer is NULL ERROR_INSUFFICIENT_BUFFER - file was located and output params are filled in, excluding ActualSourceFileNameBuffer. Others - something is wrong with the hardware or system. --*/ { WIN32_FIND_DATA FindData; DWORD d; PCTSTR source; PTSTR actualsource = NULL; DWORD targetsize; UINT type; DWORD reqbufsize; d = CaptureStringArg(SourceFileName,&source); if(d != NO_ERROR) { SetLastError(d); return (d==NO_ERROR); } d = SetupInternalGetFileCompressionInfo(source,&actualsource,&FindData,&targetsize,&type); if(d == NO_ERROR) { MYASSERT(actualsource); try { reqbufsize = lstrlen(actualsource)+1; if (RequiredBufferLen) { *RequiredBufferLen = reqbufsize; } if(ActualSourceFileNameBuffer) { if(ActualSourceFileNameBufferLen < reqbufsize) { d = ERROR_INSUFFICIENT_BUFFER; } else { lstrcpy(ActualSourceFileNameBuffer,actualsource); } } else if(ActualSourceFileNameBufferLen) { d = ERROR_INVALID_USER_BUFFER; } *SourceFileSize = FindData.nFileSizeLow; *TargetFileSize = targetsize; *CompressionType = type; } except(EXCEPTION_EXECUTE_HANDLER) { d = ERROR_INVALID_PARAMETER; } MyFree(actualsource); } MyFree(source); SetLastError(d); return (d==NO_ERROR); } #ifdef UNICODE // // ANSI version // DWORD SetupDecompressOrCopyFileA( IN PCSTR SourceFileName, OUT PCSTR TargetFileName, OUT PUINT CompressionType OPTIONAL ) { DWORD rc; PCWSTR s,t; rc = pSetupCaptureAndConvertAnsiArg(SourceFileName,&s); if(rc == NO_ERROR) { rc = pSetupCaptureAndConvertAnsiArg(TargetFileName,&t); if(rc == NO_ERROR) { rc = pSetupDecompressOrCopyFile(s,t,CompressionType,FALSE,NULL); MyFree(t); } MyFree(s); } return(rc); } #else // // Unicode stub // DWORD SetupDecompressOrCopyFileW( IN PCWSTR SourceFileName, OUT PCWSTR TargetFileName, OUT PUINT CompressionType OPTIONAL ) { UNREFERENCED_PARAMETER(SourceFileName); UNREFERENCED_PARAMETER(TargetFileName); UNREFERENCED_PARAMETER(CompressionType); return(ERROR_CALL_NOT_IMPLEMENTED); } #endif DWORD SetupDecompressOrCopyFile( IN PCTSTR SourceFileName, IN PCTSTR TargetFileName, IN PUINT CompressionType OPTIONAL ) /*++ Routine Description: Decompress or copy a file. Arguments: SourceFileName - supplies filename of the file to be decompressed. If CompressionType is specified, no additional processing is performed on this name -- the caller is responsible for determining the actual file name (ie, foo.ex_ instead of foo.exe) before calling this routine. If CompressionType is not specified, then this routine attempts to locate the compressed form of the filename if the file is not found with the name given. TargetFileName - supplies filename of target file. CompressionType - if specified, supplies type of compression in use on the source. This can be determined by calling SetupGetFileCompressionInfo(). Specifying FILE_COMPRESSION_NONE results in the file being copied and not decompressed, regardless of the type of compression that may be in use on the source. If this value is not specified then this routine attempts to determine the compression type and decompresses/copies accordingly. Return Value: Win32 error code indicating the outcome. --*/ { DWORD rc; PCTSTR s,t; rc = CaptureStringArg(SourceFileName,&s); if(rc == NO_ERROR) { rc = CaptureStringArg(TargetFileName,&t); if(rc == NO_ERROR) { rc = pSetupDecompressOrCopyFile(s,t,CompressionType,FALSE,NULL); MyFree(t); } MyFree(s); } return(rc); } DWORD pSetupDecompressOrCopyFile( IN PCTSTR SourceFileName, IN PCTSTR TargetFileName, IN PUINT CompressionType, OPTIONAL IN BOOL AllowMove, OUT PBOOL Moved OPTIONAL ) /*++ Routine Description: Decompress or copy a file. Arguments: SourceFileName - supplies filename of the file to be decompressed. If CompressionType is specified, no additional processing is performed on this name -- the caller is responsible for determining the actual file name (ie, foo.ex_ instead of foo.exe) before calling this routine. If CompressionType is not specified, then this routine attempts to locate the compressed form of the filename if the file is not found with the name given. TargetFileName - supplies filename of target file. CompressionType - if specified, supplies type of compression in use on the source. This can be determined by calling SetupGetFileCompressionInfo(). Specifying FILE_COMPRESSION_NONE results in the file being copied and not decompressed, regardless of the type of compression that may be in use on the source. If this value is not specified then this routine attempts to determine the compression type and decompresses/copies accordingly. AllowMove - if specified, then files that do not require decompression will be moved instead of copied. Moved - if specified receives a boolean indicating whether the file was moved (as opposed to copied or decompressed). Return Value: Win32 error code indicating the outcome. --*/ { DWORD d; UINT ComprType; PTSTR ActualName; DWORD TargetSize; FILETIME CreateTime,AccessTime,WriteTime; SFD_INFO CBData; BOOL moved; WIN32_FIND_DATA FindData; if(Moved) { *Moved = FALSE; } if(CompressionType) { ComprType = *CompressionType; ActualName = (PTSTR)SourceFileName; } else { // // Need to determine compresison type. // d = SetupInternalGetFileCompressionInfo( SourceFileName, &ActualName, &FindData, &TargetSize, &ComprType ); if(d != NO_ERROR) { return(d); } } // // Blast the target file. Ignore if failure -- it'll be caught later. // SetFileAttributes(TargetFileName,FILE_ATTRIBUTE_NORMAL); DeleteFile(TargetFileName); switch(ComprType) { case FILE_COMPRESSION_NONE: moved = (AllowMove ? MoveFile(ActualName,TargetFileName) : FALSE); if(moved) { d = NO_ERROR; if(Moved) { *Moved = TRUE; } } else { d = GetSetFileTimestamp(ActualName,&CreateTime,&AccessTime,&WriteTime,FALSE); if(d == NO_ERROR) { d = CopyFile(ActualName,TargetFileName,FALSE) ? NO_ERROR : GetLastError(); if(d == NO_ERROR) { GetSetFileTimestamp(TargetFileName,&CreateTime,&AccessTime,&WriteTime,TRUE); } } } break; case FILE_COMPRESSION_WINLZA: d = pSetupDecompressWinLzFile(ActualName,(PTSTR)TargetFileName); break; case FILE_COMPRESSION_MSZIP: CBData.FileCount = 0; CBData.TargetFile = TargetFileName; CBData.GotTimestamp = FALSE; d = DiamondProcessCabinet( ActualName, 0, pSingleFileDecompCB, &CBData, TRUE ); break; default: d = ERROR_INVALID_PARAMETER; break; } if(!CompressionType) { MyFree(ActualName); } return(d); } UINT pSingleFileDecompCB( IN PVOID Context, IN UINT Notification, IN UINT_PTR Param1, IN UINT_PTR Param2 ) { PSFD_INFO Data; PFILE_IN_CABINET_INFO FileInfo; PFILEPATHS FilePaths; DWORD rc; HANDLE h; Data = Context; switch(Notification) { case SPFILENOTIFY_CABINETINFO: // // We don't do anything with this. // rc = NO_ERROR; break; case SPFILENOTIFY_FILEINCABINET: // // New file within a cabinet. // FileInfo = (PFILE_IN_CABINET_INFO)Param1; FileInfo->Win32Error = NO_ERROR; // // We only want the first file. If this is a subsequent file, // bail out. // if(Data->FileCount++) { rc = FILEOP_ABORT; SetLastError(NO_ERROR); } else { // // We want the file. Ignore the names in the cabinet and // use the name given to us. Also, we want to preserve // the timestamp of the cabinet, not of the file within it. // lstrcpyn(FileInfo->FullTargetName,Data->TargetFile,MAX_PATH); h = CreateFile( (PCTSTR)Param2, // cabinet filename GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); if(h != INVALID_HANDLE_VALUE) { if(GetFileTime(h,NULL,NULL,&Data->FileTime)) { Data->GotTimestamp = TRUE; } CloseHandle(h); } rc = FILEOP_DOIT; } break; case SPFILENOTIFY_FILEEXTRACTED: // // File was successfully extracted. // Preserve timestamp. // FilePaths = (PFILEPATHS)Param1; if(Data->GotTimestamp) { h = CreateFile( FilePaths->Target, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); if(h != INVALID_HANDLE_VALUE) { SetFileTime(h,NULL,NULL,&Data->FileTime); CloseHandle(h); } } rc = NO_ERROR; break; //case SPFILENOTIFY_NEEDNEWCABINET: default: // // We should never get this. // MYASSERT(0); rc = ERROR_INVALID_FUNCTION; break; } return(rc); }