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(LocateModule(name, moduleName))
55 f = FileOpen(moduleName, read);
58 f = FileOpen(archive, read);
63 // First attempt to treat this as an archive file
64 if(f.Read(header, sizeof(EARHeader), 1) == 1 &&
65 !memcmp(header.recognition, earRecognition, sizeof(earRecognition)))
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 !memcmp(header.recognition, earRecognition, sizeof(earRecognition)))
81 static FileAttribs EARGetEntry(File f, EAREntry entry, const char * name, char * path)
83 uint first = 0, last = 0;
85 return FileAttribs { isDirectory = true };
86 if(!f.Read(&first, sizeof(uint), 1))
88 f.Read(&last, sizeof(uint), 1);
91 char namePart[MAX_FILENAME], nameRest[MAX_LOCATION];
92 SplitDirectory(name, namePart, nameRest);
94 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
95 strcpy(namePart, DIR_SEPS);
100 char fileName[MAX_FILENAME];
102 f.Read(entry, sizeof(EAREntry), 1);
103 f.Read(fileName, 1, entry.nameLen);
104 fileName[entry.nameLen] = '\0';
106 if(!strcmp(fileName, "/") || !strcmp(fileName, "\\"))
107 strcpy(fileName, DIR_SEPS);
109 if(!fstrcmp(fileName, namePart))
112 PathCat(path, fileName);
114 return EARGetEntry(f, entry, nameRest, path);
116 return (entry.type == ENTRY_FILE) ? FileAttribs { isFile = true } : FileAttribs { isDirectory = true };
119 f.Seek(entry.next, start);
124 return FileAttribs { };
127 #if !defined(ECERE_NOARCHIVE) && !defined(ECERE_VANILLA)
128 class EARArchive : Archive
131 //BufferedFile bf { };
132 // char path[MAX_LOCATION];
144 end = ((FreeBlock)freeBlocks.last).start;
147 f.Seek(archiveStart + OFFSET(EARHeader, totalSize), start);
148 f.Write(&totalSize, sizeof(uint), 1);
157 f.Write(&end, sizeof(uint), 1);
158 f.Truncate(archiveStart + end);
167 if(f && rootDir && writeAccess)
171 archiveStart += Update();
176 f.Unlock(0, 0, true);
180 /*if(rootDir && writeAccess)
182 // Fix the size of the archive
183 FileTruncate(path, archiveStart);
186 freeBlocks.Free(null);
195 ArchiveDir OpenDirectory(const char * name, FileStats stats, ArchiveAddMode addMode)
197 ArchiveDir result = null;
198 EARArchiveDir dir { readOnly = addMode == readOnlyDir };
201 char namePart[MAX_LOCATION] = "", nameRest[MAX_LOCATION];
205 strcpy(nameRest, name);
207 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
208 strcpy(namePart, DIR_SEPS);
210 // Search for directory
213 dir.position = rootDir;
214 if(f.Seek(dir.position, start))
219 f.Read(&dir.first, sizeof(uint), 1);
220 f.Read(&dir.last, sizeof(uint), 1);
226 // If directory doesn't exist already
227 if(!result && addMode != refresh)
229 rootDir = Position(2*sizeof(uint));
230 dir.position = rootDir;
235 // Open rest of directory...
236 if(result && nameRest[0])
238 result = dir.OpenDirectory(nameRest, stats, addMode);
245 uint Position(uint size)
248 for(block = freeBlocks.first; block; block = block.next)
250 if(block.end - block.start + 1 >= size)
252 uint position = block.start;
253 if(block.end - block.start + 1 == size)
254 freeBlocks.Delete(block);
263 bool DefragOffset(uint * offset)
267 for(block = freeBlocks.first; block; block = block.next)
269 if(*offset > block.start)
270 subtract += block.end - block.start + 1;
282 #define MAX_BUFFERSIZE 0x400000
284 void Defrag(uint dirPosition)
286 // Update all offsets within the files
287 uint first = 0, last = 0;
288 uint position = 0, next = 0;
290 f.Seek(dirPosition, start);
291 f.Read(&first, sizeof(uint), 1);
292 f.Read(&last, sizeof(uint), 1);
296 if(first && DefragOffset(&first))
298 if(f.Seek(dirPosition, start))
299 f.Write(&first, sizeof(uint), 1);
301 if(last && DefragOffset(&last))
303 if(f.Seek(dirPosition + sizeof(uint), start))
304 f.Write(&last, sizeof(uint), 1);
307 for(; position; position = next)
311 if(f.Seek(position, start) && f.Read(entry, sizeof(EAREntry), 1))
315 if(entry.prev && DefragOffset(&entry.prev))
317 f.Seek(position + OFFSET(EAREntry, prev), start);
318 f.Write(&entry.prev, sizeof(uint), 1);
320 if(entry.next && DefragOffset(&entry.next))
322 f.Seek(position + OFFSET(EAREntry, next), start);
323 f.Write(&entry.next, sizeof(uint), 1);
326 if(entry.type == ENTRY_FOLDER)
327 Defrag(position + sizeof(EAREntry) + entry.nameLen);
333 // Move all the blocks
334 if(dirPosition == rootDir)
337 byte * buffer = null;
338 FreeBlock block, nextBlock;
339 for(block = freeBlocks.first; block && block.next; block = nextBlock)
343 nextBlock = block.next;
344 dataSize = nextBlock.start - (block.end + 1);
346 if(dataSize > bufferSize && (!bufferSize || bufferSize < MAX_BUFFERSIZE))
348 bufferSize = Min(dataSize, MAX_BUFFERSIZE);
349 buffer = renew buffer byte[bufferSize];
352 for(c = 0; c<dataSize; c += bufferSize)
354 uint size = (dataSize > c + bufferSize) ? bufferSize : (dataSize - c);
356 // Read block of data
357 f.Seek((block.end + 1) + c, start);
358 f.Read(buffer, size, 1);
360 // Write block of data
361 f.Seek(block.start + c, start);
362 f.Write(buffer, size, 1);
365 nextBlock.start -= (block.end - block.start) + 1;
367 freeBlocks.Delete(block);
373 uint Find(EARArchiveDir directory, const char * namePart, EAREntry entry)
376 for(position = directory.first; position; position = entry.next)
378 char fileName[MAX_FILENAME];
380 if(f.Seek(position, start) && f.Read(entry, sizeof(EAREntry), 1))
382 if(entry.nameLen > MAX_FILENAME)
383 return 0; // CORRUPTION ERROR
384 f.Read(fileName, 1, entry.nameLen);
385 fileName[entry.nameLen] = '\0';
387 if(!strcmp(fileName, "/") || !strcmp(fileName, "\\"))
388 strcpy(fileName, DIR_SEPS);
390 if(!fstrcmp(fileName, namePart))
394 return 0; // ERROR OUT OF SPACE?
399 void AddFreeBlock(uint position, uint size)
401 FreeBlock block, prevBlock, nextBlock = null;
403 // Find the previous and next free block
405 for(block = freeBlocks.first; block; block = block.next)
406 if(block.end < position)
414 // Try to merge with previous block
415 if(prevBlock && prevBlock.end + 1 == position)
417 prevBlock.end += size;
418 // Try to merge with next block as well
419 if(nextBlock && nextBlock.start == prevBlock.end + 1)
421 prevBlock.end = nextBlock.end;
422 freeBlocks.Delete(nextBlock);
425 // Try to merge with next block
426 else if(nextBlock && nextBlock.start == position + size)
428 nextBlock.start = position;
430 // This free block is not connected to any other block
433 freeBlocks.Insert(prevBlock, FreeBlock { start = position, end = position + size - 1 });
437 void SubtractBlock(uint start, uint size)
440 for(block = freeBlocks.first; block; block = block.next)
442 if(block.end >= start - 1L && block.start <= start + size)
444 if(block.end > start + size && block.start < start - 1L)
446 FreeBlock newBlock { start = start + size, end = block.end };
447 block.end = start - 1L;
448 freeBlocks.Insert(block, newBlock);
450 else if(block.end > start + size)
452 block.start = start + size;
454 else if(block.start < start - 1L)
456 block.end = start - 1L;
460 freeBlocks.Remove(block);
468 void Delete(EARArchiveDir dir, uint position, EAREntry entry)
471 if(entry.type == ENTRY_FOLDER)
473 EARArchiveDir subDir {};
477 subDir.position = dir.position;
478 f.Read(&subDir.first, sizeof(uint), 1);
479 f.Read(&subDir.last, sizeof(uint), 1);
481 // Erase directory contents first
482 for(filePosition = subDir.first; filePosition; filePosition = fileEntry.next)
484 f.Seek(filePosition, start);
485 f.Read(&fileEntry, sizeof(EAREntry), 1);
486 f.Seek(fileEntry.nameLen, current);
487 Delete(subDir, filePosition, &fileEntry);
489 size = sizeof(EAREntry) + entry.nameLen + 2 * sizeof(uint);
493 size = sizeof(EAREntry) + entry.nameLen + (entry.cSize ? entry.cSize : entry.size);
498 f.Seek(entry.prev + OFFSET(EAREntry, next), start);
499 f.Write(&entry.next, sizeof(uint), 1);
503 f.Seek(entry.next + OFFSET(EAREntry, prev), start);
504 f.Write(&entry.prev, sizeof(uint), 1);
506 if(dir.last == position) dir.last = entry.prev;
507 if(dir.first == position) dir.first = entry.next;
509 AddFreeBlock(position, size);
510 totalSize -= entry.size;
516 File FileOpen(const char * name)
524 f.Seek(archiveStart + sizeof(EARHeader), start);
525 if(EARGetEntry(f, entry, name, null).isFile)
529 byte * uncompressed = new byte[entry.size];
532 byte * compressed = new byte[entry.cSize];
535 if(f.Read(compressed, 1, entry.cSize) == entry.cSize)
537 unsigned long destLen = entry.size;
538 uncompress(uncompressed, &destLen, compressed, entry.cSize);
539 entry.size = (FileSize)destLen; // TODO: Support 64 bit file sizes
545 file.size = entry.size;
546 file.buffer = uncompressed;
553 file.start = f.Tell();
555 file.size = entry.size;
558 file.f.Seek(file.start, start);
568 File FileOpenAtPosition(uint position)
571 char fileName[MAX_LOCATION];
573 f.Seek(position, start);
574 f.Read(entry, sizeof(EAREntry), 1);
575 /*if(entry.nameLen > 1024)
577 f.Read(fileName, 1, entry.nameLen);
580 byte * uncompressed = new byte[entry.size];
583 byte * compressed = new byte[entry.cSize];
586 if(f.Read(compressed, 1, entry.cSize) == entry.cSize)
588 unsigned long destLen = entry.size;
589 uncompress(uncompressed, &destLen, compressed, entry.cSize);
590 entry.size = (FileSize)destLen;
596 file.size = entry.size;
597 file.buffer = uncompressed;
602 file.start = f.Tell();
604 file.size = entry.size;
606 file.f.Seek(file.start, start);
612 FileAttribs FileExists(const char * fileName)
616 f.Seek(archiveStart + sizeof(EARHeader), start);
617 result = EARGetEntry(f, entry, fileName, null);
621 void SubtractUsedBlocks()
624 if(!f.Read(&first, sizeof(uint), 1))
627 if(first > f.GetSize())
632 f.Read(&last, sizeof(uint), 1);
638 char fileName[MAX_FILENAME];
641 f.Seek(first, start);
642 f.Read(entry, sizeof(EAREntry), 1);
643 if(entry.nameLen < MAX_FILENAME)
645 f.Read(fileName, 1, entry.nameLen);
646 fileName[entry.nameLen] = 0;
654 size += sizeof(EAREntry) + entry.nameLen;
656 if(entry.type == ENTRY_FILE)
658 size += entry.cSize ? entry.cSize : entry.size;
660 else if(entry.type == ENTRY_FOLDER)
662 size += 2 * sizeof(uint);
663 SubtractUsedBlocks();
665 SubtractBlock(first, size);
670 void SetBufferSize(uint bufferSize)
672 if(f && f._class == class(BufferedFile))
673 ((BufferedFile)f).bufferSize = bufferSize;
676 void SetBufferRead(uint bufferRead)
678 if(f && f._class == class(BufferedFile))
679 ((BufferedFile)f).bufferRead = bufferRead;
683 class EARArchiveDir : ArchiveDir
694 archive.f.Seek(position, start);
695 archive.f.Write(&first, sizeof(uint), 1);
696 archive.f.Write(&last, sizeof(uint), 1);
701 File FileOpen(const char * name)
709 archive.f.Seek(position, start);
710 if(EARGetEntry(archive.f, entry, name, null).isFile)
714 byte * uncompressed = new byte[entry.size];
717 byte * compressed = new byte[entry.cSize];
720 if(archive.f.Read(compressed, 1, entry.cSize) == entry.cSize)
722 unsigned long destLen = entry.size;
723 uncompress(uncompressed, &destLen, compressed, entry.cSize);
724 entry.size = (FileSize)destLen;
730 file.size = entry.size;
731 file.buffer = uncompressed;
738 file.start = archive.f.Tell();
740 file.size = entry.size;
742 file.f.Seek(file.start, start);
753 FileAttribs FileExists(const char * fileName)
757 archive.f.Seek(position, start);
758 result = EARGetEntry(archive.f, entry, fileName, null);
762 ArchiveDir OpenDirectory(const char * name, FileStats stats, ArchiveAddMode addMode)
764 ArchiveDir result = null;
765 EARArchiveDir dir { readOnly = addMode == readOnlyDir };
768 char namePart[MAX_LOCATION] = "", nameRest[MAX_LOCATION];
772 dir.archive = archive;
774 SplitDirectory(name, namePart, nameRest);
776 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
777 strcpy(namePart, DIR_SEPS);
779 // Search for directory
781 position = archive.Find(this, namePart, entry);
784 // Fail if file of same name already exists
785 if(entry.type == ENTRY_FILE)
789 dir.position = position + sizeof(EAREntry) + entry.nameLen;
793 archive.f.Read(&dir.first, sizeof(uint), 1);
794 archive.f.Read(&dir.last, sizeof(uint), 1);
800 // If directory doesn't exist already
801 if(!result && addMode != refresh)
803 // Write Header if it's not the root directory
807 entry.nameLen = strlen(namePart);
810 entry.type = ENTRY_FOLDER;
811 if(!nameRest[0] && stats)
813 entry.created = (TimeStamp32)stats.created;
814 entry.modified = (TimeStamp32)stats.modified;
817 position = archive.Position(sizeof(EAREntry) + entry.nameLen + 2*sizeof(uint));
819 archive.f.Seek(position, start);
820 archive.f.Write(entry, sizeof(EAREntry), 1);
821 archive.f.Write(namePart, entry.nameLen, 1);
824 if(!first) first = position;
826 // Update the next pointer of previous entry
829 archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
830 archive.f.Write(&position, sizeof(uint), 1);
833 // Make the dir position point after the header
834 dir.position = position + sizeof(EAREntry) + entry.nameLen;
836 // Just update the time stamps
837 else if(result && !nameRest[0] && stats)
839 archive.f.Seek(position + OFFSET(EAREntry, created), start);
840 archive.f.Write(&stats.created, sizeof(uint), 1);
841 archive.f.Write(&stats.modified, sizeof(uint), 1);
845 // Open rest of directory...
846 if(result && nameRest[0])
848 result = dir.OpenDirectory(nameRest, stats, addMode);
855 bool Delete(const char * name)
859 char namePart[MAX_LOCATION];
861 strcpy(namePart, name);
862 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
863 strcpy(namePart, DIR_SEPS);
865 position = archive.Find(this, namePart, entry);
868 archive.Delete(this, position, entry);
874 bool Move(const char * name, EARArchiveDir to)
877 if(position != to.position)
881 char namePart[MAX_LOCATION];
883 strcpy(namePart, name);
884 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
885 strcpy(namePart, DIR_SEPS);
887 position = archive.Find(this, name, entry);
890 // Unlink from old directory
893 archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
894 archive.f.Write(&entry.next, sizeof(uint), 1);
898 archive.f.Seek(entry.next + OFFSET(EAREntry, prev), start);
899 archive.f.Write(&entry.prev, sizeof(uint), 1);
901 if(last == position) last = entry.prev;
902 if(first == position) first = entry.next;
904 // Relink to new directory
905 entry.prev = to.last;
910 archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
911 archive.f.Write(&position, sizeof(uint), 1);
917 archive.f.Seek(position + OFFSET(EAREntry, prev), start);
918 archive.f.Write(&entry.prev, sizeof(uint), 1);
919 archive.f.Write(&entry.next, sizeof(uint), 1);
927 bool Rename(const char * name, const char * newName)
932 char namePart[MAX_LOCATION];
934 strcpy(namePart, name);
935 if(!strcmp(namePart, "/") || !strcmp(namePart, "\\"))
936 strcpy(namePart, DIR_SEPS);
938 position = archive.Find(this, namePart, entry);
942 EAREntry newEntry = entry;
943 uint newPosition = position;
945 if(entry.type == ENTRY_FOLDER)
946 dataSize = 2 * sizeof(uint);
948 dataSize = entry.cSize ? entry.cSize : entry.size;
950 newEntry.nameLen = strlen(newName);
951 if(newEntry.nameLen > entry.nameLen)
954 newPosition = archive.Position(sizeof(EAREntry) + newEntry.nameLen + dataSize);
956 archive.f.Seek(newPosition, start);
957 archive.f.Write(&newEntry, sizeof(EAREntry), 1);
958 archive.f.Write(newName, sizeof(char), newEntry.nameLen);
963 archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
964 archive.f.Write(&newPosition, sizeof(uint), 1);
968 archive.f.Seek(entry.next + OFFSET(EAREntry, prev), start);
969 archive.f.Write(&newPosition, sizeof(uint), 1);
971 if(first == position) first = newPosition;
972 if(last == position) last = newPosition;
977 archive.f.Seek(position + OFFSET(EAREntry, nameLen), start);
978 archive.f.Write(&newEntry.nameLen, sizeof(uint), 1);
979 archive.f.Seek(position + sizeof(EAREntry), start);
980 archive.f.Write(newName, sizeof(char), newEntry.nameLen);
982 // There will be free space at the end of an entry with a shorter new name
983 if(newEntry.nameLen < entry.nameLen)
984 archive.AddFreeBlock(position + sizeof(EAREntry) + newEntry.nameLen + dataSize, entry.nameLen - newEntry.nameLen);
986 if(entry.nameLen != newEntry.nameLen)
989 uint bufferSize = Min(dataSize, MAX_BUFFERSIZE);
990 buffer = new byte[bufferSize];
993 uint readPosition = position + sizeof(EAREntry) + entry.nameLen;
994 uint writePosition = newPosition + sizeof(EAREntry) + newEntry.nameLen;
997 for(c = 0; c<dataSize; c += bufferSize)
999 uint size = (dataSize > c + bufferSize) ? bufferSize : (dataSize - c);
1001 archive.f.Seek(readPosition + c, start);
1002 archive.f.Read(buffer, size, 1);
1004 archive.f.Seek(writePosition + c, start);
1005 archive.f.Write(buffer, size, 1);
1010 if(newEntry.nameLen > entry.nameLen)
1012 // Prevent the children to be deleted
1013 if(entry.type == ENTRY_FOLDER)
1015 uint first = 0, last = 0;
1016 archive.f.Seek(position + sizeof(EAREntry) + entry.nameLen, start);
1017 archive.f.Write(&first, sizeof(uint), 1);
1018 archive.f.Write(&last, sizeof(uint), 1);
1021 // Delete the old entry
1022 entry.prev = entry.next = 0;
1023 archive.f.Seek(position + sizeof(EAREntry) + entry.nameLen, start);
1024 archive.Delete(this, position, entry);
1032 bool AddFromFile(const char * name, File input, FileStats stats, ArchiveAddMode addMode, int compression, int * ratio, uint * newPosition)
1034 // Search for identical entry
1036 uint oldPosition = archive.Find(this, name, oldEntry);
1037 return _AddFromFileAtPosition(oldEntry, oldPosition, name, input, stats, addMode, compression, ratio, newPosition);
1040 bool AddFromFileAtPosition(uint oldPosition, const char * name, File input, FileStats stats, ArchiveAddMode addMode, int compression, int * ratio, uint * newPosition)
1045 if(!archive.f.Seek(oldPosition, start) || !archive.f.Read(oldEntry, sizeof(EAREntry), 1))
1048 return _AddFromFileAtPosition(oldEntry, oldPosition, name, input, stats, addMode, compression, ratio, newPosition);
1051 bool _AddFromFileAtPosition(EAREntry oldEntry, uint oldPosition, const char * name, File input, FileStats stats, ArchiveAddMode addMode, int compression, int * ratio, uint * newPosition)
1054 FileStats oldStats { };
1058 oldStats.modified = (TimeStamp)oldEntry.modified;
1059 oldStats.created = (TimeStamp)oldEntry.created;
1063 oldStats.size = input.GetSize();
1072 archive.Delete(this, oldPosition, oldEntry);
1074 // Only updates changed files
1077 (oldEntry.size != stats.size ||
1078 oldEntry.modified != (TimeStamp32)stats.modified ||
1079 oldEntry.created != (TimeStamp32)stats.created))
1080 archive.Delete(this, oldPosition, oldEntry);
1084 // Only updates changed or new files
1088 if(oldEntry.size != stats.size ||
1089 oldEntry.modified != (TimeStamp32)stats.modified ||
1090 oldEntry.created != (TimeStamp32)stats.created)
1091 archive.Delete(this, oldPosition, oldEntry);
1101 uint position, size;
1102 byte * compressed = null;
1105 entry.nameLen = strlen(name);
1108 entry.type = ENTRY_FILE;
1110 entry.size = stats.size;
1111 entry.created = (TimeStamp32)stats.created;
1112 entry.modified = (TimeStamp32)stats.modified;
1116 byte * uncompressed = new byte[entry.size];
1119 if(input.Read(uncompressed, 1, entry.size) == entry.size)
1121 unsigned long destLen = entry.size + entry.size / 1000 + 12;
1123 compressed = new byte[destLen];
1126 if(compression > 9 || compression < 0) compression = 9;
1127 compress2(compressed, &destLen, uncompressed, entry.size, compression);
1128 entry.cSize = (FileSize)destLen;
1131 delete uncompressed;
1142 *ratio = entry.size ? (entry.cSize * 1000 / entry.size) : 0;
1145 size = sizeof(EAREntry) + entry.nameLen + (entry.cSize ? entry.cSize : entry.size);
1146 position = archive.Position(size);
1149 if(!archive.f.Seek(position, start) ||
1150 !archive.f.Write(entry, sizeof(EAREntry), 1) ||
1151 !archive.f.Write(name, entry.nameLen, 1))
1160 if(!archive.f.Write(compressed, 1, entry.cSize))
1172 for(c = 0; c<entry.size && count; c+= count)
1174 count = input.Read(buffer, 1, sizeof(buffer));
1175 if(!archive.f.Write(buffer, 1, count))
1180 // Update the next pointer previous entry
1183 archive.f.Seek(entry.prev + OFFSET(EAREntry, next), start);
1184 archive.f.Write(&position, sizeof(uint), 1);
1187 // Update total size of archive
1188 archive.totalSize += entry.size;
1191 if(!first) first = position;
1192 if(newPosition) *newPosition = position;
1196 if(newPosition) *newPosition = 0;
1199 // archive.f.handle = archive.f;
1203 #endif // !defined(ECERE_NOARCHIVE) && !defined(ECERE_VANILLA)
1205 // Directory Description for file listing
1206 class EARDir : struct
1208 char path[MAX_LOCATION];
1213 class EARFile : File
1218 // -- For reading compressed file (entirely buffered)
1221 // -- For reading uncompressed file (not buffered)
1243 int Read(byte * buffer, uint size, uint count)
1247 f.Seek(position + start, start);
1248 read = Min(count, (this.size - position) / size);
1250 CopyBytes(buffer, this.buffer + position, read * size);
1252 read = f.Read(buffer, size, read);
1253 position += read * size;
1257 int Write(const byte * buffer, uint size, uint count)
1262 bool Getc(char * ch)
1268 char b = buffer[position++];
1274 f.Seek(position + start, start);
1287 bool Puts(const char * string)
1292 bool Seek(int pos, FileSeekMode mode)
1294 bool result = false;
1298 if(pos <= (int)size)
1302 result = f.Seek(position + start, start);
1308 if(position + pos <= (int)size && (int)position >= -pos)
1312 result = f.Seek(position + start, start);
1318 if(pos < 0 && -pos <= (int)size)
1320 position = size + pos;
1322 f.Seek(position + start, start);
1338 return position >= size || (f && f.Eof());
1347 class EARFileSystem : FileSystem
1349 File ::Open(const char * archive, const char * name, FileOpenMode mode)
1357 char fileName[MAX_LOCATION];
1359 File f = EAROpenArchive(archive, &header);
1360 strcpy(fileName, name);
1362 if(!f && archive[0] == ':')
1364 f = EAROpenArchive(":", &header);
1367 strcpy(fileName, archive + 1);
1368 PathCat(fileName, name);
1375 if(EARGetEntry(f, entry, fileName, null).isFile)
1379 byte * uncompressed = new byte[entry.size];
1382 byte * compressed = new byte[entry.cSize];
1385 if(f.Read(compressed, 1, entry.cSize) == entry.cSize)
1387 unsigned long destLen = entry.size;
1388 uncompress(uncompressed, &destLen, compressed, entry.cSize);
1389 entry.size = (FileSize)destLen;
1395 file.size = entry.size;
1396 file.buffer = uncompressed;
1403 file.start = f.Tell();
1405 file.size = entry.size;
1421 FileAttribs ::Exists(const char * archive, const char * fileName)
1425 File f = EAROpenArchive(archive, &header);
1429 result = EARGetEntry(f, entry, fileName, null);
1435 bool ::GetSize(const char * archive, const char * fileName, FileSize * size)
1437 bool result = false;
1439 File f = EAROpenArchive(archive, &header);
1443 if(EARGetEntry(f, entry, fileName, null))
1451 bool ::Stats(const char * archive, const char * fileName, FileStats stats)
1453 bool result = false;
1455 File f = EAROpenArchive(archive, &header);
1459 if(EARGetEntry(f, entry, fileName, null))
1461 stats.size = entry.size;
1463 stats.modified = (TimeStamp)entry.modified;
1464 stats.created = (TimeStamp)entry.created;
1472 void ::FixCase(const char * archive, char * name)
1476 File f = EAROpenArchive(archive, &header);
1480 char fileName[MAX_LOCATION] = "";
1481 if(EARGetEntry(f, entry, name, fileName))
1482 strcpy(name, fileName);
1488 bool ::Find(FileDesc file, const char * archive, const char * name)
1490 bool result = false;
1495 File f = EAROpenArchive(archive, &header);
1499 if(EARGetEntry(f, entry, name, null).isDirectory)
1503 sprintf(d.path, "<%s>%s", archive, name);
1505 f.Read(&first, sizeof(uint), 1);
1506 f.Read(&last, sizeof(uint), 1);
1511 d.f.Seek(d.next, start);
1512 d.f.Read(entry, sizeof(EAREntry), 1);
1513 d.f.Read(file.name, 1, entry.nameLen);
1514 file.name[entry.nameLen] = '\0';
1515 file.stats.attribs = { isDirectory = (entry.type == ENTRY_FOLDER), isFile = (entry.type != ENTRY_FOLDER) };
1516 file.stats.accessed = file.stats.modified = (TimeStamp)entry.modified;
1517 file.stats.created = (TimeStamp)entry.created;
1518 file.stats.size = entry.size;
1520 strcpy(file.path, d.path);
1521 PathCat(file.path, file.name);
1522 d.next = entry.next;
1538 bool ::FindNext(FileDesc file)
1540 bool result = false;
1541 EARDir d = (EARDir)file.dir;
1545 d.f.Seek(d.next, start);
1546 d.f.Read(entry, sizeof(EAREntry), 1);
1547 d.f.Read(file.name, 1, entry.nameLen);
1548 file.name[entry.nameLen] = '\0';
1549 file.stats.attribs = FileAttribs { isDirectory = (entry.type == ENTRY_FOLDER), isFile = (entry.type != ENTRY_FOLDER) };
1550 file.stats.accessed = file.stats.modified = (TimeStamp)entry.modified;
1551 file.stats.created = (TimeStamp)entry.created;
1552 file.stats.size = entry.size;
1554 strcpy(file.path, d.path);
1555 PathCat(file.path, file.name);
1556 d.next = entry.next;
1563 void ::CloseDir(FileDesc file)
1565 EARDir d = (EARDir)file.dir;
1572 #if !defined(ECERE_NOARCHIVE) && !defined(ECERE_VANILLA)
1573 Archive ::OpenArchive(const char * fileName, ArchiveOpenFlags flags)
1575 Archive result = null;
1576 EARArchive archive { writeAccess = flags.writeAccess };
1579 int try = flags.waitLock ? 10 : 0;
1580 for(; try >= 0; try--)
1582 // Check for existing Archive
1583 if((archive.f = fileName ? (flags.buffered ? FileOpenBuffered : FileOpen)(fileName, flags.writeAccess ? readWrite : read) : TempFile { openMode = readWrite } ))
1586 bool opened = false;
1587 uint archiveSize = 0;
1588 archive.f.Seek(-(int)sizeof(uint), end);
1589 archive.f.Read(&archiveSize, sizeof(uint), 1);
1590 archive.f.Seek(-(int)archiveSize, end);
1592 archive.archiveStart = archive.f.Tell();
1593 if(archive.f.Read(&header, sizeof(EARHeader), 1) == 1 &&
1594 !memcmp(header.recognition, earRecognition, sizeof(earRecognition)))
1599 archive.f.Seek(0, start);
1600 archive.archiveStart = archive.f.Tell();
1601 archiveSize = archive.f.GetSize();
1602 if(archive.f.Read(&header, sizeof(EARHeader), 1) == 1 &&
1603 !memcmp(header.recognition, earRecognition, sizeof(earRecognition)))
1609 // At this point we recognized the file as a valid eAR archive
1610 archive.rootDir = archive.archiveStart + sizeof(EARHeader);
1611 archive.totalSize = header.totalSize;
1613 archive.f.Seek(archive.rootDir, start);
1616 archive.freeBlocks.Add(FreeBlock { start = archive.rootDir + 2 * sizeof(uint), end = MAXDWORD });
1617 archive.SubtractUsedBlocks();
1621 archive.freeBlocks.Add(FreeBlock { start = archive.archiveStart + (archiveSize - sizeof(uint)), end = MAXDWORD });
1625 if(!flags.writeAccess)
1628 archive.f = FileOpen(fileName, readWrite);
1643 // This piece of code will create a new archive as a new file or at the footer
1644 // of an existing file.
1645 if(!result && flags.writeAccess)
1647 // If the file doesn't exist, create it
1650 archive.f = FileOpen(fileName, writeRead);
1652 archive.f = (flags.buffered ? FileOpenBuffered : FileOpen)(fileName, readWrite);
1662 archive.f.Seek(0, end);
1664 archive.archiveStart = archive.f.Tell();
1665 archive.freeBlocks.Add(FreeBlock { start = archive.archiveStart + sizeof(EARHeader), end = MAXDWORD });
1668 archive.f.Write(&header, sizeof(EARHeader), 1);
1670 uint first = 0, last = 0;
1671 archive.f.Write(&first, sizeof(first), 1);
1672 archive.f.Write(&last, sizeof(last), 1);
1675 archive.rootDir = 0;
1680 if(archive.f && flags.writeAccess && flags.exclusive && !archive.f.Lock(flags.exclusive ? exclusive : shared, 0, 0, flags.waitLock))
1689 // archive.f.handle = archive.f;
1694 #endif // !defined(ECERE_NOARCHIVE) && !defined(ECERE_VANILLA)
1695 bool ::QuerySize(const char * archive, FileSize * size)
1697 bool result = false;
1699 File f = EAROpenArchive(archive, &header);
1702 *size = header.totalSize;