private static void fileCache_updateFiletable(FileCache fileCache, Int32 extraEntriesToAllocate) { // recreate file table with bigger size (optional) fileCache.FileTableEntries[0].Name1 = FilecacheFiletableFreeName; fileCache.FileTableEntries[0].Name2 = FilecacheFiletableFreeName; Int32 newFileTableEntryCount = (Int32)fileCache.FileTableEntryCount + extraEntriesToAllocate; Array.Resize(ref fileCache.FileTableEntries, newFileTableEntryCount); for (Int32 f = (Int32)fileCache.FileTableEntryCount; f < newFileTableEntryCount; f++) { fileCache.FileTableEntries[f] = new FileCacheEntry { Name1 = FilecacheFiletableFreeName, Name2 = FilecacheFiletableFreeName, FileOffset = 0, FileSize = 0 }; } fileCache.FileTableEntryCount = (UInt32)newFileTableEntryCount; byte[] fileTable = AsByteArray(fileCache.FileTableEntries); fileCache_addFile(fileCache, FilecacheFiletableName1, FilecacheFiletableName2, fileTable, SizeofFileCacheEntryT * newFileTableEntryCount); // update file table info in struct if (fileCache.FileTableEntries[0].Name1 != FilecacheFiletableName1 || fileCache.FileTableEntries[0].Name2 != FilecacheFiletableName2) { __debugbreak(); } fileCache.FileTableOffset = fileCache.FileTableEntries[0].FileOffset; fileCache.FileTableSize = fileCache.FileTableEntries[0].FileSize; // update header stream_setSeek64(fileCache.StreamFile2, 0); stream_writeU32(fileCache.StreamFile2, FilecacheMagicV2); stream_writeU32(fileCache.StreamFile2, fileCache.ExtraVersion); stream_writeU64(fileCache.StreamFile2, fileCache.DataOffset); stream_writeU64(fileCache.StreamFile2, fileCache.FileTableOffset); stream_writeU32(fileCache.StreamFile2, fileCache.FileTableSize); }
public static FileCache fileCache_create(string path, UInt32 extraVersion) { FileCache fileCache = new FileCache(); BinaryWriter streamFile = new BinaryWriter(File.Open(path, FileMode.Create)); // init file cache fileCache.StreamFile2 = streamFile; fileCache.DataOffset = FilecacheHeaderResv; fileCache.FileTableEntryCount = 32; fileCache.FileTableOffset = 0; fileCache.FileTableSize = SizeofFileCacheEntryT * fileCache.FileTableEntryCount; fileCache.FileTableEntries = new FileCacheEntry[fileCache.FileTableEntryCount]; for (Int32 f = 0; f < (Int32)fileCache.FileTableEntryCount; f++) { fileCache.FileTableEntries[f] = new FileCacheEntry(); } fileCache.ExtraVersion = extraVersion; // file table stores info about itself fileCache.FileTableEntries[0].Name1 = FilecacheFiletableName1; fileCache.FileTableEntries[0].Name2 = FilecacheFiletableName2; fileCache.FileTableEntries[0].FileOffset = fileCache.FileTableOffset; fileCache.FileTableEntries[0].FileSize = fileCache.FileTableSize; // write header stream_writeU32(streamFile, FilecacheMagicV2); stream_writeU32(fileCache.StreamFile2, fileCache.ExtraVersion); stream_writeU64(fileCache.StreamFile2, fileCache.DataOffset); stream_writeU64(fileCache.StreamFile2, fileCache.FileTableOffset); stream_writeU32(fileCache.StreamFile2, fileCache.FileTableSize); // write file table stream_setSeek64(fileCache.StreamFile2, fileCache.DataOffset + fileCache.FileTableOffset); stream_writeData(fileCache.StreamFile2, fileCache.FileTableEntries, fileCache.FileTableEntryCount); // done return(fileCache); }
public static FileCache fileCache_openExisting(string path, UInt32 extraVersion) { FileCache fileCache = new FileCache(); using (BinaryReader streamFile = new BinaryReader(File.Open(path, FileMode.Open))) { UInt32 headerMagic = stream_readU32(streamFile); bool isV1 = false; if (headerMagic != FilecacheMagic && headerMagic != FilecacheMagicV2) { stream_destroy(); return(null); } if (headerMagic == FilecacheMagic) { isV1 = true; } UInt32 headerExtraVersion = stream_readU32(streamFile); if (headerExtraVersion != extraVersion) { stream_destroy(); return(null); } UInt64 headerDataOffset; if (isV1) { headerDataOffset = stream_readU32(streamFile); } else { headerDataOffset = stream_readU64(streamFile); } UInt64 headerFileTableOffset; if (isV1) { headerFileTableOffset = stream_readU32(streamFile); } else { headerFileTableOffset = stream_readU64(streamFile); } UInt32 headerFileTableSize = stream_readU32(streamFile); UInt32 fileTableEntryCount; bool invalidFileTableSize; if (isV1) { fileTableEntryCount = headerFileTableSize / 24; invalidFileTableSize = (headerFileTableSize % 24) != 0; } else { fileTableEntryCount = headerFileTableSize / SizeofFileCacheEntryT; invalidFileTableSize = (headerFileTableSize % SizeofFileCacheEntryT) != 0; } if (invalidFileTableSize) { Console.WriteLine(Resources.FileCache_fileCache_openExisting___0___is_corrupted, path); stream_destroy(); return(null); } InitializeCriticalSection(); fileCache.StreamFile = streamFile; fileCache.ExtraVersion = extraVersion; fileCache.DataOffset = headerDataOffset; fileCache.FileTableEntryCount = fileTableEntryCount; fileCache.FileTableOffset = headerFileTableOffset; fileCache.FileTableSize = fileTableEntryCount * SizeofFileCacheEntryT; fileCache.FileTableEntries = new FileCacheEntry[fileTableEntryCount]; stream_setSeek64(streamFile, fileCache.DataOffset + fileCache.FileTableOffset); if (isV1) { // read file table entries in old format for (UInt32 i = 0; i < fileTableEntryCount; i++) { UInt64 name1 = stream_readU64(streamFile); UInt64 name2 = stream_readU64(streamFile); UInt32 fileOffset = stream_readU32(streamFile); UInt32 fileSize = stream_readU32(streamFile); fileCache.FileTableEntries[i].Name1 = name1; fileCache.FileTableEntries[i].Name2 = name2; fileCache.FileTableEntries[i].FileOffset = fileOffset; fileCache.FileTableEntries[i].FileSize = fileSize; fileCache.FileTableEntries[i].ExtraReserved = 0; } } else { stream_readData(streamFile, fileCache.FileTableEntries, fileTableEntryCount); } // upgrade file table and header if V1 if (isV1) { fileCache_updateFiletable(fileCache, 0); } } return(fileCache); }
public static void fileCache_addFile(FileCache fileCache, UInt64 name1, UInt64 name2, byte[] fileData, Int32 fileSize, UInt32 extraReserved = 0) { if (fileCache == null) { return; } EnterCriticalSection(); // find free entry in file table Int32 entryIndex = -1; // scan for already existing entry for (Int32 i = 0; i < fileCache.FileTableEntryCount; i++) { if (fileCache.FileTableEntries[i].Name1 == name1 && fileCache.FileTableEntries[i].Name2 == name2) { entryIndex = i; // note: We don't delete the entry right here to avoid it being overwritten before the new file is added break; } } if (entryIndex == -1) { while (true) { // if no entry exists, search for empty one for (Int32 i = 0; i < fileCache.FileTableEntryCount; i++) { if (fileCache.FileTableEntries[i].Name1 == FilecacheFiletableFreeName && fileCache.FileTableEntries[i].Name2 == FilecacheFiletableFreeName) { entryIndex = i; break; } } if (entryIndex == -1) { if (name1 == FilecacheFiletableName1 && name2 == FilecacheFiletableName2) { __debugbreak(); } // no free entry, recreate file table with bigger size fileCache_updateFiletable(fileCache, 64); // try again } else { break; } } } // find free space UInt64 currentStartOffset = 0; int s0 = 0; while (true) { bool hasCollision = false; UInt64 currentEndOffset = currentStartOffset + (UInt64)fileSize; int entry = s0; int entryLast = (int)fileCache.FileTableEntryCount; while (entry < entryLast) { if (fileCache.FileTableEntries[entry].Name1 == FilecacheFiletableFreeName && fileCache.FileTableEntries[entry].Name2 == FilecacheFiletableFreeName) { entry++; continue; } if (currentEndOffset >= fileCache.FileTableEntries[entry].FileOffset && currentStartOffset < fileCache.FileTableEntries[entry].FileOffset + fileCache.FileTableEntries[entry].FileSize) { currentStartOffset = fileCache.FileTableEntries[entry].FileOffset + fileCache.FileTableEntries[entry].FileSize; hasCollision = true; s0 = 0; break; } s0 = entry; entry++; } // special logic to speed up scanning for free offsets // assumes that most of the time entries are stored in direct succession (unless entries are frequently deleted) if (hasCollision && (entry + 1) < entryLast) { entry++; while (entry < entryLast) { if (fileCache.FileTableEntries[entry].Name1 == FilecacheFiletableFreeName && fileCache.FileTableEntries[entry].Name2 == FilecacheFiletableFreeName) { entry++; continue; } if (fileCache.FileTableEntries[entry].FileOffset == currentStartOffset) { currentStartOffset = fileCache.FileTableEntries[entry].FileOffset + fileCache.FileTableEntries[entry].FileSize; entry++; continue; } entry = entryLast; } } // retry in case of collision if (!hasCollision) { break; } } // update file table entry fileCache.FileTableEntries[entryIndex].Name1 = name1; fileCache.FileTableEntries[entryIndex].Name2 = name2; fileCache.FileTableEntries[entryIndex].ExtraReserved = extraReserved; fileCache.FileTableEntries[entryIndex].FileOffset = currentStartOffset; fileCache.FileTableEntries[entryIndex].FileSize = (UInt32)fileSize; // write file data stream_setSeek64(fileCache.StreamFile2, fileCache.DataOffset + currentStartOffset); stream_writeData(fileCache.StreamFile2, fileData); // write file table entry stream_setSeek64(fileCache.StreamFile2, fileCache.DataOffset + fileCache.FileTableOffset + (UInt64)(SizeofFileCacheEntryT * entryIndex)); stream_writeData(fileCache.StreamFile2, fileCache.FileTableEntries, fileCache.FileTableEntryCount); LeaveCriticalSection(); }