10 #define OFFSET(s, m) ((uint)(uintptr) (&((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)];
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(const char * archive, EARHeader header)
47 char moduleName[MAX_LOCATION];
48 const char * name = archive + 1;
49 #if defined(__ANDROID__)
51 name = ((SubModule)__thisModule.application.modules.first).next.module.name;
54 #if defined(__EMSCRIPTEN__)
56 f = FileOpen("resources.ear", read);
59 if(!f && LocateModule(name, moduleName))
60 f = FileOpen(moduleName, read);
63 f = FileOpen(archive, read);
68 // First attempt to treat this as an archive file
69 if(f.Read(header, sizeof(EARHeader), 1) == 1 &&
70 !memcmp(header.recognition, earRecognition, sizeof(earRecognition)))
73 // Then try to see if an archive is at the end of the file
74 f.Seek(-(int)sizeof(uint), end);
75 f.Read(&archiveSize, sizeof(uint), 1);
76 f.Seek(-(int)archiveSize, end);
77 if(f.Read(header, sizeof(EARHeader), 1) == 1 &&
78 !memcmp(header.recognition, earRecognition, sizeof(earRecognition)))
86 static FileAttribs EARGetEntry(File f, EAREntry entry, const char * name, char * path)
88 uint first = 0, last = 0;
90 return FileAttribs { isDirectory = true };
91 if(!f.Read(&first, sizeof(uint), 1))
93 f.Read(&last, sizeof(uint), 1);
96 char namePart[MAX_FILENAME], nameRest[MAX_LOCATION];
97 SplitDirectory(name, namePart, nameRest);
99 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
100 strcpy(namePart, DIR_SEPS);
102 f.Seek(first, start);
105 char fileName[MAX_FILENAME];
107 f.Read(entry, sizeof(EAREntry), 1);
108 f.Read(fileName, 1, entry.nameLen);
109 fileName[entry.nameLen] = '\0';
111 if(!strcmp(fileName, "/") || !strcmp(fileName, "\\"))
112 strcpy(fileName, DIR_SEPS);
114 if(!fstrcmp(fileName, namePart))
117 PathCat(path, fileName);
119 return EARGetEntry(f, entry, nameRest, path);
121 return (entry.type == ENTRY_FILE) ? FileAttribs { isFile = true } : FileAttribs { isDirectory = true };
124 f.Seek(entry.next, start);
129 return FileAttribs { };
132 #if !defined(ECERE_NOARCHIVE) && !defined(ECERE_VANILLA)
133 class EARArchive : Archive
136 //BufferedFile bf { };
137 // char path[MAX_LOCATION];
149 end = ((FreeBlock)freeBlocks.last).start;
152 f.Seek(archiveStart + OFFSET(EARHeader, totalSize), start);
153 f.Write(&totalSize, sizeof(uint), 1);
162 f.Write(&end, sizeof(uint), 1);
163 f.Truncate(archiveStart + end);
172 if(f && rootDir && writeAccess)
176 archiveStart += Update();
181 f.Unlock(0, 0, true);
185 /*if(rootDir && writeAccess)
187 // Fix the size of the archive
188 FileTruncate(path, archiveStart);
191 freeBlocks.Free(null);
200 ArchiveDir OpenDirectory(const char * name, FileStats stats, ArchiveAddMode addMode)
202 ArchiveDir result = null;
203 EARArchiveDir dir { readOnly = addMode == readOnlyDir };
206 char namePart[MAX_LOCATION] = "", nameRest[MAX_LOCATION];
210 strcpy(nameRest, name);
212 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
213 strcpy(namePart, DIR_SEPS);
215 // Search for directory
218 dir.position = rootDir;
219 if(f.Seek(dir.position, start))
224 f.Read(&dir.first, sizeof(uint), 1);
225 f.Read(&dir.last, sizeof(uint), 1);
231 // If directory doesn't exist already
232 if(!result && addMode != refresh)
234 rootDir = Position(2*sizeof(uint));
235 dir.position = rootDir;
240 // Open rest of directory...
241 if(result && nameRest[0])
243 result = dir.OpenDirectory(nameRest, stats, addMode);
250 uint Position(uint size)
253 for(block = freeBlocks.first; block; block = block.next)
255 if(block.end - block.start + 1 >= size)
257 uint position = block.start;
258 if(block.end - block.start + 1 == size)
259 freeBlocks.Delete(block);
268 bool DefragOffset(uint * offset)
272 for(block = freeBlocks.first; block; block = block.next)
274 if(*offset > block.start)
275 subtract += block.end - block.start + 1;
287 #define MAX_BUFFERSIZE 0x400000
289 void Defrag(uint dirPosition)
291 // Update all offsets within the files
292 uint first = 0, last = 0;
293 uint position = 0, next = 0;
295 f.Seek(dirPosition, start);
296 f.Read(&first, sizeof(uint), 1);
297 f.Read(&last, sizeof(uint), 1);
301 if(first && DefragOffset(&first))
303 if(f.Seek(dirPosition, start))
304 f.Write(&first, sizeof(uint), 1);
306 if(last && DefragOffset(&last))
308 if(f.Seek(dirPosition + sizeof(uint), start))
309 f.Write(&last, sizeof(uint), 1);
312 for(; position; position = next)
316 if(f.Seek(position, start) && f.Read(entry, sizeof(EAREntry), 1))
320 if(entry.prev && DefragOffset(&entry.prev))
322 f.Seek(position + OFFSET(EAREntry, prev), start);
323 f.Write(&entry.prev, sizeof(uint), 1);
325 if(entry.next && DefragOffset(&entry.next))
327 f.Seek(position + OFFSET(EAREntry, next), start);
328 f.Write(&entry.next, sizeof(uint), 1);
331 if(entry.type == ENTRY_FOLDER)
332 Defrag(position + sizeof(EAREntry) + entry.nameLen);
338 // Move all the blocks
339 if(dirPosition == rootDir)
342 byte * buffer = null;
343 FreeBlock block, nextBlock;
344 for(block = freeBlocks.first; block && block.next; block = nextBlock)
348 nextBlock = block.next;
349 dataSize = nextBlock.start - (block.end + 1);
351 if(dataSize > bufferSize && (!bufferSize || bufferSize < MAX_BUFFERSIZE))
353 bufferSize = Min(dataSize, MAX_BUFFERSIZE);
354 buffer = renew buffer byte[bufferSize];
357 for(c = 0; c<dataSize; c += bufferSize)
359 uint size = (dataSize > c + bufferSize) ? bufferSize : (dataSize - c);
361 // Read block of data
362 f.Seek((block.end + 1) + c, start);
363 f.Read(buffer, size, 1);
365 // Write block of data
366 f.Seek(block.start + c, start);
367 f.Write(buffer, size, 1);
370 nextBlock.start -= (block.end - block.start) + 1;
372 freeBlocks.Delete(block);
378 uint Find(EARArchiveDir directory, const char * namePart, EAREntry entry)
381 for(position = directory.first; position; position = entry.next)
383 char fileName[MAX_FILENAME];
385 if(f.Seek(position, start) && f.Read(entry, sizeof(EAREntry), 1))
387 if(entry.nameLen > MAX_FILENAME)
388 return 0; // CORRUPTION ERROR
389 f.Read(fileName, 1, entry.nameLen);
390 fileName[entry.nameLen] = '\0';
392 if(!strcmp(fileName, "/") || !strcmp(fileName, "\\"))
393 strcpy(fileName, DIR_SEPS);
395 if(!fstrcmp(fileName, namePart))
399 return 0; // ERROR OUT OF SPACE?
404 void AddFreeBlock(uint position, uint size)
406 FreeBlock block, prevBlock, nextBlock = null;
408 // Find the previous and next free block
410 for(block = freeBlocks.first; block; block = block.next)
411 if(block.end < position)
419 // Try to merge with previous block
420 if(prevBlock && prevBlock.end + 1 == position)
422 prevBlock.end += size;
423 // Try to merge with next block as well
424 if(nextBlock && nextBlock.start == prevBlock.end + 1)
426 prevBlock.end = nextBlock.end;
427 freeBlocks.Delete(nextBlock);
430 // Try to merge with next block
431 else if(nextBlock && nextBlock.start == position + size)
433 nextBlock.start = position;
435 // This free block is not connected to any other block
438 freeBlocks.Insert(prevBlock, FreeBlock { start = position, end = position + size - 1 });
442 void SubtractBlock(uint start, uint size)
445 for(block = freeBlocks.first; block; block = block.next)
447 if(block.end >= start - 1L && block.start <= start + size)
449 if(block.end > start + size && block.start < start - 1L)
451 FreeBlock newBlock { start = start + size, end = block.end };
452 block.end = start - 1L;
453 freeBlocks.Insert(block, newBlock);
455 else if(block.end > start + size)
457 block.start = start + size;
459 else if(block.start < start - 1L)
461 block.end = start - 1L;
465 freeBlocks.Remove(block);
473 void Delete(EARArchiveDir dir, uint position, EAREntry entry)
476 if(entry.type == ENTRY_FOLDER)
478 EARArchiveDir subDir {};
482 subDir.position = dir.position;
483 f.Read(&subDir.first, sizeof(uint), 1);
484 f.Read(&subDir.last, sizeof(uint), 1);
486 // Erase directory contents first
487 for(filePosition = subDir.first; filePosition; filePosition = fileEntry.next)
489 f.Seek(filePosition, start);
490 f.Read(&fileEntry, sizeof(EAREntry), 1);
491 f.Seek(fileEntry.nameLen, current);
492 Delete(subDir, filePosition, &fileEntry);
494 size = sizeof(EAREntry) + entry.nameLen + 2 * sizeof(uint);
498 size = sizeof(EAREntry) + entry.nameLen + (entry.cSize ? entry.cSize : entry.size);
503 f.Seek(entry.prev + OFFSET(EAREntry, next), start);
504 f.Write(&entry.next, sizeof(uint), 1);
508 f.Seek(entry.next + OFFSET(EAREntry, prev), start);
509 f.Write(&entry.prev, sizeof(uint), 1);
511 if(dir.last == position) dir.last = entry.prev;
512 if(dir.first == position) dir.first = entry.next;
514 AddFreeBlock(position, size);
515 totalSize -= entry.size;
521 File FileOpen(const char * name)
529 f.Seek(archiveStart + sizeof(EARHeader), start);
530 if(EARGetEntry(f, entry, name, null).isFile)
534 byte * uncompressed = new byte[entry.size];
537 byte * compressed = new byte[entry.cSize];
540 if(f.Read(compressed, 1, entry.cSize) == entry.cSize)
542 unsigned long destLen = entry.size;
543 uncompress(uncompressed, &destLen, compressed, entry.cSize);
544 entry.size = (FileSize)destLen; // TODO: Support 64 bit file sizes
550 file.size = entry.size;
551 file.buffer = uncompressed;
558 file.start = f.Tell();
560 file.size = entry.size;
563 file.f.Seek(file.start, start);
573 File FileOpenAtPosition(uint position)
576 char fileName[MAX_LOCATION];
578 f.Seek(position, start);
579 f.Read(entry, sizeof(EAREntry), 1);
580 /*if(entry.nameLen > 1024)
582 f.Read(fileName, 1, entry.nameLen);
585 byte * uncompressed = new byte[entry.size];
588 byte * compressed = new byte[entry.cSize];
591 if(f.Read(compressed, 1, entry.cSize) == entry.cSize)
593 unsigned long destLen = entry.size;
594 uncompress(uncompressed, &destLen, compressed, entry.cSize);
595 entry.size = (FileSize)destLen;
601 file.size = entry.size;
602 file.buffer = uncompressed;
607 file.start = f.Tell();
609 file.size = entry.size;
611 file.f.Seek(file.start, start);
617 FileAttribs FileExists(const char * fileName)
621 f.Seek(archiveStart + sizeof(EARHeader), start);
622 result = EARGetEntry(f, entry, fileName, null);
626 void SubtractUsedBlocks()
629 if(!f.Read(&first, sizeof(uint), 1))
632 if(first > f.GetSize())
637 f.Read(&last, sizeof(uint), 1);
643 char fileName[MAX_FILENAME];
646 f.Seek(first, start);
647 f.Read(entry, sizeof(EAREntry), 1);
648 if(entry.nameLen < MAX_FILENAME)
650 f.Read(fileName, 1, entry.nameLen);
651 fileName[entry.nameLen] = 0;
659 size += sizeof(EAREntry) + entry.nameLen;
661 if(entry.type == ENTRY_FILE)
663 size += entry.cSize ? entry.cSize : entry.size;
665 else if(entry.type == ENTRY_FOLDER)
667 size += 2 * sizeof(uint);
668 SubtractUsedBlocks();
670 SubtractBlock(first, size);
675 void SetBufferSize(uint bufferSize)
677 if(f && f._class == class(BufferedFile))
678 ((BufferedFile)f).bufferSize = bufferSize;
681 void SetBufferRead(uint bufferRead)
683 if(f && f._class == class(BufferedFile))
684 ((BufferedFile)f).bufferRead = bufferRead;
688 class EARArchiveDir : ArchiveDir
699 archive.f.Seek(position, start);
700 archive.f.Write(&first, sizeof(uint), 1);
701 archive.f.Write(&last, sizeof(uint), 1);
706 File FileOpen(const char * name)
714 archive.f.Seek(position, start);
715 if(EARGetEntry(archive.f, entry, name, null).isFile)
719 byte * uncompressed = new byte[entry.size];
722 byte * compressed = new byte[entry.cSize];
725 if(archive.f.Read(compressed, 1, entry.cSize) == entry.cSize)
727 unsigned long destLen = entry.size;
728 uncompress(uncompressed, &destLen, compressed, entry.cSize);
729 entry.size = (FileSize)destLen;
735 file.size = entry.size;
736 file.buffer = uncompressed;
743 file.start = archive.f.Tell();
745 file.size = entry.size;
747 file.f.Seek(file.start, start);
758 FileAttribs FileExists(const char * fileName)
762 archive.f.Seek(position, start);
763 result = EARGetEntry(archive.f, entry, fileName, null);
767 ArchiveDir OpenDirectory(const char * name, FileStats stats, ArchiveAddMode addMode)
769 ArchiveDir result = null;
770 EARArchiveDir dir { readOnly = addMode == readOnlyDir };
773 char namePart[MAX_LOCATION] = "", nameRest[MAX_LOCATION];
777 dir.archive = archive;
779 SplitDirectory(name, namePart, nameRest);
781 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
782 strcpy(namePart, DIR_SEPS);
784 // Search for directory
786 position = archive.Find(this, namePart, entry);
789 // Fail if file of same name already exists
790 if(entry.type == ENTRY_FILE)
794 dir.position = position + sizeof(EAREntry) + entry.nameLen;
798 archive.f.Read(&dir.first, sizeof(uint), 1);
799 archive.f.Read(&dir.last, sizeof(uint), 1);
805 // If directory doesn't exist already
806 if(!result && addMode != refresh)
808 // Write Header if it's not the root directory
812 entry.nameLen = strlen(namePart);
815 entry.type = ENTRY_FOLDER;
816 if(!nameRest[0] && stats)
818 entry.created = (TimeStamp32)stats.created;
819 entry.modified = (TimeStamp32)stats.modified;
822 position = archive.Position(sizeof(EAREntry) + entry.nameLen + 2*sizeof(uint));
824 archive.f.Seek(position, start);
825 archive.f.Write(entry, sizeof(EAREntry), 1);
826 archive.f.Write(namePart, entry.nameLen, 1);
829 if(!first) first = position;
831 // Update the next pointer of previous entry
834 archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
835 archive.f.Write(&position, sizeof(uint), 1);
838 // Make the dir position point after the header
839 dir.position = position + sizeof(EAREntry) + entry.nameLen;
841 // Just update the time stamps
842 else if(result && !nameRest[0] && stats)
844 archive.f.Seek(position + OFFSET(EAREntry, created), start);
845 archive.f.Write(&stats.created, sizeof(uint), 1);
846 archive.f.Write(&stats.modified, sizeof(uint), 1);
850 // Open rest of directory...
851 if(result && nameRest[0])
853 result = dir.OpenDirectory(nameRest, stats, addMode);
860 bool Delete(const char * name)
864 char namePart[MAX_LOCATION];
866 strcpy(namePart, name);
867 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
868 strcpy(namePart, DIR_SEPS);
870 position = archive.Find(this, namePart, entry);
873 archive.Delete(this, position, entry);
879 bool Move(const char * name, EARArchiveDir to)
882 if(position != to.position)
886 char namePart[MAX_LOCATION];
888 strcpy(namePart, name);
889 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
890 strcpy(namePart, DIR_SEPS);
892 position = archive.Find(this, name, entry);
895 // Unlink from old directory
898 archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
899 archive.f.Write(&entry.next, sizeof(uint), 1);
903 archive.f.Seek(entry.next + OFFSET(EAREntry, prev), start);
904 archive.f.Write(&entry.prev, sizeof(uint), 1);
906 if(last == position) last = entry.prev;
907 if(first == position) first = entry.next;
909 // Relink to new directory
910 entry.prev = to.last;
915 archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
916 archive.f.Write(&position, sizeof(uint), 1);
922 archive.f.Seek(position + OFFSET(EAREntry, prev), start);
923 archive.f.Write(&entry.prev, sizeof(uint), 1);
924 archive.f.Write(&entry.next, sizeof(uint), 1);
932 bool Rename(const char * name, const char * newName)
937 char namePart[MAX_LOCATION];
939 strcpy(namePart, name);
940 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
941 strcpy(namePart, DIR_SEPS);
943 position = archive.Find(this, namePart, entry);
947 EAREntry newEntry = entry;
948 uint newPosition = position;
950 if(entry.type == ENTRY_FOLDER)
951 dataSize = 2 * sizeof(uint);
953 dataSize = entry.cSize ? entry.cSize : entry.size;
955 newEntry.nameLen = strlen(newName);
956 if(newEntry.nameLen > entry.nameLen)
959 newPosition = archive.Position(sizeof(EAREntry) + newEntry.nameLen + dataSize);
961 archive.f.Seek(newPosition, start);
962 archive.f.Write(&newEntry, sizeof(EAREntry), 1);
963 archive.f.Write(newName, sizeof(char), newEntry.nameLen);
968 archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
969 archive.f.Write(&newPosition, sizeof(uint), 1);
973 archive.f.Seek(entry.next + OFFSET(EAREntry, prev), start);
974 archive.f.Write(&newPosition, sizeof(uint), 1);
976 if(first == position) first = newPosition;
977 if(last == position) last = newPosition;
982 archive.f.Seek(position + OFFSET(EAREntry, nameLen), start);
983 archive.f.Write(&newEntry.nameLen, sizeof(uint), 1);
984 archive.f.Seek(position + sizeof(EAREntry), start);
985 archive.f.Write(newName, sizeof(char), newEntry.nameLen);
987 // There will be free space at the end of an entry with a shorter new name
988 if(newEntry.nameLen < entry.nameLen)
989 archive.AddFreeBlock(position + sizeof(EAREntry) + newEntry.nameLen + dataSize, entry.nameLen - newEntry.nameLen);
991 if(entry.nameLen != newEntry.nameLen)
994 uint bufferSize = Min(dataSize, MAX_BUFFERSIZE);
995 buffer = new byte[bufferSize];
998 uint readPosition = position + sizeof(EAREntry) + entry.nameLen;
999 uint writePosition = newPosition + sizeof(EAREntry) + newEntry.nameLen;
1002 for(c = 0; c<dataSize; c += bufferSize)
1004 uint size = (dataSize > c + bufferSize) ? bufferSize : (dataSize - c);
1006 archive.f.Seek(readPosition + c, start);
1007 archive.f.Read(buffer, size, 1);
1009 archive.f.Seek(writePosition + c, start);
1010 archive.f.Write(buffer, size, 1);
1015 if(newEntry.nameLen > entry.nameLen)
1017 // Prevent the children to be deleted
1018 if(entry.type == ENTRY_FOLDER)
1020 uint first = 0, last = 0;
1021 archive.f.Seek(position + sizeof(EAREntry) + entry.nameLen, start);
1022 archive.f.Write(&first, sizeof(uint), 1);
1023 archive.f.Write(&last, sizeof(uint), 1);
1026 // Delete the old entry
1027 entry.prev = entry.next = 0;
1028 archive.f.Seek(position + sizeof(EAREntry) + entry.nameLen, start);
1029 archive.Delete(this, position, entry);
1037 bool AddFromFile(const char * name, File input, FileStats stats, ArchiveAddMode addMode, int compression, int * ratio, uint * newPosition)
1039 // Search for identical entry
1041 uint oldPosition = archive.Find(this, name, oldEntry);
1042 return _AddFromFileAtPosition(oldEntry, oldPosition, name, input, stats, addMode, compression, ratio, newPosition);
1045 bool AddFromFileAtPosition(uint oldPosition, const char * name, File input, FileStats stats, ArchiveAddMode addMode, int compression, int * ratio, uint * newPosition)
1050 if(!archive.f.Seek(oldPosition, start) || !archive.f.Read(oldEntry, sizeof(EAREntry), 1))
1053 return _AddFromFileAtPosition(oldEntry, oldPosition, name, input, stats, addMode, compression, ratio, newPosition);
1056 bool _AddFromFileAtPosition(EAREntry oldEntry, uint oldPosition, const char * name, File input, FileStats stats, ArchiveAddMode addMode, int compression, int * ratio, uint * newPosition)
1059 FileStats oldStats { };
1063 oldStats.modified = (TimeStamp)oldEntry.modified;
1064 oldStats.created = (TimeStamp)oldEntry.created;
1068 oldStats.size = input.GetSize();
1077 archive.Delete(this, oldPosition, oldEntry);
1079 // Only updates changed files
1082 (oldEntry.size != stats.size ||
1083 oldEntry.modified != (TimeStamp32)stats.modified ||
1084 oldEntry.created != (TimeStamp32)stats.created))
1085 archive.Delete(this, oldPosition, oldEntry);
1089 // Only updates changed or new files
1093 if(oldEntry.size != stats.size ||
1094 oldEntry.modified != (TimeStamp32)stats.modified ||
1095 oldEntry.created != (TimeStamp32)stats.created)
1096 archive.Delete(this, oldPosition, oldEntry);
1106 uint position, size;
1107 byte * compressed = null;
1110 entry.nameLen = strlen(name);
1113 entry.type = ENTRY_FILE;
1115 entry.size = stats.size;
1116 entry.created = (TimeStamp32)stats.created;
1117 entry.modified = (TimeStamp32)stats.modified;
1121 byte * uncompressed = new byte[entry.size];
1124 if(input.Read(uncompressed, 1, entry.size) == entry.size)
1126 unsigned long destLen = entry.size + entry.size / 1000 + 12;
1128 compressed = new byte[destLen];
1131 if(compression > 9 || compression < 0) compression = 9;
1132 compress2(compressed, &destLen, uncompressed, entry.size, compression);
1133 entry.cSize = (FileSize)destLen;
1136 delete uncompressed;
1147 *ratio = entry.size ? (entry.cSize * 1000 / entry.size) : 0;
1150 size = sizeof(EAREntry) + entry.nameLen + (entry.cSize ? entry.cSize : entry.size);
1151 position = archive.Position(size);
1154 if(!archive.f.Seek(position, start) ||
1155 !archive.f.Write(entry, sizeof(EAREntry), 1) ||
1156 !archive.f.Write(name, entry.nameLen, 1))
1165 if(!archive.f.Write(compressed, 1, entry.cSize))
1177 for(c = 0; c<entry.size && count; c+= count)
1179 count = input.Read(buffer, 1, sizeof(buffer));
1180 if(!archive.f.Write(buffer, 1, count))
1185 // Update the next pointer previous entry
1188 archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
1189 archive.f.Write(&position, sizeof(uint), 1);
1192 // Update total size of archive
1193 archive.totalSize += entry.size;
1196 if(!first) first = position;
1197 if(newPosition) *newPosition = position;
1201 if(newPosition) *newPosition = 0;
1204 // archive.f.handle = archive.f;
1208 #endif // !defined(ECERE_NOARCHIVE) && !defined(ECERE_VANILLA)
1210 // Directory Description for file listing
1211 class EARDir : struct
1213 char path[MAX_LOCATION];
1218 class EARFile : File
1223 // -- For reading compressed file (entirely buffered)
1226 // -- For reading uncompressed file (not buffered)
1248 int Read(byte * buffer, uint size, uint count)
1252 f.Seek(position + start, start);
1253 read = Min(count, (this.size - position) / size);
1255 CopyBytes(buffer, this.buffer + position, read * size);
1257 read = f.Read(buffer, size, read);
1258 position += read * size;
1262 int Write(const byte * buffer, uint size, uint count)
1267 bool Getc(char * ch)
1273 char b = buffer[position++];
1279 f.Seek(position + start, start);
1292 bool Puts(const char * string)
1297 bool Seek(int pos, FileSeekMode mode)
1299 bool result = false;
1303 if(pos <= (int)size)
1307 result = f.Seek(position + start, start);
1313 if(position + pos <= (int)size && (int)position >= -pos)
1317 result = f.Seek(position + start, start);
1323 if(pos < 0 && -pos <= (int)size)
1325 position = size + pos;
1327 f.Seek(position + start, start);
1343 return position >= size || (f && f.Eof());
1352 class EARFileSystem : FileSystem
1354 File ::Open(const char * archive, const char * name, FileOpenMode mode)
1362 char fileName[MAX_LOCATION];
1364 File f = EAROpenArchive(archive, &header);
1365 strcpy(fileName, name);
1367 if(!f && archive[0] == ':')
1369 f = EAROpenArchive(":", &header);
1372 strcpy(fileName, archive + 1);
1373 PathCat(fileName, name);
1380 if(EARGetEntry(f, entry, fileName, null).isFile)
1384 byte * uncompressed = new byte[entry.size];
1387 byte * compressed = new byte[entry.cSize];
1390 if(f.Read(compressed, 1, entry.cSize) == entry.cSize)
1392 unsigned long destLen = entry.size;
1393 uncompress(uncompressed, &destLen, compressed, entry.cSize);
1394 entry.size = (FileSize)destLen;
1400 file.size = entry.size;
1401 file.buffer = uncompressed;
1408 file.start = f.Tell();
1410 file.size = entry.size;
1426 FileAttribs ::Exists(const char * archive, const char * fileName)
1430 File f = EAROpenArchive(archive, &header);
1434 result = EARGetEntry(f, entry, fileName, null);
1438 if(!f && archive[0] == ':')
1440 f = EAROpenArchive(":", &header);
1444 char fn[MAX_LOCATION];
1445 strcpy(fn, archive + 1);
1446 PathCat(fn, fileName);
1447 result = EARGetEntry(f, entry, fn, null);
1455 bool ::GetSize(const char * archive, const char * fileName, FileSize * size)
1457 bool result = false;
1459 File f = EAROpenArchive(archive, &header);
1463 if(EARGetEntry(f, entry, fileName, null))
1471 bool ::Stats(const char * archive, const char * fileName, FileStats stats)
1473 bool result = false;
1475 File f = EAROpenArchive(archive, &header);
1479 if(EARGetEntry(f, entry, fileName, null))
1481 stats.size = entry.size;
1483 stats.modified = (TimeStamp)entry.modified;
1484 stats.created = (TimeStamp)entry.created;
1492 void ::FixCase(const char * archive, char * name)
1496 File f = EAROpenArchive(archive, &header);
1500 char fileName[MAX_LOCATION] = "";
1501 if(EARGetEntry(f, entry, name, fileName))
1502 strcpy(name, fileName);
1508 bool ::Find(FileDesc file, const char * archive, const char * name)
1510 bool result = false;
1515 File f = EAROpenArchive(archive, &header);
1519 if(EARGetEntry(f, entry, name, null).isDirectory)
1523 sprintf(d.path, "<%s>%s", archive, name);
1525 f.Read(&first, sizeof(uint), 1);
1526 f.Read(&last, sizeof(uint), 1);
1531 d.f.Seek(d.next, start);
1532 d.f.Read(entry, sizeof(EAREntry), 1);
1533 d.f.Read(file.name, 1, entry.nameLen);
1534 file.name[entry.nameLen] = '\0';
1535 file.stats.attribs = { isDirectory = (entry.type == ENTRY_FOLDER), isFile = (entry.type != ENTRY_FOLDER) };
1536 file.stats.accessed = file.stats.modified = (TimeStamp)entry.modified;
1537 file.stats.created = (TimeStamp)entry.created;
1538 file.stats.size = entry.size;
1540 strcpy(file.path, d.path);
1541 PathCat(file.path, file.name);
1542 d.next = entry.next;
1558 bool ::FindNext(FileDesc file)
1560 bool result = false;
1561 EARDir d = (EARDir)file.dir;
1565 d.f.Seek(d.next, start);
1566 d.f.Read(entry, sizeof(EAREntry), 1);
1567 d.f.Read(file.name, 1, entry.nameLen);
1568 file.name[entry.nameLen] = '\0';
1569 file.stats.attribs = FileAttribs { isDirectory = (entry.type == ENTRY_FOLDER), isFile = (entry.type != ENTRY_FOLDER) };
1570 file.stats.accessed = file.stats.modified = (TimeStamp)entry.modified;
1571 file.stats.created = (TimeStamp)entry.created;
1572 file.stats.size = entry.size;
1574 strcpy(file.path, d.path);
1575 PathCat(file.path, file.name);
1576 d.next = entry.next;
1583 void ::CloseDir(FileDesc file)
1585 EARDir d = (EARDir)file.dir;
1592 #if !defined(ECERE_NOARCHIVE) && !defined(ECERE_VANILLA)
1593 Archive ::OpenArchive(const char * fileName, ArchiveOpenFlags flags)
1595 Archive result = null;
1596 EARArchive archive { writeAccess = flags.writeAccess };
1599 int try = flags.waitLock ? 10 : 0;
1600 for(; try >= 0; try--)
1602 // Check for existing Archive
1603 if((archive.f = fileName ? (flags.buffered ? FileOpenBuffered : FileOpen)(fileName, flags.writeAccess ? readWrite : read) : TempFile { openMode = readWrite } ))
1606 bool opened = false;
1607 uint archiveSize = 0;
1608 archive.f.Seek(-(int)sizeof(uint), end);
1609 archive.f.Read(&archiveSize, sizeof(uint), 1);
1610 archive.f.Seek(-(int)archiveSize, end);
1612 archive.archiveStart = archive.f.Tell();
1613 if(archive.f.Read(&header, sizeof(EARHeader), 1) == 1 &&
1614 !memcmp(header.recognition, earRecognition, sizeof(earRecognition)))
1619 archive.f.Seek(0, start);
1620 archive.archiveStart = archive.f.Tell();
1621 archiveSize = archive.f.GetSize();
1622 if(archive.f.Read(&header, sizeof(EARHeader), 1) == 1 &&
1623 !memcmp(header.recognition, earRecognition, sizeof(earRecognition)))
1629 // At this point we recognized the file as a valid eAR archive
1630 archive.rootDir = archive.archiveStart + sizeof(EARHeader);
1631 archive.totalSize = header.totalSize;
1633 archive.f.Seek(archive.rootDir, start);
1634 if(flags.writeAccess)
1638 archive.freeBlocks.Add(FreeBlock { start = archive.rootDir + 2 * sizeof(uint), end = MAXDWORD });
1639 archive.SubtractUsedBlocks();
1643 archive.freeBlocks.Add(FreeBlock { start = archive.archiveStart + (archiveSize - sizeof(uint)), end = MAXDWORD });
1648 if(!flags.writeAccess)
1651 archive.f = FileOpen(fileName, readWrite);
1666 // This piece of code will create a new archive as a new file or at the footer
1667 // of an existing file.
1668 if(!result && flags.writeAccess)
1670 // If the file doesn't exist, create it
1673 archive.f = FileOpen(fileName, writeRead);
1675 archive.f = (flags.buffered ? FileOpenBuffered : FileOpen)(fileName, readWrite);
1685 archive.f.Seek(0, end);
1687 archive.archiveStart = archive.f.Tell();
1688 archive.freeBlocks.Add(FreeBlock { start = archive.archiveStart + sizeof(EARHeader), end = MAXDWORD });
1691 archive.f.Write(&header, sizeof(EARHeader), 1);
1693 uint first = 0, last = 0;
1694 archive.f.Write(&first, sizeof(first), 1);
1695 archive.f.Write(&last, sizeof(last), 1);
1698 archive.rootDir = 0;
1703 if(archive.f && flags.writeAccess && flags.exclusive && !archive.f.Lock(flags.exclusive ? exclusive : shared, 0, 0, flags.waitLock))
1712 // archive.f.handle = archive.f;
1717 #endif // !defined(ECERE_NOARCHIVE) && !defined(ECERE_VANILLA)
1718 bool ::QuerySize(const char * archive, FileSize * size)
1720 bool result = false;
1722 File f = EAROpenArchive(archive, &header);
1725 *size = header.totalSize;