2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS text-mode setup
4 * FILE: subsys/system/usetup/cabinet.c
5 * PURPOSE: Cabinet routines
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
8 * CSH 15/08-2003 Created
21 #define SEEK_CURRENT 1
26 typedef struct __DOSTIME
34 typedef struct __DOSDATE
41 static WCHAR CabinetName
[256]; // Filename of current cabinet
42 static WCHAR CabinetPrev
[256]; // Filename of previous cabinet
43 static WCHAR DiskPrev
[256]; // Label of cabinet in file CabinetPrev
44 static WCHAR CabinetNext
[256]; // Filename of next cabinet
45 static WCHAR DiskNext
[256]; // Label of cabinet in file CabinetNext
46 static ULONG FolderUncompSize
= 0; // Uncompressed size of folder
47 static ULONG BytesLeftInBlock
= 0; // Number of bytes left in current block
48 static BOOL ReuseBlock
= FALSE
;
49 static WCHAR DestPath
[MAX_PATH
];
50 static HANDLE FileHandle
;
51 static BOOL FileOpen
= FALSE
;
52 static CFHEADER CABHeader
;
53 static ULONG CabinetReserved
= 0;
54 static ULONG FolderReserved
= 0;
55 static ULONG DataReserved
= 0;
56 static PCFFOLDER_NODE FolderListHead
= NULL
;
57 static PCFFOLDER_NODE FolderListTail
= NULL
;
58 static PCFFOLDER_NODE CurrentFolderNode
= NULL
;
59 static PCFDATA_NODE CurrentDataNode
= NULL
;
60 static PCFFILE_NODE FileListHead
= NULL
;
61 static PCFFILE_NODE FileListTail
= NULL
;
63 static PCABINET_CODEC_UNCOMPRESS CodecUncompress
= NULL
;
64 static BOOL CodecSelected
= FALSE
;
65 static PVOID InputBuffer
= NULL
;
66 static PVOID CurrentIBuffer
= NULL
; // Current offset in input buffer
67 static ULONG CurrentIBufferSize
= 0; // Bytes left in input buffer
68 static PVOID OutputBuffer
= NULL
;
69 static PVOID CurrentOBuffer
= NULL
; // Current offset in output buffer
70 static ULONG CurrentOBufferSize
= 0; // Bytes left in output buffer
71 static BOOL RestartSearch
= FALSE
;
72 static ULONG LastFileOffset
= 0; // Uncompressed offset of last extracted file
73 static PCABINET_OVERWRITE OverwriteHandler
= NULL
;
74 static PCABINET_EXTRACT ExtractHandler
= NULL
;
75 static PCABINET_DISK_CHANGE DiskChangeHandler
= NULL
;
76 static z_stream ZStream
;
77 static PVOID CabinetReservedArea
= NULL
;
80 /* Needed by zlib, but we don't want the dependency on msvcrt.dll */
84 RtlFreeHeap(ProcessHeap
, 0, _ptr
);
87 void* calloc(size_t _nmemb
, size_t _size
)
89 return (void*)RtlAllocateHeap (ProcessHeap
, HEAP_ZERO_MEMORY
, _size
);
95 RawCodecUncompress(PVOID OutputBuffer
,
100 * FUNCTION: Uncompresses data in a buffer
102 * OutputBuffer = Pointer to buffer to place uncompressed data
103 * InputBuffer = Pointer to buffer with data to be uncompressed
104 * InputLength = Length of input buffer
105 * OutputLength = Address of buffer to place size of uncompressed data
108 memcpy(OutputBuffer
, InputBuffer
, InputLength
);
109 *OutputLength
= InputLength
;
117 MSZipCodecUncompress(PVOID OutputBuffer
,
122 * FUNCTION: Uncompresses data in a buffer
124 * OutputBuffer = Pointer to buffer to place uncompressed data
125 * InputBuffer = Pointer to buffer with data to be uncompressed
126 * InputLength = Length of input buffer
127 * OutputLength = Address of buffer to place size of uncompressed data
133 DPRINT("InputLength (%d).\n", InputLength
);
135 Magic
= *((PUSHORT
)InputBuffer
);
137 if (Magic
!= MSZIP_MAGIC
)
139 DPRINT("Bad MSZIP block header magic (0x%X)\n", Magic
);
143 ZStream
.next_in
= (PUCHAR
)((ULONG
)InputBuffer
+ 2);
144 ZStream
.avail_in
= InputLength
- 2;
145 ZStream
.next_out
= (PUCHAR
)OutputBuffer
;
146 ZStream
.avail_out
= CAB_BLOCKSIZE
+ 12;
148 /* WindowBits is passed < 0 to tell that there is no zlib header.
149 * Note that in this case inflate *requires* an extra "dummy" byte
150 * after the compressed stream in order to complete decompression and
151 * return Z_STREAM_END.
153 Status
= inflateInit2(&ZStream
, -MAX_WBITS
);
156 DPRINT("inflateInit2() returned (%d).\n", Status
);
160 while ((ZStream
.total_out
< CAB_BLOCKSIZE
+ 12) &&
161 (ZStream
.total_in
< InputLength
- 2))
163 Status
= inflate(&ZStream
, Z_NO_FLUSH
);
164 if (Status
== Z_STREAM_END
) break;
167 DPRINT("inflate() returned (%d) (%s).\n", Status
, ZStream
.msg
);
168 if (Status
== Z_MEM_ERROR
)
174 *OutputLength
= ZStream
.total_out
;
176 Status
= inflateEnd(&ZStream
);
179 DPRINT("inflateEnd() returned (%d).\n", Status
);
187 /* Memory functions */
189 voidpf
MSZipAlloc(voidpf opaque
, uInt items
, uInt size
)
191 return (voidpf
)RtlAllocateHeap (ProcessHeap
, 0, items
* size
);
194 void MSZipFree (voidpf opaque
, voidpf address
)
196 RtlFreeHeap(ProcessHeap
, 0, address
);
201 SeekInFile(HANDLE hFile
,
202 LONG lDistanceToMove
,
203 PLONG lpDistanceToMoveHigh
,
207 FILE_POSITION_INFORMATION FilePosition
;
208 FILE_STANDARD_INFORMATION FileStandard
;
210 IO_STATUS_BLOCK IoStatusBlock
;
211 LARGE_INTEGER Distance
;
213 DPRINT("SeekInFile(hFile %x, lDistanceToMove %d, dwMoveMethod %d)\n",
214 hFile
,lDistanceToMove
,dwMoveMethod
);
216 Distance
.u
.LowPart
= lDistanceToMove
;
217 if (lpDistanceToMoveHigh
)
219 Distance
.u
.HighPart
= *lpDistanceToMoveHigh
;
221 else if (lDistanceToMove
>= 0)
223 Distance
.u
.HighPart
= 0;
227 Distance
.u
.HighPart
= -1;
230 if (dwMoveMethod
== SEEK_CURRENT
)
232 NtQueryInformationFile(hFile
,
235 sizeof(FILE_POSITION_INFORMATION
),
236 FilePositionInformation
);
237 FilePosition
.CurrentByteOffset
.QuadPart
+= Distance
.QuadPart
;
239 else if (dwMoveMethod
== SEEK_END
)
241 NtQueryInformationFile(hFile
,
244 sizeof(FILE_STANDARD_INFORMATION
),
245 FileStandardInformation
);
246 FilePosition
.CurrentByteOffset
.QuadPart
=
247 FileStandard
.EndOfFile
.QuadPart
+ Distance
.QuadPart
;
249 else if ( dwMoveMethod
== SEEK_BEGIN
)
251 FilePosition
.CurrentByteOffset
.QuadPart
= Distance
.QuadPart
;
254 // DPRINT1("GOTO FILE OFFSET: %I64d\n", FilePosition.CurrentByteOffset.QuadPart);
256 errCode
= NtSetInformationFile(hFile
,
259 sizeof(FILE_POSITION_INFORMATION
),
260 FilePositionInformation
);
261 if (!NT_SUCCESS(errCode
))
270 if (lpDistanceToMoveHigh
!= NULL
)
272 *lpDistanceToMoveHigh
= FilePosition
.CurrentByteOffset
.u
.HighPart
;
276 *Status
= STATUS_SUCCESS
;
278 return FilePosition
.CurrentByteOffset
.u
.LowPart
;
283 ConvertSystemTimeToFileTime(
284 CONST SYSTEMTIME
* lpSystemTime
,
285 LPFILETIME lpFileTime
)
287 TIME_FIELDS TimeFields
;
288 LARGE_INTEGER liTime
;
290 TimeFields
.Year
= lpSystemTime
->wYear
;
291 TimeFields
.Month
= lpSystemTime
->wMonth
;
292 TimeFields
.Day
= lpSystemTime
->wDay
;
293 TimeFields
.Hour
= lpSystemTime
->wHour
;
294 TimeFields
.Minute
= lpSystemTime
->wMinute
;
295 TimeFields
.Second
= lpSystemTime
->wSecond
;
296 TimeFields
.Milliseconds
= lpSystemTime
->wMilliseconds
;
298 if (RtlTimeFieldsToTime(&TimeFields
, &liTime
))
300 lpFileTime
->dwLowDateTime
= liTime
.u
.LowPart
;
301 lpFileTime
->dwHighDateTime
= liTime
.u
.HighPart
;
309 ConvertDosDateTimeToFileTime(
312 LPFILETIME lpFileTime
)
314 PDOSTIME pdtime
= (PDOSTIME
) &wFatTime
;
315 PDOSDATE pddate
= (PDOSDATE
) &wFatDate
;
316 SYSTEMTIME SystemTime
;
318 if (lpFileTime
== NULL
)
321 SystemTime
.wMilliseconds
= 0;
322 SystemTime
.wSecond
= pdtime
->Second
;
323 SystemTime
.wMinute
= pdtime
->Minute
;
324 SystemTime
.wHour
= pdtime
->Hour
;
326 SystemTime
.wDay
= pddate
->Day
;
327 SystemTime
.wMonth
= pddate
->Month
;
328 SystemTime
.wYear
= 1980 + pddate
->Year
;
330 ConvertSystemTimeToFileTime(&SystemTime
,lpFileTime
);
337 GetFileName(PWCHAR Path
)
339 * FUNCTION: Returns a pointer to file name
341 * Path = Pointer to string with pathname
343 * Pointer to filename
352 if (Path
[i
- 1] == L
'\\') j
= i
;
359 RemoveFileName(PWCHAR Path
)
361 * FUNCTION: Removes a file name from a path
363 * Path = Pointer to string with path
370 FileName
= GetFileName(Path
+ i
);
372 if ((FileName
!= (Path
+ i
)) && (FileName
[-1] == L
'\\'))
374 if ((FileName
== (Path
+ i
)) && (FileName
[0] == L
'\\'))
381 SetAttributesOnFile(PCFFILE_NODE File
, HANDLE hFile
)
383 * FUNCTION: Sets attributes on a file
385 * File = Pointer to CFFILE node for file
387 * Status of operation
390 FILE_BASIC_INFORMATION FileBasic
;
391 IO_STATUS_BLOCK IoStatusBlock
;
393 ULONG Attributes
= 0;
395 if (File
->File
.Attributes
& CAB_ATTRIB_READONLY
)
396 Attributes
|= FILE_ATTRIBUTE_READONLY
;
398 if (File
->File
.Attributes
& CAB_ATTRIB_HIDDEN
)
399 Attributes
|= FILE_ATTRIBUTE_HIDDEN
;
401 if (File
->File
.Attributes
& CAB_ATTRIB_SYSTEM
)
402 Attributes
|= FILE_ATTRIBUTE_SYSTEM
;
404 if (File
->File
.Attributes
& CAB_ATTRIB_DIRECTORY
)
405 Attributes
|= FILE_ATTRIBUTE_DIRECTORY
;
407 if (File
->File
.Attributes
& CAB_ATTRIB_ARCHIVE
)
408 Attributes
|= FILE_ATTRIBUTE_ARCHIVE
;
410 NtStatus
= NtQueryInformationFile(hFile
,
413 sizeof(FILE_BASIC_INFORMATION
),
414 FileBasicInformation
);
415 if (!NT_SUCCESS(NtStatus
))
417 DPRINT("NtQueryInformationFile() failed (%x).\n", NtStatus
);
421 FileBasic
.FileAttributes
= Attributes
;
423 NtStatus
= NtSetInformationFile(hFile
,
426 sizeof(FILE_BASIC_INFORMATION
),
427 FileBasicInformation
);
428 if (!NT_SUCCESS(NtStatus
))
430 DPRINT("NtSetInformationFile() failed (%x).\n", NtStatus
);
434 return NT_SUCCESS(NtStatus
);
439 ReadBlock(PVOID Buffer
,
443 * FUNCTION: Read a block of data from file
445 * Buffer = Pointer to data buffer
446 * Size = Length of data buffer
447 * BytesRead = Pointer to ULONG that on return will contain
448 * number of bytes read
450 * Status of operation
453 IO_STATUS_BLOCK IoStatusBlock
;
456 NtStatus
= NtReadFile(FileHandle
,
465 if (!NT_SUCCESS(NtStatus
))
467 DPRINT("ReadBlock for %d bytes failed (%x)\n", Size
, NtStatus
);
469 return CAB_STATUS_INVALID_CAB
;
472 return CAB_STATUS_SUCCESS
;
476 static PCFFOLDER_NODE
479 * FUNCTION: Creates a new folder node
481 * Pointer to node if there was enough free memory available, otherwise NULL
486 Node
= (PCFFOLDER_NODE
)RtlAllocateHeap (ProcessHeap
, 0, sizeof(CFFOLDER_NODE
));
490 RtlZeroMemory(Node
, sizeof(CFFOLDER_NODE
));
492 Node
->Folder
.CompressionType
= CAB_COMP_NONE
;
494 Node
->Prev
= FolderListTail
;
496 if (FolderListTail
!= NULL
)
498 FolderListTail
->Next
= Node
;
502 FolderListHead
= Node
;
504 FolderListTail
= Node
;
513 * FUNCTION: Creates a new file node
515 * FolderNode = Pointer to folder node to bind file to
517 * Pointer to node if there was enough free memory available, otherwise NULL
522 Node
= (PCFFILE_NODE
)RtlAllocateHeap (ProcessHeap
, 0, sizeof(CFFILE_NODE
));
526 RtlZeroMemory(Node
, sizeof(CFFILE_NODE
));
528 Node
->Prev
= FileListTail
;
530 if (FileListTail
!= NULL
)
532 FileListTail
->Next
= Node
;
545 NewDataNode(PCFFOLDER_NODE FolderNode
)
547 * FUNCTION: Creates a new data block node
549 * FolderNode = Pointer to folder node to bind data block to
551 * Pointer to node if there was enough free memory available, otherwise NULL
556 Node
= (PCFDATA_NODE
)RtlAllocateHeap (ProcessHeap
, 0, sizeof(CFDATA_NODE
));
560 RtlZeroMemory(Node
, sizeof(CFDATA_NODE
));
562 Node
->Prev
= FolderNode
->DataListTail
;
564 if (FolderNode
->DataListTail
!= NULL
)
566 FolderNode
->DataListTail
->Next
= Node
;
570 FolderNode
->DataListHead
= Node
;
572 FolderNode
->DataListTail
= Node
;
579 DestroyDataNodes(PCFFOLDER_NODE FolderNode
)
581 * FUNCTION: Destroys data block nodes bound to a folder node
583 * FolderNode = Pointer to folder node
586 PCFDATA_NODE PrevNode
;
587 PCFDATA_NODE NextNode
;
589 NextNode
= FolderNode
->DataListHead
;
590 while (NextNode
!= NULL
)
592 PrevNode
= NextNode
->Next
;
593 RtlFreeHeap(ProcessHeap
, 0, NextNode
);
596 FolderNode
->DataListHead
= NULL
;
597 FolderNode
->DataListTail
= NULL
;
604 * FUNCTION: Destroys file nodes
606 * FolderNode = Pointer to folder node
609 PCFFILE_NODE PrevNode
;
610 PCFFILE_NODE NextNode
;
612 NextNode
= FileListHead
;
613 while (NextNode
!= NULL
)
615 PrevNode
= NextNode
->Next
;
616 if (NextNode
->FileName
)
617 RtlFreeHeap(ProcessHeap
, 0, NextNode
->FileName
);
618 RtlFreeHeap(ProcessHeap
, 0, NextNode
);
628 DestroyDeletedFileNodes()
630 * FUNCTION: Destroys file nodes that are marked for deletion
633 PCFFILE_NODE CurNode
;
634 PCFFILE_NODE NextNode
;
636 CurNode
= FileListHead
;
637 while (CurNode
!= NULL
)
639 NextNode
= CurNode
->Next
;
643 if (CurNode
->Prev
!= NULL
)
645 CurNode
->Prev
->Next
= CurNode
->Next
;
649 FileListHead
= CurNode
->Next
;
651 FileListHead
->Prev
= NULL
;
654 if (CurNode
->Next
!= NULL
)
656 CurNode
->Next
->Prev
= CurNode
->Prev
;
660 FileListTail
= CurNode
->Prev
;
662 FileListTail
->Next
= NULL
;
665 DPRINT("Deleting file: '%S'\n", CurNode
->FileName
);
667 if (CurNode
->FileName
)
668 RtlFreeHeap(ProcessHeap
, 0, CurNode
->FileName
);
669 RtlFreeHeap(ProcessHeap
, 0, CurNode
);
679 * FUNCTION: Destroys folder nodes
682 PCFFOLDER_NODE PrevNode
;
683 PCFFOLDER_NODE NextNode
;
685 NextNode
= FolderListHead
;
686 while (NextNode
!= NULL
)
688 PrevNode
= NextNode
->Next
;
689 DestroyDataNodes(NextNode
);
690 RtlFreeHeap(ProcessHeap
, 0, NextNode
);
693 FolderListHead
= NULL
;
694 FolderListTail
= NULL
;
699 DestroyDeletedFolderNodes()
701 * FUNCTION: Destroys folder nodes that are marked for deletion
704 PCFFOLDER_NODE CurNode
;
705 PCFFOLDER_NODE NextNode
;
707 CurNode
= FolderListHead
;
708 while (CurNode
!= NULL
)
710 NextNode
= CurNode
->Next
;
714 if (CurNode
->Prev
!= NULL
)
716 CurNode
->Prev
->Next
= CurNode
->Next
;
720 FolderListHead
= CurNode
->Next
;
722 FolderListHead
->Prev
= NULL
;
725 if (CurNode
->Next
!= NULL
)
727 CurNode
->Next
->Prev
= CurNode
->Prev
;
731 FolderListTail
= CurNode
->Prev
;
733 FolderListTail
->Next
= NULL
;
736 DestroyDataNodes(CurNode
);
737 RtlFreeHeap(ProcessHeap
, 0, CurNode
);
744 static PCFFOLDER_NODE
745 LocateFolderNode(ULONG Index
)
747 * FUNCTION: Locates a folder node
749 * Index = Folder index
751 * Pointer to folder node or NULL if the folder node was not found
759 return FolderListTail
;
761 case CAB_FILE_CONTINUED
:
762 case CAB_FILE_PREV_NEXT
:
763 return FolderListHead
;
766 Node
= FolderListHead
;
769 if (Node
->Index
== Index
)
778 GetAbsoluteOffset(PCFFILE_NODE File
)
780 * FUNCTION: Returns the absolute offset of a file
782 * File = Pointer to CFFILE_NODE structure for file
784 * Status of operation
789 DPRINT("FileName '%S' FileOffset (0x%X) FileSize (%d).\n",
790 (PWCHAR
)File
->FileName
,
791 (UINT
)File
->File
.FileOffset
,
792 (UINT
)File
->File
.FileSize
);
794 Node
= CurrentFolderNode
->DataListHead
;
797 DPRINT("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%d).\n",
798 (UINT
)Node
->UncompOffset
,
799 (UINT
)Node
->UncompOffset
+ Node
->Data
.UncompSize
,
800 (UINT
)Node
->Data
.UncompSize
);
802 /* Node->Data.UncompSize will be 0 if the block is split
803 (ie. it is the last block in this cabinet) */
804 if ((Node
->Data
.UncompSize
== 0) ||
805 ((File
->File
.FileOffset
>= Node
->UncompOffset
) &&
806 (File
->File
.FileOffset
< Node
->UncompOffset
+
807 Node
->Data
.UncompSize
)))
809 File
->DataBlock
= Node
;
810 return CAB_STATUS_SUCCESS
;
815 return CAB_STATUS_INVALID_CAB
;
820 LocateFile(PWCHAR FileName
,
823 * FUNCTION: Locates a file in the cabinet
825 * FileName = Pointer to string with name of file to locate
826 * File = Address of pointer to CFFILE_NODE structure to fill
828 * Status of operation
830 * Current folder is set to the folder of the file
836 DPRINT("FileName '%S'\n", FileName
);
841 if (_wcsicmp(FileName
, Node
->FileName
) == 0)
843 CurrentFolderNode
= LocateFolderNode(Node
->File
.FileControlID
);
844 if (!CurrentFolderNode
)
846 DPRINT("Folder with index number (%d) not found.\n",
847 (UINT
)Node
->File
.FileControlID
);
848 return CAB_STATUS_INVALID_CAB
;
851 if (Node
->DataBlock
== NULL
)
853 Status
= GetAbsoluteOffset(Node
);
856 Status
= CAB_STATUS_SUCCESS
;
862 return CAB_STATUS_NOFILE
;
867 ReadString(PWCHAR String
, ULONG MaxLength
)
869 * FUNCTION: Reads a NULL-terminated string from the cabinet
871 * String = Pointer to buffer to place string
872 * MaxLength = Maximum length of string
874 * Status of operation
891 Size
= ((Offset
+ 32) <= MaxLength
)? 32 : MaxLength
- Offset
;
895 DPRINT("Too long a filename.\n");
896 return CAB_STATUS_INVALID_CAB
;
899 Status
= ReadBlock((PCFDATA
)&buf
[Offset
], Size
, &BytesRead
);
900 if ((Status
!= CAB_STATUS_SUCCESS
) || (BytesRead
!= Size
))
902 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
903 return CAB_STATUS_INVALID_CAB
;
906 for (Size
= Offset
; Size
< Offset
+ BytesRead
; Size
++)
908 if (buf
[Size
] == '\0')
918 /* Back up some bytes */
919 Size
= (BytesRead
- Size
) - 1;
920 SeekInFile(FileHandle
, -(LONG
)Size
, NULL
, SEEK_CURRENT
, &NtStatus
);
921 if (!NT_SUCCESS(NtStatus
))
923 DPRINT("SeekInFile() failed (%x).\n", NtStatus
);
924 return CAB_STATUS_INVALID_CAB
;
927 RtlInitAnsiString(&as
, buf
);
929 us
.MaximumLength
= MaxLength
* sizeof(WCHAR
);
932 RtlAnsiStringToUnicodeString(&us
, &as
, FALSE
);
934 return CAB_STATUS_SUCCESS
;
941 * FUNCTION: Reads the file table from the cabinet file
943 * Status of operation
952 DPRINT("Reading file table at absolute offset (0x%X).\n",
953 CABHeader
.FileTableOffset
);
955 /* Seek to file table */
956 SeekInFile(FileHandle
, CABHeader
.FileTableOffset
, NULL
, SEEK_BEGIN
, &NtStatus
);
957 if (!NT_SUCCESS(NtStatus
))
959 DPRINT("SeekInFile() failed (%x).\n", NtStatus
);
960 return CAB_STATUS_INVALID_CAB
;
963 for (i
= 0; i
< CABHeader
.FileCount
; i
++)
965 File
= NewFileNode();
968 DPRINT("Insufficient memory.\n");
969 return CAB_STATUS_NOMEMORY
;
972 if ((Status
= ReadBlock(&File
->File
, sizeof(CFFILE
),
973 &BytesRead
)) != CAB_STATUS_SUCCESS
) {
974 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
975 return CAB_STATUS_INVALID_CAB
;
978 File
->FileName
= (PWCHAR
)RtlAllocateHeap(ProcessHeap
, 0, MAX_PATH
* sizeof(WCHAR
));
981 DPRINT("Insufficient memory.\n");
982 return CAB_STATUS_NOMEMORY
;
986 Status
= ReadString(File
->FileName
, MAX_PATH
);
987 if (Status
!= CAB_STATUS_SUCCESS
)
990 DPRINT("Found file '%S' at uncompressed offset (0x%X). Size (%d bytes) ControlId (0x%X).\n",
991 (PWCHAR
)File
->FileName
,
992 (UINT
)File
->File
.FileOffset
,
993 (UINT
)File
->File
.FileSize
,
994 (UINT
)File
->File
.FileControlID
);
996 return CAB_STATUS_SUCCESS
;
1001 ReadDataBlocks(PCFFOLDER_NODE FolderNode
)
1003 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file
1005 * FolderNode = Pointer to CFFOLDER_NODE structure for folder
1007 * Status of operation
1010 ULONG AbsoluteOffset
;
1018 DPRINT("Reading data blocks for folder (%d) at absolute offset (0x%X).\n",
1019 FolderNode
->Index
, FolderNode
->Folder
.DataOffset
);
1021 AbsoluteOffset
= FolderNode
->Folder
.DataOffset
;
1022 UncompOffset
= FolderNode
->UncompOffset
;
1024 for (i
= 0; i
< FolderNode
->Folder
.DataBlockCount
; i
++)
1026 Node
= NewDataNode(FolderNode
);
1029 DPRINT("Insufficient memory.\n");
1030 return CAB_STATUS_NOMEMORY
;
1033 /* Seek to data block */
1034 SeekInFile(FileHandle
, AbsoluteOffset
, NULL
, SEEK_BEGIN
, &NtStatus
);
1035 if (!NT_SUCCESS(NtStatus
))
1037 DPRINT("SeekInFile() failed (%x).\n", NtStatus
);
1038 return CAB_STATUS_INVALID_CAB
;
1041 if ((Status
= ReadBlock(&Node
->Data
, sizeof(CFDATA
),
1042 &BytesRead
)) != CAB_STATUS_SUCCESS
)
1044 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1045 return CAB_STATUS_INVALID_CAB
;
1048 DPRINT("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%d) UncompSize (%d).\n",
1049 (UINT
)AbsoluteOffset
,
1051 (UINT
)Node
->Data
.Checksum
,
1052 (UINT
)Node
->Data
.CompSize
,
1053 (UINT
)Node
->Data
.UncompSize
);
1055 Node
->AbsoluteOffset
= AbsoluteOffset
;
1056 Node
->UncompOffset
= UncompOffset
;
1058 AbsoluteOffset
+= sizeof(CFDATA
) + Node
->Data
.CompSize
;
1059 UncompOffset
+= Node
->Data
.UncompSize
;
1062 FolderUncompSize
= UncompOffset
;
1064 return CAB_STATUS_SUCCESS
;
1069 ComputeChecksum(PVOID Buffer
,
1073 * FUNCTION: Computes checksum for data block
1075 * Buffer = Pointer to data buffer
1076 * Size = Length of data buffer
1077 * Seed = Previously computed checksum
1079 * Checksum of buffer
1082 INT UlongCount
; // Number of ULONGs in block
1083 ULONG Checksum
; // Checksum accumulator
1087 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE
1088 won't accept checksums computed by this routine */
1090 DPRINT("Checksumming buffer (0x%X) Size (%d)\n", (UINT
)Buffer
, Size
);
1092 UlongCount
= Size
/ 4; // Number of ULONGs
1093 Checksum
= Seed
; // Init checksum
1094 pb
= (PBYTE
)Buffer
; // Start at front of data block
1096 /* Checksum integral multiple of ULONGs */
1097 while (UlongCount
-- > 0)
1099 /* NOTE: Build ULONG in big/little-endian independent manner */
1100 ul
= *pb
++; // Get low-order byte
1101 ul
|= (((ULONG
)(*pb
++)) << 8); // Add 2nd byte
1102 ul
|= (((ULONG
)(*pb
++)) << 16); // Add 3nd byte
1103 ul
|= (((ULONG
)(*pb
++)) << 24); // Add 4th byte
1105 Checksum
^= ul
; // Update checksum
1108 /* Checksum remainder bytes */
1113 ul
|= (((ULONG
)(*pb
++)) << 16); // Add 3rd byte
1115 ul
|= (((ULONG
)(*pb
++)) << 8); // Add 2nd byte
1117 ul
|= *pb
++; // Get low-order byte
1121 Checksum
^= ul
; // Update checksum
1123 /* Return computed checksum */
1131 * FUNCTION: Closes the current cabinet
1133 * Status of operation
1138 DestroyFolderNodes();
1142 RtlFreeHeap(ProcessHeap
, 0, InputBuffer
);
1148 RtlFreeHeap(ProcessHeap
, 0, OutputBuffer
);
1149 OutputBuffer
= NULL
;
1152 NtClose(FileHandle
);
1158 CabinetInitialize(VOID
)
1160 * FUNCTION: Initialize archiver
1163 ZStream
.zalloc
= MSZipAlloc
;
1164 ZStream
.zfree
= MSZipFree
;
1165 ZStream
.opaque
= (voidpf
)0;
1168 wcscpy(DestPath
, L
"");
1170 FolderListHead
= NULL
;
1171 FolderListTail
= NULL
;
1172 FileListHead
= NULL
;
1173 FileListTail
= NULL
;
1175 CodecId
= CAB_CODEC_RAW
;
1176 CodecSelected
= TRUE
;
1178 OutputBuffer
= NULL
;
1179 CurrentOBuffer
= NULL
;
1180 CurrentOBufferSize
= 0;
1182 CurrentIBuffer
= NULL
;
1183 CurrentIBufferSize
= 0;
1185 FolderUncompSize
= 0;
1186 BytesLeftInBlock
= 0;
1187 CabinetReserved
= 0;
1191 CurrentFolderNode
= NULL
;
1192 CurrentDataNode
= NULL
;
1193 CabinetReservedArea
= NULL
;
1194 RestartSearch
= FALSE
;
1200 CabinetCleanup(VOID
)
1202 * FUNCTION: Cleanup archiver
1210 CabinetNormalizePath(PWCHAR Path
,
1213 * FUNCTION: Normalizes a path
1215 * Path = Pointer to string with pathname
1216 * Length = Number of characters in Path
1218 * TRUE if there was enough room in Path, or FALSE
1224 if ((n
= wcslen(Path
)) &&
1225 (Path
[n
- 1] != L
'\\') &&
1226 (OK
= ((n
+ 1) < Length
)))
1236 CabinetGetCabinetName()
1238 * FUNCTION: Returns pointer to cabinet file name
1240 * Pointer to string with name of cabinet
1248 CabinetSetCabinetName(PWCHAR FileName
)
1250 * FUNCTION: Sets cabinet file name
1252 * FileName = Pointer to string with name of cabinet
1255 wcscpy(CabinetName
, FileName
);
1260 CabinetSetDestinationPath(PWCHAR DestinationPath
)
1262 * FUNCTION: Sets destination path
1264 * DestinationPath = Pointer to string with name of destination path
1267 wcscpy(DestPath
, DestinationPath
);
1268 if (wcslen(DestPath
) > 0)
1269 CabinetNormalizePath(DestPath
, MAX_PATH
);
1274 CabinetGetDestinationPath()
1276 * FUNCTION: Returns destination path
1278 * Pointer to string with name of destination path
1288 * FUNCTION: Opens a cabinet file
1290 * Status of operation
1293 WCHAR CabinetFileName
[256];
1294 PCFFOLDER_NODE FolderNode
;
1300 OBJECT_ATTRIBUTES ObjectAttributes
;
1301 IO_STATUS_BLOCK IoStatusBlock
;
1302 UNICODE_STRING FileName
;
1307 OutputBuffer
= RtlAllocateHeap(ProcessHeap
, 0, CAB_BLOCKSIZE
+ 12); // This should be enough
1309 return CAB_STATUS_NOMEMORY
;
1311 RtlInitUnicodeString(&FileName
,
1314 InitializeObjectAttributes(&ObjectAttributes
,
1316 OBJ_CASE_INSENSITIVE
,
1320 NtStatus
= NtOpenFile(&FileHandle
,
1325 FILE_SYNCHRONOUS_IO_NONALERT
);
1326 if (!NT_SUCCESS(NtStatus
))
1328 DPRINT("Cannot open file (%S) (%x).\n", CabinetName
, NtStatus
);
1329 return CAB_STATUS_CANNOT_OPEN
;
1334 /* Load CAB header */
1335 if ((Status
= ReadBlock(&CABHeader
, sizeof(CFHEADER
), &BytesRead
)) != CAB_STATUS_SUCCESS
)
1337 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1338 return CAB_STATUS_INVALID_CAB
;
1342 if ((BytesRead
!= sizeof(CFHEADER
)) ||
1343 (CABHeader
.Signature
!= CAB_SIGNATURE
) ||
1344 (CABHeader
.Version
!= CAB_VERSION
) ||
1345 (CABHeader
.FolderCount
== 0 ) ||
1346 (CABHeader
.FileCount
== 0 ) ||
1347 (CABHeader
.FileTableOffset
< sizeof(CFHEADER
))) {
1349 DPRINT("File has invalid header.\n");
1350 return CAB_STATUS_INVALID_CAB
;
1355 /* Read/skip any reserved bytes */
1356 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
)
1358 if ((Status
= ReadBlock(&Size
, sizeof(ULONG
), &BytesRead
)) != CAB_STATUS_SUCCESS
)
1360 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1361 return CAB_STATUS_INVALID_CAB
;
1363 CabinetReserved
= Size
& 0xFFFF;
1364 FolderReserved
= (Size
>> 16) & 0xFF;
1365 DataReserved
= (Size
>> 24) & 0xFF;
1367 if (CabinetReserved
> 0)
1369 CabinetReservedArea
= RtlAllocateHeap(ProcessHeap
, 0, CabinetReserved
);
1370 if (!CabinetReservedArea
)
1372 return CAB_STATUS_NOMEMORY
;
1375 if ((Status
= ReadBlock(CabinetReservedArea
, CabinetReserved
, &BytesRead
)) != CAB_STATUS_SUCCESS
)
1377 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1378 return CAB_STATUS_INVALID_CAB
;
1382 SeekInFile(FileHandle
, CabinetReserved
, NULL
, SEEK_CURRENT
, &NtStatus
);
1383 if (!NT_SUCCESS(NtStatus
))
1385 DPRINT("SeekInFile() failed (%x).\n", NtStatus
);
1386 return CAB_STATUS_INVALID_CAB
;
1391 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0)
1393 /* Read name of previous cabinet */
1394 Status
= ReadString(CabinetFileName
, 256);
1395 if (Status
!= CAB_STATUS_SUCCESS
)
1398 /* The previous cabinet file is in the same directory as the current */
1399 wcscpy(CabinetPrev
, CabinetName
);
1400 RemoveFileName(CabinetPrev
);
1401 CabinetNormalizePath(CabinetPrev
, 256);
1402 wcscat(CabinetPrev
, CabinetFileName
);
1404 /* Read label of previous disk */
1405 Status
= ReadString(DiskPrev
, 256);
1406 if (Status
!= CAB_STATUS_SUCCESS
)
1411 wcscpy(CabinetPrev
, L
"");
1412 wcscpy(DiskPrev
, L
"");
1415 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0)
1417 /* Read name of next cabinet */
1418 Status
= ReadString(CabinetFileName
, 256);
1419 if (Status
!= CAB_STATUS_SUCCESS
)
1422 /* The next cabinet file is in the same directory as the previous */
1423 wcscpy(CabinetNext
, CabinetName
);
1424 RemoveFileName(CabinetNext
);
1425 CabinetNormalizePath(CabinetNext
, 256);
1426 wcscat(CabinetNext
, CabinetFileName
);
1428 /* Read label of next disk */
1429 Status
= ReadString(DiskNext
, 256);
1430 if (Status
!= CAB_STATUS_SUCCESS
)
1435 wcscpy(CabinetNext
, L
"");
1436 wcscpy(DiskNext
, L
"");
1439 /* Read all folders */
1440 for (Index
= 0; Index
< CABHeader
.FolderCount
; Index
++)
1442 FolderNode
= NewFolderNode();
1445 DPRINT("Insufficient resources.\n");
1446 return CAB_STATUS_NOMEMORY
;
1450 FolderNode
->UncompOffset
= FolderUncompSize
;
1452 FolderNode
->Index
= Index
;
1454 if ((Status
= ReadBlock(&FolderNode
->Folder
,
1455 sizeof(CFFOLDER
), &BytesRead
)) != CAB_STATUS_SUCCESS
)
1457 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1458 return CAB_STATUS_INVALID_CAB
;
1462 /* Read file entries */
1463 Status
= ReadFileTable();
1464 if (Status
!= CAB_STATUS_SUCCESS
)
1466 DPRINT("ReadFileTable() failed (%d).\n", (UINT
)Status
);
1470 /* Read data blocks for all folders */
1471 FolderNode
= FolderListHead
;
1472 while (FolderNode
!= NULL
)
1474 Status
= ReadDataBlocks(FolderNode
);
1475 if (Status
!= CAB_STATUS_SUCCESS
)
1477 DPRINT("ReadDataBlocks() failed (%d).\n", (UINT
)Status
);
1480 FolderNode
= FolderNode
->Next
;
1483 return CAB_STATUS_SUCCESS
;
1490 * FUNCTION: Closes the cabinet file
1497 if (CabinetReservedArea
!= NULL
)
1499 RtlFreeHeap(ProcessHeap
, 0, CabinetReservedArea
);
1500 CabinetReservedArea
= NULL
;
1509 CabinetFindFirst(PWCHAR FileName
,
1512 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
1514 * FileName = Pointer to search criteria
1515 * Search = Pointer to search structure
1517 * Status of operation
1520 RestartSearch
= FALSE
;
1521 wcsncpy(Search
->Search
, FileName
, MAX_PATH
);
1522 Search
->Next
= FileListHead
;
1523 return CabinetFindNext(Search
);
1528 CabinetFindNext(PCAB_SEARCH Search
)
1530 * FUNCTION: Finds next file in the cabinet that matches a search criteria
1532 * Search = Pointer to search structure
1534 * Status of operation
1541 Search
->Next
= FileListHead
;
1543 /* Skip split files already extracted */
1544 while ((Search
->Next
) &&
1545 (Search
->Next
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
) &&
1546 (Search
->Next
->File
.FileOffset
<= LastFileOffset
))
1548 DPRINT("Skipping file (%s) FileOffset (0x%X) LastFileOffset (0x%X).\n",
1549 Search
->Next
->FileName
, Search
->Next
->File
.FileOffset
, LastFileOffset
);
1550 Search
->Next
= Search
->Next
->Next
;
1553 RestartSearch
= FALSE
;
1556 /* FIXME: Check search criteria */
1560 if (wcslen(DiskNext
) > 0)
1564 CabinetSetCabinetName(CabinetNext
);
1566 if (DiskChangeHandler
!= NULL
)
1568 DiskChangeHandler(CabinetNext
, DiskNext
);
1571 Status
= CabinetOpen();
1572 if (Status
!= CAB_STATUS_SUCCESS
)
1575 Search
->Next
= FileListHead
;
1577 return CAB_STATUS_NOFILE
;
1581 return CAB_STATUS_NOFILE
;
1585 Search
->File
= &Search
->Next
->File
;
1586 Search
->FileName
= Search
->Next
->FileName
;
1587 Search
->Next
= Search
->Next
->Next
;
1588 return CAB_STATUS_SUCCESS
;
1593 CabinetExtractFile(PWCHAR FileName
)
1595 * FUNCTION: Extracts a file from the cabinet
1597 * FileName = Pointer to buffer with name of file
1599 * Status of operation
1609 ULONG TotalBytesRead
;
1610 ULONG CurrentOffset
;
1612 PUCHAR CurrentBuffer
;
1619 WCHAR DestName
[MAX_PATH
];
1620 WCHAR TempName
[MAX_PATH
];
1623 UNICODE_STRING UnicodeString
;
1624 IO_STATUS_BLOCK IoStatusBlock
;
1625 OBJECT_ATTRIBUTES ObjectAttributes
;
1626 FILE_BASIC_INFORMATION FileBasic
;
1628 Status
= LocateFile(FileName
, &File
);
1629 if (Status
!= CAB_STATUS_SUCCESS
)
1631 DPRINT("Cannot locate file (%d).\n", (UINT
)Status
);
1635 LastFileOffset
= File
->File
.FileOffset
;
1637 switch (CurrentFolderNode
->Folder
.CompressionType
& CAB_COMP_MASK
)
1640 CabinetSelectCodec(CAB_CODEC_RAW
);
1642 case CAB_COMP_MSZIP
:
1643 CabinetSelectCodec(CAB_CODEC_MSZIP
);
1646 return CAB_STATUS_UNSUPPCOMP
;
1649 DPRINT("Extracting file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
1650 (UINT
)File
->File
.FileOffset
,
1651 (UINT
)File
->File
.FileSize
,
1652 (UINT
)File
->DataBlock
->AbsoluteOffset
,
1653 (UINT
)File
->DataBlock
->UncompOffset
);
1655 wcscpy(DestName
, DestPath
);
1656 wcscat(DestName
, FileName
);
1658 while (NULL
!= (s
= wcsstr(DestName
, L
"\\.\\")))
1660 memmove(s
, s
+ 2, (wcslen(s
+ 2) + 1) *sizeof(WCHAR
));
1663 /* Create destination file, fail if it already exists */
1664 RtlInitUnicodeString(&UnicodeString
,
1668 InitializeObjectAttributes(&ObjectAttributes
,
1670 OBJ_CASE_INSENSITIVE
,
1674 NtStatus
= NtCreateFile(&DestFile
,
1679 FILE_ATTRIBUTE_NORMAL
,
1682 FILE_SYNCHRONOUS_IO_NONALERT
,
1685 if (!NT_SUCCESS(NtStatus
))
1687 DPRINT("NtCreateFile() failed (%S) (%x).\n", DestName
, NtStatus
);
1689 /* If file exists, ask to overwrite file */
1690 if (OverwriteHandler
== NULL
|| OverwriteHandler(&File
->File
, FileName
))
1692 /* Create destination file, overwrite if it already exists */
1693 NtStatus
= NtCreateFile(&DestFile
,
1698 FILE_ATTRIBUTE_NORMAL
,
1701 FILE_SYNCHRONOUS_IO_ALERT
,
1704 if (!NT_SUCCESS(NtStatus
))
1706 DPRINT("NtCreateFile() failed 2 (%S) (%x).\n", DestName
, NtStatus
);
1707 return CAB_STATUS_CANNOT_CREATE
;
1712 DPRINT("File (%S) exists.\n", DestName
);
1713 return CAB_STATUS_FILE_EXISTS
;
1717 if (!ConvertDosDateTimeToFileTime(File
->File
.FileDate
, File
->File
.FileTime
, &FileTime
))
1720 DPRINT("DosDateTimeToFileTime() failed.\n");
1721 return CAB_STATUS_CANNOT_WRITE
;
1724 NtStatus
= NtQueryInformationFile(DestFile
,
1727 sizeof(FILE_BASIC_INFORMATION
),
1728 FileBasicInformation
);
1729 if (!NT_SUCCESS(Status
))
1731 DPRINT("NtQueryInformationFile() failed (%x).\n", NtStatus
);
1735 memcpy(&FileBasic
.LastAccessTime
, &FileTime
, sizeof(FILETIME
));
1737 NtStatus
= NtSetInformationFile(DestFile
,
1740 sizeof(FILE_BASIC_INFORMATION
),
1741 FileBasicInformation
);
1742 if (!NT_SUCCESS(NtStatus
))
1744 DPRINT("NtSetInformationFile() failed (%x).\n", NtStatus
);
1748 SetAttributesOnFile(File
, DestFile
);
1750 Buffer
= RtlAllocateHeap(ProcessHeap
, 0, CAB_BLOCKSIZE
+ 12); // This should be enough
1754 DPRINT("Insufficient memory.\n");
1755 return CAB_STATUS_NOMEMORY
;
1758 /* Call extract event handler */
1759 if (ExtractHandler
!= NULL
)
1761 ExtractHandler(&File
->File
, FileName
);
1764 /* Search to start of file */
1765 Offset
= SeekInFile(FileHandle
,
1766 File
->DataBlock
->AbsoluteOffset
,
1770 if (!NT_SUCCESS(NtStatus
))
1772 DPRINT("SeekInFile() failed (%x).\n", NtStatus
);
1773 return CAB_STATUS_INVALID_CAB
;
1776 Size
= File
->File
.FileSize
;
1777 Offset
= File
->File
.FileOffset
;
1778 CurrentOffset
= File
->DataBlock
->UncompOffset
;
1782 ReuseBlock
= (CurrentDataNode
== File
->DataBlock
);
1787 DPRINT("CO (0x%X) ReuseBlock (%d) Offset (0x%X) Size (%d) BytesLeftInBlock (%d)\n",
1788 File
->DataBlock
->UncompOffset
, (UINT
)ReuseBlock
, Offset
, Size
,
1791 if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock
) || (BytesLeftInBlock
<= 0))
1793 DPRINT("Filling buffer. ReuseBlock (%d)\n", (UINT
)ReuseBlock
);
1795 CurrentBuffer
= Buffer
;
1799 DPRINT("Size (%d bytes).\n", Size
);
1801 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1802 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
)))
1805 RtlFreeHeap(ProcessHeap
, 0, Buffer
);
1806 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1807 return CAB_STATUS_INVALID_CAB
;
1810 DPRINT("Data block: Checksum (0x%X) CompSize (%d bytes) UncompSize (%d bytes) Offset (0x%X).\n",
1811 (UINT
)CFData
.Checksum
,
1812 (UINT
)CFData
.CompSize
,
1813 (UINT
)CFData
.UncompSize
,
1814 (UINT
)SeekInFile(FileHandle
, 0, NULL
, SEEK_CURRENT
, &NtStatus
));
1816 //ASSERT(CFData.CompSize <= CAB_BLOCKSIZE + 12);
1818 BytesToRead
= CFData
.CompSize
;
1820 DPRINT("Read: (0x%X,0x%X).\n",
1821 CurrentBuffer
, Buffer
);
1823 if (((Status
= ReadBlock(CurrentBuffer
, BytesToRead
, &BytesRead
)) !=
1824 CAB_STATUS_SUCCESS
) || (BytesToRead
!= BytesRead
))
1827 RtlFreeHeap(ProcessHeap
, 0, Buffer
);
1828 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1829 return CAB_STATUS_INVALID_CAB
;
1832 /* FIXME: Does not work with files generated by makecab.exe */
1834 if (CFData.Checksum != 0)
1836 ULONG Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0);
1837 if (Checksum != CFData.Checksum)
1840 RtlFreeHeap(ProcessHeap, 0, Buffer);
1841 DPRINT("Bad checksum (is 0x%X, should be 0x%X).\n",
1842 Checksum, CFData.Checksum);
1843 return CAB_STATUS_INVALID_CAB;
1847 TotalBytesRead
+= BytesRead
;
1849 CurrentBuffer
+= BytesRead
;
1851 if (CFData
.UncompSize
== 0)
1853 if (wcslen(DiskNext
) == 0)
1854 return CAB_STATUS_NOFILE
;
1856 /* CloseCabinet() will destroy all file entries so in case
1857 FileName refers to the FileName field of a CFFOLDER_NODE
1858 structure, we have to save a copy of the filename */
1859 wcscpy(TempName
, FileName
);
1863 CabinetSetCabinetName(CabinetNext
);
1865 if (DiskChangeHandler
!= NULL
)
1867 DiskChangeHandler(CabinetNext
, DiskNext
);
1870 Status
= CabinetOpen();
1871 if (Status
!= CAB_STATUS_SUCCESS
)
1874 /* The first data block of the file will not be
1875 found as it is located in the previous file */
1876 Status
= LocateFile(TempName
, &File
);
1877 if (Status
== CAB_STATUS_NOFILE
)
1879 DPRINT("Cannot locate file (%d).\n", (UINT
)Status
);
1883 /* The file is continued in the first data block in the folder */
1884 File
->DataBlock
= CurrentFolderNode
->DataListHead
;
1886 /* Search to start of file */
1887 SeekInFile(FileHandle
,
1888 File
->DataBlock
->AbsoluteOffset
,
1892 if (!NT_SUCCESS(NtStatus
))
1894 DPRINT("SeekInFile() failed (%x).\n", NtStatus
);
1895 return CAB_STATUS_INVALID_CAB
;
1898 DPRINT("Continuing extraction of file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
1899 (UINT
)File
->File
.FileOffset
,
1900 (UINT
)File
->File
.FileSize
,
1901 (UINT
)File
->DataBlock
->AbsoluteOffset
,
1902 (UINT
)File
->DataBlock
->UncompOffset
);
1904 CurrentDataNode
= File
->DataBlock
;
1907 RestartSearch
= TRUE
;
1909 } while (CFData
.UncompSize
== 0);
1911 DPRINT("TotalBytesRead (%d).\n", TotalBytesRead
);
1913 Status
= CodecUncompress(OutputBuffer
, Buffer
, TotalBytesRead
, &BytesToWrite
);
1914 if (Status
!= CS_SUCCESS
)
1917 RtlFreeHeap(ProcessHeap
, 0, Buffer
);
1918 DPRINT("Cannot uncompress block.\n");
1919 if (Status
== CS_NOMEMORY
)
1920 return CAB_STATUS_NOMEMORY
;
1921 return CAB_STATUS_INVALID_CAB
;
1924 if (BytesToWrite
!= CFData
.UncompSize
)
1926 DPRINT("BytesToWrite (%d) != CFData.UncompSize (%d)\n",
1927 BytesToWrite
, CFData
.UncompSize
);
1928 return CAB_STATUS_INVALID_CAB
;
1931 BytesLeftInBlock
= BytesToWrite
;
1935 DPRINT("Using same buffer. ReuseBlock (%d)\n", (UINT
)ReuseBlock
);
1937 BytesToWrite
= BytesLeftInBlock
;
1939 DPRINT("Seeking to absolute offset 0x%X.\n",
1940 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1941 CurrentDataNode
->Data
.CompSize
);
1943 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1944 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
)))
1947 RtlFreeHeap(ProcessHeap
, 0, Buffer
);
1948 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1949 return CAB_STATUS_INVALID_CAB
;
1952 DPRINT("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n",
1953 CFData
.CompSize
, CFData
.UncompSize
);
1955 /* Go to next data block */
1956 SeekInFile(FileHandle
,
1957 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1958 CurrentDataNode
->Data
.CompSize
,
1962 if (!NT_SUCCESS(NtStatus
))
1964 DPRINT("SeekInFile() failed (%x).\n", NtStatus
);
1965 return CAB_STATUS_INVALID_CAB
;
1972 BytesSkipped
= (Offset
- CurrentOffset
);
1976 BytesToWrite
-= BytesSkipped
;
1978 if (Size
< BytesToWrite
)
1979 BytesToWrite
= Size
;
1981 DPRINT("Offset (0x%X) CurrentOffset (0x%X) ToWrite (%d) Skipped (%d)(%d) Size (%d).\n",
1983 (UINT
)CurrentOffset
,
1985 (UINT
)BytesSkipped
, (UINT
)Skip
,
1988 // if (!WriteFile(DestFile, (PVOID)((ULONG)OutputBuffer + BytesSkipped),
1989 // BytesToWrite, &BytesWritten, NULL) ||
1990 // (BytesToWrite != BytesWritten))
1992 NtStatus
= NtWriteFile(DestFile
,
1997 (PVOID
)((ULONG
)OutputBuffer
+ BytesSkipped
),
2001 BytesWritten
= BytesToWrite
;
2002 if (!NT_SUCCESS(NtStatus
))
2004 DPRINT("Status 0x%X.\n", NtStatus
);
2007 RtlFreeHeap(ProcessHeap
, 0, Buffer
);
2008 DPRINT("Cannot write to file.\n");
2009 return CAB_STATUS_CANNOT_WRITE
;
2011 Size
-= BytesToWrite
;
2013 CurrentOffset
+= BytesToWrite
;
2015 /* Don't skip any more bytes */
2022 RtlFreeHeap(ProcessHeap
, 0, Buffer
);
2024 return CAB_STATUS_SUCCESS
;
2029 CabinetSelectCodec(ULONG Id
)
2031 * FUNCTION: Selects codec engine to use
2033 * Id = Codec identifier
2041 CodecSelected
= FALSE
;
2047 CodecUncompress
= RawCodecUncompress
;
2049 case CAB_CODEC_MSZIP
:
2050 CodecUncompress
= MSZipCodecUncompress
;
2057 CodecSelected
= TRUE
;
2062 CabinetSetEventHandlers(PCABINET_OVERWRITE Overwrite
,
2063 PCABINET_EXTRACT Extract
,
2064 PCABINET_DISK_CHANGE DiskChange
)
2066 * FUNCTION: Set event handlers
2068 * Overwrite = Handler called when a file is to be overwritten
2069 * Extract = Handler called when a file is to be extracted
2070 * DiskChange = Handler called when changing the disk
2073 OverwriteHandler
= Overwrite
;
2074 ExtractHandler
= Extract
;
2075 DiskChangeHandler
= DiskChange
;
2080 CabinetGetCabinetReservedArea(PULONG Size
)
2082 * FUNCTION: Get pointer to cabinet reserved area. NULL if none
2085 if (CabinetReservedArea
!= NULL
)
2089 *Size
= CabinetReserved
;
2091 return CabinetReservedArea
;