diff --git a/reactos/ChangeLog b/reactos/ChangeLog index f4ba33b967a..2d5800c6a3e 100644 --- a/reactos/ChangeLog +++ b/reactos/ChangeLog @@ -1,3 +1,47 @@ +2003-08-24 Casper S. Hornstrup + + * Makefile: Support creating compressed bootable CDs. + * rules.mak (HOST_CXX, HOST_AR, CABMAN): Define. + * bootdata/txtsetup.sif: Add Cabinets section. + Remove files that are put in reactos.cab. + * subsys/system/usetup/filequeue.c (SetupCloseFileQueue): Release + memory allocated for SourceCabinet. + (SetupQueueCopy): Support copying files in cabinets. + (SetupCommitFileQueue): Support copying files in cabinets. + * subsys/system/usetup/filequeue.h (SetupQueueCopy): Add SourceCabinet + parameter. + * subsys/system/usetup/filesup.c (SetupExtractFile): New function. + * subsys/system/usetup/filesup.h (SetupExtractFile): Prototype. + * subsys/system/usetup/infcache.c (InfOpenBufferedFile): New function. + * subsys/system/usetup/infcache.h (InfOpenBufferedFile): Prototype. + * subsys/system/usetup/makefile (TARGET_SDKLIBS): Add zlib.a. + (TARGET_CFLAGS): Add zlib to include path. + (TARGET_OBJECTS): Add cabinet.o. + * subsys/system/usetup/usetup.c (PrepareCopyPageInfFile): New function. + (PrepareCopyPage): Process Cabinets section. + * tools/helper.mk: Support C++ applications. + * rosbin.txt: Remove. + * bootdata/packages: New directory. + * bootdata/packages/reactos.dff: New file. + * lib/zlib/Makefile.host: New file. + * subsys/system/usetup/cabinet.c: Ditto. + * subsys/system/usetup/cabinet.h: Ditto. + * tools/cabman: New directory. + * tools/cabman/.cvsignore: New file. + * tools/cabman/cabinet.cxx: Ditto. + * tools/cabman/cabinet.h: Ditto. + * tools/cabman/cabman.h: Ditto. + * tools/cabman/dff.txt: Ditto. + * tools/cabman/dfp.cxx: Ditto. + * tools/cabman/dfp.h: Ditto. + * tools/cabman/main.cxx: Ditto. + * tools/cabman/makefile: Ditto. + * tools/cabman/makefile.win32: Ditto. + * tools/cabman/mszip.cxx: Ditto. + * tools/cabman/mszip.h: Ditto. + * tools/cabman/raw.cxx: Ditto. + * tools/cabman/raw.h: Ditto. + 2003-08-24 Casper S. Hornstrup * include/structs.h: Apply packed attribute to the typedef, not the diff --git a/reactos/Makefile b/reactos/Makefile index 5865cd64c0e..a5cbd8d84aa 100644 --- a/reactos/Makefile +++ b/reactos/Makefile @@ -158,8 +158,23 @@ bootcd_bootstrap_files: $(COMPONENTS:%=%_bootcd) $(HALS:%=%_bootcd) $(BUS:%=%_bo $(LIB_STATIC:%=%_bootcd) $(LIB_FSLIB:%=%_bootcd) $(DLLS:%=%_bootcd) $(KERNEL_DRIVERS:%=%_bootcd) \ $(SUBSYS:%=%_bootcd) $(SYS_APPS:%=%_bootcd) -bootcd: all bootcd_directory_layout bootcd_bootstrap_files - $(MAKE) install INSTALL_DIR=$(BOOTCD_DIR)/reactos INSTALL_SYMBOLS=no BOOTCD_INSTALL=yes +bootcd_install_before: + $(RLINE) bootdata/autorun.inf $(BOOTCD_DIR)/autorun.inf + $(RLINE) bootdata/readme.txt $(BOOTCD_DIR)/readme.txt + $(RLINE) bootdata/hivecls.inf $(BOOTCD_DIR)/reactos/hivecls.inf + $(RLINE) bootdata/hivedef.inf $(BOOTCD_DIR)/reactos/hivedef.inf + $(RLINE) bootdata/hivesft.inf $(BOOTCD_DIR)/reactos/hivesft.inf + $(RLINE) bootdata/hivesys.inf $(BOOTCD_DIR)/reactos/hivesys.inf + $(RLINE) bootdata/txtsetup.sif $(BOOTCD_DIR)/reactos/txtsetup.sif + $(CP) bootdata/icon.ico $(BOOTCD_DIR)/icon.ico + $(CP) media/nls/c_1252.nls $(BOOTCD_DIR)/reactos/c_1252.nls + $(CP) media/nls/c_437.nls $(BOOTCD_DIR)/reactos/c_437.nls + $(CP) media/nls/l_intl.nls $(BOOTCD_DIR)/reactos/l_intl.nls + +bootcd: all bootcd_directory_layout bootcd_bootstrap_files bootcd_install_before + $(CABMAN) /C bootdata/packages/reactos.dff /L $(BOOTCD_DIR)/reactos /I + $(CABMAN) /C bootdata/packages/reactos.dff /RC $(BOOTCD_DIR)/reactos/reactos.inf /L $(BOOTCD_DIR)/reactos /N + - $(RM) $(BOOTCD_DIR)/reactos/reactos.inf .PHONY: all depends implib clean clean_before install dist bootcd_directory_layout \ bootcd_bootstrap_files bootcd diff --git a/reactos/bootdata/packages/reactos.dff b/reactos/bootdata/packages/reactos.dff new file mode 100755 index 00000000000..3e6209d34f5 --- /dev/null +++ b/reactos/bootdata/packages/reactos.dff @@ -0,0 +1,134 @@ +; Main ReactOS package + +.Set DiskLabelTemplate="ReactOS" ; Label of disk +.Set CabinetNameTemplate="reactos.cab" ; reactos.cab +.Set InfFileName="reactos.inf" ; reactos.inf + + +;.Set Cabinet=on +;.Set Compress=on + +.InfBegin +[Version] +Signature = "$ReactOS$" + +[Directories] +1 = system32 +2 = system32\drivers +3 = media\fonts + +.InfEnd + +; Contents of disk +.InfBegin +[SourceFiles] +.InfEnd +drivers\bus\acpi\acpi.sys 2 +drivers\bus\isapnp\isapnp.sys 2 +drivers\bus\pci\pci.sys 2 +drivers\dd\beep\beep.sys 2 +drivers\dd\null\null.sys 2 +drivers\dd\serial\serial.sys 2 +drivers\dd\vga\display\vgaddi.dll 1 +drivers\dd\vga\miniport\vgamp.sys 2 +drivers\dd\videoprt\videoprt.sys 2 +drivers\fs\fs_rec\fs_rec.sys 2 +drivers\fs\ms\msfs.sys 2 +drivers\fs\mup\mup.sys 2 +drivers\fs\np\npfs.sys 2 +drivers\input\mouclass\mouclass.sys 2 +drivers\input\psaux\psaux.sys 2 +drivers\input\sermouse\sermouse.sys 2 +drivers\net\afd\afd.sys 2 +drivers\net\ndis\ndis.sys 2 +drivers\net\tcpip\tcpip.sys 2 +drivers\net\tdi\tdi.sys 2 +drivers\net\wshtcpip\wshtcpip.dll 1 +drivers\net\dd\ne2000\ne2000.sys 2 +lib\advapi32\advapi32.dll 1 +lib\crtdll\crtdll.dll 1 +lib\fmifs\fmifs.dll 1 +lib\freetype\freetype.dll 1 +lib\gdi32\gdi32.dll 1 +lib\iphlpapi\iphlpapi.dll 1 +lib\kbdus\kbdus.dll 1 +lib\kernel32\kernel32.dll 1 +lib\lzexpand\lz32.dll 1 +lib\msafd\msafd.dll 1 +lib\msvcrt\msvcrt.dll 1 +lib\ntdll\ntdll.dll 1 +lib\secur32\secur32.dll 1 +lib\syssetup\syssetup.dll 1 +lib\twain\twain_32.dll 1 +lib\user32\user32.dll 1 +lib\version\version.dll 1 +lib\winmm\winmm.dll 1 +lib\winspool\winspool.drv 1 +lib\ws2_32\ws2_32.dll 1 +lib\ws2help\ws2help.dll 1 +lib\wshirda\wshirda.dll 1 +lib\wsock32\wsock32.dll 1 +subsys\csrss\csrss.exe 1 +subsys\ntvdm\ntvdm.exe 1 +subsys\smss\smss.exe 1 +subsys\win32k\win32k.sys 1 +subsys\system\autochk\autochk.exe 1 +subsys\system\cmd\cmd.exe 1 +subsys\system\format\format.exe 1 +subsys\system\services\services.exe 1 +subsys\system\setup\setup.exe 1 +subsys\system\winlogon\winlogon.exe 1 +services\eventlog\eventlog.exe 1 +services\rpcss\rpcss.exe 1 +media\fonts\helb____.ttf 3 +media\fonts\timr____.ttf 3 +media\nls\c_037.nls 1 +media\nls\c_500.nls 1 +media\nls\c_737.nls 1 +media\nls\c_775.nls 1 +media\nls\c_850.nls 1 +media\nls\c_852.nls 1 +media\nls\c_855.nls 1 +media\nls\c_856.nls 1 +media\nls\c_857.nls 1 +media\nls\c_860.nls 1 +media\nls\c_861.nls 1 +media\nls\c_862.nls 1 +media\nls\c_863.nls 1 +media\nls\c_864.nls 1 +media\nls\c_865.nls 1 +media\nls\c_866.nls 1 +media\nls\c_869.nls 1 +media\nls\c_874.nls 1 +media\nls\c_875.nls 1 +media\nls\c_878.nls 1 +media\nls\c_932.nls 1 +media\nls\c_936.nls 1 +media\nls\c_949.nls 1 +media\nls\c_950.nls 1 +media\nls\c_1006.nls 1 +media\nls\c_1026.nls 1 +media\nls\c_1250.nls 1 +media\nls\c_1251.nls 1 +media\nls\c_1253.nls 1 +media\nls\c_1254.nls 1 +media\nls\c_1255.nls 1 +media\nls\c_1256.nls 1 +media\nls\c_1257.nls 1 +media\nls\c_1258.nls 1 +media\nls\c_10000.nls 1 +media\nls\c_10006.nls 1 +media\nls\c_10007.nls 1 +media\nls\c_10029.nls 1 +media\nls\c_10079.nls 1 +media\nls\c_10081.nls 1 +media\nls\c_20866.nls 1 +media\nls\c_28591.nls 1 +media\nls\c_28592.nls 1 +media\nls\c_28593.nls 1 +media\nls\c_28594.nls 1 +media\nls\c_28595.nls 1 +media\nls\c_28596.nls 1 +media\nls\c_28597.nls 1 +media\nls\c_28598.nls 1 +media\nls\c_28599.nls 1 diff --git a/reactos/bootdata/txtsetup.sif b/reactos/bootdata/txtsetup.sif index ebfd4f940c3..94b71144052 100644 --- a/reactos/bootdata/txtsetup.sif +++ b/reactos/bootdata/txtsetup.sif @@ -11,112 +11,25 @@ Signature = "$ReactOS$" 7 = bin [SourceFiles] -ntoskrnl.exe = 2 -hal.dll = 2 +atapi.sys = 3 +blue.sys = 3 +c_437.nls = 2 +c_1252.nls = 2 +cdfs.sys = 3 +cdrom.sys = 3 +class2.sys = 3 +disk.sys = 3 +floppy.sys = 3 +hal.dll = 2 +keyboard.sys = 3 +l_intl.nls = 2 +ntfs.sys = 3 +ntoskrnl.exe = 2 +scsiport.sys = 3 +vfatfs.sys = 3 -acpi.sys = 3 -isapnp.sys = 3 - -beep.sys = 3 -blue.sys = 3 -debugout.sys = 3 -floppy.sys = 3 -null.sys = 3 -serial.sys = 3 -vgaddi.dll = 2 -vgamp.sys = 3 -videoprt.sys = 3 - -cdfs.sys = 3 -fs_rec.sys = 3 -msfs.sys = 3 -mup.sys = 3 -npfs.sys = 3 -ntfs.sys = 3 -vfatfs.sys = 3 - -keyboard.sys = 3 -mouclass.sys = 3 -psaux.sys = 3 -sermouse.sys = 3 - -unbzip2.sys = 3 - -afd.sys = 3 -ne2000.sys = 3 -ndis.sys = 3 -npf.sys = 3 -;packet.sys = 3 -tcpip.sys = 3 -tdi.sys = 3 - -atapi.sys = 3 -cdrom.sys = 3 -class2.sys = 3 -disk.sys = 3 -scsiport.sys = 3 - -advapi32.dll = 2 -crtdll.dll = 2 -fmifs.dll = 2 -freetype.dll = 2 -gdi32.dll = 2 -kernel32.dll = 2 -msafd.dll = 2 -msvcrt.dll = 2 -ntdll.dll = 2 -;ole32.dll = 2 -;oleaut32.dll = 2 -packet.dll = 2 -secur32.dll = 2 -;shell32.dll = 2 -syssetup.dll = 2 -user32.dll = 2 -version.dll = 2 -winedbgc.dll = 2 -winspool.drv = 2 -;winmm.dll = 2 -ws2_32.dll = 2 -ws2help.dll = 2 -wshirda.dll = 2 - -autochk.exe = 2 -cmd.exe = 2 -csrss.exe = 2 -eventlog.exe = 2 -;lsass.exe = 2 -ntvdm.exe = 2 -rpcss.exe = 2 -setup.exe = 2 -smss.exe = 2 -services.exe = 2 -winlogon.exe = 2 - -win32k.sys = 2 - -helb____.ttf = 6 -timr____.ttf = 6 -Vera.ttf = 6 -VeraBd.ttf = 6 -VeraBI.ttf = 6 -VeraIt.ttf = 6 -VeraMoBd.ttf = 6 -VeraMoBI.ttf = 6 -VeraMoIt.ttf = 6 -VeraMono.ttf = 6 -VeraSe.ttf = 6 -VeraSeBd.ttf = 6 - -c_10000.nls = 2 -c_1252.nls = 2 -c_437.nls = 2 -l_intl.nls = 2 - -winhello.exe = 2 - -hcalc.exe = 2 -mc.exe = 2 -winemine.exe = 2 +[Cabinets] +Cabinet=reactos.cab [SetupData] DefaultPath = \ReactOS diff --git a/reactos/lib/zlib/Makefile.host b/reactos/lib/zlib/Makefile.host new file mode 100755 index 00000000000..b7dd2190c4f --- /dev/null +++ b/reactos/lib/zlib/Makefile.host @@ -0,0 +1,44 @@ +# $Id: Makefile.host,v 1.1 2003/08/24 10:36:06 chorns Exp $ +PATH_TO_TOP = ../.. + +TARGET = zlib.host.a + +CFLAGS = \ + -MMD -O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion \ + -Wstrict-prototypes -Wmissing-prototypes + +OBJECTS = \ + adler32.o \ + compress.o \ + crc32.o \ + gzio.o \ + uncompr.o \ + deflate.o \ + trees.o \ + zutil.o \ + inflate.o \ + infblock.o \ + inftrees.o \ + infcodes.o \ + infutil.o \ + inffast.o + +all: hostobjs $(TARGET) + +hostobjs: + - $(RMKDIR) hostobjs + +%.o: %.c + $(HOST_CC) $(CFLAGS) -c $< -o hostobjs/$@ + +$(TARGET): $(OBJECTS) + $(HOST_AR) -r $(TARGET) $(addprefix hostobjs/, $(OBJECTS)) + +clean: + - $(RM) hostobjs/* + - $(RM) $(TARGET) + - $(RMDIR) hostobjs + +.phony: clean + +include $(PATH_TO_TOP)/rules.mak diff --git a/reactos/rosbin.txt b/reactos/rosbin.txt deleted file mode 100644 index 910314b891f..00000000000 --- a/reactos/rosbin.txt +++ /dev/null @@ -1,67 +0,0 @@ -; ReactOS Cabinet Manager Directive File - ReactOS Binary Distribution - -;.Set DiskLabel1="ReactOS Setup Disk" ; Label of 1st disk -;.Set DiskLabel2="ReactOS Drivers" ; Label of 2nd disk -;.Set DiskLabel3="ReactOS Subsystems" ; Label of 3rd disk -;.Set DiskLabel4="ReactOS Applications" ; Label of 4th disk -.Set DiskLabelTemplate="ReactOS Disk *" ; Label of all disks -.Set CabinetNameTemplate="ROS*.CAB" ; ROS1.CAB, ROS2.CAB, etc. -.Set MaxDiskSize=500000 ; 500000 bytes disks -;.Set MaxDiskSize=720K ; 720KB disks -;.Set MaxDiskSize=1.44M ; 1.44MB disks -;.Set MaxDiskSize=2.88M ; 2.88MB disks - - -;.Set Cabinet=on -;.Set Compress=on - -; Contents of disk 1 -boot.bat -loaders\dos\loadros.com -ntoskrnl\ntoskrnl.exe system32 -lib\ntdll\ntdll.dll system32 -apps\system\services\services.exe system32 -apps\system\winlogon\winlogon.exe system32 - -; Contents of disk 2 -;.New Disk -;services\dd\beep\beep.sys system32\drivers -services\dd\blue\blue.sys system32\drivers -services\dd\floppy\floppy.sys system32\drivers -services\dd\ide\ide.sys system32\drivers -services\dd\null\null.sys system32\drivers -services\dd\parallel\parallel.sys system32\drivers -services\dd\serial\serial.sys system32\drivers -services\dd\vga\display\vgaddi.dll system32\drivers -services\dd\vga\miniport\vgamp.sys system32\drivers -services\dd\vidport\vidport.sys system32\drivers -services\fs\vfat\vfatfs.sys system32\drivers -services\input\keyboard\keyboard.sys system32\drivers -services\net\afd\afd.sys system32 -services\net\ndis\ndis.sys system32 -services\net\tcpip\tcpip.sys system32 -;services\net\tdi\tdi.sys system32 -services\net\wshtcpip\wshtcpip.dll system32 -services\net\dd\ne2000\ne2000.sys system32\drivers - -; Contents of disk 3 -;.New Disk -lib\advapi32\advapi32.dll system32 -lib\crtdll\crtdll.dll system32 -lib\fmifs\fmifs.dll system32 -lib\gdi32\gdi32.dll system32 -lib\kernel32\kernel32.dll system32 -lib\msafd\msafd.dll system32 -;lib\msvcrt\msvcrt.dll system32 -lib\secur32\secur32.dll system32 -;lib\user32\user32.dll system32 -lib\ws2_32\ws2_32.dll system32 -subsys\csrss\csrss.exe system32 -subsys\smss\smss.exe system32 -subsys\win32k\win32k.sys system32 - -; Contents of disk 4 -;.New Disk -apps\system\cmd\cmd.exe system32 - -; EOF diff --git a/reactos/rules.mak b/reactos/rules.mak index c0af630f583..241fc28cf11 100644 --- a/reactos/rules.mak +++ b/reactos/rules.mak @@ -73,6 +73,8 @@ BOOTCD_DIR=$(TOPDIR)/../bootcd/disk CC = $(PREFIX)gcc CXX = $(PREFIX)g++ HOST_CC = gcc +HOST_CXX = g++ +HOST_AR = ar HOST_NM = nm LD = $(PREFIX)ld NM = $(PREFIX)nm @@ -96,6 +98,7 @@ RSYM = $(TOOLS_PATH)/rsym RTOUCH = $(TOOLS_PATH)/rtouch REGTESTS = $(TOOLS_PATH)/regtests MC = $(TOOLS_PATH)/wmc/wmc +CABMAN = $(TOOLS_PATH)/cabman/cabman XSLTPROC = xsltproc diff --git a/reactos/subsys/system/usetup/cabinet.c b/reactos/subsys/system/usetup/cabinet.c new file mode 100755 index 00000000000..129241e1725 --- /dev/null +++ b/reactos/subsys/system/usetup/cabinet.c @@ -0,0 +1,2094 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS text-mode setup + * FILE: subsys/system/usetup/cabinet.c + * PURPOSE: Cabinet routines + * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) + * REVISIONS: + * CSH 15/08-2003 Created + */ +#include +#include +#include +#include +#include "cabinet.h" +#include "usetup.h" + +#define NDEBUG +#include + +#define SEEK_BEGIN 0 +#define SEEK_CURRENT 1 +#define SEEK_END 2 + +typedef struct __DOSTIME +{ + WORD Second:5; + WORD Minute:6; + WORD Hour:5; +} DOSTIME, *PDOSTIME; + + +typedef struct __DOSDATE +{ + WORD Day:5; + WORD Month:4; + WORD Year:5; +} DOSDATE, *PDOSDATE; + +static WCHAR CabinetName[256]; // Filename of current cabinet +static WCHAR CabinetPrev[256]; // Filename of previous cabinet +static WCHAR DiskPrev[256]; // Label of cabinet in file CabinetPrev +static WCHAR CabinetNext[256]; // Filename of next cabinet +static WCHAR DiskNext[256]; // Label of cabinet in file CabinetNext +static ULONG FolderUncompSize = 0; // Uncompressed size of folder +static ULONG BytesLeftInBlock = 0; // Number of bytes left in current block +static BOOL ReuseBlock = FALSE; +static WCHAR DestPath[MAX_PATH]; +static HANDLE FileHandle; +static BOOL FileOpen = FALSE; +static CFHEADER CABHeader; +static ULONG CabinetReserved = 0; +static ULONG FolderReserved = 0; +static ULONG DataReserved = 0; +static PCFFOLDER_NODE FolderListHead = NULL; +static PCFFOLDER_NODE FolderListTail = NULL; +static PCFFOLDER_NODE CurrentFolderNode = NULL; +static PCFDATA_NODE CurrentDataNode = NULL; +static PCFFILE_NODE FileListHead = NULL; +static PCFFILE_NODE FileListTail = NULL; +static ULONG CodecId; +static PCABINET_CODEC_UNCOMPRESS CodecUncompress = NULL; +static BOOL CodecSelected = FALSE; +static PVOID InputBuffer = NULL; +static PVOID CurrentIBuffer = NULL; // Current offset in input buffer +static ULONG CurrentIBufferSize = 0; // Bytes left in input buffer +static PVOID OutputBuffer = NULL; +static PVOID CurrentOBuffer = NULL; // Current offset in output buffer +static ULONG CurrentOBufferSize = 0; // Bytes left in output buffer +static BOOL RestartSearch = FALSE; +static ULONG LastFileOffset = 0; // Uncompressed offset of last extracted file +static PCABINET_OVERWRITE OverwriteHandler = NULL; +static PCABINET_EXTRACT ExtractHandler = NULL; +static PCABINET_DISK_CHANGE DiskChangeHandler = NULL; +static z_stream ZStream; +static PVOID CabinetReservedArea = NULL; + + +/* Needed by zlib, but we don't want the dependency on msvcrt.dll */ + +void free(void* _ptr) +{ + RtlFreeHeap(ProcessHeap, 0, _ptr); +} + +void* calloc(size_t _nmemb, size_t _size) +{ + return (void*)RtlAllocateHeap (ProcessHeap, HEAP_ZERO_MEMORY, _size); +} + +/* RAW codec */ + +ULONG +RawCodecUncompress(PVOID OutputBuffer, + PVOID InputBuffer, + ULONG InputLength, + PULONG OutputLength) +/* + * FUNCTION: Uncompresses data in a buffer + * ARGUMENTS: + * OutputBuffer = Pointer to buffer to place uncompressed data + * InputBuffer = Pointer to buffer with data to be uncompressed + * InputLength = Length of input buffer + * OutputLength = Address of buffer to place size of uncompressed data + */ +{ + memcpy(OutputBuffer, InputBuffer, InputLength); + *OutputLength = InputLength; + return CS_SUCCESS; +} + + +/* MSZIP codec */ + +ULONG +MSZipCodecUncompress(PVOID OutputBuffer, + PVOID InputBuffer, + ULONG InputLength, + PULONG OutputLength) +/* + * FUNCTION: Uncompresses data in a buffer + * ARGUMENTS: + * OutputBuffer = Pointer to buffer to place uncompressed data + * InputBuffer = Pointer to buffer with data to be uncompressed + * InputLength = Length of input buffer + * OutputLength = Address of buffer to place size of uncompressed data + */ +{ + USHORT Magic; + INT Status; + + DPRINT("InputLength (%d).\n", InputLength); + + Magic = *((PUSHORT)InputBuffer); + + if (Magic != MSZIP_MAGIC) + { + DPRINT("Bad MSZIP block header magic (0x%X)\n", Magic); + return CS_BADSTREAM; + } + + ZStream.next_in = (PUCHAR)((ULONG)InputBuffer + 2); + ZStream.avail_in = InputLength - 2; + ZStream.next_out = (PUCHAR)OutputBuffer; + ZStream.avail_out = CAB_BLOCKSIZE + 12; + + /* WindowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + */ + Status = inflateInit2(&ZStream, -MAX_WBITS); + if (Status != Z_OK) + { + DPRINT("inflateInit2() returned (%d).\n", Status); + return CS_BADSTREAM; + } + + while ((ZStream.total_out < CAB_BLOCKSIZE + 12) && + (ZStream.total_in < InputLength - 2)) + { + Status = inflate(&ZStream, Z_NO_FLUSH); + if (Status == Z_STREAM_END) break; + if (Status != Z_OK) + { + DPRINT("inflate() returned (%d) (%s).\n", Status, ZStream.msg); + if (Status == Z_MEM_ERROR) + return CS_NOMEMORY; + return CS_BADSTREAM; + } + } + + *OutputLength = ZStream.total_out; + + Status = inflateEnd(&ZStream); + if (Status != Z_OK) + { + DPRINT("inflateEnd() returned (%d).\n", Status); + return CS_BADSTREAM; + } + return CS_SUCCESS; +} + + + +/* Memory functions */ + +voidpf MSZipAlloc(voidpf opaque, uInt items, uInt size) +{ + return (voidpf)RtlAllocateHeap (ProcessHeap, 0, items * size); +} + +void MSZipFree (voidpf opaque, voidpf address) +{ + RtlFreeHeap(ProcessHeap, 0, address); +} + + +static DWORD +SeekInFile(HANDLE hFile, + LONG lDistanceToMove, + PLONG lpDistanceToMoveHigh, + DWORD dwMoveMethod, + PNTSTATUS Status) +{ + FILE_POSITION_INFORMATION FilePosition; + FILE_STANDARD_INFORMATION FileStandart; + NTSTATUS errCode; + IO_STATUS_BLOCK IoStatusBlock; + LARGE_INTEGER Distance; + + DPRINT("SeekInFile(hFile %x, lDistanceToMove %d, dwMoveMethod %d)\n", + hFile,lDistanceToMove,dwMoveMethod); + + Distance.u.LowPart = lDistanceToMove; + if (lpDistanceToMoveHigh) + { + Distance.u.HighPart = *lpDistanceToMoveHigh; + } + else if (lDistanceToMove >= 0) + { + Distance.u.HighPart = 0; + } + else + { + Distance.u.HighPart = -1; + } + + if (dwMoveMethod == SEEK_CURRENT) + { + NtQueryInformationFile(hFile, + &IoStatusBlock, + &FilePosition, + sizeof(FILE_POSITION_INFORMATION), + FilePositionInformation); + FilePosition.CurrentByteOffset.QuadPart += Distance.QuadPart; + } + else if (dwMoveMethod == SEEK_END) + { + NtQueryInformationFile(hFile, + &IoStatusBlock, + &FileStandart, + sizeof(FILE_STANDARD_INFORMATION), + FileStandardInformation); + FilePosition.CurrentByteOffset.QuadPart = + FileStandart.EndOfFile.QuadPart + Distance.QuadPart; + } + else if ( dwMoveMethod == SEEK_BEGIN ) + { + FilePosition.CurrentByteOffset.QuadPart = Distance.QuadPart; + } + +// DPRINT1("GOTO FILE OFFSET: %I64d\n", FilePosition.CurrentByteOffset.QuadPart); + + errCode = NtSetInformationFile(hFile, + &IoStatusBlock, + &FilePosition, + sizeof(FILE_POSITION_INFORMATION), + FilePositionInformation); + if (!NT_SUCCESS(errCode)) + { + if (Status != NULL) + { + *Status = errCode; + } + return -1; + } + + if (lpDistanceToMoveHigh != NULL) + { + *lpDistanceToMoveHigh = FilePosition.CurrentByteOffset.u.HighPart; + } + if (Status != NULL) + { + *Status = STATUS_SUCCESS; + } + return FilePosition.CurrentByteOffset.u.LowPart; +} + + +static BOOL +ConvertSystemTimeToFileTime( + CONST SYSTEMTIME * lpSystemTime, + LPFILETIME lpFileTime) +{ + TIME_FIELDS TimeFields; + LARGE_INTEGER liTime; + + TimeFields.Year = lpSystemTime->wYear; + TimeFields.Month = lpSystemTime->wMonth; + TimeFields.Day = lpSystemTime->wDay; + TimeFields.Hour = lpSystemTime->wHour; + TimeFields.Minute = lpSystemTime->wMinute; + TimeFields.Second = lpSystemTime->wSecond; + TimeFields.Milliseconds = lpSystemTime->wMilliseconds; + + if (RtlTimeFieldsToTime(&TimeFields, &liTime)) + { + lpFileTime->dwLowDateTime = liTime.u.LowPart; + lpFileTime->dwHighDateTime = liTime.u.HighPart; + return TRUE; + } + return FALSE; +} + + +static BOOL +ConvertDosDateTimeToFileTime( + WORD wFatDate, + WORD wFatTime, + LPFILETIME lpFileTime) +{ + PDOSTIME pdtime = (PDOSTIME) &wFatTime; + PDOSDATE pddate = (PDOSDATE) &wFatDate; + SYSTEMTIME SystemTime; + + if (lpFileTime == NULL) + return FALSE; + + SystemTime.wMilliseconds = 0; + SystemTime.wSecond = pdtime->Second; + SystemTime.wMinute = pdtime->Minute; + SystemTime.wHour = pdtime->Hour; + + SystemTime.wDay = pddate->Day; + SystemTime.wMonth = pddate->Month; + SystemTime.wYear = 1980 + pddate->Year; + + ConvertSystemTimeToFileTime(&SystemTime,lpFileTime); + + return TRUE; +} + + +static PWCHAR +GetFileName(PWCHAR Path) +/* + * FUNCTION: Returns a pointer to file name + * ARGUMENTS: + * Path = Pointer to string with pathname + * RETURNS: + * Pointer to filename + */ +{ + ULONG i, j; + + j = i = 0; + + while (Path [i++]) + { + if (Path[i - 1] == L'\\') j = i; + } + return Path + j; +} + + +static VOID +RemoveFileName(PWCHAR Path) +/* + * FUNCTION: Removes a file name from a path + * ARGUMENTS: + * Path = Pointer to string with path + */ +{ + PWCHAR FileName; + DWORD i; + + i = 0; + FileName = GetFileName(Path + i); + + if ((FileName != (Path + i)) && (FileName [-1] == L'\\')) + FileName--; + if ((FileName == (Path + i)) && (FileName [0] == L'\\')) + FileName++; + FileName[0] = 0; +} + + +static BOOL +SetAttributesOnFile(PCFFILE_NODE File, HANDLE hFile) +/* + * FUNCTION: Sets attributes on a file + * ARGUMENTS: + * File = Pointer to CFFILE node for file + * RETURNS: + * Status of operation + */ +{ + FILE_BASIC_INFORMATION FileBasic; + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS NtStatus; + ULONG Attributes = 0; + + if (File->File.Attributes & CAB_ATTRIB_READONLY) + Attributes |= FILE_ATTRIBUTE_READONLY; + + if (File->File.Attributes & CAB_ATTRIB_HIDDEN) + Attributes |= FILE_ATTRIBUTE_HIDDEN; + + if (File->File.Attributes & CAB_ATTRIB_SYSTEM) + Attributes |= FILE_ATTRIBUTE_SYSTEM; + + if (File->File.Attributes & CAB_ATTRIB_DIRECTORY) + Attributes |= FILE_ATTRIBUTE_DIRECTORY; + + if (File->File.Attributes & CAB_ATTRIB_ARCHIVE) + Attributes |= FILE_ATTRIBUTE_ARCHIVE; + + NtStatus = NtQueryInformationFile(hFile, + &IoStatusBlock, + &FileBasic, + sizeof(FILE_BASIC_INFORMATION), + FileBasicInformation); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("NtQueryInformationFile() failed (%x).\n", NtStatus); + } + else + { + FileBasic.FileAttributes = Attributes; + + NtStatus = NtSetInformationFile(hFile, + &IoStatusBlock, + &FileBasic, + sizeof(FILE_BASIC_INFORMATION), + FileBasicInformation); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("NtSetInformationFile() failed (%x).\n", NtStatus); + } + } + + return NT_SUCCESS(NtStatus); +} + + +static ULONG +ReadBlock(PVOID Buffer, + ULONG Size, + PULONG BytesRead) +/* + * FUNCTION: Read a block of data from file + * ARGUMENTS: + * Buffer = Pointer to data buffer + * Size = Length of data buffer + * BytesRead = Pointer to ULONG that on return will contain + * number of bytes read + * RETURNS: + * Status of operation + */ +{ + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS NtStatus; + + NtStatus = NtReadFile(FileHandle, + NULL, + NULL, + NULL, + &IoStatusBlock, + Buffer, + Size, + NULL, + NULL); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("ReadBlock for %d bytes failed (%x)\n", Size, NtStatus); + *BytesRead = 0; + return CAB_STATUS_INVALID_CAB; + } + *BytesRead = Size; + return CAB_STATUS_SUCCESS; +} + + +static PCFFOLDER_NODE +NewFolderNode() +/* + * FUNCTION: Creates a new folder node + * RETURNS: + * Pointer to node if there was enough free memory available, otherwise NULL + */ +{ + PCFFOLDER_NODE Node; + + Node = (PCFFOLDER_NODE)RtlAllocateHeap (ProcessHeap, 0, sizeof(CFFOLDER_NODE)); + if (!Node) + return NULL; + + RtlZeroMemory(Node, sizeof(CFFOLDER_NODE)); + + Node->Folder.CompressionType = CAB_COMP_NONE; + + Node->Prev = FolderListTail; + + if (FolderListTail != NULL) + { + FolderListTail->Next = Node; + } + else + { + FolderListHead = Node; + } + FolderListTail = Node; + + return Node; +} + + +static PCFFILE_NODE +NewFileNode() +/* + * FUNCTION: Creates a new file node + * ARGUMENTS: + * FolderNode = Pointer to folder node to bind file to + * RETURNS: + * Pointer to node if there was enough free memory available, otherwise NULL + */ +{ + PCFFILE_NODE Node; + + Node = (PCFFILE_NODE)RtlAllocateHeap (ProcessHeap, 0, sizeof(CFFILE_NODE)); + if (!Node) + return NULL; + + RtlZeroMemory(Node, sizeof(CFFILE_NODE)); + + Node->Prev = FileListTail; + + if (FileListTail != NULL) + { + FileListTail->Next = Node; + } + else + { + FileListHead = Node; + } + FileListTail = Node; + + return Node; +} + + +static PCFDATA_NODE +NewDataNode(PCFFOLDER_NODE FolderNode) +/* + * FUNCTION: Creates a new data block node + * ARGUMENTS: + * FolderNode = Pointer to folder node to bind data block to + * RETURNS: + * Pointer to node if there was enough free memory available, otherwise NULL + */ +{ + PCFDATA_NODE Node; + + Node = (PCFDATA_NODE)RtlAllocateHeap (ProcessHeap, 0, sizeof(CFDATA_NODE)); + if (!Node) + return NULL; + + RtlZeroMemory(Node, sizeof(CFDATA_NODE)); + + Node->Prev = FolderNode->DataListTail; + + if (FolderNode->DataListTail != NULL) + { + FolderNode->DataListTail->Next = Node; + } + else + { + FolderNode->DataListHead = Node; + } + FolderNode->DataListTail = Node; + + return Node; +} + + +static VOID +DestroyDataNodes(PCFFOLDER_NODE FolderNode) +/* + * FUNCTION: Destroys data block nodes bound to a folder node + * ARGUMENTS: + * FolderNode = Pointer to folder node + */ +{ + PCFDATA_NODE PrevNode; + PCFDATA_NODE NextNode; + + NextNode = FolderNode->DataListHead; + while (NextNode != NULL) + { + PrevNode = NextNode->Next; + RtlFreeHeap(ProcessHeap, 0, NextNode); + NextNode = PrevNode; + } + FolderNode->DataListHead = NULL; + FolderNode->DataListTail = NULL; +} + + +static VOID +DestroyFileNodes() +/* + * FUNCTION: Destroys file nodes + * ARGUMENTS: + * FolderNode = Pointer to folder node + */ +{ + PCFFILE_NODE PrevNode; + PCFFILE_NODE NextNode; + + NextNode = FileListHead; + while (NextNode != NULL) + { + PrevNode = NextNode->Next; + if (NextNode->FileName) + RtlFreeHeap(ProcessHeap, 0, NextNode->FileName); + RtlFreeHeap(ProcessHeap, 0, NextNode); + NextNode = PrevNode; + } + FileListHead = NULL; + FileListTail = NULL; +} + + +static VOID +DestroyDeletedFileNodes() +/* + * FUNCTION: Destroys file nodes that are marked for deletion + */ +{ + PCFFILE_NODE CurNode; + PCFFILE_NODE NextNode; + + CurNode = FileListHead; + while (CurNode != NULL) + { + NextNode = CurNode->Next; + + if (CurNode->Delete) + { + if (CurNode->Prev != NULL) + { + CurNode->Prev->Next = CurNode->Next; + } + else + { + FileListHead = CurNode->Next; + if (FileListHead) + FileListHead->Prev = NULL; + } + + if (CurNode->Next != NULL) + { + CurNode->Next->Prev = CurNode->Prev; + } + else + { + FileListTail = CurNode->Prev; + if (FileListTail) + FileListTail->Next = NULL; + } + + DPRINT("Deleting file: '%S'\n", CurNode->FileName); + + if (CurNode->FileName) + RtlFreeHeap(ProcessHeap, 0, CurNode->FileName); + RtlFreeHeap(ProcessHeap, 0, CurNode); + } + CurNode = NextNode; + } +} + + +static VOID +DestroyFolderNodes() +/* + * FUNCTION: Destroys folder nodes + */ +{ + PCFFOLDER_NODE PrevNode; + PCFFOLDER_NODE NextNode; + + NextNode = FolderListHead; + while (NextNode != NULL) + { + PrevNode = NextNode->Next; + DestroyDataNodes(NextNode); + RtlFreeHeap(ProcessHeap, 0, NextNode); + NextNode = PrevNode; + } + FolderListHead = NULL; + FolderListTail = NULL; +} + + +static VOID +DestroyDeletedFolderNodes() +/* + * FUNCTION: Destroys folder nodes that are marked for deletion + */ +{ + PCFFOLDER_NODE CurNode; + PCFFOLDER_NODE NextNode; + + CurNode = FolderListHead; + while (CurNode != NULL) + { + NextNode = CurNode->Next; + + if (CurNode->Delete) + { + if (CurNode->Prev != NULL) + { + CurNode->Prev->Next = CurNode->Next; + } + else + { + FolderListHead = CurNode->Next; + if (FolderListHead) + FolderListHead->Prev = NULL; + } + + if (CurNode->Next != NULL) + { + CurNode->Next->Prev = CurNode->Prev; + } + else + { + FolderListTail = CurNode->Prev; + if (FolderListTail) + FolderListTail->Next = NULL; + } + + DestroyDataNodes(CurNode); + RtlFreeHeap(ProcessHeap, 0, CurNode); + } + CurNode = NextNode; + } +} + + +static PCFFOLDER_NODE +LocateFolderNode(ULONG Index) +/* + * FUNCTION: Locates a folder node + * ARGUMENTS: + * Index = Folder index + * RETURNS: + * Pointer to folder node or NULL if the folder node was not found + */ +{ + PCFFOLDER_NODE Node; + + switch (Index) + { + case CAB_FILE_SPLIT: + return FolderListTail; + + case CAB_FILE_CONTINUED: + case CAB_FILE_PREV_NEXT: + return FolderListHead; + } + + Node = FolderListHead; + while (Node != NULL) + { + if (Node->Index == Index) + return Node; + Node = Node->Next; + } + return NULL; +} + + +static ULONG +GetAbsoluteOffset(PCFFILE_NODE File) +/* + * FUNCTION: Returns the absolute offset of a file + * ARGUMENTS: + * File = Pointer to CFFILE_NODE structure for file + * RETURNS: + * Status of operation + */ +{ + PCFDATA_NODE Node; + + DPRINT("FileName '%S' FileOffset (0x%X) FileSize (%d).\n", + (PWCHAR)File->FileName, + (UINT)File->File.FileOffset, + (UINT)File->File.FileSize); + + Node = CurrentFolderNode->DataListHead; + while (Node != NULL) + { + DPRINT("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%d).\n", + (UINT)Node->UncompOffset, + (UINT)Node->UncompOffset + Node->Data.UncompSize, + (UINT)Node->Data.UncompSize); + + /* Node->Data.UncompSize will be 0 if the block is split + (ie. it is the last block in this cabinet) */ + if ((Node->Data.UncompSize == 0) || + ((File->File.FileOffset >= Node->UncompOffset) && + (File->File.FileOffset < Node->UncompOffset + + Node->Data.UncompSize))) + { + File->DataBlock = Node; + return CAB_STATUS_SUCCESS; + } + + Node = Node->Next; + } + return CAB_STATUS_INVALID_CAB; +} + + +static ULONG +LocateFile(PWCHAR FileName, + PCFFILE_NODE *File) +/* + * FUNCTION: Locates a file in the cabinet + * ARGUMENTS: + * FileName = Pointer to string with name of file to locate + * File = Address of pointer to CFFILE_NODE structure to fill + * RETURNS: + * Status of operation + * NOTES: + * Current folder is set to the folder of the file + */ +{ + PCFFILE_NODE Node; + ULONG Status; + + DPRINT("FileName '%S'\n", FileName); + + Node = FileListHead; + while (Node != NULL) + { + if (_wcsicmp(FileName, Node->FileName) == 0) + { + CurrentFolderNode = LocateFolderNode(Node->File.FileControlID); + if (!CurrentFolderNode) + { + DPRINT("Folder with index number (%d) not found.\n", + (UINT)Node->File.FileControlID); + return CAB_STATUS_INVALID_CAB; + } + + if (Node->DataBlock == NULL) + { + Status = GetAbsoluteOffset(Node); + } + else + Status = CAB_STATUS_SUCCESS; + *File = Node; + return Status; + } + Node = Node->Next; + } + return CAB_STATUS_NOFILE; +} + + +static ULONG +ReadString(PWCHAR String, ULONG MaxLength) +/* + * FUNCTION: Reads a NULL-terminated string from the cabinet + * ARGUMENTS: + * String = Pointer to buffer to place string + * MaxLength = Maximum length of string + * RETURNS: + * Status of operation + */ +{ + NTSTATUS NtStatus; + ULONG BytesRead; + ULONG Offset; + ULONG Status; + ULONG Size; + BOOL Found; + CHAR buf[MAX_PATH]; + ANSI_STRING as; + UNICODE_STRING us; + + Offset = 0; + Found = FALSE; + do + { + Size = ((Offset + 32) <= MaxLength)? 32 : MaxLength - Offset; + + if (Size == 0) + { + DPRINT("Too long a filename.\n"); + return CAB_STATUS_INVALID_CAB; + } + + Status = ReadBlock((PCFDATA)&buf[Offset], Size, &BytesRead); + if ((Status != CAB_STATUS_SUCCESS) || (BytesRead != Size)) + { + DPRINT("Cannot read from file (%d).\n", (UINT)Status); + return CAB_STATUS_INVALID_CAB; + } + + for (Size = Offset; Size < Offset + BytesRead; Size++) + { + if (buf[Size] == '\0') + { + Found = TRUE; + break; + } + } + + Offset += BytesRead; + } while (!Found); + + /* Back up some bytes */ + Size = (BytesRead - Size) - 1; + SeekInFile(FileHandle, -(LONG)Size, NULL, SEEK_CURRENT, &NtStatus); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("SeekInFile() failed (%x).\n", NtStatus); + return CAB_STATUS_INVALID_CAB; + } + + RtlInitAnsiString(&as, buf); + us.Buffer = String; + us.MaximumLength = MaxLength * sizeof(WCHAR); + us.Length = 0; + + RtlAnsiStringToUnicodeString(&us, &as, FALSE); + + return CAB_STATUS_SUCCESS; +} + + +static ULONG +ReadFileTable() +/* + * FUNCTION: Reads the file table from the cabinet file + * RETURNS: + * Status of operation + */ +{ + ULONG i; + ULONG Status; + ULONG BytesRead; + PCFFILE_NODE File; + NTSTATUS NtStatus; + + DPRINT("Reading file table at absolute offset (0x%X).\n", + CABHeader.FileTableOffset); + + /* Seek to file table */ + SeekInFile(FileHandle, CABHeader.FileTableOffset, NULL, SEEK_BEGIN, &NtStatus); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("SeekInFile() failed (%x).\n", NtStatus); + return CAB_STATUS_INVALID_CAB; + } + + for (i = 0; i < CABHeader.FileCount; i++) + { + File = NewFileNode(); + if (!File) + { + DPRINT("Insufficient memory.\n"); + return CAB_STATUS_NOMEMORY; + } + + if ((Status = ReadBlock(&File->File, sizeof(CFFILE), + &BytesRead)) != CAB_STATUS_SUCCESS) { + DPRINT("Cannot read from file (%d).\n", (UINT)Status); + return CAB_STATUS_INVALID_CAB; + } + + File->FileName = (PWCHAR)RtlAllocateHeap(ProcessHeap, 0, MAX_PATH * sizeof(WCHAR)); + if (!File->FileName) + { + DPRINT("Insufficient memory.\n"); + return CAB_STATUS_NOMEMORY; + } + + /* Read file name */ + Status = ReadString(File->FileName, MAX_PATH); + if (Status != CAB_STATUS_SUCCESS) + return Status; + + DPRINT("Found file '%S' at uncompressed offset (0x%X). Size (%d bytes) ControlId (0x%X).\n", + (PWCHAR)File->FileName, + (UINT)File->File.FileOffset, + (UINT)File->File.FileSize, + (UINT)File->File.FileControlID); + } + return CAB_STATUS_SUCCESS; +} + + +static ULONG +ReadDataBlocks(PCFFOLDER_NODE FolderNode) +/* + * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file + * ARGUMENTS: + * FolderNode = Pointer to CFFOLDER_NODE structure for folder + * RETURNS: + * Status of operation + */ +{ + ULONG AbsoluteOffset; + ULONG UncompOffset; + PCFDATA_NODE Node; + NTSTATUS NtStatus; + ULONG BytesRead; + ULONG Status; + ULONG i; + + DPRINT("Reading data blocks for folder (%d) at absolute offset (0x%X).\n", + FolderNode->Index, FolderNode->Folder.DataOffset); + + AbsoluteOffset = FolderNode->Folder.DataOffset; + UncompOffset = FolderNode->UncompOffset; + + for (i = 0; i < FolderNode->Folder.DataBlockCount; i++) + { + Node = NewDataNode(FolderNode); + if (!Node) + { + DPRINT("Insufficient memory.\n"); + return CAB_STATUS_NOMEMORY; + } + + /* Seek to data block */ + SeekInFile(FileHandle, AbsoluteOffset, NULL, SEEK_BEGIN, &NtStatus); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("SeekInFile() failed (%x).\n", NtStatus); + return CAB_STATUS_INVALID_CAB; + } + + if ((Status = ReadBlock(&Node->Data, sizeof(CFDATA), + &BytesRead)) != CAB_STATUS_SUCCESS) + { + DPRINT("Cannot read from file (%d).\n", (UINT)Status); + return CAB_STATUS_INVALID_CAB; + } + + DPRINT("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%d) UncompSize (%d).\n", + (UINT)AbsoluteOffset, + (UINT)UncompOffset, + (UINT)Node->Data.Checksum, + (UINT)Node->Data.CompSize, + (UINT)Node->Data.UncompSize); + + Node->AbsoluteOffset = AbsoluteOffset; + Node->UncompOffset = UncompOffset; + + AbsoluteOffset += sizeof(CFDATA) + Node->Data.CompSize; + UncompOffset += Node->Data.UncompSize; + } + + FolderUncompSize = UncompOffset; + + return CAB_STATUS_SUCCESS; +} + + +static ULONG +ComputeChecksum(PVOID Buffer, + UINT Size, + ULONG Seed) +/* + * FUNCTION: Computes checksum for data block + * ARGUMENTS: + * Buffer = Pointer to data buffer + * Size = Length of data buffer + * Seed = Previously computed checksum + * RETURNS: + * Checksum of buffer + */ +{ + INT UlongCount; // Number of ULONGs in block + ULONG Checksum; // Checksum accumulator + PBYTE pb; + ULONG ul; + + /* FIXME: Doesn't seem to be correct. EXTRACT.EXE + won't accept checksums computed by this routine */ + + DPRINT("Checksumming buffer (0x%X) Size (%d)\n", (UINT)Buffer, Size); + + UlongCount = Size / 4; // Number of ULONGs + Checksum = Seed; // Init checksum + pb = (PBYTE)Buffer; // Start at front of data block + + /* Checksum integral multiple of ULONGs */ + while (UlongCount-- > 0) + { + /* NOTE: Build ULONG in big/little-endian independent manner */ + ul = *pb++; // Get low-order byte + ul |= (((ULONG)(*pb++)) << 8); // Add 2nd byte + ul |= (((ULONG)(*pb++)) << 16); // Add 3nd byte + ul |= (((ULONG)(*pb++)) << 24); // Add 4th byte + + Checksum ^= ul; // Update checksum + } + + /* Checksum remainder bytes */ + ul = 0; + switch (Size % 4) + { + case 3: + ul |= (((ULONG)(*pb++)) << 16); // Add 3rd byte + case 2: + ul |= (((ULONG)(*pb++)) << 8); // Add 2nd byte + case 1: + ul |= *pb++; // Get low-order byte + default: + break; + } + Checksum ^= ul; // Update checksum + + /* Return computed checksum */ + return Checksum; +} + + +static ULONG +CloseCabinet() +/* + * FUNCTION: Closes the current cabinet + * RETURNS: + * Status of operation + */ +{ + PCFFOLDER_NODE PrevNode; + PCFFOLDER_NODE NextNode; + ULONG Status; + + DestroyFileNodes(); + + DestroyFolderNodes(); + + if (InputBuffer) + { + RtlFreeHeap(ProcessHeap, 0, InputBuffer); + InputBuffer = NULL; + } + + if (OutputBuffer) + { + RtlFreeHeap(ProcessHeap, 0, OutputBuffer); + OutputBuffer = NULL; + } + + NtClose(FileHandle); +} + + +VOID +CabinetInitialize() +/* + * FUNCTION: Initialize archiver + */ +{ + ZStream.zalloc = MSZipAlloc; + ZStream.zfree = MSZipFree; + ZStream.opaque = (voidpf)0; + + FileOpen = FALSE; + wcscpy(DestPath, L""); + + FolderListHead = NULL; + FolderListTail = NULL; + FileListHead = NULL; + FileListTail = NULL; + + CodecId = CAB_CODEC_RAW; + CodecSelected = TRUE; + + OutputBuffer = NULL; + CurrentOBuffer = NULL; + CurrentOBufferSize = 0; + InputBuffer = NULL; + CurrentIBuffer = NULL; + CurrentIBufferSize = 0; + + FolderUncompSize = 0; + BytesLeftInBlock = 0; + CabinetReserved = 0; + FolderReserved = 0; + DataReserved = 0; + ReuseBlock = FALSE; + CurrentFolderNode = NULL; + CurrentDataNode = NULL; + CabinetReservedArea = NULL; + RestartSearch = FALSE; + LastFileOffset = 0; +} + + +VOID +CabinetCleanup() +/* + * FUNCTION: Cleanup archiver + */ +{ + CabinetClose(); +} + + +BOOL +CabinetNormalizePath(PWCHAR Path, + ULONG Length) +/* + * FUNCTION: Normalizes a path + * ARGUMENTS: + * Path = Pointer to string with pathname + * Length = Number of characters in Path + * RETURNS: + * TRUE if there was enough room in Path, or FALSE + */ +{ + ULONG n; + BOOL OK = TRUE; + + if ((n = wcslen(Path)) && + (Path[n - 1] != L'\\') && + (OK = ((n + 1) < Length))) + { + Path[n] = L'\\'; + Path[n + 1] = 0; + } + return OK; +} + + +PWCHAR +CabinetGetCabinetName() +/* + * FUNCTION: Returns pointer to cabinet file name + * RETURNS: + * Pointer to string with name of cabinet + */ +{ + return CabinetName; +} + + +VOID +CabinetSetCabinetName(PWCHAR FileName) +/* + * FUNCTION: Sets cabinet file name + * ARGUMENTS: + * FileName = Pointer to string with name of cabinet + */ +{ + wcscpy(CabinetName, FileName); +} + + +VOID +CabinetSetDestinationPath(PWCHAR DestinationPath) +/* + * FUNCTION: Sets destination path + * ARGUMENTS: + * DestinationPath = Pointer to string with name of destination path + */ +{ + wcscpy(DestPath, DestinationPath); + if (wcslen(DestPath) > 0) + CabinetNormalizePath(DestPath, MAX_PATH); +} + + +PWCHAR +CabinetGetDestinationPath() +/* + * FUNCTION: Returns destination path + * RETURNS: + * Pointer to string with name of destination path + */ +{ + return DestPath; +} + + +ULONG +CabinetOpen() +/* + * FUNCTION: Opens a cabinet file + * RETURNS: + * Status of operation + */ +{ + WCHAR CabinetFileName[256]; + PCFFOLDER_NODE FolderNode; + ULONG Status; + ULONG Index; + + if (!FileOpen) + { + OBJECT_ATTRIBUTES ObjectAttributes; + IO_STATUS_BLOCK IoStatusBlock; + UNICODE_STRING FileName; + NTSTATUS NtStatus; + ULONG BytesRead; + ULONG Size; + + OutputBuffer = RtlAllocateHeap(ProcessHeap, 0, CAB_BLOCKSIZE + 12); // This should be enough + if (!OutputBuffer) + return CAB_STATUS_NOMEMORY; + + RtlInitUnicodeString(&FileName, + CabinetName); + + InitializeObjectAttributes(&ObjectAttributes, + &FileName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + NtStatus = NtOpenFile(&FileHandle, + FILE_READ_ACCESS, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ, + FILE_SYNCHRONOUS_IO_ALERT); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("Cannot open file (%S) (%x).\n", CabinetName, NtStatus); + return CAB_STATUS_CANNOT_OPEN; + } + + FileOpen = TRUE; + + /* Load CAB header */ + if ((Status = ReadBlock(&CABHeader, sizeof(CFHEADER), &BytesRead)) != CAB_STATUS_SUCCESS) + { + DPRINT("Cannot read from file (%d).\n", (UINT)Status); + return CAB_STATUS_INVALID_CAB; + } + + /* Check header */ + if ((BytesRead != sizeof(CFHEADER)) || + (CABHeader.Signature != CAB_SIGNATURE ) || + (CABHeader.Version != CAB_VERSION ) || + (CABHeader.FolderCount == 0 ) || + (CABHeader.FileCount == 0 ) || + (CABHeader.FileTableOffset < sizeof(CFHEADER))) { + CloseCabinet(); + DPRINT("File has invalid header.\n"); + return CAB_STATUS_INVALID_CAB; + } + + Size = 0; + + /* Read/skip any reserved bytes */ + if (CABHeader.Flags & CAB_FLAG_RESERVE) + { + if ((Status = ReadBlock(&Size, sizeof(ULONG), &BytesRead)) != CAB_STATUS_SUCCESS) + { + DPRINT("Cannot read from file (%d).\n", (UINT)Status); + return CAB_STATUS_INVALID_CAB; + } + CabinetReserved = Size & 0xFFFF; + FolderReserved = (Size >> 16) & 0xFF; + DataReserved = (Size >> 24) & 0xFF; + + if (CabinetReserved > 0) + { + CabinetReservedArea = RtlAllocateHeap(ProcessHeap, 0, CabinetReserved); + if (!CabinetReservedArea) + { + return CAB_STATUS_NOMEMORY; + } + + if ((Status = ReadBlock(CabinetReservedArea, CabinetReserved, &BytesRead)) != CAB_STATUS_SUCCESS) + { + DPRINT("Cannot read from file (%d).\n", (UINT)Status); + return CAB_STATUS_INVALID_CAB; + } + } +#if 0 + SeekInFile(FileHandle, CabinetReserved, NULL, SEEK_CURRENT, &NtStatus); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("SeekInFile() failed (%x).\n", NtStatus); + return CAB_STATUS_INVALID_CAB; + } +#endif + } + + if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0) + { + /* Read name of previous cabinet */ + Status = ReadString(CabinetFileName, 256); + if (Status != CAB_STATUS_SUCCESS) + return Status; + + /* The previous cabinet file is in the same directory as the current */ + wcscpy(CabinetPrev, CabinetName); + RemoveFileName(CabinetPrev); + CabinetNormalizePath(CabinetPrev, 256); + wcscat(CabinetPrev, CabinetFileName); + + /* Read label of previous disk */ + Status = ReadString(DiskPrev, 256); + if (Status != CAB_STATUS_SUCCESS) + return Status; + } + else + { + wcscpy(CabinetPrev, L""); + wcscpy(DiskPrev, L""); + } + + if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0) + { + /* Read name of next cabinet */ + Status = ReadString(CabinetFileName, 256); + if (Status != CAB_STATUS_SUCCESS) + return Status; + + /* The next cabinet file is in the same directory as the previous */ + wcscpy(CabinetNext, CabinetName); + RemoveFileName(CabinetNext); + CabinetNormalizePath(CabinetNext, 256); + wcscat(CabinetNext, CabinetFileName); + + /* Read label of next disk */ + Status = ReadString(DiskNext, 256); + if (Status != CAB_STATUS_SUCCESS) + return Status; + } + else + { + wcscpy(CabinetNext, L""); + wcscpy(DiskNext, L""); + } + + /* Read all folders */ + for (Index = 0; Index < CABHeader.FolderCount; Index++) + { + FolderNode = NewFolderNode(); + if (!FolderNode) + { + DPRINT("Insufficient resources.\n"); + return CAB_STATUS_NOMEMORY; + } + + if (Index == 0) + FolderNode->UncompOffset = FolderUncompSize; + + FolderNode->Index = Index; + + if ((Status = ReadBlock(&FolderNode->Folder, + sizeof(CFFOLDER), &BytesRead)) != CAB_STATUS_SUCCESS) + { + DPRINT("Cannot read from file (%d).\n", (UINT)Status); + return CAB_STATUS_INVALID_CAB; + } + } + + /* Read file entries */ + Status = ReadFileTable(); + if (Status != CAB_STATUS_SUCCESS) + { + DPRINT("ReadFileTable() failed (%d).\n", (UINT)Status); + return Status; + } + + /* Read data blocks for all folders */ + FolderNode = FolderListHead; + while (FolderNode != NULL) + { + Status = ReadDataBlocks(FolderNode); + if (Status != CAB_STATUS_SUCCESS) + { + DPRINT("ReadDataBlocks() failed (%d).\n", (UINT)Status); + return Status; + } + FolderNode = FolderNode->Next; + } + } + return CAB_STATUS_SUCCESS; +} + + +VOID +CabinetClose() +/* + * FUNCTION: Closes the cabinet file + */ +{ + if (FileOpen) + { + CloseCabinet(); + + if (CabinetReservedArea != NULL) + { + RtlFreeHeap(ProcessHeap, 0, CabinetReservedArea); + CabinetReservedArea = NULL; + } + + FileOpen = FALSE; + } +} + + +ULONG +CabinetFindFirst(PWCHAR FileName, + PCAB_SEARCH Search) +/* + * FUNCTION: Finds the first file in the cabinet that matches a search criteria + * ARGUMENTS: + * FileName = Pointer to search criteria + * Search = Pointer to search structure + * RETURNS: + * Status of operation + */ +{ + RestartSearch = FALSE; + wcsncpy(Search->Search, FileName, MAX_PATH); + Search->Next = FileListHead; + return CabinetFindNext(Search); +} + + +ULONG +CabinetFindNext(PCAB_SEARCH Search) +/* + * FUNCTION: Finds next file in the cabinet that matches a search criteria + * ARGUMENTS: + * Search = Pointer to search structure + * RETURNS: + * Status of operation + */ +{ + ULONG Status; + + if (RestartSearch) + { + Search->Next = FileListHead; + + /* Skip split files already extracted */ + while ((Search->Next) && + (Search->Next->File.FileControlID > CAB_FILE_MAX_FOLDER) && + (Search->Next->File.FileOffset <= LastFileOffset)) + { + DPRINT("Skipping file (%s) FileOffset (0x%X) LastFileOffset (0x%X).\n", + Search->Next->FileName, Search->Next->File.FileOffset, LastFileOffset); + Search->Next = Search->Next->Next; + } + + RestartSearch = FALSE; + } + + /* FIXME: Check search criteria */ + + if (!Search->Next) + { + if (wcslen(DiskNext) > 0) + { + CloseCabinet(); + + CabinetSetCabinetName(CabinetNext); + + if (DiskChangeHandler != NULL) + { + DiskChangeHandler(CabinetNext, DiskNext); + } + + Status = CabinetOpen(); + if (Status != CAB_STATUS_SUCCESS) + return Status; + + Search->Next = FileListHead; + if (!Search->Next) + return CAB_STATUS_NOFILE; + } + else + { + return CAB_STATUS_NOFILE; + } + } + + Search->File = &Search->Next->File; + Search->FileName = Search->Next->FileName; + Search->Next = Search->Next->Next; + return CAB_STATUS_SUCCESS; +} + + +ULONG +CabinetExtractFile(PWCHAR FileName) +/* + * FUNCTION: Extracts a file from the cabinet + * ARGUMENTS: + * FileName = Pointer to buffer with name of file + * RETURNS + * Status of operation + */ +{ + ULONG Size; + ULONG Offset; + ULONG BytesRead; + ULONG BytesToRead; + ULONG BytesWritten; + ULONG BytesSkipped; + ULONG BytesToWrite; + ULONG TotalBytesRead; + ULONG CurrentOffset; + PUCHAR Buffer; + PUCHAR CurrentBuffer; + HANDLE DestFile; + PCFFILE_NODE File; + CFDATA CFData; + ULONG Status; + BOOL Skip; + FILETIME FileTime; + WCHAR DestName[MAX_PATH]; + WCHAR TempName[MAX_PATH]; + NTSTATUS NtStatus; + UNICODE_STRING UnicodeString; + IO_STATUS_BLOCK IoStatusBlock; + OBJECT_ATTRIBUTES ObjectAttributes; + FILE_BASIC_INFORMATION FileBasic; + + Status = LocateFile(FileName, &File); + if (Status != CAB_STATUS_SUCCESS) + { + DPRINT("Cannot locate file (%d).\n", (UINT)Status); + return Status; + } + + LastFileOffset = File->File.FileOffset; + + switch (CurrentFolderNode->Folder.CompressionType & CAB_COMP_MASK) + { + case CAB_COMP_NONE: + CabinetSelectCodec(CAB_CODEC_RAW); + break; + case CAB_COMP_MSZIP: + CabinetSelectCodec(CAB_CODEC_MSZIP); + break; + default: + return CAB_STATUS_UNSUPPCOMP; + } + + DPRINT("Extracting file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n", + (UINT)File->File.FileOffset, + (UINT)File->File.FileSize, + (UINT)File->DataBlock->AbsoluteOffset, + (UINT)File->DataBlock->UncompOffset); + + wcscpy(DestName, DestPath); + wcscat(DestName, FileName); + + /* Create destination file, fail if it already exists */ + RtlInitUnicodeString(&UnicodeString, + DestName); + + InitializeObjectAttributes(&ObjectAttributes, + &UnicodeString, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + NtStatus = NtCreateFile(&DestFile, + FILE_WRITE_ACCESS, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + 0, + FILE_CREATE, + FILE_SYNCHRONOUS_IO_ALERT, + NULL, + 0); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("NtCreateFile() failed (%S) (%x).\n", DestName, NtStatus); + + /* If file exists, ask to overwrite file */ + if (OverwriteHandler == NULL || OverwriteHandler(&File->File, FileName)) + { + /* Create destination file, overwrite if it already exists */ + NtStatus = NtCreateFile(&DestFile, + FILE_WRITE_ACCESS, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + 0, + FILE_OVERWRITE, + FILE_SYNCHRONOUS_IO_ALERT, + NULL, + 0); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("NtCreateFile() failed 2 (%S) (%x).\n", DestName, NtStatus); + return CAB_STATUS_CANNOT_CREATE; + } + } + else + { + DPRINT("File (%S) exists.\n", DestName); + return CAB_STATUS_FILE_EXISTS; + } + } + + if (!ConvertDosDateTimeToFileTime(File->File.FileDate, File->File.FileTime, &FileTime)) + { + NtClose(DestFile); + DPRINT("DosDateTimeToFileTime() failed.\n"); + return CAB_STATUS_CANNOT_WRITE; + } + + NtStatus = NtQueryInformationFile(DestFile, + &IoStatusBlock, + &FileBasic, + sizeof(FILE_BASIC_INFORMATION), + FileBasicInformation); + if (!NT_SUCCESS(Status)) + { + DPRINT("NtQueryInformationFile() failed (%x).\n", NtStatus); + } + else + { + memcpy(&FileBasic.LastAccessTime, &FileTime, sizeof(FILETIME)); + + NtStatus = NtSetInformationFile(DestFile, + &IoStatusBlock, + &FileBasic, + sizeof(FILE_BASIC_INFORMATION), + FileBasicInformation); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("NtSetInformationFile() failed (%x).\n", NtStatus); + } + } + + SetAttributesOnFile(File, DestFile); + + Buffer = RtlAllocateHeap(ProcessHeap, 0, CAB_BLOCKSIZE + 12); // This should be enough + if (!Buffer) + { + NtClose(DestFile); + DPRINT("Insufficient memory.\n"); + return CAB_STATUS_NOMEMORY; + } + + /* Call extract event handler */ + if (ExtractHandler != NULL) + { + ExtractHandler(&File->File, FileName); + } + + /* Search to start of file */ + Offset = SeekInFile(FileHandle, + File->DataBlock->AbsoluteOffset, + NULL, + SEEK_BEGIN, + &NtStatus); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("SeekInFile() failed (%x).\n", NtStatus); + return CAB_STATUS_INVALID_CAB; + } + + Size = File->File.FileSize; + Offset = File->File.FileOffset; + CurrentOffset = File->DataBlock->UncompOffset; + + Skip = TRUE; + + ReuseBlock = (CurrentDataNode == File->DataBlock); + if (Size > 0) + { + do + { + DPRINT("CO (0x%X) ReuseBlock (%d) Offset (0x%X) Size (%d) BytesLeftInBlock (%d)\n", + File->DataBlock->UncompOffset, (UINT)ReuseBlock, Offset, Size, + BytesLeftInBlock); + + if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock) || (BytesLeftInBlock <= 0)) + { + DPRINT("Filling buffer. ReuseBlock (%d)\n", (UINT)ReuseBlock); + + CurrentBuffer = Buffer; + TotalBytesRead = 0; + do + { + DPRINT("Size (%d bytes).\n", Size); + + if (((Status = ReadBlock(&CFData, sizeof(CFDATA), &BytesRead)) != + CAB_STATUS_SUCCESS) || (BytesRead != sizeof(CFDATA))) + { + NtClose(DestFile); + RtlFreeHeap(ProcessHeap, 0, Buffer); + DPRINT("Cannot read from file (%d).\n", (UINT)Status); + return CAB_STATUS_INVALID_CAB; + } + + DPRINT("Data block: Checksum (0x%X) CompSize (%d bytes) UncompSize (%d bytes) Offset (0x%X).\n", + (UINT)CFData.Checksum, + (UINT)CFData.CompSize, + (UINT)CFData.UncompSize, + (UINT)SeekInFile(FileHandle, 0, NULL, SEEK_CURRENT, &NtStatus)); + + //ASSERT(CFData.CompSize <= CAB_BLOCKSIZE + 12); + + BytesToRead = CFData.CompSize; + + DPRINT("Read: (0x%X,0x%X).\n", + CurrentBuffer, Buffer); + + if (((Status = ReadBlock(CurrentBuffer, BytesToRead, &BytesRead)) != + CAB_STATUS_SUCCESS) || (BytesToRead != BytesRead)) + { + NtClose(DestFile); + RtlFreeHeap(ProcessHeap, 0, Buffer); + DPRINT("Cannot read from file (%d).\n", (UINT)Status); + return CAB_STATUS_INVALID_CAB; + } + + /* FIXME: Does not work with files generated by makecab.exe */ +/* + if (CFData.Checksum != 0) + { + ULONG Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0); + if (Checksum != CFData.Checksum) + { + NtClose(DestFile); + RtlFreeHeap(ProcessHeap, 0, Buffer); + DPRINT("Bad checksum (is 0x%X, should be 0x%X).\n", + Checksum, CFData.Checksum); + return CAB_STATUS_INVALID_CAB; + } + } +*/ + TotalBytesRead += BytesRead; + + CurrentBuffer += BytesRead; + + if (CFData.UncompSize == 0) + { + if (wcslen(DiskNext) == 0) + return CAB_STATUS_NOFILE; + + /* CloseCabinet() will destroy all file entries so in case + FileName refers to the FileName field of a CFFOLDER_NODE + structure, we have to save a copy of the filename */ + wcscpy(TempName, FileName); + + CloseCabinet(); + + CabinetSetCabinetName(CabinetNext); + + if (DiskChangeHandler != NULL) + { + DiskChangeHandler(CabinetNext, DiskNext); + } + + Status = CabinetOpen(); + if (Status != CAB_STATUS_SUCCESS) + return Status; + + /* The first data block of the file will not be + found as it is located in the previous file */ + Status = LocateFile(TempName, &File); + if (Status == CAB_STATUS_NOFILE) + { + DPRINT("Cannot locate file (%d).\n", (UINT)Status); + return Status; + } + + /* The file is continued in the first data block in the folder */ + File->DataBlock = CurrentFolderNode->DataListHead; + + /* Search to start of file */ + SeekInFile(FileHandle, + File->DataBlock->AbsoluteOffset, + NULL, + SEEK_BEGIN, + &NtStatus); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("SeekInFile() failed (%x).\n", NtStatus); + return CAB_STATUS_INVALID_CAB; + } + + DPRINT("Continuing extraction of file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n", + (UINT)File->File.FileOffset, + (UINT)File->File.FileSize, + (UINT)File->DataBlock->AbsoluteOffset, + (UINT)File->DataBlock->UncompOffset); + + CurrentDataNode = File->DataBlock; + ReuseBlock = TRUE; + + RestartSearch = TRUE; + } + } while (CFData.UncompSize == 0); + + DPRINT("TotalBytesRead (%d).\n", TotalBytesRead); + + Status = CodecUncompress(OutputBuffer, Buffer, TotalBytesRead, &BytesToWrite); + if (Status != CS_SUCCESS) + { + NtClose(DestFile); + RtlFreeHeap(ProcessHeap, 0, Buffer); + DPRINT("Cannot uncompress block.\n"); + if (Status == CS_NOMEMORY) + return CAB_STATUS_NOMEMORY; + return CAB_STATUS_INVALID_CAB; + } + + if (BytesToWrite != CFData.UncompSize) + { + DPRINT("BytesToWrite (%d) != CFData.UncompSize (%d)\n", + BytesToWrite, CFData.UncompSize); + return CAB_STATUS_INVALID_CAB; + } + + BytesLeftInBlock = BytesToWrite; + } + else + { + DPRINT("Using same buffer. ReuseBlock (%d)\n", (UINT)ReuseBlock); + + BytesToWrite = BytesLeftInBlock; + + DPRINT("Seeking to absolute offset 0x%X.\n", + CurrentDataNode->AbsoluteOffset + sizeof(CFDATA) + + CurrentDataNode->Data.CompSize); + + if (((Status = ReadBlock(&CFData, sizeof(CFDATA), &BytesRead)) != + CAB_STATUS_SUCCESS) || (BytesRead != sizeof(CFDATA))) + { + NtClose(DestFile); + RtlFreeHeap(ProcessHeap, 0, Buffer); + DPRINT("Cannot read from file (%d).\n", (UINT)Status); + return CAB_STATUS_INVALID_CAB; + } + + DPRINT("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n", + CFData.CompSize, CFData.UncompSize); + + /* Go to next data block */ + SeekInFile(FileHandle, + CurrentDataNode->AbsoluteOffset + sizeof(CFDATA) + + CurrentDataNode->Data.CompSize, + NULL, + SEEK_BEGIN, + &NtStatus); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("SeekInFile() failed (%x).\n", NtStatus); + return CAB_STATUS_INVALID_CAB; + } + + ReuseBlock = FALSE; + } + + if (Skip) + BytesSkipped = (Offset - CurrentOffset); + else + BytesSkipped = 0; + + BytesToWrite -= BytesSkipped; + + if (Size < BytesToWrite) + BytesToWrite = Size; + + DPRINT("Offset (0x%X) CurrentOffset (0x%X) ToWrite (%d) Skipped (%d)(%d) Size (%d).\n", + (UINT)Offset, + (UINT)CurrentOffset, + (UINT)BytesToWrite, + (UINT)BytesSkipped, (UINT)Skip, + (UINT)Size); + +// if (!WriteFile(DestFile, (PVOID)((ULONG)OutputBuffer + BytesSkipped), +// BytesToWrite, &BytesWritten, NULL) || +// (BytesToWrite != BytesWritten)) + + NtStatus = NtWriteFile(DestFile, + NULL, + NULL, + NULL, + &IoStatusBlock, + (PVOID)((ULONG)OutputBuffer + BytesSkipped), + BytesToWrite, + NULL, + NULL); + BytesWritten = BytesToWrite; + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("Status 0x%X.\n", NtStatus); + + NtClose(DestFile); + RtlFreeHeap(ProcessHeap, 0, Buffer); + DPRINT("Cannot write to file.\n"); + return CAB_STATUS_CANNOT_WRITE; + } + Size -= BytesToWrite; + + CurrentOffset += BytesToWrite; + + /* Don't skip any more bytes */ + Skip = FALSE; + } while (Size > 0); + } + + NtClose(DestFile); + + RtlFreeHeap(ProcessHeap, 0, Buffer); + + return CAB_STATUS_SUCCESS; +} + + +VOID +CabinetSelectCodec(ULONG Id) +/* + * FUNCTION: Selects codec engine to use + * ARGUMENTS: + * Id = Codec identifier + */ +{ + if (CodecSelected) + { + if (Id == CodecId) + return; + + CodecSelected = FALSE; + } + + switch (Id) + { + case CAB_CODEC_RAW: + CodecUncompress = RawCodecUncompress; + break; + case CAB_CODEC_MSZIP: + CodecUncompress = MSZipCodecUncompress; + break; + default: + return; + } + + CodecId = Id; + CodecSelected = TRUE; +} + + +VOID +CabinetSetEventHandlers(PCABINET_OVERWRITE Overwrite, + PCABINET_EXTRACT Extract, + PCABINET_DISK_CHANGE DiskChange) +/* + * FUNCTION: Set event handlers + * ARGUMENTS: + * Overwrite = Handler called when a file is to be overwritten + * Extract = Handler called when a file is to be extracted + * DiskChange = Handler called when changing the disk + */ +{ + OverwriteHandler = Overwrite; + ExtractHandler = Extract; + DiskChangeHandler = DiskChange; +} + + +PVOID +CabinetGetCabinetReservedArea(PULONG Size) +/* + * FUNCTION: Get pointer to cabinet reserved area. NULL if none + */ +{ + if (CabinetReservedArea != NULL) + { + if (Size != NULL) + { + *Size = CabinetReserved; + } + return CabinetReservedArea; + } + else + { + if (Size != NULL) + { + *Size = 0; + } + return NULL; + } +} diff --git a/reactos/subsys/system/usetup/cabinet.h b/reactos/subsys/system/usetup/cabinet.h new file mode 100755 index 00000000000..10dd53c34b4 --- /dev/null +++ b/reactos/subsys/system/usetup/cabinet.h @@ -0,0 +1,245 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS cabinet manager + * FILE: apps/cabman/cabinet.h + * PURPOSE: Cabinet definitions + */ +#ifndef __CABINET_H +#define __CABINET_H + +#include + +/* Cabinet constants */ + +#define CAB_SIGNATURE 0x4643534D // "MSCF" +#define CAB_VERSION 0x0103 +#define CAB_BLOCKSIZE 32768 + +#define CAB_COMP_MASK 0x00FF +#define CAB_COMP_NONE 0x0000 +#define CAB_COMP_MSZIP 0x0001 +#define CAB_COMP_QUANTUM 0x0002 +#define CAB_COMP_LZX 0x0003 + +#define CAB_FLAG_HASPREV 0x0001 +#define CAB_FLAG_HASNEXT 0x0002 +#define CAB_FLAG_RESERVE 0x0004 + +#define CAB_ATTRIB_READONLY 0x0001 +#define CAB_ATTRIB_HIDDEN 0x0002 +#define CAB_ATTRIB_SYSTEM 0x0004 +#define CAB_ATTRIB_VOLUME 0x0008 +#define CAB_ATTRIB_DIRECTORY 0x0010 +#define CAB_ATTRIB_ARCHIVE 0x0020 +#define CAB_ATTRIB_EXECUTE 0x0040 +#define CAB_ATTRIB_UTF_NAME 0x0080 + +#define CAB_FILE_MAX_FOLDER 0xFFFC +#define CAB_FILE_CONTINUED 0xFFFD +#define CAB_FILE_SPLIT 0xFFFE +#define CAB_FILE_PREV_NEXT 0xFFFF + + +/* Cabinet structures */ + +typedef struct _CFHEADER +{ + ULONG Signature; // File signature 'MSCF' (CAB_SIGNATURE) + ULONG Reserved1; // Reserved field + ULONG CabinetSize; // Cabinet file size + ULONG Reserved2; // Reserved field + ULONG FileTableOffset; // Offset of first CFFILE + ULONG Reserved3; // Reserved field + WORD Version; // Cabinet version (CAB_VERSION) + WORD FolderCount; // Number of folders + WORD FileCount; // Number of files + WORD Flags; // Cabinet flags (CAB_FLAG_*) + WORD SetID; // Cabinet set id + WORD CabinetNumber; // Zero-based cabinet number +/* Optional fields (depends on Flags) + WORD CabinetResSize // Per-cabinet reserved area size + CHAR FolderResSize // Per-folder reserved area size + CHAR FileResSize // Per-file reserved area size + CHAR CabinetReserved[] // Per-cabinet reserved area + CHAR CabinetPrev[] // Name of previous cabinet file + CHAR DiskPrev[] // Name of previous disk + CHAR CabinetNext[] // Name of next cabinet file + CHAR DiskNext[] // Name of next disk + */ +} CFHEADER, *PCFHEADER; + + +typedef struct _CFFOLDER +{ + ULONG DataOffset; // Absolute offset of first CFDATA block in this folder + WORD DataBlockCount; // Number of CFDATA blocks in this folder in this cabinet + WORD CompressionType; // Type of compression used for all CFDATA blocks in this folder +/* Optional fields (depends on Flags) + CHAR FolderReserved[] // Per-folder reserved area + */ +} CFFOLDER, *PCFFOLDER; + + +typedef struct _CFFILE +{ + ULONG FileSize; // Uncompressed file size in bytes + ULONG FileOffset; // Uncompressed offset of file in the folder + WORD FileControlID; // File control ID (CAB_FILE_*) + WORD FileDate; // File date stamp, as used by DOS + WORD FileTime; // File time stamp, as used by DOS + WORD Attributes; // File attributes (CAB_ATTRIB_*) + /* After this is the NULL terminated filename */ +} CFFILE, *PCFFILE; + + +typedef struct _CFDATA +{ + ULONG Checksum; // Checksum of CFDATA entry + WORD CompSize; // Number of compressed bytes in this block + WORD UncompSize; // Number of uncompressed bytes in this block +/* Optional fields (depends on Flags) + CHAR DataReserved[] // Per-datablock reserved area + */ +} CFDATA, *PCFDATA; + +typedef struct _CFDATA_NODE +{ + struct _CFDATA_NODE *Next; + struct _CFDATA_NODE *Prev; + ULONG ScratchFilePosition; // Absolute offset in scratch file + ULONG AbsoluteOffset; // Absolute offset in cabinet + ULONG UncompOffset; // Uncompressed offset in folder + CFDATA Data; +} CFDATA_NODE, *PCFDATA_NODE; + +typedef struct _CFFOLDER_NODE +{ + struct _CFFOLDER_NODE *Next; + struct _CFFOLDER_NODE *Prev; + ULONG UncompOffset; // File size accumulator + ULONG AbsoluteOffset; + ULONG TotalFolderSize; // Total size of folder in current disk + PCFDATA_NODE DataListHead; + PCFDATA_NODE DataListTail; + ULONG Index; + BOOL Commit; // TRUE if the folder should be committed + BOOL Delete; // TRUE if marked for deletion + CFFOLDER Folder; +} CFFOLDER_NODE, *PCFFOLDER_NODE; + +typedef struct _CFFILE_NODE +{ + struct _CFFILE_NODE *Next; + struct _CFFILE_NODE *Prev; + CFFILE File; + PWCHAR FileName; + PCFDATA_NODE DataBlock; // First data block of file. NULL if not known + BOOL Commit; // TRUE if the file data should be committed + BOOL Delete; // TRUE if marked for deletion + PCFFOLDER_NODE FolderNode; // Folder this file belong to +} CFFILE_NODE, *PCFFILE_NODE; + + +typedef struct _CAB_SEARCH +{ + WCHAR Search[MAX_PATH]; // Search criteria + PCFFILE_NODE Next; // Pointer to next node + PCFFILE File; // Pointer to current CFFILE + PWCHAR FileName; // Current filename +} CAB_SEARCH, *PCAB_SEARCH; + + +/* Constants */ + +/* Status codes */ +#define CAB_STATUS_SUCCESS 0x00000000 +#define CAB_STATUS_FAILURE 0x00000001 +#define CAB_STATUS_NOMEMORY 0x00000002 +#define CAB_STATUS_CANNOT_OPEN 0x00000003 +#define CAB_STATUS_CANNOT_CREATE 0x00000004 +#define CAB_STATUS_CANNOT_READ 0x00000005 +#define CAB_STATUS_CANNOT_WRITE 0x00000006 +#define CAB_STATUS_FILE_EXISTS 0x00000007 +#define CAB_STATUS_INVALID_CAB 0x00000008 +#define CAB_STATUS_NOFILE 0x00000009 +#define CAB_STATUS_UNSUPPCOMP 0x0000000A + + +/* Codecs */ + +/* Uncompresses a data block */ +typedef ULONG (*PCABINET_CODEC_UNCOMPRESS)(PVOID OutputBuffer, + PVOID InputBuffer, + ULONG InputLength, + PULONG OutputLength); + + +/* Codec status codes */ +#define CS_SUCCESS 0x0000 /* All data consumed */ +#define CS_NOMEMORY 0x0001 /* Not enough free memory */ +#define CS_BADSTREAM 0x0002 /* Bad data stream */ + +/* Codec indentifiers */ +#define CAB_CODEC_RAW 0x00 +#define CAB_CODEC_LZX 0x01 +#define CAB_CODEC_MSZIP 0x02 + +#define MSZIP_MAGIC 0x4B43 + + + +/* Event handler prototypes */ + +typedef BOOL (*PCABINET_OVERWRITE)(PCFFILE File, + PWCHAR FileName); + +typedef VOID (*PCABINET_EXTRACT)(PCFFILE File, + PWCHAR FileName); + +typedef VOID (*PCABINET_DISK_CHANGE)(PWCHAR CabinetName, + PWCHAR DiskLabel); + + + +/* Classes */ + +/* Default constructor */ +VOID CabinetInitialize(); +/* Default destructor */ +VOID CabinetCleanup(); +/* Returns a pointer to the filename part of a fully qualified filename */ +PWCHAR CabinetGetFileName(PWCHAR Path); +/* Removes a filename from a fully qualified filename */ +VOID CabinetRemoveFileName(PWCHAR Path); +/* Normalizes a path */ +BOOL CabinetNormalizePath(PWCHAR Path, ULONG Length); +/* Returns name of cabinet file */ +PWCHAR CabinetGetCabinetName(); +/* Sets the name of the cabinet file */ +VOID CabinetSetCabinetName(PWCHAR FileName); +/* Sets destination path for extracted files */ +VOID CabinetSetDestinationPath(PWCHAR DestinationPath); +/* Returns destination path */ +PWCHAR CabinetGetDestinationPath(); +/* Returns zero-based current disk number */ +ULONG CabinetGetCurrentDiskNumber(); +/* Opens the current cabinet file */ +ULONG CabinetOpen(); +/* Closes the current open cabinet file */ +VOID CabinetClose(); +/* Locates the first file in the current cabinet file that matches a search criteria */ +ULONG CabinetFindFirst(PWCHAR FileName, PCAB_SEARCH Search); +/* Locates the next file in the current cabinet file */ +ULONG CabinetFindNext(PCAB_SEARCH Search); +/* Extracts a file from the current cabinet file */ +ULONG CabinetExtractFile(PWCHAR FileName); +/* Select codec engine to use */ +VOID CabinetSelectCodec(ULONG Id); +/* Set event handlers */ +VOID CabinetSetEventHandlers(PCABINET_OVERWRITE Overwrite, + PCABINET_EXTRACT Extract, + PCABINET_DISK_CHANGE DiskChange); +/* Get pointer to cabinet reserved area. NULL if none */ +PVOID CabinetGetCabinetReservedArea(PULONG Size); + +#endif /* __CABINET_H */ diff --git a/reactos/subsys/system/usetup/filequeue.c b/reactos/subsys/system/usetup/filequeue.c index cd9da1cdfc9..c2559145880 100644 --- a/reactos/subsys/system/usetup/filequeue.c +++ b/reactos/subsys/system/usetup/filequeue.c @@ -16,12 +16,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* $Id: filequeue.c,v 1.1 2002/11/23 01:55:27 ekohl Exp $ +/* $Id: filequeue.c,v 1.2 2003/08/24 10:36:06 chorns Exp $ * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS text-mode setup * FILE: subsys/system/usetup/filequeue.c * PURPOSE: File queue functions * PROGRAMMER: Eric Kohl + * Casper S. Hornstrup (chorns@users.sourceforge.net) */ /* INCLUDES *****************************************************************/ @@ -42,6 +43,7 @@ typedef struct _QUEUEENTRY struct _QUEUEENTRY *Prev; struct _QUEUEENTRY *Next; + PWSTR SourceCabinet; /* May be NULL if file is not in a cabinet */ PWSTR SourceRootPath; PWSTR SourcePath; PWSTR SourceFilename; @@ -98,6 +100,8 @@ SetupCloseFileQueue(HSPFILEQ QueueHandle) while (Entry != NULL) { /* Delete all strings */ + if (Entry->SourceCabinet != NULL) + RtlFreeHeap(ProcessHeap, 0, Entry->SourceCabinet); if (Entry->SourceRootPath != NULL) RtlFreeHeap(ProcessHeap, 0, Entry->SourceRootPath); if (Entry->SourcePath != NULL) @@ -139,6 +143,7 @@ SetupCloseFileQueue(HSPFILEQ QueueHandle) BOOL SetupQueueCopy(HSPFILEQ QueueHandle, + PCWSTR SourceCabinet, PCWSTR SourceRootPath, PCWSTR SourcePath, PCWSTR SourceFilename, @@ -149,6 +154,7 @@ SetupQueueCopy(HSPFILEQ QueueHandle, PQUEUEENTRY Entry; ULONG Length; + /* SourceCabinet may be NULL */ if (QueueHandle == NULL || SourceRootPath == NULL || SourceFilename == NULL || @@ -167,6 +173,26 @@ SetupQueueCopy(HSPFILEQ QueueHandle, RtlZeroMemory(Entry, sizeof(QUEUEENTRY)); + /* Copy source cabinet if available */ + if (SourceCabinet != NULL) + { + Length = wcslen(SourceCabinet); + Entry->SourceCabinet = RtlAllocateHeap(ProcessHeap, + 0, + (Length + 1) * sizeof(WCHAR)); + if (Entry->SourceCabinet == NULL) + { + RtlFreeHeap(ProcessHeap, 0, Entry); + return(FALSE); + } + wcsncpy(Entry->SourceCabinet, SourceCabinet, Length); + Entry->SourceCabinet[Length] = (WCHAR)0; + } + else + { + Entry->SourceCabinet = NULL; + } + /* Copy source root path */ Length = wcslen(SourceRootPath); Entry->SourceRootPath = RtlAllocateHeap(ProcessHeap, @@ -174,6 +200,10 @@ SetupQueueCopy(HSPFILEQ QueueHandle, (Length + 1) * sizeof(WCHAR)); if (Entry->SourceRootPath == NULL) { + if (Entry->SourceCabinet != NULL) + { + RtlFreeHeap(ProcessHeap, 0, Entry->SourceCabinet); + } RtlFreeHeap(ProcessHeap, 0, Entry); return(FALSE); } @@ -189,6 +219,10 @@ SetupQueueCopy(HSPFILEQ QueueHandle, (Length + 1) * sizeof(WCHAR)); if (Entry->SourcePath == NULL) { + if (Entry->SourceCabinet != NULL) + { + RtlFreeHeap(ProcessHeap, 0, Entry->SourceCabinet); + } RtlFreeHeap(ProcessHeap, 0, Entry->SourceRootPath); RtlFreeHeap(ProcessHeap, 0, Entry); return(FALSE); @@ -204,6 +238,10 @@ SetupQueueCopy(HSPFILEQ QueueHandle, (Length + 1) * sizeof(WCHAR)); if (Entry->SourceFilename == NULL) { + if (Entry->SourceCabinet != NULL) + { + RtlFreeHeap(ProcessHeap, 0, Entry->SourceCabinet); + } RtlFreeHeap(ProcessHeap, 0, Entry->SourceRootPath); RtlFreeHeap(ProcessHeap, 0, Entry->SourcePath); RtlFreeHeap(ProcessHeap, 0, Entry); @@ -221,6 +259,10 @@ SetupQueueCopy(HSPFILEQ QueueHandle, (Length + 1) * sizeof(WCHAR)); if (Entry->TargetDirectory == NULL) { + if (Entry->SourceCabinet != NULL) + { + RtlFreeHeap(ProcessHeap, 0, Entry->SourceCabinet); + } RtlFreeHeap(ProcessHeap, 0, Entry->SourceRootPath); RtlFreeHeap(ProcessHeap, 0, Entry->SourcePath); RtlFreeHeap(ProcessHeap, 0, Entry->SourceFilename); @@ -239,6 +281,10 @@ SetupQueueCopy(HSPFILEQ QueueHandle, (Length + 1) * sizeof(WCHAR)); if (Entry->TargetFilename == NULL) { + if (Entry->SourceCabinet != NULL) + { + RtlFreeHeap(ProcessHeap, 0, Entry->SourceCabinet); + } RtlFreeHeap(ProcessHeap, 0, Entry->SourceRootPath); RtlFreeHeap(ProcessHeap, 0, Entry->SourcePath); RtlFreeHeap(ProcessHeap, 0, Entry->SourceFilename); @@ -278,6 +324,7 @@ SetupCommitFileQueue(HSPFILEQ QueueHandle, PSP_FILE_CALLBACK MsgHandler, PVOID Context) { + WCHAR CabinetName[MAX_PATH]; PFILEQUEUEHEADER QueueHeader; PQUEUEENTRY Entry; NTSTATUS Status; @@ -304,7 +351,6 @@ SetupCommitFileQueue(HSPFILEQ QueueHandle, Entry = QueueHeader->CopyHead; while (Entry != NULL) { - /* Build the full source path */ wcscpy(FileSrcPath, Entry->SourceRootPath); if (Entry->SourcePath != NULL) wcscat(FileSrcPath, Entry->SourcePath); @@ -328,11 +374,16 @@ SetupCommitFileQueue(HSPFILEQ QueueHandle, wcscat(FileDstPath, L"\\"); wcscat(FileDstPath, Entry->TargetDirectory); } - wcscat(FileDstPath, L"\\"); - if (Entry->TargetFilename != NULL) - wcscat(FileDstPath, Entry->TargetFilename); - else - wcscat(FileDstPath, Entry->SourceFilename); + + /* Use only the destination path if the file is in a cabinet */ + if (Entry->SourceCabinet == NULL) + { + wcscat(FileDstPath, L"\\"); + if (Entry->TargetFilename != NULL) + wcscat(FileDstPath, Entry->TargetFilename); + else + wcscat(FileDstPath, Entry->SourceFilename); + } /* FIXME: Do it! */ DPRINT("'%S' ==> '%S'\n", @@ -344,8 +395,21 @@ SetupCommitFileQueue(HSPFILEQ QueueHandle, (PVOID)Entry->SourceFilename, (PVOID)FILEOP_COPY); - /* Copy the file */ - Status = SetupCopyFile(FileSrcPath, FileDstPath); + if (Entry->SourceCabinet != NULL) + { + /* Extract the file */ + wcscpy(CabinetName, Entry->SourceRootPath); + if (Entry->SourcePath != NULL) + wcscat(CabinetName, Entry->SourcePath); + wcscat(CabinetName, L"\\"); + wcscat(CabinetName, Entry->SourceCabinet); + Status = SetupExtractFile(CabinetName, Entry->SourceFilename, FileDstPath); + } + else + { + /* Copy the file */ + Status = SetupCopyFile(FileSrcPath, FileDstPath); + } if (!NT_SUCCESS(Status)) { MsgHandler(Context, diff --git a/reactos/subsys/system/usetup/filequeue.h b/reactos/subsys/system/usetup/filequeue.h index a65b90773b1..47c395026bb 100644 --- a/reactos/subsys/system/usetup/filequeue.h +++ b/reactos/subsys/system/usetup/filequeue.h @@ -16,7 +16,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* $Id: filequeue.h,v 1.1 2002/11/23 01:55:27 ekohl Exp $ +/* $Id: filequeue.h,v 1.2 2003/08/24 10:36:06 chorns Exp $ * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS text-mode setup * FILE: subsys/system/usetup/filequeue.h @@ -69,6 +69,7 @@ SetupCloseFileQueue(HSPFILEQ QueueHandle); BOOL SetupQueueCopy(HSPFILEQ QueueHandle, + PCWSTR SourceCabinet, PCWSTR SourceRootPath, PCWSTR SourcePath, PCWSTR SourceFilename, diff --git a/reactos/subsys/system/usetup/filesup.c b/reactos/subsys/system/usetup/filesup.c index 1020e46d5c9..3fbf899de77 100644 --- a/reactos/subsys/system/usetup/filesup.c +++ b/reactos/subsys/system/usetup/filesup.c @@ -16,12 +16,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* $Id: filesup.c,v 1.6 2003/05/18 12:50:17 ekohl Exp $ +/* $Id: filesup.c,v 1.7 2003/08/24 10:36:06 chorns Exp $ * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS text-mode setup * FILE: subsys/system/usetup/filesup.c * PURPOSE: File support functions * PROGRAMMER: Eric Kohl + * Casper S. Hornstrup (chorns@users.sourceforge.net) */ /* INCLUDES *****************************************************************/ @@ -31,11 +32,15 @@ #include "usetup.h" #include "filesup.h" +#include "cabinet.h" /* FUNCTIONS ****************************************************************/ +static BOOLEAN HasCurrentCabinet = FALSE; +static WCHAR CurrentCabinetName[MAX_PATH]; + NTSTATUS CreateDirectory(PWCHAR DirectoryName) { @@ -274,6 +279,65 @@ SetupCopyFile(PWCHAR SourceFileName, } +NTSTATUS +SetupExtractFile(PWCHAR CabinetFileName, + PWCHAR SourceFileName, + PWCHAR DestinationPathName) +{ + ULONG CabStatus; + + DPRINT("SetupExtractFile(CabinetFileName %S, SourceFileName %S, DestinationPathName %S)\n", + CabinetFileName, SourceFileName, DestinationPathName); + + if (HasCurrentCabinet) + { + DPRINT("CurrentCabinetName: %S\n", CurrentCabinetName); + } + + if ((HasCurrentCabinet) && (wcscmp(CabinetFileName, CurrentCabinetName) == 0)) + { + DPRINT("Using same cabinet as last time\n"); + } + else + { + DPRINT("Using new cabinet\n"); + + if (HasCurrentCabinet) + { + CabinetCleanup(); + } + + wcscpy(CurrentCabinetName, CabinetFileName); + + CabinetInitialize(); + CabinetSetEventHandlers(NULL, NULL, NULL); + CabinetSetCabinetName(CabinetFileName); + + CabStatus = CabinetOpen(); + if (CabStatus == CAB_STATUS_SUCCESS) + { + DPRINT("Opened cabinet %S\n", CabinetGetCabinetName()); + HasCurrentCabinet = TRUE; + } + else + { + DPRINT("Cannot open cabinet (%d)\n", CabStatus); + return STATUS_UNSUCCESSFUL; + } + } + + CabinetSetDestinationPath(DestinationPathName); + CabStatus = CabinetExtractFile(SourceFileName); + if (CabStatus != CAB_STATUS_SUCCESS) + { + DPRINT("Cannot extract file %S (%d)\n", SourceFileName, CabStatus); + return STATUS_UNSUCCESSFUL; + } + + return STATUS_SUCCESS; +} + + BOOLEAN DoesFileExist(PWSTR PathName, PWSTR FileName) diff --git a/reactos/subsys/system/usetup/filesup.h b/reactos/subsys/system/usetup/filesup.h index a428373d67b..c76b16bba93 100644 --- a/reactos/subsys/system/usetup/filesup.h +++ b/reactos/subsys/system/usetup/filesup.h @@ -16,7 +16,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* $Id: filesup.h,v 1.3 2003/01/17 13:18:15 ekohl Exp $ +/* $Id: filesup.h,v 1.4 2003/08/24 10:36:06 chorns Exp $ * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS text-mode setup * FILE: subsys/system/usetup/filesup.h @@ -34,6 +34,11 @@ NTSTATUS SetupCopyFile(PWCHAR SourceFileName, PWCHAR DestinationFileName); +NTSTATUS +SetupExtractFile(PWCHAR CabinetFileName, + PWCHAR SourceFileName, + PWCHAR DestinationFileName); + BOOLEAN DoesFileExist(PWSTR PathName, PWSTR FileName); diff --git a/reactos/subsys/system/usetup/infcache.c b/reactos/subsys/system/usetup/infcache.c index 76f4f417918..8fc073097ad 100644 --- a/reactos/subsys/system/usetup/infcache.c +++ b/reactos/subsys/system/usetup/infcache.c @@ -16,7 +16,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* $Id: infcache.c,v 1.4 2003/05/18 12:50:17 ekohl Exp $ +/* $Id: infcache.c,v 1.5 2003/08/24 10:36:06 chorns Exp $ * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS text-mode setup * FILE: subsys/system/usetup/infcache.c @@ -893,6 +893,75 @@ InfpParseBuffer (PINFCACHE file, /* PUBLIC FUNCTIONS *********************************************************/ +NTSTATUS +InfOpenBufferedFile(PHINF InfHandle, + PVOID Buffer, + ULONG BufferSize, + PULONG ErrorLine) +{ + NTSTATUS Status; + PINFCACHE Cache; + PCHAR FileBuffer; + + *InfHandle = NULL; + *ErrorLine = (ULONG)-1; + + /* Allocate file buffer */ + FileBuffer = RtlAllocateHeap(ProcessHeap, + 0, + BufferSize + 1); + if (FileBuffer == NULL) + { + DPRINT1("RtlAllocateHeap() failed\n"); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + RtlCopyMemory(FileBuffer, Buffer, BufferSize); + + /* Append string terminator */ + FileBuffer[BufferSize] = 0; + + /* Allocate infcache header */ + Cache = (PINFCACHE)RtlAllocateHeap(ProcessHeap, + 0, + sizeof(INFCACHE)); + if (Cache == NULL) + { + DPRINT("RtlAllocateHeap() failed\n"); + RtlFreeHeap(ProcessHeap, + 0, + FileBuffer); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + /* Initialize inicache header */ + RtlZeroMemory(Cache, + sizeof(INFCACHE)); + + /* Parse the inf buffer */ + Status = InfpParseBuffer (Cache, + FileBuffer, + FileBuffer + BufferSize, + ErrorLine); + if (!NT_SUCCESS(Status)) + { + RtlFreeHeap(ProcessHeap, + 0, + Cache); + Cache = NULL; + } + + /* Free file buffer */ + RtlFreeHeap(ProcessHeap, + 0, + FileBuffer); + + *InfHandle = (HINF)Cache; + + return(Status); +} + + NTSTATUS InfOpenFile(PHINF InfHandle, PUNICODE_STRING FileName, diff --git a/reactos/subsys/system/usetup/infcache.h b/reactos/subsys/system/usetup/infcache.h index 6e65924665d..cea0c5fb13e 100644 --- a/reactos/subsys/system/usetup/infcache.h +++ b/reactos/subsys/system/usetup/infcache.h @@ -16,7 +16,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* $Id: infcache.h,v 1.3 2003/03/15 19:36:11 ekohl Exp $ +/* $Id: infcache.h,v 1.4 2003/08/24 10:36:06 chorns Exp $ * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS text-mode setup * FILE: subsys/system/usetup/infcache.h @@ -49,6 +49,12 @@ typedef struct _INFCONTEXT /* FUNCTIONS ****************************************************************/ +NTSTATUS +InfOpenBufferedFile(PHINF InfHandle, + PVOID Buffer, + ULONG BufferSize, + PULONG ErrorLine); + NTSTATUS InfOpenFile (PHINF InfHandle, PUNICODE_STRING FileName, diff --git a/reactos/subsys/system/usetup/makefile b/reactos/subsys/system/usetup/makefile index fab9b362eb4..f0752234478 100644 --- a/reactos/subsys/system/usetup/makefile +++ b/reactos/subsys/system/usetup/makefile @@ -1,4 +1,4 @@ -# $Id: makefile,v 1.12 2003/08/09 16:32:25 ekohl Exp $ +# $Id: makefile,v 1.13 2003/08/24 10:36:06 chorns Exp $ PATH_TO_TOP = ../../.. @@ -12,15 +12,15 @@ TARGET_APPTYPE = native TARGET_NAME = usetup -TARGET_SDKLIBS = vfatlib.a ntdll.a +TARGET_SDKLIBS = zlib.a vfatlib.a ntdll.a TARGET_INSTALLDIR = system32 -TARGET_CFLAGS = -D__NTAPP__ +TARGET_CFLAGS = -D__NTAPP__ -I$(PATH_TO_TOP)/lib/zlib -TARGET_OBJECTS = $(TARGET_NAME).o bootsup.o console.o drivesup.o filequeue.o \ - filesup.o format.o fslist.o infcache.o inicache.o partlist.o \ - progress.o registry.o +TARGET_OBJECTS = $(TARGET_NAME).o bootsup.o cabinet.o console.o drivesup.o \ + filequeue.o filesup.o format.o fslist.o infcache.o \ + inicache.o partlist.o progress.o registry.o include $(PATH_TO_TOP)/rules.mak diff --git a/reactos/subsys/system/usetup/usetup.c b/reactos/subsys/system/usetup/usetup.c index e1b7037d0a1..7e890784bb0 100644 --- a/reactos/subsys/system/usetup/usetup.c +++ b/reactos/subsys/system/usetup/usetup.c @@ -42,6 +42,7 @@ #include "registry.h" #include "format.h" #include "fslist.h" +#include "cabinet.h" #define NDEBUG #include @@ -1811,7 +1812,7 @@ CheckFileSystemPage(PINPUT_RECORD Ir) PartitionList->CurrentPartition->PartInfo[0].PartitionNumber); RtlCreateUnicodeString (&DestinationRootPath, PathBuffer); - DPRINT1 ("DestinationRootPath: %wZ\n", &DestinationRootPath); + DPRINT ("DestinationRootPath: %wZ\n", &DestinationRootPath); /* Set SystemRootPath */ RtlFreeUnicodeString (&SystemRootPath); @@ -1821,7 +1822,7 @@ CheckFileSystemPage(PINPUT_RECORD Ir) PartitionList->ActiveBootPartition->PartInfo[0].PartitionNumber); RtlCreateUnicodeString (&SystemRootPath, PathBuffer); - DPRINT1 ("SystemRootPath: %wZ\n", &SystemRootPath); + DPRINT ("SystemRootPath: %wZ\n", &SystemRootPath); while(TRUE) @@ -1977,8 +1978,8 @@ InstallDirectoryPage(PINPUT_RECORD Ir) } -static PAGE_NUMBER -PrepareCopyPage(PINPUT_RECORD Ir) +static BOOLEAN +PrepareCopyPageInfFile(HINF InfFile, PWCHAR SourceCabinet, PINPUT_RECORD Ir) { WCHAR PathBuffer[MAX_PATH]; INFCONTEXT FilesContext; @@ -1987,25 +1988,16 @@ PrepareCopyPage(PINPUT_RECORD Ir) PWCHAR KeyValue; ULONG Length; NTSTATUS Status; - PWCHAR FileKeyName; PWCHAR FileKeyValue; PWCHAR DirKeyName; PWCHAR DirKeyValue; - SetTextXY(6, 8, "Setup prepares your computer for copying the ReactOS files. "); - - - /* - * Build the file copy list - */ - SetStatusText(" Building the file copy list..."); - /* Search for the 'SourceFiles' section */ - if (!InfFindFirstLine (SetupInf, L"SourceFiles", NULL, &FilesContext)) + if (!InfFindFirstLine (InfFile, L"SourceFiles", NULL, &FilesContext)) { PopupError("Setup failed to find the 'SourceFiles' section\n" - "in TXTSETUP.SIF.\n", + "in TXTSETUP.SIF.\n", // FIXME "ENTER = Reboot computer"); while(TRUE) @@ -2014,26 +2006,7 @@ PrepareCopyPage(PINPUT_RECORD Ir) if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */ { - return(QUIT_PAGE); - } - } - } - - - /* Create the file queue */ - SetupFileQueue = SetupOpenFileQueue(); - if (SetupFileQueue == NULL) - { - PopupError("Setup failed to open the copy file queue.\n", - "ENTER = Reboot computer"); - - while(TRUE) - { - ConInKey(Ir); - - if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */ - { - return(QUIT_PAGE); + return(FALSE); } } } @@ -2045,12 +2018,15 @@ PrepareCopyPage(PINPUT_RECORD Ir) do { if (!InfGetData (&FilesContext, &FileKeyName, &FileKeyValue)) - break; + { + DPRINT("break\n"); + break; + } DPRINT ("FileKeyName: '%S' FileKeyValue: '%S'\n", FileKeyName, FileKeyValue); /* Lookup target directory */ - if (!InfFindFirstLine (SetupInf, L"Directories", FileKeyValue, &DirContext)) + if (!InfFindFirstLine (InfFile, L"Directories", FileKeyValue, &DirContext)) { /* FIXME: Handle error! */ DPRINT1("InfFindFirstLine() failed\n"); @@ -2065,6 +2041,7 @@ PrepareCopyPage(PINPUT_RECORD Ir) } if (!SetupQueueCopy(SetupFileQueue, + SourceCabinet, SourceRootPath.Buffer, L"\\reactos", FileKeyName, @@ -2079,7 +2056,6 @@ PrepareCopyPage(PINPUT_RECORD Ir) /* Create directories */ - SetStatusText(" Creating directories..."); /* * FIXME: @@ -2110,18 +2086,25 @@ PrepareCopyPage(PINPUT_RECORD Ir) if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */ { - return(QUIT_PAGE); + return(FALSE); } } } /* Search for the 'Directories' section */ - if (!InfFindFirstLine(SetupInf, L"Directories", NULL, &DirContext)) + if (!InfFindFirstLine(InfFile, L"Directories", NULL, &DirContext)) { - PopupError("Setup failed to find the 'Directories' section\n" - "in TXTSETUP.SIF.\n", - "ENTER = Reboot computer"); + if (SourceCabinet) + { + PopupError("Setup failed to find the 'Directories' section\n" + "in the cabinet.\n", "ENTER = Reboot computer"); + } + else + { + PopupError("Setup failed to find the 'Directories' section\n" + "in TXTSETUP.SIF.\n", "ENTER = Reboot computer"); + } while(TRUE) { @@ -2129,7 +2112,7 @@ PrepareCopyPage(PINPUT_RECORD Ir) if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */ { - return(QUIT_PAGE); + return(FALSE); } } } @@ -2138,7 +2121,10 @@ PrepareCopyPage(PINPUT_RECORD Ir) do { if (!InfGetData (&DirContext, NULL, &KeyValue)) - break; + { + DPRINT1("break\n"); + break; + } if (KeyValue[0] == L'\\' && KeyValue[1] != 0) { @@ -2171,7 +2157,7 @@ PrepareCopyPage(PINPUT_RECORD Ir) if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */ { - return(QUIT_PAGE); + return(FALSE); } } } @@ -2179,6 +2165,159 @@ PrepareCopyPage(PINPUT_RECORD Ir) } while (InfFindNextLine (&DirContext, &DirContext)); + return(TRUE); +} + + +static PAGE_NUMBER +PrepareCopyPage(PINPUT_RECORD Ir) +{ + INT CabStatus; + HINF InfHandle; + WCHAR PathBuffer[MAX_PATH]; + INFCONTEXT CabinetsContext; + ULONG InfFileSize; + PWCHAR KeyValue; + NTSTATUS Status; + ULONG ErrorLine; + PVOID InfFileData; + + SetTextXY(6, 8, "Setup prepares your computer for copying the ReactOS files. "); + + + /* + * Build the file copy list + */ + SetStatusText(" Building the file copy list..."); + + /* Create the file queue */ + SetupFileQueue = SetupOpenFileQueue(); + if (SetupFileQueue == NULL) + { + PopupError("Setup failed to open the copy file queue.\n", + "ENTER = Reboot computer"); + + while(TRUE) + { + ConInKey(Ir); + + if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */ + { + return(QUIT_PAGE); + } + } + } + + + if (!PrepareCopyPageInfFile(SetupInf, NULL, Ir)) + { + return(QUIT_PAGE); + } + + + /* Search for the 'Cabinets' section */ + if (!InfFindFirstLine (SetupInf, L"Cabinets", NULL, &CabinetsContext)) + { + PopupError("Setup failed to find the 'Cabinets' section\n" + "in TXTSETUP.SIF.\n", + "ENTER = Reboot computer"); + + while(TRUE) + { + ConInKey(Ir); + + if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */ + { + return(QUIT_PAGE); + } + } + } + + + /* + * Enumerate the directory values in the 'Cabinets' + * section and parse their inf files. + */ + do + { + if (!InfGetData (&CabinetsContext, NULL, &KeyValue)) + break; + + wcscpy(PathBuffer, SourcePath.Buffer); + wcscat(PathBuffer, L"\\"); + wcscat(PathBuffer, KeyValue); + + CabinetInitialize(); + CabinetSetEventHandlers(NULL, NULL, NULL); + CabinetSetCabinetName(PathBuffer); + + if (CabinetOpen() == CAB_STATUS_SUCCESS) + { + DPRINT("Cabinet %S\n", CabinetGetCabinetName()); + + InfFileData = CabinetGetCabinetReservedArea(&InfFileSize); + if (InfFileData == NULL) + { + PopupError("Cabinet has no setup script.\n", + "ENTER = Reboot computer"); + + while(TRUE) + { + ConInKey(Ir); + + if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */ + { + return(QUIT_PAGE); + } + } + } + } + else + { + DPRINT("Cannot open cabinet: %S.\n", CabinetGetCabinetName()); + + PopupError("Cabinet not found.\n", "ENTER = Reboot computer"); + + while(TRUE) + { + ConInKey(Ir); + + if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */ + { + return(QUIT_PAGE); + } + } + } + + Status = InfOpenBufferedFile(&InfHandle, + InfFileData, + InfFileSize, + &ErrorLine); + if (!NT_SUCCESS(Status)) + { + PopupError("Cabinet has no valid inf file.\n", + "ENTER = Reboot computer"); + + while(TRUE) + { + ConInKey(Ir); + + if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */ + { + return(QUIT_PAGE); + } + } + } + + CabinetCleanup(); + + if (!PrepareCopyPageInfFile(InfHandle, KeyValue, Ir)) + { + return(QUIT_PAGE); + } + } + while (InfFindNextLine (&CabinetsContext, &CabinetsContext)); + return(FILE_COPY_PAGE); } @@ -2388,7 +2527,6 @@ BootLoaderPage(PINPUT_RECORD Ir) SetStatusText(" Please wait..."); - if (PartitionList->ActiveBootPartition->PartInfo[0].PartitionType == PARTITION_ENTRY_UNUSED) { DPRINT1("Error: active partition invalid (unused)\n"); @@ -2405,7 +2543,6 @@ BootLoaderPage(PINPUT_RECORD Ir) } } } - if (PartitionList->ActiveBootPartition->PartInfo[0].PartitionType == 0x0A) { /* OS/2 boot manager partition */ diff --git a/reactos/tools/Makefile b/reactos/tools/Makefile index 3136e6d085e..c212e70fe79 100644 --- a/reactos/tools/Makefile +++ b/reactos/tools/Makefile @@ -15,7 +15,7 @@ TOOLS = \ CLEAN_FILES = $(TOOLS) -all: $(TOOLS) wmc_target cdmake_target mkhive_target rgenstat_target +all: $(TOOLS) zlib_target wmc_target cabman_target cdmake_target mkhive_target rgenstat_target buildno$(EXE_POSTFIX): buildno.c ../include/reactos/version.h $(HOST_CC) $(CFLAGS) -o buildno$(EXE_POSTFIX) buildno.c @@ -94,9 +94,15 @@ mkflpimg$(EXE_POSTFIX): mkflpimg.c endif +zlib_target: + $(MAKE) -C ../lib/zlib -f Makefile.host + wmc_target: $(MAKE) -C wmc wmc$(EXE_POSTFIX) +cabman_target: + $(MAKE) -C cabman cabman$(EXE_POSTFIX) + cdmake_target: $(MAKE) -C cdmake cdmake$(EXE_POSTFIX) diff --git a/reactos/tools/cabman/.cvsignore b/reactos/tools/cabman/.cvsignore new file mode 100755 index 00000000000..d34edb9f14b --- /dev/null +++ b/reactos/tools/cabman/.cvsignore @@ -0,0 +1,7 @@ +cabman +*.o +*.d +*.exe +*.coff +*.sym +*.map diff --git a/reactos/tools/cabman/cabinet.cxx b/reactos/tools/cabman/cabinet.cxx new file mode 100755 index 00000000000..1a9051f293b --- /dev/null +++ b/reactos/tools/cabman/cabinet.cxx @@ -0,0 +1,3194 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS cabinet manager + * FILE: tools/cabman/cabinet.cpp + * PURPOSE: Cabinet routines + * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) + * NOTES: Define CAB_READ_ONLY for read only version + * REVISIONS: + * CSH 21/03-2001 Created + * CSH 15/08-2003 Made it portable + * TODO: + * - Checksum of datablocks should be calculated + * - EXTRACT.EXE complains if a disk is created manually + * - Folders that are created manually and span disks will result in a damaged cabinet + */ +#include +#include +#include +#include "cabinet.h" +#include "raw.h" +#include "mszip.h" + + +#ifndef CAB_READ_ONLY + +#if 0 +#ifdef DBG + +void DumpBuffer(void* Buffer, unsigned long Size) +{ + FILEHANDLE FileHandle; + unsigned long BytesWritten; + + /* Create file, overwrite if it already exists */ + FileHandle = CreateFile("dump.bin", // Create this file + GENERIC_WRITE, // Open for writing + 0, // No sharing + NULL, // No security + CREATE_ALWAYS, // Create or overwrite + FILE_ATTRIBUTE_NORMAL, // Normal file + NULL); // No attribute template + if (FileHandle == INVALID_HANDLE_VALUE) { + DPRINT(MID_TRACE, ("ERROR OPENING '%d'.\n", (unsigned int)GetLastError())); + return; + } + + if (!WriteFile(FileHandle, Buffer, Size, &BytesWritten, NULL)) { + DPRINT(MID_TRACE, ("ERROR WRITING '%d'.\n", (unsigned int)GetLastError())); + } + + CloseFile(FileHandle); +} + +#endif /* DBG */ +#endif + +/* CCFDATAStorage */ + +CCFDATAStorage::CCFDATAStorage() +/* + * FUNCTION: Default constructor + */ +{ + FileCreated = false; +} + + +CCFDATAStorage::~CCFDATAStorage() +/* + * FUNCTION: Default destructor + */ +{ + ASSERT(!FileCreated); +} + + +unsigned long CCFDATAStorage::Create(char* FileName) +/* + * FUNCTION: Creates the file + * ARGUMENTS: + * FileName = Pointer to name of file + * RETURNS: + * Status of operation + */ +{ + ASSERT(!FileCreated); + +#if defined(WIN32) + if (GetTempPath(MAX_PATH, FullName) == 0) + return CAB_STATUS_CANNOT_CREATE; + + strcat(FullName, FileName); + + /* Create file, overwrite if it already exists */ + FileHandle = CreateFile(FullName, // Create this file + GENERIC_READ | GENERIC_WRITE, // Open for reading/writing + 0, // No sharing + NULL, // No security + CREATE_ALWAYS, // Create or overwrite + FILE_FLAG_SEQUENTIAL_SCAN | // Optimize for sequential scans + FILE_FLAG_DELETE_ON_CLOSE | // Delete file when closed + FILE_ATTRIBUTE_TEMPORARY, // Temporary file + NULL); // No attribute template + if (FileHandle == INVALID_HANDLE_VALUE) { + DPRINT(MID_TRACE, ("ERROR '%d'.\n", (unsigned int)GetLastError())); + return CAB_STATUS_CANNOT_CREATE; + } +#else /* !WIN32 */ + if (tmpnam(FullName) == NULL) + return CAB_STATUS_CANNOT_CREATE; + FileHandle = fopen(FullName, "w+b"); + if (FileHandle == NULL) { + DPRINT(MID_TRACE, ("ERROR '%d'.\n", (unsigned int)errno)); + return CAB_STATUS_CANNOT_CREATE; + } +#endif + + FileCreated = true; + + return CAB_STATUS_SUCCESS; +} + + +unsigned long CCFDATAStorage::Destroy() +/* + * FUNCTION: Destroys the file + * RETURNS: + * Status of operation + */ +{ + ASSERT(FileCreated); + + CloseFile(FileHandle); + + FileCreated = false; + + return CAB_STATUS_SUCCESS; +} + + +unsigned long CCFDATAStorage::Truncate() +/* + * FUNCTION: Truncate the scratch file to zero bytes + * RETURNS: + * Status of operation + */ +{ +#if defined(WIN32) + if (!SetFilePointer(FileHandle, 0, NULL, FILE_BEGIN)) + return CAB_STATUS_FAILURE; + if (!SetEndOfFile(FileHandle)) + return CAB_STATUS_FAILURE; +#else /* !WIN32 */ + fclose(FileHandle); + FileHandle = fopen(FullName, "w+b"); + if (FileHandle == NULL) { + DPRINT(MID_TRACE, ("ERROR '%d'.\n", (unsigned int)errno)); + return CAB_STATUS_FAILURE; + } +#endif + return CAB_STATUS_SUCCESS; +} + + +unsigned long CCFDATAStorage::Position() +/* + * FUNCTION: Returns current position in file + * RETURNS: + * Current position + */ +{ +#if defined(WIN32) + return SetFilePointer(FileHandle, 0, NULL, FILE_CURRENT); +#else + return (unsigned long)ftell(FileHandle); +#endif +} + + +unsigned long CCFDATAStorage::Seek(long Position) +/* + * FUNCTION: Seeks to an absolute position + * ARGUMENTS: + * Position = Absolute position to seek to + * RETURNS: + * Status of operation + */ +{ +#if defined(WIN32) + if (SetFilePointer(FileHandle, + Position, + NULL, + FILE_BEGIN) == 0xFFFFFFFF) + return CAB_STATUS_FAILURE; + else + return CAB_STATUS_SUCCESS; +#else + if (fseek(FileHandle, (off_t)Position, SEEK_SET) != 0) { + return CAB_STATUS_FAILURE; + } else { + return CAB_STATUS_SUCCESS; + } +#endif +} + + +unsigned long CCFDATAStorage::ReadBlock(PCFDATA Data, void* Buffer, unsigned long* BytesRead) +/* + * FUNCTION: Reads a CFDATA block from the file + * ARGUMENTS: + * Data = Pointer to CFDATA block for the buffer + * Buffer = Pointer to buffer to store data read + * BytesWritten = Pointer to buffer to write number of bytes read + * RETURNS: + * Status of operation + */ +{ +#if defined(WIN32) + if (!ReadFile(FileHandle, Buffer, Data->CompSize, BytesRead, NULL)) + return CAB_STATUS_CANNOT_READ; +#else + + *BytesRead = fread(Buffer, 1, Data->CompSize, FileHandle); + if (*BytesRead != Data->CompSize) { + return CAB_STATUS_CANNOT_READ; + } +#endif + return CAB_STATUS_SUCCESS; +} + + +unsigned long CCFDATAStorage::WriteBlock(PCFDATA Data, void* Buffer, unsigned long* BytesWritten) +/* + * FUNCTION: Writes a CFDATA block to the file + * ARGUMENTS: + * Data = Pointer to CFDATA block for the buffer + * Buffer = Pointer to buffer with data to write + * BytesWritten = Pointer to buffer to write number of bytes written + * RETURNS: + * Status of operation + */ +{ +#if defined(WIN32) + if (!WriteFile(FileHandle, Buffer, Data->CompSize, BytesWritten, NULL)) + return CAB_STATUS_CANNOT_WRITE; +#else + *BytesWritten = fwrite(Buffer, 1, Data->CompSize, FileHandle); + if (*BytesWritten != Data->CompSize) + return CAB_STATUS_CANNOT_WRITE; +#endif + return CAB_STATUS_SUCCESS; +} + +#endif /* CAB_READ_ONLY */ + + +/* CCabinet */ + +CCabinet::CCabinet() +/* + * FUNCTION: Default constructor + */ +{ + FileOpen = false; + strcpy(DestPath, ""); + strcpy(CabinetReservedFile, ""); + CabinetReservedFileBuffer = NULL; + CabinetReservedFileSize = 0; + + FolderListHead = NULL; + FolderListTail = NULL; + FileListHead = NULL; + FileListTail = NULL; + + Codec = new CRawCodec(); + CodecId = CAB_CODEC_RAW; + CodecSelected = true; + + OutputBuffer = NULL; + InputBuffer = NULL; + MaxDiskSize = 0; + BlockIsSplit = false; + ScratchFile = NULL; + + FolderUncompSize = 0; + BytesLeftInBlock = 0; + ReuseBlock = false; + CurrentDataNode = NULL; +} + + +CCabinet::~CCabinet() +/* + * FUNCTION: Default destructor + */ +{ + if (CabinetReservedFileBuffer != NULL) { + FreeMemory(CabinetReservedFileBuffer); + CabinetReservedFileBuffer = NULL; + CabinetReservedFileSize = 0; + } + + if (CodecSelected) + delete Codec; +} + +bool CCabinet::IsSeparator(char Char) +/* + * FUNCTION: Determines if a character is a separator + * ARGUMENTS: + * Char = Character to check + * RETURNS: + * Wether it is a separator + */ +{ + if ((Char == '\\') || (Char == '/')) { + return true; + } else { + return false; + } +} + +char* CCabinet::ConvertPath(char* Path, bool Allocate) +/* + * FUNCTION: Replaces \ or / with the one used be the host environment + * ARGUMENTS: + * Path = Pointer to string with pathname + * Allocate = Specifies wther to allocate memory for the new + * string or to change the existing buffer + * RETURNS: + * Pointer to new path + */ +{ + char *newpath; + int i; + + if (Allocate) { + newpath = strdup(Path); + } else { + newpath = Path; + } + + i = 0; + while (Path[i] != 0) { +#if defined(WIN32) + if (Path[i] == '/') { + newpath[i] = '\\'; + } else { +#else + if (Path[i] == '\\') { + newpath[i] = '/'; + } else { +#endif + newpath[i] = Path[i]; + } + i++; + } + + return(newpath); +} + + +char* CCabinet::GetFileName(char* Path) +/* + * FUNCTION: Returns a pointer to file name + * ARGUMENTS: + * Path = Pointer to string with pathname + * RETURNS: + * Pointer to filename + */ +{ + unsigned long i, j; + + j = i = (Path[0] ? (Path[1] == ':' ? 2 : 0) : 0); + + while (Path [i++]) { + if (IsSeparator(Path [i - 1])) j = i; + } + return Path + j; +} + + +void CCabinet::RemoveFileName(char* Path) +/* + * FUNCTION: Removes a file name from a path + * ARGUMENTS: + * Path = Pointer to string with path + */ +{ + char* FileName; + unsigned long i; + + i = (Path [0] ? (Path[1] == ':' ? 2 : 0) : 0); + FileName = GetFileName(Path + i); + + if ((FileName != (Path + i)) && (IsSeparator(FileName [-1]))) + FileName--; + if ((FileName == (Path + i)) && (IsSeparator(FileName [0]))) + FileName++; + FileName[0] = 0; +} + + +bool CCabinet::NormalizePath(char* Path, + unsigned long Length) +/* + * FUNCTION: Normalizes a path + * ARGUMENTS: + * Path = Pointer to string with pathname + * Length = Number of bytes in Path + * RETURNS: + * true if there was enough room in Path, or false + */ +{ + unsigned long n; + bool OK = true; + + if ((n = strlen(Path)) && + (!IsSeparator(Path[n - 1])) && + (OK = ((n + 1) < Length))) { + Path[n] = DIR_SEPARATOR_CHAR; + Path[n + 1] = 0; + } + return OK; +} + + +char* CCabinet::GetCabinetName() +/* + * FUNCTION: Returns pointer to cabinet file name + * RETURNS: + * Pointer to string with name of cabinet + */ +{ + return CabinetName; +} + + +void CCabinet::SetCabinetName(char* FileName) +/* + * FUNCTION: Sets cabinet file name + * ARGUMENTS: + * FileName = Pointer to string with name of cabinet + */ +{ + strcpy(CabinetName, FileName); +} + + +void CCabinet::SetDestinationPath(char* DestinationPath) +/* + * FUNCTION: Sets destination path + * ARGUMENTS: + * DestinationPath = Pointer to string with name of destination path + */ +{ + strcpy(DestPath, DestinationPath); + ConvertPath(DestPath, false); + if (strlen(DestPath) > 0) + NormalizePath(DestPath, MAX_PATH); +} + + +char* CCabinet::GetDestinationPath() +/* + * FUNCTION: Returns destination path + * RETURNS: + * Pointer to string with name of destination path + */ +{ + return DestPath; +} + + +bool CCabinet::SetCabinetReservedFile(char* FileName) +/* + * FUNCTION: Sets cabinet reserved file + * ARGUMENTS: + * FileName = Pointer to string with name of cabinet reserved file + */ +{ + FILEHANDLE FileHandle; + unsigned long BytesRead; + +#if defined(WIN32) + FileHandle = CreateFile(ConvertPath(FileName, true), // Open this file + GENERIC_READ, // Open for reading + FILE_SHARE_READ, // Share for reading + NULL, // No security + OPEN_EXISTING, // Existing file only + FILE_ATTRIBUTE_NORMAL, // Normal file + NULL); // No attribute template + if (FileHandle == INVALID_HANDLE_VALUE) { + DPRINT(MID_TRACE, ("Cannot open cabinet reserved file.\n")); + return false; + } +#else /* !WIN32 */ + FileHandle = fopen(ConvertPath(FileName, true), "rb"); + if (FileHandle == NULL) { + DPRINT(MID_TRACE, ("Cannot open cabinet reserved file.\n")); + return false; + } +#endif + + CabinetReservedFileSize = GetSizeOfFile(FileHandle); + if (CabinetReservedFileSize == (unsigned long)-1) { + DPRINT(MIN_TRACE, ("Cannot read from cabinet reserved file.\n")); + return false; + } + + if (CabinetReservedFileSize == 0) + { + CloseFile(FileHandle); + return false; + } + + CabinetReservedFileBuffer = AllocateMemory(CabinetReservedFileSize); + if (!CabinetReservedFileBuffer) { + CloseFile(FileHandle); + return false; + } + + if (!ReadFileData(FileHandle, CabinetReservedFileBuffer, CabinetReservedFileSize, &BytesRead)) { + CloseFile(FileHandle); + return false; + } + + CloseFile(FileHandle); + + strcpy(CabinetReservedFile, FileName); + + return true; +} + + +char* CCabinet::GetCabinetReservedFile() +/* + * FUNCTION: Returns cabionet reserved file + * RETURNS: + * Pointer to string with name of cabinet reserved file + */ +{ + return CabinetReservedFile; +} + + +unsigned long CCabinet::GetCurrentDiskNumber() +/* + * FUNCTION: Returns current disk number + * RETURNS: + * Current disk number + */ +{ + return CurrentDiskNumber; +} + + +unsigned long CCabinet::Open() +/* + * FUNCTION: Opens a cabinet file + * RETURNS: + * Status of operation + */ +{ + PCFFOLDER_NODE FolderNode; + unsigned long Status; + unsigned long Index; + + if (!FileOpen) { + unsigned long BytesRead; + unsigned long Size; + + OutputBuffer = AllocateMemory(CAB_BLOCKSIZE + 12); // This should be enough + if (!OutputBuffer) + return CAB_STATUS_NOMEMORY; + +#if defined(WIN32) + FileHandle = CreateFile(CabinetName, // Open this file + GENERIC_READ, // Open for reading + FILE_SHARE_READ, // Share for reading + NULL, // No security + OPEN_EXISTING, // Existing file only + FILE_ATTRIBUTE_NORMAL, // Normal file + NULL); // No attribute template + + if (FileHandle == INVALID_HANDLE_VALUE) { + DPRINT(MID_TRACE, ("Cannot open file.\n")); + return CAB_STATUS_CANNOT_OPEN; + } +#else /* !WIN32 */ + FileHandle = fopen(CabinetName, "rb"); + if (FileHandle == NULL) { + DPRINT(MID_TRACE, ("Cannot open file.\n")); + return CAB_STATUS_CANNOT_OPEN; + } +#endif + + FileOpen = true; + + /* Load CAB header */ + if ((Status = ReadBlock(&CABHeader, sizeof(CFHEADER), &BytesRead)) + != CAB_STATUS_SUCCESS) { + DPRINT(MIN_TRACE, ("Cannot read from file (%d).\n", (unsigned int)Status)); + return CAB_STATUS_INVALID_CAB; + } + + /* Check header */ + if ((BytesRead != sizeof(CFHEADER)) || + (CABHeader.Signature != CAB_SIGNATURE ) || + (CABHeader.Version != CAB_VERSION ) || + (CABHeader.FolderCount == 0 ) || + (CABHeader.FileCount == 0 ) || + (CABHeader.FileTableOffset < sizeof(CFHEADER))) { + CloseCabinet(); + DPRINT(MID_TRACE, ("File has invalid header.\n")); + return CAB_STATUS_INVALID_CAB; + } + + Size = 0; + + /* Read/skip any reserved bytes */ + if (CABHeader.Flags & CAB_FLAG_RESERVE) { + if ((Status = ReadBlock(&Size, sizeof(unsigned long), &BytesRead)) + != CAB_STATUS_SUCCESS) { + DPRINT(MIN_TRACE, ("Cannot read from file (%d).\n", (unsigned int)Status)); + return CAB_STATUS_INVALID_CAB; + } + CabinetReserved = Size & 0xFFFF; + FolderReserved = (Size >> 16) & 0xFF; + DataReserved = (Size >> 24) & 0xFF; + +#if defined(WIN32) + SetFilePointer(FileHandle, CabinetReserved, NULL, FILE_CURRENT); + if (GetLastError() != NO_ERROR) { + DPRINT(MIN_TRACE, ("SetFilePointer() failed.\n")); + return CAB_STATUS_FAILURE; + } +#else + if (fseek(FileHandle, (off_t)CabinetReserved, SEEK_CUR) != 0) { + DPRINT(MIN_TRACE, ("fseek() failed.\n")); + return CAB_STATUS_FAILURE; + } +#endif + } + + if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0) { + /* Read name of previous cabinet */ + Status = ReadString(CabinetPrev, 256); + if (Status != CAB_STATUS_SUCCESS) + return Status; + /* Read label of previous disk */ + Status = ReadString(DiskPrev, 256); + if (Status != CAB_STATUS_SUCCESS) + return Status; + } else { + strcpy(CabinetPrev, ""); + strcpy(DiskPrev, ""); + } + + if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0) { + /* Read name of next cabinet */ + Status = ReadString(CabinetNext, 256); + if (Status != CAB_STATUS_SUCCESS) + return Status; + /* Read label of next disk */ + Status = ReadString(DiskNext, 256); + if (Status != CAB_STATUS_SUCCESS) + return Status; + } else { + strcpy(CabinetNext, ""); + strcpy(DiskNext, ""); + } + + /* Read all folders */ + for (Index = 0; Index < CABHeader.FolderCount; Index++) { + FolderNode = NewFolderNode(); + if (!FolderNode) { + DPRINT(MIN_TRACE, ("Insufficient resources.\n")); + return CAB_STATUS_NOMEMORY; + } + + if (Index == 0) + FolderNode->UncompOffset = FolderUncompSize; + + FolderNode->Index = Index; + + if ((Status = ReadBlock(&FolderNode->Folder, + sizeof(CFFOLDER), &BytesRead)) != CAB_STATUS_SUCCESS) { + DPRINT(MIN_TRACE, ("Cannot read from file (%d).\n", (unsigned int)Status)); + return CAB_STATUS_INVALID_CAB; + } + } + + /* Read file entries */ + Status = ReadFileTable(); + if (Status != CAB_STATUS_SUCCESS) { + DPRINT(MIN_TRACE, ("ReadFileTable() failed (%d).\n", (unsigned int)Status)); + return Status; + } + + /* Read data blocks for all folders */ + FolderNode = FolderListHead; + while (FolderNode != NULL) { + Status = ReadDataBlocks(FolderNode); + if (Status != CAB_STATUS_SUCCESS) { + DPRINT(MIN_TRACE, ("ReadDataBlocks() failed (%d).\n", (unsigned int)Status)); + return Status; + } + FolderNode = FolderNode->Next; + } + } + return CAB_STATUS_SUCCESS; +} + + +void CCabinet::Close() +/* + * FUNCTION: Closes the cabinet file + */ +{ + if (FileOpen) { + CloseFile(FileHandle); + FileOpen = false; + } +} + + +unsigned long CCabinet::FindFirst(char* FileName, + PCAB_SEARCH Search) +/* + * FUNCTION: Finds the first file in the cabinet that matches a search criteria + * ARGUMENTS: + * FileName = Pointer to search criteria + * Search = Pointer to search structure + * RETURNS: + * Status of operation + */ +{ + RestartSearch = false; + strncpy(Search->Search, FileName, MAX_PATH); + Search->Next = FileListHead; + return FindNext(Search); +} + + +unsigned long CCabinet::FindNext(PCAB_SEARCH Search) +/* + * FUNCTION: Finds next file in the cabinet that matches a search criteria + * ARGUMENTS: + * Search = Pointer to search structure + * RETURNS: + * Status of operation + */ +{ + unsigned long Status; + + if (RestartSearch) { + Search->Next = FileListHead; + + /* Skip split files already extracted */ + while ((Search->Next) && + (Search->Next->File.FileControlID > CAB_FILE_MAX_FOLDER) && + (Search->Next->File.FileOffset <= LastFileOffset)) { + DPRINT(MAX_TRACE, ("Skipping file (%s) FileOffset (0x%X) LastFileOffset (0x%X).\n", + Search->Next->FileName, Search->Next->File.FileOffset, LastFileOffset)); + Search->Next = Search->Next->Next; + } + + RestartSearch = false; + } + + /* FIXME: Check search criteria */ + + if (!Search->Next) { + if (strlen(DiskNext) > 0) { + CloseCabinet(); + + SetCabinetName(CabinetNext); + + OnDiskChange(CabinetNext, DiskNext); + + Status = Open(); + if (Status != CAB_STATUS_SUCCESS) + return Status; + + Search->Next = FileListHead; + if (!Search->Next) + return CAB_STATUS_NOFILE; + } else + return CAB_STATUS_NOFILE; + } + + Search->File = &Search->Next->File; + Search->FileName = Search->Next->FileName; + Search->Next = Search->Next->Next; + return CAB_STATUS_SUCCESS; +} + + +unsigned long CCabinet::ExtractFile(char* FileName) +/* + * FUNCTION: Extracts a file from the cabinet + * ARGUMENTS: + * FileName = Pointer to buffer with name of file + * RETURNS + * Status of operation + */ +{ + unsigned long Size; + unsigned long Offset; + unsigned long BytesRead; + unsigned long BytesToRead; + unsigned long BytesWritten; + unsigned long BytesSkipped; + unsigned long BytesToWrite; + unsigned long TotalBytesRead; + unsigned long CurrentOffset; + unsigned char* Buffer; + unsigned char* CurrentBuffer; + FILEHANDLE DestFile; + PCFFILE_NODE File; + CFDATA CFData; + unsigned long Status; + bool Skip; +#if defined(WIN32) + FILETIME FileTime; +#endif + char DestName[MAX_PATH]; + char TempName[MAX_PATH]; + + Status = LocateFile(FileName, &File); + if (Status != CAB_STATUS_SUCCESS) { + DPRINT(MID_TRACE, ("Cannot locate file (%d).\n", (unsigned int)Status)); + return Status; + } + + LastFileOffset = File->File.FileOffset; + + switch (CurrentFolderNode->Folder.CompressionType & CAB_COMP_MASK) { + case CAB_COMP_NONE: + SelectCodec(CAB_CODEC_RAW); + break; + case CAB_COMP_MSZIP: + SelectCodec(CAB_CODEC_MSZIP); + break; + default: + return CAB_STATUS_UNSUPPCOMP; + } + + DPRINT(MAX_TRACE, ("Extracting file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n", + (unsigned int)File->File.FileOffset, + (unsigned int)File->File.FileSize, + (unsigned int)File->DataBlock->AbsoluteOffset, + (unsigned int)File->DataBlock->UncompOffset)); + + strcpy(DestName, DestPath); + strcat(DestName, FileName); + + /* Create destination file, fail if it already exists */ +#if defined(WIN32) + DestFile = CreateFile(DestName, // Create this file + GENERIC_WRITE, // Open for writing + 0, // No sharing + NULL, // No security + CREATE_NEW, // New file only + FILE_ATTRIBUTE_NORMAL, // Normal file + NULL); // No attribute template + if (DestFile == INVALID_HANDLE_VALUE) { + /* If file exists, ask to overwrite file */ + if (((Status = GetLastError()) == ERROR_FILE_EXISTS) && + (OnOverwrite(&File->File, FileName))) { + /* Create destination file, overwrite if it already exists */ + DestFile = CreateFile(DestName, // Create this file + GENERIC_WRITE, // Open for writing + 0, // No sharing + NULL, // No security + TRUNCATE_EXISTING, // Truncate the file + FILE_ATTRIBUTE_NORMAL, // Normal file + NULL); // No attribute template + if (DestFile == INVALID_HANDLE_VALUE) + return CAB_STATUS_CANNOT_CREATE; + } else { + if (Status == ERROR_FILE_EXISTS) + return CAB_STATUS_FILE_EXISTS; + else + return CAB_STATUS_CANNOT_CREATE; + } + } +#else /* !WIN32 */ + DestFile = fopen(DestName, "rb"); + if (DestFile != NULL) { + fclose(DestFile); + /* If file exists, ask to overwrite file */ + if (OnOverwrite(&File->File, FileName)) { + DestFile = fopen(DestName, "w+b"); + if (DestFile == NULL) { + return CAB_STATUS_CANNOT_CREATE; + } + } else { + return CAB_STATUS_FILE_EXISTS; + } + } else { + DestFile = fopen(DestName, "w+b"); + if (DestFile == NULL) { + return CAB_STATUS_CANNOT_CREATE; + } + } +#endif +#if defined(WIN32) + if (!DosDateTimeToFileTime(File->File.FileDate, File->File.FileTime, &FileTime)) { + CloseFile(DestFile); + DPRINT(MIN_TRACE, ("DosDateTimeToFileTime() failed (%d).\n", GetLastError())); + return CAB_STATUS_CANNOT_WRITE; + } + + SetFileTime(DestFile, NULL, &FileTime, NULL); +#else + //DPRINT(MIN_TRACE, ("FIXME: DosDateTimeToFileTime\n")); +#endif + SetAttributesOnFile(File); + + Buffer = (unsigned char*)AllocateMemory(CAB_BLOCKSIZE + 12); // This should be enough + if (!Buffer) { + CloseFile(DestFile); + DPRINT(MIN_TRACE, ("Insufficient memory.\n")); + return CAB_STATUS_NOMEMORY; + } + + /* Call OnExtract event handler */ + OnExtract(&File->File, FileName); + + /* Search to start of file */ +#if defined(WIN32) + Offset = SetFilePointer(FileHandle, + File->DataBlock->AbsoluteOffset, + NULL, + FILE_BEGIN); + if (GetLastError() != NO_ERROR) { + DPRINT(MIN_TRACE, ("SetFilePointer() failed.\n")); + return CAB_STATUS_INVALID_CAB; + } +#else + if (fseek(FileHandle, (off_t)File->DataBlock->AbsoluteOffset, SEEK_SET) != 0) { + DPRINT(MIN_TRACE, ("fseek() failed.\n")); + return CAB_STATUS_FAILURE; + } + Offset = ftell(FileHandle); +#endif + + Size = File->File.FileSize; + Offset = File->File.FileOffset; + CurrentOffset = File->DataBlock->UncompOffset; + + Skip = true; + + ReuseBlock = (CurrentDataNode == File->DataBlock); + if (Size > 0) { + do { + DPRINT(MAX_TRACE, ("CO (0x%X) ReuseBlock (%d) Offset (0x%X) Size (%d) BytesLeftInBlock (%d)\n", + File->DataBlock->UncompOffset, (unsigned int)ReuseBlock, Offset, Size, + BytesLeftInBlock)); + + if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock) || (BytesLeftInBlock <= 0)) { + + DPRINT(MAX_TRACE, ("Filling buffer. ReuseBlock (%d)\n", (unsigned int)ReuseBlock)); + + CurrentBuffer = Buffer; + TotalBytesRead = 0; + do { + DPRINT(MAX_TRACE, ("Size (%d bytes).\n", Size)); + + if (((Status = ReadBlock(&CFData, sizeof(CFDATA), &BytesRead)) != + CAB_STATUS_SUCCESS) || (BytesRead != sizeof(CFDATA))) { + CloseFile(DestFile); + FreeMemory(Buffer); + DPRINT(MIN_TRACE, ("Cannot read from file (%d).\n", (unsigned int)Status)); + return CAB_STATUS_INVALID_CAB; + } + + DPRINT(MAX_TRACE, ("Data block: Checksum (0x%X) CompSize (%d bytes) UncompSize (%d bytes)\n", + (unsigned int)CFData.Checksum, + (unsigned int)CFData.CompSize, + (unsigned int)CFData.UncompSize)); + + ASSERT(CFData.CompSize <= CAB_BLOCKSIZE + 12); + + BytesToRead = CFData.CompSize; + + DPRINT(MAX_TRACE, ("Read: (0x%X,0x%X).\n", + CurrentBuffer, Buffer)); + + if (((Status = ReadBlock(CurrentBuffer, BytesToRead, &BytesRead)) != + CAB_STATUS_SUCCESS) || (BytesToRead != BytesRead)) { + CloseFile(DestFile); + FreeMemory(Buffer); + DPRINT(MIN_TRACE, ("Cannot read from file (%d).\n", (unsigned int)Status)); + return CAB_STATUS_INVALID_CAB; + } + + /* FIXME: Does not work with files generated by makecab.exe */ +/* + if (CFData.Checksum != 0) { + unsigned long Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0); + if (Checksum != CFData.Checksum) { + CloseFile(DestFile); + FreeMemory(Buffer); + DPRINT(MIN_TRACE, ("Bad checksum (is 0x%X, should be 0x%X).\n", + Checksum, CFData.Checksum)); + return CAB_STATUS_INVALID_CAB; + } + } +*/ + TotalBytesRead += BytesRead; + + CurrentBuffer += BytesRead; + + if (CFData.UncompSize == 0) { + if (strlen(DiskNext) == 0) + return CAB_STATUS_NOFILE; + + /* CloseCabinet() will destroy all file entries so in case + FileName refers to the FileName field of a CFFOLDER_NODE + structure, we have to save a copy of the filename */ + strcpy(TempName, FileName); + + CloseCabinet(); + + SetCabinetName(CabinetNext); + + OnDiskChange(CabinetNext, DiskNext); + + Status = Open(); + if (Status != CAB_STATUS_SUCCESS) + return Status; + + /* The first data block of the file will not be + found as it is located in the previous file */ + Status = LocateFile(TempName, &File); + if (Status == CAB_STATUS_NOFILE) { + DPRINT(MID_TRACE, ("Cannot locate file (%d).\n", (unsigned int)Status)); + return Status; + } + + /* The file is continued in the first data block in the folder */ + File->DataBlock = CurrentFolderNode->DataListHead; + + /* Search to start of file */ +#if defined(WIN32) + (unsigned int)SetFilePointer(FileHandle, + File->DataBlock->AbsoluteOffset, + NULL, + FILE_BEGIN); + if (GetLastError() != NO_ERROR) { + DPRINT(MIN_TRACE, ("SetFilePointer() failed.\n")); + return CAB_STATUS_INVALID_CAB; + } +#else + if (fseek(FileHandle, (off_t)File->DataBlock->AbsoluteOffset, SEEK_SET) != 0) { + DPRINT(MIN_TRACE, ("fseek() failed.\n")); + return CAB_STATUS_INVALID_CAB; + } +#endif + + DPRINT(MAX_TRACE, ("Continuing extraction of file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n", + (unsigned int)File->File.FileOffset, + (unsigned int)File->File.FileSize, + (unsigned int)File->DataBlock->AbsoluteOffset, + (unsigned int)File->DataBlock->UncompOffset)); + + CurrentDataNode = File->DataBlock; + ReuseBlock = true; + + RestartSearch = true; + } + } while (CFData.UncompSize == 0); + + DPRINT(MAX_TRACE, ("TotalBytesRead (%d).\n", TotalBytesRead)); + + Status = Codec->Uncompress(OutputBuffer, Buffer, TotalBytesRead, &BytesToWrite); + if (Status != CS_SUCCESS) { + CloseFile(DestFile); + FreeMemory(Buffer); + DPRINT(MID_TRACE, ("Cannot uncompress block.\n")); + if (Status == CS_NOMEMORY) + return CAB_STATUS_NOMEMORY; + return CAB_STATUS_INVALID_CAB; + } + + if (BytesToWrite != CFData.UncompSize) { + DPRINT(MID_TRACE, ("BytesToWrite (%d) != CFData.UncompSize (%d)\n", + BytesToWrite, CFData.UncompSize)); + return CAB_STATUS_INVALID_CAB; + } + + BytesLeftInBlock = BytesToWrite; + } else { + DPRINT(MAX_TRACE, ("Using same buffer. ReuseBlock (%d)\n", (unsigned int)ReuseBlock)); + + BytesToWrite = BytesLeftInBlock; + + DPRINT(MAX_TRACE, ("Seeking to absolute offset 0x%X.\n", + CurrentDataNode->AbsoluteOffset + sizeof(CFDATA) + + CurrentDataNode->Data.CompSize)); + + if (((Status = ReadBlock(&CFData, sizeof(CFDATA), &BytesRead)) != + CAB_STATUS_SUCCESS) || (BytesRead != sizeof(CFDATA))) { + CloseFile(DestFile); + FreeMemory(Buffer); + DPRINT(MIN_TRACE, ("Cannot read from file (%d).\n", (unsigned int)Status)); + return CAB_STATUS_INVALID_CAB; + } + + DPRINT(MAX_TRACE, ("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n", + CFData.CompSize, CFData.UncompSize)); + + /* Go to next data block */ +#if defined(WIN32) + (unsigned int)SetFilePointer(FileHandle, + CurrentDataNode->AbsoluteOffset + sizeof(CFDATA) + + CurrentDataNode->Data.CompSize, + NULL, + FILE_BEGIN); + if (GetLastError() != NO_ERROR) { + DPRINT(MIN_TRACE, ("SetFilePointer() failed.\n")); + return CAB_STATUS_INVALID_CAB; + } +#else + if (fseek(FileHandle, (off_t)CurrentDataNode->AbsoluteOffset + sizeof(CFDATA) + + CurrentDataNode->Data.CompSize, SEEK_SET) != 0) { + DPRINT(MIN_TRACE, ("fseek() failed.\n")); + return CAB_STATUS_INVALID_CAB; + } +#endif + + ReuseBlock = false; + } + + if (Skip) + BytesSkipped = (Offset - CurrentOffset); + else + BytesSkipped = 0; + + BytesToWrite -= BytesSkipped; + + if (Size < BytesToWrite) + BytesToWrite = Size; + + DPRINT(MAX_TRACE, ("Offset (0x%X) CurrentOffset (0x%X) ToWrite (%d) Skipped (%d)(%d) Size (%d).\n", + (unsigned int)Offset, + (unsigned int)CurrentOffset, + (unsigned int)BytesToWrite, + (unsigned int)BytesSkipped, (unsigned int)Skip, + (unsigned int)Size)); + +#if defined(WIN32) + if (!WriteFile(DestFile, (void*)((unsigned long)OutputBuffer + BytesSkipped), + BytesToWrite, &BytesWritten, NULL) || + (BytesToWrite != BytesWritten)) { + DPRINT(MIN_TRACE, ("Status 0x%X.\n", GetLastError())); +#else + BytesWritten = BytesToWrite; + if (fwrite((void*)((unsigned long)OutputBuffer + BytesSkipped), + BytesToWrite, 1, DestFile) < 1) { +#endif + CloseFile(DestFile); + FreeMemory(Buffer); + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } + Size -= BytesToWrite; + + CurrentOffset += BytesToWrite; + + /* Don't skip any more bytes */ + Skip = false; + } while (Size > 0); + } + + CloseFile(DestFile); + + FreeMemory(Buffer); + + return CAB_STATUS_SUCCESS; +} + + +void CCabinet::SelectCodec(unsigned long Id) +/* + * FUNCTION: Selects codec engine to use + * ARGUMENTS: + * Id = Codec identifier + */ +{ + if (CodecSelected) { + if (Id == CodecId) + return; + + CodecSelected = false; + delete Codec; + } + + switch (Id) { + case CAB_CODEC_RAW: +#if 0 + Codec = new CRawCodec(); +#endif + break; + case CAB_CODEC_MSZIP: + Codec = new CMSZipCodec(); + break; + default: + return; + } + + CodecId = Id; + CodecSelected = true; +} + + +#ifndef CAB_READ_ONLY + +/* CAB write methods */ + +unsigned long CCabinet::NewCabinet() +/* + * FUNCTION: Creates a new cabinet + * RETURNS: + * Status of operation + */ +{ + unsigned long Status; + + CurrentDiskNumber = 0; + + OutputBuffer = AllocateMemory(CAB_BLOCKSIZE + 12); // This should be enough + InputBuffer = AllocateMemory(CAB_BLOCKSIZE + 12); // This should be enough + if ((!OutputBuffer) || (!InputBuffer)) { + DPRINT(MIN_TRACE, ("Insufficient memory.\n")); + return CAB_STATUS_NOMEMORY; + } + CurrentIBuffer = InputBuffer; + CurrentIBufferSize = 0; + + CABHeader.Signature = CAB_SIGNATURE; + CABHeader.Reserved1 = 0; // Not used + CABHeader.CabinetSize = 0; // Not yet known + CABHeader.Reserved2 = 0; // Not used + CABHeader.Reserved3 = 0; // Not used + CABHeader.Version = CAB_VERSION; + CABHeader.FolderCount = 0; // Not yet known + CABHeader.FileCount = 0; // Not yet known + CABHeader.Flags = 0; // Not yet known + // FIXME: Should be random + CABHeader.SetID = 0x534F; + CABHeader.CabinetNumber = 0; + + + TotalFolderSize = 0; + TotalFileSize = 0; + + DiskSize = sizeof(CFHEADER); + + InitCabinetHeader(); + + // NextFolderNumber is 0-based + NextFolderNumber = 0; + + CurrentFolderNode = NULL; + Status = NewFolder(); + if (Status != CAB_STATUS_SUCCESS) + return Status; + + CurrentFolderNode->Folder.DataOffset = DiskSize - TotalHeaderSize; + + ScratchFile = new CCFDATAStorage; + if (!ScratchFile) { + DPRINT(MIN_TRACE, ("Insufficient memory.\n")); + return CAB_STATUS_NOMEMORY; + } + + Status = ScratchFile->Create("~CAB.tmp"); + + CreateNewFolder = false; + + CreateNewDisk = false; + + PrevCabinetNumber = 0; + + return Status; +} + + +unsigned long CCabinet::NewDisk() +/* + * FUNCTION: Forces a new disk to be created + * RETURNS: + * Status of operation + */ +{ + // NextFolderNumber is 0-based + NextFolderNumber = 1; + + CreateNewDisk = false; + + DiskSize = sizeof(CFHEADER) + TotalFolderSize + TotalFileSize; + + InitCabinetHeader(); + + CurrentFolderNode->TotalFolderSize = 0; + + CurrentFolderNode->Folder.DataBlockCount = 0; + + return CAB_STATUS_SUCCESS; +} + + +unsigned long CCabinet::NewFolder() +/* + * FUNCTION: Forces a new folder to be created + * RETURNS: + * Status of operation + */ +{ + DPRINT(MAX_TRACE, ("Creating new folder.\n")); + + CurrentFolderNode = NewFolderNode(); + if (!CurrentFolderNode) { + DPRINT(MIN_TRACE, ("Insufficient memory.\n")); + return CAB_STATUS_NOMEMORY; + } + + switch (CodecId) { + case CAB_CODEC_RAW: + CurrentFolderNode->Folder.CompressionType = CAB_COMP_NONE; + break; + case CAB_CODEC_MSZIP: + CurrentFolderNode->Folder.CompressionType = CAB_COMP_MSZIP; + break; + default: + return CAB_STATUS_UNSUPPCOMP; + } + + /* FIXME: This won't work if no files are added to the new folder */ + + DiskSize += sizeof(CFFOLDER); + + TotalFolderSize += sizeof(CFFOLDER); + + NextFolderNumber++; + + CABHeader.FolderCount++; + + LastBlockStart = 0; + + return CAB_STATUS_SUCCESS; +} + + +unsigned long CCabinet::WriteFileToScratchStorage(PCFFILE_NODE FileNode) +/* + * FUNCTION: Writes a file to the scratch file + * ARGUMENTS: + * FileNode = Pointer to file node + * RETURNS: + * Status of operation + */ +{ + unsigned long BytesToRead; + unsigned long BytesRead; + unsigned long Status; + unsigned long Size; + + if (!ContinueFile) { + /* Try to open file */ +#if defined(WIN32) + SourceFile = CreateFile( + FileNode->FileName, // Open this file + GENERIC_READ, // Open for reading + FILE_SHARE_READ, // Share for reading + NULL, // No security + OPEN_EXISTING, // File must exist + FILE_ATTRIBUTE_NORMAL, // Normal file + NULL); // No attribute template + if (SourceFile == INVALID_HANDLE_VALUE) { + DPRINT(MID_TRACE, ("File not found (%s).\n", FileNode->FileName)); + return CAB_STATUS_NOFILE; + } +#else /* !WIN32 */ + SourceFile = fopen(FileNode->FileName, "rb"); + if (SourceFile == NULL) { + DPRINT(MID_TRACE, ("Cannot open cabinet reserved file.\n")); + return CAB_STATUS_NOFILE; + } +#endif + + if (CreateNewFolder) { + /* There is always a new folder after + a split file is completely stored */ + Status = NewFolder(); + if (Status != CAB_STATUS_SUCCESS) + return Status; + CreateNewFolder = false; + } + + /* Call OnAdd event handler */ + OnAdd(&FileNode->File, FileNode->FileName); + + TotalBytesLeft = FileNode->File.FileSize; + + FileNode->File.FileOffset = CurrentFolderNode->UncompOffset; + CurrentFolderNode->UncompOffset += TotalBytesLeft; + FileNode->File.FileControlID = NextFolderNumber - 1; + CurrentFolderNode->Commit = true; + PrevCabinetNumber = CurrentDiskNumber; + + Size = sizeof(CFFILE) + strlen(GetFileName(FileNode->FileName)) + 1; + CABHeader.FileTableOffset += Size; + TotalFileSize += Size; + DiskSize += Size; + } + + FileNode->Commit = true; + + if (TotalBytesLeft > 0) { + do { + if (TotalBytesLeft > (unsigned long)CAB_BLOCKSIZE - CurrentIBufferSize) + BytesToRead = CAB_BLOCKSIZE - CurrentIBufferSize; + else + BytesToRead = TotalBytesLeft; + + if (!ReadFileData(SourceFile, CurrentIBuffer, BytesToRead, &BytesRead) || (BytesToRead != BytesRead)) { + DPRINT(MIN_TRACE, ("Cannot read from file. BytesToRead (%d) BytesRead (%d) CurrentIBufferSize (%d).\n", + BytesToRead, BytesRead, CurrentIBufferSize)); + return CAB_STATUS_INVALID_CAB; + } + + (unsigned char*)CurrentIBuffer += BytesRead; + + CurrentIBufferSize += (unsigned short)BytesRead; + + if (CurrentIBufferSize == CAB_BLOCKSIZE) { + Status = WriteDataBlock(); + if (Status != CAB_STATUS_SUCCESS) + return Status; + } + TotalBytesLeft -= BytesRead; + } while ((TotalBytesLeft > 0) && (!CreateNewDisk)); + } + + if (TotalBytesLeft == 0) { + CloseFile(SourceFile); + FileNode->Delete = true; + + if (FileNode->File.FileControlID > CAB_FILE_MAX_FOLDER) { + FileNode->File.FileControlID = CAB_FILE_CONTINUED; + CurrentFolderNode->Delete = true; + + if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0)) { + Status = WriteDataBlock(); + if (Status != CAB_STATUS_SUCCESS) + return Status; + } + + CreateNewFolder = true; + } + } else { + if (FileNode->File.FileControlID <= CAB_FILE_MAX_FOLDER) + FileNode->File.FileControlID = CAB_FILE_SPLIT; + else + FileNode->File.FileControlID = CAB_FILE_PREV_NEXT; + } + + return CAB_STATUS_SUCCESS; +} + + +unsigned long CCabinet::WriteDisk(unsigned long MoreDisks) +/* + * FUNCTION: Forces the current disk to be written + * ARGUMENTS: + * MoreDisks = true if there is one or more disks after this disk + * RETURNS: + * Status of operation + */ +{ + PCFFILE_NODE FileNode; + unsigned long Status; + + ContinueFile = false; + FileNode = FileListHead; + while (FileNode != NULL) { + + Status = WriteFileToScratchStorage(FileNode); + if (Status != CAB_STATUS_SUCCESS) + return Status; + if (CreateNewDisk) { + /* A data block could span more than two + disks if MaxDiskSize is very small */ + while (CreateNewDisk) { + DPRINT(MAX_TRACE, ("Creating new disk.\n")); + CommitDisk(true); + CloseDisk(); + NewDisk(); + + ContinueFile = true; + CreateNewDisk = false; + + DPRINT(MAX_TRACE, ("First on new disk. CurrentIBufferSize (%d) CurrentOBufferSize (%d).\n", + CurrentIBufferSize, CurrentOBufferSize)); + + if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0)) { + Status = WriteDataBlock(); + if (Status != CAB_STATUS_SUCCESS) + return Status; + } + } + } else { + ContinueFile = false; + FileNode = FileNode->Next; + } + } + + if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0)) { + /* A data block could span more than two + disks if MaxDiskSize is very small */ + + ASSERT(CreateNewDisk == false); + + do { + if (CreateNewDisk) { + DPRINT(MID_TRACE, ("Creating new disk 2.\n")); + CommitDisk(true); + CloseDisk(); + NewDisk(); + CreateNewDisk = false; + + ASSERT(FileNode == FileListHead); + } + + if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0)) { + Status = WriteDataBlock(); + if (Status != CAB_STATUS_SUCCESS) + return Status; + } + } while (CreateNewDisk); + } + CommitDisk(MoreDisks); + + return CAB_STATUS_SUCCESS; +} + + +unsigned long CCabinet::CommitDisk(unsigned long MoreDisks) +/* + * FUNCTION: Commits the current disk + * ARGUMENTS: + * MoreDisks = true if there is one or more disks after this disk + * RETURNS: + * Status of operation + */ +{ + PCFFOLDER_NODE FolderNode; + unsigned long Status; + + OnCabinetName(CurrentDiskNumber, CabinetName); + + /* Create file, fail if it already exists */ +#if defined(WIN32) + FileHandle = CreateFile(CabinetName, // Create this file + GENERIC_WRITE, // Open for writing + 0, // No sharing + NULL, // No security + CREATE_NEW, // New file only + FILE_ATTRIBUTE_NORMAL, // Normal file + NULL); // No attribute template + if (FileHandle == INVALID_HANDLE_VALUE) { + unsigned long Status; + /* If file exists, ask to overwrite file */ + if (((Status = GetLastError()) == ERROR_FILE_EXISTS) && + (OnOverwrite(NULL, CabinetName))) { + + /* Create cabinet file, overwrite if it already exists */ + FileHandle = CreateFile(CabinetName, // Create this file + GENERIC_WRITE, // Open for writing + 0, // No sharing + NULL, // No security + TRUNCATE_EXISTING, // Truncate the file + FILE_ATTRIBUTE_NORMAL, // Normal file + NULL); // No attribute template + if (FileHandle == INVALID_HANDLE_VALUE) + return CAB_STATUS_CANNOT_CREATE; + } else { + if (Status == ERROR_FILE_EXISTS) + return CAB_STATUS_FILE_EXISTS; + else + return CAB_STATUS_CANNOT_CREATE; + } + } +#else /* !WIN32 */ + FileHandle = fopen(CabinetName, "rb"); + if (FileHandle != NULL) { + fclose(FileHandle); + /* If file exists, ask to overwrite file */ + if (OnOverwrite(NULL, CabinetName)) { + FileHandle = fopen(CabinetName, "w+b"); + if (FileHandle == NULL) { + return CAB_STATUS_CANNOT_CREATE; + } + } else { + return CAB_STATUS_FILE_EXISTS; + } + } else { + FileHandle = fopen(CabinetName, "w+b"); + if (FileHandle == NULL) { + return CAB_STATUS_CANNOT_CREATE; + } + } +#endif + + WriteCabinetHeader(MoreDisks); + + Status = WriteFolderEntries(); + if (Status != CAB_STATUS_SUCCESS) + return Status; + + /* Write file entries */ + WriteFileEntries(); + + /* Write data blocks */ + FolderNode = FolderListHead; + while (FolderNode != NULL) { + if (FolderNode->Commit) { + Status = CommitDataBlocks(FolderNode); + if (Status != CAB_STATUS_SUCCESS) + return Status; + /* Remove data blocks for folder */ + DestroyDataNodes(FolderNode); + } + FolderNode = FolderNode->Next; + } + + CloseFile(FileHandle); + + ScratchFile->Truncate(); + + return CAB_STATUS_SUCCESS; +} + + +unsigned long CCabinet::CloseDisk() +/* + * FUNCTION: Closes the current disk + * RETURNS: + * Status of operation + */ +{ + DestroyDeletedFileNodes(); + + /* Destroy folder nodes that are completely stored */ + DestroyDeletedFolderNodes(); + + CurrentDiskNumber++; + + return CAB_STATUS_SUCCESS; +} + + +unsigned long CCabinet::CloseCabinet() +/* + * FUNCTION: Closes the current cabinet + * RETURNS: + * Status of operation + */ +{ + PCFFOLDER_NODE PrevNode; + PCFFOLDER_NODE NextNode; + unsigned long Status; + + DestroyFileNodes(); + + DestroyFolderNodes(); + + if (InputBuffer) { + FreeMemory(InputBuffer); + InputBuffer = NULL; + } + + if (OutputBuffer) { + FreeMemory(OutputBuffer); + OutputBuffer = NULL; + } + + Close(); + + if (ScratchFile) { + Status = ScratchFile->Destroy(); + delete ScratchFile; + return Status; + } + + return CAB_STATUS_SUCCESS; +} + + +unsigned long CCabinet::AddFile(char* FileName) +/* + * FUNCTION: Adds a file to the current disk + * ARGUMENTS: + * FileName = Pointer to string with file name (full path) + * RETURNS: + * Status of operation + */ +{ + FILEHANDLE SrcFile; + PCFFILE_NODE FileNode; + + FileNode = NewFileNode(); + if (!FileNode) { + DPRINT(MIN_TRACE, ("Insufficient memory.\n")); + return CAB_STATUS_NOMEMORY; + } + + FileNode->FolderNode = CurrentFolderNode; + + FileNode->FileName = (char*)AllocateMemory(strlen(FileName) + 1); + strcpy(FileNode->FileName, FileName); + ConvertPath(FileNode->FileName, false); + + /* Try to open file */ +#if defined(WIN32) + SrcFile = CreateFile( + FileNode->FileName, // Open this file + GENERIC_READ, // Open for reading + FILE_SHARE_READ, // Share for reading + NULL, // No security + OPEN_EXISTING, // File must exist + FILE_ATTRIBUTE_NORMAL, // Normal file + NULL); // No attribute template + if (SrcFile == INVALID_HANDLE_VALUE) { + DPRINT(MID_TRACE, ("File not found (%s).\n", FileNode->FileName)); + return CAB_STATUS_CANNOT_OPEN; + } +#else /* !WIN32 */ + SrcFile = fopen(FileNode->FileName, "rb"); + if (SrcFile == NULL) { + DPRINT(MID_TRACE, ("File not found (%s).\n", FileNode->FileName)); + return CAB_STATUS_CANNOT_OPEN; + } +#endif + + /* FIXME: Check for and handle large files (>= 2GB) */ + FileNode->File.FileSize = GetSizeOfFile(SrcFile); + if (FileNode->File.FileSize == (unsigned long)-1) { + DPRINT(MIN_TRACE, ("Cannot read from file.\n")); + return CAB_STATUS_CANNOT_READ; + } + + GetFileTimes(SrcFile, FileNode); + + GetAttributesOnFile(FileNode); + + CloseFile(SrcFile); + + return CAB_STATUS_SUCCESS; +} + + +void CCabinet::SetMaxDiskSize(unsigned long Size) +/* + * FUNCTION: Sets the maximum size of the current disk + * ARGUMENTS: + * Size = Maximum size of current disk (0 means no maximum size) + */ +{ + MaxDiskSize = Size; +} + +#endif /* CAB_READ_ONLY */ + + +/* Default event handlers */ + +bool CCabinet::OnOverwrite(PCFFILE File, + char* FileName) +/* + * FUNCTION: Called when extracting a file and it already exists + * ARGUMENTS: + * File = Pointer to CFFILE for file being extracted + * FileName = Pointer to buffer with name of file (full path) + * RETURNS + * true if the file should be overwritten, false if not + */ +{ + return false; +} + + +void CCabinet::OnExtract(PCFFILE File, + char* FileName) +/* + * FUNCTION: Called just before extracting a file + * ARGUMENTS: + * File = Pointer to CFFILE for file being extracted + * FileName = Pointer to buffer with name of file (full path) + */ +{ +} + + +void CCabinet::OnDiskChange(char* CabinetName, + char* DiskLabel) +/* + * FUNCTION: Called when a new disk is to be processed + * ARGUMENTS: + * CabinetName = Pointer to buffer with name of cabinet + * DiskLabel = Pointer to buffer with label of disk + */ +{ +} + + +#ifndef CAB_READ_ONLY + +void CCabinet::OnAdd(PCFFILE File, + char* FileName) +/* + * FUNCTION: Called just before adding a file to a cabinet + * ARGUMENTS: + * File = Pointer to CFFILE for file being added + * FileName = Pointer to buffer with name of file (full path) + */ +{ +} + + +bool CCabinet::OnDiskLabel(unsigned long Number, char* Label) +/* + * FUNCTION: Called when a disk needs a label + * ARGUMENTS: + * Number = Cabinet number that needs a label + * Label = Pointer to buffer to place label of disk + * RETURNS: + * true if a disk label was returned, false if not + */ +{ + return false; +} + + +bool CCabinet::OnCabinetName(unsigned long Number, char* Name) +/* + * FUNCTION: Called when a cabinet needs a name + * ARGUMENTS: + * Number = Disk number that needs a name + * Name = Pointer to buffer to place name of cabinet + * RETURNS: + * true if a cabinet name was returned, false if not + */ +{ + return false; +} + +#endif /* CAB_READ_ONLY */ + +PCFFOLDER_NODE CCabinet::LocateFolderNode(unsigned long Index) +/* + * FUNCTION: Locates a folder node + * ARGUMENTS: + * Index = Folder index + * RETURNS: + * Pointer to folder node or NULL if the folder node was not found + */ +{ + PCFFOLDER_NODE Node; + + switch (Index) { + case CAB_FILE_SPLIT: + return FolderListTail; + + case CAB_FILE_CONTINUED: + case CAB_FILE_PREV_NEXT: + return FolderListHead; + } + + Node = FolderListHead; + while (Node != NULL) { + if (Node->Index == Index) + return Node; + Node = Node->Next; + } + return NULL; +} + + +unsigned long CCabinet::GetAbsoluteOffset(PCFFILE_NODE File) +/* + * FUNCTION: Returns the absolute offset of a file + * ARGUMENTS: + * File = Pointer to CFFILE_NODE structure for file + * RETURNS: + * Status of operation + */ +{ + PCFDATA_NODE Node; + + DPRINT(MAX_TRACE, ("FileName '%s' FileOffset (0x%X) FileSize (%d).\n", + (char*)File->FileName, + (unsigned int)File->File.FileOffset, + (unsigned int)File->File.FileSize)); + + Node = CurrentFolderNode->DataListHead; + while (Node != NULL) { + + DPRINT(MAX_TRACE, ("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%d).\n", + (unsigned int)Node->UncompOffset, + (unsigned int)Node->UncompOffset + Node->Data.UncompSize, + (unsigned int)Node->Data.UncompSize)); + + /* Node->Data.UncompSize will be 0 if the block is split + (ie. it is the last block in this cabinet) */ + if ((Node->Data.UncompSize == 0) || + ((File->File.FileOffset >= Node->UncompOffset) && + (File->File.FileOffset < Node->UncompOffset + + Node->Data.UncompSize))) { + File->DataBlock = Node; + return CAB_STATUS_SUCCESS; + } + + Node = Node->Next; + } + return CAB_STATUS_INVALID_CAB; +} + + +unsigned long CCabinet::LocateFile(char* FileName, + PCFFILE_NODE *File) +/* + * FUNCTION: Locates a file in the cabinet + * ARGUMENTS: + * FileName = Pointer to string with name of file to locate + * File = Address of pointer to CFFILE_NODE structure to fill + * RETURNS: + * Status of operation + * NOTES: + * Current folder is set to the folder of the file + */ +{ + PCFFILE_NODE Node; + unsigned long Status; + + DPRINT(MAX_TRACE, ("FileName '%s'\n", FileName)); + + Node = FileListHead; + while (Node != NULL) { + if (strcasecmp(FileName, Node->FileName) == 0) { + + CurrentFolderNode = LocateFolderNode(Node->File.FileControlID); + if (!CurrentFolderNode) { + DPRINT(MID_TRACE, ("Folder with index number (%d) not found.\n", + (unsigned int)Node->File.FileControlID)); + return CAB_STATUS_INVALID_CAB; + } + + if (Node->DataBlock == NULL) { + Status = GetAbsoluteOffset(Node); + } else + Status = CAB_STATUS_SUCCESS; + *File = Node; + return Status; + } + Node = Node->Next; + } + return CAB_STATUS_NOFILE; +} + + +unsigned long CCabinet::ReadString(char* String, unsigned long MaxLength) +/* + * FUNCTION: Reads a NULL-terminated string from the cabinet + * ARGUMENTS: + * String = Pointer to buffer to place string + * MaxLength = Maximum length of string + * RETURNS: + * Status of operation + */ +{ + unsigned long BytesRead; + unsigned long Offset; + unsigned long Status; + unsigned long Size; + bool Found; + + Offset = 0; + Found = false; + do { + Size = ((Offset + 32) <= MaxLength)? 32 : MaxLength - Offset; + + if (Size == 0) { + DPRINT(MIN_TRACE, ("Too long a filename.\n")); + return CAB_STATUS_INVALID_CAB; + } + + Status = ReadBlock(&String[Offset], Size, &BytesRead); + if ((Status != CAB_STATUS_SUCCESS) || (BytesRead != Size)) { + DPRINT(MIN_TRACE, ("Cannot read from file (%d).\n", (unsigned int)Status)); + return CAB_STATUS_INVALID_CAB; + } + + for (Size = Offset; Size < Offset + BytesRead; Size++) { + if (String[Size] == '\0') { + Found = true; + break; + } + } + + Offset += BytesRead; + + } while (!Found); + + /* Back up some bytes */ + Size = (BytesRead - Size) - 1; +#if defined(WIN32) + (unsigned int)SetFilePointer(FileHandle, + -(long)Size, + NULL, + FILE_CURRENT); + if (GetLastError() != NO_ERROR) { + DPRINT(MIN_TRACE, ("SetFilePointer() failed.\n")); + return CAB_STATUS_INVALID_CAB; + } +#else + if (fseek(FileHandle, (off_t)(-(long)Size), SEEK_CUR) != 0) { + DPRINT(MIN_TRACE, ("fseek() failed.\n")); + return CAB_STATUS_INVALID_CAB; + } +#endif + return CAB_STATUS_SUCCESS; +} + + +unsigned long CCabinet::ReadFileTable() +/* + * FUNCTION: Reads the file table from the cabinet file + * RETURNS: + * Status of operation + */ +{ + unsigned long i; + unsigned long Status; + unsigned long BytesRead; + PCFFILE_NODE File; + + DPRINT(MAX_TRACE, ("Reading file table at absolute offset (0x%X).\n", + CABHeader.FileTableOffset)); + + /* Seek to file table */ +#if defined(WIN32) + (unsigned int)SetFilePointer(FileHandle, + CABHeader.FileTableOffset, + NULL, + FILE_BEGIN); + if (GetLastError() != NO_ERROR) { + DPRINT(MIN_TRACE, ("SetFilePointer() failed.\n")); + return CAB_STATUS_INVALID_CAB; + } +#else + if (fseek(FileHandle, (off_t)CABHeader.FileTableOffset, SEEK_SET) != 0) { + DPRINT(MIN_TRACE, ("fseek() failed.\n")); + return CAB_STATUS_INVALID_CAB; + } +#endif + + for (i = 0; i < CABHeader.FileCount; i++) { + File = NewFileNode(); + if (!File) { + DPRINT(MIN_TRACE, ("Insufficient memory.\n")); + return CAB_STATUS_NOMEMORY; + } + + if ((Status = ReadBlock(&File->File, sizeof(CFFILE), + &BytesRead)) != CAB_STATUS_SUCCESS) { + DPRINT(MIN_TRACE, ("Cannot read from file (%d).\n", (unsigned int)Status)); + return CAB_STATUS_INVALID_CAB; + } + + File->FileName = (char*)AllocateMemory(MAX_PATH); + if (!File->FileName) { + DPRINT(MIN_TRACE, ("Insufficient memory.\n")); + return CAB_STATUS_NOMEMORY; + } + + /* Read file name */ + Status = ReadString(File->FileName, MAX_PATH); + if (Status != CAB_STATUS_SUCCESS) + return Status; + + DPRINT(MAX_TRACE, ("Found file '%s' at uncompressed offset (0x%X). Size (%d bytes) ControlId (0x%X).\n", + (char*)File->FileName, + (unsigned int)File->File.FileOffset, + (unsigned int)File->File.FileSize, + (unsigned int)File->File.FileControlID)); + + } + return CAB_STATUS_SUCCESS; +} + + +unsigned long CCabinet::ReadDataBlocks(PCFFOLDER_NODE FolderNode) +/* + * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file + * ARGUMENTS: + * FolderNode = Pointer to CFFOLDER_NODE structure for folder + * RETURNS: + * Status of operation + */ +{ + unsigned long AbsoluteOffset; + unsigned long UncompOffset; + PCFDATA_NODE Node; + unsigned long BytesRead; + unsigned long Status; + unsigned long i; + + DPRINT(MAX_TRACE, ("Reading data blocks for folder (%d) at absolute offset (0x%X).\n", + FolderNode->Index, FolderNode->Folder.DataOffset)); + + AbsoluteOffset = FolderNode->Folder.DataOffset; + UncompOffset = FolderNode->UncompOffset; + + for (i = 0; i < FolderNode->Folder.DataBlockCount; i++) { + Node = NewDataNode(FolderNode); + if (!Node) { + DPRINT(MIN_TRACE, ("Insufficient memory.\n")); + return CAB_STATUS_NOMEMORY; + } + + /* Seek to data block */ +#if defined(WIN32) + (unsigned int)SetFilePointer(FileHandle, + AbsoluteOffset, + NULL, + FILE_BEGIN); + if (GetLastError() != NO_ERROR) { + DPRINT(MIN_TRACE, ("SetFilePointer() failed.\n")); + return CAB_STATUS_INVALID_CAB; + } +#else + if (fseek(FileHandle, (off_t)AbsoluteOffset, SEEK_SET) != 0) { + DPRINT(MIN_TRACE, ("fseek() failed.\n")); + return CAB_STATUS_INVALID_CAB; + } +#endif + + if ((Status = ReadBlock(&Node->Data, sizeof(CFDATA), + &BytesRead)) != CAB_STATUS_SUCCESS) { + DPRINT(MIN_TRACE, ("Cannot read from file (%d).\n", (unsigned int)Status)); + return CAB_STATUS_INVALID_CAB; + } + + DPRINT(MAX_TRACE, ("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%d) UncompSize (%d).\n", + (unsigned int)AbsoluteOffset, + (unsigned int)UncompOffset, + (unsigned int)Node->Data.Checksum, + (unsigned int)Node->Data.CompSize, + (unsigned int)Node->Data.UncompSize)); + + Node->AbsoluteOffset = AbsoluteOffset; + Node->UncompOffset = UncompOffset; + + AbsoluteOffset += sizeof(CFDATA) + Node->Data.CompSize; + UncompOffset += Node->Data.UncompSize; + } + + FolderUncompSize = UncompOffset; + + return CAB_STATUS_SUCCESS; +} + + +PCFFOLDER_NODE CCabinet::NewFolderNode() +/* + * FUNCTION: Creates a new folder node + * RETURNS: + * Pointer to node if there was enough free memory available, otherwise NULL + */ +{ + PCFFOLDER_NODE Node; + + Node = (PCFFOLDER_NODE)AllocateMemory(sizeof(CFFOLDER_NODE)); + if (!Node) + return NULL; + + memset(Node, 0, sizeof(CFFOLDER_NODE)); + + Node->Folder.CompressionType = CAB_COMP_NONE; + + Node->Prev = FolderListTail; + + if (FolderListTail != NULL) { + FolderListTail->Next = Node; + } else { + FolderListHead = Node; + } + FolderListTail = Node; + + return Node; +} + + +PCFFILE_NODE CCabinet::NewFileNode() +/* + * FUNCTION: Creates a new file node + * ARGUMENTS: + * FolderNode = Pointer to folder node to bind file to + * RETURNS: + * Pointer to node if there was enough free memory available, otherwise NULL + */ +{ + PCFFILE_NODE Node; + + Node = (PCFFILE_NODE)AllocateMemory(sizeof(CFFILE_NODE)); + if (!Node) + return NULL; + + memset(Node, 0, sizeof(CFFILE_NODE)); + + Node->Prev = FileListTail; + + if (FileListTail != NULL) { + FileListTail->Next = Node; + } else { + FileListHead = Node; + } + FileListTail = Node; + + return Node; +} + + +PCFDATA_NODE CCabinet::NewDataNode(PCFFOLDER_NODE FolderNode) +/* + * FUNCTION: Creates a new data block node + * ARGUMENTS: + * FolderNode = Pointer to folder node to bind data block to + * RETURNS: + * Pointer to node if there was enough free memory available, otherwise NULL + */ +{ + PCFDATA_NODE Node; + + Node = (PCFDATA_NODE)AllocateMemory(sizeof(CFDATA_NODE)); + if (!Node) + return NULL; + + memset(Node, 0, sizeof(CFDATA_NODE)); + + Node->Prev = FolderNode->DataListTail; + + if (FolderNode->DataListTail != NULL) { + FolderNode->DataListTail->Next = Node; + } else { + FolderNode->DataListHead = Node; + } + FolderNode->DataListTail = Node; + + return Node; +} + + +void CCabinet::DestroyDataNodes(PCFFOLDER_NODE FolderNode) +/* + * FUNCTION: Destroys data block nodes bound to a folder node + * ARGUMENTS: + * FolderNode = Pointer to folder node + */ +{ + PCFDATA_NODE PrevNode; + PCFDATA_NODE NextNode; + + NextNode = FolderNode->DataListHead; + while (NextNode != NULL) { + PrevNode = NextNode->Next; + FreeMemory(NextNode); + NextNode = PrevNode; + } + FolderNode->DataListHead = NULL; + FolderNode->DataListTail = NULL; +} + + +void CCabinet::DestroyFileNodes() +/* + * FUNCTION: Destroys file nodes + * ARGUMENTS: + * FolderNode = Pointer to folder node + */ +{ + PCFFILE_NODE PrevNode; + PCFFILE_NODE NextNode; + + NextNode = FileListHead; + while (NextNode != NULL) { + PrevNode = NextNode->Next; + if (NextNode->FileName) + FreeMemory(NextNode->FileName); + FreeMemory(NextNode); + NextNode = PrevNode; + } + FileListHead = NULL; + FileListTail = NULL; +} + + +void CCabinet::DestroyDeletedFileNodes() +/* + * FUNCTION: Destroys file nodes that are marked for deletion + */ +{ + PCFFILE_NODE CurNode; + PCFFILE_NODE NextNode; + + CurNode = FileListHead; + while (CurNode != NULL) { + NextNode = CurNode->Next; + + if (CurNode->Delete) { + if (CurNode->Prev != NULL) { + CurNode->Prev->Next = CurNode->Next; + } else { + FileListHead = CurNode->Next; + if (FileListHead) + FileListHead->Prev = NULL; + } + + if (CurNode->Next != NULL) { + CurNode->Next->Prev = CurNode->Prev; + } else { + FileListTail = CurNode->Prev; + if (FileListTail) + FileListTail->Next = NULL; + } + + DPRINT(MAX_TRACE, ("Deleting file: '%s'\n", CurNode->FileName)); + + TotalFileSize -= (sizeof(CFFILE) + strlen(GetFileName(CurNode->FileName)) + 1); + + if (CurNode->FileName) + FreeMemory(CurNode->FileName); + FreeMemory(CurNode); + } + CurNode = NextNode; + } +} + + +void CCabinet::DestroyFolderNodes() +/* + * FUNCTION: Destroys folder nodes + */ +{ + PCFFOLDER_NODE PrevNode; + PCFFOLDER_NODE NextNode; + + NextNode = FolderListHead; + while (NextNode != NULL) { + PrevNode = NextNode->Next; + DestroyDataNodes(NextNode); + FreeMemory(NextNode); + NextNode = PrevNode; + } + FolderListHead = NULL; + FolderListTail = NULL; +} + + +void CCabinet::DestroyDeletedFolderNodes() +/* + * FUNCTION: Destroys folder nodes that are marked for deletion + */ +{ + PCFFOLDER_NODE CurNode; + PCFFOLDER_NODE NextNode; + + CurNode = FolderListHead; + while (CurNode != NULL) { + NextNode = CurNode->Next; + + if (CurNode->Delete) { + if (CurNode->Prev != NULL) { + CurNode->Prev->Next = CurNode->Next; + } else { + FolderListHead = CurNode->Next; + if (FolderListHead) + FolderListHead->Prev = NULL; + } + + if (CurNode->Next != NULL) { + CurNode->Next->Prev = CurNode->Prev; + } else { + FolderListTail = CurNode->Prev; + if (FolderListTail) + FolderListTail->Next = NULL; + } + + DestroyDataNodes(CurNode); + FreeMemory(CurNode); + + TotalFolderSize -= sizeof(CFFOLDER); + } + CurNode = NextNode; + } +} + + +unsigned long CCabinet::ComputeChecksum(void* Buffer, + unsigned int Size, + unsigned long Seed) +/* + * FUNCTION: Computes checksum for data block + * ARGUMENTS: + * Buffer = Pointer to data buffer + * Size = Length of data buffer + * Seed = Previously computed checksum + * RETURNS: + * Checksum of buffer + */ +{ + int UlongCount; // Number of ULONGs in block + unsigned long Checksum; // Checksum accumulator + unsigned char* pb; + unsigned long ul; + + /* FIXME: Doesn't seem to be correct. EXTRACT.EXE + won't accept checksums computed by this routine */ + + DPRINT(MIN_TRACE, ("Checksumming buffer (0x%X) Size (%d)\n", (unsigned int)Buffer, Size)); + + UlongCount = Size / 4; // Number of ULONGs + Checksum = Seed; // Init checksum + pb = (unsigned char*)Buffer; // Start at front of data block + + /* Checksum integral multiple of ULONGs */ + while (UlongCount-- > 0) { + /* NOTE: Build unsigned long in big/little-endian independent manner */ + ul = *pb++; // Get low-order byte + ul |= (((unsigned long)(*pb++)) << 8); // Add 2nd byte + ul |= (((unsigned long)(*pb++)) << 16); // Add 3nd byte + ul |= (((unsigned long)(*pb++)) << 24); // Add 4th byte + + Checksum ^= ul; // Update checksum + } + + /* Checksum remainder bytes */ + ul = 0; + switch (Size % 4) { + case 3: + ul |= (((unsigned long)(*pb++)) << 16); // Add 3rd byte + case 2: + ul |= (((unsigned long)(*pb++)) << 8); // Add 2nd byte + case 1: + ul |= *pb++; // Get low-order byte + default: + break; + } + Checksum ^= ul; // Update checksum + + /* Return computed checksum */ + return Checksum; +} + + +unsigned long CCabinet::ReadBlock(void* Buffer, + unsigned long Size, + unsigned long* BytesRead) +/* + * FUNCTION: Read a block of data from file + * ARGUMENTS: + * Buffer = Pointer to data buffer + * Size = Length of data buffer + * BytesRead = Pointer to unsigned long that on return will contain + * number of bytes read + * RETURNS: + * Status of operation + */ +{ + if (!ReadFileData(FileHandle, Buffer, Size, BytesRead)) + return CAB_STATUS_INVALID_CAB; + return CAB_STATUS_SUCCESS; +} + +#ifndef CAB_READ_ONLY + +unsigned long CCabinet::InitCabinetHeader() +/* + * FUNCTION: Initializes cabinet header and optional fields + * RETURNS: + * Status of operation + */ +{ + PCFFOLDER_NODE FolderNode; + PCFFILE_NODE FileNode; + unsigned long TotalSize; + unsigned long Size; + + CABHeader.FileTableOffset = 0; // Not known yet + CABHeader.FolderCount = 0; // Not known yet + CABHeader.FileCount = 0; // Not known yet + CABHeader.Flags = 0; // Not known yet + + CABHeader.CabinetNumber = CurrentDiskNumber; + + if ((CurrentDiskNumber > 0) && (OnCabinetName(PrevCabinetNumber, CabinetPrev))) { + CABHeader.Flags |= CAB_FLAG_HASPREV; + if (!OnDiskLabel(PrevCabinetNumber, DiskPrev)) + strcpy(CabinetPrev, ""); + } + + if (OnCabinetName(CurrentDiskNumber + 1, CabinetNext)) { + CABHeader.Flags |= CAB_FLAG_HASNEXT; + if (!OnDiskLabel(CurrentDiskNumber + 1, DiskNext)) + strcpy(DiskNext, ""); + } + + TotalSize = 0; + + if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0) { + + DPRINT(MAX_TRACE, ("CabinetPrev '%s'.\n", CabinetPrev)); + + /* Calculate size of name of previous cabinet */ + TotalSize += strlen(CabinetPrev) + 1; + + /* Calculate size of label of previous disk */ + TotalSize += strlen(DiskPrev) + 1; + } + + if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0) { + + DPRINT(MAX_TRACE, ("CabinetNext '%s'.\n", CabinetNext)); + + /* Calculate size of name of next cabinet */ + Size = strlen(CabinetNext) + 1; + TotalSize += Size; + NextFieldsSize = Size; + + /* Calculate size of label of next disk */ + Size = strlen(DiskNext) + 1; + TotalSize += Size; + NextFieldsSize += Size; + } else + NextFieldsSize = 0; + + /* Add cabinet reserved area size if present */ + if (CabinetReservedFileSize > 0) + { + CABHeader.Flags |= CAB_FLAG_RESERVE; + TotalSize += CabinetReservedFileSize; + TotalSize += sizeof(unsigned long); /* For CabinetResSize, FolderResSize, and FileResSize fields */ + } + + DiskSize += TotalSize; + + TotalHeaderSize = sizeof(CFHEADER) + TotalSize; + + return CAB_STATUS_SUCCESS; +} + + +unsigned long CCabinet::WriteCabinetHeader(bool MoreDisks) +/* + * FUNCTION: Writes the cabinet header and optional fields + * ARGUMENTS: + * MoreDisks = true if next cabinet name should be included + * RETURNS: + * Status of operation + */ +{ + PCFFOLDER_NODE FolderNode; + PCFFILE_NODE FileNode; + unsigned long BytesWritten; + unsigned long Size; + + if (MoreDisks) { + CABHeader.Flags |= CAB_FLAG_HASNEXT; + Size = TotalHeaderSize; + } else { + CABHeader.Flags &= ~CAB_FLAG_HASNEXT; + DiskSize -= NextFieldsSize; + Size = TotalHeaderSize - NextFieldsSize; + } + + /* Set absolute folder offsets */ + BytesWritten = Size + TotalFolderSize + TotalFileSize; + CABHeader.FolderCount = 0; + FolderNode = FolderListHead; + while (FolderNode != NULL) { + FolderNode->Folder.DataOffset = BytesWritten; + + BytesWritten += FolderNode->TotalFolderSize; + + CABHeader.FolderCount++; + + FolderNode = FolderNode->Next; + } + + /* Set absolute offset of file table */ + CABHeader.FileTableOffset = Size + TotalFolderSize; + + /* Count number of files to be committed */ + CABHeader.FileCount = 0; + FileNode = FileListHead; + while (FileNode != NULL) { + if (FileNode->Commit) + CABHeader.FileCount++; + FileNode = FileNode->Next; + } + + CABHeader.CabinetSize = DiskSize; + + /* Write header */ +#if defined(WIN32) + if (!WriteFile(FileHandle, &CABHeader, sizeof(CFHEADER), &BytesWritten, NULL)) { + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } +#else + BytesWritten = sizeof(CFHEADER); + if (fwrite(&CABHeader, sizeof(CFHEADER), 1, FileHandle) < 1) { + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } +#endif + + /* Write per-cabinet reserved area if present */ + if (CABHeader.Flags & CAB_FLAG_RESERVE) { + unsigned long ReservedSize; + + ReservedSize = CabinetReservedFileSize & 0xffff; + ReservedSize |= (0 << 16); /* Folder reserved area size */ + ReservedSize |= (0 << 24); /* Folder reserved area size */ +#if defined(WIN32) + if (!WriteFile(FileHandle, &ReservedSize, sizeof(unsigned long), &BytesWritten, NULL)) { + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } +#else + BytesWritten = sizeof(unsigned long); + if (fwrite(&ReservedSize, sizeof(unsigned long), 1, FileHandle) < 1) { + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } +#endif + +#if defined(WIN32) + if (!WriteFile(FileHandle, CabinetReservedFileBuffer, CabinetReservedFileSize, &BytesWritten, NULL)) { + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } +#else + BytesWritten = CabinetReservedFileSize; + if (fwrite(CabinetReservedFileBuffer, CabinetReservedFileSize, 1, FileHandle) < 1) { + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } +#endif + } + + if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0) { + + DPRINT(MAX_TRACE, ("CabinetPrev '%s'.\n", CabinetPrev)); + + /* Write name of previous cabinet */ + Size = strlen(CabinetPrev) + 1; +#if defined(WIN32) + if (!WriteFile(FileHandle, CabinetPrev, Size, &BytesWritten, NULL)) { + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } +#else + BytesWritten = Size; + if (fwrite(CabinetPrev, Size, 1, FileHandle) < 1) { + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } +#endif + + DPRINT(MAX_TRACE, ("DiskPrev '%s'.\n", DiskPrev)); + + /* Write label of previous disk */ + Size = strlen(DiskPrev) + 1; +#if defined(WIN32) + if (!WriteFile(FileHandle, DiskPrev, Size, &BytesWritten, NULL)) { + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } +#else + BytesWritten = Size; + if (fwrite(DiskPrev, Size, 1, FileHandle) < 1) { + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } +#endif + } + + if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0) { + + DPRINT(MAX_TRACE, ("CabinetNext '%s'.\n", CabinetNext)); + + /* Write name of next cabinet */ + Size = strlen(CabinetNext) + 1; +#if defined(WIN32) + if (!WriteFile(FileHandle, CabinetNext, Size, &BytesWritten, NULL)) { + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } +#else + BytesWritten = Size; + if (fwrite(CabinetNext, Size, 1, FileHandle) < 1) { + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } +#endif + + DPRINT(MAX_TRACE, ("DiskNext '%s'.\n", DiskNext)); + + /* Write label of next disk */ + Size = strlen(DiskNext) + 1; +#if defined(WIN32) + if (!WriteFile(FileHandle, DiskNext, Size, &BytesWritten, NULL)) { + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } +#else + BytesWritten = Size; + if (fwrite(DiskNext, Size, 1, FileHandle) < 1) { + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } +#endif + } + + return CAB_STATUS_SUCCESS; +} + + +unsigned long CCabinet::WriteFolderEntries() +/* + * FUNCTION: Writes folder entries + * RETURNS: + * Status of operation + */ +{ + PCFFOLDER_NODE FolderNode; + unsigned long BytesWritten; + + DPRINT(MAX_TRACE, ("Writing folder table.\n")); + + FolderNode = FolderListHead; + while (FolderNode != NULL) { + if (FolderNode->Commit) { + + DPRINT(MAX_TRACE, ("Writing folder entry. CompressionType (0x%X) DataBlockCount (%d) DataOffset (0x%X).\n", + FolderNode->Folder.CompressionType, FolderNode->Folder.DataBlockCount, FolderNode->Folder.DataOffset)); + +#if defined(WIN32) + if (!WriteFile(FileHandle, + &FolderNode->Folder, + sizeof(CFFOLDER), + &BytesWritten, + NULL)) { + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } +#else + BytesWritten = sizeof(CFFOLDER); + if (fwrite(&FolderNode->Folder, sizeof(CFFOLDER), 1, FileHandle) < 1) { + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } +#endif + } + FolderNode = FolderNode->Next; + } + + return CAB_STATUS_SUCCESS; +} + + +unsigned long CCabinet::WriteFileEntries() +/* + * FUNCTION: Writes file entries for all files + * RETURNS: + * Status of operation + */ +{ + PCFFILE_NODE File; + unsigned long BytesWritten; + bool SetCont; + + DPRINT(MAX_TRACE, ("Writing file table.\n")); + + File = FileListHead; + while (File != NULL) { + if (File->Commit) { + /* Remove any continued files that ends in this disk */ + if (File->File.FileControlID == CAB_FILE_CONTINUED) + File->Delete = true; + + /* The file could end in the last (split) block and should therefore + appear in the next disk too */ + + if ((File->File.FileOffset + File->File.FileSize >= LastBlockStart) && + (File->File.FileControlID <= CAB_FILE_MAX_FOLDER) && (BlockIsSplit)) { + File->File.FileControlID = CAB_FILE_SPLIT; + File->Delete = false; + SetCont = true; + } + + DPRINT(MAX_TRACE, ("Writing file entry. FileControlID (0x%X) FileOffset (0x%X) FileSize (%d) FileName (%s).\n", + File->File.FileControlID, File->File.FileOffset, File->File.FileSize, File->FileName)); + +#if defined(WIN32) + if (!WriteFile(FileHandle, + &File->File, + sizeof(CFFILE), + &BytesWritten, + NULL)) + return CAB_STATUS_CANNOT_WRITE; +#else + BytesWritten = sizeof(CFFILE); + if (fwrite(&File->File, sizeof(CFFILE), 1, FileHandle) < 1) { + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } +#endif + +#if defined(WIN32) + if (!WriteFile(FileHandle, + GetFileName(File->FileName), + strlen(GetFileName(File->FileName)) + 1, &BytesWritten, NULL)) + return CAB_STATUS_CANNOT_WRITE; +#else + BytesWritten = strlen(GetFileName(File->FileName)) + 1; + if (fwrite(GetFileName(File->FileName), strlen(GetFileName(File->FileName)) + 1, 1, FileHandle) < 1) { + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } +#endif + + if (SetCont) { + File->File.FileControlID = CAB_FILE_CONTINUED; + SetCont = false; + } + } + + File = File->Next; + } + return CAB_STATUS_SUCCESS; +} + + +unsigned long CCabinet::CommitDataBlocks(PCFFOLDER_NODE FolderNode) +/* + * FUNCTION: Writes data blocks to the cabinet + * ARGUMENTS: + * FolderNode = Pointer to folder node containing the data blocks + * RETURNS: + * Status of operation + */ +{ + PCFDATA_NODE DataNode; + unsigned long BytesWritten; + unsigned long BytesRead; + unsigned long Status; + + DataNode = FolderNode->DataListHead; + if (DataNode != NULL) + Status = ScratchFile->Seek(DataNode->ScratchFilePosition); + + while (DataNode != NULL) { + DPRINT(MAX_TRACE, ("Reading block at (0x%X) CompSize (%d) UncompSize (%d).\n", + DataNode->ScratchFilePosition, + DataNode->Data.CompSize, + DataNode->Data.UncompSize)); + + /* InputBuffer is free for us to use here, so we use it and avoid a + memory allocation. OutputBuffer can't be used here because it may + still contain valid data (if a data block spans two or more disks) */ + Status = ScratchFile->ReadBlock(&DataNode->Data, InputBuffer, &BytesRead); + if (Status != CAB_STATUS_SUCCESS) { + DPRINT(MIN_TRACE, ("Cannot read from scratch file (%d).\n", (unsigned int)Status)); + return Status; + } + +#if defined(WIN32) + if (!WriteFile(FileHandle, &DataNode->Data, + sizeof(CFDATA), &BytesWritten, NULL)) { + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } +#else + BytesWritten = sizeof(CFDATA); + if (fwrite(&DataNode->Data, sizeof(CFDATA), 1, FileHandle) < 1) { + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } +#endif + +#if defined(WIN32) + if (!WriteFile(FileHandle, InputBuffer, + DataNode->Data.CompSize, &BytesWritten, NULL)) { + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } +#else + BytesWritten = DataNode->Data.CompSize; + if (fwrite(InputBuffer, DataNode->Data.CompSize, 1, FileHandle) < 1) { + DPRINT(MIN_TRACE, ("Cannot write to file.\n")); + return CAB_STATUS_CANNOT_WRITE; + } +#endif + + DataNode = DataNode->Next; + } + return CAB_STATUS_SUCCESS; +} + + +unsigned long CCabinet::WriteDataBlock() +/* + * FUNCTION: Writes the current data block to the scratch file + * RETURNS: + * Status of operation + */ +{ + unsigned long Status; + unsigned long BytesWritten; + PCFDATA_NODE DataNode; + + if (!BlockIsSplit) { + Status = Codec->Compress(OutputBuffer, + InputBuffer, + CurrentIBufferSize, + &TotalCompSize); + + DPRINT(MAX_TRACE, ("Block compressed. CurrentIBufferSize (%d) TotalCompSize(%d).\n", + CurrentIBufferSize, TotalCompSize)); + + CurrentOBuffer = OutputBuffer; + CurrentOBufferSize = TotalCompSize; + } + + DataNode = NewDataNode(CurrentFolderNode); + if (!DataNode) { + DPRINT(MIN_TRACE, ("Insufficient memory.\n")); + return CAB_STATUS_NOMEMORY; + } + + DiskSize += sizeof(CFDATA); + + if (MaxDiskSize > 0) + /* Disk size is limited */ + BlockIsSplit = (DiskSize + CurrentOBufferSize > MaxDiskSize); + else + BlockIsSplit = false; + + if (BlockIsSplit) { + DataNode->Data.CompSize = (unsigned short)(MaxDiskSize - DiskSize); + DataNode->Data.UncompSize = 0; + CreateNewDisk = true; + } else { + DataNode->Data.CompSize = (unsigned short)CurrentOBufferSize; + DataNode->Data.UncompSize = (unsigned short)CurrentIBufferSize; + } + + DataNode->Data.Checksum = 0; + DataNode->ScratchFilePosition = ScratchFile->Position(); + + // FIXME: MAKECAB.EXE does not like this checksum algorithm + //DataNode->Data.Checksum = ComputeChecksum(CurrentOBuffer, DataNode->Data.CompSize, 0); + + DPRINT(MAX_TRACE, ("Writing block. Checksum (0x%X) CompSize (%d) UncompSize (%d).\n", + (unsigned int)DataNode->Data.Checksum, + (unsigned int)DataNode->Data.CompSize, + (unsigned int)DataNode->Data.UncompSize)); + + Status = ScratchFile->WriteBlock(&DataNode->Data, + CurrentOBuffer, &BytesWritten); + if (Status != CAB_STATUS_SUCCESS) + return Status; + + DiskSize += BytesWritten; + + CurrentFolderNode->TotalFolderSize += (BytesWritten + sizeof(CFDATA)); + CurrentFolderNode->Folder.DataBlockCount++; + + (unsigned char*)CurrentOBuffer += DataNode->Data.CompSize; + CurrentOBufferSize -= DataNode->Data.CompSize; + + LastBlockStart += DataNode->Data.UncompSize; + + if (!BlockIsSplit) { + CurrentIBufferSize = 0; + CurrentIBuffer = InputBuffer; + } + + return CAB_STATUS_SUCCESS; +} + + +#if !defined(WIN32) + +void CCabinet::ConvertDateAndTime(time_t* Time, + unsigned short* DosDate, + unsigned short* DosTime) +/* + * FUNCTION: Returns file times of a file + * ARGUMENTS: + * FileHandle = File handle of file to get file times from + * File = Pointer to CFFILE node for file + * RETURNS: + * Status of operation + */ +{ + struct tm *timedef; + + timedef = localtime(Time); + + DPRINT(MAX_TRACE, ("day: %d, mon: %d, year:%d, hour: %d, min: %d, sec: %d\n", + timedef->tm_mday, timedef->tm_mon, timedef->tm_year, + timedef->tm_sec, timedef->tm_min, timedef->tm_hour)); + + *DosDate = ((timedef->tm_mday + 1) << 0) + | ((timedef->tm_mon + 1) << 5) + | (((timedef->tm_year + 1900) - 1980) << 9); + + *DosTime = (timedef->tm_sec << 0) + | (timedef->tm_min << 5) + | (timedef->tm_hour << 11); +} + +#endif // !WIN32 + + +unsigned long CCabinet::GetFileTimes(FILEHANDLE FileHandle, PCFFILE_NODE File) +/* + * FUNCTION: Returns file times of a file + * ARGUMENTS: + * FileHandle = File handle of file to get file times from + * File = Pointer to CFFILE node for file + * RETURNS: + * Status of operation + */ +{ +#if defined(WIN32) + FILETIME FileTime; + + if (GetFileTime(FileHandle, NULL, &FileTime, NULL)) + FileTimeToDosDateTime(&FileTime, + &File->File.FileDate, + &File->File.FileTime); +#else + struct stat stbuf; + char buf[MAX_PATH]; + + // Check for an absolute path + if (IsSeparator(File->FileName[0])) + { + strcpy(buf, File->FileName); + } + else + { + getcwd(buf, sizeof(buf)); + strcat(buf, DIR_SEPARATOR_STRING); + strcat(buf, File->FileName); + } + + if (stat(buf, &stbuf) == -1) + { + return CAB_STATUS_CANNOT_READ; + } + + ConvertDateAndTime(&stbuf.st_mtime, &File->File.FileDate, &File->File.FileTime); +#endif + return CAB_STATUS_SUCCESS; +} + + +unsigned long CCabinet::GetAttributesOnFile(PCFFILE_NODE File) +/* + * FUNCTION: Returns attributes on a file + * ARGUMENTS: + * File = Pointer to CFFILE node for file + * RETURNS: + * Status of operation + */ +{ +#if defined(WIN32) + unsigned long Attributes; + + Attributes = GetFileAttributes(File->FileName); + if (Attributes == -1) + return CAB_STATUS_CANNOT_READ; + + if (Attributes & FILE_ATTRIBUTE_READONLY) + File->File.Attributes |= CAB_ATTRIB_READONLY; + + if (Attributes & FILE_ATTRIBUTE_HIDDEN) + File->File.Attributes |= CAB_ATTRIB_HIDDEN; + + if (Attributes & FILE_ATTRIBUTE_SYSTEM) + File->File.Attributes |= CAB_ATTRIB_SYSTEM; + + if (Attributes & FILE_ATTRIBUTE_DIRECTORY) + File->File.Attributes |= CAB_ATTRIB_DIRECTORY; + + if (Attributes & FILE_ATTRIBUTE_ARCHIVE) + File->File.Attributes |= CAB_ATTRIB_ARCHIVE; +#else + struct stat stbuf; + char buf[MAX_PATH]; + + // Check for an absolute path + if (IsSeparator(File->FileName[0])) + { + strcpy(buf, File->FileName); + } + else + { + getcwd(buf, sizeof(buf)); + strcat(buf, DIR_SEPARATOR_STRING); + strcat(buf, File->FileName); + } + + if (stat(buf, &stbuf) == -1) + { + return CAB_STATUS_CANNOT_READ; + } + +#if 0 + File->File.Attributes |= CAB_ATTRIB_READONLY; + File->File.Attributes |= CAB_ATTRIB_HIDDEN; + File->File.Attributes |= CAB_ATTRIB_SYSTEM; +#endif + + if (stbuf.st_mode & S_IFDIR) + File->File.Attributes |= CAB_ATTRIB_DIRECTORY; + + File->File.Attributes |= CAB_ATTRIB_ARCHIVE; + +#endif + return CAB_STATUS_SUCCESS; +} + + +unsigned long CCabinet::SetAttributesOnFile(PCFFILE_NODE File) +/* + * FUNCTION: Sets attributes on a file + * ARGUMENTS: + * File = Pointer to CFFILE node for file + * RETURNS: + * Status of operation + */ +{ +#if defined(WIN32) + unsigned long Attributes = 0; + + if (File->File.Attributes & CAB_ATTRIB_READONLY) + Attributes |= FILE_ATTRIBUTE_READONLY; + + if (File->File.Attributes & CAB_ATTRIB_HIDDEN) + Attributes |= FILE_ATTRIBUTE_HIDDEN; + + if (File->File.Attributes & CAB_ATTRIB_SYSTEM) + Attributes |= FILE_ATTRIBUTE_SYSTEM; + + if (File->File.Attributes & CAB_ATTRIB_DIRECTORY) + Attributes |= FILE_ATTRIBUTE_DIRECTORY; + + if (File->File.Attributes & CAB_ATTRIB_ARCHIVE) + Attributes |= FILE_ATTRIBUTE_ARCHIVE; + + SetFileAttributes(File->FileName, Attributes); + + return CAB_STATUS_SUCCESS; +#else + //DPRINT(MIN_TRACE, ("FIXME: SetAttributesOnFile() is unimplemented\n")); + return CAB_STATUS_SUCCESS; +#endif +} + +#endif /* CAB_READ_ONLY */ + +/* EOF */ diff --git a/reactos/tools/cabman/cabinet.h b/reactos/tools/cabman/cabinet.h new file mode 100755 index 00000000000..59874068236 --- /dev/null +++ b/reactos/tools/cabman/cabinet.h @@ -0,0 +1,504 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS cabinet manager + * FILE: tools/cabman/cabinet.h + * PURPOSE: Cabinet definitions + */ +#ifndef __CABINET_H +#define __CABINET_H + +#if defined(WIN32) +#include +#else +#include +#include +#include +#include +#include +#ifndef MAX_PATH +#define MAX_PATH 260 +#endif +#endif +#include +#include +#include + +#if defined(WIN32) +#define DIR_SEPARATOR_CHAR '\\' +#define DIR_SEPARATOR_STRING "\\" + +#define strcasecmp strcmpi +#define AllocateMemory(size) HeapAlloc(GetProcessHeap(), 0, size) +#define FreeMemory(buffer) HeapFree(GetProcessHeap(), 0, buffer) +#define FILEHANDLE HANDLE +#define CloseFile(handle) CloseHandle(handle) +#define GetSizeOfFile(handle) _GetSizeOfFile(handle) +static long _GetSizeOfFile(FILEHANDLE handle) +{ + long size = GetFileSize(handle, NULL); + if (size == INVALID_FILE_SIZE) + { + return -1; + } + return size; +} +#define ReadFileData(handle, buffer, size, bytesread) _ReadFileData(handle, buffer, size, bytesread) +static bool _ReadFileData(FILEHANDLE handle, void* buffer, unsigned long size, unsigned long* bytesread) +{ + return ReadFile(handle, buffer, size, bytesread, NULL); +} +#else +#define DIR_SEPARATOR_CHAR '/' +#define DIR_SEPARATOR_STRING "/" + +#define AllocateMemory(size) malloc(size) +#define FreeMemory(buffer) free(buffer) +#define CloseFile(handle) fclose(handle) +#define FILEHANDLE FILE* +#define GetSizeOfFile(handle) _GetSizeOfFile(handle) +static long _GetSizeOfFile(FILEHANDLE handle) +{ + long size; + fseek(handle, 0, SEEK_END); + size = ftell(handle); + fseek(handle, 0, SEEK_SET); + return size; +} +#define ReadFileData(handle, buffer, size, bytesread) _ReadFileData(handle, buffer, size, bytesread) +static bool _ReadFileData(FILEHANDLE handle, void* buffer, unsigned long size, unsigned long* bytesread) +{ + *bytesread = fread(buffer, 1, size, handle); + return *bytesread == size; +} +#endif + +/* Debugging */ + +#define DBG + +#define NORMAL_MASK 0x000000FF +#define SPECIAL_MASK 0xFFFFFF00 +#define MIN_TRACE 0x00000001 +#define MID_TRACE 0x00000002 +#define MAX_TRACE 0x00000003 + +#define DEBUG_MEMORY 0x00000100 + +#ifdef DBG + +extern unsigned long DebugTraceLevel; + +#define DPRINT(_t_, _x_) \ + if (((DebugTraceLevel & NORMAL_MASK) >= _t_) || \ + ((DebugTraceLevel & _t_) > NORMAL_MASK)) { \ + printf("(%s:%d)(%s) ", __FILE__, __LINE__, __FUNCTION__); \ + printf _x_ ; \ + } + +#define ASSERT(_b_) { \ + if (!(_b_)) { \ + printf("(%s:%d)(%s) ASSERTION: ", __FILE__, __LINE__, __FUNCTION__); \ + printf(#_b_); \ + exit(0); \ + } \ +} + +#else /* DBG */ + +#define DPRINT(_t_, _x_) + +#define ASSERT(_x_) + +#endif /* DBG */ + + +/* Cabinet constants */ + +#define CAB_SIGNATURE 0x4643534D // "MSCF" +#define CAB_VERSION 0x0103 +#define CAB_BLOCKSIZE 32768 + +#define CAB_COMP_MASK 0x00FF +#define CAB_COMP_NONE 0x0000 +#define CAB_COMP_MSZIP 0x0001 +#define CAB_COMP_QUANTUM 0x0002 +#define CAB_COMP_LZX 0x0003 + +#define CAB_FLAG_HASPREV 0x0001 +#define CAB_FLAG_HASNEXT 0x0002 +#define CAB_FLAG_RESERVE 0x0004 + +#define CAB_ATTRIB_READONLY 0x0001 +#define CAB_ATTRIB_HIDDEN 0x0002 +#define CAB_ATTRIB_SYSTEM 0x0004 +#define CAB_ATTRIB_VOLUME 0x0008 +#define CAB_ATTRIB_DIRECTORY 0x0010 +#define CAB_ATTRIB_ARCHIVE 0x0020 +#define CAB_ATTRIB_EXECUTE 0x0040 +#define CAB_ATTRIB_UTF_NAME 0x0080 + +#define CAB_FILE_MAX_FOLDER 0xFFFC +#define CAB_FILE_CONTINUED 0xFFFD +#define CAB_FILE_SPLIT 0xFFFE +#define CAB_FILE_PREV_NEXT 0xFFFF + + +/* Cabinet structures */ + +typedef struct _CFHEADER +{ + unsigned long Signature; // File signature 'MSCF' (CAB_SIGNATURE) + unsigned long Reserved1; // Reserved field + unsigned long CabinetSize; // Cabinet file size + unsigned long Reserved2; // Reserved field + unsigned long FileTableOffset; // Offset of first CFFILE + unsigned long Reserved3; // Reserved field + unsigned short Version; // Cabinet version (CAB_VERSION) + unsigned short FolderCount; // Number of folders + unsigned short FileCount; // Number of files + unsigned short Flags; // Cabinet flags (CAB_FLAG_*) + unsigned short SetID; // Cabinet set id + unsigned short CabinetNumber; // Zero-based cabinet number +/* Optional fields (depends on Flags) + unsigned short CabinetResSize // Per-cabinet reserved area size + char FolderResSize // Per-folder reserved area size + char FileResSize // Per-file reserved area size + char CabinetReserved[] // Per-cabinet reserved area + char CabinetPrev[] // Name of previous cabinet file + char DiskPrev[] // Name of previous disk + char CabinetNext[] // Name of next cabinet file + char DiskNext[] // Name of next disk + */ +} CFHEADER, *PCFHEADER; + + +typedef struct _CFFOLDER +{ + unsigned long DataOffset; // Absolute offset of first CFDATA block in this folder + unsigned short DataBlockCount; // Number of CFDATA blocks in this folder in this cabinet + unsigned short CompressionType; // Type of compression used for all CFDATA blocks in this folder +/* Optional fields (depends on Flags) + char FolderReserved[] // Per-folder reserved area + */ +} CFFOLDER, *PCFFOLDER; + + +typedef struct _CFFILE +{ + unsigned long FileSize; // Uncompressed file size in bytes + unsigned long FileOffset; // Uncompressed offset of file in the folder + unsigned short FileControlID; // File control ID (CAB_FILE_*) + unsigned short FileDate; // File date stamp, as used by DOS + unsigned short FileTime; // File time stamp, as used by DOS + unsigned short Attributes; // File attributes (CAB_ATTRIB_*) + /* After this is the NULL terminated filename */ +} CFFILE, *PCFFILE; + + +typedef struct _CFDATA +{ + unsigned long Checksum; // Checksum of CFDATA entry + unsigned short CompSize; // Number of compressed bytes in this block + unsigned short UncompSize; // Number of uncompressed bytes in this block +/* Optional fields (depends on Flags) + char DataReserved[] // Per-datablock reserved area + */ +} CFDATA, *PCFDATA; + +typedef struct _CFDATA_NODE +{ + struct _CFDATA_NODE *Next; + struct _CFDATA_NODE *Prev; + unsigned long ScratchFilePosition; // Absolute offset in scratch file + unsigned long AbsoluteOffset; // Absolute offset in cabinet + unsigned long UncompOffset; // Uncompressed offset in folder + CFDATA Data; +} CFDATA_NODE, *PCFDATA_NODE; + +typedef struct _CFFOLDER_NODE +{ + struct _CFFOLDER_NODE *Next; + struct _CFFOLDER_NODE *Prev; + unsigned long UncompOffset; // File size accumulator + unsigned long AbsoluteOffset; + unsigned long TotalFolderSize; // Total size of folder in current disk + PCFDATA_NODE DataListHead; + PCFDATA_NODE DataListTail; + unsigned long Index; + bool Commit; // true if the folder should be committed + bool Delete; // true if marked for deletion + CFFOLDER Folder; +} CFFOLDER_NODE, *PCFFOLDER_NODE; + +typedef struct _CFFILE_NODE +{ + struct _CFFILE_NODE *Next; + struct _CFFILE_NODE *Prev; + CFFILE File; + char* FileName; + PCFDATA_NODE DataBlock; // First data block of file. NULL if not known + bool Commit; // true if the file data should be committed + bool Delete; // true if marked for deletion + PCFFOLDER_NODE FolderNode; // Folder this file belong to +} CFFILE_NODE, *PCFFILE_NODE; + + +typedef struct _CAB_SEARCH +{ + char Search[MAX_PATH]; // Search criteria + PCFFILE_NODE Next; // Pointer to next node + PCFFILE File; // Pointer to current CFFILE + char* FileName; // Current filename +} CAB_SEARCH, *PCAB_SEARCH; + + +/* Constants */ + +/* Status codes */ +#define CAB_STATUS_SUCCESS 0x00000000 +#define CAB_STATUS_FAILURE 0x00000001 +#define CAB_STATUS_NOMEMORY 0x00000002 +#define CAB_STATUS_CANNOT_OPEN 0x00000003 +#define CAB_STATUS_CANNOT_CREATE 0x00000004 +#define CAB_STATUS_CANNOT_READ 0x00000005 +#define CAB_STATUS_CANNOT_WRITE 0x00000006 +#define CAB_STATUS_FILE_EXISTS 0x00000007 +#define CAB_STATUS_INVALID_CAB 0x00000008 +#define CAB_STATUS_NOFILE 0x00000009 +#define CAB_STATUS_UNSUPPCOMP 0x0000000A + + + +/* Codecs */ + +class CCABCodec { +public: + /* Default constructor */ + CCABCodec() {}; + /* Default destructor */ + virtual ~CCABCodec() {}; + /* Compresses a data block */ + virtual unsigned long Compress(void* OutputBuffer, + void* InputBuffer, + unsigned long InputLength, + unsigned long* OutputLength) = 0; + /* Uncompresses a data block */ + virtual unsigned long Uncompress(void* OutputBuffer, + void* InputBuffer, + unsigned long InputLength, + unsigned long* OutputLength) = 0; +}; + + +/* Codec status codes */ +#define CS_SUCCESS 0x0000 /* All data consumed */ +#define CS_NOMEMORY 0x0001 /* Not enough free memory */ +#define CS_BADSTREAM 0x0002 /* Bad data stream */ + + +/* Codec indentifiers */ +#define CAB_CODEC_RAW 0x00 +#define CAB_CODEC_LZX 0x01 +#define CAB_CODEC_MSZIP 0x02 + + + +/* Classes */ + +#ifndef CAB_READ_ONLY + +class CCFDATAStorage { +public: + /* Default constructor */ + CCFDATAStorage(); + /* Default destructor */ + virtual ~CCFDATAStorage(); + unsigned long Create(char* FileName); + unsigned long Destroy(); + unsigned long Truncate(); + unsigned long Position(); + unsigned long Seek(long Position); + unsigned long ReadBlock(PCFDATA Data, void* Buffer, unsigned long* BytesRead); + unsigned long WriteBlock(PCFDATA Data, void* Buffer, unsigned long* BytesWritten); +private: + char FullName[MAX_PATH]; + bool FileCreated; + FILEHANDLE FileHandle; +}; + +#endif /* CAB_READ_ONLY */ + +class CCabinet { +public: + /* Default constructor */ + CCabinet(); + /* Default destructor */ + virtual ~CCabinet(); + /* Determines if a character is a separator */ + bool CCabinet::IsSeparator(char Char); + /* Replaces \ or / with the one used be the host environment */ + char* CCabinet::ConvertPath(char* Path, bool Allocate); + /* Returns a pointer to the filename part of a fully qualified filename */ + char* GetFileName(char* Path); + /* Removes a filename from a fully qualified filename */ + void RemoveFileName(char* Path); + /* Normalizes a path */ + bool NormalizePath(char* Path, unsigned long Length); + /* Returns name of cabinet file */ + char* GetCabinetName(); + /* Sets the name of the cabinet file */ + void SetCabinetName(char* FileName); + /* Sets destination path for extracted files */ + void SetDestinationPath(char* DestinationPath); + /* Sets cabinet reserved file */ + bool SetCabinetReservedFile(char* FileName); + /* Returns cabinet reserved file */ + char* GetCabinetReservedFile(); + /* Returns destination path */ + char* GetDestinationPath(); + /* Returns zero-based current disk number */ + unsigned long GetCurrentDiskNumber(); + /* Opens the current cabinet file */ + unsigned long Open(); + /* Closes the current open cabinet file */ + void Close(); + /* Locates the first file in the current cabinet file that matches a search criteria */ + unsigned long FindFirst(char* FileName, PCAB_SEARCH Search); + /* Locates the next file in the current cabinet file */ + unsigned long FindNext(PCAB_SEARCH Search); + /* Extracts a file from the current cabinet file */ + unsigned long ExtractFile(char* FileName); + /* Select codec engine to use */ + void SelectCodec(unsigned long Id); +#ifndef CAB_READ_ONLY + /* Creates a new cabinet file */ + unsigned long NewCabinet(); + /* Forces a new disk to be created */ + unsigned long NewDisk(); + /* Forces a new folder to be created */ + unsigned long NewFolder(); + /* Writes a file to scratch storage */ + unsigned long WriteFileToScratchStorage(PCFFILE_NODE FileNode); + /* Forces the current disk to be written */ + unsigned long WriteDisk(unsigned long MoreDisks); + /* Commits the current disk */ + unsigned long CommitDisk(unsigned long MoreDisks); + /* Closes the current disk */ + unsigned long CloseDisk(); + /* Closes the current cabinet */ + unsigned long CloseCabinet(); + /* Adds a file to the current disk */ + unsigned long AddFile(char* FileName); + /* Sets the maximum size of the current disk */ + void SetMaxDiskSize(unsigned long Size); +#endif /* CAB_READ_ONLY */ + + /* Default event handlers */ + + /* Handler called when a file is about to be overridden */ + virtual bool OnOverwrite(PCFFILE Entry, char* FileName); + /* Handler called when a file is about to be extracted */ + virtual void OnExtract(PCFFILE Entry, char* FileName); + /* Handler called when a new disk is to be processed */ + virtual void OnDiskChange(char* CabinetName, char* DiskLabel); +#ifndef CAB_READ_ONLY + /* Handler called when a file is about to be added */ + virtual void OnAdd(PCFFILE Entry, char* FileName); + /* Handler called when a cabinet need a name */ + virtual bool OnCabinetName(unsigned long Number, char* Name); + /* Handler called when a disk needs a label */ + virtual bool OnDiskLabel(unsigned long Number, char* Label); +#endif /* CAB_READ_ONLY */ +private: + PCFFOLDER_NODE LocateFolderNode(unsigned long Index); + unsigned long GetAbsoluteOffset(PCFFILE_NODE File); + unsigned long LocateFile(char* FileName, PCFFILE_NODE *File); + unsigned long ReadString(char* String, unsigned long MaxLength); + unsigned long ReadFileTable(); + unsigned long ReadDataBlocks(PCFFOLDER_NODE FolderNode); + PCFFOLDER_NODE NewFolderNode(); + PCFFILE_NODE NewFileNode(); + PCFDATA_NODE NewDataNode(PCFFOLDER_NODE FolderNode); + void DestroyDataNodes(PCFFOLDER_NODE FolderNode); + void DestroyFileNodes(); + void DestroyDeletedFileNodes(); + void DestroyFolderNodes(); + void DestroyDeletedFolderNodes(); + unsigned long ComputeChecksum(void* Buffer, unsigned int Size, unsigned long Seed); + unsigned long ReadBlock(void* Buffer, unsigned long Size, unsigned long* BytesRead); +#ifndef CAB_READ_ONLY + unsigned long InitCabinetHeader(); + unsigned long WriteCabinetHeader(bool MoreDisks); + unsigned long WriteFolderEntries(); + unsigned long WriteFileEntries(); + unsigned long CommitDataBlocks(PCFFOLDER_NODE FolderNode); + unsigned long WriteDataBlock(); + unsigned long GetAttributesOnFile(PCFFILE_NODE File); + unsigned long SetAttributesOnFile(PCFFILE_NODE File); + unsigned long GetFileTimes(FILEHANDLE FileHandle, PCFFILE_NODE File); +#if !defined(WIN32) + void ConvertDateAndTime(time_t* Time, unsigned short* DosDate, unsigned short* DosTime); +#endif +#endif /* CAB_READ_ONLY */ + unsigned long CurrentDiskNumber; // Zero based disk number + char CabinetName[256]; // Filename of current cabinet + char CabinetPrev[256]; // Filename of previous cabinet + char DiskPrev[256]; // Label of cabinet in file CabinetPrev + char CabinetNext[256]; // Filename of next cabinet + char DiskNext[256]; // Label of cabinet in file CabinetNext + unsigned long TotalHeaderSize; // Size of header and optional fields + unsigned long NextFieldsSize; // Size of next cabinet name and next disk label + unsigned long TotalFolderSize; // Size of all folder entries + unsigned long TotalFileSize; // Size of all file entries + unsigned long FolderUncompSize; // Uncompressed size of folder + unsigned long BytesLeftInBlock; // Number of bytes left in current block + bool ReuseBlock; + char DestPath[MAX_PATH]; + char CabinetReservedFile[MAX_PATH]; + void* CabinetReservedFileBuffer; + unsigned long CabinetReservedFileSize; + FILEHANDLE FileHandle; + bool FileOpen; + CFHEADER CABHeader; + unsigned long CabinetReserved; + unsigned long FolderReserved; + unsigned long DataReserved; + PCFFOLDER_NODE FolderListHead; + PCFFOLDER_NODE FolderListTail; + PCFFOLDER_NODE CurrentFolderNode; + PCFDATA_NODE CurrentDataNode; + PCFFILE_NODE FileListHead; + PCFFILE_NODE FileListTail; + CCABCodec *Codec; + unsigned long CodecId; + bool CodecSelected; + void* InputBuffer; + void* CurrentIBuffer; // Current offset in input buffer + unsigned long CurrentIBufferSize; // Bytes left in input buffer + void* OutputBuffer; + unsigned long TotalCompSize; // Total size of current CFDATA block + void* CurrentOBuffer; // Current offset in output buffer + unsigned long CurrentOBufferSize; // Bytes left in output buffer + unsigned long BytesLeftInCabinet; + bool RestartSearch; + unsigned long LastFileOffset; // Uncompressed offset of last extracted file +#ifndef CAB_READ_ONLY + unsigned long LastBlockStart; // Uncompressed offset of last block in folder + unsigned long MaxDiskSize; + unsigned long DiskSize; + unsigned long PrevCabinetNumber; // Previous cabinet number (where split file starts) + bool CreateNewDisk; + bool CreateNewFolder; + + CCFDATAStorage *ScratchFile; + FILEHANDLE SourceFile; + bool ContinueFile; + unsigned long TotalBytesLeft; + bool BlockIsSplit; // true if current data block is split + unsigned long NextFolderNumber; // Zero based folder number +#endif /* CAB_READ_ONLY */ +}; + +#endif /* __CABINET_H */ + +/* EOF */ diff --git a/reactos/tools/cabman/cabman.h b/reactos/tools/cabman/cabman.h new file mode 100755 index 00000000000..a4b0351c00e --- /dev/null +++ b/reactos/tools/cabman/cabman.h @@ -0,0 +1,47 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS cabinet manager + * FILE: tools/cabman/cabman.h + * PURPOSE: Cabinet manager header + */ +#ifndef __CABMAN_H +#define __CABMAN_H + +#include "cabinet.h" +#include "dfp.h" + +/* Cabinet manager modes */ +#define CM_MODE_CREATE 0 +#define CM_MODE_DISPLAY 1 +#define CM_MODE_EXTRACT 2 + + +/* Classes */ + +class CCABManager : public CDFParser { +public: + CCABManager(); + virtual ~CCABManager(); + bool ParseCmdline(int argc, char* argv[]); + bool Run(); +private: + void Usage(); + bool CreateCabinet(); + bool DisplayCabinet(); + bool ExtractFromCabinet(); + /* Event handlers */ + virtual bool OnOverwrite(PCFFILE File, char* FileName); + virtual void OnExtract(PCFFILE File, char* FileName); + virtual void OnDiskChange(char* CabinetName, char* DiskLabel); + virtual void OnAdd(PCFFILE Entry, char* FileName); + /* Configuration */ + bool ProcessAll; + unsigned long Mode; + bool PromptOnOverwrite; + char Location[MAX_PATH]; + char FileName[MAX_PATH]; +}; + +#endif /* __CABMAN_H */ + +/* EOF */ diff --git a/reactos/tools/cabman/dff.txt b/reactos/tools/cabman/dff.txt new file mode 100755 index 00000000000..cde00f71d9a --- /dev/null +++ b/reactos/tools/cabman/dff.txt @@ -0,0 +1,55 @@ +Directive File Format for ReactOS Cabinet Manager +------------------------------------------------- + +Directives begin with a period ("."), and are followed by a command +name, and possibly blank delimited arguments. Commands and variable names are +case insensitive. + +Syntax Description +------------------------------------------------------------------------------- +; Anything on a line after this is a comment + [destination] File copy command +.Define variable=[value] Define variable to be equal to value (*) +.Delete variable Delete a variable definition (*) +.New Disk|Cabinet|Folder Start a new disk, cabinet or folder (* -- new disk will work) +.Set variable=[value] Set variable to be equal to value (*) +%variable% Substitute value of variable (*) + Blank lines are ignored +------------------------------------------------------------------------------- + + +Standard variable Description +------------------------------------------------------------------------------- +Cabinet=ON|OFF Turns cabinet mode on or off (* -- currently always on) +CabinetFileCountThreshold=count Threshold count of files per cabinet (*) +CabinetNamen=filename Cabinet file name for cabinet number n +CabinetNameTemplate=template Cabinet file name template + * is replaced by cabinet number +Compress=ON|OFF Turns compression on or off (* -- currently always on) +CompressionType=NONE|MSZIP Compression engine to use (* -- currently always mszip) +DiskLabeln=label Printed disk label name for disk n +DiskLabelTemplate=template Printed disk label name template + * is replaced by disk number +FolderFileCountThreshold=count Threshold count of files per folder (*) +FolderSizeThreshold=size Threshold folder size for current folder (*) +MaxDiskFileCount=count Maximum count of files per disk (*) +MaxDiskSize[n]=size Maximum disk size (for disk n) +ReservePerCabinetSize=size Amount of space to reserve in each cabinet (*) +ReservePerDataBlockSize=size Amount of space to reserve in each data block (*) +ReservePerFolderSize=size Amount of space to reserve in each folder (*) +SourceDir=path Default path for source files (*) +------------------------------------------------------------------------------- +(*) = not implemented + + +MaxDiskSize +----------- +0 means disk size is unlimited. Standard sizes available are: + + 2.88M + 1.44M + 1.25M + 1.2M + 720K + 360K + CDROM diff --git a/reactos/tools/cabman/dfp.cxx b/reactos/tools/cabman/dfp.cxx new file mode 100755 index 00000000000..b0a0720273f --- /dev/null +++ b/reactos/tools/cabman/dfp.cxx @@ -0,0 +1,1179 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS cabinet manager + * FILE: tools/cabman/dfp.cpp + * PURPOSE: Directive file parser + * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) + * NOTES: The directive file format is similar to the + * directive file format used by Microsoft's MAKECAB + * REVISIONS: + * CSH 21/03-2001 Created + * CSH 15/08-2003 Made it portable + */ +#include +#include +#include "cabinet.h" +#include "dfp.h" + + +/* CDFParser */ + +CDFParser::CDFParser() +/* + * FUNCTION: Default constructor + */ +{ + InfFileOnly = false; + DontGenerateInf = false; + + FileBuffer = NULL; + FileLoaded = false; + CurrentOffset = 0; + CurrentLine = 0; + CabinetCreated = false; + DiskCreated = false; + FolderCreated = false; + CabinetName = NULL; + DiskLabel = NULL; + MaxDiskSize = NULL; + + MaxDiskSizeAllSet = false; + CabinetNameTemplateSet = false; + DiskLabelTemplateSet = false; + InfFileNameSet = false; + + InfModeEnabled = false; + InfFileHandle == NULL; +} + +CDFParser::~CDFParser() +/* + * FUNCTION: Default destructor + */ +{ + PCABINET_NAME CNPrev; + PCABINET_NAME CNNext; + PDISK_NUMBER DNPrev; + PDISK_NUMBER DNNext; + + if (FileBuffer) + FreeMemory(FileBuffer); + CNNext = CabinetName; + while (CNNext != NULL) { + CNPrev = CNNext->Next; + FreeMemory(CNNext); + CNNext = CNPrev; + } + CNNext = DiskLabel; + while (CNNext != NULL) { + CNPrev = CNNext->Next; + FreeMemory(CNNext); + CNNext = CNPrev; + } + DNNext = MaxDiskSize; + while (DNNext != NULL) { + DNPrev = DNNext->Next; + FreeMemory(DNNext); + DNNext = DNPrev; + } + + if (InfFileHandle != NULL) { + CloseFile(InfFileHandle); + } +} + +void CDFParser::WriteInfLine(char* InfLine) +{ + char buf[MAX_PATH]; + char eolbuf[2]; + char* destpath; +#if defined(WIN32) + unsigned long BytesWritten; +#endif + + if (DontGenerateInf) { + return; + } + + if (InfFileHandle == NULL) { + if (!InfFileNameSet) { + /* FIXME: Use cabinet name with extension .inf */ + return; + } + + destpath = GetDestinationPath(); + if (strlen(destpath) > 0) { + strcpy(buf, destpath); + strcat(buf, InfFileName); + } else { + strcpy(buf, InfFileName); + } + + /* Create .inf file, overwrite if it already exists */ +#if defined(WIN32) + InfFileHandle = CreateFile(buf, // Create this file + GENERIC_WRITE, // Open for writing + 0, // No sharing + NULL, // No security + CREATE_ALWAYS, // Create or overwrite + FILE_ATTRIBUTE_NORMAL, // Normal file + NULL); // No attribute template + if (InfFileHandle == INVALID_HANDLE_VALUE) { + DPRINT(MID_TRACE, ("Error creating '%d'.\n", (unsigned int)GetLastError())); + return; + } +#else /* !WIN32 */ + InfFileHandle = fopen(buf, "wb"); + if (InfFileHandle == NULL) { + DPRINT(MID_TRACE, ("Error creating '%d'.\n", (unsigned int)errno)); + return; + } +#endif + } + +#if defined(WIN32) + if (!WriteFile(InfFileHandle, InfLine, strlen(InfLine), &BytesWritten, NULL)) { + DPRINT(MID_TRACE, ("ERROR WRITING '%d'.\n", (unsigned int)GetLastError())); + return; + } +#else + if (fwrite(InfLine, strlen(InfLine), 1, InfFileHandle) < 1) + return; +#endif + + eolbuf[0] = 0x0d; + eolbuf[1] = 0x0a; + +#if defined(WIN32) + if (!WriteFile(InfFileHandle, eolbuf, sizeof(eolbuf), &BytesWritten, NULL)) { + DPRINT(MID_TRACE, ("ERROR WRITING '%d'.\n", (unsigned int)GetLastError())); + return; + } +#else + if (fwrite(eolbuf, 1, sizeof(eolbuf), InfFileHandle) < 1) + return; +#endif +} + + +unsigned long CDFParser::Load(char* FileName) +/* + * FUNCTION: Loads a directive file into memory + * ARGUMENTS: + * FileName = Pointer to name of directive file + * RETURNS: + * Status of operation + */ +{ + unsigned long BytesRead; + long FileSize; + + if (FileLoaded) + return CAB_STATUS_SUCCESS; + + /* Create cabinet file, overwrite if it already exists */ +#if defined(WIN32) + FileHandle = CreateFile(FileName, // Create this file + GENERIC_READ, // Open for reading + 0, // No sharing + NULL, // No security + OPEN_EXISTING, // Open the file + FILE_ATTRIBUTE_NORMAL, // Normal file + NULL); // No attribute template + if (FileHandle == INVALID_HANDLE_VALUE) + return CAB_STATUS_CANNOT_OPEN; +#else /* !WIN32 */ + FileHandle = fopen(FileName, "rb"); + if (FileHandle == NULL) { + return CAB_STATUS_CANNOT_OPEN; + } +#endif + + FileSize = GetSizeOfFile(FileHandle); + if (FileSize == (unsigned long)-1) { + CloseFile(FileHandle); + return CAB_STATUS_CANNOT_OPEN; + } + + FileBufferSize = (unsigned long)FileSize; + + FileBuffer = (char*)AllocateMemory(FileBufferSize); + if (!FileBuffer) { + CloseFile(FileHandle); + return CAB_STATUS_NOMEMORY; + } + + if (!ReadFileData(FileHandle, FileBuffer, FileBufferSize, &BytesRead)) { + CloseFile(FileHandle); + FreeMemory(FileBuffer); + FileBuffer = NULL; + return CAB_STATUS_CANNOT_READ; + } + + CloseFile(FileHandle); + + FileLoaded = true; + + DPRINT(MAX_TRACE, ("File (%d bytes)\n", FileBufferSize)); + + return CAB_STATUS_SUCCESS; +} + + +unsigned long CDFParser::Parse() +/* + * FUNCTION: Parses a loaded directive file + * RETURNS: + * Status of operation + */ +{ + bool Command; + unsigned long Status; + + if (!FileLoaded) + return CAB_STATUS_NOFILE; + + while (ReadLine()) { + Command = false; + + if (InfModeEnabled) { + bool WriteLine = true; + while (CurrentToken != TokenEnd) { + switch (CurrentToken) { + case TokenIdentifier: + if (Command) { + /* Command */ + Status = PerformCommand(); + if (Status == CAB_STATUS_FAILURE) { + WriteLine = true; + } else { + if (!InfModeEnabled) { + WriteLine = false; + } + } + CurrentToken = TokenEnd; + continue; + } else { + WriteLine = true; + CurrentToken = TokenEnd; + continue; + } + break; + case TokenSpace: + break; + case TokenPeriod: + Command = true; + break; + default: + WriteLine = true; + CurrentToken = TokenEnd; + continue; + } + NextToken(); + } + if (WriteLine) { + WriteInfLine(Line); + } + } else { + while (CurrentToken != TokenEnd) { + switch (CurrentToken) { + case TokenInteger: + sprintf(CurrentString, "%d", CurrentInteger); + case TokenIdentifier: + if (Command) { + /* Command */ + Status = PerformCommand(); + + if (Status == CAB_STATUS_FAILURE) { + printf("Directive file contains errors at line %d.\n", (unsigned int)CurrentLine); + DPRINT(MID_TRACE, ("Error while executing command.\n")); + } + + if (Status != CAB_STATUS_SUCCESS) + return Status; + } else { + /* File copy */ + Status = PerformFileCopy(); + + if (Status == CAB_STATUS_FAILURE) { + printf("Directive file contains errors at line %d.\n", (unsigned int)CurrentLine); + DPRINT(MID_TRACE, ("Error while copying file.\n")); + } + + if (Status != CAB_STATUS_SUCCESS) + return Status; + } + break; + case TokenSpace: + break; + case TokenSemi: + CurrentToken = TokenEnd; + continue; + case TokenPeriod: + Command = true; + break; + default: + printf("Directive file contains errors at line %d.\n", (unsigned int)CurrentLine); + DPRINT(MID_TRACE, ("Token is (%d).\n", (unsigned int)CurrentToken)); + return CAB_STATUS_SUCCESS; + } + NextToken(); + } + } + } + + if (!InfFileOnly) { + printf("\nWriting cabinet. This may take a while...\n\n"); + + if (DiskCreated) { + Status = WriteDisk(false); + if (Status == CAB_STATUS_SUCCESS) + Status = CloseDisk(); + if (Status != CAB_STATUS_SUCCESS) { + DPRINT(MIN_TRACE, ("Cannot write disk (%d).\n", (unsigned int)Status)); + return Status; + } + } + + if (CabinetCreated) { + Status = CloseCabinet(); + if (Status != CAB_STATUS_SUCCESS) { + DPRINT(MIN_TRACE, ("Cannot close cabinet (%d).\n", (unsigned int)Status)); + return Status; + } + } + + printf("\nDone.\n"); + } + + return CAB_STATUS_SUCCESS; +} + + +bool CDFParser::OnDiskLabel(unsigned long Number, char* Label) +/* + * FUNCTION: Called when a disk needs a label + * ARGUMENTS: + * Number = Cabinet number that needs a label + * Label = Pointer to buffer to place label of disk + * RETURNS: + * true if a disk label was returned, false if not + */ +{ + char Buffer[20]; + int i, j; + char ch; + + Number += 1; + + DPRINT(MID_TRACE, ("Giving disk (%d) a label...\n", (unsigned int)Number)); + + if (GetDiskName(&DiskLabel, Number, Label)) + return true; + + if (DiskLabelTemplateSet) { + j = 0; + strcpy(Label, ""); + for (i = 0; i < strlen(DiskLabelTemplate); i++) { + ch = DiskLabelTemplate[i]; + if (ch == '*') { + sprintf(Buffer, "%d", Number); + strcat(Label, Buffer); + j += strlen(Buffer); + } else { + Label[j] = ch; + j++; + } + Label[j] = '\0'; + } + + DPRINT(MID_TRACE, ("Giving disk (%s) as a label...\n", Label)); + + return true; + } else + return false; +} + + +bool CDFParser::OnCabinetName(unsigned long Number, char* Name) +/* + * FUNCTION: Called when a cabinet needs a name + * ARGUMENTS: + * Number = Disk number that needs a name + * Name = Pointer to buffer to place name of cabinet + * RETURNS: + * true if a cabinet name was returned, false if not + */ +{ + char Buffer[MAX_PATH]; + int i, j; + char ch; + + Number += 1; + + DPRINT(MID_TRACE, ("Giving cabinet (%d) a name...\n", (unsigned int)Number)); + + if (GetDiskName(&CabinetName, Number, Buffer)) { + strcpy(Name, GetDestinationPath()); + strcat(Name, Buffer); + return true; + } + + if (CabinetNameTemplateSet) { + strcpy(Name, GetDestinationPath()); + j = strlen(Name); + for (i = 0; i < strlen(CabinetNameTemplate); i++) { + ch = CabinetNameTemplate[i]; + if (ch == '*') { + sprintf(Buffer, "%d", Number); + strcat(Name, Buffer); + j += strlen(Buffer); + } else { + Name[j] = ch; + j++; + } + Name[j] = '\0'; + } + + DPRINT(MID_TRACE, ("Giving cabinet (%s) as a name...\n", Name)); + return true; + } else { + return false; + } +} + + +bool CDFParser::SetDiskName(PCABINET_NAME *List, unsigned long Number, char* String) +/* + * FUNCTION: Sets an entry in a list + * ARGUMENTS: + * List = Address of pointer to list + * Number = Disk number + * String = Pointer to string + * RETURNS: + * false if there was not enough free memory available + */ +{ + PCABINET_NAME CN; + + CN = *List; + while (CN != NULL) { + if (CN->DiskNumber == Number) { + strcpy(CN->Name, String); + return true; + } + CN = CN->Next; + } + + CN = (PCABINET_NAME)AllocateMemory(sizeof(CABINET_NAME)); + if (!CN) + return false; + + CN->DiskNumber = Number; + strcpy(CN->Name, String); + + CN->Next = *List; + *List = CN; + + return true; +} + + +bool CDFParser::GetDiskName(PCABINET_NAME *List, unsigned long Number, char* String) +/* + * FUNCTION: Returns an entry in a list + * ARGUMENTS: + * List = Address of pointer to list + * Number = Disk number + * String = Address of buffer to copy string to + * RETURNS: + * false if there was not enough free memory available + */ +{ + PCABINET_NAME CN; + + CN = *List; + while (CN != NULL) { + if (CN->DiskNumber == Number) { + strcpy(String, CN->Name); + return true; + } + CN = CN->Next; + } + + return false; +} + + +bool CDFParser::SetDiskNumber(PDISK_NUMBER *List, unsigned long Number, unsigned long Value) +/* + * FUNCTION: Sets an entry in a list + * ARGUMENTS: + * List = Address of pointer to list + * Number = Disk number + * Value = Value to set + * RETURNS: + * false if there was not enough free memory available + */ +{ + PDISK_NUMBER DN; + + DN = *List; + while (DN != NULL) { + if (DN->DiskNumber == Number) { + DN->Number = Value; + return true; + } + DN = DN->Next; + } + + DN = (PDISK_NUMBER)AllocateMemory(sizeof(DISK_NUMBER)); + if (!DN) + return false; + + DN->DiskNumber = Number; + DN->Number = Value; + + DN->Next = *List; + *List = DN; + + return true; +} + + +bool CDFParser::GetDiskNumber(PDISK_NUMBER *List, unsigned long Number, unsigned long* Value) +/* + * FUNCTION: Returns an entry in a list + * ARGUMENTS: + * List = Address of pointer to list + * Number = Disk number + * Value = Address of buffer to place value + * RETURNS: + * true if the entry was found + */ +{ + PDISK_NUMBER DN; + + DN = *List; + while (DN != NULL) { + if (DN->DiskNumber == Number) { + *Value = DN->Number; + return true; + } + DN = DN->Next; + } + + return false; +} + + +bool CDFParser::DoDiskLabel(unsigned long Number, char* Label) +/* + * FUNCTION: Sets the label of a disk + * ARGUMENTS: + * Number = Disk number + * Label = Pointer to label of disk + * RETURNS: + * false if there was not enough free memory available + */ +{ + DPRINT(MID_TRACE, ("Setting label of disk (%d) to '%s'\n", (unsigned int)Number, Label)); + + return SetDiskName(&DiskLabel, Number, Label); +} + + +void CDFParser::DoDiskLabelTemplate(char* Template) +/* + * FUNCTION: Sets a disk label template to use + * ARGUMENTS: + * Template = Pointer to disk label template + */ +{ + DPRINT(MID_TRACE, ("Setting disk label template to '%s'\n", Template)); + + strcpy(DiskLabelTemplate, Template); + DiskLabelTemplateSet = true; +} + + +bool CDFParser::DoCabinetName(unsigned long Number, char* Name) +/* + * FUNCTION: Sets the name of a cabinet + * ARGUMENTS: + * Number = Disk number + * Name = Pointer to name of cabinet + * RETURNS: + * false if there was not enough free memory available + */ +{ + DPRINT(MID_TRACE, ("Setting name of cabinet (%d) to '%s'\n", (unsigned int)Number, Name)); + + return SetDiskName(&CabinetName, Number, Name); +} + + +void CDFParser::DoCabinetNameTemplate(char* Template) +/* + * FUNCTION: Sets a cabinet name template to use + * ARGUMENTS: + * Template = Pointer to cabinet name template + */ +{ + DPRINT(MID_TRACE, ("Setting cabinet name template to '%s'\n", Template)); + + strcpy(CabinetNameTemplate, Template); + CabinetNameTemplateSet = true; +} + + +unsigned long CDFParser::DoMaxDiskSize(bool NumberValid, unsigned long Number) +/* + * FUNCTION: Sets the maximum disk size + * ARGUMENTS: + * NumberValid = true if disk number is valid + * Number = Disk number + * RETURNS: + * Status of operation + * NOTES: + * Standard sizes are 2.88M, 1.44M, 1.25M, 1.2M, 720K, 360K, and CDROM + */ +{ + unsigned long A, B, Value; + + if (IsNextToken(TokenInteger, true)) { + + A = CurrentInteger; + + if (IsNextToken(TokenPeriod, false)) { + if (!IsNextToken(TokenInteger, false)) + return CAB_STATUS_FAILURE; + + B = CurrentInteger; + + } else + B = 0; + + if (CurrentToken == TokenIdentifier) { + switch (CurrentString[0]) { + case 'K': + if (B != 0) + return CAB_STATUS_FAILURE; + + if (A == 720) + /* 720K disk */ + Value = 730112; + else if (A == 360) + /* 360K disk */ + Value = 362496; + else + return CAB_STATUS_FAILURE; + break; + case 'M': + if (A == 1) { + if (B == 44) + /* 1.44M disk */ + Value = 1457664; + else if (B == 25) + /* 1.25M disk */ + Value = 1300000; // FIXME: Value? + else if (B == 2) + /* 1.2M disk */ + Value = 1213952; + else + return CAB_STATUS_FAILURE; + } else if (A == 2) { + if (B == 88) + /* 2.88M disk */ + Value = 2915328; + else + return CAB_STATUS_FAILURE; + } else + return CAB_STATUS_FAILURE; + break; + default: + DPRINT(MID_TRACE, ("Bad suffix (%c)\n", CurrentString[0])); + return CAB_STATUS_FAILURE; + } + } else + Value = A; + } else { + if ((CurrentToken != TokenString) && + (strcasecmp(CurrentString, "CDROM") != 0)) + return CAB_STATUS_FAILURE; + /* CDROM */ + Value = 640*1024*1024; // FIXME: Correct size for CDROM? + } + + if (NumberValid) + return (SetDiskNumber(&MaxDiskSize, Number, Value)? + CAB_STATUS_SUCCESS : CAB_STATUS_FAILURE); + + MaxDiskSizeAll = Value; + MaxDiskSizeAllSet = true; + + SetMaxDiskSize(Value); + + return CAB_STATUS_SUCCESS; +} + + +void CDFParser::DoInfFileName(char* FileName) +/* + * FUNCTION: Sets filename of the generated .inf file + * ARGUMENTS: + * FileName = Pointer to .inf filename + */ +{ + DPRINT(MID_TRACE, ("Setting .inf filename to '%s'\n", FileName)); + + strcpy(InfFileName, FileName); + InfFileNameSet = true; +} + +unsigned long CDFParser::SetupNewDisk() +/* + * FUNCTION: Sets up parameters for a new disk + * RETURNS: + * Status of operation + */ +{ + unsigned long Value; + + if (!GetDiskNumber(&MaxDiskSize, GetCurrentDiskNumber(), &Value)) { + if (MaxDiskSizeAllSet) + Value = MaxDiskSizeAll; + else + Value = 0; + } + SetMaxDiskSize(Value); + + return CAB_STATUS_SUCCESS; +} + + +unsigned long CDFParser::PerformSetCommand() +/* + * FUNCTION: Performs a set variable command + * RETURNS: + * Status of operation + */ +{ + SETTYPE SetType; + bool NumberValid = false; + unsigned long Number = 0; + + if (!IsNextToken(TokenIdentifier, true)) + return CAB_STATUS_FAILURE; + + if (strcasecmp(CurrentString, "DiskLabel") == 0) + SetType = stDiskLabel; + else if (strcasecmp(CurrentString, "DiskLabelTemplate") == 0) + SetType = stDiskLabelTemplate; + else if (strcasecmp(CurrentString, "CabinetName") == 0) + SetType = stCabinetName; + else if (strcasecmp(CurrentString, "CabinetNameTemplate") == 0) + SetType = stCabinetNameTemplate; + else if (strcasecmp(CurrentString, "MaxDiskSize") == 0) + SetType = stMaxDiskSize; + else if (strcasecmp(CurrentString, "InfFileName") == 0) + SetType = stInfFileName; + else + return CAB_STATUS_FAILURE; + + if ((SetType == stDiskLabel) || (SetType == stCabinetName)) { + if (!IsNextToken(TokenInteger, false)) + return CAB_STATUS_FAILURE; + Number = CurrentInteger; + + if (!IsNextToken(TokenEqual, true)) + return CAB_STATUS_FAILURE; + } else if (SetType == stMaxDiskSize) { + if (IsNextToken(TokenInteger, false)) { + NumberValid = true; + Number = CurrentInteger; + } else { + NumberValid = false; + while (CurrentToken == TokenSpace) + NextToken(); + if (CurrentToken != TokenEqual) + return CAB_STATUS_FAILURE; + } + } else if (!IsNextToken(TokenEqual, true)) + return CAB_STATUS_FAILURE; + + if (SetType != stMaxDiskSize) { + if (!IsNextToken(TokenString, true)) + return CAB_STATUS_FAILURE; + } + + switch (SetType) { + case stDiskLabel: + if (!DoDiskLabel(Number, CurrentString)) + DPRINT(MIN_TRACE, ("Not enough available free memory.\n")); + return CAB_STATUS_SUCCESS; + case stCabinetName: + if (!DoCabinetName(Number, CurrentString)) + DPRINT(MIN_TRACE, ("Not enough available free memory.\n")); + return CAB_STATUS_SUCCESS; + case stDiskLabelTemplate: + DoDiskLabelTemplate(CurrentString); + return CAB_STATUS_SUCCESS; + case stCabinetNameTemplate: + DoCabinetNameTemplate(CurrentString); + return CAB_STATUS_SUCCESS; + case stMaxDiskSize: + return DoMaxDiskSize(NumberValid, Number); + case stInfFileName: + DoInfFileName(CurrentString); + return CAB_STATUS_SUCCESS; + default: + return CAB_STATUS_FAILURE; + } +} + + +unsigned long CDFParser::PerformNewCommand() +/* + * FUNCTION: Performs a new disk|cabinet|folder command + * RETURNS: + * Status of operation + */ +{ + NEWTYPE NewType; + unsigned long Status; + + if (!IsNextToken(TokenIdentifier, true)) + return CAB_STATUS_FAILURE; + + if (strcasecmp(CurrentString, "Disk") == 0) + NewType = ntDisk; + else if (strcasecmp(CurrentString, "Cabinet") == 0) + NewType = ntCabinet; + else if (strcasecmp(CurrentString, "Folder") == 0) + NewType = ntFolder; + else + return CAB_STATUS_FAILURE; + + switch (NewType) { + case ntDisk: + if (DiskCreated) { + Status = WriteDisk(true); + if (Status == CAB_STATUS_SUCCESS) + Status = CloseDisk(); + if (Status != CAB_STATUS_SUCCESS) { + DPRINT(MIN_TRACE, ("Cannot write disk (%d).\n", (unsigned int)Status)); + return CAB_STATUS_SUCCESS; + } + DiskCreated = false; + } + + Status = NewDisk(); + if (Status != CAB_STATUS_SUCCESS) { + DPRINT(MIN_TRACE, ("Cannot create disk (%d).\n", (unsigned int)Status)); + return CAB_STATUS_SUCCESS; + } + DiskCreated = true; + SetupNewDisk(); + return CAB_STATUS_SUCCESS; + case ntCabinet: + if (DiskCreated) { + Status = WriteDisk(true); + if (Status == CAB_STATUS_SUCCESS) + Status = CloseDisk(); + if (Status != CAB_STATUS_SUCCESS) { + DPRINT(MIN_TRACE, ("Cannot write disk (%d).\n", (unsigned int)Status)); + return CAB_STATUS_SUCCESS; + } + DiskCreated = false; + } + + Status = NewCabinet(); + if (Status != CAB_STATUS_SUCCESS) { + DPRINT(MIN_TRACE, ("Cannot create cabinet (%d).\n", (unsigned int)Status)); + return CAB_STATUS_SUCCESS; + } + DiskCreated = true; + SetupNewDisk(); + return CAB_STATUS_SUCCESS; + case ntFolder: + Status = NewFolder(); + ASSERT(Status == CAB_STATUS_SUCCESS); + return CAB_STATUS_SUCCESS; + default: + return CAB_STATUS_FAILURE; + } +} + + +unsigned long CDFParser::PerformInfBeginCommand() +/* + * FUNCTION: Begins inf mode + * RETURNS: + * Status of operation + */ +{ + InfModeEnabled = true; + return CAB_STATUS_SUCCESS; +} + + +unsigned long CDFParser::PerformInfEndCommand() +/* + * FUNCTION: Begins inf mode + * RETURNS: + * Status of operation + */ +{ + InfModeEnabled = false; + return CAB_STATUS_SUCCESS; +} + + +unsigned long CDFParser::PerformCommand() +/* + * FUNCTION: Performs a command + * RETURNS: + * Status of operation + */ +{ + if (strcasecmp(CurrentString, "Set") == 0) + return PerformSetCommand(); + if (strcasecmp(CurrentString, "New") == 0) + return PerformNewCommand(); + if (strcasecmp(CurrentString, "InfBegin") == 0) + return PerformInfBeginCommand(); + if (strcasecmp(CurrentString, "InfEnd") == 0) + return PerformInfEndCommand(); + + return CAB_STATUS_FAILURE; +} + + +unsigned long CDFParser::PerformFileCopy() +/* + * FUNCTION: Performs a file copy + * RETURNS: + * Status of operation + */ +{ + unsigned long Status; + unsigned long i, j; + char ch; + char SrcName[MAX_PATH]; + char DstName[MAX_PATH]; + char InfLine[MAX_PATH]; + + strcpy(SrcName, ""); + strcpy(DstName, ""); + + i = strlen(CurrentString); + while ((i < LineLength) && + ((ch = Line[i]) != ' ') && + (ch != 0x09) && + (ch != ';')) { + CurrentString[i] = ch; + i++; + } + CurrentString[i] = '\0'; + CurrentToken = TokenString; + CurrentChar = i + 1; + strcpy(SrcName, CurrentString); + + SkipSpaces(); + + if (CurrentToken != TokenEnd) { + j = strlen(CurrentString); i = 0; + while ((CurrentChar + i < LineLength) && + ((ch = Line[CurrentChar + i]) != ' ') && + (ch != 0x09) && + (ch != ';')) { + CurrentString[j + i] = ch; + i++; + } + CurrentString[j + i] = '\0'; + CurrentToken = TokenString; + CurrentChar += i + 1; + strcpy(DstName, CurrentString); + } + + if (!CabinetCreated) { + + DPRINT(MID_TRACE, ("Creating cabinet.\n")); + + Status = NewCabinet(); + if (Status != CAB_STATUS_SUCCESS) { + DPRINT(MIN_TRACE, ("Cannot create cabinet (%d).\n", (unsigned int)Status)); + printf("Cannot create cabinet.\n"); + return CAB_STATUS_FAILURE; + } + CabinetCreated = true; + + DPRINT(MID_TRACE, ("Creating disk.\n")); + + Status = NewDisk(); + if (Status != CAB_STATUS_SUCCESS) { + DPRINT(MIN_TRACE, ("Cannot create disk (%d).\n", (unsigned int)Status)); + printf("Cannot create disk.\n"); + return CAB_STATUS_FAILURE; + } + DiskCreated = true; + SetupNewDisk(); + } + + DPRINT(MID_TRACE, ("Adding file: '%s' destination: '%s'.\n", SrcName, DstName)); + + sprintf(InfLine, "%s=%s", GetFileName(SrcName), DstName); + WriteInfLine(InfLine); + + Status = AddFile(SrcName); + if (Status != CAB_STATUS_SUCCESS) { + if (Status == CAB_STATUS_CANNOT_OPEN) + printf("File does not exist: %s.\n", SrcName); + else if (Status == CAB_STATUS_NOMEMORY) + printf("Insufficient memory to add file: %s.\n", SrcName); + else + printf("Cannot add file: %s (%d).\n", SrcName, Status); + return Status; + } + + return CAB_STATUS_SUCCESS; +} + + +void CDFParser::SkipSpaces() +/* + * FUNCTION: Skips any spaces in the current line + */ +{ + NextToken(); + while (CurrentToken == TokenSpace) + NextToken(); +} + + +bool CDFParser::IsNextToken(TOKEN Token, bool NoSpaces) +/* + * FUNCTION: Checks if next token equals Token + * ARGUMENTS: + * Token = Token to compare with + * SkipSp = true if spaces should be skipped + * RETURNS: + * false if next token is diffrent from Token + */ +{ + if (NoSpaces) + SkipSpaces(); + else + NextToken(); + return (CurrentToken == Token); +} + + +bool CDFParser::ReadLine() +/* + * FUNCTION: Reads the next line into the line buffer + * RETURNS: + * true if there is a new line, false if not + */ +{ + unsigned long i, j; + char ch; + + if (CurrentOffset >= FileBufferSize) + return false; + + i = 0; + while (((j = CurrentOffset + i) < FileBufferSize) && (i < 127) && + ((ch = FileBuffer[j]) != 0x0D && (ch = FileBuffer[j]) != 0x0A)) { + Line[i] = ch; + i++; + } + + Line[i] = '\0'; + LineLength = i; + + if ((FileBuffer[CurrentOffset + i] == 0x0D) && (FileBuffer[CurrentOffset + i + 1] == 0x0A)) + CurrentOffset++; + + CurrentOffset += i + 1; + + CurrentChar = 0; + + CurrentLine++; + + NextToken(); + + return true; +} + + +void CDFParser::NextToken() +/* + * FUNCTION: Reads the next token from the current line + */ +{ + unsigned long i; + char ch = ' '; + + if (CurrentChar >= LineLength) { + CurrentToken = TokenEnd; + return; + } + + switch (Line[CurrentChar]) { + case ' ': + case 0x09: CurrentToken = TokenSpace; + break; + case ';': CurrentToken = TokenSemi; + break; + case '=': CurrentToken = TokenEqual; + break; + case '.': CurrentToken = TokenPeriod; + break; + case '\\': CurrentToken = TokenBackslash; + break; + case '"': + i = 0; + while ((CurrentChar + i + 1 < LineLength) && + ((ch = Line[CurrentChar + i + 1]) != '"')) { + CurrentString[i] = ch; + i++; + } + CurrentString[i] = '\0'; + CurrentToken = TokenString; + CurrentChar += i + 2; + return; + default: + i = 0; + while ((CurrentChar + i < LineLength) && + ((ch = Line[CurrentChar + i]) >= '0') && (ch <= '9')) { + CurrentString[i] = ch; + i++; + } + if (i > 0) { + CurrentString[i] = '\0'; + CurrentInteger = atoi((char*)CurrentString); + CurrentToken = TokenInteger; + CurrentChar += i; + return; + } + i = 0; + while (((CurrentChar + i < LineLength) && + (((ch = Line[CurrentChar + i]) >= 'a') && (ch <= 'z')) || + ((ch >= 'A') && (ch <= 'Z')) || (ch == '_'))) { + CurrentString[i] = ch; + i++; + } + if (i > 0) { + CurrentString[i] = '\0'; + CurrentToken = TokenIdentifier; + CurrentChar += i; + return; + } + CurrentToken = TokenEnd; + } + CurrentChar++; +} + +/* EOF */ diff --git a/reactos/tools/cabman/dfp.h b/reactos/tools/cabman/dfp.h new file mode 100755 index 00000000000..8a127bcc199 --- /dev/null +++ b/reactos/tools/cabman/dfp.h @@ -0,0 +1,141 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS cabinet manager + * FILE: tools/cabman/dfp.h + * PURPOSE: Directive file parser header + */ +#ifndef __DFP_H +#define __DFP_H + +#include "cabinet.h" + +typedef struct _CABINET_NAME { + struct _CABINET_NAME *Next; + unsigned long DiskNumber; + char Name[128]; +} CABINET_NAME, *PCABINET_NAME; + +typedef struct _DISK_NUMBER { + struct _DISK_NUMBER *Next; + unsigned long DiskNumber; + unsigned long Number; +} DISK_NUMBER, *PDISK_NUMBER; + +typedef enum { + TokenUnknown, + TokenInteger, + TokenIdentifier, + TokenString, + TokenSpace, + TokenSemi, + TokenEqual, + TokenPeriod, + TokenBackslash, + TokenEnd, +} TOKEN; + + +typedef enum { + stCabinetName, + stCabinetNameTemplate, + stDiskLabel, + stDiskLabelTemplate, + stMaxDiskSize, + stInfFileName +} SETTYPE; + + +typedef enum { + ntDisk, + ntCabinet, + ntFolder, +} NEWTYPE; + + +/* Classes */ + +class CDFParser : public CCabinet { +public: + CDFParser(); + virtual ~CDFParser(); + unsigned long Load(char* FileName); + unsigned long Parse(); + bool InfFileOnly; + bool DontGenerateInf; +private: + /* Event handlers */ + virtual bool OnDiskLabel(unsigned long Number, char* Label); + virtual bool OnCabinetName(unsigned long Number, char* Name); + + void WriteInfLine(char* InfLine); + bool SetDiskName(PCABINET_NAME *List, unsigned long Number, char* String); + bool GetDiskName(PCABINET_NAME *List, unsigned long Number, char* String); + bool SetDiskNumber(PDISK_NUMBER *List, unsigned long Number, unsigned long Value); + bool GetDiskNumber(PDISK_NUMBER *List, unsigned long Number, unsigned long* Value); + bool DoDiskLabel(unsigned long Number, char* Label); + void DoDiskLabelTemplate(char* Template); + bool DoCabinetName(unsigned long Number, char* Name); + void DoCabinetNameTemplate(char* Template); + void DoInfFileName(char* InfFileName); + unsigned long DoMaxDiskSize(bool NumberValid, unsigned long Number); + unsigned long SetupNewDisk(); + unsigned long PerformSetCommand(); + unsigned long PerformNewCommand(); + unsigned long PerformInfBeginCommand(); + unsigned long PerformInfEndCommand(); + unsigned long PerformCommand(); + unsigned long PerformFileCopy(); + void SkipSpaces(); + bool IsNextToken(TOKEN Token, bool NoSpaces); + bool ReadLine(); + void NextToken(); + /* Parsing */ + bool FileLoaded; + FILEHANDLE FileHandle; + char* FileBuffer; + unsigned long FileBufferSize; + unsigned long CurrentOffset; + char Line[128]; + unsigned long LineLength; + unsigned long CurrentLine; + unsigned long CurrentChar; + /* Token */ + TOKEN CurrentToken; + unsigned long CurrentInteger; + char CurrentString[256]; + + /* State */ + bool CabinetCreated; + bool DiskCreated; + bool FolderCreated; + /* Standard directive variable */ + bool Cabinet; + unsigned long CabinetFileCountThreshold; + PCABINET_NAME CabinetName; + bool CabinetNameTemplateSet; + char CabinetNameTemplate[128]; + bool InfFileNameSet; + char InfFileName[256]; + bool Compress; + unsigned long CompressionType; + PCABINET_NAME DiskLabel; + bool DiskLabelTemplateSet; + char DiskLabelTemplate[128]; + unsigned long FolderFileCountThreshold; + unsigned long FolderSizeThreshold; + unsigned long MaxCabinetSize; + unsigned long MaxDiskFileCount; + PDISK_NUMBER MaxDiskSize; + bool MaxDiskSizeAllSet; + unsigned long MaxDiskSizeAll; + unsigned long ReservePerCabinetSize; + unsigned long ReservePerDataBlockSize; + unsigned long ReservePerFolderSize; + char SourceDir[256]; + FILEHANDLE InfFileHandle; + bool InfModeEnabled; +}; + +#endif /* __DFP_H */ + +/* EOF */ diff --git a/reactos/tools/cabman/main.cxx b/reactos/tools/cabman/main.cxx new file mode 100755 index 00000000000..a94de6687a1 --- /dev/null +++ b/reactos/tools/cabman/main.cxx @@ -0,0 +1,518 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS cabinet manager + * FILE: tools/cabman/main.cpp + * PURPOSE: Main program + * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) + * REVISIONS: + * CSH 21/03-2001 Created + * CSH 15/08-2003 Made it portable + */ +#include +#include +#include +#include +#include "cabman.h" + + +#ifdef DBG + +unsigned long DebugTraceLevel = MIN_TRACE; +//unsigned long DebugTraceLevel = MID_TRACE; +//unsigned long DebugTraceLevel = MAX_TRACE; + +#endif /* DBG */ + + +#define CM_VERSION "0.9" + + +char* Pad(char* Str, char PadChar, unsigned int Length) +/* + * FUNCTION: Pads a string with a character to make a given length + * ARGUMENTS: + * Str = Pointer to string to pad + * PadChar = Character to pad with + * Length = Disired length of string + * RETURNS: + * Pointer to string + * NOTES: + * Str must be at least Length + 1 bytes + */ +{ + unsigned int Len; + + Len = strlen(Str); + + if (Len < Length) { + memcpy(&Str[Length - Len], Str, Len + 1); + memset(Str, PadChar, Length - Len); + } + return Str; +} + + +char* Date2Str(char* Str, unsigned short Date) +/* + * FUNCTION: Converts a DOS style date to a string + * ARGUMENTS: + * Str = Pointer to destination string + * Date = DOS style date + * RETURNS: + * Pointer to string + */ +{ + unsigned long dw; + + /* Month */ + Str[0] = (char)('0' + ((Date & 0x01E0) >> 5) / 10); + Str[1] = (char)('0' + ((Date & 0x01E0) >> 5) % 10); + Str[2] = '-'; + /* Day */ + Str[3] = (char)('0' + (Date & 0x001F) / 10); + Str[4] = (char)('0' + (Date & 0x001F) % 10); + Str[5] = '-'; + /* Year */ + dw = 1980 + ((Date & 0xFE00) >> 9); + Str[6] = (char)('0' + dw / 1000); dw %= 1000; + Str[7] = (char)('0' + dw / 100); dw %= 100; + Str[8] = (char)('0' + dw / 10); dw %= 10; + Str[9] = (char)('0' + dw % 10); + Str[10] = '\0'; + return Str; +} + + +char* Time2Str(char* Str, unsigned short Time) +/* + * FUNCTION: Converts a DOS style time to a string + * ARGUMENTS: + * Str = Pointer to destination string + * Time = DOS style time + * RETURNS: + * Pointer to string + */ +{ + bool PM; + unsigned long Hour; + unsigned long dw; + + Hour = ((Time & 0xF800) >> 11); + PM = (Hour >= 12); + Hour %= 12; + if (Hour == 0) + Hour = 12; + + if (Hour >= 10) + Str[0] = (char)('0' + Hour / 10); + else Str[0] = ' '; + Str[1] = (char)('0' + Hour % 10); + Str[2] = ':'; + /* Minute */ + Str[3] = (char)('0' + ((Time & 0x07E0) >> 5) / 10); + Str[4] = (char)('0' + ((Time & 0x07E0) >> 5) % 10); + Str[5] = ':'; + /* Second */ + dw = 2 * (Time & 0x001F); + Str[6] = (char)('0' + dw / 10); + Str[7] = (char)('0' + dw % 10); + + Str[8] = PM? 'p' : 'a'; + Str[9] = '\0'; + return Str; +} + + +char* Attr2Str(char* Str, unsigned short Attr) +/* + * FUNCTION: Converts attributes to a string + * ARGUMENTS: + * Str = Pointer to destination string + * Attr = Attributes + * RETURNS: + * Pointer to string + */ +{ + /* Archive */ + if (Attr & CAB_ATTRIB_ARCHIVE) + Str[0] = 'A'; + else + Str[0] = '-'; + + /* Hidden */ + if (Attr & CAB_ATTRIB_HIDDEN) + Str[1] = 'H'; + else + Str[1] = '-'; + + /* Read only */ + if (Attr & CAB_ATTRIB_READONLY) + Str[2] = 'R'; + else + Str[2] = '-'; + + /* System */ + if (Attr & CAB_ATTRIB_SYSTEM) + Str[3] = 'S'; + else + Str[3] = '-'; + + Str[4] = '\0'; + return Str; +} + + +/* CCABManager */ + +CCABManager::CCABManager() +/* + * FUNCTION: Default constructor + */ +{ + ProcessAll = false; + InfFileOnly = false; + Mode = CM_MODE_DISPLAY; +} + + +CCABManager::~CCABManager() +/* + * FUNCTION: Default destructor + */ +{ +} + + +void CCABManager::Usage() +/* + * FUNCTION: Display usage information on screen + */ +{ + printf("ReactOS Cabinet Manager - Version %s\n\n", CM_VERSION); + printf("CABMAN [/D | /E] [/A] [/L dir] cabinet [filename ...]\n"); + printf("CABMAN /C dirfile [/I] [/RC file]\n"); + printf(" cabinet Cabinet file.\n"); + printf(" filename Name of the file to extract from the cabinet.\n"); + printf(" Wild cards and multiple filenames\n"); + printf(" (separated by blanks) may be used.\n\n"); + + printf(" dirfile Name of the directive file to use.\n"); + + printf(" /A Process ALL cabinets. Follows cabinet chain\n"); + printf(" starting in first cabinet mentioned.\n"); + printf(" /C Create cabinet.\n"); + printf(" /D Display cabinet directory.\n"); + printf(" /E Extract files from cabinet.\n"); + printf(" /I Don't create the cabinet, only the .inf file.\n"); + printf(" /L dir Location to place extracted or generated files\n"); + printf(" (default is current directory).\n"); + printf(" /N Don't create the .inf file, only the cabinet.\n"); + printf(" /RC Specify file to put in cabinet reserved area\n"); + printf(" (size must be less than 64KB).\n"); +} + +bool CCABManager::ParseCmdline(int argc, char* argv[]) +/* + * FUNCTION: Parse command line arguments + * ARGUMENTS: + * argc = Number of arguments on command line + * argv = Pointer to list of command line arguments + * RETURNS: + * true if command line arguments was successfully parsed, false if not + */ +{ + int i; + bool ShowUsage; + bool FoundCabinet = false; + + ShowUsage = (argc < 2); + + for (i = 1; i < argc; i++) { + if (argv[i][0] == '/') { + switch (argv[i][1]) { + case 'a': + case 'A': ProcessAll = true; break; + case 'c': + case 'C': Mode = CM_MODE_CREATE; break; + case 'd': + case 'D': Mode = CM_MODE_DISPLAY; break; + case 'e': + case 'E': Mode = CM_MODE_EXTRACT; break; + case 'i': + case 'I': InfFileOnly = true; break; + case 'l': + case 'L': + if (argv[i][2] == 0) { + i++; + SetDestinationPath((char*)&argv[i][0]); + } else { + SetDestinationPath((char*)&argv[i][1]); + } + break; + case 'n': + case 'N': DontGenerateInf = true; break; + case 'R': + switch (argv[i][2]) { + case 'C': /* File to put in cabinet reserved area */ + if (argv[i][3] == 0) { + i++; + if (!SetCabinetReservedFile((char*)&argv[i][0])) { + printf("Cannot open cabinet reserved area file.\n"); + return false; + } + } else { + if (!SetCabinetReservedFile((char*)&argv[i][3])) { + printf("Cannot open cabinet reserved area file.\n"); + return false; + } + } + break; + default: + printf("Bad parameter %s.\n", argv[i]); + return false; + } + break; + default: + printf("Bad parameter %s.\n", argv[i]); + return false; + } + } else { + if ((FoundCabinet) || (Mode == CM_MODE_CREATE)) { + /* FIXME: There may be many of these if Mode != CM_MODE_CREATE */ + strcpy((char*)FileName, argv[i]); + } else { + SetCabinetName(argv[i]); + FoundCabinet = true; + } + } + } + + if (ShowUsage) { + Usage(); + return false; + } + + /* FIXME */ + SelectCodec(CAB_CODEC_MSZIP); + + return true; +} + + +bool CCABManager::CreateCabinet() +/* + * FUNCTION: Create cabinet + */ +{ + unsigned long Status; + + Status = Load((char*)&FileName); + if (Status != CAB_STATUS_SUCCESS) { + printf("Specified directive file could not be found: %s.\n", (char*)&FileName); + return false; + } + + Parse(); + + return true; +} + + +bool CCABManager::DisplayCabinet() +/* + * FUNCTION: Display cabinet contents + */ +{ + CAB_SEARCH Search; + char Str[20]; + unsigned long FileCount = 0; + unsigned long ByteCount = 0; + + if (Open() == CAB_STATUS_SUCCESS) { + printf("Cabinet %s\n\n", GetCabinetName()); + + if (FindFirst("", &Search) == CAB_STATUS_SUCCESS) { + do { + if (Search.File->FileControlID != CAB_FILE_CONTINUED) { + printf("%s ", Date2Str((char*)&Str, Search.File->FileDate)); + printf("%s ", Time2Str((char*)&Str, Search.File->FileTime)); + printf("%s ", Attr2Str((char*)&Str, Search.File->Attributes)); + sprintf(Str, "%d", Search.File->FileSize); + printf("%s ", Pad(Str, ' ', 13)); + printf("%s\n", Search.FileName); + + FileCount++; + ByteCount += Search.File->FileSize; + } + } while (FindNext(&Search) == CAB_STATUS_SUCCESS); + } + + if (FileCount > 0) { + if (FileCount == 1) + printf(" 1 file "); + else { + sprintf(Str, "%d", FileCount); + printf(" %s files ", Pad(Str, ' ', 12)); + } + + if (ByteCount == 1) + printf(" 1 byte\n"); + else { + sprintf(Str, "%d", ByteCount); + printf("%s bytes\n", Pad(Str, ' ', 12)); + } + } else { + /* There should be at least one file in a cabinet */ + printf("No files in cabinet."); + } + return true; + } else + printf("Cannot not open file: %s\n", GetCabinetName()); + + return false; +} + + +bool CCABManager::ExtractFromCabinet() +/* + * FUNCTION: Extract file(s) from cabinet + */ +{ + CAB_SEARCH Search; + unsigned long Status; + + if (Open() == CAB_STATUS_SUCCESS) { + printf("Cabinet %s\n\n", GetCabinetName()); + + if (FindFirst("", &Search) == CAB_STATUS_SUCCESS) { + do { + switch (Status = ExtractFile(Search.FileName)) { + case CAB_STATUS_SUCCESS: + break; + case CAB_STATUS_INVALID_CAB: + printf("Cabinet contains errors.\n"); + return false; + case CAB_STATUS_UNSUPPCOMP: + printf("Cabinet uses unsupported compression type.\n"); + return false; + case CAB_STATUS_CANNOT_WRITE: + printf("You've run out of free space on the destination volume or the volume is damaged.\n"); + return false; + default: + printf("Unspecified error code (%d).\n", (unsigned int)Status); + return false; + } + } while (FindNext(&Search) == CAB_STATUS_SUCCESS); + } + return true; + } else + printf("Cannot not open file: %s.\n", GetCabinetName()); + + return false; +} + + +bool CCABManager::Run() +/* + * FUNCTION: Process cabinet + */ +{ + printf("ReactOS Cabinet Manager - Version %s\n\n", CM_VERSION); + + switch (Mode) { + case CM_MODE_CREATE: return CreateCabinet(); break; + case CM_MODE_DISPLAY: return DisplayCabinet(); break; + case CM_MODE_EXTRACT: return ExtractFromCabinet(); break; + default: + break; + } + return false; +} + + +/* Event handlers */ + +bool CCABManager::OnOverwrite(PCFFILE File, + char* FileName) +/* + * FUNCTION: Called when extracting a file and it already exists + * ARGUMENTS: + * File = Pointer to CFFILE for file being extracted + * Filename = Pointer to buffer with name of file (full path) + * RETURNS + * true if the file should be overwritten, false if not + */ +{ + char ch; + + if (Mode == CM_MODE_CREATE) + return true; + + /* Always overwrite */ + return true; +} + + +void CCABManager::OnExtract(PCFFILE File, + char* FileName) +/* + * FUNCTION: Called just before extracting a file + * ARGUMENTS: + * File = Pointer to CFFILE for file being extracted + * FileName = Pointer to buffer with name of file (full path) + */ +{ + printf("Extracting %s\n", GetFileName(FileName)); +} + + + +void CCABManager::OnDiskChange(char* CabinetName, + char* DiskLabel) +/* + * FUNCTION: Called when a new disk is to be processed + * ARGUMENTS: + * CabinetName = Pointer to buffer with name of cabinet + * DiskLabel = Pointer to buffer with label of disk + */ +{ + printf("\nChanging to cabinet %s - %s\n\n", CabinetName, DiskLabel); +} + + +void CCABManager::OnAdd(PCFFILE File, + char* FileName) +/* + * FUNCTION: Called just before adding a file to a cabinet + * ARGUMENTS: + * File = Pointer to CFFILE for file being added + * FileName = Pointer to buffer with name of file (full path) + */ +{ + printf("Adding %s\n", GetFileName(FileName)); +} + + +int main(int argc, char * argv[]) +/* + * FUNCTION: Main entry point + * ARGUMENTS: + * argc = Number of arguments on command line + * argv = Pointer to list of command line arguments + */ +{ + CCABManager CABMgr; + bool status = false; + + if (CABMgr.ParseCmdline(argc, argv)) { + status = CABMgr.Run(); + } + + if (status) { + return 0; + } else { + return 1; + } +} + +/* EOF */ diff --git a/reactos/tools/cabman/makefile b/reactos/tools/cabman/makefile new file mode 100755 index 00000000000..3e46d93bb10 --- /dev/null +++ b/reactos/tools/cabman/makefile @@ -0,0 +1,49 @@ +# +# ReactOS Cabinet Manager +# +PATH_TO_TOP = ../.. + +TARGET=cabman$(EXE_POSTFIX) + +all: $(TARGET) + +OBJECTS = cabinet.o mszip.o raw.o main.o dfp.o + +CLEAN_FILES = *.o cabman$(EXE_POSTFIX) + +HOST_CFLAGS = -g -I. -I $(PATH_TO_TOP)/lib/zlib + +HOST_LFLAGS = -g $(PATH_TO_TOP)/lib/zlib/zlib.host.a + +cabinet.o: cabinet.cxx + $(HOST_CXX) $(HOST_CFLAGS) -c cabinet.cxx -o cabinet.o + +mszip.o: mszip.cxx + $(HOST_CXX) $(HOST_CFLAGS) -c mszip.cxx -o mszip.o + +raw.o: raw.cxx + $(HOST_CXX) $(HOST_CFLAGS) -c raw.cxx -o raw.o + +main.o: main.cxx + $(HOST_CXX) $(HOST_CFLAGS) -c main.cxx -o main.o + +dfp.o: dfp.cxx + $(HOST_CXX) $(HOST_CFLAGS) -c dfp.cxx -o dfp.o + +cabman$(EXE_POSTFIX): $(OBJECTS) + $(HOST_CXX) $(OBJECTS) $(HOST_LFLAGS) -o cabman$(EXE_POSTFIX) + +ifeq ($(HOST),mingw32-linux) +clean: + rm -f *.o + rm -f cabman$(EXE_POSTFIX) +endif +ifeq ($(HOST),mingw32-windows) +clean: + del *.o + del cabman$(EXE_POSTFIX) +endif + +.phony: clean + +include $(PATH_TO_TOP)/rules.mak diff --git a/reactos/tools/cabman/makefile.win32 b/reactos/tools/cabman/makefile.win32 new file mode 100755 index 00000000000..b347d6dfce6 --- /dev/null +++ b/reactos/tools/cabman/makefile.win32 @@ -0,0 +1,29 @@ +# $Id: makefile.win32,v 1.1 2003/08/24 10:36:07 chorns Exp $ + +PATH_TO_TOP = ../.. + +TARGET_TYPE = program + +TARGET_APPTYPE = console + +TARGET_NAME = cabman +#TARGET_NAME = test + +TARGET_CPPAPP = yes + +TARGET_OBJECTS = cabinet.o mszip.o raw.o main.o dfp.o + +TARGET_SDKLIBS = zlib.a + +TARGET_CFLAGS = -I$(PATH_TO_TOP)/lib/zlib + +TARGET_CPPFLAGS = $(TARGET_CFLAGS) + +TARGET_GCCLIBS = stdc++ + +TARGET_NORC = yes + +include $(PATH_TO_TOP)/rules.mak + +include $(TOOLS_PATH)/helper.mk + diff --git a/reactos/tools/cabman/mszip.cxx b/reactos/tools/cabman/mszip.cxx new file mode 100755 index 00000000000..d1ae6786981 --- /dev/null +++ b/reactos/tools/cabman/mszip.cxx @@ -0,0 +1,172 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS cabinet manager + * FILE: tools/cabman/mszip.cpp + * PURPOSE: CAB codec for MSZIP compressed data + * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) + * NOTES: The ZLIB does the real work. Get the full version + * from http://www.cdrom.com/pub/infozip/zlib/ + * REVISIONS: + * CSH 21/03-2001 Created + * CSH 15/08-2003 Made it portable + */ +#include +#include "mszip.h" + + +/* Memory functions */ + +voidpf MSZipAlloc(voidpf opaque, uInt items, uInt size) +{ + DPRINT(DEBUG_MEMORY, ("items = (%d) size = (%d)\n", items, size)); + return AllocateMemory(items * size); +} + +void MSZipFree (voidpf opaque, voidpf address) +{ + DPRINT(DEBUG_MEMORY, ("\n")); + FreeMemory(address); +} + + +/* CMSZipCodec */ + +CMSZipCodec::CMSZipCodec() +/* + * FUNCTION: Default constructor + */ +{ + ZStream.zalloc = MSZipAlloc; + ZStream.zfree = MSZipFree; + ZStream.opaque = (voidpf)0; +} + + +CMSZipCodec::~CMSZipCodec() +/* + * FUNCTION: Default destructor + */ +{ +} + + +unsigned long CMSZipCodec::Compress(void* OutputBuffer, + void* InputBuffer, + unsigned long InputLength, + unsigned long* OutputLength) +/* + * FUNCTION: Compresses data in a buffer + * ARGUMENTS: + * OutputBuffer = Pointer to buffer to place compressed data + * InputBuffer = Pointer to buffer with data to be compressed + * InputLength = Length of input buffer + * OutputLength = Address of buffer to place size of compressed data + */ +{ + unsigned short* Magic; + + DPRINT(MAX_TRACE, ("InputLength (%d).\n", InputLength)); + + Magic = (unsigned short*)OutputBuffer; + *Magic = MSZIP_MAGIC; + + ZStream.next_in = (unsigned char*)InputBuffer; + ZStream.avail_in = InputLength; + ZStream.next_out = (unsigned char*)((unsigned long)OutputBuffer + 2); + ZStream.avail_out = CAB_BLOCKSIZE + 12; + + /* WindowBits is passed < 0 to tell that there is no zlib header */ + Status = deflateInit2(&ZStream, + Z_BEST_COMPRESSION, + Z_DEFLATED, + -MAX_WBITS, + 8, /* memLevel */ + Z_DEFAULT_STRATEGY); + if (Status != Z_OK) { + DPRINT(MIN_TRACE, ("deflateInit() returned (%d).\n", Status)); + return CS_NOMEMORY; + } + + Status = deflate(&ZStream, Z_FINISH); + if ((Status != Z_OK) && (Status != Z_STREAM_END)) { + DPRINT(MIN_TRACE, ("deflate() returned (%d) (%s).\n", Status, ZStream.msg)); + if (Status == Z_MEM_ERROR) + return CS_NOMEMORY; + return CS_BADSTREAM; + } + + *OutputLength = ZStream.total_out + 2; + + Status = deflateEnd(&ZStream); + if (Status != Z_OK) { + DPRINT(MIN_TRACE, ("deflateEnd() returned (%d).\n", Status)); + return CS_BADSTREAM; + } + + return CS_SUCCESS; +} + + +unsigned long CMSZipCodec::Uncompress(void* OutputBuffer, + void* InputBuffer, + unsigned long InputLength, + unsigned long* OutputLength) +/* + * FUNCTION: Uncompresses data in a buffer + * ARGUMENTS: + * OutputBuffer = Pointer to buffer to place uncompressed data + * InputBuffer = Pointer to buffer with data to be uncompressed + * InputLength = Length of input buffer + * OutputLength = Address of buffer to place size of uncompressed data + */ +{ + unsigned short Magic; + + DPRINT(MAX_TRACE, ("InputLength (%d).\n", InputLength)); + + Magic = *((unsigned short*)InputBuffer); + + if (Magic != MSZIP_MAGIC) { + DPRINT(MID_TRACE, ("Bad MSZIP block header magic (0x%X)\n", Magic)); + return CS_BADSTREAM; + } + + ZStream.next_in = (unsigned char*)((unsigned long)InputBuffer + 2); + ZStream.avail_in = InputLength - 2; + ZStream.next_out = (unsigned char*)OutputBuffer; + ZStream.avail_out = CAB_BLOCKSIZE + 12; + + /* WindowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + */ + Status = inflateInit2(&ZStream, -MAX_WBITS); + if (Status != Z_OK) { + DPRINT(MIN_TRACE, ("inflateInit2() returned (%d).\n", Status)); + return CS_BADSTREAM; + } + + while ((ZStream.total_out < CAB_BLOCKSIZE + 12) && + (ZStream.total_in < InputLength - 2)) { + Status = inflate(&ZStream, Z_NO_FLUSH); + if (Status == Z_STREAM_END) break; + if (Status != Z_OK) { + DPRINT(MIN_TRACE, ("inflate() returned (%d) (%s).\n", Status, ZStream.msg)); + if (Status == Z_MEM_ERROR) + return CS_NOMEMORY; + return CS_BADSTREAM; + } + } + + *OutputLength = ZStream.total_out; + + Status = inflateEnd(&ZStream); + if (Status != Z_OK) { + DPRINT(MIN_TRACE, ("inflateEnd() returned (%d).\n", Status)); + return CS_BADSTREAM; + } + return CS_SUCCESS; +} + +/* EOF */ diff --git a/reactos/tools/cabman/mszip.h b/reactos/tools/cabman/mszip.h new file mode 100755 index 00000000000..9ff60801e0b --- /dev/null +++ b/reactos/tools/cabman/mszip.h @@ -0,0 +1,41 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS cabinet manager + * FILE: tools/cabman/mszip.h + * PURPOSE: CAB codec for MSZIP compressed data + */ +#ifndef __MSZIP_H +#define __MSZIP_H + +#include "cabinet.h" +#include + +#define MSZIP_MAGIC 0x4B43 + + +/* Classes */ + +class CMSZipCodec : public CCABCodec { +public: + /* Default constructor */ + CMSZipCodec(); + /* Default destructor */ + virtual ~CMSZipCodec(); + /* Compresses a data block */ + virtual unsigned long Compress(void* OutputBuffer, + void* InputBuffer, + unsigned long InputLength, + unsigned long* OutputLength); + /* Uncompresses a data block */ + virtual unsigned long Uncompress(void* OutputBuffer, + void* InputBuffer, + unsigned long InputLength, + unsigned long* OutputLength); +private: + int Status; + z_stream ZStream; /* Zlib stream */ +}; + +#endif /* __MSZIP_H */ + +/* EOF */ diff --git a/reactos/tools/cabman/raw.cxx b/reactos/tools/cabman/raw.cxx new file mode 100755 index 00000000000..e42cca21582 --- /dev/null +++ b/reactos/tools/cabman/raw.cxx @@ -0,0 +1,68 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS cabinet manager + * FILE: tools/cabman/raw.cpp + * PURPOSE: CAB codec for uncompressed "raw" data + * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) + * REVISIONS: + * CSH 21/03-2001 Created + * CSH 15/08-2003 Made it portable + */ +#include "raw.h" + + +/* CRawCodec */ + +CRawCodec::CRawCodec() +/* + * FUNCTION: Default constructor + */ +{ +} + + +CRawCodec::~CRawCodec() +/* + * FUNCTION: Default destructor + */ +{ +} + + +unsigned long CRawCodec::Compress(void* OutputBuffer, + void* InputBuffer, + unsigned long InputLength, + unsigned long* OutputLength) +/* + * FUNCTION: Compresses data in a buffer + * ARGUMENTS: + * OutputBuffer = Pointer to buffer to place compressed data + * InputBuffer = Pointer to buffer with data to be compressed + * InputLength = Length of input buffer + * OutputLength = Address of buffer to place size of compressed data + */ +{ + memcpy(OutputBuffer, InputBuffer, InputLength); + *OutputLength = InputLength; + return CS_SUCCESS; +} + +unsigned long CRawCodec::Uncompress(void* OutputBuffer, + void* InputBuffer, + unsigned long InputLength, + unsigned long* OutputLength) +/* + * FUNCTION: Uncompresses data in a buffer + * ARGUMENTS: + * OutputBuffer = Pointer to buffer to place uncompressed data + * InputBuffer = Pointer to buffer with data to be uncompressed + * InputLength = Length of input buffer + * OutputLength = Address of buffer to place size of uncompressed data + */ +{ + memcpy(OutputBuffer, InputBuffer, InputLength); + *OutputLength = InputLength; + return CS_SUCCESS; +} + +/* EOF */ diff --git a/reactos/tools/cabman/raw.h b/reactos/tools/cabman/raw.h new file mode 100755 index 00000000000..160a9d500ca --- /dev/null +++ b/reactos/tools/cabman/raw.h @@ -0,0 +1,35 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS cabinet manager + * FILE: tools/cabman/raw.h + * PURPOSE: CAB codec for uncompressed data + */ +#ifndef __RAW_H +#define __RAW_H + +#include "cabinet.h" + + +/* Classes */ + +class CRawCodec : public CCABCodec { +public: + /* Default constructor */ + CRawCodec(); + /* Default destructor */ + virtual ~CRawCodec(); + /* Compresses a data block */ + virtual unsigned long Compress(void* OutputBuffer, + void* InputBuffer, + unsigned long InputLength, + unsigned long* OutputLength); + /* Uncompresses a data block */ + virtual unsigned long Uncompress(void* OutputBuffer, + void* InputBuffer, + unsigned long InputLength, + unsigned long* OutputLength); +}; + +#endif /* __RAW_H */ + +/* EOF */ diff --git a/reactos/tools/helper.mk b/reactos/tools/helper.mk index f9eb9933861..81d49eb5d6c 100644 --- a/reactos/tools/helper.mk +++ b/reactos/tools/helper.mk @@ -1,4 +1,4 @@ -# $Id: helper.mk,v 1.42 2003/08/09 17:08:14 mf Exp $ +# $Id: helper.mk,v 1.43 2003/08/24 10:36:07 chorns Exp $ # # Helper makefile for ReactOS modules # Variables this makefile accepts: @@ -19,6 +19,7 @@ # $TARGET_APPTYPE = Application type (windows,native,console) # $TARGET_NAME = Base name of output file and .rc, .def, and .edf files # $TARGET_OBJECTS = Object files that compose the module +# $TARGET_CPPAPP = C++ application (no,yes) (optional) # $TARGET_HEADERS = Header files that the object files depend on (optional) # $TARGET_DEFNAME = Base name of .def and .edf files (optional) # $TARGET_BASENAME = Base name of output file (overrides $TARGET_NAME if it exists) (optional) @@ -42,7 +43,7 @@ # $TARGET_LIBPATH = Destination path for import libraries (optional) # $TARGET_INSTALLDIR = Destination path when installed (optional) # $TARGET_PCH = Filename of header to use to generate a PCH if supported by the compiler (optional) -# $TARGET_BOOTSTRAP = Wether this file is needed to bootstrap the installation (no,yes) (optional) +# $TARGET_BOOTSTRAP = Wether this file is needed to bootstrap the installation (no,yes) (optional) # $TARGET_BOOTSTRAP_NAME = Name on the installation medium (optional) # $TARGET_GENREGTESTS = Generate regression test registrations (optional) # $WINE_MODE = Compile using WINE headers (no,yes) (optional) @@ -530,7 +531,14 @@ MK_NOSTRIPNAME := $(MK_BASENAME).nostrip$(MK_EXT) # We don't want to link header files MK_OBJECTS := $(filter-out %.h,$(TARGET_OBJECTS)) -MK_STRIPPED_OBJECT := $(MK_BASENAME).stripped.o + +# There is problems with C++ applications and ld -r. Ld can cause errors like: +# reloc refers to symbol `.text$_ZN9CCABCodecC2Ev' which is not being output +ifeq ($(TARGET_CPPAPP),yes) + MK_STRIPPED_OBJECT := $(MK_OBJECTS) +else + MK_STRIPPED_OBJECT := $(MK_BASENAME).stripped.o +endif ifeq ($(MK_IMPLIBONLY),yes) @@ -620,8 +628,11 @@ else endif $(MK_FULLNAME): $(MK_NOSTRIPNAME) $(MK_EXTRADEP) + - +ifneq ($(TARGET_CPPAPP),yes) $(LD) -r -o $(MK_STRIPPED_OBJECT) $(MK_OBJECTS) $(STRIP) --strip-debug $(MK_STRIPPED_OBJECT) +endif ifeq ($(MK_EXETYPE),dll) $(LD_CC) -Wl,--base-file,base.tmp \ -Wl,--entry,$(TARGET_ENTRY) \ @@ -653,7 +664,11 @@ endif $(MK_EXTRACMD2) \ -o $(MK_FULLNAME) \ $(MK_FULLRES) $(MK_STRIPPED_OBJECT) $(MK_LIBS) $(MK_GCCLIBS) +ifneq ($(TARGET_CPPAPP),yes) - $(RM) temp.exp $(MK_STRIPPED_OBJECT) +else + - $(RM) temp.exp +endif endif # KM_MODE @@ -699,8 +714,11 @@ else endif $(MK_FULLNAME): $(MK_FULLRES) $(TARGET_OBJECTS) $(MK_EXTRADEP) $(MK_LIBS) $(MK_NOSTRIPNAME) + - +ifneq ($(TARGET_CPPAPP),yes) $(LD) -r -o $(MK_STRIPPED_OBJECT) $(MK_OBJECTS) $(STRIP) --strip-debug $(MK_STRIPPED_OBJECT) +endif $(LD_CC) -Wl,--base-file,base.tmp \ -Wl,--entry,$(TARGET_ENTRY) \ $(TARGET_LFLAGS) \ @@ -722,7 +740,11 @@ $(MK_FULLNAME): $(MK_FULLRES) $(TARGET_OBJECTS) $(MK_EXTRADEP) $(MK_LIBS) $(MK_N -mdll -nostartfiles -nostdlib \ -o $(MK_FULLNAME) \ $(MK_FULLRES) $(MK_STRIPPED_OBJECT) $(MK_LIBS) $(MK_GCCLIBS) +ifneq ($(TARGET_CPPAPP),yes) + - $(RM) temp.exp $(MK_STRIPPED_OBJECT) +else - $(RM) temp.exp +endif endif # MK_MODE @@ -884,6 +906,8 @@ endif # ROS_USE_PCH $(CC) $(TARGET_CFLAGS) -c $< -o $@ %.o: %.cc $(CXX) $(TARGET_CPPFLAGS) -c $< -o $@ +%.o: %.cxx + $(CXX) $(TARGET_CPPFLAGS) -c $< -o $@ %.o: %.cpp $(CXX) $(TARGET_CPPFLAGS) -c $< -o $@ %.o: %.S