JStream ParseBStream(byte typeId)
        {
            if (debugWriter != null)
            {
                debugWriter.WriteLine($"{PsbTokenType.BStream} {typeId} <");
                ++debugWriter.Indent;
                debugWriter.Write("index - ");
            }
            uint index = ParseUInt((byte)(typeId - 21));

            if (debugWriter != null)
            {
                debugWriter.WriteLine("* offset - 0x{0:x8}", bStreamsOffsets[index]);
                debugWriter.WriteLine("* length - 0x{0:x8}", bStreamsSizes[index]);
                --debugWriter.Indent;
                debugWriter.WriteLine(">");
            }

            var js = new JStream(index, true, this);

            if (!lazyStreamLoading)
            {
                long oldPos = stream.Position;
                stream.Seek(bStreamsBlobOffset + bStreamsOffsets[index], SeekOrigin.Begin);
                byte[] data = br.ReadBytes((int)bStreamsSizes[index]);
                stream.Position = oldPos;
                js.BinaryData   = data;
                js.Reader       = null;
            }
            BStreamCache[index] = js;
            return(js);
        }
 internal byte[] GetStreamData(JStream js)
 {
     if (js.Reader != this)
     {
         throw new ArgumentException("Stream does not belong to this reader.", nameof(js));
     }
     CheckDisposed();
     if (js.IsBStream)
     {
         stream.Seek(bStreamsBlobOffset + bStreamsOffsets[js.Index], SeekOrigin.Begin);
         return(br.ReadBytes((int)bStreamsSizes[js.Index]));
     }
     else
     {
         stream.Seek(streamsBlobOffset + streamsOffsets[js.Index], SeekOrigin.Begin);
         return(br.ReadBytes((int)streamsSizes[js.Index]));
     }
 }
        void PrepareNode(JToken node)
        {
            if (node.Type == JTokenType.String)
            {
                var stringValue = (string)node;
                if ((stringValue.StartsWith("_stream:") || stringValue.StartsWith("_bstream:")) &&
                    !(node is JStream))
                {
                    // Replace stream string representation with a JStream
                    var newNode = JStream.CreateFromStringRepresentation(stringValue);
                    ((JValue)node).Value = string.Empty; // Invalidate old node so replacement goes through
                    node.Replace(newNode);
                    node = newNode;
                }

                if (node is JStream stream)
                {
                    PrepareStream(stream);
                }
                else
                {
                    PrepareString(stringValue);
                }
            }
            else if (node is JArray array)
            {
                foreach (var token in array)
                {
                    PrepareNode(token);
                }
            }
            else if (node is JObject obj)
            {
                foreach (var prop in obj)
                {
                    keyNamesGen.AddString(prop.Key);
                    PrepareNode(prop.Value);
                }
            }
        }
 void Write(BinaryWriter bw, JStream stream)
 {
     Write(bw, stream.Index, stream.IsBStream ? 34 : 25);
 }
        void PrepareStream(JStream stream)
        {
            if (stream.IsBStream && Version < 4)
            {
                stream.IsBStream = false;
            }
            var  cache      = stream.IsBStream ? bStreamsCache : streamsCache;
            bool doOptimize = Optimize;

            byte[] hash = null;
            long   length;

            if (stream.BinaryData != null)
            {
                if (doOptimize)
                {
                    hash = hasher.ComputeHash(stream.BinaryData);
                }
                length = stream.BinaryData.Length;
            }
            else if (streamSource != null)
            {
                using (Stream dataStream = streamSource.GetStream((string)stream))
                {
                    if (doOptimize)
                    {
                        hash = hasher.ComputeHash(dataStream);
                    }
                    length = dataStream.Length;
                }
            }
            else
            {
                throw new Exception("No binary data for stream");
            }

            if (doOptimize)
            {
                string hashString = BitConverter.ToString(hash).Replace("-", string.Empty);
                if (cache.TryGetValue(hashString, out var entry))
                {
                    entry.Streams.Add(stream);
                }
                else
                {
                    cache.Add(hashString, new StreamCacheEntry
                    {
                        OrigValue = (string)stream,
                        Streams   = new List <JStream> {
                            stream
                        },
                        Length = length
                    });
                }
            }
            else
            {
                cache.Add(cache.Count.ToString(), new StreamCacheEntry
                {
                    OrigValue = (string)stream,
                    Streams   = new List <JStream> {
                        stream
                    },
                    Length = length
                });
            }
        }