private static int ReadBuild(Archive compressor, Stream stream, DatFile.MftEntry e) { byte[] data; int position; return(ReadBuild(compressor, stream, e, out data, out position)); }
private bool HasFreeSpace(DatFile.MftEntry e, int blockSize, int space) { var l = e.offset + e.size; var r = (int)(l % blockSize); if (r > 0) { r = blockSize - r; } return(r >= space); }
private static int ReadBuild(Archive compressor, Stream stream, DatFile.MftEntry e, out byte[] data, out int position) { stream.Position = e.offset; data = compressor.Decompress(stream, e.size); using (var r = new BinaryReader(new MemoryStream(data))) { if (r.ReadUInt16() != 18000) //PF { throw new IOException("Unknown header"); } r.BaseStream.Position += 4; if (r.ReadUInt16() != 12) { throw new IOException("Unknown version"); } if (r.ReadUInt32() != 1818455916U) //locl { throw new IOException("Unknown header"); } while (r.BaseStream.Position < data.Length) { var chunkType = r.ReadUInt32(); var chunkSize = r.ReadUInt32(); var chunkEnd = r.BaseStream.Position + chunkSize; if (chunkType == 1701998435U) //core { var chunkVersion = r.ReadUInt16(); var chunkHeaderSize = r.ReadUInt16(); var chunkTableOffset = r.ReadUInt32(); if (chunkVersion != 0) { throw new IOException("Unknown core version"); } position = (int)r.BaseStream.Position; return(r.ReadInt32()); } r.BaseStream.Position = chunkEnd; } } throw new FileNotFoundException(); }
private static void WriteEntry(BinaryWriter w, DatFile.MftEntry e) { if (e == null) { w.Write(0L); w.Write(0L); w.Write(0L); } else { w.Write(e.offset); w.Write(e.size); w.Write(e.compression); w.Write(e.flags); w.Write(e.counter); w.Write(e.crc); } }
private bool UpdateCache(BinaryReader r, int build = 0) { var mft = DatFile.ReadMft(r); var s = session; CustomEntry c = null; var counter = 2; for (var i = mft.entries.Length - 1; i >= 0; --i) { var e = mft.entries[i]; switch (e.baseId) { case ID_CACHE: c = new CustomEntry() { index = i, entry = e, }; if (--counter == 0) { i = 1; } break; case ID_LOCAL: if (build == 0) { build = ReadBuild(s.compressor, r.BaseStream, e); } if (--counter == 0) { i = 1; } break; } } if (build > 0) { if (c == null) { //entry will need to be created, which will require updating the entries and ids var blockSize = mft.BlockSize; var ofs = 0L; DatFile.MftEntry entry; c = new CustomEntry() { index = mft.entries.Length, }; //find the next position to write data (not considering free space) for (var i = mft.entries.Length - 1; i >= 0; --i) { var e = mft.entries[i]; var l = e.offset + e.size; if (l > ofs) { ofs = l; } } ofs = (ofs / blockSize + 1) * blockSize; using (var w = new BinaryWriter(r.BaseStream, Encoding.ASCII, true)) { var crc32 = new Crc32(); byte[] buffer; entry = mft.entries[INDEX_IDS]; if (!HasFreeSpace(entry, blockSize, 8)) { r.BaseStream.Position = entry.offset; buffer = r.ReadBytes(entry.size); w.BaseStream.Position = ofs; w.Write(buffer); entry.offset = ofs; ofs += ((entry.size + 8) / blockSize + 1) * blockSize; } //adding id w.BaseStream.Position = entry.offset + entry.size; w.Write(ID_CACHE); w.Write(c.index + 1); entry.size += 8; //update size w.BaseStream.Position = mft.entries[INDEX_ENTRIES].offset + (INDEX_IDS + 1) * 24; w.Write(entry.offset); w.Write(entry.size); //recalculate crc r.BaseStream.Position = entry.offset; buffer = r.ReadBytes(entry.size); foreach (var b in buffer) { crc32.Add(b); } //update crc w.BaseStream.Position = mft.entries[INDEX_ENTRIES].offset + (INDEX_IDS + 1) * 24 + 20; w.Write(crc32.CRC); crc32.Reset(); entry = mft.entries[INDEX_ENTRIES]; if (!HasFreeSpace(entry, blockSize, 24)) { r.BaseStream.Position = entry.offset; buffer = r.ReadBytes(entry.size); w.BaseStream.Position = ofs; w.Write(buffer); entry.offset = ofs; ofs += ((entry.size + 24) / blockSize + 1) * blockSize; } c.entry = new DatFile.MftEntry() { compression = 8, baseId = ID_CACHE, fileId = ID_CACHE, crc = 1214729159, flags = 3, offset = ofs, }; c.data = CreateGw2Cache(s.compressor, build, c.entry.compression == 8); c.length = c.data.Length; var entries = new DatFile.MftEntry[c.index + 1]; Array.Copy(mft.entries, entries, mft.entries.Length); entries[c.index] = c.entry; mft.entries = entries; //adding entry w.BaseStream.Position = entry.offset + entry.size; WriteEntry(w, c.entry); entry.size += 24; //update count w.BaseStream.Position = entry.offset + 12; w.Write(entries.Length + 1); //update size w.BaseStream.Position = entry.offset + (INDEX_ENTRIES + 1) * 24; w.Write(entry.offset); w.Write(entry.size); //entries crc is updated when writing data //update header w.BaseStream.Position = 24; w.Write(entry.offset); w.Write(entry.size); } } else { c.data = CreateGw2Cache(s.compressor, build, c.entry.compression == 8); c.length = c.data.Length; } UpdateCache(s, r, mft, c); return(true); } return(false); }
/// <summary> /// Creates a bare Local.dat file /// </summary> /// <param name="build">If > 0, sets the gw2cache location</param> /// <returns>Local.dat file in bytes</returns> public static byte[] CreateNew(int build = 0) { int blockSize = 512; using (var ms = new MemoryStream(blockSize * (build > 0 ? 4 : 2))) //4 blocks: header, entries, cache, ids (cache + ids only needed if build is set) { using (var fs = new Dat.Compression.IO.FileStream(ms, true, false, false)) { using (var w = new BinaryWriter(fs)) { DatFile.MftEntry e; var entries = new DatFile.MftEntry[build > 0 ? 16 : 15]; //first 15 entries are reserved long ofs = 0; entries[INDEX_HEADER] = new DatFile.MftEntry() { flags = 3, size = 40, }; ofs += blockSize; if (build > 0) { var indexCache = entries.Length - 1; e = entries[indexCache] = new DatFile.MftEntry() { offset = ofs, compression = 8, flags = 3, crc = 1214729159, //note the crc is always the same because the file ends with a crc, which cancels out when crc'd again }; w.BaseStream.Position = ofs; w.Write(CreateGw2Cache(new Archive(), build, true)); e.size = (int)(w.BaseStream.Position - ofs); ofs += (e.size / blockSize + 1) * blockSize; e = entries[INDEX_IDS] = new DatFile.MftEntry() { offset = ofs, flags = 3, }; w.BaseStream.Position = ofs; fs.ComputeCRC = true; w.Write(ID_CACHE); w.Write(indexCache + 1); e.size = (int)(w.BaseStream.Position - ofs); e.crc = fs.CRC; fs.ComputeCRC = false; fs.ResetCRC(); ofs += blockSize; } e = entries[INDEX_ENTRIES] = new DatFile.MftEntry() { offset = ofs, flags = 3, }; w.BaseStream.Position = e.offset; fs.ComputeCRC = true; w.Write(443835981U); w.Write(0L); w.Write(entries.Length + 1); //this header is included in the count w.Write(0L); for (var i = 0; i < INDEX_ENTRIES; i++) { WriteEntry(w, entries[i]); } w.BaseStream.Position += 24; //INDEX_ENTRIES is written last for (var i = INDEX_ENTRIES + 1; i < entries.Length; i++) { WriteEntry(w, entries[i]); } e.size = (int)(w.BaseStream.Position - e.offset); e.crc = fs.CRC; fs.ComputeCRC = false; fs.ResetCRC(); ofs += blockSize; w.BaseStream.Position = e.offset + (INDEX_ENTRIES + 1) * 24; WriteEntry(w, entries[INDEX_ENTRIES]); w.BaseStream.Position = 0; w.Write(441336215U); w.Write(entries[INDEX_HEADER].size); w.Write(3401187329U); w.Write(blockSize); w.Write(2396038944U); w.Write(0); w.Write(entries[INDEX_ENTRIES].offset); w.Write(entries[INDEX_ENTRIES].size); w.Write(0); } } return(ms.ToArray()); } }
private void Initialize(Session s) { const byte RESERVED = 15; //first 15 indexes are reserved var i = 0; #warning !BitConverter.IsLittleEndian if (!BitConverter.IsLittleEndian) { throw new NotSupportedException(); } var hs = new HashSet <ushort>(); var queue = new Queue <Settings.IDatFile>(); s.queue = queue; s.queued = hs; var modifyGw2Cache = Settings.GuildWars2.UseCustomGw2Cache.Value; using (var r = new BinaryReader(new BufferedStream(File.Open(s.master.Path, FileMode.Open, modifyGw2Cache ? FileAccess.ReadWrite : FileAccess.Read, FileShare.Read)))) { var mft = DatFile.ReadMft(r); var blockSize = mft.BlockSize; var custom = new Dictionary <int, CustomEntry>(); var entries = new DatFile.MftEntry[mft.entries.Length + 50]; var snapshot = new Dictionary <int, int>(entries.Length); var changed = s.snapshot == null; var count = 0; var ofs = 0; for (i = 0; i < mft.entries.Length; i++) { var entry = mft.entries[i]; var size = entry.size; if (!changed && entry.baseId > 0) { int fileId; if (!s.snapshot.TryGetValue(entry.baseId, out fileId) || fileId != entry.fileId) { changed = true; } } if (i < RESERVED) { //lower indexes are reserved switch (i) { case INDEX_IDS: case INDEX_ENTRIES: size = 0; break; } } else if (entry.baseId == entry.fileId && entry.baseId < 100) { //lower IDs are core files and specific to the account switch (entry.baseId) { case ID_LOCAL: s.build = ReadBuild(s.compressor, r.BaseStream, entry); int previous; if (!changed && s.build > 0 && s.snapshot.TryGetValue(0, out previous) && previous != s.build) { changed = true; } break; case ID_CACHE: custom[entry.baseId] = new CustomEntry() { entry = entry, index = i, }; break; } continue; } entries[count++] = entry; if (entry.baseId > 0) { snapshot[entry.baseId] = entry.fileId; } if (size > 0) { ofs += (size / blockSize + 1) * blockSize; } } //if (!changed) // return; if (modifyGw2Cache && s.build > 0) { CustomEntry c; if (!custom.TryGetValue(ID_CACHE, out c)) { custom[ID_CACHE] = c = new CustomEntry() { entry = new DatFile.MftEntry() { compression = 8, baseId = ID_CACHE, fileId = ID_CACHE, crc = 1214729159, flags = 3, } }; } var e = c.entry; if (e.compression == 8 || e.compression == 0) { var data = CreateGw2Cache(s.compressor, s.build, e.compression == 8); var entry = new DatFile.MftEntry() { baseId = e.baseId, fileId = e.fileId, compression = e.compression, counter = e.counter, crc = e.crc, flags = e.flags, offset = long.MaxValue, size = data.Length, }; entries[count++] = entry; snapshot[entry.baseId] = entry.fileId; c.data = data; c.length = data.Length; ofs += (entry.size / blockSize + 1) * blockSize; } } Array.Sort(entries, RESERVED, count - RESERVED, Comparer <DatFile.MftEntry> .Create( delegate(DatFile.MftEntry a, DatFile.MftEntry b) { return(a.offset.CompareTo(b.offset)); })); var buffer = new byte[ofs]; ofs = blockSize; Array.Copy(mft.header, buffer, mft.header.Length); for (i = INDEX_ENTRIES + 1; i < count; i++) { var e = entries[i]; if (e.size == 0) { continue; } if (e.offset != long.MaxValue) { r.BaseStream.Position = e.offset; ReadBytes(r, buffer, ofs, e.size); } else { CustomEntry c; if (!custom.TryGetValue(e.baseId, out c)) { continue; } e.size = c.WriteTo(buffer, ofs, s.compressor); } e.offset = ofs; ofs += (e.size / blockSize + 1) * blockSize; } s.entries = entries; s.entriesCount = count; s.buffer1 = buffer; s.snapshot = snapshot; if (modifyGw2Cache) { CustomEntry c; if (custom.TryGetValue(ID_CACHE, out c) && c.data != null) { if (c.index > 0) { UpdateCache(s, r, mft, c); } else { r.BaseStream.Position = 0; UpdateCache(r, s.build); } } } } }