diff options
author | Rob Mensching <rob@firegiant.com> | 2017-09-03 11:22:38 -0700 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2017-09-03 13:33:33 -0700 |
commit | 5d8375007754101ff2889d0e79486c8f9b7cf5ab (patch) | |
tree | a76d6fb6a38dd9f04a93ffcfd9d64e76779b3414 /src/dutil/rexutil.cpp | |
parent | 8e8da6dbc051ec884b5d439bb4f44dc027d05bbf (diff) | |
download | wix-5d8375007754101ff2889d0e79486c8f9b7cf5ab.tar.gz wix-5d8375007754101ff2889d0e79486c8f9b7cf5ab.tar.bz2 wix-5d8375007754101ff2889d0e79486c8f9b7cf5ab.zip |
Initial commit
Diffstat (limited to 'src/dutil/rexutil.cpp')
-rw-r--r-- | src/dutil/rexutil.cpp | 586 |
1 files changed, 586 insertions, 0 deletions
diff --git a/src/dutil/rexutil.cpp b/src/dutil/rexutil.cpp new file mode 100644 index 00000000..73500630 --- /dev/null +++ b/src/dutil/rexutil.cpp | |||
@@ -0,0 +1,586 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | #include "rexutil.h" | ||
5 | |||
6 | // | ||
7 | // static globals | ||
8 | // | ||
9 | static HMODULE vhCabinetDll = NULL; | ||
10 | static HFDI vhfdi = NULL; | ||
11 | static ERF verf; | ||
12 | |||
13 | static FAKE_FILE vrgffFileTable[FILETABLESIZE]; | ||
14 | static DWORD vcbRes; | ||
15 | static LPCBYTE vpbRes; | ||
16 | static CHAR vszResource[MAX_PATH]; | ||
17 | static REX_CALLBACK_WRITE vpfnWrite = NULL; | ||
18 | |||
19 | static HRESULT vhrLastError = S_OK; | ||
20 | |||
21 | // | ||
22 | // structs | ||
23 | // | ||
24 | struct REX_CALLBACK_STRUCT | ||
25 | { | ||
26 | BOOL fStopExtracting; // flag set when no more files are needed | ||
27 | LPCWSTR pwzExtract; // file to extract ("*" means extract all) | ||
28 | LPCWSTR pwzExtractDir; // directory to extract files to | ||
29 | LPCWSTR pwzExtractName; // name of file (pwzExtract can't be "*") | ||
30 | |||
31 | // possible user data | ||
32 | REX_CALLBACK_PROGRESS pfnProgress; | ||
33 | LPVOID pvContext; | ||
34 | }; | ||
35 | |||
36 | // | ||
37 | // prototypes | ||
38 | // | ||
39 | static __callback LPVOID DIAMONDAPI RexAlloc(DWORD dwSize); | ||
40 | static __callback void DIAMONDAPI RexFree(LPVOID pvData); | ||
41 | static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int oflag, int pmode); | ||
42 | static __callback UINT FAR DIAMONDAPI RexRead(INT_PTR hf, __out_bcount(cb) void FAR *pv, UINT cb); | ||
43 | static __callback UINT FAR DIAMONDAPI RexWrite(INT_PTR hf, __in_bcount(cb) void FAR *pv, UINT cb); | ||
44 | static __callback int FAR DIAMONDAPI RexClose(INT_PTR hf); | ||
45 | static __callback long FAR DIAMONDAPI RexSeek(INT_PTR hf, long dist, int seektype); | ||
46 | static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotification, FDINOTIFICATION *pFDINotify); | ||
47 | |||
48 | |||
49 | /******************************************************************** | ||
50 | RexInitialize - initializes internal static variables | ||
51 | |||
52 | *******************************************************************/ | ||
53 | extern "C" HRESULT RexInitialize() | ||
54 | { | ||
55 | Assert(!vhfdi); | ||
56 | |||
57 | HRESULT hr = S_OK; | ||
58 | |||
59 | vhfdi = ::FDICreate(RexAlloc, RexFree, RexOpen, RexRead, RexWrite, RexClose, RexSeek, cpuUNKNOWN, &verf); | ||
60 | if (!vhfdi) | ||
61 | { | ||
62 | hr = E_FAIL; | ||
63 | ExitOnFailure(hr, "failed to initialize cabinet.dll"); // TODO: put verf info in trace message here | ||
64 | } | ||
65 | |||
66 | ::ZeroMemory(vrgffFileTable, sizeof(vrgffFileTable)); | ||
67 | |||
68 | LExit: | ||
69 | if (FAILED(hr)) | ||
70 | { | ||
71 | ::FDIDestroy(vhfdi); | ||
72 | vhfdi = NULL; | ||
73 | } | ||
74 | |||
75 | return hr; | ||
76 | } | ||
77 | |||
78 | |||
79 | /******************************************************************** | ||
80 | RexUninitialize - initializes internal static variables | ||
81 | |||
82 | *******************************************************************/ | ||
83 | extern "C" void RexUninitialize() | ||
84 | { | ||
85 | if (vhfdi) | ||
86 | { | ||
87 | ::FDIDestroy(vhfdi); | ||
88 | vhfdi = NULL; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | |||
93 | /******************************************************************** | ||
94 | RexExtract - extracts one or all files from a resource cabinet | ||
95 | |||
96 | NOTE: wzExtractId can be a single file id or "*" to extract all files | ||
97 | wzExttractDir must be normalized (end in a "\") | ||
98 | wzExtractName is ignored if wzExtractId is "*" | ||
99 | *******************************************************************/ | ||
100 | extern "C" HRESULT RexExtract( | ||
101 | __in_z LPCSTR szResource, | ||
102 | __in_z LPCWSTR wzExtractId, | ||
103 | __in_z LPCWSTR wzExtractDir, | ||
104 | __in_z LPCWSTR wzExtractName, | ||
105 | __in REX_CALLBACK_PROGRESS pfnProgress, | ||
106 | __in REX_CALLBACK_WRITE pfnWrite, | ||
107 | __in LPVOID pvContext | ||
108 | ) | ||
109 | { | ||
110 | Assert(vhfdi); | ||
111 | HRESULT hr = S_OK; | ||
112 | BOOL fResult; | ||
113 | |||
114 | HRSRC hResInfo = NULL; | ||
115 | HANDLE hRes = NULL; | ||
116 | |||
117 | REX_CALLBACK_STRUCT rcs; | ||
118 | |||
119 | // remember the write callback | ||
120 | vpfnWrite = pfnWrite; | ||
121 | |||
122 | // | ||
123 | // load the cabinet resource | ||
124 | // | ||
125 | hResInfo = ::FindResourceExA(NULL, RT_RCDATA, szResource, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); | ||
126 | ExitOnNullWithLastError(hResInfo, hr, "Failed to find resource."); | ||
127 | //hResInfo = ::FindResourceW(NULL, wzResource, /*RT_RCDATA*/MAKEINTRESOURCEW(10)); | ||
128 | //ExitOnNullWithLastError(hResInfo, hr, "failed to load resource info"); | ||
129 | |||
130 | hRes = ::LoadResource(NULL, hResInfo); | ||
131 | ExitOnNullWithLastError(hRes, hr, "failed to load resource"); | ||
132 | |||
133 | vcbRes = ::SizeofResource(NULL, hResInfo); | ||
134 | vpbRes = (const BYTE*)::LockResource(hRes); | ||
135 | |||
136 | // TODO: Call FDIIsCabinet to confirm resource is a cabinet before trying to extract from it | ||
137 | |||
138 | // | ||
139 | // convert the resource name to multi-byte | ||
140 | // | ||
141 | //if (!::WideCharToMultiByte(CP_ACP, 0, wzResource, -1, vszResource, countof(vszResource), NULL, NULL)) | ||
142 | //{ | ||
143 | // ExitOnLastError(hr, "failed to convert cabinet resource name to ASCII: %ls", wzResource); | ||
144 | //} | ||
145 | |||
146 | hr = ::StringCchCopyA(vszResource, countof(vszResource), szResource); | ||
147 | ExitOnFailure(hr, "Failed to copy resource name to global."); | ||
148 | |||
149 | // | ||
150 | // iterate through files in cabinet extracting them to the callback function | ||
151 | // | ||
152 | rcs.fStopExtracting = FALSE; | ||
153 | rcs.pwzExtract = wzExtractId; | ||
154 | rcs.pwzExtractDir = wzExtractDir; | ||
155 | rcs.pwzExtractName = wzExtractName; | ||
156 | rcs.pfnProgress = pfnProgress; | ||
157 | rcs.pvContext = pvContext; | ||
158 | |||
159 | fResult = ::FDICopy(vhfdi, vszResource, "", 0, RexCallback, NULL, static_cast<void*>(&rcs)); | ||
160 | if (!fResult && !rcs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure | ||
161 | { | ||
162 | hr = vhrLastError; // TODO: put verf info in trace message here | ||
163 | } | ||
164 | |||
165 | LExit: | ||
166 | return hr; | ||
167 | } | ||
168 | |||
169 | |||
170 | /**************************************************************************** | ||
171 | default extract routines | ||
172 | |||
173 | ****************************************************************************/ | ||
174 | static __callback LPVOID DIAMONDAPI RexAlloc(DWORD dwSize) | ||
175 | { | ||
176 | return MemAlloc(dwSize, FALSE); | ||
177 | } | ||
178 | |||
179 | |||
180 | static __callback void DIAMONDAPI RexFree(LPVOID pvData) | ||
181 | { | ||
182 | MemFree(pvData); | ||
183 | } | ||
184 | |||
185 | |||
186 | static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int oflag, int pmode) | ||
187 | { | ||
188 | HRESULT hr = S_OK; | ||
189 | HANDLE hFile = INVALID_HANDLE_VALUE; | ||
190 | int i = 0; | ||
191 | |||
192 | // if FDI asks for some unusual mode (__in low memory situation it could ask for a scratch file) fail | ||
193 | if ((oflag != (/*_O_BINARY*/ 0x8000 | /*_O_RDONLY*/ 0x0000)) || (pmode != (_S_IREAD | _S_IWRITE))) | ||
194 | { | ||
195 | hr = E_OUTOFMEMORY; | ||
196 | ExitOnFailure(hr, "FDI asked for to create a scratch file, which is unusual"); | ||
197 | } | ||
198 | |||
199 | // find an empty spot in the fake file table | ||
200 | for (i = 0; i < FILETABLESIZE; ++i) | ||
201 | { | ||
202 | if (!vrgffFileTable[i].fUsed) | ||
203 | { | ||
204 | break; | ||
205 | } | ||
206 | } | ||
207 | |||
208 | // we should never run out of space in the fake file table | ||
209 | if (FILETABLESIZE <= i) | ||
210 | { | ||
211 | hr = E_OUTOFMEMORY; | ||
212 | ExitOnFailure(hr, "File table exceeded"); | ||
213 | } | ||
214 | |||
215 | if (0 == lstrcmpA(vszResource, pszFile)) | ||
216 | { | ||
217 | vrgffFileTable[i].fUsed = TRUE; | ||
218 | vrgffFileTable[i].fftType = MEMORY_FILE; | ||
219 | vrgffFileTable[i].mfFile.vpStart = static_cast<LPCBYTE>(vpbRes); | ||
220 | vrgffFileTable[i].mfFile.uiCurrent = 0; | ||
221 | vrgffFileTable[i].mfFile.uiLength = vcbRes; | ||
222 | } | ||
223 | else // it's a real file | ||
224 | { | ||
225 | hFile = ::CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | ||
226 | if (INVALID_HANDLE_VALUE == hFile) | ||
227 | { | ||
228 | ExitWithLastError(hr, "failed to open file: %s", pszFile); | ||
229 | } | ||
230 | |||
231 | vrgffFileTable[i].fUsed = TRUE; | ||
232 | vrgffFileTable[i].fftType = NORMAL_FILE; | ||
233 | vrgffFileTable[i].hFile = hFile; | ||
234 | } | ||
235 | |||
236 | LExit: | ||
237 | if (FAILED(hr)) | ||
238 | { | ||
239 | vhrLastError = hr; | ||
240 | } | ||
241 | |||
242 | return FAILED(hr) ? -1 : i; | ||
243 | } | ||
244 | |||
245 | |||
246 | static __callback UINT FAR DIAMONDAPI RexRead(INT_PTR hf, __out_bcount(cb) void FAR *pv, UINT cb) | ||
247 | { | ||
248 | Assert(vrgffFileTable[hf].fUsed); | ||
249 | |||
250 | HRESULT hr = S_OK; | ||
251 | DWORD cbRead = 0; | ||
252 | DWORD cbAvailable = 0; | ||
253 | |||
254 | if (MEMORY_FILE == vrgffFileTable[hf].fftType) | ||
255 | { | ||
256 | // ensure that we don't read past the length of the resource | ||
257 | cbAvailable = vrgffFileTable[hf].mfFile.uiLength - vrgffFileTable[hf].mfFile.uiCurrent; | ||
258 | cbRead = cb < cbAvailable? cb : cbAvailable; | ||
259 | |||
260 | memcpy(pv, static_cast<const void *>(vrgffFileTable[hf].mfFile.vpStart + vrgffFileTable[hf].mfFile.uiCurrent), cbRead); | ||
261 | |||
262 | vrgffFileTable[hf].mfFile.uiCurrent += cbRead; | ||
263 | } | ||
264 | else // NORMAL_FILE | ||
265 | { | ||
266 | Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); | ||
267 | |||
268 | if (!::ReadFile(vrgffFileTable[hf].hFile, pv, cb, &cbRead, NULL)) | ||
269 | { | ||
270 | ExitWithLastError(hr, "failed to read during cabinet extraction"); | ||
271 | } | ||
272 | } | ||
273 | |||
274 | LExit: | ||
275 | if (FAILED(hr)) | ||
276 | { | ||
277 | vhrLastError = hr; | ||
278 | } | ||
279 | |||
280 | return FAILED(hr) ? -1 : cbRead; | ||
281 | } | ||
282 | |||
283 | |||
284 | static __callback UINT FAR DIAMONDAPI RexWrite(INT_PTR hf, __in_bcount(cb) void FAR *pv, UINT cb) | ||
285 | { | ||
286 | Assert(vrgffFileTable[hf].fUsed); | ||
287 | Assert(vrgffFileTable[hf].fftType == NORMAL_FILE); // we should never be writing to a memory file | ||
288 | |||
289 | HRESULT hr = S_OK; | ||
290 | DWORD cbWrite = 0; | ||
291 | |||
292 | Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); | ||
293 | if (!::WriteFile(reinterpret_cast<HANDLE>(vrgffFileTable[hf].hFile), pv, cb, &cbWrite, NULL)) | ||
294 | { | ||
295 | ExitWithLastError(hr, "failed to write during cabinet extraction"); | ||
296 | } | ||
297 | |||
298 | // call the writer callback if defined | ||
299 | if (vpfnWrite) | ||
300 | { | ||
301 | vpfnWrite(cb); | ||
302 | } | ||
303 | |||
304 | LExit: | ||
305 | if (FAILED(hr)) | ||
306 | { | ||
307 | vhrLastError = hr; | ||
308 | } | ||
309 | |||
310 | return FAILED(hr) ? -1 : cbWrite; | ||
311 | } | ||
312 | |||
313 | |||
314 | static __callback long FAR DIAMONDAPI RexSeek(INT_PTR hf, long dist, int seektype) | ||
315 | { | ||
316 | Assert(vrgffFileTable[hf].fUsed); | ||
317 | |||
318 | HRESULT hr = S_OK; | ||
319 | DWORD dwMoveMethod; | ||
320 | LONG lMove = 0; | ||
321 | |||
322 | switch (seektype) | ||
323 | { | ||
324 | case 0: // SEEK_SET | ||
325 | dwMoveMethod = FILE_BEGIN; | ||
326 | break; | ||
327 | case 1: /// SEEK_CUR | ||
328 | dwMoveMethod = FILE_CURRENT; | ||
329 | break; | ||
330 | case 2: // SEEK_END | ||
331 | dwMoveMethod = FILE_END; | ||
332 | break; | ||
333 | default : | ||
334 | dwMoveMethod = 0; | ||
335 | hr = E_UNEXPECTED; | ||
336 | ExitOnFailure(hr, "unexpected seektype in FDISeek(): %d", seektype); | ||
337 | } | ||
338 | |||
339 | if (MEMORY_FILE == vrgffFileTable[hf].fftType) | ||
340 | { | ||
341 | if (FILE_BEGIN == dwMoveMethod) | ||
342 | { | ||
343 | vrgffFileTable[hf].mfFile.uiCurrent = dist; | ||
344 | } | ||
345 | else if (FILE_CURRENT == dwMoveMethod) | ||
346 | { | ||
347 | vrgffFileTable[hf].mfFile.uiCurrent += dist; | ||
348 | } | ||
349 | else // FILE_END | ||
350 | { | ||
351 | vrgffFileTable[hf].mfFile.uiCurrent = vrgffFileTable[hf].mfFile.uiLength + dist; | ||
352 | } | ||
353 | |||
354 | lMove = vrgffFileTable[hf].mfFile.uiCurrent; | ||
355 | } | ||
356 | else // NORMAL_FILE | ||
357 | { | ||
358 | Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); | ||
359 | |||
360 | // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error. | ||
361 | // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET) | ||
362 | lMove = ::SetFilePointer(vrgffFileTable[hf].hFile, dist, NULL, dwMoveMethod); | ||
363 | if (0xFFFFFFFF == lMove) | ||
364 | { | ||
365 | ExitWithLastError(hr, "failed to move file pointer %d bytes", dist); | ||
366 | } | ||
367 | } | ||
368 | |||
369 | LExit: | ||
370 | if (FAILED(hr)) | ||
371 | { | ||
372 | vhrLastError = hr; | ||
373 | } | ||
374 | |||
375 | return FAILED(hr) ? -1 : lMove; | ||
376 | } | ||
377 | |||
378 | |||
379 | __callback int FAR DIAMONDAPI RexClose(INT_PTR hf) | ||
380 | { | ||
381 | Assert(vrgffFileTable[hf].fUsed); | ||
382 | |||
383 | HRESULT hr = S_OK; | ||
384 | |||
385 | if (MEMORY_FILE == vrgffFileTable[hf].fftType) | ||
386 | { | ||
387 | vrgffFileTable[hf].mfFile.vpStart = NULL; | ||
388 | vrgffFileTable[hf].mfFile.uiCurrent = 0; | ||
389 | vrgffFileTable[hf].mfFile.uiLength = 0; | ||
390 | } | ||
391 | else | ||
392 | { | ||
393 | Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE); | ||
394 | |||
395 | if (!::CloseHandle(vrgffFileTable[hf].hFile)) | ||
396 | { | ||
397 | ExitWithLastError(hr, "failed to close file during cabinet extraction"); | ||
398 | } | ||
399 | |||
400 | vrgffFileTable[hf].hFile = INVALID_HANDLE_VALUE; | ||
401 | } | ||
402 | |||
403 | vrgffFileTable[hf].fUsed = FALSE; | ||
404 | |||
405 | LExit: | ||
406 | if (FAILED(hr)) | ||
407 | { | ||
408 | vhrLastError = hr; | ||
409 | } | ||
410 | |||
411 | return FAILED(hr) ? -1 : 0; | ||
412 | } | ||
413 | |||
414 | |||
415 | static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotification, FDINOTIFICATION *pFDINotify) | ||
416 | { | ||
417 | Assert(pFDINotify->pv); | ||
418 | |||
419 | HRESULT hr = S_OK; | ||
420 | int ipResult = 0; // result to return on success | ||
421 | HANDLE hFile = INVALID_HANDLE_VALUE; | ||
422 | |||
423 | REX_CALLBACK_STRUCT* prcs = static_cast<REX_CALLBACK_STRUCT*>(pFDINotify->pv); | ||
424 | LPCSTR sz; | ||
425 | WCHAR wz[MAX_PATH]; | ||
426 | FILETIME ft; | ||
427 | int i = 0; | ||
428 | |||
429 | switch (iNotification) | ||
430 | { | ||
431 | case fdintCOPY_FILE: // beGIN extracting a resource from cabinet | ||
432 | Assert(pFDINotify->psz1); | ||
433 | |||
434 | if (prcs->fStopExtracting) | ||
435 | { | ||
436 | ExitFunction1(hr = S_FALSE); // no more extracting | ||
437 | } | ||
438 | |||
439 | // convert params to useful variables | ||
440 | sz = static_cast<LPCSTR>(pFDINotify->psz1); | ||
441 | if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) | ||
442 | { | ||
443 | ExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); | ||
444 | } | ||
445 | |||
446 | if (prcs->pfnProgress) | ||
447 | { | ||
448 | hr = prcs->pfnProgress(TRUE, wz, prcs->pvContext); | ||
449 | if (S_OK != hr) | ||
450 | { | ||
451 | ExitFunction(); | ||
452 | } | ||
453 | } | ||
454 | |||
455 | if (L'*' == *prcs->pwzExtract || 0 == lstrcmpW(prcs->pwzExtract, wz)) | ||
456 | { | ||
457 | // get the created date for the resource in the cabinet | ||
458 | if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ft)) | ||
459 | { | ||
460 | ExitWithLastError(hr, "failed to get time for resource: %ls", wz); | ||
461 | } | ||
462 | |||
463 | WCHAR wzPath[MAX_PATH]; | ||
464 | |||
465 | hr = ::StringCchCopyW(wzPath, countof(wzPath), prcs->pwzExtractDir); | ||
466 | ExitOnFailure(hr, "failed to copy extract directory: %ls for file: %ls", prcs->pwzExtractDir, wz); | ||
467 | |||
468 | if (L'*' == *prcs->pwzExtract) | ||
469 | { | ||
470 | hr = ::StringCchCatW(wzPath, countof(wzPath), wz); | ||
471 | ExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz); | ||
472 | } | ||
473 | else | ||
474 | { | ||
475 | Assert(*prcs->pwzExtractName); | ||
476 | |||
477 | hr = ::StringCchCatW(wzPath, countof(wzPath), prcs->pwzExtractName); | ||
478 | ExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, prcs->pwzExtractName); | ||
479 | } | ||
480 | |||
481 | // Quickly chop off the file name part of the path to ensure the path exists | ||
482 | // then put the file name back on the path (by putting the first character | ||
483 | // back over the null terminator). | ||
484 | LPWSTR wzFile = PathFile(wzPath); | ||
485 | WCHAR wzFileFirstChar = *wzFile; | ||
486 | *wzFile = L'\0'; | ||
487 | |||
488 | hr = DirEnsureExists(wzPath, NULL); | ||
489 | ExitOnFailure(hr, "failed to ensure directory: %ls", wzPath); | ||
490 | |||
491 | hr = S_OK; | ||
492 | |||
493 | *wzFile = wzFileFirstChar; | ||
494 | |||
495 | // find an empty spot in the fake file table | ||
496 | for (i = 0; i < FILETABLESIZE; ++i) | ||
497 | { | ||
498 | if (!vrgffFileTable[i].fUsed) | ||
499 | { | ||
500 | break; | ||
501 | } | ||
502 | } | ||
503 | |||
504 | // we should never run out of space in the fake file table | ||
505 | if (FILETABLESIZE <= i) | ||
506 | { | ||
507 | hr = E_OUTOFMEMORY; | ||
508 | ExitOnFailure(hr, "File table exceeded"); | ||
509 | } | ||
510 | |||
511 | // open the file | ||
512 | hFile = ::CreateFileW(wzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); | ||
513 | if (INVALID_HANDLE_VALUE == hFile) | ||
514 | { | ||
515 | ExitWithLastError(hr, "failed to open file: %ls", wzPath); | ||
516 | } | ||
517 | |||
518 | vrgffFileTable[i].fUsed = TRUE; | ||
519 | vrgffFileTable[i].fftType = NORMAL_FILE; | ||
520 | vrgffFileTable[i].hFile = hFile; | ||
521 | |||
522 | ipResult = i; | ||
523 | |||
524 | ::SetFileTime(vrgffFileTable[i].hFile, &ft, &ft, &ft); // try to set the file time (who cares if it fails) | ||
525 | |||
526 | if (::SetFilePointer(vrgffFileTable[i].hFile, pFDINotify->cb, NULL, FILE_BEGIN)) // try to set the end of the file (don't worry if this fails) | ||
527 | { | ||
528 | if (::SetEndOfFile(vrgffFileTable[i].hFile)) | ||
529 | { | ||
530 | ::SetFilePointer(vrgffFileTable[i].hFile, 0, NULL, FILE_BEGIN); // reset the file pointer | ||
531 | } | ||
532 | } | ||
533 | } | ||
534 | else // resource wasn't requested, skip it | ||
535 | { | ||
536 | hr = S_OK; | ||
537 | ipResult = 0; | ||
538 | } | ||
539 | |||
540 | break; | ||
541 | case fdintCLOSE_FILE_INFO: // resource extraction complete | ||
542 | Assert(pFDINotify->hf && pFDINotify->psz1); | ||
543 | |||
544 | // convert params to useful variables | ||
545 | sz = static_cast<LPCSTR>(pFDINotify->psz1); | ||
546 | if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) | ||
547 | { | ||
548 | ExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); | ||
549 | } | ||
550 | |||
551 | RexClose(pFDINotify->hf); | ||
552 | |||
553 | if (prcs->pfnProgress) | ||
554 | { | ||
555 | hr = prcs->pfnProgress(FALSE, wz, prcs->pvContext); | ||
556 | } | ||
557 | |||
558 | if (S_OK == hr && L'*' == *prcs->pwzExtract) // if everything is okay and we're extracting all files, keep going | ||
559 | { | ||
560 | ipResult = TRUE; | ||
561 | } | ||
562 | else // something went wrong or we only needed to extract one file | ||
563 | { | ||
564 | hr = S_OK; | ||
565 | ipResult = FALSE; | ||
566 | prcs->fStopExtracting = TRUE; | ||
567 | } | ||
568 | |||
569 | break; | ||
570 | case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages, fall through | ||
571 | case fdintNEXT_CABINET: __fallthrough; | ||
572 | case fdintENUMERATE: __fallthrough; | ||
573 | case fdintCABINET_INFO: | ||
574 | break; | ||
575 | default: | ||
576 | AssertSz(FALSE, "RexCallback() - unknown FDI notification command"); | ||
577 | }; | ||
578 | |||
579 | LExit: | ||
580 | if (FAILED(hr)) | ||
581 | { | ||
582 | vhrLastError = hr; | ||
583 | } | ||
584 | |||
585 | return (S_OK == hr) ? ipResult : -1; | ||
586 | } | ||