documentor: Fixed first Save on doc file creation
[sdk] / ecere / src / sys / EARArchive.ec
1 namespace sys;
2
3 #define uint _uint
4 #include "zlib.h"
5 #undef uint
6
7 import "System"
8 import "BufferedFile"
9
10 #define OFFSET(s, m) ((uint) (&((s *) 0)->m))
11 #define MDWORD(a,b) ((((uint32)((uint16)(b)))<<16)+((uint16)(a)))
12
13 #define EAR_RECOGNITION { 'e', 'A', 'R', 228, 11, 12, 3, 0 }
14
15 static byte earRecognition[] = EAR_RECOGNITION;
16
17 static class FreeBlock : struct
18 {
19    FreeBlock prev, next;
20    uint start, end;
21 };
22
23 static struct EARHeader
24 {                                               
25    byte recognition[sizeof(earRecognition)] __attribute__((packed));
26    uint version                            __attribute__((packed));
27    FileSize totalSize                      __attribute__((packed));
28 };
29
30 static enum EAREntryType { ENTRY_FILE = 1, ENTRY_FOLDER = 2 };
31
32 static struct EAREntry
33 {
34    EAREntryType type             __attribute__((packed));
35    TimeStamp32 created, modified __attribute__((packed));
36    FileSize size, cSize          __attribute__((packed));
37    uint prev, next               __attribute__((packed));
38    uint nameLen                  __attribute__((packed));
39    // null terminated file name follows
40 };
41
42 static File EAROpenArchive(char * archive, EARHeader header)
43 {
44    File f = null;
45    if(archive[0] == ':')
46    {
47       char moduleName[MAX_LOCATION];
48       char * name = archive + 1;
49 #if defined(__ANDROID__)
50       if(!name[0])
51          name = ((SubModule)__thisModule.application.modules.first).next.module.name;
52 #endif
53
54       if(LocateModule(name, moduleName))
55          f = FileOpen(moduleName, read);
56    }
57    else
58       f = FileOpen(archive, read);
59    if(f)
60    {
61       uint archiveSize;
62
63       // First attempt to treat this as an archive file
64       if(f.Read(header, sizeof(EARHeader), 1) == 1 &&
65          !strncmp(header.recognition, earRecognition, sizeof(earRecognition)))
66          return f;
67
68       // Then try to see if an archive is at the end of the file
69       f.Seek(-(int)sizeof(uint), end);
70       f.Read(&archiveSize, sizeof(uint), 1);
71       f.Seek(-(int)archiveSize, end);
72       if(f.Read(header, sizeof(EARHeader), 1) == 1 &&
73          !strncmp(header.recognition, earRecognition, sizeof(earRecognition)))
74          return f;
75
76       delete f;
77    }
78    return null;
79 }
80
81 static FileAttribs EARGetEntry(File f, EAREntry entry, char * name, char * path)
82 {
83    uint first = 0, last = 0;
84    if(!name[0])
85       return FileAttribs { isDirectory = true };
86    if(!f.Read(&first, sizeof(uint), 1))
87       return 0;
88    f.Read(&last, sizeof(uint), 1);
89    if(first)
90    {
91       char namePart[MAX_FILENAME], nameRest[MAX_LOCATION];
92       SplitDirectory(name, namePart, nameRest);
93
94       if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
95          strcpy(namePart, DIR_SEPS);
96
97       f.Seek(first, start);
98       for(;;)
99       {
100          char fileName[MAX_FILENAME];
101          
102          f.Read(entry, sizeof(EAREntry), 1);
103          f.Read(fileName, 1, entry.nameLen);
104          fileName[entry.nameLen] = '\0';
105
106          if(!strcmp(fileName, "/") || !strcmp(fileName, "\\"))
107             strcpy(fileName, DIR_SEPS);
108
109          if(!fstrcmp(fileName, namePart))
110          {
111             if(path)
112                PathCat(path, fileName);
113             if(nameRest[0])
114                return EARGetEntry(f, entry, nameRest, path);
115             else
116                return (entry.type == ENTRY_FILE) ? FileAttribs { isFile = true } : FileAttribs { isDirectory = true };
117          }
118          if(entry.next)
119             f.Seek(entry.next, start);
120          else
121             break;
122       }
123    }
124    return FileAttribs { };
125 }
126
127 #if !defined(ECERE_NOARCHIVE) && !defined(ECERE_VANILLA)
128 class EARArchive : Archive
129 {
130    File f;
131    //BufferedFile bf { };
132    // char path[MAX_LOCATION];
133    uint archiveStart;
134    uint rootDir;
135    OldList freeBlocks;
136    bool writeAccess;
137
138    uint Update()
139    {
140       if(rootDir)
141       {
142          uint end;
143
144          end = ((FreeBlock)freeBlocks.last).start;
145
146          // Update header
147          f.Seek(archiveStart + OFFSET(EARHeader, totalSize), start);
148          f.Write(&totalSize, sizeof(uint), 1);
149
150          // Write Footer
151          f.Seek(end, start);
152
153          end += sizeof(uint);
154
155          end -= archiveStart;
156
157          f.Write(&end, sizeof(uint), 1);
158          f.Truncate(archiveStart + end);
159
160          return end;
161       }
162    }
163
164    ~EARArchive()
165    {
166       if(f && rootDir && writeAccess)
167       {
168          // Perform Defrag
169          Defrag(rootDir);
170          archiveStart += Update();
171       }
172       if(f && writeAccess)
173       {
174          f.Flush();
175          f.Unlock(0, 0, true);
176       }
177       delete f;
178
179       /*if(rootDir && writeAccess)
180       {
181          // Fix the size of the archive
182          FileTruncate(path, archiveStart);
183       }*/
184       
185       freeBlocks.Free(null);
186    }
187
188    bool Clear()
189    {
190       rootDir = 0;
191       return true;
192    }
193
194    ArchiveDir OpenDirectory(char * name, FileStats stats, ArchiveAddMode addMode)
195    {
196       ArchiveDir result = null;
197       EARArchiveDir dir { readOnly = addMode == readOnlyDir };
198       if(dir)
199       {
200          char namePart[MAX_LOCATION] = "", nameRest[MAX_LOCATION];
201          uint position;
202
203          dir.archive = this;
204
205          strcpy(nameRest, name);
206
207          if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
208             strcpy(namePart, DIR_SEPS);
209
210          // Search for directory
211          if(rootDir)
212          {
213             dir.position = rootDir;
214             if(f.Seek(dir.position, start))
215             {
216                dir.first = 0;
217                dir.last = 0;
218
219                f.Read(&dir.first, sizeof(uint), 1);
220                f.Read(&dir.last, sizeof(uint), 1);
221
222                result = dir;
223             }
224          }
225
226          // If directory doesn't exist already
227          if(!result && addMode != refresh)
228          {
229             rootDir = Position(2*sizeof(uint));
230             dir.position = rootDir;
231          }
232          
233          result = dir;
234
235          // Open rest of directory...
236          if(result && nameRest[0])
237          {
238             result = dir.OpenDirectory(nameRest, stats, addMode);
239             delete dir;
240          }
241       }
242       return result;
243    }
244
245    uint Position(uint size)
246    {
247       FreeBlock block;
248       for(block = freeBlocks.first; block; block = block.next)
249       {
250          if(block.end - block.start + 1 >= size)
251          {
252             uint position = block.start;
253             if(block.end - block.start + 1 == size)
254                freeBlocks.Delete(block);
255             else
256                block.start += size;
257             return position;
258          }
259       }
260       return 0;
261    }
262
263    bool DefragOffset(uint * offset)
264    {
265       FreeBlock block;
266       uint subtract = 0;
267       for(block = freeBlocks.first; block; block = block.next)
268       {
269          if(*offset > block.start)
270             subtract += block.end - block.start + 1;
271          else
272             break;
273       }
274       if(subtract)
275       {
276          *offset -= subtract;
277          return true;
278       }
279       return false;
280    }
281
282    #define MAX_BUFFERSIZE 0x400000
283
284    void Defrag(uint dirPosition)
285    {
286       // Update all offsets within the files
287       uint first = 0, last = 0;
288       uint position = 0, next = 0;
289
290       f.Seek(dirPosition, start);
291       f.Read(&first, sizeof(uint), 1);
292       f.Read(&last, sizeof(uint), 1);
293
294       position = first;
295
296       if(first && DefragOffset(&first))
297       {
298          if(f.Seek(dirPosition, start))
299             f.Write(&first, sizeof(uint), 1);
300       }
301       if(last && DefragOffset(&last))
302       {
303          if(f.Seek(dirPosition + sizeof(uint), start))
304             f.Write(&last, sizeof(uint), 1);
305       }
306       
307       for(; position; position = next)
308       {
309          EAREntry entry { };
310
311          if(f.Seek(position, start) && f.Read(entry, sizeof(EAREntry), 1))
312          {
313             next = entry.next;
314
315             if(entry.prev && DefragOffset(&entry.prev))
316             {
317                f.Seek(position + OFFSET(EAREntry, prev), start);
318                f.Write(&entry.prev, sizeof(uint), 1);
319             }
320             if(entry.next && DefragOffset(&entry.next))
321             {
322                f.Seek(position + OFFSET(EAREntry, next), start);
323                f.Write(&entry.next, sizeof(uint), 1);
324             }
325
326             if(entry.type == ENTRY_FOLDER)
327                Defrag(position + sizeof(EAREntry) + entry.nameLen);
328          }
329          else
330             return 0;
331       }
332
333       // Move all the blocks
334       if(dirPosition == rootDir)
335       {
336          uint bufferSize = 0;
337          byte * buffer = null;
338          FreeBlock block, nextBlock;
339          for(block = freeBlocks.first; block && block.next; block = nextBlock)
340          {
341             uint dataSize, c;
342
343             nextBlock = block.next;
344             dataSize = nextBlock.start - (block.end + 1);
345
346             if(dataSize > bufferSize && (!bufferSize || bufferSize < MAX_BUFFERSIZE))
347             {
348                bufferSize = Min(dataSize, MAX_BUFFERSIZE);
349                buffer = renew buffer byte[bufferSize];
350             }
351
352             for(c = 0; c<dataSize; c += bufferSize)
353             {
354                uint size = (dataSize > c + bufferSize) ? bufferSize : (dataSize - c);
355
356                // Read block of data
357                f.Seek((block.end + 1) + c, start);
358                f.Read(buffer, size, 1);
359
360                // Write block of data
361                f.Seek(block.start + c, start);
362                f.Write(buffer, size, 1);
363             }
364
365             nextBlock.start -= (block.end - block.start) + 1;
366
367             freeBlocks.Delete(block);
368          }
369          delete buffer;
370       }
371    }
372
373    uint Find(EARArchiveDir directory, char * namePart, EAREntry entry)
374    {
375       uint position;
376       for(position = directory.first; position; position = entry.next)
377       {
378          char fileName[MAX_FILENAME];
379       
380          if(f.Seek(position, start) && f.Read(entry, sizeof(EAREntry), 1))
381          {
382             if(entry.nameLen > MAX_FILENAME)
383                return 0;   // CORRUPTION ERROR
384             f.Read(fileName, 1, entry.nameLen);
385             fileName[entry.nameLen] = '\0';
386
387             if(!strcmp(fileName, "/") || !strcmp(fileName, "\\"))
388                strcpy(fileName, DIR_SEPS);
389
390             if(!fstrcmp(fileName, namePart))
391                return position;
392          }
393          else
394             return 0;   // ERROR OUT OF SPACE?
395       }
396       return 0;
397    }
398
399    void AddFreeBlock(uint position, uint size)
400    {
401       FreeBlock block, prevBlock, nextBlock = null;
402       
403       // Find the previous and next free block
404       prevBlock = null;
405       for(block = freeBlocks.first; block; block = block.next)
406          if(block.end < position)
407             prevBlock = block; 
408          else
409          {
410             nextBlock = block;
411             break;
412          }
413
414       // Try to merge with previous block
415       if(prevBlock && prevBlock.end + 1 == position)
416       {
417           prevBlock.end += size;
418          // Try to merge with next block as well
419          if(nextBlock && nextBlock.start == prevBlock.end + 1)
420          {
421             prevBlock.end = nextBlock.end;
422             freeBlocks.Delete(nextBlock);
423          }
424       }
425       // Try to merge with next block
426       else if(nextBlock && nextBlock.start == position + size)
427       {
428          nextBlock.start = position;
429       }
430       // This free block is not connected to any other block
431       else
432       {
433          freeBlocks.Insert(prevBlock, FreeBlock { start = position, end = position + size - 1 });
434       }
435    }
436
437    void SubtractBlock(uint start, uint size)
438    {
439       FreeBlock block;
440       for(block = freeBlocks.first; block; block = block.next)
441       {
442          if(block.end >= start - 1L && block.start <= start + size)
443          {
444             if(block.end > start + size && block.start < start - 1L)
445             {
446                FreeBlock newBlock { start = start + size, end = block.end };
447                block.end = start - 1L;
448                freeBlocks.Insert(block, newBlock);
449             }
450             else if(block.end > start + size)
451             {
452                block.start = start + size;
453             }
454             else if(block.start < start - 1L)
455             {
456                block.end = start - 1L;
457             }
458             else
459             {
460                freeBlocks.Remove(block);
461                delete block;
462             }
463             break;
464          }
465       }
466    }
467
468    void Delete(EARArchiveDir dir, uint position, EAREntry entry)
469    {
470       uint size;
471       if(entry.type == ENTRY_FOLDER)
472       {
473          EARArchiveDir subDir {};
474          uint filePosition;
475          EAREntry fileEntry;
476
477          subDir.position = dir.position;
478          f.Read(&subDir.first, sizeof(uint), 1);
479          f.Read(&subDir.last, sizeof(uint), 1);
480
481          // Erase directory contents first
482          for(filePosition = subDir.first; filePosition; filePosition = fileEntry.next)
483          {
484             f.Seek(filePosition, start);
485             f.Read(&fileEntry, sizeof(EAREntry), 1);
486             f.Seek(fileEntry.nameLen, current);
487             Delete(subDir, filePosition, &fileEntry);
488          }
489          size = sizeof(EAREntry) + entry.nameLen + 2 * sizeof(uint);
490          delete subDir;
491       }
492       else
493          size = sizeof(EAREntry) + entry.nameLen + (entry.cSize ? entry.cSize : entry.size);
494
495       // Unlink this file
496       if(entry.prev) 
497       {
498          f.Seek(entry.prev + OFFSET(EAREntry, next), start);
499          f.Write(&entry.next, sizeof(uint), 1);
500       }
501       if(entry.next) 
502       {
503          f.Seek(entry.next + OFFSET(EAREntry, prev), start);
504          f.Write(&entry.prev, sizeof(uint), 1);
505       }
506       if(dir.last == position) dir.last = entry.prev;
507       if(dir.first == position) dir.first = entry.next;
508
509       AddFreeBlock(position, size);
510       totalSize -= entry.size;
511
512       // Invalidate Buffer
513       // bf.handle = f;
514    }
515
516    File FileOpen(char * name)
517    {
518       File result = null;
519       EARFile file {};
520       if(file)
521       {
522          char fileName[MAX_LOCATION];
523          EAREntry entry { };
524
525          f.Seek(archiveStart + sizeof(EARHeader), start);
526          if(EARGetEntry(f, entry, name, null).isFile)
527          {
528             if(entry.cSize)
529             {
530                byte * uncompressed = new byte[entry.size];
531                if(uncompressed)
532                {
533                   byte * compressed = new byte[entry.cSize];
534                   if(compressed)
535                   {
536                      if(f.Read(compressed, 1, entry.cSize) == entry.cSize)
537                      {
538                         unsigned long destLen = entry.size;
539                         uncompress(uncompressed, &destLen, compressed, entry.cSize);
540                         entry.size = (FileSize)destLen;  // TODO: Support 64 bit file sizes
541                      }
542                      delete compressed;
543                   }
544
545                   file.position = 0;
546                   file.size = entry.size;
547                   file.buffer = uncompressed;
548
549                   result = file;
550                }
551             }
552             else
553             {
554                file.start = f.Tell();
555                file.position = 0;
556                file.size = entry.size;
557                file.f = f;
558                incref file.f;
559                file.f.Seek(file.start, start);
560                result = file;
561             }
562          }
563          if(!result)
564             delete file;
565       }
566       return result;
567    }
568
569    File FileOpenAtPosition(uint position)
570    {
571       EARFile file {};
572       char fileName[MAX_LOCATION];
573       EAREntry entry { };
574       f.Seek(position, start);
575       f.Read(entry, sizeof(EAREntry), 1);
576       /*if(entry.nameLen > 1024)
577          printf("");*/
578       f.Read(fileName, 1, entry.nameLen);
579       if(entry.cSize)
580       {
581          byte * uncompressed = new byte[entry.size];
582          if(uncompressed)
583          {
584             byte * compressed = new byte[entry.cSize];
585             if(compressed)
586             {
587                if(f.Read(compressed, 1, entry.cSize) == entry.cSize)
588                {
589                   unsigned long destLen = entry.size;
590                   uncompress(uncompressed, &destLen, compressed, entry.cSize);
591                   entry.size = (FileSize)destLen;
592                }
593                delete compressed;
594             }
595
596             file.position = 0;
597             file.size = entry.size;
598             file.buffer = uncompressed;
599          }
600       }
601       else
602       {
603          file.start = f.Tell();
604          file.position = 0;
605          file.size = entry.size;
606          file.f = f;
607          file.f.Seek(file.start, start);
608          incref file.f;
609       }
610       return file;
611    }
612
613    FileAttribs FileExists(char * fileName)
614    {
615       FileAttribs result;
616       EAREntry entry { };
617       f.Seek(archiveStart + sizeof(EARHeader), start);
618       result = EARGetEntry(f, entry, fileName, null);
619       return result;
620    }
621
622    void SubtractUsedBlocks()
623    {
624       uint first, last;
625       if(!f.Read(&first, sizeof(uint), 1))
626          return 0;
627 #ifdef _DEBUG
628       if(first > f.GetSize())
629       {
630          printf("Error\n");
631       }
632 #endif
633       f.Read(&last, sizeof(uint), 1);
634
635       while(first)
636       {
637          uint size = 0;
638
639          char fileName[MAX_FILENAME];
640          EAREntry entry { };
641
642          f.Seek(first, start);
643          f.Read(entry, sizeof(EAREntry), 1);
644          if(entry.nameLen < MAX_FILENAME)
645          {
646             f.Read(fileName, 1, entry.nameLen);
647             fileName[entry.nameLen] = 0;
648          }
649          else
650          {
651             fileName[0] = 0;
652             break;
653          }
654
655          size += sizeof(EAREntry) + entry.nameLen;
656
657          if(entry.type == ENTRY_FILE)
658          {
659             size += entry.cSize ? entry.cSize : entry.size;
660          }
661          else if(entry.type == ENTRY_FOLDER)
662          {
663             size += 2 * sizeof(uint);
664             SubtractUsedBlocks();
665          }
666          SubtractBlock(first, size);
667          first = entry.next;
668       }
669    }
670
671    void SetBufferSize(uint bufferSize)
672    {
673       if(f && f._class == class(BufferedFile))
674          ((BufferedFile)f).bufferSize = bufferSize;
675    }
676
677    void SetBufferRead(uint bufferRead)
678    {
679       if(f && f._class == class(BufferedFile))
680          ((BufferedFile)f).bufferRead = bufferRead;
681    }
682 }
683
684 class EARArchiveDir : ArchiveDir
685 {
686    EARArchive archive;
687    uint position;
688    uint first, last;
689    bool readOnly;
690
691    ~EARArchiveDir()
692    {
693       if(!readOnly)
694       {
695          archive.f.Seek(position, start);
696          archive.f.Write(&first, sizeof(uint), 1);
697          archive.f.Write(&last, sizeof(uint), 1);
698          archive.Update();
699       }
700    }
701
702    File FileOpen(char * name)
703    {
704       File result = null;
705       EARFile file {};
706       if(file)
707       {
708          char fileName[MAX_LOCATION];
709          EAREntry entry { };
710
711          archive.f.Seek(position, start);
712          if(EARGetEntry(archive.f, entry, name, null).isFile)
713          {
714             if(entry.cSize)
715             {
716                byte * uncompressed = new byte[entry.size];
717                if(uncompressed)
718                {
719                   byte * compressed = new byte[entry.cSize];
720                   if(compressed)
721                   {
722                      if(archive.f.Read(compressed, 1, entry.cSize) == entry.cSize)
723                      {
724                         unsigned long destLen = entry.size;
725                         uncompress(uncompressed, &destLen, compressed, entry.cSize);
726                         entry.size = (FileSize)destLen;
727                      }
728                      delete compressed;
729                   }
730
731                   file.position = 0;
732                   file.size = entry.size;
733                   file.buffer = uncompressed;
734
735                   result = file;
736                }
737             }
738             else
739             {
740                file.start = archive.f.Tell();
741                file.position = 0;
742                file.size = entry.size;
743                file.f = archive.f;
744                file.f.Seek(file.start, start);
745                incref file.f;
746                result = file;
747             }
748          }
749          if(!result)
750             delete file;
751       }
752       return result;
753    }
754
755    FileAttribs FileExists(char * fileName)
756    {
757       FileAttribs result;
758       EAREntry entry { };
759       archive.f.Seek(position, start);
760       result = EARGetEntry(archive.f, entry, fileName, null);
761       return result;
762    }
763
764    ArchiveDir OpenDirectory(char * name, FileStats stats, ArchiveAddMode addMode)
765    {
766       ArchiveDir result = null;
767       EARArchiveDir dir { readOnly = addMode == readOnlyDir };
768       if(dir)
769       {
770          char namePart[MAX_LOCATION] = "", nameRest[MAX_LOCATION];
771          uint position;
772          EAREntry entry { };
773
774          dir.archive = archive;
775
776          SplitDirectory(name, namePart, nameRest);
777
778          if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
779             strcpy(namePart, DIR_SEPS);
780
781          // Search for directory
782          
783          position = archive.Find(this, namePart, entry);
784          if(position)
785          {
786             // Fail if file of same name already exists
787             if(entry.type == ENTRY_FILE)
788                return null;
789             else
790             {
791                dir.position = position + sizeof(EAREntry) + entry.nameLen;
792                dir.first = 0;
793                dir.last = 0;
794
795                archive.f.Read(&dir.first, sizeof(uint), 1);
796                archive.f.Read(&dir.last, sizeof(uint), 1);
797                
798                result = dir;
799             }               
800          }
801
802          // If directory doesn't exist already
803          if(!result && addMode != refresh)
804          {
805             // Write Header if it's not the root directory
806             EAREntry entry {};
807             uint position;
808
809             entry.nameLen = strlen(namePart);
810             entry.prev = last;
811             entry.next = 0;
812             entry.type = ENTRY_FOLDER;
813             if(!nameRest[0] && stats)
814             {
815                entry.created = (TimeStamp32)stats.created;
816                entry.modified = (TimeStamp32)stats.modified;
817             }
818
819             position = archive.Position(sizeof(EAREntry) + entry.nameLen + 2*sizeof(uint));
820
821             archive.f.Seek(position, start);
822             archive.f.Write(entry, sizeof(EAREntry), 1);
823             archive.f.Write(namePart, entry.nameLen, 1);
824
825             last = position;
826             if(!first) first = position;
827
828             // Update the next pointer of previous entry
829             if(entry.prev)
830             {
831                archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
832                archive.f.Write(&position, sizeof(uint), 1);
833             }
834
835             // Make the dir position point after the header
836             dir.position = position + sizeof(EAREntry) + entry.nameLen;
837          }
838          // Just update the time stamps
839          else if(result && !nameRest[0] && stats)
840          {
841             archive.f.Seek(position + OFFSET(EAREntry, created), start);
842             archive.f.Write(&stats.created, sizeof(uint), 1);
843             archive.f.Write(&stats.modified, sizeof(uint), 1);
844          }
845          result = dir;
846
847          // Open rest of directory...
848          if(result && nameRest[0])
849          {
850             result = dir.OpenDirectory(nameRest, stats, addMode);
851             delete dir;
852          }
853       }
854       return result;
855    }
856
857    bool Delete(char * name)
858    {
859       EAREntry entry { };
860       uint position;
861       char namePart[MAX_LOCATION];
862
863       strcpy(namePart, name);
864       if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
865          strcpy(namePart, DIR_SEPS);
866   
867       position = archive.Find(this, namePart, entry);
868       if(position)
869       {
870          archive.Delete(this, position, entry);
871          return true;
872       }
873       return false;
874    }
875
876    bool Move(char * name, EARArchiveDir to)
877    {
878       bool result = false;
879       if(position != to.position)
880       {
881          EAREntry entry { };
882          uint position = 0;
883          char namePart[MAX_LOCATION];
884
885          strcpy(namePart, name);
886          if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
887             strcpy(namePart, DIR_SEPS);
888
889          position = archive.Find(this, name, entry);
890          if(position)
891          {
892             // Unlink from old directory
893             if(entry.prev)
894             {
895                archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
896                archive.f.Write(&entry.next, sizeof(uint), 1);
897             }
898             if(entry.next) 
899             {
900                archive.f.Seek(entry.next + OFFSET(EAREntry, prev), start);
901                archive.f.Write(&entry.prev, sizeof(uint), 1);
902             }
903             if(last == position) last = entry.prev;
904             if(first == position) first = entry.next;
905
906             // Relink to new directory
907             entry.prev = to.last;
908             entry.next = 0;
909
910             if(entry.prev)
911             {
912                archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
913                archive.f.Write(&position, sizeof(uint), 1);
914             }
915             if(!to.first)
916                to.first = position;
917             to.last = position;
918
919             archive.f.Seek(position + OFFSET(EAREntry, prev), start);
920             archive.f.Write(&entry.prev, sizeof(uint), 1);
921             archive.f.Write(&entry.next, sizeof(uint), 1);
922
923             result = true;
924          }
925       }
926       return result;
927    }
928
929    bool Rename(char * name, char * newName)
930    {
931       bool result = false;
932       EAREntry entry { };
933       uint position = 0;
934       char namePart[MAX_LOCATION];
935
936       strcpy(namePart, name);
937       if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
938          strcpy(namePart, DIR_SEPS);
939
940       position = archive.Find(this, namePart, entry);
941       if(position)
942       {
943          uint dataSize;
944          EAREntry newEntry = entry;
945          uint newPosition = position;
946
947          if(entry.type == ENTRY_FOLDER)
948             dataSize = 2 * sizeof(uint);
949          else
950             dataSize = entry.cSize ? entry.cSize : entry.size;
951       
952          newEntry.nameLen = strlen(newName);
953          if(newEntry.nameLen > entry.nameLen)
954          {
955             // Write new entry
956             newPosition = archive.Position(sizeof(EAREntry) + newEntry.nameLen + dataSize);
957
958             archive.f.Seek(newPosition, start);
959             archive.f.Write(&newEntry, sizeof(EAREntry), 1);
960             archive.f.Write(newName, sizeof(char), newEntry.nameLen);
961
962             // Fix the links
963             if(entry.prev) 
964             {
965                archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
966                archive.f.Write(&newPosition, sizeof(uint), 1);
967             }
968             if(entry.next) 
969             {
970                archive.f.Seek(entry.next + OFFSET(EAREntry, prev), start);
971                archive.f.Write(&newPosition, sizeof(uint), 1);
972             }
973             if(first == position) first = newPosition;
974             if(last == position) last = newPosition;
975          }
976          else
977          {
978             // Change the name
979             archive.f.Seek(position + OFFSET(EAREntry, nameLen), start);
980             archive.f.Write(&newEntry.nameLen, sizeof(uint), 1);
981             archive.f.Seek(position + sizeof(EAREntry), start);
982             archive.f.Write(newName, sizeof(char), newEntry.nameLen);
983
984             // There will be free space at the end of an entry with a shorter new name
985             if(newEntry.nameLen < entry.nameLen)
986                archive.AddFreeBlock(position + sizeof(EAREntry) + newEntry.nameLen + dataSize, entry.nameLen - newEntry.nameLen);            
987          }
988          if(entry.nameLen != newEntry.nameLen)
989          {
990             byte * buffer;
991             uint bufferSize = Min(dataSize, MAX_BUFFERSIZE);
992             buffer = new byte[bufferSize];
993             if(buffer)
994             {
995                uint readPosition = position + sizeof(EAREntry) + entry.nameLen;
996                uint writePosition = newPosition + sizeof(EAREntry) + newEntry.nameLen;
997                uint c;
998
999                for(c = 0; c<dataSize; c += bufferSize)
1000                {
1001                   uint size = (dataSize > c + bufferSize) ? bufferSize : (dataSize - c);
1002
1003                   archive.f.Seek(readPosition + c, start);
1004                   archive.f.Read(buffer, size, 1);
1005
1006                   archive.f.Seek(writePosition + c, start);
1007                   archive.f.Write(buffer, size, 1);
1008                }
1009                delete buffer;
1010             }
1011
1012             if(newEntry.nameLen > entry.nameLen)
1013             {
1014                // Prevent the children to be deleted
1015                if(entry.type == ENTRY_FOLDER)
1016                {
1017                   uint first = 0, last = 0;
1018                   archive.f.Seek(position + sizeof(EAREntry) + entry.nameLen, start);
1019                   archive.f.Write(&first, sizeof(uint), 1);
1020                   archive.f.Write(&last, sizeof(uint), 1);
1021                }
1022
1023                // Delete the old entry
1024                entry.prev = entry.next = 0;
1025                archive.f.Seek(position + sizeof(EAREntry) + entry.nameLen, start);
1026                archive.Delete(this, position, entry);
1027             }
1028          }
1029          result = true;
1030       }
1031       return result;
1032    }
1033
1034    bool AddFromFile(char * name, File input, FileStats stats, ArchiveAddMode addMode, int compression, int * ratio, uint * newPosition)
1035    {
1036       // Search for identical entry
1037       EAREntry oldEntry;
1038       uint oldPosition = archive.Find(this, name, oldEntry);
1039       return _AddFromFileAtPosition(oldEntry, oldPosition, name, input, stats, addMode, compression, ratio, newPosition);
1040    }
1041
1042    bool AddFromFileAtPosition(uint oldPosition, char * name, File input, FileStats stats, ArchiveAddMode addMode, int compression, int * ratio, uint * newPosition)
1043    {
1044       EAREntry oldEntry;
1045       if(oldPosition)
1046       {
1047          if(!archive.f.Seek(oldPosition, start) || !archive.f.Read(oldEntry, sizeof(EAREntry), 1))
1048             return false;
1049       }
1050       return _AddFromFileAtPosition(oldEntry, oldPosition, name, input, stats, addMode, compression, ratio, newPosition);
1051    }
1052
1053    bool _AddFromFileAtPosition(EAREntry oldEntry, uint oldPosition, char * name, File input, FileStats stats, ArchiveAddMode addMode, int compression, int * ratio, uint * newPosition)
1054    {
1055       bool result = false;
1056       bool skip = false;
1057       FileStats oldStats { };
1058
1059       if(oldPosition)
1060       {
1061          oldStats.modified = (TimeStamp)oldEntry.modified;
1062          oldStats.created = (TimeStamp)oldEntry.created;
1063       }
1064       if(stats == null)
1065       {
1066          oldStats.size = input.GetSize();
1067          stats = &oldStats;
1068       }
1069
1070       switch(addMode)
1071       {
1072          // Add all files
1073          case replace:
1074             if(oldPosition)
1075                archive.Delete(this, oldPosition, oldEntry);
1076             break;
1077          // Only updates changed files
1078          case refresh:
1079             if(oldPosition && 
1080                  (oldEntry.size != stats.size || 
1081                   oldEntry.modified != (TimeStamp32)stats.modified || 
1082                   oldEntry.created != (TimeStamp32)stats.created))
1083                   archive.Delete(this, oldPosition, oldEntry);
1084             else
1085                skip = true;
1086             break;
1087          // Only updates changed or new files
1088          case update:
1089             if(oldPosition)
1090             {
1091                if(oldEntry.size != stats.size || 
1092                   oldEntry.modified != (TimeStamp32)stats.modified || 
1093                   oldEntry.created != (TimeStamp32)stats.created)
1094                   archive.Delete(this, oldPosition, oldEntry);
1095                else
1096                   skip = true;
1097             }
1098             break;
1099       }
1100
1101       if(!skip)
1102       {
1103          EAREntry entry { };
1104          uint position, size;
1105          byte * compressed = null;
1106
1107          // Add the file
1108          entry.nameLen = strlen(name);
1109          entry.prev = last;
1110          entry.next = 0;
1111          entry.type = ENTRY_FILE;
1112          
1113          entry.size = stats.size;
1114          entry.created = (TimeStamp32)stats.created;
1115          entry.modified = (TimeStamp32)stats.modified;
1116       
1117          if(compression)
1118          {
1119             byte * uncompressed = new byte[entry.size];
1120             if(uncompressed)
1121             {
1122                if(input.Read(uncompressed, 1, entry.size) == entry.size)
1123                {
1124                   unsigned long destLen = entry.size + entry.size / 1000 + 12;
1125
1126                   compressed = new byte[destLen];
1127                   if(compressed)
1128                   {
1129                      compress2(compressed, &destLen, uncompressed, entry.size, compression);
1130                      entry.cSize = (FileSize)destLen;
1131                   }
1132                }
1133                delete uncompressed;
1134             }
1135          }
1136
1137          if(!compressed)
1138          {
1139             entry.cSize = 0;
1140             if(ratio)
1141                *ratio = 0;
1142          }
1143          else if(ratio)
1144             *ratio = entry.size ? (entry.cSize * 1000 / entry.size) : 0;
1145
1146          // Find block
1147          size = sizeof(EAREntry) + entry.nameLen + (entry.cSize ? entry.cSize : entry.size);
1148          position = archive.Position(size);
1149
1150          // Write Header
1151          if(!archive.f.Seek(position, start) ||
1152             !archive.f.Write(entry, sizeof(EAREntry), 1) ||
1153             !archive.f.Write(name, entry.nameLen, 1))
1154          {
1155             delete compressed;
1156             return false;
1157          }
1158
1159          // Write File Data
1160          if(compressed)
1161          {
1162             if(!archive.f.Write(compressed, 1, entry.cSize))
1163             {
1164                delete compressed;
1165                return false;
1166             }
1167             delete compressed;
1168          }
1169          else
1170          {
1171             byte buffer[8192];
1172             uint c;
1173             int count = 1;
1174             for(c = 0; c<entry.size && count; c+= count)
1175             {
1176                count = input.Read(buffer, 1, sizeof(buffer));
1177                if(!archive.f.Write(buffer, 1, count))
1178                   return false;
1179             }
1180          }
1181
1182          // Update the next pointer previous entry
1183          if(entry.prev)
1184          {
1185             archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
1186             archive.f.Write(&position, sizeof(uint), 1);
1187          }
1188
1189          // Update total size of archive
1190          archive.totalSize += entry.size;
1191
1192          last = position;
1193          if(!first) first = position;
1194          if(newPosition) *newPosition = position;
1195       }
1196       else
1197       {
1198          if(newPosition) *newPosition = 0;
1199       }
1200                
1201       // archive.f.handle = archive.f;
1202       return true;
1203    }
1204 };
1205 #endif
1206
1207 // Directory Description for file listing
1208 class EARDir : struct
1209 {
1210    char path[MAX_LOCATION];
1211    File f;
1212    uint next;
1213 };
1214
1215 class EARFile : File
1216 {
1217    uint position;
1218    uint size;
1219
1220    // -- For reading compressed file (entirely buffered)
1221    byte * buffer;
1222
1223    // -- For reading uncompressed file (not buffered)
1224    File f;
1225    uint start;
1226
1227    ~EARFile()
1228    {
1229       delete buffer;
1230       delete f;
1231    }
1232
1233    void CloseInput()
1234    {
1235       if(f)
1236          f.CloseInput();
1237    }
1238
1239    void CloseOutput()
1240    {
1241       if(f)
1242          f.CloseOutput();
1243    }
1244
1245    int Read(byte * buffer, uint size, uint count)
1246    {
1247       int read = 0;
1248       if(f)
1249          f.Seek(position + start, start);
1250       read = Min(count, (this.size - position) / size);
1251       if(this.buffer)
1252          CopyBytes(buffer, this.buffer + position, read * size);
1253       else
1254          read = f.Read(buffer, size, read);
1255       position += read * size;
1256       return read;
1257    }
1258
1259    int Write(byte * buffer, uint size, uint count)
1260    {
1261       return 0;
1262    }
1263
1264    bool Getc(char * ch)
1265    {
1266       if(position < size)
1267       {
1268          if(buffer)
1269          {
1270             char b = buffer[position++];
1271             if(ch) *ch = b;
1272             return true;
1273          }
1274          else
1275          {
1276             f.Seek(position + start, start);
1277             position++;
1278             return f.Getc(ch);
1279          }
1280       }
1281       return false;
1282    }
1283
1284    bool Putc(char ch)
1285    {
1286       return false;
1287    }
1288
1289    bool Puts(char * string)
1290    {
1291       return false;
1292    }
1293
1294    bool Seek(int pos, FileSeekMode mode)
1295    {
1296       bool result = false;
1297       switch(mode)
1298       {
1299          case start:   
1300             if(pos <= (int)size)
1301             {
1302                position = pos;
1303                if(f)
1304                   result = f.Seek(position + start, start);
1305                else
1306                   result = true;
1307             }
1308             break;
1309          case current:
1310             if(position + pos <= (int)size && (int)position >= -pos)
1311             {
1312                position += pos;
1313                if(f)
1314                   result = f.Seek(position + start, start);
1315                else
1316                   result = true;
1317             }
1318             break;
1319          case end:
1320             if(pos < 0 && -pos <= (int)size)
1321             {
1322                position = size + pos;
1323                if(f)
1324                   f.Seek(position + start, start);
1325                else
1326                   result = true;
1327             }
1328             break;
1329       }
1330       return result;   
1331    }
1332
1333    uint Tell()
1334    {
1335       return position;
1336    }
1337
1338    bool Eof()
1339    {
1340       return position >= size || (f && f.Eof());
1341    }
1342
1343    bool GetSize()
1344    {
1345       return size;
1346    }
1347 };
1348
1349 class EARFileSystem : FileSystem
1350 {
1351    File ::Open(char * archive, char * name, FileOpenMode mode)
1352    {
1353       File result = null;
1354       if(mode == read)
1355       {
1356          EARFile file {};
1357          if(file)
1358          {
1359             char fileName[MAX_LOCATION];
1360             EARHeader header;
1361             File f = EAROpenArchive(archive, &header);
1362             strcpy(fileName, name);
1363    #ifdef ECERE_STATIC
1364             if(!f && archive[0] == ':')
1365             {
1366                f = EAROpenArchive(":", &header);
1367                if(f)
1368                {
1369                   strcpy(fileName, archive + 1);
1370                   PathCat(fileName, name);
1371                }
1372             }
1373    #endif
1374             if(f)
1375             {
1376                EAREntry entry { };
1377                if(EARGetEntry(f, entry, fileName, null).isFile)
1378                {
1379                   if(entry.cSize)
1380                   {
1381                      byte * uncompressed = new byte[entry.size];
1382                      if(uncompressed)
1383                      {
1384                         byte * compressed = new byte[entry.cSize];
1385                         if(compressed)
1386                         {
1387                            if(f.Read(compressed, 1, entry.cSize) == entry.cSize)
1388                            {
1389                               unsigned long destLen = entry.size;
1390                               uncompress(uncompressed, &destLen, compressed, entry.cSize);
1391                               entry.size = (FileSize)destLen;
1392                            }
1393                            delete compressed;
1394                         }
1395
1396                         file.position = 0;
1397                         file.size = entry.size;
1398                         file.buffer = uncompressed;
1399
1400                         result = file;
1401                      }
1402                   }
1403                   else
1404                   {
1405                      file.start = f.Tell();
1406                      file.position = 0;
1407                      file.size = entry.size;
1408                      file.f = f;
1409                      f = null;
1410
1411                      result = file;
1412                   }
1413                }
1414                delete f;
1415             }
1416             if(!result)
1417                delete file;
1418          }
1419       }
1420       return result;
1421    }
1422
1423    FileAttribs ::Exists(char * archive, char * fileName)
1424    {
1425       uint result = 0;
1426       EARHeader header;
1427       File f = EAROpenArchive(archive, &header);
1428       if(f)
1429       {
1430          EAREntry entry { };
1431          result = EARGetEntry(f, entry, fileName, null);
1432          delete f;
1433       }
1434       return result;
1435    }
1436
1437    bool ::GetSize(char * archive, char * fileName, FileSize * size)
1438    {
1439       bool result = false;
1440       EARHeader header;
1441       File f = EAROpenArchive(archive, &header);
1442       if(f)
1443       {
1444          EAREntry entry { };
1445          if(EARGetEntry(f, entry, fileName, null))
1446             *size = entry.size;
1447          delete f;
1448          result = true;
1449       }
1450       return result;
1451    }
1452
1453    bool ::Stats(char * archive, char * fileName, FileStats stats)
1454    {
1455       bool result = false;
1456       EARHeader header;
1457       File f = EAROpenArchive(archive, &header);
1458       if(f)
1459       {
1460          EAREntry entry { };
1461          if(EARGetEntry(f, entry, fileName, null))
1462          {
1463             stats.size = entry.size;
1464             stats.accessed = 0;
1465             stats.modified = (TimeStamp)entry.modified;
1466             stats.created = (TimeStamp)entry.created;
1467             result = true;
1468          }
1469          delete f;
1470       }
1471       return result;
1472    }
1473
1474    void ::FixCase(char * archive, char * name)
1475    {
1476    #ifdef __WIN32__
1477       EARHeader header;
1478       File f = EAROpenArchive(archive, &header);
1479       if(f)
1480       {
1481          EAREntry entry { };
1482          char fileName[MAX_LOCATION] = "";
1483          if(EARGetEntry(f, entry, name, fileName))
1484             strcpy(name, fileName);
1485          delete f;
1486       }
1487    #endif
1488    }
1489
1490    bool ::Find(FileDesc file, char * archive, char * name)
1491    {
1492       bool result = false;
1493       EARDir d {};
1494       if(d)
1495       {
1496          EARHeader header;
1497          File f = EAROpenArchive(archive, &header);
1498          if(f)
1499          {
1500             EAREntry entry { };
1501             if(EARGetEntry(f, entry, name, null).isDirectory)
1502             {
1503                uint first, last;
1504
1505                sprintf(d.path, "<%s>%s", archive, name);
1506                d.f = f;
1507                f.Read(&first, sizeof(uint), 1);
1508                f.Read(&last, sizeof(uint), 1);
1509                d.next = first;
1510                if(d.next)
1511                {
1512                   EAREntry entry { };
1513                   d.f.Seek(d.next, start);
1514                   d.f.Read(entry, sizeof(EAREntry), 1);
1515                   d.f.Read(file.name, 1, entry.nameLen);
1516                   file.name[entry.nameLen] = '\0';
1517                   file.stats.attribs = { isDirectory = (entry.type == ENTRY_FOLDER), isFile = (entry.type != ENTRY_FOLDER) };
1518                   file.stats.accessed = file.stats.modified = (TimeStamp)entry.modified;
1519                   file.stats.created = (TimeStamp)entry.created;
1520                   file.stats.size = entry.size;
1521                   
1522                   strcpy(file.path, d.path);
1523                   PathCat(file.path, file.name);
1524                   d.next = entry.next;
1525
1526                   file.dir = (Dir)d;
1527
1528                   result = true;
1529                }
1530             }
1531             if(!result)
1532                delete f;
1533          }
1534          if(!result)
1535             delete d;
1536       }
1537       return result;
1538    }
1539
1540    bool ::FindNext(FileDesc file)
1541    {
1542       bool result = false;
1543       EARDir d = (EARDir)file.dir;
1544       if(d.next)
1545       {
1546          EAREntry entry { };
1547          d.f.Seek(d.next, start);
1548          d.f.Read(entry, sizeof(EAREntry), 1);
1549          d.f.Read(file.name, 1, entry.nameLen);
1550          file.name[entry.nameLen] = '\0';
1551          file.stats.attribs = FileAttribs { isDirectory = (entry.type == ENTRY_FOLDER), isFile = (entry.type != ENTRY_FOLDER) };
1552          file.stats.accessed = file.stats.modified = (TimeStamp)entry.modified;
1553          file.stats.created = (TimeStamp)entry.created;
1554          file.stats.size = entry.size;
1555
1556          strcpy(file.path, d.path);
1557          PathCat(file.path, file.name);
1558          d.next = entry.next;
1559
1560          result = true;
1561       }
1562       return result;
1563    }
1564
1565    void ::CloseDir(FileDesc file)
1566    {
1567       EARDir d = (EARDir)file.dir;
1568       if(d.f)
1569          delete d.f;
1570       if(d)
1571          delete d;
1572    }
1573
1574 #if !defined(ECERE_NOARCHIVE) && !defined(ECERE_VANILLA)
1575    Archive ::OpenArchive(char * fileName, ArchiveOpenFlags flags)
1576    {
1577       Archive result = null;
1578       EARArchive archive { writeAccess = flags.writeAccess };
1579       if(archive)
1580       {
1581          int try = flags.waitLock ? 10 : 0;
1582          for(; try >= 0; try--)
1583          {
1584             // Check for existing Archive
1585             if((archive.f = fileName ? (flags.buffered ? FileOpenBuffered : FileOpen)(fileName, flags.writeAccess ? readWrite : read) : TempFile { openMode = readWrite } ))
1586             {
1587                EARHeader header;
1588                bool opened = false;
1589                uint archiveSize = 0;
1590                archive.f.Seek(-(int)sizeof(uint), end);
1591                archive.f.Read(&archiveSize, sizeof(uint), 1);
1592                archive.f.Seek(-(int)archiveSize, end);
1593
1594                archive.archiveStart = archive.f.Tell();
1595                if(archive.f.Read(&header, sizeof(EARHeader), 1) == 1 &&
1596                   !strncmp(header.recognition, earRecognition, sizeof(earRecognition)))
1597                   opened = true;
1598
1599                if(!opened)
1600                {
1601                   archive.f.Seek(0, start);
1602                   archive.archiveStart = archive.f.Tell();
1603                   archiveSize = archive.f.GetSize();
1604                   if(archive.f.Read(&header, sizeof(EARHeader), 1) == 1 &&
1605                      !strncmp(header.recognition, earRecognition, sizeof(earRecognition)))
1606                      opened = true;
1607                }
1608
1609                if(opened)
1610                {
1611                   // At this point we recognized the file as a valid eAR archive
1612                   archive.rootDir = archive.archiveStart + sizeof(EARHeader);
1613                   archive.totalSize = header.totalSize;
1614
1615                   archive.f.Seek(archive.rootDir, start);
1616                   if(flags.buffered)
1617                   {
1618                      archive.freeBlocks.Add(FreeBlock { start = archive.rootDir + 2 * sizeof(uint), end = MAXDWORD });
1619                      archive.SubtractUsedBlocks();
1620                   }
1621                   else
1622                   {
1623                      archive.freeBlocks.Add(FreeBlock { start = archive.archiveStart + (archiveSize - sizeof(uint)), end = MAXDWORD });
1624                   }
1625
1626                   /*
1627                   if(!flags.writeAccess)
1628                   {
1629                      delete archive.f;
1630                      archive.f = FileOpen(fileName, readWrite);
1631                   }
1632                   */
1633                   if(archive.f)
1634                   {
1635                      incref archive.f;
1636                      result = archive;
1637                   }
1638                }
1639                break;
1640             }
1641             else if(try > 0)
1642                Sleep(0.01);
1643          }
1644
1645          // This piece of code will create a new archive as a new file or at the footer
1646          // of an existing file.
1647          if(!result && flags.writeAccess)
1648          {
1649             // If the file doesn't exist, create it
1650             if(!archive.f)
1651             {
1652                archive.f = FileOpen(fileName, writeRead);
1653                delete archive.f;
1654                archive.f = (flags.buffered ? FileOpenBuffered : FileOpen)(fileName, readWrite);
1655             }
1656             if(archive.f)
1657             {
1658                EARHeader header
1659                {
1660                   EAR_RECOGNITION,
1661                   MDWORD(0, 1)
1662                };
1663                     
1664                archive.f.Seek(0, end);
1665          
1666                archive.archiveStart = archive.f.Tell();
1667                archive.freeBlocks.Add(FreeBlock { start = archive.archiveStart + sizeof(EARHeader), end = MAXDWORD });
1668
1669                // Write Header
1670                archive.f.Write(&header, sizeof(EARHeader), 1);
1671                {
1672                   uint first = 0, last = 0;
1673                   archive.f.Write(&first, sizeof(first), 1);
1674                   archive.f.Write(&last, sizeof(last), 1);
1675                }
1676
1677                archive.rootDir = 0;
1678                incref archive.f;
1679                result = archive;
1680             }
1681          }
1682          if(archive.f && flags.writeAccess && flags.exclusive && !archive.f.Lock(flags.exclusive ? exclusive : shared, 0, 0, flags.waitLock))
1683             result = null;
1684          if(!result)
1685          {
1686             delete archive.f;
1687             delete archive;
1688          }
1689          else
1690          {
1691             // archive.f.handle = archive.f;
1692          }
1693       }
1694       return result;
1695    }
1696 #endif
1697    bool ::QuerySize(char * archive, FileSize * size)
1698    {
1699       bool result = false;
1700       EARHeader header;
1701       File f = EAROpenArchive(archive, &header);
1702       if(f)
1703       {
1704          *size = header.totalSize;
1705          result = true;
1706          delete f;
1707       }
1708       return result;
1709    }
1710 };