-
Notifications
You must be signed in to change notification settings - Fork 1
/
compressor.cpp
359 lines (325 loc) · 10.9 KB
/
compressor.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
// CEZEO software Ltd. https://www.cezeo.com
#include "compressor.h"
#include "system_utils.h"
#include "string_utils.h"
#include <io.h>
#include <errno.h>
#include <shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
// #include <fcntl.h>
#define _O_RDONLY 0x0000 // open for reading only
#define _O_WRONLY 0x0001 // open for writing only
#define _O_RDWR 0x0002 // open for reading and writing
#define _O_APPEND 0x0008 // writes done at eof
#define _O_CREAT 0x0100 // create and open file
#define _O_TRUNC 0x0200 // open and truncate
#define _O_EXCL 0x0400 // open only if file doesn't already exist
// O_TEXT files have <cr><lf> sequences translated to <lf> on read()'s and <lf>
// sequences translated to <cr><lf> on write()'s
#define _O_TEXT 0x4000 // file mode is text (translated)
#define _O_BINARY 0x8000 // file mode is binary (untranslated)
#define _O_WTEXT 0x10000 // file mode is UTF16 (translated)
#define _O_U16TEXT 0x20000 // file mode is UTF16 no BOM (translated)
#define _O_U8TEXT 0x40000 // file mode is UTF8 no BOM (translated)
// macro to translate the C 2.0 name used to force binary mode for files
#define _O_RAW _O_BINARY
#define _O_NOINHERIT 0x0080 // child process doesn't inherit file
#define _O_TEMPORARY 0x0040 // temporary file bit (file is deleted when last handle is closed)
#define _O_SHORT_LIVED 0x1000 // temporary storage file, try not to flush
#define _O_OBTAIN_DIR 0x2000 // get information about a directory
#define _O_SEQUENTIAL 0x0020 // file access is primarily sequential
#define _O_RANDOM 0x0010 // file access is primarily random
// #include <sys/stat.h>
#define _S_IFMT 0xF000 // File type mask
#define _S_IFDIR 0x4000 // Directory
#define _S_IFCHR 0x2000 // Character special
#define _S_IFIFO 0x1000 // Pipe
#define _S_IFREG 0x8000 // Regular
#define _S_IREAD 0x0100 // Read permission, owner
#define _S_IWRITE 0x0080 // Write permission, owner
#define _S_IEXEC 0x0040 // Execute/search permission, owner
namespace cab
{
/*
struct CCAB
{
// longs first
ULONG cb; // size available for cabinet on this media
ULONG cbFolderThresh; // Thresshold for forcing a new Folder
// then ints
UINT cbReserveCFHeader; // Space to reserve in CFHEADER
UINT cbReserveCFFolder; // Space to reserve in CFFOLDER
UINT cbReserveCFData; // Space to reserve in CFDATA
int iCab; // sequential numbers for cabinets
int iDisk; // Disk number
#ifndef REMOVE_CHICAGO_M6_HACK
int fFailOnIncompressible; // TRUE => Fail if a block is incompressible
#endif
// then shorts
USHORT setID; // Cabinet set ID
// then chars
char szDisk[ CB_MAX_DISK_NAME ]; // current disk name
char szCab[ CB_MAX_CABINET_NAME ]; // current cabinet name
char szCabPath[ CB_MAX_CAB_PATH ]; // path for creating cabinet
};
*/
namespace
{
// FCI callbacks
// will not override
FNFCIALLOC(fnMemAlloc)
{
return HeapAlloc(GetProcessHeap(), NULL, cb);
}
// will not override
FNFCIFREE(fnMemFree)
{
HeapFree(GetProcessHeap(), NULL, memory);
}
// file placed callback
FNFCIFILEPLACED(fnFilePlaced)
{
// int (PCCAB pccab, _In_ LPSTR pszFile, long cbFile, BOOL fContinuation, void FAR *pv)
return 0;
}
FNFCIGETNEXTCABINET(fnGetNextCab)
{
// BOOL fn(PCCAB pccab, ULONG cbPrevCab, void FAR *pv)
return FALSE;
}
FNFCISTATUS(fnStatus)
{
// long fn(UINT typeStatus, ULONG cb1, ULONG cb2, void FAR *pv)
return 0;
}
FNFCIOPEN(fnOpen)
{
// INT_PTR fn(_In_ LPSTR pszFile, int oflag, int pmode, int FAR* err, void FAR* pv)
// convert from UTF-8 because we use it in worker
std::wstring fileName(StringUtils::FromUtf8(pszFile));
int result = _wopen(fileName.c_str(), oflag, pmode);
if (result == -1)
{
*err = errno;
}
return (INT_PTR)result;
}
FNFCIGETOPENINFO(fnGetOpenInfo)
{
// INT_PTR fn(_In_ LPSTR pszName, USHORT *pdate, USHORT *ptime, USHORT *pattribs, int FAR *err, void FAR *pv)
std::wstring fileName(StringUtils::FromUtf8(pszName));
WIN32_FILE_ATTRIBUTE_DATA fileAttrs;
if (GetFileAttributesEx(fileName.c_str(), GetFileExInfoStandard, &fileAttrs))
{
// UTC time
FileTimeToDosDateTime(&fileAttrs.ftLastWriteTime, pdate, ptime);
*pattribs = (USHORT)(fileAttrs.dwFileAttributes & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE));
// file names in utf
*pattribs |= _A_NAME_IS_UTF;
return fnOpen(pszName, _O_RDONLY | _O_BINARY, _S_IREAD, err, nullptr);
}
else
{
*err = GetLastError();
return -1;
}
}
FNFCIREAD(fnRead)
{
// UINT fn(INT_PTR hf, void FAR* memory, UINT cb, int FAR* err, void FAR* pv)
UINT result = (UINT)_read((int)hf, memory, cb);
if (result != cb)
{
*err = errno;
}
return result;
}
FNFCIWRITE(fnWrite)
{
// UINT fn(INT_PTR hf, void FAR* memory, UINT cb, int FAR* err, void FAR* pv)
UINT result = (UINT)_write((int)hf, memory, cb);
if (result != cb)
{
*err = errno;
}
return result;
}
FNFCICLOSE(fnClose)
{
// int fn(INT_PTR hf, int FAR* err, void FAR* pv)
int result = _close((int)hf);
if (result != 0)
{
*err = errno;
}
return result;
}
FNFCISEEK(fnSeek)
{
// long fn(INT_PTR hf, long dist, int seektype, int FAR* err, void FAR* pv)
long result = _lseek((int)hf, dist, seektype);
if (result == -1)
{
*err = errno;
}
return result;
}
FNFCIDELETE(fnDelete)
{
// int fn(_In_ LPSTR pszFile, int FAR* err, void FAR* pv)
std::wstring fileName(StringUtils::FromUtf8(pszFile));
int result = _wremove(fileName.c_str());
if (result != 0)
{
*err = errno;
}
return result;
}
FNFCIGETTEMPFILE(fnGetTempFile)
{
// BOOL fn(_Out_writes_bytes_(cbTempName) char* pszTempName, _In_range_(>=, MAX_PATH) int cbTempName, void FAR* pv)
memset(pszTempName, 0, cbTempName);
std::wstring tempPath;
if (SystemUtils::GenerateTempFileName(tempPath))
{
if (PathFileExists(tempPath.c_str()))
{
DeleteFile(tempPath.c_str());
}
std::string tempFilePath(StringUtils::ToUtf8(tempPath));
if (tempFilePath.size() <= (size_t)cbTempName)
{
memcpy(pszTempName, &tempFilePath[ 0 ], tempFilePath.size());
return TRUE;
}
}
// error
return FALSE;
}
} // namespace
compressor::compressor()
{
memset(&compressErrors, 0, sizeof(compressErrors));
memset(&cabParams, 0, sizeof(cabParams));
#ifdef CAB_STATIC_LINK
pfnFCICreate = FCICreate;
pfnFCIAddFile = FCIAddFile;
pfnFCIFlushFolder = FCIFlushFolder;
pfnFCIFlushCabinet = FCIFlushCabinet;
pfnFCIDestroy = FCIDestroy;
#else
cabinetDll.load("cabinet.dll");
if (nullptr != cabinetDll)
{
// use fci.lib
// dynamic link to cabinet.dll
pfnFCICreate = (fnFCICreate)GetProcAddress(cabinetDll, "FCICreate");
pfnFCIAddFile = (fnFCIAddFile)GetProcAddress(cabinetDll, "FCIAddFile");
pfnFCIFlushFolder = (fnFCIFlushFolder)GetProcAddress(cabinetDll, "FCIFlushFolder");
pfnFCIFlushCabinet = (fnFCIFlushCabinet)GetProcAddress(cabinetDll, "FCIFlushCabinet");
pfnFCIDestroy = (fnFCIDestroy)GetProcAddress(cabinetDll, "FCIDestroy");
}
else
{
throw(std::runtime_error("cabinet.dll can not be loaded"));
}
#endif
if (nullptr == pfnFCICreate || nullptr == pfnFCIDestroy || nullptr == pfnFCIAddFile || nullptr == pfnFCIFlushFolder || nullptr == pfnFCIFlushCabinet || nullptr == pfnFCIDestroy)
{
throw(std::runtime_error("fci funciton are not found"));
}
}
compressor::~compressor()
{
destroy_context();
}
void compressor::create_context(const std::wstring& target_path)
{
destroy_context();
// fill cab params
memset(&cabParams, 0, sizeof(CCAB));
cabParams.cb = 0x7FFFFFFF;
cabParams.cbFolderThresh = 0x7FFFFFFF;
cabParams.iCab = 1;
cabParams.iDisk = 1;
cabParams.setID = 0;
// char szCab[ CB_MAX_CABINET_NAME ]; // current cabinet name
// char szCabPath[ CB_MAX_CAB_PATH ]; // path for creating cabinet
std::string cabPath(StringUtils::ToUtf8(SystemUtils::GetFolderFromPath(target_path)));
std::string cabName(StringUtils::ToUtf8(SystemUtils::GetFileNameFromPath(target_path)));
if (cabPath.size() < CB_MAX_CAB_PATH && cabName.size() < CB_MAX_CABINET_NAME)
{
// copy path into structure
memcpy(cabParams.szCabPath, cabPath.c_str(), cabPath.size());
memcpy(cabParams.szCab, cabName.c_str(), cabName.size());
// create FCI context
compressHandler = pfnFCICreate(&compressErrors, fnFilePlaced, fnMemAlloc, fnMemFree, fnOpen, fnRead, fnWrite, fnClose, fnSeek, fnDelete, fnGetTempFile, &cabParams, this);
}
else
{
throw std::runtime_error("cab file path too long");
}
}
void compressor::destroy_context()
{
if (nullptr != compressHandler)
{
if (nullptr != pfnFCIDestroy)
{
pfnFCIDestroy(compressHandler);
}
compressHandler = nullptr;
}
}
void compressor::add_file(const std::wstring& filePath)
{
uint64_t fileSize = 0;
// check if file exist to check the size
if (SystemUtils::GetFileSize(filePath.c_str(), fileSize))
{
// ñabinet.dll supports max 2GB
if (fileSize < 0x7FFF0000)
{
std::string fileName(StringUtils::ToUtf8(SystemUtils::GetFileNameFromPath(filePath)));
std::string fullPath(StringUtils::ToUtf8(filePath));
if (!pfnFCIAddFile(compressHandler, &fullPath[ 0 ], &fileName[ 0 ], FALSE, fnGetNextCab, fnStatus, fnGetOpenInfo, tcompTYPE_LZX | tcompLZX_WINDOW_HI))
{
throw std::runtime_error("can't add file to archive");
}
}
else
{
throw std::runtime_error("file size too big for cab file");
}
}
else
{
throw std::runtime_error("can't get file size");
}
}
void compressor::file_to_cab(const std::wstring& target_path, const std::wstring& file_path)
{
// create compression context
create_context(target_path);
// add file
add_file(file_path);
// flush cabinet data
pfnFCIFlushCabinet(compressHandler, FALSE, fnGetNextCab, fnStatus);
// destroy context
destroy_context();
}
void compressor::files_to_cab(const std::wstring& target_path, const std::vector<std::wstring>& files)
{
// create compression context
create_context(target_path);
// add files
for (const std::wstring& file : files)
{
add_file(file);
}
// flush cabinet data
pfnFCIFlushCabinet(compressHandler, FALSE, fnGetNextCab, fnStatus);
// destroy context
destroy_context();
}
} // namespace cab