4794d5e78f1f96c282a1edc7dfba09a636590b88
[reactos.git] / reactos / tools / cabman / cabinet.h
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS cabinet manager
4 * FILE: tools/cabman/cabinet.h
5 * PURPOSE: Cabinet definitions
6 */
7
8 #pragma once
9
10 #if defined(_WIN32)
11 #define WIN32_LEAN_AND_MEAN
12 #include <windows.h>
13 #else
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <sys/types.h>
17 #include <time.h>
18 #include <typedefs.h>
19 #include <unistd.h>
20 #endif
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <limits.h>
26
27 #ifndef PATH_MAX
28 #define PATH_MAX MAX_PATH
29 #endif
30
31 #if defined(_WIN32)
32 #define DIR_SEPARATOR_CHAR '\\'
33 #define DIR_SEPARATOR_STRING "\\"
34
35 #define strcasecmp _stricmp
36 #define strdup _strdup
37
38 #define AllocateMemory(size) HeapAlloc(GetProcessHeap(), 0, size)
39 #define FreeMemory(buffer) HeapFree(GetProcessHeap(), 0, buffer)
40 #define FILEHANDLE HANDLE
41 #define CloseFile(handle) CloseHandle(handle)
42 #else
43 #define DIR_SEPARATOR_CHAR '/'
44 #define DIR_SEPARATOR_STRING "/"
45
46 #define AllocateMemory(size) malloc(size)
47 #define FreeMemory(buffer) free(buffer)
48 #define CloseFile(handle) fclose(handle)
49 #define FILEHANDLE FILE*
50 #endif
51
52 /* Debugging */
53
54 #define NORMAL_MASK 0x000000FF
55 #define SPECIAL_MASK 0xFFFFFF00
56 #define MIN_TRACE 0x00000001
57 #define MID_TRACE 0x00000002
58 #define MAX_TRACE 0x00000003
59
60 #define DEBUG_MEMORY 0x00000100
61
62 #if DBG
63
64 extern ULONG DebugTraceLevel;
65
66 #undef DPRINT
67 #define DPRINT(_t_, _x_) \
68 if (((DebugTraceLevel & NORMAL_MASK) >= _t_) || \
69 ((DebugTraceLevel & _t_) > NORMAL_MASK)) { \
70 printf("(%s:%d)(%s) ", __FILE__, __LINE__, __FUNCTION__); \
71 printf _x_ ; \
72 }
73
74 #undef ASSERT
75 #define ASSERT(_b_) { \
76 if (!(_b_)) { \
77 printf("(%s:%d)(%s) ASSERTION: ", __FILE__, __LINE__, __FUNCTION__); \
78 printf(#_b_); \
79 exit(0); \
80 } \
81 }
82
83 #else /* DBG */
84
85 #undef DPRINT
86 #define DPRINT(_t_, _x_) do { } while(0)
87
88 #undef ASSERT
89 #define ASSERT(_x_)
90
91 #endif /* DBG */
92
93
94 /* Cabinet constants */
95
96 #define CAB_SIGNATURE 0x4643534D // "MSCF"
97 #define CAB_VERSION 0x0103
98 #define CAB_BLOCKSIZE 32768
99
100 #define CAB_COMP_MASK 0x00FF
101 #define CAB_COMP_NONE 0x0000
102 #define CAB_COMP_MSZIP 0x0001
103 #define CAB_COMP_QUANTUM 0x0002
104 #define CAB_COMP_LZX 0x0003
105
106 #define CAB_FLAG_HASPREV 0x0001
107 #define CAB_FLAG_HASNEXT 0x0002
108 #define CAB_FLAG_RESERVE 0x0004
109
110 #define CAB_ATTRIB_READONLY 0x0001
111 #define CAB_ATTRIB_HIDDEN 0x0002
112 #define CAB_ATTRIB_SYSTEM 0x0004
113 #define CAB_ATTRIB_VOLUME 0x0008
114 #define CAB_ATTRIB_DIRECTORY 0x0010
115 #define CAB_ATTRIB_ARCHIVE 0x0020
116 #define CAB_ATTRIB_EXECUTE 0x0040
117 #define CAB_ATTRIB_UTF_NAME 0x0080
118
119 #define CAB_FILE_MAX_FOLDER 0xFFFC
120 #define CAB_FILE_CONTINUED 0xFFFD
121 #define CAB_FILE_SPLIT 0xFFFE
122 #define CAB_FILE_PREV_NEXT 0xFFFF
123
124
125 /* Cabinet structures */
126
127 typedef struct _CFHEADER
128 {
129 ULONG Signature; // File signature 'MSCF' (CAB_SIGNATURE)
130 ULONG Reserved1; // Reserved field
131 ULONG CabinetSize; // Cabinet file size
132 ULONG Reserved2; // Reserved field
133 ULONG FileTableOffset; // Offset of first CFFILE
134 ULONG Reserved3; // Reserved field
135 USHORT Version; // Cabinet version (CAB_VERSION)
136 USHORT FolderCount; // Number of folders
137 USHORT FileCount; // Number of files
138 USHORT Flags; // Cabinet flags (CAB_FLAG_*)
139 USHORT SetID; // Cabinet set id
140 USHORT CabinetNumber; // Zero-based cabinet number
141 /* Optional fields (depends on Flags)
142 USHORT CabinetResSize // Per-cabinet reserved area size
143 char FolderResSize // Per-folder reserved area size
144 char FileResSize // Per-file reserved area size
145 char CabinetReserved[] // Per-cabinet reserved area
146 char CabinetPrev[] // Name of previous cabinet file
147 char DiskPrev[] // Name of previous disk
148 char CabinetNext[] // Name of next cabinet file
149 char DiskNext[] // Name of next disk
150 */
151 } CFHEADER, *PCFHEADER;
152
153
154 typedef struct _CFFOLDER
155 {
156 ULONG DataOffset; // Absolute offset of first CFDATA block in this folder
157 USHORT DataBlockCount; // Number of CFDATA blocks in this folder in this cabinet
158 USHORT CompressionType; // Type of compression used for all CFDATA blocks in this folder
159 /* Optional fields (depends on Flags)
160 char FolderReserved[] // Per-folder reserved area
161 */
162 } CFFOLDER, *PCFFOLDER;
163
164
165 typedef struct _CFFILE
166 {
167 ULONG FileSize; // Uncompressed file size in bytes
168 ULONG FileOffset; // Uncompressed offset of file in the folder
169 USHORT FileControlID; // File control ID (CAB_FILE_*)
170 USHORT FileDate; // File date stamp, as used by DOS
171 USHORT FileTime; // File time stamp, as used by DOS
172 USHORT Attributes; // File attributes (CAB_ATTRIB_*)
173 /* After this is the NULL terminated filename */
174 } CFFILE, *PCFFILE;
175
176
177 typedef struct _CFDATA
178 {
179 ULONG Checksum; // Checksum of CFDATA entry
180 USHORT CompSize; // Number of compressed bytes in this block
181 USHORT UncompSize; // Number of uncompressed bytes in this block
182 /* Optional fields (depends on Flags)
183 char DataReserved[] // Per-datablock reserved area
184 */
185 } CFDATA, *PCFDATA;
186
187 typedef struct _CFDATA_NODE
188 {
189 struct _CFDATA_NODE *Next;
190 struct _CFDATA_NODE *Prev;
191 ULONG ScratchFilePosition; // Absolute offset in scratch file
192 ULONG AbsoluteOffset; // Absolute offset in cabinet
193 ULONG UncompOffset; // Uncompressed offset in folder
194 CFDATA Data;
195 } CFDATA_NODE, *PCFDATA_NODE;
196
197 typedef struct _CFFOLDER_NODE
198 {
199 struct _CFFOLDER_NODE *Next;
200 struct _CFFOLDER_NODE *Prev;
201 ULONG UncompOffset; // File size accumulator
202 ULONG AbsoluteOffset;
203 ULONG TotalFolderSize; // Total size of folder in current disk
204 PCFDATA_NODE DataListHead;
205 PCFDATA_NODE DataListTail;
206 ULONG Index;
207 bool Commit; // true if the folder should be committed
208 bool Delete; // true if marked for deletion
209 CFFOLDER Folder;
210 } CFFOLDER_NODE, *PCFFOLDER_NODE;
211
212 typedef struct _CFFILE_NODE
213 {
214 struct _CFFILE_NODE *Next;
215 struct _CFFILE_NODE *Prev;
216 CFFILE File;
217 char* FileName;
218 PCFDATA_NODE DataBlock; // First data block of file. NULL if not known
219 bool Commit; // true if the file data should be committed
220 bool Delete; // true if marked for deletion
221 PCFFOLDER_NODE FolderNode; // Folder this file belong to
222 } CFFILE_NODE, *PCFFILE_NODE;
223
224 typedef struct _SEARCH_CRITERIA
225 {
226 struct _SEARCH_CRITERIA *Next; // Pointer to next search criteria
227 struct _SEARCH_CRITERIA *Prev; // Pointer to previous search criteria
228 char* Search; // The actual search criteria
229 } SEARCH_CRITERIA, *PSEARCH_CRITERIA;
230
231 typedef struct _CAB_SEARCH
232 {
233 PCFFILE_NODE Next; // Pointer to next node
234 PCFFILE File; // Pointer to current CFFILE
235 char* FileName; // Current filename
236 } CAB_SEARCH, *PCAB_SEARCH;
237
238
239 /* Constants */
240
241 /* Status codes */
242 #define CAB_STATUS_SUCCESS 0x00000000
243 #define CAB_STATUS_FAILURE 0x00000001
244 #define CAB_STATUS_NOMEMORY 0x00000002
245 #define CAB_STATUS_CANNOT_OPEN 0x00000003
246 #define CAB_STATUS_CANNOT_CREATE 0x00000004
247 #define CAB_STATUS_CANNOT_READ 0x00000005
248 #define CAB_STATUS_CANNOT_WRITE 0x00000006
249 #define CAB_STATUS_FILE_EXISTS 0x00000007
250 #define CAB_STATUS_INVALID_CAB 0x00000008
251 #define CAB_STATUS_NOFILE 0x00000009
252 #define CAB_STATUS_UNSUPPCOMP 0x0000000A
253
254
255
256 /* Codecs */
257
258 class CCABCodec
259 {
260 public:
261 /* Default constructor */
262 CCABCodec() {};
263 /* Default destructor */
264 virtual ~CCABCodec() {};
265 /* Compresses a data block */
266 virtual ULONG Compress(void* OutputBuffer,
267 void* InputBuffer,
268 ULONG InputLength,
269 PULONG OutputLength) = 0;
270 /* Uncompresses a data block */
271 virtual ULONG Uncompress(void* OutputBuffer,
272 void* InputBuffer,
273 ULONG InputLength,
274 PULONG OutputLength) = 0;
275 };
276
277
278 /* Codec status codes */
279 #define CS_SUCCESS 0x0000 /* All data consumed */
280 #define CS_NOMEMORY 0x0001 /* Not enough free memory */
281 #define CS_BADSTREAM 0x0002 /* Bad data stream */
282
283
284 /* Codec indentifiers */
285 #define CAB_CODEC_RAW 0x00
286 #define CAB_CODEC_LZX 0x01
287 #define CAB_CODEC_MSZIP 0x02
288
289
290
291 /* Classes */
292
293 #ifndef CAB_READ_ONLY
294
295 class CCFDATAStorage
296 {
297 public:
298 /* Default constructor */
299 CCFDATAStorage();
300 /* Default destructor */
301 virtual ~CCFDATAStorage();
302 ULONG Create();
303 ULONG Destroy();
304 ULONG Truncate();
305 ULONG Position();
306 ULONG Seek(LONG Position);
307 ULONG ReadBlock(PCFDATA Data, void* Buffer, PULONG BytesRead);
308 ULONG WriteBlock(PCFDATA Data, void* Buffer, PULONG BytesWritten);
309 private:
310 char FullName[PATH_MAX];
311 bool FileCreated;
312 FILEHANDLE FileHandle;
313 };
314
315 #endif /* CAB_READ_ONLY */
316
317 class CCabinet
318 {
319 public:
320 /* Default constructor */
321 CCabinet();
322 /* Default destructor */
323 virtual ~CCabinet();
324 /* Determines if a character is a separator */
325 bool IsSeparator(char Char);
326 /* Replaces \ or / with the one used be the host environment */
327 char* ConvertPath(char* Path, bool Allocate);
328 /* Returns a pointer to the filename part of a fully qualified filename */
329 char* GetFileName(char* Path);
330 /* Removes a filename from a fully qualified filename */
331 void RemoveFileName(char* Path);
332 /* Normalizes a path */
333 bool NormalizePath(char* Path, ULONG Length);
334 /* Returns name of cabinet file */
335 char* GetCabinetName();
336 /* Sets the name of the cabinet file */
337 void SetCabinetName(char* FileName);
338 /* Sets destination path for extracted files */
339 void SetDestinationPath(char* DestinationPath);
340 /* Sets cabinet reserved file */
341 bool SetCabinetReservedFile(char* FileName);
342 /* Returns cabinet reserved file */
343 char* GetCabinetReservedFile();
344 /* Returns destination path */
345 char* GetDestinationPath();
346 /* Returns zero-based current disk number */
347 ULONG GetCurrentDiskNumber();
348 /* Opens the current cabinet file */
349 ULONG Open();
350 /* Closes the current open cabinet file */
351 void Close();
352 /* Locates the first file in the current cabinet file that matches a search criteria */
353 ULONG FindFirst(PCAB_SEARCH Search);
354 /* Locates the next file in the current cabinet file */
355 ULONG FindNext(PCAB_SEARCH Search);
356 /* Extracts a file from the current cabinet file */
357 ULONG ExtractFile(char* FileName);
358 /* Select codec engine to use */
359 void SelectCodec(LONG Id);
360 /* Returns whether a codec engine is selected */
361 bool IsCodecSelected();
362 /* Adds a search criteria for adding files to a simple cabinet, displaying files in a cabinet or extracting them */
363 ULONG AddSearchCriteria(char* SearchCriteria);
364 /* Destroys the search criteria list */
365 void DestroySearchCriteria();
366 /* Returns whether we have search criteria */
367 bool HasSearchCriteria();
368
369 #ifndef CAB_READ_ONLY
370 /* Creates a simple cabinet based on the search criteria data */
371 bool CreateSimpleCabinet();
372 /* Sets the codec to use for compression (based on a string value) */
373 bool SetCompressionCodec(char* CodecName);
374 /* Creates a new cabinet file */
375 ULONG NewCabinet();
376 /* Forces a new disk to be created */
377 ULONG NewDisk();
378 /* Forces a new folder to be created */
379 ULONG NewFolder();
380 /* Writes a file to scratch storage */
381 ULONG WriteFileToScratchStorage(PCFFILE_NODE FileNode);
382 /* Forces the current disk to be written */
383 ULONG WriteDisk(ULONG MoreDisks);
384 /* Commits the current disk */
385 ULONG CommitDisk(ULONG MoreDisks);
386 /* Closes the current disk */
387 ULONG CloseDisk();
388 /* Closes the current cabinet */
389 ULONG CloseCabinet();
390 /* Adds a file to the current disk */
391 ULONG AddFile(char* FileName);
392 /* Sets the maximum size of the current disk */
393 void SetMaxDiskSize(ULONG Size);
394 #endif /* CAB_READ_ONLY */
395
396 /* Default event handlers */
397
398 /* Handler called when a file is about to be overridden */
399 virtual bool OnOverwrite(PCFFILE Entry, char* FileName);
400 /* Handler called when a file is about to be extracted */
401 virtual void OnExtract(PCFFILE Entry, char* FileName);
402 /* Handler called when a new disk is to be processed */
403 virtual void OnDiskChange(char* CabinetName, char* DiskLabel);
404 #ifndef CAB_READ_ONLY
405 /* Handler called when a file is about to be added */
406 virtual void OnAdd(PCFFILE Entry, char* FileName);
407 /* Handler called when a cabinet need a name */
408 virtual bool OnCabinetName(ULONG Number, char* Name);
409 /* Handler called when a disk needs a label */
410 virtual bool OnDiskLabel(ULONG Number, char* Label);
411 #endif /* CAB_READ_ONLY */
412 private:
413 PCFFOLDER_NODE LocateFolderNode(ULONG Index);
414 ULONG GetAbsoluteOffset(PCFFILE_NODE File);
415 ULONG LocateFile(char* FileName, PCFFILE_NODE *File);
416 ULONG ReadString(char* String, LONG MaxLength);
417 ULONG ReadFileTable();
418 ULONG ReadDataBlocks(PCFFOLDER_NODE FolderNode);
419 PCFFOLDER_NODE NewFolderNode();
420 PCFFILE_NODE NewFileNode();
421 PCFDATA_NODE NewDataNode(PCFFOLDER_NODE FolderNode);
422 void DestroyDataNodes(PCFFOLDER_NODE FolderNode);
423 void DestroyFileNodes();
424 void DestroyDeletedFileNodes();
425 void DestroyFolderNodes();
426 void DestroyDeletedFolderNodes();
427 ULONG ComputeChecksum(void* Buffer, ULONG Size, ULONG Seed);
428 ULONG ReadBlock(void* Buffer, ULONG Size, PULONG BytesRead);
429 bool MatchFileNamePattern(char* FileName, char* Pattern);
430 #ifndef CAB_READ_ONLY
431 ULONG InitCabinetHeader();
432 ULONG WriteCabinetHeader(bool MoreDisks);
433 ULONG WriteFolderEntries();
434 ULONG WriteFileEntries();
435 ULONG CommitDataBlocks(PCFFOLDER_NODE FolderNode);
436 ULONG WriteDataBlock();
437 ULONG GetAttributesOnFile(PCFFILE_NODE File);
438 ULONG SetAttributesOnFile(char* FileName, USHORT FileAttributes);
439 ULONG GetFileTimes(FILEHANDLE FileHandle, PCFFILE_NODE File);
440 #if !defined(_WIN32)
441 void ConvertDateAndTime(time_t* Time, PUSHORT DosDate, PUSHORT DosTime);
442 #endif
443 #endif /* CAB_READ_ONLY */
444 ULONG CurrentDiskNumber; // Zero based disk number
445 char CabinetName[256]; // Filename of current cabinet
446 char CabinetPrev[256]; // Filename of previous cabinet
447 char DiskPrev[256]; // Label of cabinet in file CabinetPrev
448 char CabinetNext[256]; // Filename of next cabinet
449 char DiskNext[256]; // Label of cabinet in file CabinetNext
450 ULONG TotalHeaderSize; // Size of header and optional fields
451 ULONG NextFieldsSize; // Size of next cabinet name and next disk label
452 ULONG TotalFolderSize; // Size of all folder entries
453 ULONG TotalFileSize; // Size of all file entries
454 ULONG FolderUncompSize; // Uncompressed size of folder
455 ULONG BytesLeftInBlock; // Number of bytes left in current block
456 bool ReuseBlock;
457 char DestPath[PATH_MAX];
458 char CabinetReservedFile[PATH_MAX];
459 void* CabinetReservedFileBuffer;
460 ULONG CabinetReservedFileSize;
461 FILEHANDLE FileHandle;
462 bool FileOpen;
463 CFHEADER CABHeader;
464 ULONG CabinetReserved;
465 ULONG FolderReserved;
466 ULONG DataReserved;
467 PCFFOLDER_NODE FolderListHead;
468 PCFFOLDER_NODE FolderListTail;
469 PCFFOLDER_NODE CurrentFolderNode;
470 PCFDATA_NODE CurrentDataNode;
471 PCFFILE_NODE FileListHead;
472 PCFFILE_NODE FileListTail;
473 PSEARCH_CRITERIA CriteriaListHead;
474 PSEARCH_CRITERIA CriteriaListTail;
475 CCABCodec *Codec;
476 LONG CodecId;
477 bool CodecSelected;
478 void* InputBuffer;
479 void* CurrentIBuffer; // Current offset in input buffer
480 ULONG CurrentIBufferSize; // Bytes left in input buffer
481 void* OutputBuffer;
482 ULONG TotalCompSize; // Total size of current CFDATA block
483 void* CurrentOBuffer; // Current offset in output buffer
484 ULONG CurrentOBufferSize; // Bytes left in output buffer
485 ULONG BytesLeftInCabinet;
486 bool RestartSearch;
487 ULONG LastFileOffset; // Uncompressed offset of last extracted file
488 #ifndef CAB_READ_ONLY
489 ULONG LastBlockStart; // Uncompressed offset of last block in folder
490 ULONG MaxDiskSize;
491 ULONG DiskSize;
492 ULONG PrevCabinetNumber; // Previous cabinet number (where split file starts)
493 bool CreateNewDisk;
494 bool CreateNewFolder;
495
496 CCFDATAStorage *ScratchFile;
497 FILEHANDLE SourceFile;
498 bool ContinueFile;
499 ULONG TotalBytesLeft;
500 bool BlockIsSplit; // true if current data block is split
501 ULONG NextFolderNumber; // Zero based folder number
502 #endif /* CAB_READ_ONLY */
503 };
504
505 /* EOF */