// .cab file compression and decompression demonstration program code.
#pragma once
#include
#include
#include // file control
#include
#include
#include
#include
#include "fci.h"
#include "fdi.h"
using namespace std;
/
// static global variables
const char* gs_dir_targ = NULL;
char gs_cab_next[MAX_PATH] = {0};
unsigned long gu_size[2] = {0};
unsigned long gu_count = 0;
/
// general functions
unsigned long fnget_desired_access(int in_oflag)
{
if(_O_RDWR & in_oflag)
return GENERIC_READ|GENERIC_WRITE;
if(_O_WRONLY & in_oflag)
return GENERIC_WRITE;
return GENERIC_READ;
}
bool fnget_temp_filename(char* out_pbuf, unsigned long in_cbbuf)
{
char szfile[MAX_PATH] = {0};
char szpath[MAX_PATH] = {0};
if(GetTempPath(MAX_PATH, szpath))
if(GetTempFileName(szpath, "~Z_", 0, szfile))
if(DeleteFile(szfile))
if(out_pbuf == strcpy(out_pbuf, szfile))// note here: this is not a secure copy.
return true;
return false;
}
/
// gecallback functions
void* fncallback_alloc(unsigned long cb)
{
return operator new(cb);
}
void fncallback_free(void* pbuf)
{
operator delete(pbuf);
}
ptrdiff_t fncallback_fci_open(char* in_pszfile, int in_oflag, int in_mode, int* out_perr, void* in_pv)
{
void* hf = NULL;
UNREFERENCED_PARAMETER(in_pv);// correspond to call unname_func(pv);
UNREFERENCED_PARAMETER(in_mode);
hf = CreateFile(in_pszfile, fnget_desired_access(in_oflag), FILE_SHARE_READ|FILE_SHARE_DELETE, NULL,
(_O_CREAT & in_oflag) ? CREATE_ALWAYS : OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);// CREATE_ALWAYS
if(INVALID_HANDLE_VALUE == hf)
*out_perr = GetLastError();
return (ptrdiff_t)hf;// like integer type
}
unsigned int fncallback_fci_read(ptrdiff_t in_hf, void* out_pbuf, unsigned int in_cbbuf, int* out_perr, void* in_pv)
{
unsigned long i = 0;
UNREFERENCED_PARAMETER(in_pv);
if(!ReadFile((void*)in_hf, out_pbuf, in_cbbuf, &i, NULL))
{
*out_perr = GetLastError();
i = 0xFFFFFFFF;
}
return i;
}
unsigned int fncallback_fci_write(ptrdiff_t in_hf, void* in_pbuf, unsigned int in_cbbuf, int* out_perr, void* in_pv)
{
unsigned long i = 0;
UNREFERENCED_PARAMETER(in_pv);
if(!WriteFile((void*)in_hf, in_pbuf, in_cbbuf, &i, NULL))
{
*out_perr = GetLastError();
i = 0xFFFFFFFF;
}
return i;
}
int fncallback_fci_close(ptrdiff_t in_hf, int* out_perr, void* in_pv)
{
int i = 0;
UNREFERENCED_PARAMETER(in_pv);
// the outpat .cab file has just been closed. reset it's handle.
if(!CloseHandle((void*)in_hf))
{
*out_perr = GetLastError();
i = -1;
}
return i;
}
// seek inside a file (move file pointer)
long fncallback_fci_seek(ptrdiff_t in_hf, long in_offset, int in_type, int* out_perr, void* in_pv)
{
long i = 0;
UNREFERENCED_PARAMETER(in_pv);
i = SetFilePointer((void* )in_hf, in_offset, NULL, in_type);
if(0xFFFFFFFF == i)
*out_perr = GetLastError();
return i;
}
int fncallback_fci_delete(char* in_pszfile, int* out_perr, void* in_pv)
{
int i = 0;
i = remove(in_pszfile);
if(0 != i)
*out_perr = errno;
return i;
}
int fncallback_fci_file_placed(CCAB* pcab, char* pszfile, long cbfile, BOOL bcontination, void* pv)
{
return 0;
}
// a function to obtain temporary file names.
// the filename returned should not occupy more than 'cbbuf' bytes.
// FCI may open several temporary files at once, so it is important to ensure
// that a different filename is returned each time, and that the file does not already exist.
BOOL fncallback_fci_temp_file(char* out_pbuf, int cbbuf, void* pv)
{
return fnget_temp_filename(out_pbuf, cbbuf) ? TRUE : FALSE;
}
// the function should return TRUE for success, or FALSE to abort cabinet creation.
BOOL fncallback_fci_get_next_cabinet(CCAB* pcab, unsigned long cbprev, void* pv)
{
strcpy(pcab->szDisk, "Disk 1");
pcab->iDisk++;
return TRUE;
}
// returns anything other than -1;
long fncallback_fci_status(unsigned int itype_status, unsigned long cb1, unsigned long cb2, void* pv)
{
return 0;
}
// open source file and return date / time / attributes
// exit-success: return file handle of open file to read
// exit-failure: return -1
ptrdiff_t fncallback_fci_get_open_info(char* in_pszfile, unsigned short* out_pdate, unsigned short* out_ptime, unsigned short* out_pattr, int* out_perr, void* in_pv)
{
void* hfile = NULL;
BY_HANDLE_FILE_INFORMATION fi = {0};
FILETIME ft = {0};
hfile = CreateFile(in_pszfile, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if(INVALID_HANDLE_VALUE == hfile || 0 == GetFileInformationByHandle(hfile, &fi))
{
*out_perr = GetLastError();
return -1;
}
CloseHandle(hfile);
// the windows filesystem stores utc times
ft = fi.ftLastWriteTime;
FileTimeToDosDateTime(&ft, out_pdate, out_ptime);
// mask out all other bits except these four, since other bite are used
// by the cabinet format to indicate a special meaning.
*out_pattr = (unsigned short)(fi.dwFileAttributes & (FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM));
// now return a handle using fncallback_fci_open
return fncallback_fci_open(in_pszfile, _O_RDONLY|_O_BINARY, _S_IREAD, out_perr, in_pv);
}
ptrdiff_t fncallback_fdi_open(char* pszfile, int oflag, int imode)
{
return _open(pszfile, oflag, _S_IREAD);
}
unsigned int fncallback_fdi_read(ptrdiff_t in_hf, void* out_pbuf, unsigned int in_cbbuf)
{
return (unsigned int)_read(in_hf, out_pbuf, in_cbbuf);
}
unsigned int fncallback_fdi_write(ptrdiff_t hf, void* pbuf, unsigned int cbbuf)
{
unsigned int i = (unsigned int)_write(hf, pbuf, cbbuf);
if(0xFFFFFFFF > i)
gu_size[0] += i;// count copy size.
return i;
}
int fncallback_fdi_close(ptrdiff_t hf)
{
return _close(hf);
}
long fncallback_fdi_seek(ptrdiff_t hf, long ioffset, int itype)
{
return _lseek(hf, ioffset, itype);
}
// callback function for the FDI context.
// this will get used when the 'fnfdi_copy' function is called.
// this function will call the appropriate handlers which can be overridden to change the default behaviour.
ptrdiff_t fncallback_fdi_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin)
{
char szfile[MAX_PATH] = {0};
int i = 0;// allow unsupported notifications to continue. default skip this file.
switch(fdint)// relay the notifications.
{
case fdintCABINET_INFO:
// pfdin->psz1 = name of next cabinet
// pfdin->psz2 = name of next disk
// pfdin->psz3 = cabinet path name
// pfdin->setID = cabinet set ID (a random 16-bit number)
// pfdin->iCabinet = cabinet number within cabinet set (0-based)
// Exit-Success:
// Return anything but -1
// Exit-Failure:
// Returns -1 => Abort FDICopy() call
if(0 == strlen(gs_cab_next))
strcpy(gs_cab_next, pfdin->psz1);
break;
case fdintCOPY_FILE:
// Entry:
// pfdin->psz1 = file name in cabinet
// pfdin->cb = uncompressed size of file
// pfdin->date = file date
// pfdin->time = file time
// pfdin->attribs = file attributes
// pfdin->iFolder = file's folder index
// Exit-Success:
// Return non-zero file handle for destination file; FDI writes
// data to this file use the PFNWRITE function supplied to FDICreate,
// and then calls fdintCLOSE_FILE_INFO to close the file and set
// the date, time, and attributes. NOTE: This file handle returned
// must also be closeable by the PFNCLOSE function supplied to
// FDICreate, since if an error occurs while writing to this handle,
// FDI will use the PFNCLOSE function to close the file so that the
// client may delete it.
// Exit-Failure:
// returns 0 => skip file, do not copy
// returns -1 => abort FDICopy() call
// combined path
strcpy(szfile, gs_dir_targ);
strcat(szfile, pfdin->psz1);
i = _open(szfile, _O_TRUNC | _O_BINARY | _O_CREAT | _O_WRONLY | _O_SEQUENTIAL, _S_IREAD | _S_IWRITE);
gu_size[0] = 0;
gu_size[1] = pfdin->cb;
break;
case fdintCLOSE_FILE_INFO:
// pfdin->psz1 = file name in cabinet
// pfdin->hf = file handle
// pfdin->date = file date
// pfdin->time = file time
// pfdin->attribs = file attributes
// pfdin->iFolder = file's folder index
// pfdin->cb = run after cabfdiact (0 - don't run, 1 Run)
// Exit-Success:
// Returns TRUE
// Exit-Failure:
// Returns FALSE, or -1 to abort;
if(gu_size[0] == gu_size[1])
gu_count++;
i = TRUE;
break;
case fdintNEXT_CABINET:
// pfdin->psz1 = name of next cabinet where current file is continued
// pfdin->psz2 = name of next disk where current file is continued
// pfdin->psz3 = cabinet path name; FDI concatenates psz3 with psz1
// to produce the fully-qualified path for the cabinet
// file. The 256-byte buffer pointed at by psz3 may
// be modified, but psz1 may not!
// pfdin->fdie = FDIERROR_WRONG_CABINET if the previous call to
// fdintNEXT_CABINET specified a cabinet file that
// did not match the setID/iCabinet that was expected.
// Exit-Success:
// Return anything but -1
// Exit-Failure:
// Returns -1 => Abort FDICopy() call
strcpy(gs_cab_next, pfdin->psz1);
break;
case fdintPARTIAL_FILE:
case fdintENUMERATE:
break;
}
return i;
}
/
// library functions
// return FCI context handle
void* fnfci_create(ERF* out_perf, CCAB* out_pcab)
{
typedef void* (__cdecl* FT)(ERF* , PFNFCIFILEPLACED, PFNFCIALLOC, PFNFCIFREE, PFNFCIOPEN, PFNFCIREAD, PFNFCIWRITE, PFNFCICLOSE, PFNFCISEEK, PFNFCIDELETE, PFNFCIGETTEMPFILE, PCCAB, void*);
void* hfci = NULL;
HINSTANCE hlib = NULL;
FT func = NULL;
hlib = LoadLibrary("cabinet.dll");
if(NULL == hlib)
return NULL;
func = (FT)GetProcAddress(hlib, "FCICreate");
if(func)
hfci = func(out_perf,
fncallback_fci_file_placed, // PFNFCIFILEPLACED
fncallback_alloc, // PFNFCIALLOC
fncallback_free, // PFNFCIFREE
fncallback_fci_open, // PFNFCIOPEN
fncallback_fci_read, // PFNFCIREAD
fncallback_fci_write, // PFNFCIWRITE
fncallback_fci_close, // PFNFCICLOSE
fncallback_fci_seek, // PFNFCISEEK
fncallback_fci_delete, // PFNFCIDELETE
fncallback_fci_temp_file, // PFNFCIGETTEMPFILE
out_pcab,
NULL);// user param
FreeLibrary(hlib);
return hfci;
}
// in_hfci - FCI context handle
// in_pfile_src - name of file to add to folder
// in_pfile_ins - name to store into folder/cabinet
bool fnfci_add(void* in_hfci, const char* in_pfile_src, const char* in_pfile_ins)
{
bool bret = false;
typedef BOOL (__cdecl* FT)(void*, const char*, const char*, BOOL, PFNFCIGETNEXTCABINET, PFNFCISTATUS, PFNFCIGETOPENINFO, unsigned short);
HINSTANCE hlib = NULL;
FT func = NULL;
hlib = LoadLibrary("cabinet.dll");
if(NULL == hlib)
return false;
func = (FT)GetProcAddress(hlib, "FCIAddFile");
if(func)
bret = func(in_hfci, in_pfile_src, in_pfile_ins, FALSE, fncallback_fci_get_next_cabinet, fncallback_fci_status, fncallback_fci_get_open_info, tcompTYPE_LZX|tcompLZX_WINDOW_HI) ? true : false;
FreeLibrary(hlib);
return bret;
}
bool fnfci_flush(void* in_hfci)
{
bool bret = false;
typedef BOOL (__cdecl* FT)(void*, BOOL, PFNFCIGETNEXTCABINET, PFNFCISTATUS);
FT func = NULL;
HINSTANCE hlib = NULL;
hlib = LoadLibrary("cabinet.dll");
if(NULL == hlib)
return false;
func = (FT)GetProcAddress(hlib, "FCIFlushCabinet");
if(func)
bret = func(in_hfci, FALSE, fncallback_fci_get_next_cabinet, fncallback_fci_status) ? true : false;
FreeLibrary(hlib);
return bret;
}
bool fnfci_destroy(void* in_hfci)
{
typedef BOOL (__cdecl* FT)(void* );
bool bret = false;
FT func = NULL;
HINSTANCE hlib = LoadLibrary("cabinet.dll");
if(NULL == hlib)
return false;
func = (FT)GetProcAddress(hlib, "FCIDestroy");
if(func)
bret = func(in_hfci) ? true : false;
FreeLibrary(hlib);
return bret;
}
void* fnfdi_create(ERF* out_perf)
{
typedef void* (__cdecl* FT)(PFNALLOC, PFNFREE, PFNOPEN, PFNREAD, PFNWRITE, PFNCLOSE, PFNSEEK, int, ERF*);
void* hf = NULL;
HINSTANCE hlib = NULL;
FT func = NULL;
hlib = LoadLibrary("cabinet.dll");
if(NULL == hlib)
return NULL;
func = (FT)GetProcAddress(hlib, "FDICreate");
if(func)
hf = func(fncallback_alloc, fncallback_free,
fncallback_fdi_open,
fncallback_fdi_read,
fncallback_fdi_write,
fncallback_fdi_close,
fncallback_fdi_seek,
cpuUNKNOWN, out_perf);
FreeLibrary(hlib);
return hf;
}
bool fnfdi_copy(void* hfdi, const char* pszcabinet, const char* pszcab_path, void* pv)
{
typedef BOOL (__cdecl* FT)(void*, const char*, const char*, int, PFNFDINOTIFY, PFNFDIDECRYPT, void*);
bool bret = false;
HINSTANCE hlib = NULL;
FT func = NULL;
hlib = LoadLibrary("cabinet.dll");
if(NULL == hlib)
return false;
func = (FT)GetProcAddress(hlib, "FDICopy");
if(func)
bret = func(hfdi, pszcabinet, pszcab_path, 0, fncallback_fdi_notify, NULL, pv) ? true : false;
FreeLibrary(hlib);
return bret;
}
bool fnfdi_destroy(void* hf)
{
typedef BOOL (__cdecl* FT)(void*);
bool bret = false;
HINSTANCE hlib = NULL;
FT func = NULL;
hlib = LoadLibrary("cabinet.dll");
if(NULL == hlib)
return false;
func = (FT)GetProcAddress(hlib, "FDIDestroy");
if(func)
bret = func(hf) ? true : false;
FreeLibrary(hlib);
return bret;
}
/
// demo functions
// example 1.
bool fncabmake()
{
void* hfci = NULL;
CCAB cab = {0};
ERF erf = {0};
// assume that the following test file already exists:
// 'debug\\test01.txt'
// 'debug\\test02.txt'
cab.cb = 0;// split size
cab.cbFolderThresh = 0x7FFFFFFF;
cab.iCab = 1;
cab.iDisk = 1;
strcpy(cab.szCabPath, "debug\\");// create path name
strcpy(cab.szCab, "pack.cab");// save file name
hfci = fnfci_create(&erf, &cab);
if(NULL == hfci)
return false;
fnfci_add(hfci, "debug\\test01.txt", "test01.txt");
fnfci_add(hfci, "debug\\test02.txt", "test02.txt");
fnfci_flush(hfci);
fnfci_destroy(hfci);
return true;
}
// example 2.
unsigned long fnextract()
{
void* hfdi = NULL;
unsigned long i = 0;
ERF erf = {0};
// assuming that 'debug\\pack.cab' file and 'debug\\dir' directory already exists.
gs_dir_targ = "debug\\dir\\";
gs_cab_next[0] = '\0';
gu_count = 0;
hfdi = fnfdi_create(&erf);
if(NULL == hfdi)
return 0;
while(fnfdi_copy(hfdi, "pack.cab", "debug\\", NULL))
if(0 == strlen(gs_cab_next))
break;
fnfdi_destroy(hfdi);
return gu_count;
}
int main(int argc, char* argv[], char* envp[])
{
try
{
if(fncabmake())
cout << "example 1 success." << endl;
else
cout << "example 1 failure." << endl;
cout << "example 2: " << fnextract() << endl;
}
catch(const char* p)
{
cout << p << endl;
}
cout << "press any key to quit." << endl;
int i = _getch();
return 0;
}