예제 #1
0
        /// <summary>
        /// Serialize bundle to a .bundle file.
        /// </summary>
        /// <param name="outdir"></param>
        public void Write(string outdir)
        {
            var filePath = Path.Combine(outdir, Name);

            using (var fs = new FileStream(filePath, FileMode.Create))
                using (var bw = new BinaryWriter(fs))
                {
                    // Write Header
                    Header.Write(bw);

                    // Write ToC
                    var minDataOffset = ALIGNMENT_TARGET;
                    foreach (BundleItem f in Items)
                    {
                        f.Write(bw);
                    }
                    //pad the ToC
                    int writePosition = (int)bw.BaseStream.Position;
                    int tocPadding    = GetOffset(writePosition) - writePosition;
                    if (tocPadding > 0)
                    {
                        bw.Write(new byte[tocPadding]);
                    }

                    // Write Body
                    for (int i = 0; i < Items.Count; i++)
                    {
                        BundleItem item = Items[i];

                        //compressed file
                        var compressedFile = new List <byte>();
                        using (var ms = new MemoryStream())
                        {
                            item.GetCompressedFile(ms);
                            compressedFile.AddRange(ms.ToArray());
                        }

                        //pad body items
                        int filesize      = (int)item.ZSize;
                        int nextOffset    = GetOffset((int)item.PageOffset + filesize);
                        int paddingLength = nextOffset - ((int)item.PageOffset + filesize);
                        if (paddingLength > 0 && i < (Items.Count - 1)) //don't pad the last item
                        {
                            compressedFile.AddRange(new byte[paddingLength]);
                        }

                        bw.Write(compressedFile.ToArray());
                    }
                }
        }
        //FIXME buffers and blobs
        /// <summary>
        /// Create a .bundle file from a directory.
        /// </summary>
        /// <param name="outDir"></param>
        /// <param name="inDir"></param>
        public static void Pack(string inDir, string outDir)
        {
            List <IWitcherFile> bufferFiles = new List <IWitcherFile>();
            List <IWitcherFile> blobFiles   = new List <IWitcherFile>();

            foreach (var f in Directory.EnumerateFiles(inDir, "*", SearchOption.AllDirectories))
            {
                var name = Encoding.Default.GetBytes(GetRelativePath(f, inDir)).ToArray();
                if (name.Length > 0x100)
                {
                    name = name.Take(0x100).ToArray();
                }
                if (name.Length < 0x100)
                {
                    Array.Resize(ref name, 0x100);
                }

                var bi = new BundleItem
                {
                    Name        = System.Text.Encoding.Default.GetString(name),
                    Compression = 5, //FIXME make variable
                };

                //sort buffers out
                if (f.Split('\\').Last().Split('.').Last() == "buffer")
                {
                    bufferFiles.Add(bi);
                }
                else
                {
                    blobFiles.Add(bi);
                }
            }

            List <Bundle> bundles = new List <Bundle>();

            if (blobFiles.Count > 0)
            {
                bundles.Add(new Bundle(blobFiles.ToArray()));
            }
            if (bufferFiles.Count > 0)
            {
                bundles.Add(new Bundle(bufferFiles.ToArray()));
            }
            foreach (var b in bundles)
            {
                b.Write(outDir);
            }
        }
예제 #3
0
        /// <summary>
        /// Generate a bundle from a list of BundleItems. (compressed)
        /// </summary>
        /// <param name="Files"></param>
        private void Read(BundleItem[] Files)
        {
            uint tocRealSize = (UInt32)(Files.Length * TOCEntrySize);

            // MAIN BODY
            int offset = GetOffset((int)tocRealSize + HEADER_SIZE);

            foreach (BundleItem item in Files)
            {
                int nextOffset = GetOffset(offset + (int)item.ZSize);

                BundleItem newItem = new BundleItem()
                {
                    FileAccessor = new BundleAccesor(item.Bundle.FileName, item.PageOffset),

                    DepotPath   = item.DepotPath,
                    Hash        = item.Hash,
                    Size        = item.Size,
                    ZSize       = item.ZSize,
                    Compression = item.Compression,
                    DateString  = item.DateString,
                    Bundle      = this,
                    PageOffset  = (uint)offset,
                    CRC         = item.CRC
                };
                Items.Add(newItem);

                offset = nextOffset;
            }

            //create header
            uint dummysize  = 0;
            uint bundlesize = (uint)offset;

            Header = new BundleHeader(IDString, bundlesize, dummysize, tocRealSize);
        }
