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
|
/*****************************************************************************/
/* ListFile.cpp Copyright (c) Ladislav Zezula 2004 */
/*---------------------------------------------------------------------------*/
/* Description: */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 12.06.04 1.00 Lad The first version of ListFile.cpp */
/*****************************************************************************/
#define __CASCLIB_SELF__
#include "../CascLib.h"
#include "../CascCommon.h"
//-----------------------------------------------------------------------------
// Listfile cache structure
#define LISTFILE_FLAG_USES_FILEDATAID 0x0001 // A new CSV format, containing FileDataId; FullFileName
typedef struct _LISTFILE_CACHE
{
char * pBegin; // The begin of the listfile cache
char * pPos; // Current position in the cache
char * pEnd; // The last character in the file cache
DWORD Flags;
// Followed by the cache (variable length)
} LISTFILE_CACHE, *PLISTFILE_CACHE;
//-----------------------------------------------------------------------------
// Creating the listfile cache for the given amount of data
static PLISTFILE_CACHE ListFile_CreateCache(DWORD dwFileSize)
{
PLISTFILE_CACHE pCache;
// Allocate cache for one file block
pCache = (PLISTFILE_CACHE)CASC_ALLOC<BYTE>(sizeof(LISTFILE_CACHE) + dwFileSize);
if(pCache != NULL)
{
// Set the initial pointers
pCache->pBegin =
pCache->pPos = (char *)(pCache + 1);
pCache->pEnd = pCache->pBegin + dwFileSize;
pCache->Flags = 0;
}
// Return the cache
return pCache;
}
static char * ListFile_SkipSpaces(PLISTFILE_CACHE pCache)
{
// Skip newlines, spaces, tabs and another non-printable stuff
while(pCache->pPos < pCache->pEnd && pCache->pPos[0] <= 0x20)
pCache->pPos++;
// Remember the begin of the line
return pCache->pPos;
}
static void ListFile_CheckFormat(PLISTFILE_CACHE pCache)
{
const size_t nSizeLimit = 0x20;
// Only if the listfile is greater than 2 MB
if((pCache->pEnd - pCache->pBegin) > nSizeLimit)
{
char * szPtr = pCache->pBegin;
size_t nDigitCount = 0;
// Calculate the amount of digits
while(nDigitCount <= nSizeLimit && '0' <= szPtr[nDigitCount] && szPtr[nDigitCount] <= '9')
nDigitCount++;
// There must be a semicolon after
if(nDigitCount <= 10 && szPtr[nDigitCount] == ';')
{
pCache->Flags |= LISTFILE_FLAG_USES_FILEDATAID;
}
}
}
static int ListFile_GetFileDataId(PLISTFILE_CACHE pCache, PDWORD PtrFileDataId)
{
char * szLineBegin = ListFile_SkipSpaces(pCache);
char * szLineEnd;
DWORD dwNewInt32 = 0;
DWORD dwInt32 = 0;
// Set the limit for loading the number
szLineEnd = CASCLIB_MIN((szLineBegin + 20), pCache->pEnd);
// Extract decimal digits from the string
while(szLineBegin < szLineEnd && '0' <= szLineBegin[0] && szLineBegin[0] <= '9')
{
// Check integer overflow
dwNewInt32 = (dwInt32 * 10) + (szLineBegin[0] - '0');
if(dwNewInt32 < dwInt32)
return ERROR_BAD_FORMAT;
dwInt32 = dwNewInt32;
szLineBegin++;
}
// There must still be some space
if(szLineBegin < szLineEnd)
{
// There must be a semicolon after the decimal integer
// The decimal integer must be smaller than 10 MB (files)
if(szLineBegin[0] != ';' || dwInt32 >= 0xA00000)
return ERROR_BAD_FORMAT;
pCache->pPos = szLineBegin + 1;
PtrFileDataId[0] = dwInt32;
return ERROR_SUCCESS;
}
return ERROR_NO_MORE_FILES;
}
//-----------------------------------------------------------------------------
// Functions for parsing an external listfile
void * ListFile_OpenExternal(LPCTSTR szListFile)
{
PLISTFILE_CACHE pCache = NULL;
TFileStream * pStream;
ULONGLONG FileSize = 0;
// Open the external listfile
pStream = FileStream_OpenFile(szListFile, STREAM_FLAG_READ_ONLY);
if(pStream != NULL)
{
// Retrieve the size of the external listfile
FileStream_GetSize(pStream, &FileSize);
if(0 < FileSize && FileSize <= 0x30000000)
{
// Create the in-memory cache for the entire listfile
// The listfile does not have any data loaded yet
pCache = ListFile_CreateCache((DWORD)FileSize);
if(pCache != NULL)
{
if(FileStream_Read(pStream, NULL, pCache->pBegin, (DWORD)FileSize))
{
ListFile_CheckFormat(pCache);
}
else
{
CASC_FREE(pCache);
}
}
}
// Close the file stream
FileStream_Close(pStream);
}
return pCache;
}
void * ListFile_FromBuffer(LPBYTE pbBuffer, DWORD cbBuffer)
{
PLISTFILE_CACHE pCache = NULL;
// Create the in-memory cache for the entire listfile
// The listfile does not have any data loaded yet
pCache = ListFile_CreateCache(cbBuffer);
if(pCache != NULL)
memcpy(pCache->pBegin, pbBuffer, cbBuffer);
return pCache;
}
// Performs the MD5-based check on the listfile
bool ListFile_VerifyMD5(void * pvListFile, LPBYTE pbHashMD5)
{
PLISTFILE_CACHE pCache = (PLISTFILE_CACHE)pvListFile;
// Must be at the beginning
assert(pCache->pPos == pCache->pBegin);
// Verify the MD5 hash for the entire block
return CascVerifyDataBlockHash(pCache->pBegin, (DWORD)(pCache->pEnd - pCache->pBegin), pbHashMD5);
}
size_t ListFile_GetNextLine(void * pvListFile, const char ** pszLineBegin, const char ** pszLineEnd)
{
PLISTFILE_CACHE pCache = (PLISTFILE_CACHE)pvListFile;
char * szExtraString = NULL;
char * szLineBegin;
char * szLineEnd;
// Skip newlines, spaces, tabs and another non-printable stuff
// Remember the begin of the line
szLineBegin = ListFile_SkipSpaces(pCache);
// Copy the remaining characters
while(pCache->pPos < pCache->pEnd)
{
// If we have found a newline, stop loading
// Note: the 0x85 char came from Overwatch build 24919
if(pCache->pPos[0] == '\x0A' || pCache->pPos[0] == '\x0D' || pCache->pPos[0] == '\x85')
break;
// Blizzard listfiles can also contain information about patch:
// Pass1\Files\MacOS\unconditional\user\Background Downloader.app\Contents\Info.plist~Patch(Data#frFR#base-frFR,1326)
if(pCache->pPos[0] == '~')
szExtraString = pCache->pPos;
// Move the position by one character forward
pCache->pPos++;
}
// Remember the end of the line
szLineEnd = (szExtraString != NULL && szExtraString[0] == '~' && szExtraString[1] == 'P') ? szExtraString : pCache->pPos;
// Give the caller the positions of the begin and end of the line
pszLineBegin[0] = szLineBegin;
pszLineEnd[0] = szLineEnd;
return (size_t)(szLineEnd - szLineBegin);
}
size_t ListFile_GetNextLine(void * pvListFile, char * szBuffer, size_t nMaxChars)
{
const char * szLineBegin = NULL;
const char * szLineEnd = NULL;
size_t nLength;
DWORD dwErrCode = ERROR_SUCCESS;
// Retrieve the next line
nLength = ListFile_GetNextLine(pvListFile, &szLineBegin, &szLineEnd);
// Check the length
if(nLength < nMaxChars)
{
// Copy the line to the user buffer
memcpy(szBuffer, szLineBegin, nLength);
szBuffer[nLength] = 0;
}
else
{
dwErrCode = ERROR_INSUFFICIENT_BUFFER;
nLength = 0;
}
// If we didn't read anything, set the error code
if(nLength == 0)
SetCascError(dwErrCode);
return nLength;
}
size_t ListFile_GetNext(void * pvListFile, char * szBuffer, size_t nMaxChars, PDWORD PtrFileDataId)
{
PLISTFILE_CACHE pCache = (PLISTFILE_CACHE)pvListFile;
const char * szTemp;
size_t nLength = 0;
DWORD dwErrCode = ERROR_SUCCESS;
// Check for parameters
for(;;)
{
DWORD FileDataId = CASC_INVALID_ID;
// If this is a CSV-format listfile, we need to extract the FileDataId
// Lines that contain bogus data, invalid numbers or too big values will be skipped
if(pCache->Flags & LISTFILE_FLAG_USES_FILEDATAID)
{
// Retrieve the data ID from the current position
dwErrCode = ListFile_GetFileDataId(pCache, &FileDataId);
if(dwErrCode == ERROR_NO_MORE_FILES)
break;
// If there was an error, skip the current line
if(dwErrCode != ERROR_SUCCESS || FileDataId == CASC_INVALID_ID)
{
ListFile_GetNextLine(pvListFile, &szTemp, &szTemp);
continue;
}
}
// Read the (next) line
nLength = ListFile_GetNextLine(pvListFile, szBuffer, nMaxChars);
if(nLength == 0)
{
dwErrCode = GetCascError();
break;
}
// Give the file data id and return true
PtrFileDataId[0] = FileDataId;
return nLength;
}
if(dwErrCode != ERROR_SUCCESS)
SetCascError(dwErrCode);
return nLength;
}
LPBYTE ListFile_GetData(void * pvListFile, PDWORD PtrDataSize)
{
PLISTFILE_CACHE pCache = (PLISTFILE_CACHE)pvListFile;
LPBYTE pbData = NULL;
DWORD cbData = 0;
// Get data from the list file cache
if(pvListFile != NULL)
{
pbData = (LPBYTE)pCache->pBegin;
cbData = (DWORD)(pCache->pEnd - pCache->pBegin);
}
// Give the data to the caller
if(PtrDataSize != NULL)
PtrDataSize[0] = cbData;
return pbData;
}
|