2 * PROJECT: ReactOS Applications Manager
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * FILE: base/applications/rapps/cabinet.cpp
5 * PURPOSE: Cabinet extraction using FDI API
6 * COPYRIGHT: Copyright 2018 Alexander Shaposhnikov (sanchaez@reactos.org)
14 * HACK: treat any input strings as Unicode (UTF-8)
15 * cabinet.dll lacks any sort of a Unicode API, but FCI/FDI
16 * provide an ability to use user-defined callbacks for any file or memory
17 * operations. This flexibility and the magic power of C/C++ casting allows
18 * us to treat input as we please.
19 * This is by far the best way to extract .cab using Unicode paths.
22 /* String conversion helper functions */
24 // converts CStringW to CStringA using a given codepage
25 inline BOOL
WideToMultiByte(const CStringW
& szSource
,
29 // determine the needed size
30 INT sz
= WideCharToMultiByte(Codepage
,
41 // do the actual conversion
42 sz
= WideCharToMultiByte(Codepage
,
51 szDest
.ReleaseBuffer();
55 // converts CStringA to CStringW using a given codepage
56 inline BOOL
MultiByteToWide(const CStringA
& szSource
,
60 // determine the needed size
61 INT sz
= MultiByteToWideChar(Codepage
,
70 // do the actual conversion
71 sz
= MultiByteToWideChar(CP_UTF8
,
78 szDest
.ReleaseBuffer();
82 /* FDICreate callbacks */
86 return HeapAlloc(GetProcessHeap(), NULL
, cb
);
91 HeapFree(GetProcessHeap(), NULL
, pv
);
97 DWORD dwDesiredAccess
= 0;
98 DWORD dwCreationDisposition
= 0;
99 ATL::CStringW szFileName
;
101 UNREFERENCED_PARAMETER(pmode
);
105 dwDesiredAccess
= GENERIC_READ
| GENERIC_WRITE
;
107 else if (oflag
& _O_WRONLY
)
109 dwDesiredAccess
= GENERIC_WRITE
;
113 dwDesiredAccess
= GENERIC_READ
;
116 if (oflag
& _O_CREAT
)
118 dwCreationDisposition
= CREATE_ALWAYS
;
122 dwCreationDisposition
= OPEN_EXISTING
;
125 MultiByteToWide(pszFile
, szFileName
, CP_UTF8
);
127 hFile
= CreateFileW(szFileName
,
131 dwCreationDisposition
,
132 FILE_ATTRIBUTE_NORMAL
,
135 return (INT_PTR
) hFile
;
140 DWORD dwBytesRead
= 0;
142 if (ReadFile((HANDLE
) hf
, pv
, cb
, &dwBytesRead
, NULL
) == FALSE
)
144 dwBytesRead
= (DWORD
) -1L;
152 DWORD dwBytesWritten
= 0;
154 if (WriteFile((HANDLE
) hf
, pv
, cb
, &dwBytesWritten
, NULL
) == FALSE
)
156 dwBytesWritten
= (DWORD
) -1;
159 return dwBytesWritten
;
164 return (CloseHandle((HANDLE
) hf
) != FALSE
) ? 0 : -1;
169 return SetFilePointer((HANDLE
) hf
, dist
, NULL
, seektype
);
172 /* FDICopy callbacks */
174 FNFDINOTIFY(fnNotify
)
182 ATL::CStringW szNewFileName
, szExtractDir
, szCabFileName
;
183 ATL::CStringA szFilePathUTF8
;
185 // Append the destination directory to the file name.
186 MultiByteToWide((LPCSTR
) pfdin
->pv
, szExtractDir
, CP_UTF8
);
187 MultiByteToWide(pfdin
->psz1
, szCabFileName
, CP_ACP
);
189 szNewFileName
= szExtractDir
+ L
"\\" + szCabFileName
;
191 WideToMultiByte(szNewFileName
, szFilePathUTF8
, CP_UTF8
);
194 iResult
= fnFileOpen((LPSTR
) szFilePathUTF8
.GetString(),
195 _O_WRONLY
| _O_CREAT
,
200 case fdintCLOSE_FILE_INFO
:
201 iResult
= !fnFileClose(pfdin
->hf
);
204 case fdintNEXT_CABINET
:
205 if (pfdin
->fdie
!= FDIERROR_NONE
)
211 case fdintPARTIAL_FILE
:
215 case fdintCABINET_INFO
:
231 /* cabinet.dll FDI function pointers */
233 typedef HFDI(*fnFDICreate
)(PFNALLOC
,
243 typedef BOOL(*fnFDICopy
)(HFDI
,
251 typedef BOOL(*fnFDIDestroy
)(HFDI
);
254 * Extraction function
255 * TODO: require only a full path to the cab as an argument
257 BOOL
ExtractFilesFromCab(const ATL::CStringW
& szCabName
,
258 const ATL::CStringW
& szCabDir
,
259 const ATL::CStringW
& szOutputDir
)
261 HINSTANCE hCabinetDll
;
264 ATL::CStringA szCabNameUTF8
, szCabDirUTF8
, szOutputDirUTF8
;
265 fnFDICreate pfnFDICreate
;
266 fnFDICopy pfnFDICopy
;
267 fnFDIDestroy pfnFDIDestroy
;
270 // Load cabinet.dll and extract needed functions
271 hCabinetDll
= LoadLibraryW(L
"cabinet.dll");
278 pfnFDICreate
= (fnFDICreate
) GetProcAddress(hCabinetDll
, "FDICreate");
279 pfnFDICopy
= (fnFDICopy
) GetProcAddress(hCabinetDll
, "FDICopy");
280 pfnFDIDestroy
= (fnFDIDestroy
) GetProcAddress(hCabinetDll
, "FDIDestroy");
282 if (!pfnFDICreate
|| !pfnFDICopy
|| !pfnFDIDestroy
)
284 FreeLibrary(hCabinetDll
);
288 // Create FDI context
289 ExtractHandler
= pfnFDICreate(fnMemAlloc
,
301 FreeLibrary(hCabinetDll
);
306 bResult
= CreateDirectoryW(szOutputDir
, NULL
);
308 if (bResult
|| GetLastError() == ERROR_ALREADY_EXISTS
)
310 // Convert wide strings to UTF-8
311 bResult
= WideToMultiByte(szCabName
, szCabNameUTF8
, CP_UTF8
);
312 bResult
&= WideToMultiByte(szCabDir
, szCabDirUTF8
, CP_UTF8
);
313 bResult
&= WideToMultiByte(szOutputDir
, szOutputDirUTF8
, CP_UTF8
);
316 // Perform extraction
319 // Add a slash to cab name as required by the api
320 szCabNameUTF8
= "\\" + szCabNameUTF8
;
322 bResult
= pfnFDICopy(ExtractHandler
,
323 (LPSTR
) szCabNameUTF8
.GetString(),
324 (LPSTR
) szCabDirUTF8
.GetString(),
328 (void FAR
*) szOutputDirUTF8
.GetString());
331 pfnFDIDestroy(ExtractHandler
);
332 FreeLibrary(hCabinetDll
);