1 /*
2 * HLLib
3 * Copyright (C) 2006-2012 Ryan Gregg
4
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later
9 * version.
10 */
11
12 #include "DirectoryFile.h"
13 #include "DirectoryFolder.h"
14 #include "HLLib.h"
15 #include "Utility.h"
16
17 #include <algorithm>
18
19 using namespace HLLib;
20
CDirectoryFolder(CPackage * pPackage)21 CDirectoryFolder::CDirectoryFolder(CPackage *pPackage) : CDirectoryItem("root", HL_ID_INVALID, 0, pPackage, 0), pDirectoryItemVector(new CDirectoryItemVector())
22 {
23
24 }
25
CDirectoryFolder(const hlChar * lpName,hlUInt uiID,hlVoid * pData,CPackage * pPackage,CDirectoryFolder * pParent)26 CDirectoryFolder::CDirectoryFolder(const hlChar *lpName, hlUInt uiID, hlVoid *pData, CPackage *pPackage, CDirectoryFolder *pParent) : CDirectoryItem(lpName, uiID, pData, pPackage, pParent), pDirectoryItemVector(new CDirectoryItemVector())
27 {
28
29 }
30
~CDirectoryFolder()31 CDirectoryFolder::~CDirectoryFolder()
32 {
33 // Delete children.
34 for(hlUInt i = 0; i < this->pDirectoryItemVector->size(); i++)
35 {
36 delete (*this->pDirectoryItemVector)[i];
37 }
38
39 delete this->pDirectoryItemVector;
40 }
41
GetType() const42 HLDirectoryItemType CDirectoryFolder::GetType() const
43 {
44 return HL_ITEM_FOLDER;
45 }
46
AddFolder(const hlChar * lpName,hlUInt uiID,hlVoid * lpData)47 CDirectoryFolder *CDirectoryFolder::AddFolder(const hlChar *lpName, hlUInt uiID, hlVoid *lpData)
48 {
49 CDirectoryFolder *pFolder = new CDirectoryFolder(lpName, uiID, lpData, this->GetPackage(), this);
50
51 this->pDirectoryItemVector->push_back(pFolder);
52
53 return pFolder;
54 }
55
AddFile(const hlChar * lpName,hlUInt uiID,hlVoid * lpData)56 CDirectoryFile *CDirectoryFolder::AddFile(const hlChar *lpName, hlUInt uiID, hlVoid *lpData)
57 {
58 CDirectoryFile *pFile = new CDirectoryFile(lpName, uiID, lpData, this->GetPackage(), this);
59
60 this->pDirectoryItemVector->push_back(pFile);
61
62 return pFile;
63 }
64
65 //
66 // GetCount()
67 // Returns the number of directory items in this folder.
68 //
GetCount() const69 hlUInt CDirectoryFolder::GetCount() const
70 {
71 return (hlUInt)this->pDirectoryItemVector->size();
72 }
73
74 //
75 // GetItem()
76 // Returns the directory item at index uiIndex.
77 //
GetItem(hlUInt uiIndex)78 CDirectoryItem *CDirectoryFolder::GetItem(hlUInt uiIndex)
79 {
80 if(uiIndex >= (hlUInt)this->pDirectoryItemVector->size())
81 {
82 return 0;
83 }
84
85 return (*this->pDirectoryItemVector)[uiIndex];
86 }
87
GetItem(hlUInt uiIndex) const88 const CDirectoryItem *CDirectoryFolder::GetItem(hlUInt uiIndex) const
89 {
90 if(uiIndex >= (hlUInt)this->pDirectoryItemVector->size())
91 {
92 return 0;
93 }
94
95 return (*this->pDirectoryItemVector)[uiIndex];
96 }
97
98 //
99 // GetItem()
100 // Returns the directory item lpName. If the directory item
101 // does not exist null is returned.
102 //
GetItem(const hlChar * lpName,HLFindType eFind)103 CDirectoryItem *CDirectoryFolder::GetItem(const hlChar *lpName, HLFindType eFind)
104 {
105 return const_cast<CDirectoryItem *>(const_cast<const CDirectoryFolder*>(this)->GetItem(lpName, eFind));
106 }
107
GetItem(const hlChar * lpName,HLFindType eFind) const108 const CDirectoryItem *CDirectoryFolder::GetItem(const hlChar *lpName, HLFindType eFind) const
109 {
110 for(hlUInt i = 0; i < this->pDirectoryItemVector->size(); i++)
111 {
112 CDirectoryItem *pItem = (*this->pDirectoryItemVector)[i];
113 if((pItem->GetType() == HL_ITEM_FILE && (eFind & HL_FIND_FILES)) || (pItem->GetType() == HL_ITEM_FOLDER && (eFind & HL_FIND_FOLDERS)))
114 {
115 if(this->Compare(lpName, pItem->GetName(), eFind) == 0)
116 {
117 return pItem;
118 }
119 }
120 }
121
122 return 0;
123 }
124
125 //
126 // GetRelativeItem()
127 // Returns the directory item lpPath. If the directory item
128 // does not exist null is returned. Transverses sub-folders too.
129 //
GetRelativeItem(const hlChar * lpPath,HLFindType eFind)130 CDirectoryItem *CDirectoryFolder::GetRelativeItem(const hlChar *lpPath, HLFindType eFind)
131 {
132 return const_cast<CDirectoryItem *>(const_cast<const CDirectoryFolder*>(this)->GetRelativeItem(lpPath, eFind));
133 }
134
GetRelativeItem(const hlChar * lpPath,HLFindType eFind) const135 const CDirectoryItem *CDirectoryFolder::GetRelativeItem(const hlChar *lpPath, HLFindType eFind) const
136 {
137 const CDirectoryFolder *pFolder = this;
138
139 hlChar *lpTemp = new hlChar[strlen(lpPath) + 1];
140 strcpy(lpTemp, lpPath);
141
142 hlChar *lpToken = strtok(lpTemp, "\\/");
143 if(lpToken != 0 && this->Compare(pFolder->GetName(), lpToken, eFind) == 0)
144 {
145 lpToken = strtok(0, "\\/");
146 }
147
148 while(lpToken != 0)
149 {
150 if(*lpToken == '\0' || strcmp(lpToken, ".") == 0)
151 {
152 lpToken = strtok(0, "\\/");
153 }
154 else if(strcmp(lpToken, "..") == 0)
155 {
156 if(pFolder->GetParent())
157 {
158 pFolder = pFolder->GetParent();
159 }
160 else
161 {
162 delete []lpTemp;
163 return 0;
164 }
165
166 lpToken = strtok(0, "\\/");
167 }
168 else
169 {
170 hlChar *lpNext = strtok(0, "\\/");
171
172 const CDirectoryItem *pItem = 0;
173
174 for(hlUInt i = 0; i < pFolder->GetCount(); i++)
175 {
176 const CDirectoryItem *pTemp = pFolder->GetItem(i);
177 if(lpNext == 0 && pTemp->GetType() == HL_ITEM_FILE && (eFind & HL_FIND_FILES) && this->Compare(lpToken, pTemp->GetName(), eFind) == 0)
178 {
179 pItem = pTemp;
180 break;
181 }
182 else if(pTemp->GetType() == HL_ITEM_FOLDER && this->Compare(lpToken, pTemp->GetName(), eFind) == 0)
183 {
184 pItem = pTemp;
185 break;
186 }
187 }
188
189 if(pItem == 0)
190 {
191 delete []lpTemp;
192 return 0;
193 }
194
195 if(pItem->GetType() == HL_ITEM_FOLDER)
196 {
197 pFolder = static_cast<const CDirectoryFolder *>(pItem);
198 }
199 else
200 {
201 delete []lpTemp;
202 return pItem;
203 }
204
205 lpToken = lpNext;
206 }
207 }
208
209 delete []lpTemp;
210 if(eFind & HL_FIND_FOLDERS)
211 {
212 return pFolder;
213 }
214 else
215 {
216 return 0;
217 }
218 }
219
220 class CCompareDirectoryItems
221 {
222 private:
223 HLSortField eField;
224 HLSortOrder eOrder;
225
226 public:
CCompareDirectoryItems(HLSortField eField,HLSortOrder eOrder)227 CCompareDirectoryItems(HLSortField eField, HLSortOrder eOrder) : eField(eField), eOrder(eOrder)
228 {
229
230 }
231
operator ()(const CDirectoryItem * const & pItem0,const CDirectoryItem * const & pItem1)232 bool operator()(const CDirectoryItem * const &pItem0, const CDirectoryItem * const &pItem1)
233 {
234 HLDirectoryItemType eType0 = pItem0->GetType();
235 HLDirectoryItemType eType1 = pItem1->GetType();
236
237 if(eType0 == HL_ITEM_FOLDER && eType1 == HL_ITEM_FILE)
238 {
239 return true;
240 }
241 else if(eType0 == HL_ITEM_FILE && eType1 == HL_ITEM_FOLDER)
242 {
243 return false;
244 }
245
246 hlInt iResult;
247
248 switch(eField)
249 {
250 case HL_FIELD_SIZE:
251 {
252 hlUInt uiSize0 = eType0 == HL_ITEM_FILE ? static_cast<const CDirectoryFile *>(pItem0)->GetSize() : static_cast<const CDirectoryFolder *>(pItem0)->GetCount();
253 hlUInt uiSize1 = eType1 == HL_ITEM_FILE ? static_cast<const CDirectoryFile *>(pItem1)->GetSize() : static_cast<const CDirectoryFolder *>(pItem1)->GetCount();
254
255 iResult = (hlInt)uiSize0 - (hlInt)uiSize1;
256
257 if(iResult != 0)
258 {
259 break;
260 }
261 }
262 //case HL_FIELD_NAME:
263 default:
264 {
265 iResult = stricmp(pItem0->GetName(), pItem1->GetName());
266 break;
267 }
268 }
269
270 if(eOrder == HL_ORDER_DESCENDING)
271 {
272 iResult *= -1;
273 }
274
275 return iResult < 0;
276 }
277 };
278
Sort(HLSortField eField,HLSortOrder eOrder,hlBool bRecurse)279 hlVoid CDirectoryFolder::Sort(HLSortField eField, HLSortOrder eOrder, hlBool bRecurse)
280 {
281 std::sort(this->pDirectoryItemVector->begin(), this->pDirectoryItemVector->end(), CCompareDirectoryItems(eField, eOrder));
282
283 if(bRecurse)
284 {
285 for(hlUInt i = 0; i < this->pDirectoryItemVector->size(); i++)
286 {
287 CDirectoryItem *pItem = (*this->pDirectoryItemVector)[i];
288 if(pItem->GetType() == HL_ITEM_FOLDER)
289 {
290 static_cast<CDirectoryFolder *>(pItem)->Sort(eField, eOrder, bRecurse);
291 }
292 }
293 }
294 }
295
FindFirst(const hlChar * lpSearch,HLFindType eFind)296 CDirectoryItem *CDirectoryFolder::FindFirst(const hlChar *lpSearch, HLFindType eFind)
297 {
298 return const_cast<CDirectoryItem *>(const_cast<const CDirectoryFolder*>(this)->FindFirst(lpSearch, eFind));
299 }
300
FindFirst(const hlChar * lpSearch,HLFindType eFind) const301 const CDirectoryItem *CDirectoryFolder::FindFirst(const hlChar *lpSearch, HLFindType eFind) const
302 {
303 return this->FindNext(this, 0, lpSearch, eFind);
304 }
305
FindNext(const CDirectoryItem * pItem,const hlChar * lpSearch,HLFindType eFind)306 CDirectoryItem *CDirectoryFolder::FindNext(const CDirectoryItem *pItem, const hlChar *lpSearch, HLFindType eFind)
307 {
308 return const_cast<CDirectoryItem *>(const_cast<const CDirectoryFolder*>(this)->FindNext(pItem, lpSearch, eFind));
309 }
310
FindNext(const CDirectoryItem * pItem,const hlChar * lpSearch,HLFindType eFind) const311 const CDirectoryItem *CDirectoryFolder::FindNext(const CDirectoryItem *pItem, const hlChar *lpSearch, HLFindType eFind) const
312 {
313 if(pItem == 0)
314 {
315 return 0;
316 }
317
318 if(pItem->GetType() == HL_ITEM_FOLDER && !(eFind & HL_FIND_NO_RECURSE))
319 {
320 return this->FindNext(static_cast<const CDirectoryFolder *>(pItem), 0, lpSearch, eFind);
321 }
322 else
323 {
324 return this->FindNext(pItem->GetParent(), pItem, lpSearch, eFind);
325 }
326 }
327
FindNext(const CDirectoryFolder * pFolder,const CDirectoryItem * pRelative,const hlChar * lpSearch,HLFindType eFind) const328 const CDirectoryItem *CDirectoryFolder::FindNext(const CDirectoryFolder *pFolder, const CDirectoryItem *pRelative, const hlChar *lpSearch, HLFindType eFind) const
329 {
330 hlUInt uiFirst = 0;
331
332 if(pRelative)
333 {
334 for(hlUInt i = 0; i < pFolder->GetCount(); i++)
335 {
336 if(pFolder->GetItem(i) == pRelative)
337 {
338 uiFirst = i + 1;
339 break;
340 }
341 }
342 }
343
344 for(hlUInt i = uiFirst; i < pFolder->GetCount(); i++)
345 {
346 const CDirectoryItem *pTest = pFolder->GetItem(i);
347 if((pTest->GetType() == HL_ITEM_FILE && (eFind & HL_FIND_FILES)) || (pTest->GetType() == HL_ITEM_FOLDER && (eFind & HL_FIND_FOLDERS)))
348 {
349 if(this->Match(pTest->GetName(), lpSearch, eFind))
350 {
351 return pTest;
352 }
353 }
354
355 if(pTest->GetType() == HL_ITEM_FOLDER && !(eFind & HL_FIND_NO_RECURSE))
356 {
357 pTest = this->FindNext(static_cast<const CDirectoryFolder *>(pTest), 0, lpSearch, eFind);
358
359 if(pTest != 0)
360 {
361 return pTest;
362 }
363 }
364 }
365
366 if(this == pFolder || pRelative == 0 || pFolder->GetParent() == 0)
367 {
368 return 0;
369 }
370
371 return this->FindNext(pFolder->GetParent(), pFolder, lpSearch, eFind);
372 }
373
Compare(const hlChar * lpString0,const hlChar * lpString1,HLFindType eFind) const374 hlInt CDirectoryFolder::Compare(const hlChar *lpString0, const hlChar *lpString1, HLFindType eFind) const
375 {
376 if(eFind & HL_FIND_CASE_SENSITIVE)
377 {
378 return strcmp(lpString0, lpString1);
379 }
380 else
381 {
382 return stricmp(lpString0, lpString1);
383 }
384 }
385
386 //
387 // Match a string to a seach string. Search string can contain wild cards like * (to match
388 // a substring) and ? (to match a letter).
389 //
Match(const hlChar * lpString,const hlChar * lpSearch,HLFindType eFind) const390 hlBool CDirectoryFolder::Match(const hlChar *lpString, const hlChar *lpSearch, HLFindType eFind) const
391 {
392 if(eFind & HL_FIND_MODE_STRING)
393 {
394 return this->Compare(lpString, lpSearch, eFind);
395 }
396 else if(eFind & HL_FIND_MODE_SUBSTRING)
397 {
398 hlInt iStringLength = (hlInt)strlen(lpString);
399 hlInt iSearchLength = (hlInt)strlen(lpSearch);
400 hlInt iTests = iStringLength - iSearchLength;
401
402 if(eFind & HL_FIND_CASE_SENSITIVE)
403 {
404 for(hlInt i = 0; i <= iTests; i++)
405 {
406 if(strncmp(lpString + i, lpSearch, iSearchLength) == 0)
407 {
408 return hlTrue;
409 }
410 }
411 }
412 else
413 {
414 for(hlInt i = 0; i <= iTests; i++)
415 {
416 if(_strnicmp(lpString + i, lpSearch, iSearchLength) == 0)
417 {
418 return hlTrue;
419 }
420 }
421 }
422
423 return hlFalse;
424 }
425 else /*if(eFind & HL_FIND_MODE_WILDCARD)*/
426 {
427 while(*lpSearch)
428 {
429 if(*lpSearch == '*')
430 {
431 if(lpSearch[1] == '*')
432 {
433 lpSearch++;
434 continue;
435 }
436 else if(lpSearch[1] == '\0')
437 {
438 return hlTrue;
439 }
440 else
441 {
442 lpSearch++;
443 while(*lpString)
444 {
445 if(this->Match(lpString, lpSearch, eFind))
446 {
447 return hlTrue;
448 }
449 lpString++;
450 }
451
452 return hlFalse;
453 }
454 }
455 else if(*lpSearch == '?')
456 {
457 if(*lpString == '\0')
458 {
459 return hlFalse;
460 }
461 lpSearch++;
462 lpString++;
463 }
464 else
465 {
466 if(*lpString == '\0')
467 {
468 return hlFalse;
469 }
470 else
471 {
472 hlChar iA = *lpSearch;
473 hlChar iB = *lpString;
474
475 if((eFind & HL_FIND_CASE_SENSITIVE) == 0)
476 {
477 if(iA >= 'a' && iA <= 'z')
478 {
479 iA -= 'a' - 'A';
480 }
481
482 if(iB >= 'a' && iB <= 'z')
483 {
484 iB -= 'a' - 'A';
485 }
486 }
487
488 if(iA != iB)
489 {
490 return hlFalse;
491 }
492 }
493 lpSearch++;
494 lpString++;
495 }
496 }
497
498 return *lpString == '\0';
499 }
500 }
501
GetSize(hlBool bRecurse) const502 hlUInt CDirectoryFolder::GetSize(hlBool bRecurse) const
503 {
504 hlUInt uiSize = 0;
505
506 for(hlUInt i = 0; i < this->pDirectoryItemVector->size(); i++)
507 {
508 const CDirectoryItem *pItem = (*this->pDirectoryItemVector)[i];
509 switch(pItem->GetType())
510 {
511 case HL_ITEM_FOLDER:
512 if(bRecurse)
513 {
514 uiSize += static_cast<const CDirectoryFolder *>(pItem)->GetSize(bRecurse);
515 }
516 break;
517 case HL_ITEM_FILE:
518 uiSize += static_cast<const CDirectoryFile *>(pItem)->GetSize();
519 break;
520 }
521 }
522
523 return uiSize;
524 }
525
GetSizeEx(hlBool bRecurse) const526 hlULongLong CDirectoryFolder::GetSizeEx(hlBool bRecurse) const
527 {
528 hlULongLong uiSize = 0;
529
530 for(hlUInt i = 0; i < this->pDirectoryItemVector->size(); i++)
531 {
532 const CDirectoryItem *pItem = (*this->pDirectoryItemVector)[i];
533 switch(pItem->GetType())
534 {
535 case HL_ITEM_FOLDER:
536 if(bRecurse)
537 {
538 uiSize += static_cast<const CDirectoryFolder *>(pItem)->GetSizeEx(bRecurse);
539 }
540 break;
541 case HL_ITEM_FILE:
542 uiSize += static_cast<hlULongLong>(static_cast<const CDirectoryFile *>(pItem)->GetSize());
543 break;
544 }
545 }
546
547 return uiSize;
548 }
549
GetSizeOnDisk(hlBool bRecurse) const550 hlUInt CDirectoryFolder::GetSizeOnDisk(hlBool bRecurse) const
551 {
552 hlUInt uiSize = 0;
553
554 for(hlUInt i = 0; i < this->pDirectoryItemVector->size(); i++)
555 {
556 const CDirectoryItem *pItem = (*this->pDirectoryItemVector)[i];
557 switch(pItem->GetType())
558 {
559 case HL_ITEM_FOLDER:
560 if(bRecurse)
561 {
562 uiSize += static_cast<const CDirectoryFolder *>(pItem)->GetSizeOnDisk(bRecurse);
563 }
564 break;
565 case HL_ITEM_FILE:
566 uiSize += static_cast<const CDirectoryFile *>(pItem)->GetSizeOnDisk();
567 break;
568 }
569 }
570
571 return uiSize;
572 }
573
GetSizeOnDiskEx(hlBool bRecurse) const574 hlULongLong CDirectoryFolder::GetSizeOnDiskEx(hlBool bRecurse) const
575 {
576 hlULongLong uiSize = 0;
577
578 for(hlUInt i = 0; i < this->pDirectoryItemVector->size(); i++)
579 {
580 const CDirectoryItem *pItem = (*this->pDirectoryItemVector)[i];
581 switch(pItem->GetType())
582 {
583 case HL_ITEM_FOLDER:
584 if(bRecurse)
585 {
586 uiSize += static_cast<const CDirectoryFolder *>(pItem)->GetSizeOnDiskEx(bRecurse);
587 }
588 break;
589 case HL_ITEM_FILE:
590 uiSize += static_cast<hlULongLong>(static_cast<const CDirectoryFile *>(pItem)->GetSizeOnDisk());
591 break;
592 }
593 }
594
595 return uiSize;
596 }
597
GetFolderCount(hlBool bRecurse) const598 hlUInt CDirectoryFolder::GetFolderCount(hlBool bRecurse) const
599 {
600 hlUInt uiCount = 0;
601
602 for(hlUInt i = 0; i < this->pDirectoryItemVector->size(); i++)
603 {
604 const CDirectoryItem *pItem = (*this->pDirectoryItemVector)[i];
605 switch(pItem->GetType())
606 {
607 case HL_ITEM_FOLDER:
608 uiCount++;
609 if(bRecurse)
610 {
611 uiCount += static_cast<const CDirectoryFolder *>(pItem)->GetFolderCount(bRecurse);
612 }
613 break;
614 }
615 }
616
617 return uiCount;
618 }
619
GetFileCount(hlBool bRecurse) const620 hlUInt CDirectoryFolder::GetFileCount(hlBool bRecurse) const
621 {
622 hlUInt uiCount = 0;
623
624 for(hlUInt i = 0; i < this->pDirectoryItemVector->size(); i++)
625 {
626 const CDirectoryItem *pItem = (*this->pDirectoryItemVector)[i];
627 switch(pItem->GetType())
628 {
629 case HL_ITEM_FOLDER:
630 if(bRecurse)
631 {
632 uiCount += static_cast<const CDirectoryFolder *>(pItem)->GetFileCount(bRecurse);
633 }
634 break;
635 case HL_ITEM_FILE:
636 uiCount++;
637 break;
638 }
639 }
640
641 return uiCount;
642 }
643
Extract(const hlChar * lpPath) const644 hlBool CDirectoryFolder::Extract(const hlChar *lpPath) const
645 {
646 hlExtractItemStart(this);
647
648 hlChar *lpName = new hlChar[strlen(this->GetName()) + 1];
649 strcpy(lpName, this->GetName());
650 RemoveIllegalCharacters(lpName);
651
652 hlChar *lpFolderName;
653 if(lpPath == 0 || *lpPath == '\0')
654 {
655 lpFolderName = new hlChar[strlen(lpName) + 1];
656 strcpy(lpFolderName, lpName);
657 }
658 else
659 {
660 lpFolderName = new hlChar[strlen(lpPath) + 1 + strlen(lpName) + 1];
661 strcpy(lpFolderName, lpPath);
662 strcat(lpFolderName, PATH_SEPARATOR_STRING);
663 strcat(lpFolderName, lpName);
664 }
665
666 FixupIllegalCharacters(lpFolderName);
667
668 hlBool bResult;
669 if(!CreateFolder(lpFolderName))
670 {
671 LastError.SetSystemErrorMessage("CreateDirectory() failed.");
672
673 bResult = hlFalse;
674 }
675 else
676 {
677 bResult = hlTrue;
678
679 for(hlUInt i = 0; i < this->pDirectoryItemVector->size(); i++)
680 {
681 const CDirectoryItem *pItem = (*this->pDirectoryItemVector)[i];
682 if(pItem->GetType() != HL_ITEM_FILE || static_cast<const CDirectoryFile *>(pItem)->GetExtractable())
683 {
684 bResult &= pItem->Extract(lpFolderName);
685 }
686 }
687 }
688
689 delete []lpFolderName;
690 delete []lpName;
691
692 hlExtractItemEnd(this, bResult);
693
694 return bResult;
695 }
696