Exemple #1
0
        /// <summary>
        /// Reads a list of TextureCacheItems. (compressed)
        /// </summary>
        /// <param name="_files"></param>
        private void Read(params TextureCacheItem[] _files)
        {
            //stringtable
            var stringTable  = new Dictionary <int, int>();
            int stoffset     = 0;
            var relMipsTable = new Dictionary <int, uint[]>();

            for (int i = 0; i < _files.Length; i++)
            {
                TextureCacheItem f = _files[i];
                stringTable.Add(i, stoffset);
                stoffset += f.DepotPath.Length + 1;

                //mipmaps
                relMipsTable.Add(i, f.GetMipsOffsettable());
            }

            //entrytable
            long offset = 0;

            for (int i = 0; i < _files.Length; i++)
            {
                TextureCacheItem item       = (TextureCacheItem)_files[i];
                long             nextOffset = GetOffset(offset + (int)item.ZSize);

                //calculate mipoffset
                var mo = relMipsTable.Where(_ => _.Key < i).SelectMany(_ => _.Value).Count();
                var l  = relMipsTable[i];
                for (int j = 0; j < relMipsTable[i].Count(); j++)
                {
                    l[j] += item.CachedZSizeNoMips + 9;
                }
                MipsOffsets.AddRange(l);

                if (offset / ALIGNMENT_TARGET < 0)
                {
                }

                TextureCacheItem newItem = new TextureCacheItem(item.Bundle)
                {
                    BundlePath = item.Bundle.FileName,

                    CachedMipsCount   = item.CachedMipsCount,
                    CachedZSizeNoMips = item.CachedZSizeNoMips,
                    CachedSizeNoMips  = item.CachedSizeNoMips,

                    DepotPath = item.DepotPath,
                    Hash      = item.Hash,
                    /*-------------TextureCacheEntryBase---------------*/
                    PathStringIndex = stringTable[i],
                    PageOffset      = offset / ALIGNMENT_TARGET,
                    ZSize           = item.ZSize,
                    Size            = item.Size,
                    BaseAlignment   = item.BaseAlignment,
                    BaseWidth       = item.BaseWidth,
                    BaseHeight      = item.BaseHeight,
                    TotalMipsCount  = item.TotalMipsCount,
                    SliceCount      = item.SliceCount,
                    MipOffsetIndex  = mo,
                    MipsCount       = item.MipsCount,
                    TimeStamp       = item.TimeStamp,
                    /*-------------TextureCacheEntryBase---------------*/
                    Type   = item.Type,
                    IsCube = item.IsCube
                };
                Items.Add(newItem);

                offset = nextOffset;
            }

            //create footer
            Footer = new TextureCacheFooter
            {
                Crc             = 0,
                UsedPages       = (UInt32)(offset / ALIGNMENT_TARGET),
                EntryCount      = (UInt32)Items.Count,
                StringTableSize = (UInt32)stoffset,
                MipEntryCount   = (uint)MipsOffsets.Count,
                IDString        = IDString,
            };
        }
