SLikeNet 0.1.3

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
FileList.cpp
Go to the documentation of this file.
1 /*
2 * Original work: Copyright (c) 2014, Oculus VR, Inc.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * RakNet License.txt file in the licenses directory of this source tree. An additional grant
7 * of patent rights can be found in the RakNet Patents.txt file in the same directory.
8 *
9 *
10 * Modified work: Copyright (c) 2016-2018, SLikeSoft UG (haftungsbeschränkt)
11 *
12 * This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style
13 * license found in the license.txt file in the root directory of this source tree.
14 */
15
16 #include "slikenet/FileList.h"
17
18 #if _RAKNET_SUPPORT_FileOperations==1
19
20 #include <stdio.h> // RAKNET_DEBUG_PRINTF
21 #include "slikenet/assert.h"
22 #if defined(ANDROID)
23 #include <asm/io.h>
24 #elif defined(_WIN32) || defined(__CYGWIN__)
25 #include <io.h>
26
27
28 #elif !defined ( __APPLE__ ) && !defined ( __APPLE_CC__ ) && !defined ( __PPC__ ) && !defined ( __FreeBSD__ ) && !defined ( __S3E__ )
29 #include <sys/io.h>
30 #endif
31
32
33 #ifdef _WIN32
34 // For mkdir
35 #include <direct.h>
36
37
38 #else
39 #include <sys/stat.h>
40 #endif
41
42 //#include "slikenet/DR_SHA1.h"
43 #include "slikenet/DS_Queue.h"
44 #include "slikenet/StringCompressor.h"
45 #include "slikenet/BitStream.h"
46 #include "slikenet/FileOperations.h"
47 #include "slikenet/SuperFastHash.h"
48 #include "slikenet/assert.h"
49 #include "slikenet/LinuxStrings.h"
50 #include "slikenet/linux_adapter.h"
51 #include "slikenet/osx_adapter.h"
52
53 #define MAX_FILENAME_LENGTH 512
54 static const unsigned HASH_LENGTH=4;
55
56 using namespace SLNet;
57
58 // alloca
59
60 #if defined(_WIN32)
61 #include <malloc.h>
62
63
64 #else
65 #if !defined ( __FreeBSD__ )
66 #include <alloca.h>
67 #endif
68 #include <unistd.h>
69 #include <stdlib.h>
70 #include <sys/stat.h>
71 #include "slikenet/_FindFirst.h"
72 #include <stdint.h> //defines intptr_t
73 #endif
74
75 #include "slikenet/alloca.h"
76
77 //int RAK_DLL_EXPORT FileListNodeComp( char * const &key, const FileListNode &data )
78 //{
79 // return strcmp(key, data.filename);
80 //}
81
82
83 STATIC_FACTORY_DEFINITIONS(FileListProgress,FileListProgress)
84 STATIC_FACTORY_DEFINITIONS(FLP_Printf,FLP_Printf)
85 STATIC_FACTORY_DEFINITIONS(FileList,FileList)
86
88 void FLP_Printf::OnAddFilesFromDirectoryStarted(FileList *fileList, char *dir) {
89 (void) fileList;
90 RAKNET_DEBUG_PRINTF("Adding files from directory %s\n",dir);}
91
93 void FLP_Printf::OnDirectory(FileList *fileList, char *dir, unsigned int directoriesRemaining) {
94 (void) fileList;
95 RAKNET_DEBUG_PRINTF("Adding %s. %i remaining.\n", dir, directoriesRemaining);}
96 void FLP_Printf::OnFilePushesComplete( SystemAddress systemAddress, unsigned short setID )
97 {
98 (void) setID;
99
100 char str[32];
101 systemAddress.ToString(true, (char*) str, static_cast<size_t>(32));
102 RAKNET_DEBUG_PRINTF("File pushes complete to %s\n", str);
103 }
104 void FLP_Printf::OnSendAborted( SystemAddress systemAddress )
105 {
106 char str[32];
107 systemAddress.ToString(true, (char*) str, static_cast<size_t>(32));
108 RAKNET_DEBUG_PRINTF("Send aborted to %s\n", str);
109 }
110 FileList::FileList()
111 {
112 }
113 FileList::~FileList()
114 {
115 Clear();
116 }
117 void FileList::AddFile(const char *filepath, const char *filename, FileListNodeContext context)
118 {
119 if (filepath==0 || filename==0)
120 return;
121
122 char *data;
123 //std::fstream file;
124 //file.open(filename, std::ios::in | std::ios::binary);
125
126 FILE *fp;
127 if (fopen_s(&fp, filepath, "rb") != 0)
128 return;
129 fseek(fp, 0, SEEK_END);
130 int length = ftell(fp);
131 fseek(fp, 0, SEEK_SET);
132
133 if (length > (int) ((unsigned int)-1 / 8))
134 {
135 // If this assert hits, split up your file. You could also change BitSize_t in types.h to unsigned long long but this is not recommended for performance reasons
136 RakAssert("Cannot add files over 536 MB" && 0);
137 fclose(fp);
138 return;
139 }
140
141
142 #if USE_ALLOCA==1
143 bool usedAlloca=false;
144 if (length < MAX_ALLOCA_STACK_ALLOCATION)
145 {
146 data = ( char* ) alloca( length );
147 usedAlloca=true;
148 }
149 else
150 #endif
151 {
152 data = (char*) rakMalloc_Ex( length, _FILE_AND_LINE_ );
153 RakAssert(data);
154 }
155
156 fread(data, 1, length, fp);
157 AddFile(filename, filepath, data, length, length, context);
158 fclose(fp);
159
160 #if USE_ALLOCA==1
161 if (usedAlloca==false)
162 #endif
163 rakFree_Ex(data, _FILE_AND_LINE_ );
164
165 }
166 void FileList::AddFile(const char *filename, const char *fullPathToFile, const char *data, const unsigned dataLength, const unsigned fileLength, FileListNodeContext context, bool isAReference, bool takeDataPointer)
167 {
168 if (filename==0)
169 return;
170 if (strlen(filename)>MAX_FILENAME_LENGTH)
171 {
172 // Should be enough for anyone
173 RakAssert(0);
174 return;
175 }
176 // If adding a reference, do not send data
177 RakAssert(isAReference==false || data==0);
178 // Avoid duplicate insertions unless the data is different, in which case overwrite the old data
179 unsigned i;
180 for (i=0; i<fileList.Size();i++)
181 {
182 if (strcmp(fileList[i].filename, filename)==0)
183 {
184 if (fileList[i].fileLengthBytes==fileLength && fileList[i].dataLengthBytes==dataLength &&
185 (dataLength==0 || fileList[i].data==0 ||
186 memcmp(fileList[i].data, data, dataLength)==0
187 ))
188 // Exact same file already here
189 return;
190
191 // File of the same name, but different contents, so overwrite
192 rakFree_Ex(fileList[i].data, _FILE_AND_LINE_ );
193 fileList.RemoveAtIndex(i);
194 break;
195 }
196 }
197
198 FileListNode n;
199 // size_t fileNameLen = strlen(filename);
200 if (dataLength && data)
201 {
202 if (takeDataPointer)
203 {
204 n.data=(char*) data;
205 }
206 else
207 {
208 n.data=(char*) rakMalloc_Ex( dataLength, _FILE_AND_LINE_ );
209 RakAssert(n.data);
210 memcpy(n.data, data, dataLength);
211 }
212 }
213 else
214 n.data=0;
215 n.dataLengthBytes=dataLength;
216 n.fileLengthBytes=fileLength;
217 n.isAReference=isAReference;
218 n.context=context;
219 if (n.context.dataPtr==0)
220 n.context.dataPtr=n.data;
221 if (n.context.dataLength==0)
222 n.context.dataLength=dataLength;
223 n.filename=filename;
224 n.fullPathToFile=fullPathToFile;
225
226 fileList.Insert(n, _FILE_AND_LINE_);
227 }
228 void FileList::AddFilesFromDirectory(const char *applicationDirectory, const char *subDirectory, bool writeHash, bool writeData, bool recursive, FileListNodeContext context)
229 {
230
231
232
233 DataStructures::Queue<char*> dirList;
234 char root[260];
235 char fullPath[520];
236 _finddata_t fileInfo;
237 intptr_t dir;
238 FILE *fp;
239 char *dirSoFar, *fileData;
240 dirSoFar=(char*) rakMalloc_Ex( 520, _FILE_AND_LINE_ );
241 RakAssert(dirSoFar);
242
243 if (applicationDirectory)
244 strcpy_s(root, applicationDirectory);
245 else
246 root[0]=0;
247
248 int rootLen=(int)strlen(root);
249 if (rootLen)
250 {
251 strcpy_s(dirSoFar, 520, root);
252 if (FixEndingSlash(dirSoFar, 520))
253 rootLen++;
254 }
255 else
256 dirSoFar[0]=0;
257
258 if (subDirectory)
259 {
260 strcat_s(dirSoFar, 520, subDirectory);
261 FixEndingSlash(dirSoFar, 520);
262 }
263 for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++)
264 fileListProgressCallbacks[flpcIndex]->OnAddFilesFromDirectoryStarted(this, dirSoFar);
265 // RAKNET_DEBUG_PRINTF("Adding files from directory %s\n",dirSoFar);
266 dirList.Push(dirSoFar, _FILE_AND_LINE_ );
267 while (dirList.Size())
268 {
269 dirSoFar=dirList.Pop();
270 strcpy_s(fullPath, dirSoFar);
271 // Changed from *.* to * for Linux compatibility
272 strcat_s(fullPath, "*");
273
274
275 dir=_findfirst(fullPath, &fileInfo );
276 if (dir==-1)
277 {
278 _findclose(dir);
279 rakFree_Ex(dirSoFar, _FILE_AND_LINE_ );
280 unsigned i;
281 for (i=0; i < dirList.Size(); i++)
282 rakFree_Ex(dirList[i], _FILE_AND_LINE_ );
283 return;
284 }
285
286 // RAKNET_DEBUG_PRINTF("Adding %s. %i remaining.\n", fullPath, dirList.Size());
287 for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++)
288 fileListProgressCallbacks[flpcIndex]->OnDirectory(this, fullPath, dirList.Size());
289
290 do
291 {
292 // no guarantee these entries are first...
293 if (strcmp("." , fileInfo.name) == 0 ||
294 strcmp("..", fileInfo.name) == 0)
295 {
296 continue;
297 }
298
299 if ((fileInfo.attrib & (_A_HIDDEN | _A_SUBDIR | _A_SYSTEM))==0)
300 {
301 strcpy_s(fullPath, dirSoFar);
302 strcat_s(fullPath, fileInfo.name);
303 fileData=0;
304
305 for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++)
306 fileListProgressCallbacks[flpcIndex]->OnFile(this, dirSoFar, fileInfo.name, fileInfo.size);
307
308 if (writeData && writeHash)
309 {
310 if (fopen_s(&fp, fullPath, "rb") == 0)
311 {
312 fileData= (char*) rakMalloc_Ex( fileInfo.size+HASH_LENGTH, _FILE_AND_LINE_ );
313 RakAssert(fileData);
314 fread(fileData+HASH_LENGTH, fileInfo.size, 1, fp);
315 fclose(fp);
316
317 unsigned int hash = SuperFastHash(fileData+HASH_LENGTH, fileInfo.size);
318 if (SLNet::BitStream::DoEndianSwap())
319 SLNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash));
320 memcpy(fileData, &hash, HASH_LENGTH);
321
322 // sha1.Reset();
323 // sha1.Update( ( unsigned char* ) fileData+HASH_LENGTH, fileInfo.size );
324 // sha1.Final();
325 // memcpy(fileData, sha1.GetHash(), HASH_LENGTH);
326 // File data and hash
327 AddFile((const char*)fullPath+rootLen, fullPath, fileData, fileInfo.size+HASH_LENGTH, fileInfo.size, context);
328 }
329 }
330 else if (writeHash)
331 {
332 // sha1.Reset();
333 // DR_SHA1.hashFile((char*)fullPath);
334 // sha1.Final();
335
336 unsigned int hash = SuperFastHashFile(fullPath);
337 if (SLNet::BitStream::DoEndianSwap())
338 SLNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash));
339
340 // Hash only
341 // AddFile((const char*)fullPath+rootLen, (const char*)sha1.GetHash(), HASH_LENGTH, fileInfo.size, context);
342 AddFile((const char*)fullPath+rootLen, fullPath, (const char*)&hash, HASH_LENGTH, fileInfo.size, context);
343 }
344 else if (writeData)
345 {
346 fileData= (char*) rakMalloc_Ex( fileInfo.size, _FILE_AND_LINE_ );
347 RakAssert(fileData);
348 fopen_s(&fp, fullPath, "rb");
349 fread(fileData, fileInfo.size, 1, fp);
350 fclose(fp);
351
352 // File data only
353 AddFile(fullPath+rootLen, fullPath, fileData, fileInfo.size, fileInfo.size, context);
354 }
355 else
356 {
357 // Just the filename
358 AddFile(fullPath+rootLen, fullPath, 0, 0, fileInfo.size, context);
359 }
360
361 if (fileData)
362 rakFree_Ex(fileData, _FILE_AND_LINE_ );
363 }
364 else if ((fileInfo.attrib & _A_SUBDIR) && (fileInfo.attrib & (_A_HIDDEN | _A_SYSTEM))==0 && recursive)
365 {
366 char *newDir=(char*) rakMalloc_Ex( 520, _FILE_AND_LINE_ );
367 RakAssert(newDir);
368 strcpy_s(newDir, 520, dirSoFar);
369 strcat_s(newDir, 520, fileInfo.name);
370 strcat_s(newDir, 520, "/");
371 dirList.Push(newDir, _FILE_AND_LINE_ );
372 }
373
374 } while (_findnext(dir, &fileInfo ) != -1);
375
376 _findclose(dir);
377 rakFree_Ex(dirSoFar, _FILE_AND_LINE_ );
378 }
379
380 }
381 void FileList::Clear(void)
382 {
383 unsigned i;
384 for (i=0; i<fileList.Size(); i++)
385 {
386 rakFree_Ex(fileList[i].data, _FILE_AND_LINE_ );
387 }
388 fileList.Clear(false, _FILE_AND_LINE_);
389 }
390 void FileList::Serialize(SLNet::BitStream *outBitStream)
391 {
392 outBitStream->WriteCompressed(fileList.Size());
393 unsigned i;
394 for (i=0; i < fileList.Size(); i++)
395 {
396 outBitStream->WriteCompressed(fileList[i].context.op);
397 outBitStream->WriteCompressed(fileList[i].context.flnc_extraData1);
398 outBitStream->WriteCompressed(fileList[i].context.flnc_extraData2);
399 StringCompressor::Instance()->EncodeString(fileList[i].filename.C_String(), MAX_FILENAME_LENGTH, outBitStream);
400
401 bool writeFileData = (fileList[i].dataLengthBytes>0)==true;
402 outBitStream->Write(writeFileData);
403 if (writeFileData)
404 {
405 outBitStream->WriteCompressed(fileList[i].dataLengthBytes);
406 outBitStream->Write(fileList[i].data, fileList[i].dataLengthBytes);
407 }
408
409 outBitStream->Write((bool)(fileList[i].fileLengthBytes==fileList[i].dataLengthBytes));
410 if (fileList[i].fileLengthBytes!=fileList[i].dataLengthBytes)
411 outBitStream->WriteCompressed(fileList[i].fileLengthBytes);
412 }
413 }
414 bool FileList::Deserialize(SLNet::BitStream *inBitStream)
415 {
416 bool b, dataLenNonZero=false, fileLenMatchesDataLen=false;
417 char filename[512];
418 uint32_t fileListSize;
419 FileListNode n;
420 b=inBitStream->ReadCompressed(fileListSize);
421 #ifdef _DEBUG
422 RakAssert(b);
423 RakAssert(fileListSize < 10000);
424 #endif
425 if (b==false || fileListSize > 10000)
426 return false; // Sanity check
427 Clear();
428 unsigned i;
429 for (i=0; i < fileListSize; i++)
430 {
431 inBitStream->ReadCompressed(n.context.op);
432 inBitStream->ReadCompressed(n.context.flnc_extraData1);
433 inBitStream->ReadCompressed(n.context.flnc_extraData2);
434 StringCompressor::Instance()->DecodeString((char*)filename, MAX_FILENAME_LENGTH, inBitStream);
435 inBitStream->Read(dataLenNonZero);
436 if (dataLenNonZero)
437 {
438 inBitStream->ReadCompressed(n.dataLengthBytes);
439 // sanity check
440 if (n.dataLengthBytes>2000000000)
441 {
442 #ifdef _DEBUG
443 RakAssert(n.dataLengthBytes<=2000000000);
444 #endif
445 return false;
446 }
447 n.data=(char*) rakMalloc_Ex( (size_t) n.dataLengthBytes, _FILE_AND_LINE_ );
448 RakAssert(n.data);
449 inBitStream->Read(n.data, n.dataLengthBytes);
450 }
451 else
452 {
453 n.dataLengthBytes=0;
454 n.data=0;
455 }
456
457 b=inBitStream->Read(fileLenMatchesDataLen);
458 if (fileLenMatchesDataLen)
459 n.fileLengthBytes=(unsigned) n.dataLengthBytes;
460 else
461 b=inBitStream->ReadCompressed(n.fileLengthBytes);
462 #ifdef _DEBUG
463 RakAssert(b);
464 #endif
465 if (b==0)
466 {
467 Clear();
468 return false;
469 }
470 n.filename=filename;
471 n.fullPathToFile=filename;
472 fileList.Insert(n, _FILE_AND_LINE_);
473 }
474
475 return true;
476 }
477 void FileList::GetDeltaToCurrent(FileList *input, FileList *output, const char *dirSubset, const char *remoteSubdir)
478 {
479 // For all files in this list that do not match the input list, write them to the output list.
480 // dirSubset allows checking only a portion of the files in this list.
481 unsigned thisIndex, inputIndex;
482 unsigned dirSubsetLen, localPathLen, remoteSubdirLen;
483 bool match;
484 if (dirSubset)
485 dirSubsetLen = (unsigned int) strlen(dirSubset);
486 else
487 dirSubsetLen = 0;
488 if (remoteSubdir && remoteSubdir[0])
489 {
490 remoteSubdirLen=(unsigned int) strlen(remoteSubdir);
491 if (IsSlash(remoteSubdir[remoteSubdirLen-1]))
492 remoteSubdirLen--;
493 }
494 else
495 remoteSubdirLen=0;
496
497 for (thisIndex=0; thisIndex < fileList.Size(); thisIndex++)
498 {
499 localPathLen = (unsigned int) fileList[thisIndex].filename.GetLength();
500 while (localPathLen>0)
501 {
502 if (IsSlash(fileList[thisIndex].filename[localPathLen-1]))
503 {
504 localPathLen--;
505 break;
506 }
507 localPathLen--;
508 }
509
510 // fileList[thisIndex].filename has to match dirSubset and be shorter or equal to it in length.
511 if (dirSubsetLen>0 &&
512 (localPathLen<dirSubsetLen ||
513 _strnicmp(fileList[thisIndex].filename.C_String(), dirSubset, dirSubsetLen)!=0 ||
514 (localPathLen>dirSubsetLen && IsSlash(fileList[thisIndex].filename[dirSubsetLen])==false)))
515 continue;
516
517 match=false;
518 for (inputIndex=0; inputIndex < input->fileList.Size(); inputIndex++)
519 {
520 // If the filenames, hashes, and lengths match then skip this element in fileList. Otherwise write it to output
521 if (_stricmp(input->fileList[inputIndex].filename.C_String()+remoteSubdirLen,fileList[thisIndex].filename.C_String()+dirSubsetLen)==0)
522 {
523 match=true;
524 if (input->fileList[inputIndex].fileLengthBytes==fileList[thisIndex].fileLengthBytes &&
525 input->fileList[inputIndex].dataLengthBytes==fileList[thisIndex].dataLengthBytes &&
526 memcmp(input->fileList[inputIndex].data,fileList[thisIndex].data,(size_t) fileList[thisIndex].dataLengthBytes)==0)
527 {
528 // File exists on both machines and is the same.
529 break;
530 }
531 else
532 {
533 // File exists on both machines and is not the same.
534 output->AddFile(fileList[thisIndex].filename, fileList[thisIndex].fullPathToFile, 0,0, fileList[thisIndex].fileLengthBytes, FileListNodeContext(0,0,0,0), false);
535 break;
536 }
537 }
538 }
539 if (match==false)
540 {
541 // Other system does not have the file at all
542 output->AddFile(fileList[thisIndex].filename, fileList[thisIndex].fullPathToFile, 0,0, fileList[thisIndex].fileLengthBytes, FileListNodeContext(0,0,0,0), false);
543 }
544 }
545 }
546 void FileList::ListMissingOrChangedFiles(const char *applicationDirectory, FileList *missingOrChangedFiles, bool alwaysWriteHash, bool neverWriteHash)
547 {
548 unsigned fileLength;
549 // CSHA1 sha1;
550 FILE *fp;
551 char fullPath[512];
552 unsigned i;
553 // char *fileData;
554
555 for (i=0; i < fileList.Size(); i++)
556 {
557 strcpy_s(fullPath, applicationDirectory);
558 FixEndingSlash(fullPath, 512);
559 strcat_s(fullPath,fileList[i].filename);
560 if (fopen_s(&fp, fullPath, "rb") != 0)
561 {
562 missingOrChangedFiles->AddFile(fileList[i].filename, fileList[i].fullPathToFile, 0, 0, 0, FileListNodeContext(0,0,0,0), false);
563 }
564 else
565 {
566 fseek(fp, 0, SEEK_END);
567 fileLength = ftell(fp);
568 fseek(fp, 0, SEEK_SET);
569
570 if (fileLength != fileList[i].fileLengthBytes && alwaysWriteHash==false)
571 {
572 missingOrChangedFiles->AddFile(fileList[i].filename, fileList[i].fullPathToFile, 0, 0, fileLength, FileListNodeContext(0,0,0,0), false);
573 }
574 else
575 {
576
577 // fileData= (char*) rakMalloc_Ex( fileLength, _FILE_AND_LINE_ );
578 // fread(fileData, fileLength, 1, fp);
579
580 // sha1.Reset();
581 // sha1.Update( ( unsigned char* ) fileData, fileLength );
582 // sha1.Final();
583
584 // rakFree_Ex(fileData, _FILE_AND_LINE_ );
585
586 unsigned int hash = SuperFastHashFilePtr(fp);
587 if (SLNet::BitStream::DoEndianSwap())
588 SLNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash));
589
590 //if (fileLength != fileList[i].fileLength || memcmp( sha1.GetHash(), fileList[i].data, HASH_LENGTH)!=0)
591 if (fileLength != fileList[i].fileLengthBytes || memcmp( &hash, fileList[i].data, HASH_LENGTH)!=0)
592 {
593 if (neverWriteHash==false)
594 // missingOrChangedFiles->AddFile((const char*)fileList[i].filename, (const char*)sha1.GetHash(), HASH_LENGTH, fileLength, 0);
595 missingOrChangedFiles->AddFile((const char*)fileList[i].filename, (const char*)fileList[i].fullPathToFile, (const char *) &hash, HASH_LENGTH, fileLength, FileListNodeContext(0,0,0,0), false);
596 else
597 missingOrChangedFiles->AddFile((const char*)fileList[i].filename, (const char*)fileList[i].fullPathToFile, 0, 0, fileLength, FileListNodeContext(0,0,0,0), false);
598 }
599 }
600 fclose(fp);
601 }
602 }
603 }
604 void FileList::PopulateDataFromDisk(const char *applicationDirectory, bool writeFileData, bool writeFileHash, bool removeUnknownFiles)
605 {
606 FILE *fp;
607 char fullPath[512];
608 unsigned i;
609 // CSHA1 sha1;
610
611 i=0;
612 while (i < fileList.Size())
613 {
614 rakFree_Ex(fileList[i].data, _FILE_AND_LINE_ );
615 strcpy_s(fullPath, applicationDirectory);
616 FixEndingSlash(fullPath, 512);
617 strcat_s(fullPath,fileList[i].filename.C_String());
618 if (fopen_s(&fp, fullPath, "rb") == 0)
619 {
620 if (writeFileHash || writeFileData)
621 {
622 fseek(fp, 0, SEEK_END);
623 fileList[i].fileLengthBytes = ftell(fp);
624 fseek(fp, 0, SEEK_SET);
625 if (writeFileHash)
626 {
627 if (writeFileData)
628 {
629 // Hash + data so offset the data by HASH_LENGTH
630 fileList[i].data=(char*) rakMalloc_Ex( fileList[i].fileLengthBytes+HASH_LENGTH, _FILE_AND_LINE_ );
631 RakAssert(fileList[i].data);
632 fread(fileList[i].data+HASH_LENGTH, fileList[i].fileLengthBytes, 1, fp);
633 // sha1.Reset();
634 // sha1.Update((unsigned char*)fileList[i].data+HASH_LENGTH, fileList[i].fileLength);
635 // sha1.Final();
636 unsigned int hash = SuperFastHash(fileList[i].data+HASH_LENGTH, fileList[i].fileLengthBytes);
637 if (SLNet::BitStream::DoEndianSwap())
638 SLNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash));
639 // memcpy(fileList[i].data, sha1.GetHash(), HASH_LENGTH);
640 memcpy(fileList[i].data, &hash, HASH_LENGTH);
641 }
642 else
643 {
644 // Hash only
645 fileList[i].dataLengthBytes=HASH_LENGTH;
646 if (fileList[i].fileLengthBytes < HASH_LENGTH)
647 fileList[i].data=(char*) rakMalloc_Ex( HASH_LENGTH, _FILE_AND_LINE_ );
648 else
649 fileList[i].data=(char*) rakMalloc_Ex( fileList[i].fileLengthBytes, _FILE_AND_LINE_ );
650 RakAssert(fileList[i].data);
651 fread(fileList[i].data, fileList[i].fileLengthBytes, 1, fp);
652 // sha1.Reset();
653 // sha1.Update((unsigned char*)fileList[i].data, fileList[i].fileLength);
654 // sha1.Final();
655 unsigned int hash = SuperFastHash(fileList[i].data, fileList[i].fileLengthBytes);
656 if (SLNet::BitStream::DoEndianSwap())
657 SLNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash));
658 // memcpy(fileList[i].data, sha1.GetHash(), HASH_LENGTH);
659 memcpy(fileList[i].data, &hash, HASH_LENGTH);
660 }
661 }
662 else
663 {
664 // Data only
665 fileList[i].dataLengthBytes=fileList[i].fileLengthBytes;
666 fileList[i].data=(char*) rakMalloc_Ex( fileList[i].fileLengthBytes, _FILE_AND_LINE_ );
667 RakAssert(fileList[i].data);
668 fread(fileList[i].data, fileList[i].fileLengthBytes, 1, fp);
669 }
670
671 fclose(fp);
672 i++;
673 }
674 else
675 {
676 fileList[i].data=0;
677 fileList[i].dataLengthBytes=0;
678 }
679 }
680 else
681 {
682 if (removeUnknownFiles)
683 {
684 fileList.RemoveAtIndex(i);
685 }
686 else
687 i++;
688 }
689 }
690 }
691 void FileList::FlagFilesAsReferences(void)
692 {
693 for (unsigned int i=0; i < fileList.Size(); i++)
694 {
695 fileList[i].isAReference=true;
696 fileList[i].dataLengthBytes=fileList[i].fileLengthBytes;
697 }
698 }
699 void FileList::WriteDataToDisk(const char *applicationDirectory)
700 {
701 char fullPath[512];
702 unsigned i,j;
703
704 for (i=0; i < fileList.Size(); i++)
705 {
706 strcpy_s(fullPath, applicationDirectory);
707 FixEndingSlash(fullPath, 512);
708 strcat_s(fullPath,fileList[i].filename.C_String());
709
710 // Security - Don't allow .. in the filename anywhere so you can't write outside of the root directory
711 for (j=1; j < fileList[i].filename.GetLength(); j++)
712 {
713 if (fileList[i].filename[j]=='.' && fileList[i].filename[j-1]=='.')
714 {
715 #ifdef _DEBUG
716 RakAssert(0);
717 #endif
718 // Just cancel the write entirely
719 return;
720 }
721 }
722
723 WriteFileWithDirectories(fullPath, fileList[i].data, (unsigned int) fileList[i].dataLengthBytes);
724 }
725 }
726
727 void FileList::DeleteFiles(const char *applicationDirectory)
728 {
729
730
731
732 char fullPath[512];
733 unsigned i,j;
734
735 for (i=0; i < fileList.Size(); i++)
736 {
737 // The filename should not have .. in the path - if it does ignore it
738 for (j=1; j < fileList[i].filename.GetLength(); j++)
739 {
740 if (fileList[i].filename[j]=='.' && fileList[i].filename[j-1]=='.')
741 {
742 #ifdef _DEBUG
743 RakAssert(0);
744 #endif
745 // Just cancel the deletion entirely
746 return;
747 }
748 }
749
750 strcpy_s(fullPath, applicationDirectory);
751 FixEndingSlash(fullPath, 512);
752 strcat_s(fullPath, fileList[i].filename.C_String());
753
754 // Do not rename to _unlink as linux uses unlink
755 #if defined(_WIN32)
756 int result = _unlink(fullPath);
757 #else
758 int result = unlink(fullPath);
759 #endif
760 if (result!=0)
761 {
762 RAKNET_DEBUG_PRINTF("FileList::DeleteFiles: unlink (%s) failed.\n", fullPath);
763 }
764 }
765
766 }
767
768 void FileList::AddCallback(FileListProgress *cb)
769 {
770 if (cb==0)
771 return;
772
773 if ((unsigned int) fileListProgressCallbacks.GetIndexOf(cb)==(unsigned int)-1)
774 fileListProgressCallbacks.Push(cb, _FILE_AND_LINE_);
775 }
776 void FileList::RemoveCallback(FileListProgress *cb)
777 {
778 unsigned int idx = fileListProgressCallbacks.GetIndexOf(cb);
779 if (idx!=(unsigned int) -1)
780 fileListProgressCallbacks.RemoveAtIndex(idx);
781 }
782 void FileList::ClearCallbacks(void)
783 {
784 fileListProgressCallbacks.Clear(true, _FILE_AND_LINE_);
785 }
786 void FileList::GetCallbacks(DataStructures::List<FileListProgress*> &callbacks)
787 {
788 callbacks = fileListProgressCallbacks;
789 }
790
791 bool FileList::FixEndingSlash(char *str)
792 {
793 #ifdef _WIN32
794 if (str[strlen(str) - 1] != '/' && str[strlen(str) - 1] != '\\')
795 {
796 #pragma warning(push)
797 #pragma warning(disable:4996)
798 strcat(str, "\\"); // Only \ works with system commands, used by AutopatcherClient
799 #pragma warning(pop)
800 return true;
801 }
802 #else
803 if (str[strlen(str) - 1] != '\\' && str[strlen(str) - 1] != '/')
804 {
805 strcat(str, "/"); // Only / works with Linux
806 return true;
807 }
808 #endif
809
810 return false;
811 }
812
813 bool FileList::FixEndingSlash(char *str, size_t strLength)
814 {
815 #ifdef _WIN32
816 if (str[strlen(str)-1]!='/' && str[strlen(str)-1]!='\\')
817 {
818 strcat_s(str, strLength, "\\"); // Only \ works with system commands, used by AutopatcherClient
819 return true;
820 }
821 #else
822 if (str[strlen(str)-1]!='\\' && str[strlen(str)-1]!='/')
823 {
824 strcat_s(str, strLength, "/"); // Only / works with Linux
825 return true;
826 }
827 #endif
828
829 return false;
830 }
831
832 #endif // _RAKNET_SUPPORT_FileOperations