예제 #1
0
 internal virtual void ReadYuzNames(byte[] yuz, FilenameMap filename_map)
 {
     using (var ystream = new MemoryStream(yuz))
         using (var zstream = ZLibCompressor.DeCompress(ystream))
             using (var input = new BinaryReader(zstream, Encoding.Unicode))
             {
                 long dir_offset = 0;
                 while (-1 != input.PeekChar())
                 {
                     uint entry_signature = input.ReadUInt32();
                     long entry_size      = input.ReadInt64();
                     if (entry_size < 0)
                     {
                         return;
                     }
                     dir_offset += 12 + entry_size;
                     uint hash      = input.ReadUInt32();
                     int  name_size = input.ReadInt16();
                     if (name_size > 0)
                     {
                         entry_size -= 6;
                         if (name_size * 2 <= entry_size)
                         {
                             var filename = new string (input.ReadChars(name_size));
                             filename_map.Add(hash, filename);
                         }
                     }
                     input.BaseStream.Position = dir_offset;
                 }
                 filename_map.AddShortcut("$", "startup.tjs");
             }
 }
예제 #2
0
        public override ArcFile TryOpen(ArcView file)
        {
            long base_offset = 0;

            if (0x5a4d == file.View.ReadUInt16(0))  // 'MZ'
            {
                base_offset = SkipExeHeader(file, s_xp3_header);
            }
            if (!file.View.BytesEqual(base_offset, s_xp3_header))
            {
                return(null);
            }
            long dir_offset = base_offset + file.View.ReadInt64(base_offset + 0x0b);

            if (dir_offset < 0x13 || dir_offset >= file.MaxOffset)
            {
                return(null);
            }
            if (0x80 == file.View.ReadUInt32(dir_offset))
            {
                dir_offset = base_offset + file.View.ReadInt64(dir_offset + 9);
                if (dir_offset < 0x13 || dir_offset >= file.MaxOffset)
                {
                    return(null);
                }
            }
            int header_type = file.View.ReadByte(dir_offset);

            if (0 != header_type && 1 != header_type)
            {
                return(null);
            }

            Stream header_stream;

            if (0 == header_type) // read unpacked header
            {
                long header_size = file.View.ReadInt64(dir_offset + 1);
                if (header_size > uint.MaxValue)
                {
                    return(null);
                }
                header_stream = file.CreateStream(dir_offset + 9, (uint)header_size);
            }
            else // read packed header
            {
                long packed_size = file.View.ReadInt64(dir_offset + 1);
                if (packed_size > uint.MaxValue)
                {
                    return(null);
                }
                long header_size = file.View.ReadInt64(dir_offset + 9);
                using (var input = file.CreateStream(dir_offset + 17, (uint)packed_size))
                    header_stream = ZLibCompressor.DeCompress(input);
            }

            var crypt_algorithm = new Lazy <ICrypt> (() => QueryCryptAlgorithm(file), false);

            var dir = new List <Entry>();

            dir_offset = 0;
            using (var header = new BinaryReader(header_stream, Encoding.Unicode))
                using (var filename_map = new FilenameMap())
                {
                    while (-1 != header.PeekChar())
                    {
                        uint entry_signature = header.ReadUInt32();
                        long entry_size      = header.ReadInt64();
                        if (entry_size < 0)
                        {
                            return(null);
                        }
                        dir_offset += 12 + entry_size;
                        if (0x656C6946 == entry_signature) // "File"
                        {
                            var entry = new Xp3Entry();
                            while (entry_size > 0)
                            {
                                uint section      = header.ReadUInt32();
                                long section_size = header.ReadInt64();
                                entry_size -= 12;
                                if (section_size > entry_size)
                                {
                                    break;
                                }
                                entry_size -= section_size;
                                long next_section_pos = header.BaseStream.Position + section_size;
                                switch (section)
                                {
                                case 0x6f666e69: // "info"
                                    if (entry.Size != 0 || !string.IsNullOrEmpty(entry.Name))
                                    {
                                        goto NextEntry; // ambiguous entry, ignore
                                    }
                                    entry.IsEncrypted = 0 != header.ReadUInt32();
                                    long file_size   = header.ReadInt64();
                                    long packed_size = header.ReadInt64();
                                    if (file_size >= uint.MaxValue || packed_size > uint.MaxValue || packed_size > file.MaxOffset)
                                    {
                                        goto NextEntry;
                                    }
                                    entry.IsPacked     = file_size != packed_size;
                                    entry.Size         = (uint)packed_size;
                                    entry.UnpackedSize = (uint)file_size;

                                    if (entry.IsEncrypted || ForceEncryptionQuery)
                                    {
                                        entry.Cipher = crypt_algorithm.Value;
                                    }
                                    else
                                    {
                                        entry.Cipher = NoCryptAlgorithm;
                                    }

                                    var name = entry.Cipher.ReadName(header);
                                    if (null == name)
                                    {
                                        goto NextEntry;
                                    }
                                    if (entry.Cipher.ObfuscatedIndex && ObfuscatedPathRe.IsMatch(name))
                                    {
                                        goto NextEntry;
                                    }
                                    if (filename_map.Count > 0)
                                    {
                                        name = filename_map.Get(entry.Hash, name);
                                    }
                                    if (name.Length > 0x100)
                                    {
                                        goto NextEntry;
                                    }
                                    entry.Name        = name;
                                    entry.Type        = FormatCatalog.Instance.GetTypeFromName(name);
                                    entry.IsEncrypted = !(entry.Cipher is NoCrypt) &&
                                                        !(entry.Cipher.StartupTjsNotEncrypted && "startup.tjs" == name);
                                    break;

                                case 0x6d676573: // "segm"
                                    int segment_count = (int)(section_size / 0x1c);
                                    if (segment_count > 0)
                                    {
                                        for (int i = 0; i < segment_count; ++i)
                                        {
                                            bool compressed          = 0 != header.ReadInt32();
                                            long segment_offset      = base_offset + header.ReadInt64();
                                            long segment_size        = header.ReadInt64();
                                            long segment_packed_size = header.ReadInt64();
                                            if (segment_offset > file.MaxOffset || segment_packed_size > file.MaxOffset)
                                            {
                                                goto NextEntry;
                                            }
                                            var segment = new Xp3Segment {
                                                IsCompressed = compressed,
                                                Offset       = segment_offset,
                                                Size         = (uint)segment_size,
                                                PackedSize   = (uint)segment_packed_size
                                            };
                                            entry.Segments.Add(segment);
                                        }
                                        entry.Offset = entry.Segments.First().Offset;
                                    }
                                    break;

                                case 0x726c6461: // "adlr"
                                    if (4 == section_size)
                                    {
                                        entry.Hash = header.ReadUInt32();
                                    }
                                    break;

                                default: // unknown section
                                    break;
                                }
                                header.BaseStream.Position = next_section_pos;
                            }
                            if (!string.IsNullOrEmpty(entry.Name) && entry.Segments.Any())
                            {
                                if (entry.Cipher.ObfuscatedIndex)
                                {
                                    DeobfuscateEntry(entry);
                                }
                                dir.Add(entry);
                            }
                        }
                        else if (entry_size > 7)
                        {
                            // 0x6E666E68 == entry_signature    // "hnfn"
                            // 0x6C696D73 == entry_signature    // "smil"
                            // 0x46696C65 == entry_signature    // "eliF"
                            // 0x757A7559 == entry_signature    // "Yuzu"
                            uint hash      = header.ReadUInt32();
                            int  name_size = header.ReadInt16();
                            if (name_size > 0)
                            {
                                entry_size -= 6;
                                if (name_size * 2 <= entry_size)
                                {
                                    var filename = new string (header.ReadChars(name_size));
                                    filename_map.Add(hash, filename);
                                }
                            }
                        }
NextEntry:
                        header.BaseStream.Position = dir_offset;
                    }
                }
            if (0 == dir.Count)
            {
                return(null);
            }
            var arc = new ArcFile(file, this, dir);

            try
            {
                if (crypt_algorithm.IsValueCreated)
                {
                    crypt_algorithm.Value.Init(arc);
                }
                return(arc);
            }
            catch
            {
                arc.Dispose();
                throw;
            }
        }