Пример #1
0
        private static int ReadBuild(Archive compressor, Stream stream, DatFile.MftEntry e)
        {
            byte[] data;
            int    position;

            return(ReadBuild(compressor, stream, e, out data, out position));
        }
Пример #2
0
        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);
        }
Пример #3
0
        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();
        }
Пример #4
0
 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);
     }
 }
Пример #5
0
        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);
        }
Пример #6
0
        /// <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());
            }
        }
Пример #7
0
        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);
                        }
                    }
                }
            }
        }