Exemple #2
0
        /// <summary>
        /// Reads a texture.cache file.
        /// </summary>
        /// <param name="filepath"></param>
        private void Read(string filepath)
        {
            FileName = filepath;
            if (!File.Exists(filepath))
            {
                return;
            }


            using (var br = new BinaryReader(new FileStream(filepath, FileMode.Open)))
            {
                Items = new List <TextureCacheItem>();

                #region Footer
                br.BaseStream.Seek(-32, SeekOrigin.End);
                Footer = new TextureCacheFooter();
                Footer.Read(br);

                //errorhandling
                if (!IDString.SequenceEqual(Footer.IDString))
                {
                    throw new InvalidCacheException("Cache header mismatch.");
                }
                //errorhandling

                #endregion

                #region InfoTable
                //JMP to the top of the info table:
                //32 is the the size of the stuff we read so far.
                //Every entry has 52 bytes of info
                //The stringtable
                //Every offset is 4 bytes
                //The sum of this is how much we need to jump from the back
                var jmp = -(32 + (Footer.EntryCount * 52) + Footer.StringTableSize + (Footer.MipEntryCount * 4));
                br.BaseStream.Seek(jmp, SeekOrigin.End);
                var jmpoffset = br.BaseStream.Position;

                //Mips
                for (var i = 0; i < Footer.MipEntryCount; i++)
                {
                    MipsOffsets.Add(br.ReadUInt32());
                }

                //Names
                //BUG: "modW3EE\\content\\texture.cache" dies here! Investigate!!!!!!!!!!!!!

                /*
                 * for some reason, some entries are doubled in the (middle of the) stringtable of the texture.cache.
                 * leading to the string table being longer than it should be (more entries than entrycount)
                 * this in turn let's the for loop (which runs over entrycount) to stop in the middle of the string-table
                 * As a twist, there are actually duplicate file NAMES (but different compressed files) inside the bob texture.cache
                 * which breaks any way of properly solving the w3ee problem
                 * FIX
                 * - check for entrys and names count
                 * - if they are different (in the case of w3ee) try to resolve the error by making the names distinct
                 * - this works for w3ee but is not guaranteed to work in all cases.
                 * - skip loading if that didnt resolve the names/entry count
                 */
                var Names            = new List <string>();
                var entrytableoffset = jmpoffset + (Footer.MipEntryCount * 4) + Footer.StringTableSize;
                while (br.BaseStream.Position < entrytableoffset)
                {
                    string entryname = br.ReadCR2WString();
                    Names.Add(entryname);
                }

                //errorhandling
                if (Footer.EntryCount != Names.Count)
                {
                    //try resolving the error
                    var resolvedNames = Names.Distinct().ToList();
                    if (Footer.EntryCount == resolvedNames.Count)
                    {
                        Names = resolvedNames;
                    }
                    else
                    {
                        throw new NotImplementedException();
                    }
                }

                //errorhandling

                //Entries
                br.BaseStream.Seek(entrytableoffset, SeekOrigin.Begin);
                for (var i = 0; i < Footer.EntryCount; i++)
                {
                    var ti = new TextureCacheItem(this)
                    {
                        DepotPath  = Names[i],
                        ParentFile = FileName,
                        Hash       = br.ReadInt32(),
                        /*-------------TextureCacheEntryBase---------------*/
                        PathStringIndex = br.ReadInt32(),
                        PageOffset      = br.ReadInt32(), //NOTE: texturecache pointers are stored as pagenumber, while bundleitems store absolute offset -_-
                        ZSize           = (uint)br.ReadInt32(),
                        Size            = br.ReadInt32(),
                        BaseAlignment   = br.ReadUInt32(),

                        BaseWidth      = br.ReadUInt16(),
                        BaseHeight     = br.ReadUInt16(),
                        TotalMipsCount = br.ReadUInt16(),
                        SliceCount     = br.ReadUInt16(),

                        MipOffsetIndex = br.ReadInt32(),
                        MipsCount      = br.ReadInt32(),
                        TimeStamp      = br.ReadInt64(),
                        /*-------------TextureCacheEntryBase---------------*/
                        Type   = br.ReadInt16(),
                        IsCube = br.ReadInt16()
                    };
                    Items.Add(ti);
                }
                #endregion

                #region Data
                //errorhandling
                var footeroffset = br.BaseStream.Length - 32;
                if (br.BaseStream.Position != footeroffset)
                {
                    throw new NotImplementedException();
                }
                //errorhandling

                for (int i = 0; i < Items.Count; i++)
                {
                    TextureCacheItem t = Items[i];
                    br.BaseStream.Seek(t.PageOffset * 4096, SeekOrigin.Begin);
                    t.CachedZSizeNoMips = br.ReadUInt32(); //Compressed size
                    t.CachedSizeNoMips  = br.ReadInt32();  //Uncompressed size
                    t.CachedMipsCount   = br.ReadByte();   //mips count
                }
                #endregion
            }
        }