10 #define OFFSET(s, m) ((uint) (&((s *) 0)->m))
11 #define MDWORD(a,b) ((((uint32)((uint16)(b)))<<16)+((uint16)(a)))
13 #define EAR_RECOGNITION { 'e', 'A', 'R', 228, 11, 12, 3, 0 }
15 static byte earRecognition[] = EAR_RECOGNITION;
17 static class FreeBlock : struct
23 static struct EARHeader
25 byte recognition[sizeof(earRecognition)] __attribute__((packed));
26 uint version  __attribute__((packed));
27 FileSize totalSize  __attribute__((packed));
30 static enum EAREntryType { ENTRY_FILE = 1, ENTRY_FOLDER = 2 };
32 static struct EAREntry
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
42 static File EAROpenArchive(char * archive, EARHeader header)
47 char moduleName[MAX_LOCATION];
48 if(LocateModule(archive + 1, moduleName))
49 f = FileOpen(moduleName, read);
52 f = FileOpen(archive, read);
57 // First attempt to treat this as an archive file
58 if(f.Read(header, sizeof(EARHeader), 1) == 1 &&
59 !strncmp(header.recognition, earRecognition, sizeof(earRecognition)))
62 // Then try to see if an archive is at the end of the file
63 f.Seek(-(int)sizeof(uint), end);
64 f.Read(&archiveSize, sizeof(uint), 1);
65 f.Seek(-(int)archiveSize, end);
66 if(f.Read(header, sizeof(EARHeader), 1) == 1 &&
67 !strncmp(header.recognition, earRecognition, sizeof(earRecognition)))
75 static FileAttribs EARGetEntry(File f, EAREntry entry, char * name, char * path)
77 uint first = 0, last = 0;
79 return FileAttribs { isDirectory = true };
80 if(!f.Read(&first, sizeof(uint), 1))
82 f.Read(&last, sizeof(uint), 1);
85 char namePart[MAX_FILENAME], nameRest[MAX_LOCATION];
86 SplitDirectory(name, namePart, nameRest);
88 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
89 strcpy(namePart, DIR_SEPS);
94 char fileName[MAX_FILENAME];
96 f.Read(entry, sizeof(EAREntry), 1);
97 f.Read(fileName, 1, entry.nameLen);
98 fileName[entry.nameLen] = '\0';
100 if(!strcmp(fileName, "/") || !strcmp(fileName, "\\"))
101 strcpy(fileName, DIR_SEPS);
103 if(!fstrcmp(fileName, namePart))
106 PathCat(path, fileName);
108 return EARGetEntry(f, entry, nameRest, path);
110 return (entry.type == ENTRY_FILE) ? FileAttribs { isFile = true } : FileAttribs { isDirectory = true };
113 f.Seek(entry.next, start);
118 return FileAttribs { };
121 #if !defined(ECERE_NOARCHIVE) && !defined(ECERE_VANILLA)
122 class EARArchive : Archive
125 //BufferedFile bf { };
126 // char path[MAX_LOCATION];
138 end = ((FreeBlock)freeBlocks.last).start;
141 f.Seek(archiveStart + OFFSET(EARHeader, totalSize), start);
142 f.Write(&totalSize, sizeof(uint), 1);
151 f.Write(&end, sizeof(uint), 1);
152 f.Truncate(archiveStart + end);
160 if(f && rootDir && writeAccess)
164 archiveStart += Update();
169 f.Unlock(0, 0, true);
173 /*if(rootDir && writeAccess)
175 // Fix the size of the archive
176 FileTruncate(path, archiveStart);
179 freeBlocks.Free(null);
188 ArchiveDir OpenDirectory(char * name, FileStats stats, ArchiveAddMode addMode)
190 ArchiveDir result = null;
191 EARArchiveDir dir { readOnly = addMode == readOnlyDir };
194 char namePart[MAX_LOCATION] = "", nameRest[MAX_LOCATION];
199 strcpy(nameRest, name);
201 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
202 strcpy(namePart, DIR_SEPS);
204 // Search for directory
207 dir.position = rootDir;
208 if(f.Seek(dir.position, start))
213 f.Read(&dir.first, sizeof(uint), 1);
214 f.Read(&dir.last, sizeof(uint), 1);
220 // If directory doesn't exist already
221 if(!result && addMode != refresh)
223 rootDir = Position(2*sizeof(uint));
224 dir.position = rootDir;
229 // Open rest of directory...
230 if(result && nameRest[0])
232 result = dir.OpenDirectory(nameRest, stats, addMode);
239 uint Position(uint size)
242 for(block = freeBlocks.first; block; block = block.next)
244 if(block.end - block.start + 1 >= size)
246 uint position = block.start;
247 if(block.end - block.start + 1 == size)
248 freeBlocks.Delete(block);
257 bool DefragOffset(uint * offset)
261 for(block = freeBlocks.first; block; block = block.next)
263 if(*offset > block.start)
264 subtract += block.end - block.start + 1;
276 #define MAX_BUFFERSIZE 0x400000
278 void Defrag(uint dirPosition)
280 // Update all offsets within the files
281 uint first = 0, last = 0;
282 uint position = 0, next = 0;
284 f.Seek(dirPosition, start);
285 f.Read(&first, sizeof(uint), 1);
286 f.Read(&last, sizeof(uint), 1);
290 if(first && DefragOffset(&first))
292 if(f.Seek(dirPosition, start))
293 f.Write(&first, sizeof(uint), 1);
295 if(last && DefragOffset(&last))
297 if(f.Seek(dirPosition + sizeof(uint), start))
298 f.Write(&last, sizeof(uint), 1);
301 for(; position; position = next)
305 if(f.Seek(position, start) && f.Read(entry, sizeof(EAREntry), 1))
309 if(entry.prev && DefragOffset(&entry.prev))
311 f.Seek(position + OFFSET(EAREntry, prev), start);
312 f.Write(&entry.prev, sizeof(uint), 1);
314 if(entry.next && DefragOffset(&entry.next))
316 f.Seek(position + OFFSET(EAREntry, next), start);
317 f.Write(&entry.next, sizeof(uint), 1);
320 if(entry.type == ENTRY_FOLDER)
321 Defrag(position + sizeof(EAREntry) + entry.nameLen);
327 // Move all the blocks
328 if(dirPosition == rootDir)
331 byte * buffer = null;
332 FreeBlock block, nextBlock;
333 for(block = freeBlocks.first; block && block.next; block = nextBlock)
337 nextBlock = block.next;
338 dataSize = nextBlock.start - (block.end + 1);
340 if(dataSize > bufferSize && (!bufferSize || bufferSize < MAX_BUFFERSIZE))
342 bufferSize = Min(dataSize, MAX_BUFFERSIZE);
343 buffer = renew buffer byte[bufferSize];
346 for(c = 0; c<dataSize; c += bufferSize)
348 uint size = (dataSize > c + bufferSize) ? bufferSize : (dataSize - c);
350 // Read block of data
351 f.Seek((block.end + 1) + c, start);
352 f.Read(buffer, size, 1);
354 // Write block of data
355 f.Seek(block.start + c, start);
356 f.Write(buffer, size, 1);
359 nextBlock.start -= (block.end - block.start) + 1;
361 freeBlocks.Delete(block);
367 uint Find(EARArchiveDir directory, char * namePart, EAREntry entry)
370 for(position = directory.first; position; position = entry.next)
372 char fileName[MAX_FILENAME];
374 if(f.Seek(position, start) && f.Read(entry, sizeof(EAREntry), 1))
376 if(entry.nameLen > MAX_FILENAME)
377 return 0; // CORRUPTION ERROR
378 f.Read(fileName, 1, entry.nameLen);
379 fileName[entry.nameLen] = '\0';
381 if(!strcmp(fileName, "/") || !strcmp(fileName, "\\"))
382 strcpy(fileName, DIR_SEPS);
384 if(!fstrcmp(fileName, namePart))
388 return 0; // ERROR OUT OF SPACE?
393 void AddFreeBlock(uint position, uint size)
395 FreeBlock block, prevBlock, nextBlock = null;
397 // Find the previous and next free block
399 for(block = freeBlocks.first; block; block = block.next)
400 if(block.end < position)
408 // Try to merge with previous block
409 if(prevBlock && prevBlock.end + 1 == position)
411 prevBlock.end += size;
412 // Try to merge with next block as well
413 if(nextBlock && nextBlock.start == prevBlock.end + 1)
415 prevBlock.end = nextBlock.end;
416 freeBlocks.Delete(nextBlock);
419 // Try to merge with next block
420 else if(nextBlock && nextBlock.start == position + size)
422 nextBlock.start = position;
424 // This free block is not connected to any other block
427 freeBlocks.Insert(prevBlock, FreeBlock { start = position, end = position + size - 1 });
431 void SubtractBlock(uint start, uint size)
434 for(block = freeBlocks.first; block; block = block.next)
436 if(block.end >= start - 1L && block.start <= start + size)
438 if(block.end > start + size && block.start < start - 1L)
440 FreeBlock newBlock { start = start + size, end = block.end };
441 block.end = start - 1L;
442 freeBlocks.Insert(block, newBlock);
444 else if(block.end > start + size)
446 block.start = start + size;
448 else if(block.start < start - 1L)
450 block.end = start - 1L;
454 freeBlocks.Remove(block);
462 void Delete(EARArchiveDir dir, uint position, EAREntry entry)
465 if(entry.type == ENTRY_FOLDER)
467 EARArchiveDir subDir {};
471 subDir.position = dir.position;
472 f.Read(&subDir.first, sizeof(uint), 1);
473 f.Read(&subDir.last, sizeof(uint), 1);
475 // Erase directory contents first
476 for(filePosition = subDir.first; filePosition; filePosition = fileEntry.next)
478 f.Seek(filePosition, start);
479 f.Read(&fileEntry, sizeof(EAREntry), 1);
480 f.Seek(fileEntry.nameLen, current);
481 Delete(subDir, filePosition, &fileEntry);
483 size = sizeof(EAREntry) + entry.nameLen + 2 * sizeof(uint);
487 size = sizeof(EAREntry) + entry.nameLen + (entry.cSize ? entry.cSize : entry.size);
492 f.Seek(entry.prev + OFFSET(EAREntry, next), start);
493 f.Write(&entry.next, sizeof(uint), 1);
497 f.Seek(entry.next + OFFSET(EAREntry, prev), start);
498 f.Write(&entry.prev, sizeof(uint), 1);
500 if(dir.last == position) dir.last = entry.prev;
501 if(dir.first == position) dir.first = entry.next;
503 AddFreeBlock(position, size);
504 totalSize -= entry.size;
510 File FileOpen(char * name)
516 char fileName[MAX_LOCATION];
519 f.Seek(archiveStart + sizeof(EARHeader), start);
520 if(EARGetEntry(f, entry, name, null).isFile)
524 byte * uncompressed = new byte[entry.size];
527 byte * compressed = new byte[entry.cSize];
530 if(f.Read(compressed, 1, entry.cSize) == entry.cSize)
531 uncompress(uncompressed, (uint *)&entry.size, compressed, entry.cSize);
536 file.size = entry.size;
537 file.buffer = uncompressed;
544 file.start = f.Tell();
546 file.size = entry.size;
549 file.f.Seek(file.start, start);
559 File FileOpenAtPosition(uint position)
562 char fileName[MAX_LOCATION];
564 f.Seek(position, start);
565 f.Read(entry, sizeof(EAREntry), 1);
566 /*if(entry.nameLen > 1024)
568 f.Read(fileName, 1, entry.nameLen);
571 byte * uncompressed = new byte[entry.size];
574 byte * compressed = new byte[entry.cSize];
577 if(f.Read(compressed, 1, entry.cSize) == entry.cSize)
578 uncompress(uncompressed, (uint *)&entry.size, compressed, entry.cSize);
583 file.size = entry.size;
584 file.buffer = uncompressed;
589 file.start = f.Tell();
591 file.size = entry.size;
593 file.f.Seek(file.start, start);
599 FileAttribs FileExists(char * fileName)
603 f.Seek(archiveStart + sizeof(EARHeader), start);
604 result = EARGetEntry(f, entry, fileName, null);
608 void SubtractUsedBlocks()
611 if(!f.Read(&first, sizeof(uint), 1))
614 if(first > f.GetSize())
619 f.Read(&last, sizeof(uint), 1);
625 char fileName[MAX_FILENAME];
628 f.Seek(first, start);
629 f.Read(entry, sizeof(EAREntry), 1);
630 if(entry.nameLen < MAX_FILENAME)
632 f.Read(fileName, 1, entry.nameLen);
633 fileName[entry.nameLen] = 0;
641 size += sizeof(EAREntry) + entry.nameLen;
643 if(entry.type == ENTRY_FILE)
645 size += entry.cSize ? entry.cSize : entry.size;
647 else if(entry.type == ENTRY_FOLDER)
649 size += 2 * sizeof(uint);
650 SubtractUsedBlocks();
652 SubtractBlock(first, size);
657 void SetBufferSize(uint bufferSize)
659 if(f && f._class == class(BufferedFile))
660 ((BufferedFile)f).bufferSize = bufferSize;
663 void SetBufferRead(uint bufferRead)
665 if(f && f._class == class(BufferedFile))
666 ((BufferedFile)f).bufferRead = bufferRead;
670 class EARArchiveDir : ArchiveDir
681 archive.f.Seek(position, start);
682 archive.f.Write(&first, sizeof(uint), 1);
683 archive.f.Write(&last, sizeof(uint), 1);
688 File FileOpen(char * name)
694 char fileName[MAX_LOCATION];
697 archive.f.Seek(position, start);
698 if(EARGetEntry(archive.f, entry, name, null).isFile)
702 byte * uncompressed = new byte[entry.size];
705 byte * compressed = new byte[entry.cSize];
708 if(archive.f.Read(compressed, 1, entry.cSize) == entry.cSize)
709 uncompress(uncompressed, (uint *)&entry.size, compressed, entry.cSize);
714 file.size = entry.size;
715 file.buffer = uncompressed;
722 file.start = archive.f.Tell();
724 file.size = entry.size;
726 file.f.Seek(file.start, start);
737 FileAttribs FileExists(char * fileName)
741 archive.f.Seek(position, start);
742 result = EARGetEntry(archive.f, entry, fileName, null);
746 ArchiveDir OpenDirectory(char * name, FileStats stats, ArchiveAddMode addMode)
748 ArchiveDir result = null;
749 EARArchiveDir dir { readOnly = addMode == readOnlyDir };
752 char namePart[MAX_LOCATION] = "", nameRest[MAX_LOCATION];
756 dir.archive = archive;
758 SplitDirectory(name, namePart, nameRest);
760 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
761 strcpy(namePart, DIR_SEPS);
763 // Search for directory
765 position = archive.Find(this, namePart, entry);
768 // Fail if file of same name already exists
769 if(entry.type == ENTRY_FILE)
773 dir.position = position + sizeof(EAREntry) + entry.nameLen;
777 archive.f.Read(&dir.first, sizeof(uint), 1);
778 archive.f.Read(&dir.last, sizeof(uint), 1);
784 // If directory doesn't exist already
785 if(!result && addMode != refresh)
787 // Write Header if it's not the root directory
791 entry.nameLen = strlen(namePart);
794 entry.type = ENTRY_FOLDER;
795 if(!nameRest[0] && stats)
797 entry.created = (TimeStamp32)stats.created;
798 entry.modified = (TimeStamp32)stats.modified;
801 position = archive.Position(sizeof(EAREntry) + entry.nameLen + 2*sizeof(uint));
803 archive.f.Seek(position, start);
804 archive.f.Write(entry, sizeof(EAREntry), 1);
805 archive.f.Write(namePart, entry.nameLen, 1);
808 if(!first) first = position;
810 // Update the next pointer of previous entry
813 archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
814 archive.f.Write(&position, sizeof(uint), 1);
817 // Make the dir position point after the header
818 dir.position = position + sizeof(EAREntry) + entry.nameLen;
820 // Just update the time stamps
821 else if(result && !nameRest[0] && stats)
823 archive.f.Seek(position + OFFSET(EAREntry, created), start);
824 archive.f.Write(&stats.created, sizeof(uint), 1);
825 archive.f.Write(&stats.modified, sizeof(uint), 1);
829 // Open rest of directory...
830 if(result && nameRest[0])
832 result = dir.OpenDirectory(nameRest, stats, addMode);
839 bool Delete(char * name)
843 char namePart[MAX_LOCATION];
845 strcpy(namePart, name);
846 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
847 strcpy(namePart, DIR_SEPS);
849 position = archive.Find(this, namePart, entry);
852 archive.Delete(this, position, entry);
858 bool Move(char * name, EARArchiveDir to)
861 if(position != to.position)
865 char namePart[MAX_LOCATION];
867 strcpy(namePart, name);
868 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
869 strcpy(namePart, DIR_SEPS);
871 position = archive.Find(this, name, entry);
874 // Unlink from old directory
877 archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
878 archive.f.Write(&entry.next, sizeof(uint), 1);
882 archive.f.Seek(entry.next + OFFSET(EAREntry, prev), start);
883 archive.f.Write(&entry.prev, sizeof(uint), 1);
885 if(last == position) last = entry.prev;
886 if(first == position) first = entry.next;
888 // Relink to new directory
889 entry.prev = to.last;
894 archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
895 archive.f.Write(&position, sizeof(uint), 1);
901 archive.f.Seek(position + OFFSET(EAREntry, prev), start);
902 archive.f.Write(&entry.prev, sizeof(uint), 1);
903 archive.f.Write(&entry.next, sizeof(uint), 1);
911 bool Rename(char * name, char * newName)
916 char namePart[MAX_LOCATION];
918 strcpy(namePart, name);
919 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
920 strcpy(namePart, DIR_SEPS);
922 position = archive.Find(this, namePart, entry);
926 EAREntry newEntry = entry;
927 uint newPosition = position;
929 if(entry.type == ENTRY_FOLDER)
930 dataSize = 2 * sizeof(uint);
932 dataSize = entry.cSize ? entry.cSize : entry.size;
934 newEntry.nameLen = strlen(newName);
935 if(newEntry.nameLen > entry.nameLen)
938 newPosition = archive.Position(sizeof(EAREntry) + newEntry.nameLen + dataSize);
940 archive.f.Seek(newPosition, start);
941 archive.f.Write(&newEntry, sizeof(EAREntry), 1);
942 archive.f.Write(newName, sizeof(char), newEntry.nameLen);
947 archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
948 archive.f.Write(&newPosition, sizeof(uint), 1);
952 archive.f.Seek(entry.next + OFFSET(EAREntry, prev), start);
953 archive.f.Write(&newPosition, sizeof(uint), 1);
955 if(first == position) first = newPosition;
956 if(last == position) last = newPosition;
961 archive.f.Seek(position + OFFSET(EAREntry, nameLen), start);
962 archive.f.Write(&newEntry.nameLen, sizeof(uint), 1);
963 archive.f.Seek(position + sizeof(EAREntry), start);
964 archive.f.Write(newName, sizeof(char), newEntry.nameLen);
966 // There will be free space at the end of an entry with a shorter new name
967 if(newEntry.nameLen < entry.nameLen)
968 archive.AddFreeBlock(position + sizeof(EAREntry) + newEntry.nameLen + dataSize, entry.nameLen - newEntry.nameLen);
970 if(entry.nameLen != newEntry.nameLen)
973 uint bufferSize = Min(dataSize, MAX_BUFFERSIZE);
974 buffer = new byte[bufferSize];
977 uint readPosition = position + sizeof(EAREntry) + entry.nameLen;
978 uint writePosition = newPosition + sizeof(EAREntry) + newEntry.nameLen;
981 for(c = 0; c<dataSize; c += bufferSize)
983 uint size = (dataSize > c + bufferSize) ? bufferSize : (dataSize - c);
985 archive.f.Seek(readPosition + c, start);
986 archive.f.Read(buffer, size, 1);
988 archive.f.Seek(writePosition + c, start);
989 archive.f.Write(buffer, size, 1);
994 if(newEntry.nameLen > entry.nameLen)
996 // Prevent the children to be deleted
997 if(entry.type == ENTRY_FOLDER)
999 uint first = 0, last = 0;
1000 archive.f.Seek(position + sizeof(EAREntry) + entry.nameLen, start);
1001 archive.f.Write(&first, sizeof(uint), 1);
1002 archive.f.Write(&last, sizeof(uint), 1);
1005 // Delete the old entry
1006 entry.prev = entry.next = 0;
1007 archive.f.Seek(position + sizeof(EAREntry) + entry.nameLen, start);
1008 archive.Delete(this, position, entry);
1016 bool AddFromFile(char * name, File input, FileStats stats, ArchiveAddMode addMode, int compression, int * ratio, uint * newPosition)
1018 // Search for identical entry
1020 uint oldPosition = archive.Find(this, name, oldEntry);
1021 return _AddFromFileAtPosition(oldEntry, oldPosition, name, input, stats, addMode, compression, ratio, newPosition);
1024 bool AddFromFileAtPosition(uint oldPosition, char * name, File input, FileStats stats, ArchiveAddMode addMode, int compression, int * ratio, uint * newPosition)
1029 if(!archive.f.Seek(oldPosition, start) || !archive.f.Read(oldEntry, sizeof(EAREntry), 1))
1032 return _AddFromFileAtPosition(oldEntry, oldPosition, name, input, stats, addMode, compression, ratio, newPosition);
1035 bool _AddFromFileAtPosition(EAREntry oldEntry, uint oldPosition, char * name, File input, FileStats stats, ArchiveAddMode addMode, int compression, int * ratio, uint * newPosition)
1037 bool result = false;
1039 FileStats oldStats { };
1043 oldStats.modified = (TimeStamp)oldEntry.modified;
1044 oldStats.created = (TimeStamp)oldEntry.created;
1048 oldStats.size = input.GetSize();
1057 archive.Delete(this, oldPosition, oldEntry);
1059 // Only updates changed files
1062 (oldEntry.size != stats.size ||
1063 oldEntry.modified != (TimeStamp32)stats.modified ||
1064 oldEntry.created != (TimeStamp32)stats.created))
1065 archive.Delete(this, oldPosition, oldEntry);
1069 // Only updates changed or new files
1073 if(oldEntry.size != stats.size ||
1074 oldEntry.modified != (TimeStamp32)stats.modified ||
1075 oldEntry.created != (TimeStamp32)stats.created)
1076 archive.Delete(this, oldPosition, oldEntry);
1086 uint position, size;
1087 byte * compressed = null;
1090 entry.nameLen = strlen(name);
1093 entry.type = ENTRY_FILE;
1095 entry.size = stats.size;
1096 entry.created = (TimeStamp32)stats.created;
1097 entry.modified = (TimeStamp32)stats.modified;
1101 byte * uncompressed = new byte[entry.size];
1104 if(input.Read(uncompressed, 1, entry.size) == entry.size)
1106 entry.cSize = entry.size + entry.size / 1000 + 12;
1108 compressed = new byte[entry.cSize];
1110 compress2(compressed, (uint *)&entry.cSize, uncompressed, entry.size, compression);
1112 delete uncompressed;
1123 *ratio = entry.size ? (entry.cSize * 1000 / entry.size) : 0;
1126 size = sizeof(EAREntry) + entry.nameLen + (entry.cSize ? entry.cSize : entry.size);
1127 position = archive.Position(size);
1130 if(!archive.f.Seek(position, start) ||
1131 !archive.f.Write(entry, sizeof(EAREntry), 1) ||
1132 !archive.f.Write(name, entry.nameLen, 1))
1141 if(!archive.f.Write(compressed, 1, entry.cSize))
1153 for(c = 0; c<entry.size && count; c+= count)
1155 count = input.Read(buffer, 1, sizeof(buffer));
1156 if(!archive.f.Write(buffer, 1, count))
1161 // Update the next pointer previous entry
1164 archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
1165 archive.f.Write(&position, sizeof(uint), 1);
1168 // Update total size of archive
1169 archive.totalSize += entry.size;
1172 if(!first) first = position;
1173 if(newPosition) *newPosition = (bool)position;
1177 if(newPosition) *newPosition = 0;
1180 // archive.f.handle = archive.f;
1186 // Directory Description for file listing
1187 class EARDir : struct
1189 char path[MAX_LOCATION];
1194 class EARFile : File
1199 // -- For reading compressed file (entirely buffered)
1202 // -- For reading uncompressed file (not buffered)
1224 int Read(byte * buffer, uint size, uint count)
1228 f.Seek(position + start, start);
1229 read = Min(count, (this.size - position) / size);
1231 CopyBytes(buffer, this.buffer + position, read * size);
1233 read = f.Read(buffer, size, read);
1234 position += read * size;
1238 int Write(byte * buffer, uint size, uint count)
1243 bool Getc(char * ch)
1249 char b = buffer[position++];
1255 f.Seek(position + start, start);
1268 bool Puts(char * string)
1273 bool Seek(int pos, FileSeekMode mode)
1275 bool result = false;
1279 if(pos <= (int)size)
1283 result = f.Seek(position + start, start);
1289 if(position + pos <= (int)size && (int)position >= -pos)
1293 result = f.Seek(position + start, start);
1299 if(pos < 0 && -pos <= (int)size)
1301 position = size + pos;
1303 f.Seek(position + start, start);
1319 return position >= size || (f && f.Eof());
1328 class EARFileSystem : FileSystem
1330 File ::Open(char * archive, char * name, FileOpenMode mode)
1338 char fileName[MAX_LOCATION];
1340 File f = EAROpenArchive(archive, &header);
1341 strcpy(fileName, name);
1343 if(!f && archive[0] == ':')
1345 f = EAROpenArchive(":", &header);
1348 strcpy(fileName, archive + 1);
1349 PathCat(fileName, name);
1356 if(EARGetEntry(f, entry, fileName, null).isFile)
1360 byte * uncompressed = new byte[entry.size];
1363 byte * compressed = new byte[entry.cSize];
1366 if(f.Read(compressed, 1, entry.cSize) == entry.cSize)
1367 uncompress(uncompressed, (uint *)&entry.size, compressed, entry.cSize);
1372 file.size = entry.size;
1373 file.buffer = uncompressed;
1380 file.start = f.Tell();
1382 file.size = entry.size;
1398 FileAttribs ::Exists(char * archive, char * fileName)
1402 File f = EAROpenArchive(archive, &header);
1406 result = EARGetEntry(f, entry, fileName, null);
1412 bool ::GetSize(char * archive, char * fileName, FileSize * size)
1414 bool result = false;
1416 File f = EAROpenArchive(archive, &header);
1420 if(EARGetEntry(f, entry, fileName, null))
1428 bool ::Stats(char * archive, char * fileName, FileStats stats)
1430 bool result = false;
1432 File f = EAROpenArchive(archive, &header);
1436 if(EARGetEntry(f, entry, fileName, null))
1438 stats.size = entry.size;
1440 stats.modified = (TimeStamp)entry.modified;
1441 stats.created = (TimeStamp)entry.created;
1449 void ::FixCase(char * archive, char * name)
1453 File f = EAROpenArchive(archive, &header);
1457 char fileName[MAX_LOCATION] = "";
1458 if(EARGetEntry(f, entry, name, fileName))
1459 strcpy(name, fileName);
1465 bool ::Find(FileDesc file, char * archive, char * name)
1467 bool result = false;
1472 File f = EAROpenArchive(archive, &header);
1476 if(EARGetEntry(f, entry, name, null).isDirectory)
1480 sprintf(d.path, "<%s>%s", archive, name);
1482 f.Read(&first, sizeof(uint), 1);
1483 f.Read(&last, sizeof(uint), 1);
1488 d.f.Seek(d.next, start);
1489 d.f.Read(entry, sizeof(EAREntry), 1);
1490 d.f.Read(file.name, 1, entry.nameLen);
1491 file.name[entry.nameLen] = '\0';
1492 file.stats.attribs = { isDirectory = (entry.type == ENTRY_FOLDER), isFile = (entry.type != ENTRY_FOLDER) };
1493 file.stats.accessed = file.stats.modified = (TimeStamp)entry.modified;
1494 file.stats.created = (TimeStamp)entry.created;
1495 file.stats.size = entry.size;
1497 strcpy(file.path, d.path);
1498 PathCat(file.path, file.name);
1499 d.next = entry.next;
1515 bool ::FindNext(FileDesc file)
1517 bool result = false;
1518 EARDir d = (EARDir)file.dir;
1522 d.f.Seek(d.next, start);
1523 d.f.Read(entry, sizeof(EAREntry), 1);
1524 d.f.Read(file.name, 1, entry.nameLen);
1525 file.name[entry.nameLen] = '\0';
1526 file.stats.attribs = FileAttribs { isDirectory = (entry.type == ENTRY_FOLDER), isFile = (entry.type != ENTRY_FOLDER) };
1527 file.stats.accessed = file.stats.modified = (TimeStamp)entry.modified;
1528 file.stats.created = (TimeStamp)entry.created;
1529 file.stats.size = entry.size;
1531 strcpy(file.path, d.path);
1532 PathCat(file.path, file.name);
1533 d.next = entry.next;
1540 void ::CloseDir(FileDesc file)
1542 EARDir d = (EARDir)file.dir;
1549 #if !defined(ECERE_NOARCHIVE) && !defined(ECERE_VANILLA)
1550 Archive ::OpenArchive(char * fileName, ArchiveOpenFlags flags)
1552 Archive result = null;
1553 EARArchive archive { writeAccess = flags.writeAccess };
1556 int try = flags.waitLock ? 10 : 0;
1557 for(; try >= 0; try--)
1559 // Check for existing Archive
1560 if((archive.f = fileName ? (flags.buffered ? FileOpenBuffered : FileOpen)(fileName, flags.writeAccess ? readWrite : read) : TempFile { openMode = readWrite } ))
1563 bool opened = false;
1564 uint archiveSize = 0;
1565 archive.f.Seek(-(int)sizeof(uint), end);
1566 archive.f.Read(&archiveSize, sizeof(uint), 1);
1567 archive.f.Seek(-(int)archiveSize, end);
1569 archive.archiveStart = archive.f.Tell();
1570 if(archive.f.Read(&header, sizeof(EARHeader), 1) == 1 &&
1571 !strncmp(header.recognition, earRecognition, sizeof(earRecognition)))
1576 archive.f.Seek(0, start);
1577 archive.archiveStart = archive.f.Tell();
1578 archiveSize = archive.f.GetSize();
1579 if(archive.f.Read(&header, sizeof(EARHeader), 1) == 1 &&
1580 !strncmp(header.recognition, earRecognition, sizeof(earRecognition)))
1586 // At this point we recognized the file as a valid eAR archive
1587 archive.rootDir = archive.archiveStart + sizeof(EARHeader);
1588 archive.totalSize = header.totalSize;
1590 archive.f.Seek(archive.rootDir, start);
1593 archive.freeBlocks.Add(FreeBlock { start = archive.rootDir + 2 * sizeof(uint), end = MAXDWORD });
1594 archive.SubtractUsedBlocks();
1598 archive.freeBlocks.Add(FreeBlock { start = archive.archiveStart + (archiveSize - sizeof(uint)), end = MAXDWORD });
1602 if(!flags.writeAccess)
1605 archive.f = FileOpen(fileName, readWrite);
1620 // This piece of code will create a new archive as a new file or at the footer
1621 // of an existing file.
1622 if(!result && flags.writeAccess)
1624 // If the file doesn't exist, create it
1627 archive.f = FileOpen(fileName, writeRead);
1629 archive.f = (flags.buffered ? FileOpenBuffered : FileOpen)(fileName, readWrite);
1639 archive.f.Seek(0, end);
1641 archive.archiveStart = archive.f.Tell();
1642 archive.freeBlocks.Add(FreeBlock { start = archive.archiveStart + sizeof(EARHeader), end = MAXDWORD });
1645 archive.f.Write(&header, sizeof(EARHeader), 1);
1647 uint first = 0, last = 0;
1648 archive.f.Write(&first, sizeof(first), 1);
1649 archive.f.Write(&last, sizeof(last), 1);
1652 archive.rootDir = 0;
1657 if(archive.f && flags.writeAccess && flags.exclusive && !archive.f.Lock(flags.exclusive ? exclusive : shared, 0, 0, flags.waitLock))
1666 // archive.f.handle = archive.f;
1672 bool ::QuerySize(char * archive, FileSize * size)
1674 bool result = false;
1676 File f = EAROpenArchive(archive, &header);
1679 *size = header.totalSize;