00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <assert.h>
00024 #include <stdio.h>
00025 #include <stdlib.h>
00026 #include <string.h>
00027
00028 #include "basetype.h"
00029 #include "ram.h"
00030
00031 #include "dirtree.h"
00032 #include "dirtree-common.h"
00033
00034 #define DIRTREE_FILE_SIGNATURE MAKEFOURCC('D', 'T', '0', '1')
00035 static int DirTree_SignatureBase=0x696C6345;
00036 static int DirTree_SignatureOffset=0x21657370;
00037
00038 typedef struct DirTree
00039 {
00040 char * Name;
00041 geVFile_Time Time;
00042 geVFile_Attributes AttributeFlags;
00043 long Size;
00044 geVFile_Hints Hints;
00045 long Offset;
00046 struct DirTree * Parent;
00047 struct DirTree * Children;
00048 struct DirTree * Siblings;
00049 } DirTree;
00050
00051 typedef struct DirTree_Finder
00052 {
00053 char * MatchName;
00054 char * MatchExt;
00055 DirTree * Current;
00056 } DirTree_Finder;
00057
00058 DirTree *DirTree_Create(void)
00059 {
00060 DirTree * Tree;
00061
00062 Tree = geRam_Allocate(sizeof(*Tree));
00063 if (!Tree)
00064 return Tree;
00065
00066 memset(Tree, 0, sizeof(*Tree));
00067 Tree->Name = DuplicateString("");
00068 if (!Tree->Name)
00069 {
00070 geRam_Free(Tree);
00071 return NULL;
00072 }
00073
00074 Tree->AttributeFlags |= GE_VFILE_ATTRIB_DIRECTORY;
00075
00076 return Tree;
00077 }
00078
00079 void DirTree_Destroy(DirTree *Tree)
00080 {
00081 assert(Tree);
00082 assert(Tree->Name);
00083
00084 if (Tree->Children)
00085 DirTree_Destroy(Tree->Children);
00086
00087 if (Tree->Siblings)
00088 DirTree_Destroy(Tree->Siblings);
00089
00090 geRam_Free(Tree->Name);
00091 geRam_Free(Tree);
00092 }
00093
00094 static geBoolean WriteTree(const DirTree *Tree, geVFile *File)
00095 {
00096 int Length;
00097 int Terminator;
00098
00099 assert(Tree);
00100 assert(Tree->Name);
00101
00102 Terminator = DIRTREE_LIST_NOTTERMINATED;
00103 if (geVFile_Write(File, &Terminator, sizeof(Terminator)) == GE_FALSE)
00104 return GE_FALSE;
00105
00106
00107 Length = strlen(Tree->Name) + 1;
00108 if (geVFile_Write(File, &Length, sizeof(Length)) == GE_FALSE)
00109 return GE_FALSE;
00110 if (Length > 0)
00111 {
00112 if (geVFile_Write(File, Tree->Name, Length) == GE_FALSE)
00113 return GE_FALSE;
00114 }
00115
00116
00117 if (geVFile_Write(File, &Tree->Time, sizeof(Tree->Time)) == GE_FALSE)
00118 return GE_FALSE;
00119
00120 if (geVFile_Write(File, &Tree->AttributeFlags, sizeof(Tree->AttributeFlags)) == GE_FALSE)
00121 return GE_FALSE;
00122
00123 if (geVFile_Write(File, &Tree->Size, sizeof(Tree->Size)) == GE_FALSE)
00124 return GE_FALSE;
00125
00126 if (geVFile_Write(File, &Tree->Offset, sizeof(Tree->Offset)) == GE_FALSE)
00127 return GE_FALSE;
00128
00129 if (geVFile_Write(File, &Tree->Hints.HintDataLength, sizeof(Tree->Hints.HintDataLength)) == GE_FALSE)
00130 return GE_FALSE;
00131
00132 if (Tree->Hints.HintDataLength != 0)
00133
00134 if (geVFile_Write(File, Tree->Hints.HintData, Tree->Hints.HintDataLength) == GE_FALSE)
00135 return GE_FALSE;
00136
00137
00138 if (Tree->Children)
00139 {
00140 WriteTree(Tree->Children, File);
00141 }
00142 else
00143 {
00144 Terminator = DIRTREE_LIST_TERMINATED;
00145 if (geVFile_Write(File, &Terminator, sizeof(Terminator)) == GE_FALSE)
00146 return GE_FALSE;
00147 }
00148
00149
00150 if (Tree->Siblings)
00151 {
00152 WriteTree(Tree->Siblings, File);
00153 }
00154 else
00155 {
00156 Terminator = DIRTREE_LIST_TERMINATED;
00157 if (geVFile_Write(File, &Terminator, sizeof(Terminator)) == GE_FALSE)
00158 return GE_FALSE;
00159 }
00160
00161 return GE_TRUE;
00162 }
00163
00164 static geBoolean DirTree_WriteToFile1(const DirTree *Tree, geVFile *File, long *Size)
00165 {
00166 DirTree_Header Header;
00167 long StartPosition;
00168 long EndPosition;
00169
00170 if (geVFile_Tell(File, &StartPosition) == GE_FALSE)
00171 return GE_FALSE;
00172
00173 Header.Signature = DIRTREE_FILE_SIGNATURE;
00174 if (geVFile_Seek(File, sizeof(Header), GE_VFILE_SEEKCUR) == GE_FALSE)
00175 return GE_FALSE;
00176
00177 if (WriteTree(Tree, File) == GE_FALSE)
00178 return GE_FALSE;
00179
00180 geVFile_Tell(File, &EndPosition);
00181 Header.Size = EndPosition - StartPosition;
00182 geVFile_Seek(File, StartPosition, GE_VFILE_SEEKSET);
00183 if (geVFile_Write(File, &Header, sizeof(Header)) == GE_FALSE)
00184 return GE_FALSE;
00185
00186
00187 geVFile_Seek(File, EndPosition, GE_VFILE_SEEKSET);
00188
00189 *Size = Header.Size;
00190
00191 return GE_TRUE;
00192 }
00193
00194 void DirTree_SetFileSize(DirTree *Tree, long Size)
00195 {
00196 assert(Tree);
00197 Tree->Size = Size;
00198 }
00199
00200 void DirTree_GetFileSize(DirTree *Tree, long *Size)
00201 {
00202 assert(Tree);
00203 *Size = Tree->Size;
00204 }
00205
00206 geBoolean DirTree_WriteToFile(const DirTree *Tree, geVFile *File)
00207 {
00208 geBoolean Res;
00209 long Size;
00210
00211 Res = DirTree_WriteToFile1(Tree, File, &Size);
00212 if (Res == GE_FALSE)
00213 return Res;
00214
00215 return GE_TRUE;
00216 }
00217
00218 geBoolean DirTree_GetSize(const DirTree *Tree, long *Size)
00219 {
00220 geVFile * FS;
00221 geVFile_MemoryContext Context;
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233 Context.Data = NULL;
00234 Context.DataLength = 0;
00235
00236 FS = geVFile_OpenNewSystem(NULL,
00237 GE_VFILE_TYPE_MEMORY,
00238 NULL,
00239 &Context,
00240 GE_VFILE_OPEN_CREATE);
00241 if (!FS)
00242 return GE_FALSE;
00243
00244 if (DirTree_WriteToFile1(Tree, FS, Size) == GE_FALSE)
00245 return GE_FALSE;
00246
00247 if (geVFile_Size(FS, Size) == GE_FALSE)
00248 return GE_FALSE;
00249
00250 geVFile_Close(FS);
00251
00252 return GE_TRUE;
00253 }
00254
00255 static geBoolean ReadTree(geVFile *File, DirTree **TreePtr)
00256 {
00257 int Terminator;
00258 int Length;
00259 DirTree * Tree;
00260
00261 if (geVFile_Read(File, &Terminator, sizeof(Terminator)) == GE_FALSE)
00262 return GE_FALSE;
00263
00264 if (Terminator == DIRTREE_LIST_TERMINATED)
00265 {
00266 *TreePtr = NULL;
00267 return GE_TRUE;
00268 }
00269
00270 Tree = geRam_Allocate(sizeof(*Tree));
00271 if (!Tree)
00272 return GE_FALSE;
00273 memset(Tree, 0, sizeof(*Tree));
00274
00275
00276 if (geVFile_Read(File, &Length, sizeof(Length)) == GE_FALSE)
00277 goto fail;
00278
00279 assert(Length > 0);
00280 Tree->Name = geRam_Allocate(Length);
00281 if (!Tree->Name)
00282 {
00283 geRam_Free(Tree);
00284 return GE_FALSE;
00285 }
00286
00287 if (geVFile_Read(File, Tree->Name, Length) == GE_FALSE)
00288 goto fail;
00289
00290
00291
00292
00293 if (geVFile_Read(File, &Tree->Time, sizeof(Tree->Time)) == GE_FALSE)
00294 goto fail;
00295
00296 if (geVFile_Read(File, &Tree->AttributeFlags, sizeof(Tree->AttributeFlags)) == GE_FALSE)
00297 goto fail;
00298
00299 if (geVFile_Read(File, &Tree->Size, sizeof(Tree->Size)) == GE_FALSE)
00300 goto fail;
00301
00302 if (geVFile_Read(File, &Tree->Offset, sizeof(Tree->Offset)) == GE_FALSE)
00303 goto fail;
00304
00305 if (geVFile_Read(File, &Tree->Hints.HintDataLength, sizeof(Tree->Hints.HintDataLength)) == GE_FALSE)
00306 goto fail;
00307
00308 if (Tree->Hints.HintDataLength != 0)
00309 {
00310 Tree->Hints.HintData = geRam_Allocate(Tree->Hints.HintDataLength);
00311 if (!Tree->Hints.HintData)
00312 goto fail;
00313
00314 if (geVFile_Read(File, Tree->Hints.HintData, Tree->Hints.HintDataLength) == GE_FALSE)
00315 goto fail;
00316 }
00317
00318
00319
00320 if (ReadTree(File, &Tree->Children) == GE_FALSE)
00321 goto fail;
00322
00323
00324
00325 if (ReadTree(File, &Tree->Siblings) == GE_FALSE)
00326 goto fail;
00327
00328
00329
00330 *TreePtr = Tree;
00331
00332 return GE_TRUE;
00333
00334 fail:
00335 DirTree_Destroy(Tree);
00336 return GE_FALSE;
00337 }
00338
00339 DirTree *DirTree_CreateFromFile(geVFile *File)
00340 {
00341 DirTree * Res;
00342 DirTree_Header Header;
00343 long StartPosition;
00344 long EndPosition;
00345
00346 if (geVFile_Tell(File, &StartPosition) == GE_FALSE)
00347 return GE_FALSE;
00348
00349 if (geVFile_Read(File, &Header, sizeof(Header)) == GE_FALSE)
00350 return NULL;
00351
00352 if (Header.Signature != DIRTREE_FILE_SIGNATURE)
00353 return GE_FALSE;
00354
00355 if (ReadTree(File, &Res) == GE_FALSE)
00356 return NULL;
00357
00358 geVFile_Tell(File, &EndPosition);
00359 if (Header.Size != EndPosition - StartPosition)
00360 {
00361 DirTree_Destroy(Res);
00362 return NULL;
00363 }
00364
00365 return Res;
00366 }
00367
00368 DirTree *DirTree_FindExact(const DirTree *Tree, const char *Path)
00369 {
00370 static char Buff[_MAX_PATH];
00371 DirTree * Siblings;
00372
00373 assert(Tree);
00374 assert(Path);
00375
00376 if (*Path == '\\')
00377 return NULL;
00378
00379 if (*Path == '\0')
00380 return (DirTree *)Tree;
00381
00382 Path = GetNextDir(Path, Buff);
00383
00384 Siblings = Tree->Children;
00385 while (Siblings)
00386 {
00387 if (!stricmp(Siblings->Name, Buff))
00388 {
00389 if (!*Path)
00390 return Siblings;
00391 return DirTree_FindExact(Siblings, Path);
00392 }
00393 Siblings = Siblings->Siblings;
00394 }
00395
00396 return NULL;
00397 }
00398
00399 DirTree *DirTree_FindPartial(
00400 const DirTree * Tree,
00401 const char * Path,
00402 const char ** LeftOvers)
00403 {
00404 static char Buff[_MAX_PATH];
00405 DirTree * Siblings;
00406
00407 assert(Tree);
00408 assert(Path);
00409
00410 if (*Path == '\\')
00411 return NULL;
00412
00413 *LeftOvers = Path;
00414
00415 if (*Path == '\0')
00416 return (DirTree *)Tree;
00417
00418 Path = GetNextDir(Path, Buff);
00419
00420 Siblings = Tree->Children;
00421 while (Siblings)
00422 {
00423 if (!stricmp(Siblings->Name, Buff))
00424 {
00425 *LeftOvers = Path;
00426 if (!*Path)
00427 return Siblings;
00428 return DirTree_FindPartial(Siblings, Path, LeftOvers);
00429 }
00430 Siblings = Siblings->Siblings;
00431 }
00432
00433 return (DirTree *)Tree;
00434 }
00435
00436 DirTree * DirTree_AddFile(DirTree *Tree, const char *Path, geBoolean IsDirectory)
00437 {
00438 DirTree * NewEntry;
00439 const char * LeftOvers;
00440
00441 assert(Tree);
00442 assert(Path);
00443 assert(IsDirectory == GE_TRUE || IsDirectory == GE_FALSE);
00444
00445 assert(strlen(Path) > 0);
00446
00447 if (PathHasDir(Path))
00448 {
00449 Tree = DirTree_FindPartial(Tree, Path, &LeftOvers);
00450 if (!Tree)
00451 return NULL;
00452
00453 if (PathHasDir(LeftOvers))
00454 return NULL;
00455
00456 Path = LeftOvers;
00457 }
00458
00459 NewEntry = geRam_Allocate(sizeof(*NewEntry));
00460 if (!NewEntry)
00461 return NULL;
00462
00463 memset(NewEntry, 0, sizeof(*NewEntry));
00464 NewEntry->Name = DuplicateString(Path);
00465 if (!NewEntry->Name)
00466 {
00467 geRam_Free(NewEntry->Name);
00468 geRam_Free(NewEntry);
00469 return NULL;
00470 }
00471
00472 NewEntry->Siblings = Tree->Children;
00473 Tree->Children = NewEntry;
00474
00475 if (IsDirectory == GE_TRUE)
00476 NewEntry->AttributeFlags |= GE_VFILE_ATTRIB_DIRECTORY;
00477
00478 return NewEntry;
00479 }
00480
00481 geBoolean DirTree_Remove(DirTree *Tree, DirTree *SubTree)
00482 {
00483 DirTree Siblings;
00484 DirTree * pSiblings;
00485 DirTree * Parent;
00486 DirTree * ParanoiaCheck;
00487
00488 assert(Tree);
00489 assert(SubTree);
00490
00491 Parent = SubTree->Parent;
00492 assert(Parent);
00493
00494 ParanoiaCheck = Parent;
00495 while (ParanoiaCheck && ParanoiaCheck != Tree)
00496 ParanoiaCheck = ParanoiaCheck->Parent;
00497 if (!ParanoiaCheck)
00498 return GE_FALSE;
00499
00500 Siblings.Siblings = Parent->Children;
00501 assert(Siblings.Siblings);
00502 pSiblings = &Siblings;
00503 while (pSiblings->Siblings)
00504 {
00505 if (pSiblings->Siblings == SubTree)
00506 {
00507 pSiblings->Siblings = SubTree->Siblings;
00508 if (SubTree == Parent->Children)
00509 Parent->Children = SubTree->Siblings;
00510 SubTree->Siblings = NULL;
00511 DirTree_Destroy(SubTree);
00512 return GE_TRUE;
00513 }
00514 pSiblings = pSiblings->Siblings;
00515 }
00516
00517 assert(!"Shouldn't be a way to get here");
00518 return GE_FALSE;
00519 }
00520
00521 void DirTree_SetFileAttributes(DirTree *Tree, geVFile_Attributes Attributes)
00522 {
00523 assert(Tree);
00524 assert(Attributes);
00525
00526
00527 assert(!(Attributes & ~GE_VFILE_ATTRIB_READONLY));
00528 assert(!(Tree->AttributeFlags & GE_VFILE_ATTRIB_DIRECTORY));
00529
00530 Tree->AttributeFlags = (Tree->AttributeFlags & ~GE_VFILE_ATTRIB_READONLY) | Attributes;
00531 }
00532
00533 void DirTree_GetFileAttributes(DirTree *Tree, geVFile_Attributes *Attributes)
00534 {
00535 assert(Tree);
00536 assert(Attributes);
00537
00538 *Attributes = Tree->AttributeFlags;
00539 }
00540
00541 void DirTree_SetFileOffset(DirTree *Leaf, long Offset)
00542 {
00543 assert(Leaf);
00544 assert(!(Leaf->AttributeFlags & GE_VFILE_ATTRIB_DIRECTORY));
00545
00546 Leaf->Offset = Offset;
00547 }
00548
00549 void DirTree_GetFileOffset(DirTree *Leaf, long *Offset)
00550 {
00551 assert(Leaf);
00552 assert(!(Leaf->AttributeFlags & GE_VFILE_ATTRIB_DIRECTORY));
00553
00554 *Offset = Leaf->Offset;
00555 }
00556
00557 void DirTree_SetFileTime(DirTree *Tree, const geVFile_Time *Time)
00558 {
00559 assert(Tree);
00560
00561 Tree->Time = *Time;
00562 }
00563
00564 void DirTree_GetFileTime(DirTree *Tree, geVFile_Time *Time)
00565 {
00566 assert(Tree);
00567
00568 *Time = Tree->Time;
00569 }
00570
00571 geBoolean DirTree_SetFileHints(DirTree *Tree, const geVFile_Hints *Hints)
00572 {
00573 if (Tree->Hints.HintData)
00574 geRam_Free(Tree->Hints.HintData);
00575
00576 if (Hints->HintData)
00577 {
00578 Tree->Hints.HintData = geRam_Allocate(Hints->HintDataLength);
00579 if (!Tree->Hints.HintData)
00580 return GE_FALSE;
00581 memcpy(Tree->Hints.HintData, Hints->HintData, Hints->HintDataLength);
00582 }
00583 Tree->Hints.HintDataLength = Hints->HintDataLength;
00584 return GE_TRUE;
00585 }
00586
00587 void DirTree_GetFileHints(DirTree *Tree, geVFile_Hints *Hints)
00588 {
00589 *Hints = Tree->Hints;
00590 }
00591
00592 geBoolean DirTree_GetName(DirTree *Tree, char *Buff, int MaxLen)
00593 {
00594 int Length;
00595
00596 assert(Tree);
00597 assert(Buff);
00598 assert(MaxLen > 0);
00599
00600 Length = strlen(Tree->Name);
00601 if (Length > MaxLen)
00602 return GE_FALSE;
00603
00604 memcpy(Buff, Tree->Name, Length + 1);
00605
00606 return GE_TRUE;
00607 }
00608
00609 geBoolean DirTree_FileExists(const DirTree *Tree, const char *Path)
00610 {
00611 if (DirTree_FindExact(Tree, Path) == NULL)
00612 return GE_FALSE;
00613
00614 return GE_TRUE;
00615 }
00616
00617 DirTree_Finder * DirTree_CreateFinder(DirTree *Tree, const char *Path)
00618 {
00619 DirTree_Finder * Finder;
00620 DirTree * SubTree;
00621 char Directory[_MAX_PATH];
00622 char Name[_MAX_FNAME];
00623 char Ext[_MAX_EXT];
00624
00625 assert(Tree);
00626 assert(Path);
00627
00628 _splitpath(Path, NULL, Directory, Name, Ext);
00629
00630 SubTree = DirTree_FindExact(Tree, Directory);
00631 if (!SubTree)
00632 return NULL;
00633
00634 Finder = geRam_Allocate(sizeof(*Finder));
00635 if (!Finder)
00636 return Finder;
00637
00638 Finder->MatchName = DuplicateString(Name);
00639 if (!Finder->MatchName)
00640 {
00641 geRam_Free(Finder);
00642 return NULL;
00643 }
00644
00645
00646 if (*Ext == '.')
00647 Finder->MatchExt = DuplicateString(&Ext[1]);
00648 else
00649 Finder->MatchExt = DuplicateString(&Ext[0]);
00650
00651 if (!Finder->MatchExt)
00652 {
00653 geRam_Free(Finder->MatchName);
00654 geRam_Free(Finder);
00655 return NULL;
00656 }
00657
00658 Finder->Current = SubTree->Children;
00659
00660 return Finder;
00661 }
00662
00663 void DirTree_DestroyFinder(DirTree_Finder *Finder)
00664 {
00665 assert(Finder);
00666 assert(Finder->MatchName);
00667 assert(Finder->MatchExt);
00668
00669 geRam_Free(Finder->MatchName);
00670 geRam_Free(Finder->MatchExt);
00671 geRam_Free(Finder);
00672 }
00673
00674 DirTree * DirTree_FinderGetNextFile(DirTree_Finder *Finder)
00675 {
00676 DirTree * Res;
00677 char Name[_MAX_FNAME];
00678 char Ext[_MAX_EXT];
00679
00680 assert(Finder);
00681
00682 Res = Finder->Current;
00683
00684 if (!Res)
00685 return Res;
00686
00687 do
00688 {
00689 _splitpath(Res->Name, NULL, NULL, Name, Ext);
00690 if (MatchPattern(Name, Finder->MatchName) == GE_TRUE &&
00691 MatchPattern(Ext, Finder->MatchExt) == GE_TRUE)
00692 {
00693 break;
00694 }
00695
00696 Res = Res->Siblings;
00697
00698 } while (Res);
00699
00700 if (Res)
00701 Finder->Current = Res->Siblings;
00702
00703 return Res;
00704 }
00705
00706 #ifdef DEBUG
00707
00708 static void DirTree_Dump1(const DirTree *Tree, int i)
00709 {
00710 DirTree * Temp;
00711
00712 indent(i);
00713 if (Tree->AttributeFlags & GE_VFILE_ATTRIB_DIRECTORY)
00714 printf("\\%s\n", Tree->Name);
00715 else
00716 printf("%-*s %08x %08x\n", 40 - i, Tree->Name, Tree->Offset, Tree->Size);
00717 Temp = Tree->Children;
00718 while (Temp)
00719 {
00720 DirTree_Dump1(Temp, i + 2);
00721 Temp = Temp->Siblings;
00722 }
00723 }
00724
00725 void DirTree_Dump(const DirTree *Tree)
00726 {
00727 printf("%-*s %-8s %-8s\n", 40, "Name", "Offset", "Size");
00728 printf("------------------------------------------------------------\n");
00729 DirTree_Dump1(Tree, 0);
00730 }
00731
00732 #endif