예제 #4
0
        /// <summary>
        /// Reads a Metadata_Store from a list of bundles.
        /// </summary>
        /// <param name="Bundles"></param>
        private void Read(params Bundle[] Bundles)
        {
            #region Info
            FileStringTable.Add(0x00);
            fileInfoList.Add(new UFileInfo());
            fileEntryInfoList.Add(new UFileEntryInfo());
            bundleInfoList.Add(new UBundleInfo());
            #endregion


            var stOffsetDict = new Dictionary <string, uint>();
            var _entries     = new List <BundleItem>();

            var _entryNames  = new List <string>();
            var _bufferNames = new List <string>();
            var _bundleNames = new List <string>();
            var _fileNames   = new List <string>();
            var _dirNames    = new List <string>();

            var _dirInfos  = new List <DirectoryInfo>();
            var _fileInfos = new List <FileInfo>();

            #region Dir and Files Table
            foreach (var b in Bundles)
            {
                string bundleName = b.Name;
                _bundleNames.Add(bundleName);

                stOffsetDict.Add(bundleName, (uint)FileStringTable.Count);
                FileStringTable.AddRange(Encoding.UTF8.GetBytes(bundleName));
                FileStringTable.Add(0x00);

                foreach (var item in b.Items)
                {
                    string relFullPath = item.DepotPath;
                    if (_entryNames.Contains(relFullPath))
                    {
                        continue;
                    }

                    // stringtable: files
                    stOffsetDict.Add(relFullPath, (uint)FileStringTable.Count);
                    FileStringTable.AddRange(Encoding.UTF8.GetBytes(relFullPath));
                    FileStringTable.Add(0x00);

                    //add buffername
                    if (relFullPath.Split('.').Last() == "buffer")
                    {
                        //add to buffer list
                        string buffername = relFullPath.Split(new string[] { ".1.buffer" }, StringSplitOptions.None).First();
                        if (_bufferNames.Contains(relFullPath))
                        {
                            continue;
                        }
                        _bufferNames.Add(buffername);
                    }
                    else
                    {
                        // stringtable: dir and file names
                        FileInfo fi = new FileInfo($"\\{relFullPath}");
                        _fileInfos.Add(fi);
                        if (!_fileNames.Contains(fi.Name))
                        {
                            _fileNames.Add(fi.Name);
                        }
                        var dirs = relFullPath.Split('\\').ToList();
                        dirs.Remove(dirs.Last());
                        _dirNames.AddRange(dirs.Where(_ => !_dirNames.Contains(_)));
                    }

                    // add to entry list
                    _entryNames.Add(relFullPath);
                    _entries.Add(item);
                }
            }

            FileStringTable.Add(0x00);
            foreach (var d in _dirNames)
            {
                stOffsetDict.Add(d, (uint)FileStringTable.Count);
                FileStringTable.AddRange(Encoding.UTF8.GetBytes(d));
                FileStringTable.Add(0x00);
            }
            foreach (var f in _fileNames)
            {
                stOffsetDict.Add(f, (uint)FileStringTable.Count);
                FileStringTable.AddRange(Encoding.UTF8.GetBytes(f));
                FileStringTable.Add(0x00);
            }

            int StringTableSize = FileStringTable.Count;
            #endregion

            #region UFileInitInfo and Hashes
            foreach (var fi in _fileInfos)
            {
                //add directoryInfo to Directory info list
                DirectoryInfo di = fi.Directory;
                while (true)
                {
                    if (_dirInfos.Select(_ => _.Name).Contains(di.Name))
                    {
                        break;
                    }

                    _dirInfos.Add(di);
                    di = di.Parent;
                    if (di.Parent == null)
                    {
                        break;
                    }
                }

                var fii = new UFileInitInfo()
                {
                    FileIF = _fileInfos.IndexOf(fi) + 1,
                    DirID  = (Int32)_dirNames.IndexOf(fi.Directory.Name) + 1,
                    Name   = (Int32)stOffsetDict[fi.Name]
                };
                fileInitInfoList.Add(fii);

                var    fullname = _entries.First(_ => _.DepotPath.Split('\\').Last() == fi.Name).DepotPath;
                UInt64 hash     = (UInt64)FNV1a.HashFNV1a64(fullname);
                var    h        = new UHash()
                {
                    Hash   = (UInt64)hash,
                    FileID = (UInt64)_fileInfos.IndexOf(fi) + 1
                };
                hashes.Add(h);
            }
            //hashes are sorted by hashsize not by filenumber
            hashes.Sort((x, y) => x.Hash.CompareTo(y.Hash));
            #endregion

            #region UDirInitInfo
            //first directoryinfo i null with offset to the first 0 byte of the dir Table
            _dirInfos.Reverse();
            dirInitInfoList.Add(new UDirInitInfo()
            {
                ParentID = 0,
                Name     = (Int32)stOffsetDict[_dirNames.First()] - 1
            });
            foreach (var di in _dirInfos)
            {
                Int32 parentID = 0;
                if (di.Parent.Parent != null)
                {
                    parentID = (int)_dirNames.IndexOf(di.Parent.Name) + 1;
                }

                var dii = new UDirInitInfo()
                {
                    ParentID = parentID,
                    Name     = (Int32)stOffsetDict[di.Name]
                };
                dirInitInfoList.Add(dii);
            }
            #endregion

            #region UBundleInfo
            foreach (var b in Bundles)
            {
                string bundleName = b.Name;

                BundleItem ffe             = _entries.First(_ => ((Bundle)_.Bundle).Name == bundleName);
                uint       dataBlockOffset = b.Header.TocRealSize + 32;
                uint       dataBlockSize   = b.Header.Bundlesize - dataBlockOffset;


                var bi = new UBundleInfo()
                {
                    Name               = stOffsetDict[bundleName],
                    FirstFileEntry     = (UInt32)(_entries.IndexOf(ffe) + 1),
                    NumBundleEntries   = (UInt32)b.Items.Count,
                    DataBlockSize      = dataBlockSize, //NOTE this is simply wrong for Buffers in vanilla (always 4096)
                    DataBlockOffset    = dataBlockOffset,
                    BurstDataBlockSize = 0,
                };
                bundleInfoList.Add(bi);
            }
            #endregion

            #region UFileInfos and UFileEntryInfos
            for (int i = 0; i < _entries.Count; i++)
            {
                BundleItem e = _entries[i];


                UInt32 bufferid  = 0;
                UInt32 hasbuffer = 0;
                if (_bufferNames.Contains(e.DepotPath))
                {
                    hasbuffer = 1;
                    bufferid  = (uint)_bufferNames.IndexOf(e.DepotPath);
                }

                var fi = new UFileInfo()
                {
                    StringTableNameOffset = stOffsetDict[e.DepotPath],
                    PathHash        = 0, //FIXME this is always 0...
                    SizeInBundle    = e.ZSize,
                    SizeInMemory    = (UInt32)e.Size,
                    FirstEntry      = (UInt32)(_entries.IndexOf(e) + 1),
                    CompressionType = e.Compression,
                    bufferid        = bufferid,
                    hasbuffer       = hasbuffer
                };
                fileInfoList.Add(fi);

                string bundleName = ((Bundle)e.Bundle).Name;
                var    fei        = new UFileEntryInfo()
                {
                    FileID         = (uint)i + 1,
                    BundleID       = (uint)_bundleNames.ToList().IndexOf(bundleName) + 1,
                    OffsetInBundle = (uint)e.PageOffset,
                    SizeInBundle   = e.ZSize,
                    NextEntry      = 0
                };
                fileEntryInfoList.Add(fei);
            }
            #endregion

            #region Buffers
            foreach (var buffer in _entries.Where(_ => _.DepotPath.Split('.')?.Last() == "buffer"))
            {
                Buffers.Add(_entries.IndexOf(buffer) + 1);
            }
            #endregion

            MaxFileSizeInBundle = fileInfoList.Select(_ => _.SizeInBundle).ToList().Max();
            MaxFileSIzeInMemory = fileInfoList.Select(_ => _.SizeInMemory).ToList().Max();
        }
        /// <summary>
        /// Reads the Table Of Contents of the bundle.
        /// </summary>
        private void Read()
        {
            Items = new Dictionary <string, BundleItem>();

            using (var reader = new BinaryReader(new FileStream(ArchiveAbsolutePath, FileMode.Open, FileAccess.Read)))
            {
                var idstring = reader.ReadBytes(IDString.Length);

                if (!IDString.SequenceEqual(idstring))
                {
                    throw new InvalidBundleException("Archive header mismatch.");
                }

                bundlesize = reader.ReadUInt32();
                dummysize  = reader.ReadUInt32();
                dataoffset = reader.ReadUInt32();

                reader.BaseStream.Seek(0x20, SeekOrigin.Begin);

                while (reader.BaseStream.Position < dataoffset + 0x20)
                {
                    var item = new BundleItem
                    {
                        Archive = this
                    };

                    var strname = Encoding.GetEncoding("ISO-8859-1").GetString(reader.ReadBytes(0x100));

                    item.Name       = strname.Substring(0, strname.IndexOf('\0'));
                    item.Hash       = reader.ReadBytes(16);
                    item.Empty      = reader.ReadUInt32();
                    item.Size       = reader.ReadUInt32();
                    item.ZSize      = reader.ReadUInt32();
                    item.PageOffset = reader.ReadUInt32();

                    var date = reader.ReadUInt32();
                    var y    = date >> 20;
                    var m    = date >> 15 & 0x1F;
                    var d    = date >> 10 & 0x1F;

                    var time = reader.ReadUInt32();
                    var h    = time >> 22;
                    var n    = time >> 16 & 0x3F;
                    var s    = time >> 10 & 0x3F;

                    item.DateString = string.Format(" {0}/{1}/{2} {3}:{4}:{5}", d, m, y, h, n, s);

                    item.Zero        = reader.ReadBytes(16); //00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (always, in every archive)
                    item.CRC         = reader.ReadUInt32();  //CRC32 for the uncompressed data
                    item.Compression = reader.ReadUInt32();

                    if (!Items.ContainsKey(item.Name))
                    {
                        Items.Add(item.Name, item);
                    }
                    else
                    {
                        Console.WriteLine("Warning: archive '" + ArchiveAbsolutePath + "' could not be fully loaded as resource '" + item.Name + "' is defined more than once. Only the first definition was loaded.");
                    }
                }

                reader.Close();
            }
        }
        /// <summary>
        /// Reads a .bundle file.
        /// </summary>
        public void Read(string filename)
        {
            FileName = filename;
            Name     = filename.Split('\\').Last();
            Items    = new Dictionary <string, BundleItem>();

            using (var reader = new BinaryReader(new FileStream(FileName, FileMode.Open, FileAccess.Read)))
            {
                var idstring = reader.ReadBytes(IDString.Length);

                if (!IDString.SequenceEqual(idstring))
                {
                    throw new InvalidBundleException("Bundle header mismatch.");
                }

                bundlesize  = reader.ReadUInt32();
                dummysize   = reader.ReadUInt32();
                tocRealSize = reader.ReadUInt32();

                DataBlockOffset = tocRealSize + (uint)HEADER_SIZE;
                DataBlockSize   = bundlesize - DataBlockOffset;

                reader.BaseStream.Seek(0x20, SeekOrigin.Begin);

                while (reader.BaseStream.Position < tocRealSize + 0x20)
                {
                    var item = new BundleItem
                    {
                        Bundle = this
                    };

                    var strname = Encoding.Default.GetString(reader.ReadBytes(0x100));

                    item.Name       = strname.Substring(0, strname.IndexOf('\0'));
                    item.Hash       = reader.ReadBytes(16);
                    item.Empty      = reader.ReadUInt32();
                    item.Size       = reader.ReadUInt32();
                    item.ZSize      = reader.ReadUInt32();
                    item.PageOFfset = reader.ReadUInt32();

                    var date = reader.ReadUInt32();
                    var y    = date >> 20;
                    var m    = date >> 15 & 0x1F;
                    var d    = date >> 10 & 0x1F;

                    var time = reader.ReadUInt32();
                    var h    = time >> 22;
                    var n    = time >> 16 & 0x3F;
                    var s    = time >> 10 & 0x3F;

                    item.DateString = string.Format(" {0}/{1}/{2} {3}:{4}:{5}", d, m, y, h, n, s);

                    item.Zero        = reader.ReadBytes(16); //00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (always, in every archive)
                    item.CRC         = reader.ReadUInt32();  //CRC32 for the uncompressed data
                    item.Compression = reader.ReadUInt32();

                    Items.Add(item.Name, item);
                    ItemsList.Add(item);
                }


                reader.Close();
            }
        }