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 /*if(rootDir && writeAccess)
171 // Fix the size of the archive
172 FileTruncate(path, archiveStart);
175 freeBlocks.Free(null);
184 ArchiveDir OpenDirectory(char * name, FileStats stats, ArchiveAddMode addMode)
186 ArchiveDir result = null;
187 EARArchiveDir dir { readOnly = addMode == readOnlyDir };
190 char namePart[MAX_LOCATION] = "", nameRest[MAX_LOCATION];
195 strcpy(nameRest, name);
197 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
198 strcpy(namePart, DIR_SEPS);
200 // Search for directory
203 dir.position = rootDir;
204 if(f.Seek(dir.position, start))
209 f.Read(&dir.first, sizeof(uint), 1);
210 f.Read(&dir.last, sizeof(uint), 1);
216 // If directory doesn't exist already
217 if(!result && addMode != refresh)
219 rootDir = Position(2*sizeof(uint));
220 dir.position = rootDir;
225 // Open rest of directory...
226 if(result && nameRest[0])
228 result = dir.OpenDirectory(nameRest, stats, addMode);
235 uint Position(uint size)
238 for(block = freeBlocks.first; block; block = block.next)
240 if(block.end - block.start + 1 >= size)
242 uint position = block.start;
243 if(block.end - block.start + 1 == size)
244 freeBlocks.Delete(block);
253 bool DefragOffset(uint * offset)
257 for(block = freeBlocks.first; block; block = block.next)
259 if(*offset > block.start)
260 subtract += block.end - block.start + 1;
272 #define MAX_BUFFERSIZE 0x400000
274 void Defrag(uint dirPosition)
276 // Update all offsets within the files
277 uint first = 0, last = 0;
278 uint position = 0, next = 0;
280 f.Seek(dirPosition, start);
281 f.Read(&first, sizeof(uint), 1);
282 f.Read(&last, sizeof(uint), 1);
286 if(first && DefragOffset(&first))
288 if(f.Seek(dirPosition, start))
289 f.Write(&first, sizeof(uint), 1);
291 if(last && DefragOffset(&last))
293 if(f.Seek(dirPosition + sizeof(uint), start))
294 f.Write(&last, sizeof(uint), 1);
297 for(; position; position = next)
301 if(f.Seek(position, start) && f.Read(entry, sizeof(EAREntry), 1))
305 if(entry.prev && DefragOffset(&entry.prev))
307 f.Seek(position + OFFSET(EAREntry, prev), start);
308 f.Write(&entry.prev, sizeof(uint), 1);
310 if(entry.next && DefragOffset(&entry.next))
312 f.Seek(position + OFFSET(EAREntry, next), start);
313 f.Write(&entry.next, sizeof(uint), 1);
316 if(entry.type == ENTRY_FOLDER)
317 Defrag(position + sizeof(EAREntry) + entry.nameLen);
323 // Move all the blocks
324 if(dirPosition == rootDir)
327 byte * buffer = null;
328 FreeBlock block, nextBlock;
329 for(block = freeBlocks.first; block && block.next; block = nextBlock)
333 nextBlock = block.next;
334 dataSize = nextBlock.start - (block.end + 1);
336 if(dataSize > bufferSize && (!bufferSize || bufferSize < MAX_BUFFERSIZE))
338 bufferSize = Min(dataSize, MAX_BUFFERSIZE);
339 buffer = renew buffer byte[bufferSize];
342 for(c = 0; c<dataSize; c += bufferSize)
344 uint size = (dataSize > c + bufferSize) ? bufferSize : (dataSize - c);
346 // Read block of data
347 f.Seek((block.end + 1) + c, start);
348 f.Read(buffer, size, 1);
350 // Write block of data
351 f.Seek(block.start + c, start);
352 f.Write(buffer, size, 1);
355 nextBlock.start -= (block.end - block.start) + 1;
357 freeBlocks.Delete(block);
363 uint Find(EARArchiveDir directory, char * namePart, EAREntry entry)
366 for(position = directory.first; position; position = entry.next)
368 char fileName[MAX_FILENAME];
370 if(f.Seek(position, start) && f.Read(entry, sizeof(EAREntry), 1))
372 if(entry.nameLen > MAX_FILENAME)
373 return 0; // CORRUPTION ERROR
374 f.Read(fileName, 1, entry.nameLen);
375 fileName[entry.nameLen] = '\0';
377 if(!strcmp(fileName, "/") || !strcmp(fileName, "\\"))
378 strcpy(fileName, DIR_SEPS);
380 if(!fstrcmp(fileName, namePart))
384 return 0; // ERROR OUT OF SPACE?
389 void AddFreeBlock(uint position, uint size)
391 FreeBlock block, prevBlock, nextBlock = null;
393 // Find the previous and next free block
395 for(block = freeBlocks.first; block; block = block.next)
396 if(block.end < position)
404 // Try to merge with previous block
405 if(prevBlock && prevBlock.end + 1 == position)
407 prevBlock.end += size;
408 // Try to merge with next block as well
409 if(nextBlock && nextBlock.start == prevBlock.end + 1)
411 prevBlock.end = nextBlock.end;
412 freeBlocks.Delete(nextBlock);
415 // Try to merge with next block
416 else if(nextBlock && nextBlock.start == position + size)
418 nextBlock.start = position;
420 // This free block is not connected to any other block
423 freeBlocks.Insert(prevBlock, FreeBlock { start = position, end = position + size - 1 });
427 void SubtractBlock(uint start, uint size)
430 for(block = freeBlocks.first; block; block = block.next)
432 if(block.end >= start - 1L && block.start <= start + size)
434 if(block.end > start + size && block.start < start - 1L)
436 FreeBlock newBlock { start = start + size, end = block.end };
437 block.end = start - 1L;
438 freeBlocks.Insert(block, newBlock);
440 else if(block.end > start + size)
442 block.start = start + size;
444 else if(block.start < start - 1L)
446 block.end = start - 1L;
450 freeBlocks.Remove(block);
458 void Delete(EARArchiveDir dir, uint position, EAREntry entry)
461 if(entry.type == ENTRY_FOLDER)
463 EARArchiveDir subDir {};
467 subDir.position = dir.position;
468 f.Read(&subDir.first, sizeof(uint), 1);
469 f.Read(&subDir.last, sizeof(uint), 1);
471 // Erase directory contents first
472 for(filePosition = subDir.first; filePosition; filePosition = fileEntry.next)
474 f.Seek(filePosition, start);
475 f.Read(&fileEntry, sizeof(EAREntry), 1);
476 f.Seek(fileEntry.nameLen, current);
477 Delete(subDir, filePosition, &fileEntry);
479 size = sizeof(EAREntry) + entry.nameLen + 2 * sizeof(uint);
483 size = sizeof(EAREntry) + entry.nameLen + (entry.cSize ? entry.cSize : entry.size);
488 f.Seek(entry.prev + OFFSET(EAREntry, next), start);
489 f.Write(&entry.next, sizeof(uint), 1);
493 f.Seek(entry.next + OFFSET(EAREntry, prev), start);
494 f.Write(&entry.prev, sizeof(uint), 1);
496 if(dir.last == position) dir.last = entry.prev;
497 if(dir.first == position) dir.first = entry.next;
499 AddFreeBlock(position, size);
500 totalSize -= entry.size;
506 File FileOpen(char * name)
512 char fileName[MAX_LOCATION];
515 f.Seek(archiveStart + sizeof(EARHeader), start);
516 if(EARGetEntry(f, entry, name, null).isFile)
520 byte * uncompressed = new byte[entry.size];
523 byte * compressed = new byte[entry.cSize];
526 if(f.Read(compressed, 1, entry.cSize) == entry.cSize)
527 uncompress(uncompressed, (uint *)&entry.size, compressed, entry.cSize);
532 file.size = entry.size;
533 file.buffer = uncompressed;
540 file.start = f.Tell();
542 file.size = entry.size;
545 file.f.Seek(file.start, start);
555 File FileOpenAtPosition(uint position)
558 char fileName[MAX_LOCATION];
560 f.Seek(position, start);
561 f.Read(entry, sizeof(EAREntry), 1);
562 /*if(entry.nameLen > 1024)
564 f.Read(fileName, 1, entry.nameLen);
567 byte * uncompressed = new byte[entry.size];
570 byte * compressed = new byte[entry.cSize];
573 if(f.Read(compressed, 1, entry.cSize) == entry.cSize)
574 uncompress(uncompressed, (uint *)&entry.size, compressed, entry.cSize);
579 file.size = entry.size;
580 file.buffer = uncompressed;
585 file.start = f.Tell();
587 file.size = entry.size;
589 file.f.Seek(file.start, start);
595 FileAttribs FileExists(char * fileName)
599 f.Seek(archiveStart + sizeof(EARHeader), start);
600 result = EARGetEntry(f, entry, fileName, null);
604 void SubtractUsedBlocks()
607 if(!f.Read(&first, sizeof(uint), 1))
610 if(first > f.GetSize())
615 f.Read(&last, sizeof(uint), 1);
621 char fileName[MAX_FILENAME];
624 f.Seek(first, start);
625 f.Read(entry, sizeof(EAREntry), 1);
626 if(entry.nameLen < MAX_FILENAME)
628 f.Read(fileName, 1, entry.nameLen);
629 fileName[entry.nameLen] = 0;
637 size += sizeof(EAREntry) + entry.nameLen;
639 if(entry.type == ENTRY_FILE)
641 size += entry.cSize ? entry.cSize : entry.size;
643 else if(entry.type == ENTRY_FOLDER)
645 size += 2 * sizeof(uint);
646 SubtractUsedBlocks();
648 SubtractBlock(first, size);
653 void SetBufferSize(uint bufferSize)
655 if(f && f._class == class(BufferedFile))
656 ((BufferedFile)f).bufferSize = bufferSize;
659 void SetBufferRead(uint bufferRead)
661 if(f && f._class == class(BufferedFile))
662 ((BufferedFile)f).bufferRead = bufferRead;
666 class EARArchiveDir : ArchiveDir
677 archive.f.Seek(position, start);
678 archive.f.Write(&first, sizeof(uint), 1);
679 archive.f.Write(&last, sizeof(uint), 1);
684 File FileOpen(char * name)
690 char fileName[MAX_LOCATION];
693 archive.f.Seek(position, start);
694 if(EARGetEntry(archive.f, entry, name, null).isFile)
698 byte * uncompressed = new byte[entry.size];
701 byte * compressed = new byte[entry.cSize];
704 if(archive.f.Read(compressed, 1, entry.cSize) == entry.cSize)
705 uncompress(uncompressed, (uint *)&entry.size, compressed, entry.cSize);
710 file.size = entry.size;
711 file.buffer = uncompressed;
718 file.start = archive.f.Tell();
720 file.size = entry.size;
722 file.f.Seek(file.start, start);
733 FileAttribs FileExists(char * fileName)
737 archive.f.Seek(position, start);
738 result = EARGetEntry(archive.f, entry, fileName, null);
742 ArchiveDir OpenDirectory(char * name, FileStats stats, ArchiveAddMode addMode)
744 ArchiveDir result = null;
745 EARArchiveDir dir { readOnly = addMode == readOnlyDir };
748 char namePart[MAX_LOCATION] = "", nameRest[MAX_LOCATION];
752 dir.archive = archive;
754 SplitDirectory(name, namePart, nameRest);
756 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
757 strcpy(namePart, DIR_SEPS);
759 // Search for directory
761 position = archive.Find(this, namePart, entry);
764 // Fail if file of same name already exists
765 if(entry.type == ENTRY_FILE)
769 dir.position = position + sizeof(EAREntry) + entry.nameLen;
773 archive.f.Read(&dir.first, sizeof(uint), 1);
774 archive.f.Read(&dir.last, sizeof(uint), 1);
780 // If directory doesn't exist already
781 if(!result && addMode != refresh)
783 // Write Header if it's not the root directory
787 entry.nameLen = strlen(namePart);
790 entry.type = ENTRY_FOLDER;
791 if(!nameRest[0] && stats)
793 entry.created = (TimeStamp32)stats.created;
794 entry.modified = (TimeStamp32)stats.modified;
797 position = archive.Position(sizeof(EAREntry) + entry.nameLen + 2*sizeof(uint));
799 archive.f.Seek(position, start);
800 archive.f.Write(entry, sizeof(EAREntry), 1);
801 archive.f.Write(namePart, entry.nameLen, 1);
804 if(!first) first = position;
806 // Update the next pointer of previous entry
809 archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
810 archive.f.Write(&position, sizeof(uint), 1);
813 // Make the dir position point after the header
814 dir.position = position + sizeof(EAREntry) + entry.nameLen;
816 // Just update the time stamps
817 else if(result && !nameRest[0] && stats)
819 archive.f.Seek(position + OFFSET(EAREntry, created), start);
820 archive.f.Write(&stats.created, sizeof(uint), 1);
821 archive.f.Write(&stats.modified, sizeof(uint), 1);
825 // Open rest of directory...
826 if(result && nameRest[0])
828 result = dir.OpenDirectory(nameRest, stats, addMode);
835 bool Delete(char * name)
839 char namePart[MAX_LOCATION];
841 strcpy(namePart, name);
842 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
843 strcpy(namePart, DIR_SEPS);
845 position = archive.Find(this, namePart, entry);
848 archive.Delete(this, position, entry);
854 bool Move(char * name, EARArchiveDir to)
857 if(position != to.position)
861 char namePart[MAX_LOCATION];
863 strcpy(namePart, name);
864 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
865 strcpy(namePart, DIR_SEPS);
867 position = archive.Find(this, name, entry);
870 // Unlink from old directory
873 archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
874 archive.f.Write(&entry.next, sizeof(uint), 1);
878 archive.f.Seek(entry.next + OFFSET(EAREntry, prev), start);
879 archive.f.Write(&entry.prev, sizeof(uint), 1);
881 if(last == position) last = entry.prev;
882 if(first == position) first = entry.next;
884 // Relink to new directory
885 entry.prev = to.last;
890 archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
891 archive.f.Write(&position, sizeof(uint), 1);
897 archive.f.Seek(position + OFFSET(EAREntry, prev), start);
898 archive.f.Write(&entry.prev, sizeof(uint), 1);
899 archive.f.Write(&entry.next, sizeof(uint), 1);
907 bool Rename(char * name, char * newName)
912 char namePart[MAX_LOCATION];
914 strcpy(namePart, name);
915 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
916 strcpy(namePart, DIR_SEPS);
918 position = archive.Find(this, namePart, entry);
922 EAREntry newEntry = entry;
923 uint newPosition = position;
925 if(entry.type == ENTRY_FOLDER)
926 dataSize = 2 * sizeof(uint);
928 dataSize = entry.cSize ? entry.cSize : entry.size;
930 newEntry.nameLen = strlen(newName);
931 if(newEntry.nameLen > entry.nameLen)
934 newPosition = archive.Position(sizeof(EAREntry) + newEntry.nameLen + dataSize);
936 archive.f.Seek(newPosition, start);
937 archive.f.Write(&newEntry, sizeof(EAREntry), 1);
938 archive.f.Write(newName, sizeof(char), newEntry.nameLen);
943 archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
944 archive.f.Write(&newPosition, sizeof(uint), 1);
948 archive.f.Seek(entry.next + OFFSET(EAREntry, prev), start);
949 archive.f.Write(&newPosition, sizeof(uint), 1);
951 if(first == position) first = newPosition;
952 if(last == position) last = newPosition;
957 archive.f.Seek(position + OFFSET(EAREntry, nameLen), start);
958 archive.f.Write(&newEntry.nameLen, sizeof(uint), 1);
959 archive.f.Seek(position + sizeof(EAREntry), start);
960 archive.f.Write(newName, sizeof(char), newEntry.nameLen);
962 // There will be free space at the end of an entry with a shorter new name
963 if(newEntry.nameLen < entry.nameLen)
964 archive.AddFreeBlock(position + sizeof(EAREntry) + newEntry.nameLen + dataSize, entry.nameLen - newEntry.nameLen);
966 if(entry.nameLen != newEntry.nameLen)
969 uint bufferSize = Min(dataSize, MAX_BUFFERSIZE);
970 buffer = new byte[bufferSize];
973 uint readPosition = position + sizeof(EAREntry) + entry.nameLen;
974 uint writePosition = newPosition + sizeof(EAREntry) + newEntry.nameLen;
977 for(c = 0; c<dataSize; c += bufferSize)
979 uint size = (dataSize > c + bufferSize) ? bufferSize : (dataSize - c);
981 archive.f.Seek(readPosition + c, start);
982 archive.f.Read(buffer, size, 1);
984 archive.f.Seek(writePosition + c, start);
985 archive.f.Write(buffer, size, 1);
990 if(newEntry.nameLen > entry.nameLen)
992 // Prevent the children to be deleted
993 if(entry.type == ENTRY_FOLDER)
995 uint first = 0, last = 0;
996 archive.f.Seek(position + sizeof(EAREntry) + entry.nameLen, start);
997 archive.f.Write(&first, sizeof(uint), 1);
998 archive.f.Write(&last, sizeof(uint), 1);
1001 // Delete the old entry
1002 entry.prev = entry.next = 0;
1003 archive.f.Seek(position + sizeof(EAREntry) + entry.nameLen, start);
1004 archive.Delete(this, position, entry);
1012 bool AddFromFile(char * name, File input, FileStats stats, ArchiveAddMode addMode, int compression, int * ratio, uint * newPosition)
1014 // Search for identical entry
1016 uint oldPosition = archive.Find(this, name, oldEntry);
1017 return _AddFromFileAtPosition(oldEntry, oldPosition, name, input, stats, addMode, compression, ratio, newPosition);
1020 bool AddFromFileAtPosition(uint oldPosition, char * name, File input, FileStats stats, ArchiveAddMode addMode, int compression, int * ratio, uint * newPosition)
1025 if(!archive.f.Seek(oldPosition, start) || !archive.f.Read(oldEntry, sizeof(EAREntry), 1))
1028 return _AddFromFileAtPosition(oldEntry, oldPosition, name, input, stats, addMode, compression, ratio, newPosition);
1031 bool _AddFromFileAtPosition(EAREntry oldEntry, uint oldPosition, char * name, File input, FileStats stats, ArchiveAddMode addMode, int compression, int * ratio, uint * newPosition)
1033 bool result = false;
1035 FileStats oldStats { };
1039 oldStats.modified = (TimeStamp)oldEntry.modified;
1040 oldStats.created = (TimeStamp)oldEntry.created;
1044 oldStats.size = input.GetSize();
1053 archive.Delete(this, oldPosition, oldEntry);
1055 // Only updates changed files
1058 (oldEntry.size != stats.size ||
1059 oldEntry.modified != (TimeStamp32)stats.modified ||
1060 oldEntry.created != (TimeStamp32)stats.created))
1061 archive.Delete(this, oldPosition, oldEntry);
1065 // Only updates changed or new files
1069 if(oldEntry.size != stats.size ||
1070 oldEntry.modified != (TimeStamp32)stats.modified ||
1071 oldEntry.created != (TimeStamp32)stats.created)
1072 archive.Delete(this, oldPosition, oldEntry);
1082 uint position, size;
1083 byte * compressed = null;
1086 entry.nameLen = strlen(name);
1089 entry.type = ENTRY_FILE;
1091 entry.size = stats.size;
1092 entry.created = (TimeStamp32)stats.created;
1093 entry.modified = (TimeStamp32)stats.modified;
1097 byte * uncompressed = new byte[entry.size];
1100 if(input.Read(uncompressed, 1, entry.size) == entry.size)
1102 entry.cSize = entry.size + entry.size / 1000 + 12;
1104 compressed = new byte[entry.cSize];
1106 compress2(compressed, (uint *)&entry.cSize, uncompressed, entry.size, compression);
1108 delete uncompressed;
1119 *ratio = entry.size ? (entry.cSize * 1000 / entry.size) : 0;
1122 size = sizeof(EAREntry) + entry.nameLen + (entry.cSize ? entry.cSize : entry.size);
1123 position = archive.Position(size);
1126 if(!archive.f.Seek(position, start) ||
1127 !archive.f.Write(entry, sizeof(EAREntry), 1) ||
1128 !archive.f.Write(name, entry.nameLen, 1))
1137 if(!archive.f.Write(compressed, 1, entry.cSize))
1149 for(c = 0; c<entry.size && count; c+= count)
1151 count = input.Read(buffer, 1, sizeof(buffer));
1152 if(!archive.f.Write(buffer, 1, count))
1157 // Update the next pointer previous entry
1160 archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
1161 archive.f.Write(&position, sizeof(uint), 1);
1164 // Update total size of archive
1165 archive.totalSize += entry.size;
1168 if(!first) first = position;
1169 if(newPosition) *newPosition = (bool)position;
1173 if(newPosition) *newPosition = 0;
1176 // archive.f.handle = archive.f;
1182 // Directory Description for file listing
1183 class EARDir : struct
1185 char path[MAX_LOCATION];
1190 class EARFile : File
1195 // -- For reading compressed file (entirely buffered)
1198 // -- For reading uncompressed file (not buffered)
1220 int Read(byte * buffer, uint size, uint count)
1224 f.Seek(position + start, start);
1225 read = Min(count, (this.size - position) / size);
1227 CopyBytes(buffer, this.buffer + position, read * size);
1229 read = f.Read(buffer, size, read);
1230 position += read * size;
1234 int Write(byte * buffer, uint size, uint count)
1239 bool Getc(char * ch)
1245 char b = buffer[position++];
1251 f.Seek(position + start, start);
1264 bool Puts(char * string)
1269 bool Seek(int pos, FileSeekMode mode)
1271 bool result = false;
1275 if(pos <= (int)size)
1279 result = f.Seek(position + start, start);
1285 if(position + pos <= (int)size && (int)position >= -pos)
1289 result = f.Seek(position + start, start);
1295 if(pos < 0 && -pos <= (int)size)
1297 position = size + pos;
1299 f.Seek(position + start, start);
1315 return position >= size || (f && f.Eof());
1324 class EARFileSystem : FileSystem
1326 File ::Open(char * archive, char * name, FileOpenMode mode)
1334 char fileName[MAX_LOCATION];
1336 File f = EAROpenArchive(archive, &header);
1337 strcpy(fileName, name);
1339 if(!f && archive[0] == ':')
1341 f = EAROpenArchive(":", &header);
1344 strcpy(fileName, archive + 1);
1345 PathCat(fileName, name);
1352 if(EARGetEntry(f, entry, fileName, null).isFile)
1356 byte * uncompressed = new byte[entry.size];
1359 byte * compressed = new byte[entry.cSize];
1362 if(f.Read(compressed, 1, entry.cSize) == entry.cSize)
1363 uncompress(uncompressed, (uint *)&entry.size, compressed, entry.cSize);
1368 file.size = entry.size;
1369 file.buffer = uncompressed;
1376 file.start = f.Tell();
1378 file.size = entry.size;
1394 FileAttribs ::Exists(char * archive, char * fileName)
1398 File f = EAROpenArchive(archive, &header);
1402 result = EARGetEntry(f, entry, fileName, null);
1408 bool ::GetSize(char * archive, char * fileName, FileSize * size)
1410 bool result = false;
1412 File f = EAROpenArchive(archive, &header);
1416 if(EARGetEntry(f, entry, fileName, null))
1424 bool ::Stats(char * archive, char * fileName, FileStats stats)
1426 bool result = false;
1428 File f = EAROpenArchive(archive, &header);
1432 if(EARGetEntry(f, entry, fileName, null))
1434 stats.size = entry.size;
1436 stats.modified = (TimeStamp)entry.modified;
1437 stats.created = (TimeStamp)entry.created;
1445 void ::FixCase(char * archive, char * name)
1449 File f = EAROpenArchive(archive, &header);
1453 char fileName[MAX_LOCATION] = "";
1454 if(EARGetEntry(f, entry, name, fileName))
1455 strcpy(name, fileName);
1461 bool ::Find(FileDesc file, char * archive, char * name)
1463 bool result = false;
1468 File f = EAROpenArchive(archive, &header);
1472 if(EARGetEntry(f, entry, name, null).isDirectory)
1476 sprintf(d.path, "<%s>%s", archive, name);
1478 f.Read(&first, sizeof(uint), 1);
1479 f.Read(&last, sizeof(uint), 1);
1484 d.f.Seek(d.next, start);
1485 d.f.Read(entry, sizeof(EAREntry), 1);
1486 d.f.Read(file.name, 1, entry.nameLen);
1487 file.name[entry.nameLen] = '\0';
1488 file.stats.attribs = { isDirectory = (entry.type == ENTRY_FOLDER), isFile = (entry.type != ENTRY_FOLDER) };
1489 file.stats.accessed = file.stats.modified = (TimeStamp)entry.modified;
1490 file.stats.created = (TimeStamp)entry.created;
1491 file.stats.size = entry.size;
1493 strcpy(file.path, d.path);
1494 PathCat(file.path, file.name);
1495 d.next = entry.next;
1511 bool ::FindNext(FileDesc file)
1513 bool result = false;
1514 EARDir d = (EARDir)file.dir;
1518 d.f.Seek(d.next, start);
1519 d.f.Read(entry, sizeof(EAREntry), 1);
1520 d.f.Read(file.name, 1, entry.nameLen);
1521 file.name[entry.nameLen] = '\0';
1522 file.stats.attribs = FileAttribs { isDirectory = (entry.type == ENTRY_FOLDER), isFile = (entry.type != ENTRY_FOLDER) };
1523 file.stats.accessed = file.stats.modified = (TimeStamp)entry.modified;
1524 file.stats.created = (TimeStamp)entry.created;
1525 file.stats.size = entry.size;
1527 strcpy(file.path, d.path);
1528 PathCat(file.path, file.name);
1529 d.next = entry.next;
1536 void ::CloseDir(FileDesc file)
1538 EARDir d = (EARDir)file.dir;
1545 #if !defined(ECERE_NOARCHIVE) && !defined(ECERE_VANILLA)
1546 Archive ::OpenArchive(char * fileName, ArchiveOpenFlags flags)
1548 Archive result = null;
1549 EARArchive archive { writeAccess = flags.writeAccess };
1552 // Check for existing Archive
1553 if((archive.f = fileName ? (flags.buffered ? FileOpenBuffered : FileOpen)(fileName, flags.writeAccess ? readWrite : read) : TempFile { openMode = readWrite } ))
1556 bool opened = false;
1557 uint archiveSize = 0;
1558 archive.f.Seek(-(int)sizeof(uint), end);
1559 archive.f.Read(&archiveSize, sizeof(uint), 1);
1560 archive.f.Seek(-(int)archiveSize, end);
1562 archive.archiveStart = archive.f.Tell();
1563 if(archive.f.Read(&header, sizeof(EARHeader), 1) == 1 &&
1564 !strncmp(header.recognition, earRecognition, sizeof(earRecognition)))
1569 archive.f.Seek(0, start);
1570 archive.archiveStart = archive.f.Tell();
1571 archiveSize = archive.f.GetSize();
1572 if(archive.f.Read(&header, sizeof(EARHeader), 1) == 1 &&
1573 !strncmp(header.recognition, earRecognition, sizeof(earRecognition)))
1579 // At this point we recognized the file as a valid eAR archive
1580 archive.rootDir = archive.archiveStart + sizeof(EARHeader);
1581 archive.totalSize = header.totalSize;
1583 archive.f.Seek(archive.rootDir, start);
1586 archive.freeBlocks.Add(FreeBlock { start = archive.rootDir + 2 * sizeof(uint), end = MAXDWORD });
1587 archive.SubtractUsedBlocks();
1591 archive.freeBlocks.Add(FreeBlock { start = archive.archiveStart + (archiveSize - sizeof(uint)), end = MAXDWORD });
1595 if(!flags.writeAccess)
1598 archive.f = FileOpen(fileName, readWrite);
1609 // This piece of code will create a new archive as a new file or at the footer
1610 // of an existing file.
1611 if(!result && flags.writeAccess)
1613 // If the file doesn't exist, create it
1616 archive.f = FileOpen(fileName, writeRead);
1618 archive.f = (flags.buffered ? FileOpenBuffered : FileOpen)(fileName, readWrite);
1628 archive.f.Seek(0, end);
1630 archive.archiveStart = archive.f.Tell();
1631 archive.freeBlocks.Add(FreeBlock { start = archive.archiveStart + sizeof(EARHeader), end = MAXDWORD });
1634 archive.f.Write(&header, sizeof(EARHeader), 1);
1636 uint first = 0, last = 0;
1637 archive.f.Write(&first, sizeof(first), 1);
1638 archive.f.Write(&last, sizeof(last), 1);
1641 archive.rootDir = 0;
1646 if(archive.f && flags.writeAccess && flags.exclusive && !archive.f.Lock(flags.exclusive ? exclusive : shared, 0, 0, false))
1655 // archive.f.handle = archive.f;
1661 bool ::QuerySize(char * archive, FileSize * size)
1663 bool result = false;
1665 File f = EAROpenArchive(archive, &header);
1668 *size = header.totalSize;