#include "nt.h"
#include "ntrtl.h"
#include "nturtl.h"
#include "windows.h"
#include "stdio.h"
#include "stdlib.h"
#include "fcntl.h"
#include "sxsexpress.h"
#include "fci.h"
#define NUMBER_OF(q) (sizeof(q)/sizeof(*q))
#ifndef PAGE_SIZE
#define PAGE_SIZE (4*1024)
#endif
#define FILETYPE_CABINET (0x00000001)
#define FILETYPE_DISKFILE (0x00000002)
#define DEFAULT_RESERVE_SIZE (1024*1024*80) // By default, reserve 80mb
//#define DEFAULT_RESERVE_SIZE (1024*8) // By default, reserve 80mb
typedef struct _FILE_HANDLE {
DWORD dwFileType;
union {
HANDLE hDiskFile;
struct {
PVOID pvFileBase;
PVOID pvCursor;
SIZE_T cbReserved;
SIZE_T cbCommitted;
SIZE_T cbWritten;
} CabinetFile;
};
} FILE_HANDLE;
typedef struct _INSTALLED_FILE_NAME {
LIST_ENTRY Links;
PCWSTR pwszFileName;
PCWSTR pwszSubDir;
} INSTALLED_FILE;
LIST_ENTRY InstalledFiles;
typedef struct _FCI_CONTEXT_OBJECT
{
bool fWasError;
DWORD dwLastError;
FILE_HANDLE *CabinetFileHandle;
PCSTR pszCabFileName;
void ObtainErrorContext()
{
if (!fWasError)
{
fWasError = true;
dwLastError = ::GetLastError();
}
}
} FCI_CONTEXT_OBJECT;
BOOL CreateCabFromPath(PCWSTR pcwszBasePath, FILE_HANDLE *CreatedCabinet, PCWSTR pcwszInfName);
void
DoHelp(PCWSTR pcwszName)
{
static const WCHAR wchHelp[] =
L"SxsExpress Self-Extracting Creator Tool\r\n"
L"Copyright (c) 2001-2002 Microsoft Corporation, all \r\n"
L"\r\n"
L"Commandline:\r\n"
L"\r\n"
L" %ls [options] outputname\r\n"
L"\r\n"
L"Options:\r\n"
L"-sourcedir
: Sets the base path to package up\r\n"
L"-sfxheader : Self-extractor core (exe or dll) file\r\n"
L"-infname : Pass in a pre-built downlevel installer .INF\r\n"
L" (If not specified, the tool will build an appropriate\r\n"
L" .INF that installs everything to system32)\r\n"
L"-overwrite : Forces the tool to output the built package even\r\n"
L" if the file already exists\r\n";
wprintf(wchHelp, pcwszName);
}
PCWSTR
GenerateInfFromTo(
PCWSTR pcwszPackageDir
)
{
return NULL;
}
int __cdecl wmain(int argc, PCWSTR* argv)
{
PCWSTR pcwszHeaderName = NULL;
PCWSTR pcwszPackageDir = NULL;
PCWSTR pcwszInfName = NULL;
PCWSTR pcwszOutputName = NULL;
bool fAllocatedInfName = false;
bool fOverwrite = false;
FILE_HANDLE Created;
HANDLE hResource = INVALID_HANDLE_VALUE;;
int iResult = -1;
BOOL fOk = FALSE;
//
// Determine parameters
//
for (int i = 1; i < argc; i++)
{
if ((*argv[i] == L'-') || (*argv[i] == L'/'))
{
PCWSTR psz = argv[i] + 1;
if (lstrcmpiW(psz, L"sourcedir") == 0)
{
if (pcwszPackageDir != NULL)
{
DoHelp(argv[0]);
wprintf(L"Already specified a packaging directory.\r\n");
goto Exit;
}
else
{
pcwszPackageDir = argv[++i];
}
}
else if (lstrcmpiW(psz, L"sfxheader") == 0)
{
if (pcwszHeaderName != NULL)
{
DoHelp(argv[0]);
wprintf(L"Already specified the SFX header.\r\n");
goto Exit;
}
else
{
pcwszHeaderName = argv[++i];
}
}
else if (lstrcmpiW(psz, L"infname") == 0)
{
if (pcwszInfName != NULL)
{
DoHelp(argv[0]);
wprintf(L"Already specified an INF name, only one at a time.\r\n");
goto Exit;
}
else
{
pcwszInfName = argv[++i];
}
}
else if (lstrcmpiW(psz, L"overwrite") == 0)
{
fOverwrite = true;
}
else
{
DoHelp(argv[0]);
wprintf(L"\r\nUnknown option %ls\r\n", psz);
goto Exit;
}
}
else
{
if (pcwszOutputName != NULL)
{
DoHelp(argv[0]);
wprintf(L"\r\nToo many output names, just one please.\r\n");
goto Exit;
}
else
{
pcwszOutputName = argv[i];
}
}
}
if ((pcwszHeaderName == NULL) || (pcwszPackageDir == NULL) || (pcwszOutputName == NULL))
{
DoHelp(argv[0]);
wprintf(L"\r\nMust specify at least the sfx header file, source directory, and output\r\n");
goto Exit;
}
//
// Ok, let's generate the cabinet file from the source directory. When this is done,
// it'll be sitting in memory. We then have to copy the source header to the target
// filename and do some work to update its resources
//
if (!CreateCabFromPath(pcwszPackageDir, &Created, pcwszInfName))
{
wprintf(L"Error %ld creating cabinet from %ls\r\n", ::GetLastError(), pcwszPackageDir);
goto Exit;
}
//
// Copy the file over
//
if (!CopyFileW(pcwszHeaderName, pcwszOutputName, fOverwrite ? FALSE : TRUE))
{
const DWORD dwError = ::GetLastError();
if (dwError == ERROR_FILE_EXISTS)
{
wprintf(L"Error, file %ls already exists. Move it or delete it, please.\r\n", pcwszOutputName);
}
else
{
wprintf(L"Error %ld copying SFX header %ls to output %ls\r\n", dwError, pcwszHeaderName, pcwszOutputName);
}
goto Exit;
}
//
// The output contains the cabinet data above in its resources at a certain name.
// Do this by updating the resource table now.
//
hResource = BeginUpdateResourceW(pcwszOutputName, FALSE);
if ((hResource == NULL) || (hResource == INVALID_HANDLE_VALUE))
{
wprintf(L"Unable to open %ls for resource updating, error 0x%08lx\n", pcwszOutputName, GetLastError());
goto Exit;
}
fOk = UpdateResourceW(hResource, SXSEXPRESS_RESOURCE_TYPE, SXSEXPRESS_RESOURCE_NAME, 0, Created.CabinetFile.pvFileBase, Created.CabinetFile.cbWritten);
if (!EndUpdateResourceW(hResource, !fOk))
{
wprintf(L"Failed updating resources, error code 0x%08lx\n", GetLastError());
goto Exit;
}
iResult = 0;
Exit:
if (fAllocatedInfName && pcwszInfName)
HeapFree(GetProcessHeap(), 0, (PVOID)pcwszInfName);
return iResult;
}
/*
Our very own mini-cabarc code to create sxsexpress packages
*/
CHAR WorkingBufferOne[2048];
CHAR WorkingBufferTwo[2048];
FNFCIFILEPLACED(s_FilePlaced) { printf("Placed file %s (%d bytes)\n", pszFile, cbFile); return 1; }
FNFCIALLOC(s_Alloc) { return HeapAlloc(GetProcessHeap(), 0, cb); }
FNFCIFREE(s_Free) { HeapFree(GetProcessHeap(), 0, memory); }
FNFCIOPEN(s_OpenFile)
{
FCI_CONTEXT_OBJECT *pContext = (FCI_CONTEXT_OBJECT*)pv;
FILE_HANDLE *hHandle = NULL;
INT_PTR ipRetVal = -1;
int iConversion = 0, iActual = 0;
PWSTR pszConverted = NULL;
hHandle = (FILE_HANDLE*)s_Alloc(sizeof(FILE_HANDLE));
if (!hHandle) {
pContext->ObtainErrorContext();
*err = ::GetLastError();
goto Exit;
}
ZeroMemory(hHandle, sizeof(*hHandle));
//
// All strings that get here are in UTF-8. Convert them back.
//
iConversion = MultiByteToWideChar(CP_UTF8, 0, pszFile, -1, NULL, 0);
if (iConversion == 0)
{
pContext->ObtainErrorContext();
*err = ::GetLastError();
goto Exit;
}
pszConverted = (PWSTR)s_Alloc(sizeof(WCHAR) * (iConversion + 1));
if (NULL == pszConverted)
{
pContext->ObtainErrorContext();
*err = ::GetLastError();
goto Exit;
}
iActual = MultiByteToWideChar(CP_UTF8, 0, pszFile, -1, pszConverted, iConversion);
if ((iActual == 0) || (iActual > iConversion))
{
pContext->ObtainErrorContext();
*err = ::GetLastError();
goto Exit;
}
else
{
pszConverted[iConversion] = UNICODE_NULL;
}
if (oflag & _O_CREAT)
{
//
// The actual cabinet file becomes a large block of committed bytes
//
if (lstrcmpiA(pContext->pszCabFileName, pszFile) == 0)
{
if (pContext->CabinetFileHandle)
{
pContext->fWasError = true;
*err = (int)(pContext->dwLastError = ERROR_INVALID_PARAMETER);
}
else
{
hHandle->dwFileType = FILETYPE_CABINET;
hHandle->CabinetFile.pvFileBase = VirtualAlloc(NULL, DEFAULT_RESERVE_SIZE, MEM_RESERVE, PAGE_READWRITE);
hHandle->CabinetFile.cbReserved = DEFAULT_RESERVE_SIZE;
hHandle->CabinetFile.cbCommitted = 0;
hHandle->CabinetFile.pvCursor = hHandle->CabinetFile.pvFileBase;
hHandle->CabinetFile.cbWritten = 0;
pContext->CabinetFileHandle = hHandle;
}
}
else
{
//
// Open a real file on disk to write - probably a temp file
//
hHandle->dwFileType = FILETYPE_DISKFILE;
hHandle->hDiskFile = CreateFileW(pszConverted, GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (hHandle->hDiskFile == INVALID_HANDLE_VALUE)
{
pContext->ObtainErrorContext();
*err = ::GetLastError();
goto Exit;
}
}
}
//
// Read a file from disk
//
else
{
hHandle->dwFileType = FILETYPE_DISKFILE;
wprintf(L"Opening %ls\n", pszConverted);
hHandle->hDiskFile = CreateFileW(pszConverted, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (hHandle->hDiskFile == INVALID_HANDLE_VALUE)
{
pContext->ObtainErrorContext();
*err = ::GetLastError();
goto Exit;
}
}
ipRetVal = (INT_PTR)hHandle;
hHandle = NULL;
Exit:
if (pszConverted)
s_Free(pszConverted);
if (hHandle)
{
s_Free(hHandle);
}
return ipRetVal;
}
FNFCIREAD(s_ReadFile)
{
FILE_HANDLE *pFile = (FILE_HANDLE*)hf;
FCI_CONTEXT_OBJECT *pContext = (FCI_CONTEXT_OBJECT*)pv;
DWORD cbReadSize = 0;
UINT uiRetVal = -1;
if (pFile->dwFileType == FILETYPE_DISKFILE)
{
if (!ReadFile(pFile->hDiskFile, memory, cb, &cbReadSize, NULL))
{
pContext->ObtainErrorContext();
*err = ::GetLastError();
goto Exit;
}
}
else if (pFile->dwFileType == FILETYPE_CABINET)
{
ULONG_PTR ulCursor = (ULONG_PTR)pFile->CabinetFile.pvCursor;
const ULONG_PTR ulTotalSize = (ULONG_PTR)pFile->CabinetFile.pvFileBase + pFile->CabinetFile.cbWritten;
if (ulCursor >= ulTotalSize)
{
cbReadSize = 0;
}
else
{
if (cb > (ulTotalSize - ulCursor))
{
cb = (ulTotalSize - ulCursor);
}
memcpy(memory, pFile->CabinetFile.pvCursor, cb);
pFile->CabinetFile.pvCursor = (PVOID)(ulCursor + cb);
}
}
else
{
pContext->fWasError = true;
*err = (int)(pContext->dwLastError = ERROR_INVALID_PARAMETER);
goto Exit;
}
uiRetVal = cbReadSize;
Exit:
return uiRetVal;
}
FNFCIWRITE(s_WriteFile)
{
FILE_HANDLE *pFile = (FILE_HANDLE*)hf;
FCI_CONTEXT_OBJECT *pContext = (FCI_CONTEXT_OBJECT*)pv;
UINT uiRetVal = -1;
//
// Cabinet in-memory files are in large chunks. If the current cursor
// plus the requested write size goes beyond the commit limit, then
// reserve some more pages.
//
if (pFile->dwFileType == FILETYPE_CABINET)
{
ULONG_PTR ulpRequiredCommitted = cb;
ulpRequiredCommitted += ((ULONG_PTR)pFile->CabinetFile.pvCursor - (ULONG_PTR)pFile->CabinetFile.pvFileBase);
//
// If the cursor is NULL, or the cursor plus the write request goes beyond
// the committed range..
//
if (ulpRequiredCommitted > pFile->CabinetFile.cbCommitted)
{
PVOID pvReserveState;
//
// Just attempt to commit all the pages required
//
pvReserveState = VirtualAlloc(pFile->CabinetFile.pvFileBase, ulpRequiredCommitted, MEM_COMMIT, PAGE_READWRITE);
//
// If this fails, reserve another blob and schmooze the bytes over from the
// original allocation
//
if (pvReserveState == NULL)
{
const SIZE_T cbNewReserve = (ulpRequiredCommitted * 2) + (PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
// Reserve the new block
pvReserveState = VirtualAlloc(NULL, cbNewReserve, MEM_RESERVE, PAGE_READWRITE);
if (!pvReserveState)
{
pContext->ObtainErrorContext();
*err = ::GetLastError();
goto Exit;
}
// Commit what's necessary
if (!VirtualAlloc(pvReserveState, ulpRequiredCommitted, MEM_COMMIT, PAGE_READWRITE))
{
pContext->ObtainErrorContext();
*err = ::GetLastError();
goto Exit;
}
// Shoof data over
memcpy(pvReserveState, pFile->CabinetFile.pvFileBase, pFile->CabinetFile.cbWritten);
// Adjust file cursor, if necessary
if (pFile->CabinetFile.pvCursor == NULL)
pFile->CabinetFile.pvCursor = pvReserveState;
else
pFile->CabinetFile.pvCursor = (PVOID)(((ULONG_PTR)pvReserveState) + ((ULONG_PTR)pFile->CabinetFile.pvCursor - (ULONG_PTR)pFile->CabinetFile.pvFileBase));
// Free old reservation
VirtualFree(pFile->CabinetFile.pvFileBase, 0, MEM_RELEASE);
// Reset stuff
pFile->CabinetFile.pvFileBase = pvReserveState;
pFile->CabinetFile.cbReserved = cbNewReserve;
pFile->CabinetFile.cbCommitted = ulpRequiredCommitted;
}
else
{
pFile->CabinetFile.cbCommitted = ulpRequiredCommitted;
}
}
//
// Okay, now just copy to the cursor and be done
//
memcpy(pFile->CabinetFile.pvCursor, memory, cb);
pFile->CabinetFile.pvCursor = (PVOID)(((ULONG_PTR)pFile->CabinetFile.pvCursor) + cb);
// If we wrote more than the high-water marker, reset it
if (ulpRequiredCommitted > pFile->CabinetFile.cbWritten)
{
pFile->CabinetFile.cbWritten = ulpRequiredCommitted;
}
uiRetVal = cb;
}
else if (pFile->dwFileType == FILETYPE_DISKFILE)
{
DWORD cbWritten;
if (!WriteFile(pFile->hDiskFile, memory, cb, &cbWritten, NULL))
{
pContext->ObtainErrorContext();
*err = ::GetLastError();
goto Exit;
}
uiRetVal = cbWritten;
}
else
{
pContext->fWasError = true;
*err = (int)(pContext->dwLastError = ERROR_INVALID_PARAMETER);
uiRetVal = -1;
goto Exit;
}
Exit:
return uiRetVal;
}
FNFCICLOSE(s_Close)
{
FILE_HANDLE *pFile = (FILE_HANDLE*)hf;
FCI_CONTEXT_OBJECT *pContext = (FCI_CONTEXT_OBJECT*)pv;
if (pFile->dwFileType == FILETYPE_DISKFILE)
{
CloseHandle(pFile->hDiskFile);
}
else if (pFile->dwFileType == FILETYPE_CABINET)
{
if (pFile == pContext->CabinetFileHandle)
pFile = NULL;
else
VirtualFree(pFile->CabinetFile.pvFileBase, 0, MEM_RELEASE);
}
if (pFile)
s_Free(pFile);
return 0;
}
FNFCISEEK(s_Seek)
{
FILE_HANDLE *pFile = (FILE_HANDLE*)hf;
long result;
if (pFile->dwFileType == FILETYPE_CABINET)
{
switch (seektype) {
case SEEK_SET:
if (dist < 0) dist = 0;
pFile->CabinetFile.pvCursor = (PVOID)(((ULONG_PTR)pFile->CabinetFile.pvFileBase) + dist);
break;
case SEEK_CUR:
pFile->CabinetFile.pvCursor = (PVOID)(((INT_PTR)pFile->CabinetFile.pvCursor) + dist);
break;
case SEEK_END:
pFile->CabinetFile.pvCursor = (PVOID)(((INT_PTR)pFile->CabinetFile.pvFileBase) + pFile->CabinetFile.cbWritten + dist);
break;
}
result = (long)((LONG_PTR)pFile->CabinetFile.pvCursor - (LONG_PTR)pFile->CabinetFile.pvFileBase);
}
else if (pFile->dwFileType == FILETYPE_DISKFILE)
{
result = (long)SetFilePointer(pFile->hDiskFile, dist, NULL, seektype);
}
return result;
}
FNFCIDELETE(s_Delete)
{
FCI_CONTEXT_OBJECT *pContext = (FCI_CONTEXT_OBJECT*)pv;
*err = 0;
if (lstrcmpiA(pContext->pszCabFileName, pszFile) == 0)
return 0;
if (!DeleteFileA(pszFile))
{
pContext->ObtainErrorContext();
*err = ::GetLastError();
return -1;
}
else
{
return 1;
}
}
FNFCIGETTEMPFILE(s_TempFile)
{
WCHAR chTemp[MAX_PATH];
WCHAR chTempOutput[MAX_PATH];
int i;
GetTempPathW(NUMBER_OF(chTemp), chTemp);
GetTempFileNameW(chTemp, L"SXP", 0, chTempOutput);
i = WideCharToMultiByte(CP_UTF8, 0, chTempOutput, -1, pszTempName, cbTempName, NULL, NULL);
return ((i > 0) && (i < cbTempName));
}
FNFCIGETNEXTCABINET(s_GetNextCabinet) { return FALSE; }
//
// All files are UTF-8 encoded
//
FNFCIGETOPENINFO(s_GetOpenInfo)
{
*pdate = *ptime = 0;
*pattribs = _A_NAME_IS_UTF;
return s_OpenFile(pszName, _O_BINARY|_O_RDONLY, _A_NAME_IS_UTF, err, pv);
}
FNFCISTATUS(s_StatusCallback)
{
switch (typeStatus)
{
case statusFile:
case statusFolder:
return 1;
break;
case statusCabinet:
return cb2;
break;
}
return -1;
}
BOOL
CabPathWorkerCallback(
HFCI hCabinet,
PWSTR pwszWorkingTemp,
SIZE_T cchTotalTemp,
PCWSTR pwszBaseInCabinet,
WIN32_FIND_DATAW *pFindData
)
{
HANDLE hFind = INVALID_HANDLE_VALUE;
BOOL fResult = FALSE;
SIZE_T ccOriginalLength = wcslen(pwszWorkingTemp);
SIZE_T cchBasePathLength;
if (pwszBaseInCabinet[0] == L'\\')
pwszBaseInCabinet++;
cchBasePathLength = wcslen(pwszBaseInCabinet);
//
// Ensure that the string is slash-terminated
//
if (pwszWorkingTemp[ccOriginalLength-1] != L'\\')
{
pwszWorkingTemp[ccOriginalLength++] = L'\\';
pwszWorkingTemp[ccOriginalLength] = UNICODE_NULL;
}
wcscpy(pwszWorkingTemp + ccOriginalLength, L"*");
hFind = ::FindFirstFileW(pwszWorkingTemp, pFindData);
if (hFind != NULL) do
{
pwszWorkingTemp[ccOriginalLength] = UNICODE_NULL;
if (pFindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
wcscat(pwszWorkingTemp, pFindData->cFileName);
if ((wcscmp(pFindData->cFileName, L".") == 0) ||
(wcscmp(pFindData->cFileName, L"..") == 0))
continue;
if (!CabPathWorkerCallback(hCabinet, pwszWorkingTemp, cchTotalTemp, pwszBaseInCabinet, pFindData))
goto Exit;
}
else
{
int i;
INSTALLED_FILE *pFile = NULL;
PWSTR pwszCursor = NULL;
SIZE_T cchRequired;
cchRequired = (cchBasePathLength + 1) + (wcslen(pFindData->cFileName) + 1);
pFile = (INSTALLED_FILE*)s_Alloc(sizeof(INSTALLED_FILE) + (sizeof(WCHAR) * cchRequired));
if (pFile == NULL)
goto Exit;
pwszCursor = (PWSTR)(pFile + 1);
wcscpy(pwszCursor, pwszBaseInCabinet);
pFile->pwszSubDir = pwszCursor;
pwszCursor[cchBasePathLength] = UNICODE_NULL;
pwszCursor += cchBasePathLength + 1;
wcscpy(pwszCursor, pFindData->cFileName);
pFile->pwszFileName = pwszCursor;
InsertHeadList(&InstalledFiles, &pFile->Links);
wcscat(pwszWorkingTemp, pFindData->cFileName);
i = WideCharToMultiByte(
CP_UTF8,
0,
pwszBaseInCabinet,
-1,
WorkingBufferTwo,
sizeof(WorkingBufferTwo),
NULL,
NULL);
if ((i == 0) || (i > sizeof(WorkingBufferTwo)))
goto Exit;
i = WideCharToMultiByte(
CP_UTF8,
0,
pwszWorkingTemp,
-1,
WorkingBufferOne,
sizeof(WorkingBufferOne),
NULL,
NULL);
if ((i == 0) || (i > sizeof(WorkingBufferOne)))
goto Exit;
if (!FCIAddFile(
hCabinet,
WorkingBufferOne,
WorkingBufferTwo,
FALSE,
s_GetNextCabinet,
s_StatusCallback,
s_GetOpenInfo,
tcompTYPE_LZX | tcompLZX_WINDOW_HI))
{
goto Exit;
}
}
}
while (::FindNextFileW(hFind, pFindData));
if (::GetLastError() != ERROR_NO_MORE_FILES)
goto Exit;
fResult = TRUE;
Exit:
if (hFind != INVALID_HANDLE_VALUE)
FindClose(hFind);
pwszWorkingTemp[ccOriginalLength] = UNICODE_NULL;
return fResult;
}
BOOL
WriteFormatted(
INT_PTR File,
PVOID pvContext,
PCWSTR fmt,
...
)
{
static WCHAR wchDump[1024];
va_list va;
int iError;
int iResult;
va_start(va, fmt);
iResult = _vsnwprintf(wchDump, NUMBER_OF(wchDump), fmt, va);
va_end(va);
if ((iResult >= 0) && (iResult < NUMBER_OF(wchDump)))
wchDump[iResult] = UNICODE_NULL;
else
iResult = 0;
return (s_WriteFile(File, wchDump, iResult * sizeof(WCHAR), &iError, pvContext) != -1);
}
BOOL
CreateCabFromPath(
PCWSTR pcwszBasePath,
FILE_HANDLE *CreatedCabinet,
PCWSTR pcwszInfName
)
{
CHAR chCabName[] = "::::";
ERF erf;
FCI_CONTEXT_OBJECT Context = { false, 0, NULL, chCabName };
const ULONG CabMaxSize = 0x04000000;
CCAB CabInfo = { CabMaxSize, CabMaxSize };
HFCI hCabinet = NULL;
WIN32_FIND_DATAW FindData;
BOOL fResult = FALSE;
HANDLE hFindFile = INVALID_HANDLE_VALUE;
static WCHAR wchBasePath[2048];
int i = 0;
InitializeListHead(&InstalledFiles);
lstrcpyA(CabInfo.szDisk, "SxsExpressCompressedCabinet");
lstrcpyA(CabInfo.szCab, "::");
lstrcpyA(CabInfo.szCabPath, "::");
wcscpy(wchBasePath, pcwszBasePath);
//
// Start off by creating a compression object
//
hCabinet = FCICreate(
&erf,
s_FilePlaced,
s_Alloc,
s_Free,
s_OpenFile,
s_ReadFile,
s_WriteFile,
s_Close,
s_Seek,
s_Delete,
s_TempFile,
&CabInfo,
(void*)&Context);
fResult = CabPathWorkerCallback(hCabinet, wchBasePath, NUMBER_OF(wchBasePath), wchBasePath + wcslen(wchBasePath), &FindData);
if (!fResult)
{
printf("Error adding cabinet files, %d (code %d)\n", erf.erfOper, erf.erfType);
goto Exit;
}
//
// No name given? Cons one up from the files found
//
if (pcwszInfName == NULL)
{
CHAR chTempFile[MAX_PATH];
int iError;
INT_PTR p;
LIST_ENTRY *pLink;
s_TempFile(chTempFile, NUMBER_OF(chTempFile), &Context);
p = s_OpenFile(chTempFile, _O_CREAT, 0, &iError, &Context);
WORD wBom = 0xfeff;
//
// This is a UCS-2 file.
//
s_WriteFile(p, &wBom, sizeof(wBom), &iError, &Context);
//
// Spit out the source and target paths
//
WriteFormatted(p, &Context, L"[FileEntries]\r\n");
pLink = InstalledFiles.Flink;
while (pLink && (pLink != &InstalledFiles))
{
INSTALLED_FILE *pFile = CONTAINING_RECORD(pLink, INSTALLED_FILE, Links);
WriteFormatted(p, &Context, L"%ls\\%ls;\\%ls\r\n", pFile->pwszSubDir, pFile->pwszFileName, pFile->pwszFileName);
pLink = pLink->Flink;
}
s_Close(p, &iError, &Context);
strcpy(WorkingBufferOne, chTempFile);
}
else
{
i = WideCharToMultiByte(CP_UTF8, 0, pcwszInfName, -1, WorkingBufferOne, sizeof(WorkingBufferOne), NULL, NULL);
if ((i == 0) || (i > sizeof(WorkingBufferOne)))
{
wprintf(L"Error converting INF file %ls name to UTF-8\r\n", pcwszInfName);
goto Exit;
}
}
fResult = FCIAddFile(
hCabinet,
WorkingBufferOne,
INF_SPECIAL_NAME,
FALSE,
s_GetNextCabinet,
s_StatusCallback,
s_GetOpenInfo,
tcompTYPE_LZX | tcompLZX_WINDOW_HI);
FCIFlushFolder(hCabinet, s_GetNextCabinet, s_StatusCallback);
FCIFlushCabinet(hCabinet, FALSE, s_GetNextCabinet, s_StatusCallback);
fResult = TRUE;
Exit:
if (hCabinet != NULL)
FCIDestroy(hCabinet);
*CreatedCabinet = *Context.CabinetFileHandle;
return fResult;
}