diff --git a/ArcFormats/Ethornell/ArcBGI.cs b/ArcFormats/Ethornell/ArcBGI.cs
index 3a066fcc..d5c0a797 100644
--- a/ArcFormats/Ethornell/ArcBGI.cs
+++ b/ArcFormats/Ethornell/ArcBGI.cs
@@ -86,7 +86,7 @@ namespace GameRes.Formats.BGI
return new BinMemoryStream (decoder.Output, entry.Name);
}
}
- return new ArcView.ArcStream (input, entry_offset, entry.Size);
+ return new ArcViewStream (input, entry_offset, entry.Size);
}
catch (Exception X)
{
@@ -172,7 +172,7 @@ namespace GameRes.Formats.BGI
public uint Length { get { return m_dst_size; } }
public DscDecoder (ArcView.Frame input)
- : base (new ArcView.ArcStream (input, input.Offset+0x20, input.Reserved-0x20))
+ : base (new ArcViewStream (input, input.Offset+0x20, input.Reserved-0x20))
{
m_magic = (uint)input.ReadUInt16 (input.Offset) << 16;
m_key = input.ReadUInt32 (input.Offset+0x10);
diff --git a/GameRes/ArcStream.cs b/GameRes/ArcStream.cs
new file mode 100644
index 00000000..6be33c13
--- /dev/null
+++ b/GameRes/ArcStream.cs
@@ -0,0 +1,449 @@
+//! \file ArcStream.cs
+//! \date Thu Mar 30 03:19:41 2017
+//! \brief Stream on top of the memory-mapped view.
+//
+// Copyright (C) 2014-2017 by morkt
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+using GameRes.Utility;
+
+namespace GameRes
+{
+ public class ArcViewStream : Stream, IBinaryStream
+ {
+ private readonly ArcView.Frame m_view;
+ private readonly long m_start;
+ private readonly long m_size;
+ private long m_position;
+ private byte[] m_buffer;
+ private int m_buffer_pos; // read position within buffer
+ private int m_buffer_len; // length of bytes read in buffer
+
+ private const int DefaultBufferSize = 0x1000;
+
+ public string Name { get; set; }
+ public uint Signature { get { return ReadSignature(); } }
+ public Stream AsStream { get { return this; } }
+
+ public override bool CanRead { get { return !disposed; } }
+ public override bool CanSeek { get { return !disposed; } }
+ public override bool CanWrite { get { return false; } }
+ public override long Length { get { return m_size; } }
+ public override long Position
+ {
+ get { return m_position + (m_buffer_pos - m_buffer_len); }
+ set {
+ if (value < 0)
+ throw new ArgumentOutOfRangeException ("value", "Stream position is out of range.");
+ var buffer_start = m_position - m_buffer_len;
+ if (m_buffer_pos != m_buffer_len && value >= buffer_start && value < m_position)
+ {
+ m_buffer_pos = (int)(value - buffer_start);
+ }
+ else
+ {
+ m_position = value;
+ m_buffer_pos = m_buffer_len = 0;
+ }
+ }
+ }
+
+ public ArcViewStream (ArcView file)
+ {
+ m_view = file.CreateFrame();
+ m_start = 0;
+ m_size = file.MaxOffset;
+ m_position = 0;
+ Name = file.Name;
+ }
+
+ public ArcViewStream (ArcView.Frame view, string name = null)
+ {
+ m_view = view;
+ m_start = m_view.Offset;
+ m_size = m_view.Reserved;
+ m_position = 0;
+ Name = name ?? "";
+ }
+
+ public ArcViewStream (ArcView file, long offset, uint size, string name = null)
+ : this (new ArcView.Frame (file, offset, size), name)
+ {
+ }
+
+ public ArcViewStream (ArcView.Frame view, long offset, uint size, string name = null)
+ {
+ m_view = view;
+ m_start = offset;
+ m_size = Math.Min (size, m_view.Reserve (offset, size));
+ m_position = 0;
+ Name = name ?? "";
+ }
+
+ ///
+ /// Read stream signature (first 4 bytes) without altering current read position.
+ ///
+ public uint ReadSignature ()
+ {
+ return m_view.ReadUInt32 (m_start);
+ }
+
+ byte[] m_header;
+ int m_header_size;
+
+ public CowArray ReadHeader (int size)
+ {
+ if (m_header_size < size)
+ {
+ if (null == m_header || m_header.Length < size)
+ Array.Resize (ref m_header, (size + 0xF) & ~0xF);
+ long position = m_start + m_header_size;
+ m_header_size += m_view.Read (position, m_header, m_header_size, (uint)(size - m_header_size));
+ }
+ if (size > m_header_size)
+ {
+ Position = m_header_size;
+ throw new EndOfStreamException();
+ }
+ Position = size;
+ return new CowArray (m_header, 0, size);
+ }
+
+ private void RefillBuffer ()
+ {
+ if (null == m_buffer)
+ m_buffer = new byte[DefaultBufferSize];
+ uint length = (uint)Math.Min (m_size - m_position, m_buffer.Length);
+ m_buffer_len = m_view.Read (m_start + m_position, m_buffer, 0, length);
+ m_position += m_buffer_len;
+ m_buffer_pos = 0;
+ }
+
+ private void FlushBuffer ()
+ {
+ if (m_buffer_len != 0)
+ {
+ m_position += m_buffer_pos - m_buffer_len;
+ m_buffer_pos = m_buffer_len = 0;
+ }
+ }
+
+ private void EnsureAvailable (int length)
+ {
+ if (m_buffer_pos + length > m_buffer_len)
+ {
+ FlushBuffer();
+ if (m_position + length > m_size)
+ throw new EndOfStreamException();
+ RefillBuffer();
+ }
+ }
+
+ private int ReadFromBuffer (byte[] array, int offset, int count)
+ {
+ int available = Math.Min (m_buffer_len - m_buffer_pos, count);
+ if (available > 0)
+ {
+ Buffer.BlockCopy (m_buffer, m_buffer_pos, array, offset, available);
+ m_buffer_pos += available;
+ }
+ return available;
+ }
+
+ public int PeekByte ()
+ {
+ if (m_buffer_pos == m_buffer_len)
+ RefillBuffer();
+ if (m_buffer_pos == m_buffer_len)
+ return -1;
+ return m_buffer[m_buffer_pos];
+ }
+
+ public override int ReadByte ()
+ {
+ int b = PeekByte();
+ if (-1 != b)
+ ++m_buffer_pos;
+ return b;
+ }
+
+ public sbyte ReadInt8 ()
+ {
+ int b = ReadByte();
+ if (-1 == b)
+ throw new EndOfStreamException();
+ return (sbyte)b;
+ }
+
+ public byte ReadUInt8 ()
+ {
+ return (byte)ReadInt8();
+ }
+
+ public short ReadInt16 ()
+ {
+ EnsureAvailable (2);
+ var v = m_buffer.ToInt16 (m_buffer_pos);
+ m_buffer_pos += 2;
+ return v;
+ }
+
+ public ushort ReadUInt16 ()
+ {
+ return (ushort)ReadInt16();
+ }
+
+ public int ReadInt24 ()
+ {
+ EnsureAvailable (3);
+ int v = m_buffer.ToInt24 (m_buffer_pos);
+ m_buffer_pos += 3;
+ return v;
+ }
+
+ public int ReadInt32 ()
+ {
+ EnsureAvailable (4);
+ int v = m_buffer.ToInt32 (m_buffer_pos);
+ m_buffer_pos += 4;
+ return v;
+ }
+
+ public uint ReadUInt32 ()
+ {
+ return (uint)ReadInt32();
+ }
+
+ public long ReadInt64 ()
+ {
+ EnsureAvailable (8);
+ var v = m_buffer.ToInt64 (m_buffer_pos);
+ m_buffer_pos += 8;
+ return v;
+ }
+
+ public ulong ReadUInt64 ()
+ {
+ return (ulong)ReadInt64();
+ }
+
+ public string ReadCString (int length)
+ {
+ return ReadCString (length, Encodings.cp932);
+ }
+
+ public string ReadCString (int length, Encoding enc)
+ {
+ if (m_buffer_pos == m_buffer_len && length <= DefaultBufferSize)
+ RefillBuffer();
+ if (m_buffer_pos + length <= m_buffer_len)
+ {
+ // whole string fit into buffer
+ var str = Binary.GetCString (m_buffer, m_buffer_pos, length, enc);
+ m_buffer_pos += length;
+ return str;
+ }
+ else if (length > DefaultBufferSize)
+ {
+ // requested string length is larger than internal buffer size
+ var string_buffer = ReadBytes (length);
+ return Binary.GetCString (string_buffer, 0, string_buffer.Length, enc);
+ }
+ else
+ {
+ int available = m_buffer_len - m_buffer_pos;
+ if (available > 0 && m_buffer_pos != 0)
+ Buffer.BlockCopy (m_buffer, m_buffer_pos, m_buffer, 0, available);
+ else if (null == m_buffer)
+ m_buffer = new byte[DefaultBufferSize];
+ int count = (int)Math.Min (m_buffer.Length - available, m_size - m_position);
+ if (count > 0)
+ {
+ int read = m_view.Read (m_start + m_position, m_buffer, available, (uint)count);
+ m_position += read;
+ available += read;
+ }
+ m_buffer_len = available;
+ m_buffer_pos = Math.Min (length, m_buffer_len);
+ return Binary.GetCString (m_buffer, 0, m_buffer_pos, enc);
+ }
+ }
+
+ public string ReadCString ()
+ {
+ return ReadCString (Encodings.cp932);
+ }
+
+ public string ReadCString (Encoding enc)
+ {
+ if (m_buffer_pos == m_buffer_len)
+ RefillBuffer();
+ int available = m_buffer_len - m_buffer_pos;
+ if (0 == available)
+ return string.Empty;
+
+ int zero = Array.IndexOf (m_buffer, 0, m_buffer_pos, available);
+ if (zero != -1)
+ {
+ // null byte found within buffer
+ var str = enc.GetString (m_buffer, m_buffer_pos, zero - m_buffer_pos);
+ m_buffer_pos = zero+1;
+ return str;
+ }
+ // underlying view includes whole stream
+ if (m_view.Offset <= m_start && m_view.Offset + m_view.Reserved >= m_start + m_size)
+ return ReadCStringUnsafe (enc, available);
+
+ var string_buf = new byte[Math.Max (0x20, available * 2)];
+ ReadFromBuffer (string_buf, 0, available);
+ int size = available;
+ for (;;)
+ {
+ int b = ReadByte();
+ if (-1 == b || 0 == b)
+ break;
+ if (string_buf.Length == size)
+ {
+ Array.Resize (ref string_buf, checked(size*3/2));
+ }
+ string_buf[size++] = (byte)b;
+ }
+ return enc.GetString (string_buf, 0, size);
+ }
+
+ private unsafe string ReadCStringUnsafe (Encoding enc, int skip_bytes = 0)
+ {
+ Debug.Assert (m_view.Offset + m_view.Reserved >= m_start + m_size);
+ FlushBuffer();
+ using (var ptr = m_view.GetPointer())
+ {
+ byte* s = ptr.Value + (m_start - m_view.Offset + m_position);
+ int view_length = (int)(m_size - m_position);
+ int string_length = Math.Min (skip_bytes, view_length);
+ while (string_length < view_length && 0 != s[string_length])
+ {
+ ++string_length;
+ }
+ m_position += string_length;
+ if (string_length < view_length)
+ ++m_position;
+// return enc.GetString (s, string_length); // .Net v4.6+ only
+ var string_buf = new byte[string_length];
+ Marshal.Copy ((IntPtr)s, string_buf, 0, string_length);
+ return enc.GetString (string_buf, 0, string_length);
+ }
+ }
+
+ public byte[] ReadBytes (int count)
+ {
+ if (m_buffer_pos + count <= m_buffer_len && m_buffer_len != 0)
+ {
+ var data = new CowArray (m_buffer, m_buffer_pos, count).ToArray();
+ m_buffer_pos += count;
+ return data;
+ }
+ var current_pos = Position;
+ if (0 == count || current_pos >= m_size)
+ return new byte[0];
+ var bytes = m_view.ReadBytes (m_start+current_pos, (uint)Math.Min (count, m_size - current_pos));
+ Position = current_pos + bytes.Length;
+ return bytes;
+ }
+
+ #region System.IO.Stream methods
+ public override void Flush()
+ {
+ FlushBuffer();
+ }
+
+ public override long Seek (long offset, SeekOrigin origin)
+ {
+ switch (origin)
+ {
+ case SeekOrigin.Begin: Position = offset; break;
+ case SeekOrigin.Current: Position += offset; break;
+ case SeekOrigin.End: Position = m_size + offset; break;
+ }
+ return Position;
+ }
+
+ public override void SetLength (long length)
+ {
+ throw new NotSupportedException ("GameRes.ArcStream.SetLength method is not supported");
+ }
+
+ public override int Read (byte[] buffer, int offset, int count)
+ {
+ int read_from_buffer = ReadFromBuffer (buffer, offset, count);
+ offset += read_from_buffer;
+ count -= read_from_buffer;
+ if (0 == count || m_position >= m_size)
+ return read_from_buffer;
+ if (count < DefaultBufferSize)
+ {
+ RefillBuffer();
+ count = Math.Min (count, m_buffer_len);
+ Buffer.BlockCopy (m_buffer, m_buffer_pos, buffer, offset, count);
+ m_buffer_pos += count;
+ return read_from_buffer + count;
+ }
+ else
+ {
+ uint view_count = (uint)Math.Min (count, m_size - m_position);
+ int read_from_view = m_view.Read (m_start + m_position, buffer, offset, view_count);
+ m_position += read_from_view;
+ return read_from_buffer + read_from_view;
+ }
+ }
+
+ public override void Write (byte[] buffer, int offset, int count)
+ {
+ throw new NotSupportedException("GameRes.ArcStream.Write method is not supported");
+ }
+
+ public override void WriteByte (byte value)
+ {
+ throw new NotSupportedException("GameRes.ArcStream.WriteByte method is not supported");
+ }
+ #endregion
+
+ #region IDisposable Members
+ bool disposed = false;
+ protected override void Dispose (bool disposing)
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ m_view.Dispose();
+ }
+ disposed = true;
+ base.Dispose (disposing);
+ }
+ }
+ #endregion
+ }
+}
diff --git a/GameRes/ArcView.cs b/GameRes/ArcView.cs
index c6b2551d..8e0aebe2 100644
--- a/GameRes/ArcView.cs
+++ b/GameRes/ArcView.cs
@@ -2,7 +2,7 @@
//! \date Mon Jul 07 10:31:10 2014
//! \brief Memory mapped view of gameres file.
//
-// Copyright (C) 2014-2015 by morkt
+// Copyright (C) 2014-2017 by morkt
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
@@ -24,12 +24,10 @@
//
using System;
-using System.Diagnostics;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;
using System.Text;
-using GameRes.Utility;
namespace GameRes
{
@@ -222,22 +220,22 @@ namespace GameRes
return new Frame (View);
}
- public ArcStream CreateStream ()
+ public ArcViewStream CreateStream ()
{
- return new ArcStream (this);
+ return new ArcViewStream (this);
}
- public ArcStream CreateStream (long offset)
+ public ArcViewStream CreateStream (long offset)
{
var size = this.MaxOffset - offset;
if (size > uint.MaxValue)
throw new ArgumentOutOfRangeException ("Too large memory mapped stream");
- return new ArcStream (this, offset, (uint)size);
+ return new ArcViewStream (this, offset, (uint)size);
}
- public ArcStream CreateStream (long offset, uint size, string name = null)
+ public ArcViewStream CreateStream (long offset, uint size, string name = null)
{
- return new ArcStream (this, offset, size, name);
+ return new ArcViewStream (this, offset, size, name);
}
public MemoryMappedViewAccessor CreateViewAccessor (long offset, uint size)
@@ -493,421 +491,6 @@ namespace GameRes
#endregion
}
- public class ArcStream : Stream, IBinaryStream
- {
- private readonly Frame m_view;
- private readonly long m_start;
- private readonly long m_size;
- private long m_position;
- private byte[] m_buffer;
- private int m_buffer_pos; // read position within buffer
- private int m_buffer_len; // length of bytes read in buffer
-
- private const int DefaultBufferSize = 0x1000;
-
- public string Name { get; set; }
- public uint Signature { get { return ReadSignature(); } }
- public Stream AsStream { get { return this; } }
-
- public override bool CanRead { get { return !disposed; } }
- public override bool CanSeek { get { return !disposed; } }
- public override bool CanWrite { get { return false; } }
- public override long Length { get { return m_size; } }
- public override long Position
- {
- get { return m_position + (m_buffer_pos - m_buffer_len); }
- set {
- if (value < 0)
- throw new ArgumentOutOfRangeException ("value", "Stream position is out of range.");
- var buffer_start = m_position - m_buffer_len;
- if (m_buffer_pos != m_buffer_len && value >= buffer_start && value < m_position)
- {
- m_buffer_pos = (int)(value - buffer_start);
- }
- else
- {
- m_position = value;
- m_buffer_pos = m_buffer_len = 0;
- }
- }
- }
-
- public ArcStream (ArcView file)
- {
- m_view = file.CreateFrame();
- m_start = 0;
- m_size = file.MaxOffset;
- m_position = 0;
- Name = file.Name;
- }
-
- public ArcStream (Frame view, string name = null)
- {
- m_view = view;
- m_start = m_view.Offset;
- m_size = m_view.Reserved;
- m_position = 0;
- Name = name ?? "";
- }
-
- public ArcStream (ArcView file, long offset, uint size, string name = null)
- : this (new Frame (file, offset, size), name)
- {
- }
-
- public ArcStream (Frame view, long offset, uint size, string name = null)
- {
- m_view = view;
- m_start = offset;
- m_size = Math.Min (size, m_view.Reserve (offset, size));
- m_position = 0;
- Name = name ?? "";
- }
-
- ///
- /// Read stream signature (first 4 bytes) without altering current read position.
- ///
- public uint ReadSignature ()
- {
- return m_view.ReadUInt32 (m_start);
- }
-
- byte[] m_header;
- int m_header_size;
-
- public CowArray ReadHeader (int size)
- {
- if (m_header_size < size)
- {
- if (null == m_header || m_header.Length < size)
- Array.Resize (ref m_header, (size + 0xF) & ~0xF);
- long position = m_start + m_header_size;
- m_header_size += m_view.Read (position, m_header, m_header_size, (uint)(size - m_header_size));
- }
- if (size > m_header_size)
- {
- Position = m_header_size;
- throw new EndOfStreamException();
- }
- Position = size;
- return new CowArray (m_header, 0, size);
- }
-
- private void RefillBuffer ()
- {
- if (null == m_buffer)
- m_buffer = new byte[DefaultBufferSize];
- uint length = (uint)Math.Min (m_size - m_position, m_buffer.Length);
- m_buffer_len = m_view.Read (m_start + m_position, m_buffer, 0, length);
- m_position += m_buffer_len;
- m_buffer_pos = 0;
- }
-
- private void FlushBuffer ()
- {
- if (m_buffer_len != 0)
- {
- m_position += m_buffer_pos - m_buffer_len;
- m_buffer_pos = m_buffer_len = 0;
- }
- }
-
- private void EnsureAvailable (int length)
- {
- if (m_buffer_pos + length > m_buffer_len)
- {
- FlushBuffer();
- if (m_position + length > m_size)
- throw new EndOfStreamException();
- RefillBuffer();
- }
- }
-
- private int ReadFromBuffer (byte[] array, int offset, int count)
- {
- int available = Math.Min (m_buffer_len - m_buffer_pos, count);
- if (available > 0)
- {
- Buffer.BlockCopy (m_buffer, m_buffer_pos, array, offset, available);
- m_buffer_pos += available;
- }
- return available;
- }
-
- public int PeekByte ()
- {
- if (m_buffer_pos == m_buffer_len)
- RefillBuffer();
- if (m_buffer_pos == m_buffer_len)
- return -1;
- return m_buffer[m_buffer_pos];
- }
-
- public override int ReadByte ()
- {
- int b = PeekByte();
- if (-1 != b)
- ++m_buffer_pos;
- return b;
- }
-
- public sbyte ReadInt8 ()
- {
- int b = ReadByte();
- if (-1 == b)
- throw new EndOfStreamException();
- return (sbyte)b;
- }
-
- public byte ReadUInt8 ()
- {
- return (byte)ReadInt8();
- }
-
- public short ReadInt16 ()
- {
- EnsureAvailable (2);
- var v = m_buffer.ToInt16 (m_buffer_pos);
- m_buffer_pos += 2;
- return v;
- }
-
- public ushort ReadUInt16 ()
- {
- return (ushort)ReadInt16();
- }
-
- public int ReadInt24 ()
- {
- EnsureAvailable (3);
- int v = m_buffer.ToInt24 (m_buffer_pos);
- m_buffer_pos += 3;
- return v;
- }
-
- public int ReadInt32 ()
- {
- EnsureAvailable (4);
- int v = m_buffer.ToInt32 (m_buffer_pos);
- m_buffer_pos += 4;
- return v;
- }
-
- public uint ReadUInt32 ()
- {
- return (uint)ReadInt32();
- }
-
- public long ReadInt64 ()
- {
- EnsureAvailable (8);
- var v = m_buffer.ToInt64 (m_buffer_pos);
- m_buffer_pos += 8;
- return v;
- }
-
- public ulong ReadUInt64 ()
- {
- return (ulong)ReadInt64();
- }
-
- public string ReadCString (int length)
- {
- return ReadCString (length, Encodings.cp932);
- }
-
- public string ReadCString (int length, Encoding enc)
- {
- if (m_buffer_pos == m_buffer_len && length <= DefaultBufferSize)
- RefillBuffer();
- if (m_buffer_pos + length <= m_buffer_len)
- {
- // whole string fit into buffer
- var str = Binary.GetCString (m_buffer, m_buffer_pos, length, enc);
- m_buffer_pos += length;
- return str;
- }
- else if (length > DefaultBufferSize)
- {
- // requested string length is larger than internal buffer size
- var string_buffer = ReadBytes (length);
- return Binary.GetCString (string_buffer, 0, string_buffer.Length, enc);
- }
- else
- {
- int available = m_buffer_len - m_buffer_pos;
- if (available > 0 && m_buffer_pos != 0)
- Buffer.BlockCopy (m_buffer, m_buffer_pos, m_buffer, 0, available);
- else if (null == m_buffer)
- m_buffer = new byte[DefaultBufferSize];
- int count = (int)Math.Min (m_buffer.Length - available, m_size - m_position);
- if (count > 0)
- {
- int read = m_view.Read (m_start + m_position, m_buffer, available, (uint)count);
- m_position += read;
- available += read;
- }
- m_buffer_len = available;
- m_buffer_pos = Math.Min (length, m_buffer_len);
- return Binary.GetCString (m_buffer, 0, m_buffer_pos, enc);
- }
- }
-
- public string ReadCString ()
- {
- return ReadCString (Encodings.cp932);
- }
-
- public string ReadCString (Encoding enc)
- {
- if (m_buffer_pos == m_buffer_len)
- RefillBuffer();
- int available = m_buffer_len - m_buffer_pos;
- if (0 == available)
- return string.Empty;
-
- int zero = Array.IndexOf (m_buffer, 0, m_buffer_pos, available);
- if (zero != -1)
- {
- // null byte found within buffer
- var str = enc.GetString (m_buffer, m_buffer_pos, zero - m_buffer_pos);
- m_buffer_pos = zero+1;
- return str;
- }
- // underlying view includes whole stream
- if (m_view.Offset <= m_start && m_view.Offset + m_view.Reserved >= m_start + m_size)
- return ReadCStringUnsafe (enc, available);
-
- var string_buf = new byte[Math.Max (0x20, available * 2)];
- ReadFromBuffer (string_buf, 0, available);
- int size = available;
- for (;;)
- {
- int b = ReadByte();
- if (-1 == b || 0 == b)
- break;
- if (string_buf.Length == size)
- {
- Array.Resize (ref string_buf, checked(size*3/2));
- }
- string_buf[size++] = (byte)b;
- }
- return enc.GetString (string_buf, 0, size);
- }
-
- private unsafe string ReadCStringUnsafe (Encoding enc, int skip_bytes = 0)
- {
- Debug.Assert (m_view.Offset + m_view.Reserved >= m_start + m_size);
- FlushBuffer();
- using (var ptr = m_view.GetPointer())
- {
- byte* s = ptr.Value + (m_start - m_view.Offset + m_position);
- int view_length = (int)(m_size - m_position);
- int string_length = Math.Min (skip_bytes, view_length);
- while (string_length < view_length && 0 != s[string_length])
- {
- ++string_length;
- }
- m_position += string_length;
- if (string_length < view_length)
- ++m_position;
-// return enc.GetString (s, string_length); // .Net v4.6+ only
- var string_buf = new byte[string_length];
- Marshal.Copy ((IntPtr)s, string_buf, 0, string_length);
- return enc.GetString (string_buf, 0, string_length);
- }
- }
-
- public byte[] ReadBytes (int count)
- {
- if (m_buffer_pos + count <= m_buffer_len && m_buffer_len != 0)
- {
- var data = new CowArray (m_buffer, m_buffer_pos, count).ToArray();
- m_buffer_pos += count;
- return data;
- }
- var current_pos = Position;
- if (0 == count || current_pos >= m_size)
- return new byte[0];
- var bytes = m_view.ReadBytes (m_start+current_pos, (uint)Math.Min (count, m_size - current_pos));
- Position = current_pos + bytes.Length;
- return bytes;
- }
-
- #region System.IO.Stream methods
- public override void Flush()
- {
- FlushBuffer();
- }
-
- public override long Seek (long offset, SeekOrigin origin)
- {
- switch (origin)
- {
- case SeekOrigin.Begin: Position = offset; break;
- case SeekOrigin.Current: Position += offset; break;
- case SeekOrigin.End: Position = m_size + offset; break;
- }
- return Position;
- }
-
- public override void SetLength (long length)
- {
- throw new NotSupportedException ("GameRes.ArcStream.SetLength method is not supported");
- }
-
- public override int Read (byte[] buffer, int offset, int count)
- {
- int read_from_buffer = ReadFromBuffer (buffer, offset, count);
- offset += read_from_buffer;
- count -= read_from_buffer;
- if (0 == count || m_position >= m_size)
- return read_from_buffer;
- if (count < DefaultBufferSize)
- {
- RefillBuffer();
- count = Math.Min (count, m_buffer_len);
- Buffer.BlockCopy (m_buffer, m_buffer_pos, buffer, offset, count);
- m_buffer_pos += count;
- return read_from_buffer + count;
- }
- else
- {
- uint view_count = (uint)Math.Min (count, m_size - m_position);
- int read_from_view = m_view.Read (m_start + m_position, buffer, offset, view_count);
- m_position += read_from_view;
- return read_from_buffer + read_from_view;
- }
- }
-
- public override void Write (byte[] buffer, int offset, int count)
- {
- throw new NotSupportedException("GameRes.ArcStream.Write method is not supported");
- }
-
- public override void WriteByte (byte value)
- {
- throw new NotSupportedException("GameRes.ArcStream.WriteByte method is not supported");
- }
- #endregion
-
- #region IDisposable Members
- bool disposed = false;
- protected override void Dispose (bool disposing)
- {
- if (!disposed)
- {
- if (disposing)
- {
- m_view.Dispose();
- }
- disposed = true;
- base.Dispose (disposing);
- }
- }
- #endregion
- }
-
public class Reader : System.IO.BinaryReader
{
public Reader (Stream stream) : base (stream, Encoding.ASCII, true)
diff --git a/GameRes/GameRes.csproj b/GameRes/GameRes.csproj
index f6a09928..3b8acf91 100644
--- a/GameRes/GameRes.csproj
+++ b/GameRes/GameRes.csproj
@@ -64,6 +64,7 @@
+