Add multiBundle support

including fake headers
This commit is contained in:
VaDiM 2025-03-08 20:45:03 +03:00
parent db4eb30a27
commit bc0e32efec
8 changed files with 317 additions and 69 deletions

View File

@ -201,7 +201,7 @@ namespace AssetStudio
{ {
if (!assetsFileListHash.Contains(reader.FileName)) if (!assetsFileListHash.Contains(reader.FileName))
{ {
Logger.Info($"Loading {reader.FullPath}"); Logger.Info($"Loading \"{reader.FullPath}\"");
try try
{ {
var assetsFile = new SerializedFile(reader, this); var assetsFile = new SerializedFile(reader, this);
@ -248,13 +248,13 @@ namespace AssetStudio
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warning($"Failed to read assets file {reader.FullPath}\r\n{e}"); Logger.Warning($"Failed to read assets file \"{reader.FullPath}\"\n{e}");
reader.Dispose(); reader.Dispose();
} }
} }
else else
{ {
Logger.Info($"Skipping {reader.FullPath}"); Logger.Info($"Skipping \"{reader.FullPath}\"");
reader.Dispose(); reader.Dispose();
} }
return true; return true;
@ -284,38 +284,46 @@ namespace AssetStudio
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warning($"Failed to read assets file {reader.FullPath} from {Path.GetFileName(originalPath)}\r\n{e}"); Logger.Warning($"Failed to read assets file \"{reader.FullPath}\" from {Path.GetFileName(originalPath)}\n{e}");
resourceFileReaders.TryAdd(reader.FileName, reader); resourceFileReaders.TryAdd(reader.FileName, reader);
} }
} }
else else
{ {
Logger.Info($"Skipping {originalPath} ({reader.FileName})"); Logger.Info($"Skipping \"{originalPath}\" ({reader.FileName})");
} }
return true; return true;
} }
private bool LoadBundleFile(FileReader reader, string originalPath = null) private bool LoadBundleFile(FileReader reader, string originalPath = null)
{ {
Logger.Info("Loading " + reader.FullPath); Logger.Info($"Loading \"{reader.FullPath}\"");
Logger.Debug($"Bundle offset: {reader.Position}");
var bundleStream = new OffsetStream(reader);
var bundleReader = new FileReader(reader.FullPath, bundleStream);
try try
{ {
var bundleFile = new BundleFile(reader, ZstdEnabled, specifiedUnityVersion); var bundleFile = new BundleFile(bundleReader, ZstdEnabled, specifiedUnityVersion);
foreach (var file in bundleFile.fileList) var isLoaded = LoadBundleFiles(bundleReader, bundleFile, originalPath);
if (!isLoaded)
return false;
while (bundleFile.IsMultiBundle && isLoaded)
{ {
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), file.fileName); bundleStream.Offset = reader.Position;
var subReader = new FileReader(dummyPath, file.stream); bundleReader = new FileReader($"{reader.FullPath}_0x{bundleStream.Offset:X}", bundleStream);
if (subReader.FileType == FileType.AssetsFile) if (bundleReader.Position > 0)
{ {
if (!LoadAssetsFromMemory(subReader, originalPath ?? reader.FullPath, bundleFile.m_Header.unityRevision)) bundleStream.Offset += bundleReader.Position;
return false; bundleReader.FullPath = $"{reader.FullPath}_0x{bundleStream.Offset:X}";
} bundleReader.FileName = $"{reader.FileName}_0x{bundleStream.Offset:X}";
else
{
resourceFileReaders.TryAdd(file.fileName, subReader);
} }
Logger.Info($"[MultiBundle] Loading \"{reader.FileName}\" from offset: 0x{bundleStream.Offset:X}");
bundleFile = new BundleFile(bundleReader, ZstdEnabled, specifiedUnityVersion);
isLoaded = LoadBundleFiles(bundleReader, bundleFile, originalPath ?? reader.FullPath);
} }
return true; return isLoaded;
} }
catch (NotSupportedException e) catch (NotSupportedException e)
{ {
@ -324,23 +332,42 @@ namespace AssetStudio
} }
catch (Exception e) catch (Exception e)
{ {
var str = $"Error while reading bundle file {reader.FullPath}"; var str = $"Error while reading bundle file \"{bundleReader.FullPath}\"";
if (originalPath != null) if (originalPath != null)
{ {
str += $" from {Path.GetFileName(originalPath)}"; str += $" from {Path.GetFileName(originalPath)}";
} }
Logger.Warning($"{str}\r\n{e}"); Logger.Warning($"{str}\n{e}");
return true; return true;
} }
finally finally
{ {
reader.Dispose(); bundleReader.Dispose();
} }
} }
private bool LoadBundleFiles(FileReader reader, BundleFile bundleFile, string originalPath = null)
{
foreach (var file in bundleFile.fileList)
{
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), file.fileName);
var subReader = new FileReader(dummyPath, file.stream);
if (subReader.FileType == FileType.AssetsFile)
{
if (!LoadAssetsFromMemory(subReader, originalPath ?? reader.FullPath, bundleFile.m_Header.unityRevision))
return false;
}
else
{
resourceFileReaders.TryAdd(file.fileName, subReader);
}
}
return true;
}
private void LoadWebFile(FileReader reader) private void LoadWebFile(FileReader reader)
{ {
Logger.Info("Loading " + reader.FullPath); Logger.Info($"Loading \"{reader.FullPath}\"");
try try
{ {
var webFile = new WebFile(reader); var webFile = new WebFile(reader);
@ -367,7 +394,7 @@ namespace AssetStudio
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Error($"Error while reading web file {reader.FullPath}", e); Logger.Error($"Error while reading web file \"{reader.FullPath}\"", e);
} }
finally finally
{ {
@ -427,7 +454,7 @@ namespace AssetStudio
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warning($"Error while reading zip split file {basePath}\r\n{e}"); Logger.Warning($"Error while reading zip split file \"{basePath}\"\n{e}");
} }
} }
@ -461,7 +488,7 @@ namespace AssetStudio
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Warning($"Error while reading zip entry {entry.FullName}\r\n{e}"); Logger.Warning($"Error while reading zip entry \"{entry.FullName}\"\n{e}");
} }
} }
} }

View File

@ -42,6 +42,8 @@ namespace AssetStudio
public class BundleFile public class BundleFile
{ {
public readonly bool IsMultiBundle;
public class Header public class Header
{ {
public string signature; public string signature;
@ -102,6 +104,17 @@ namespace AssetStudio
case "UnityFS": case "UnityFS":
ReadHeader(reader); ReadHeader(reader);
var bundleSize = m_Header.size;
var streamSize = reader.BaseStream.Length;
if (bundleSize > streamSize)
{
Logger.Warning("Bundle size is incorrect.");
}
else if (streamSize - bundleSize > 200)
{
IsMultiBundle = true;
}
var isUnityCnEnc = false; var isUnityCnEnc = false;
var unityVer = m_Header.unityRevision; var unityVer = m_Header.unityRevision;
if (specUnityVer != null) if (specUnityVer != null)

View File

@ -1,8 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text;
using static AssetStudio.EndianSpanReader;
namespace AssetStudio namespace AssetStudio
{ {
@ -59,24 +57,25 @@ namespace AssetStudio
} }
isBigEndian = BitConverter.ToBoolean(modelData, 5); isBigEndian = BitConverter.ToBoolean(modelData, 5);
var modelDataSpan = modelData.AsSpan();
//offsets //offsets
var countInfoTableOffset = (int)SpanToUint32(modelData, 64, isBigEndian); var countInfoTableOffset = (int)modelDataSpan.ReadUInt32(64, isBigEndian);
var canvasInfoOffset = (int)SpanToUint32(modelData, 68, isBigEndian); var canvasInfoOffset = (int)modelDataSpan.ReadUInt32(68, isBigEndian);
var partIdsOffset = SpanToUint32(modelData, 76, isBigEndian); var partIdsOffset = modelDataSpan.ReadUInt32(76, isBigEndian);
var parameterIdsOffset = SpanToUint32(modelData, 264, isBigEndian); var parameterIdsOffset = modelDataSpan.ReadUInt32(264, isBigEndian);
//canvas //canvas
PixelPerUnit = ToSingle(modelData, canvasInfoOffset, isBigEndian); PixelPerUnit = modelDataSpan.ReadSingle(canvasInfoOffset, isBigEndian);
CentralPosX = ToSingle(modelData, canvasInfoOffset + 4, isBigEndian); CentralPosX = modelDataSpan.ReadSingle(canvasInfoOffset + 4, isBigEndian);
CentralPosY = ToSingle(modelData, canvasInfoOffset + 8, isBigEndian); CentralPosY = modelDataSpan.ReadSingle(canvasInfoOffset + 8, isBigEndian);
CanvasWidth = ToSingle(modelData, canvasInfoOffset + 12, isBigEndian); CanvasWidth = modelDataSpan.ReadSingle(canvasInfoOffset + 12, isBigEndian);
CanvasHeight = ToSingle(modelData, canvasInfoOffset + 16, isBigEndian); CanvasHeight = modelDataSpan.ReadSingle(canvasInfoOffset + 16, isBigEndian);
//model //model
PartCount = SpanToUint32(modelData, countInfoTableOffset, isBigEndian); PartCount = modelDataSpan.ReadUInt32(countInfoTableOffset, isBigEndian);
ParamCount = SpanToUint32(modelData, countInfoTableOffset + 20, isBigEndian); ParamCount = modelDataSpan.ReadUInt32(countInfoTableOffset + 20, isBigEndian);
PartNames = ReadMocStringHashSet(modelData, (int)partIdsOffset, (int)PartCount); PartNames = ReadMocStrings(modelData, (int)partIdsOffset, (int)PartCount);
ParamNames = ReadMocStringHashSet(modelData, (int)parameterIdsOffset, (int)ParamCount); ParamNames = ReadMocStrings(modelData, (int)parameterIdsOffset, (int)ParamCount);
} }
public void SaveMoc3(string savePath) public void SaveMoc3(string savePath)
@ -103,16 +102,7 @@ namespace AssetStudio
} }
} }
private static float ToSingle(ReadOnlySpan<byte> data, int index, bool isBigEndian) //net framework ver private static HashSet<string> ReadMocStrings(Span<byte> data, int index, int count)
{
var bytes = data.Slice(index, index + 4).ToArray();
if ((isBigEndian && BitConverter.IsLittleEndian) || (!isBigEndian && !BitConverter.IsLittleEndian))
(bytes[0], bytes[1], bytes[2], bytes[3]) = (bytes[3], bytes[2], bytes[1], bytes[0]);
return BitConverter.ToSingle(bytes, 0);
}
private static HashSet<string> ReadMocStringHashSet(ReadOnlySpan<byte> data, int index, int count)
{ {
const int strLen = 64; const int strLen = 64;
var strHashSet = new HashSet<string>(); var strHashSet = new HashSet<string>();
@ -120,8 +110,8 @@ namespace AssetStudio
{ {
if (index + i * strLen <= data.Length) if (index + i * strLen <= data.Length)
{ {
var buff = data.Slice(index + i * strLen, strLen); var str = data.Slice(index + i * strLen, strLen).ReadStringToNull();
strHashSet.Add(Encoding.UTF8.GetString(buff.ToArray()).TrimEnd('\0')); strHashSet.Add(str);
} }
} }
return strHashSet; return strHashSet;

View File

@ -82,6 +82,7 @@ namespace AssetStudio
return base.ReadUInt64(); return base.ReadUInt64();
} }
#if NETFRAMEWORK
public override float ReadSingle() public override float ReadSingle()
{ {
if (Endian == EndianType.BigEndian) if (Endian == EndianType.BigEndian)
@ -103,5 +104,26 @@ namespace AssetStudio
} }
return base.ReadDouble(); return base.ReadDouble();
} }
#else
public override float ReadSingle()
{
if (Endian == EndianType.BigEndian)
{
Read(buffer, 0, 4);
return BinaryPrimitives.ReadSingleBigEndian(buffer);
}
return base.ReadSingle();
}
public override double ReadDouble()
{
if (Endian == EndianType.BigEndian)
{
Read(buffer, 0, 8);
return BinaryPrimitives.ReadDoubleBigEndian(buffer);
}
return base.ReadDouble();
}
#endif
} }
} }

View File

@ -1,29 +1,90 @@
using System; using System;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Text;
namespace AssetStudio namespace AssetStudio
{ {
public static class EndianSpanReader public static class EndianSpanReader
{ {
public static uint SpanToUint32(Span<byte> data, int start, bool isBigEndian) public static uint ReadUInt32(this Span<byte> data, int start, bool isBigEndian)
{
return SpanToUInt32(data, start, isBigEndian);
}
public static uint SpanToUInt32(Span<byte> data, int start, bool isBigEndian)
{ {
return isBigEndian return isBigEndian
? BinaryPrimitives.ReadUInt32BigEndian(data.Slice(start)) ? BinaryPrimitives.ReadUInt32BigEndian(data.Slice(start))
: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(start)); : BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(start));
} }
public static uint SpanToUint16(Span<byte> data, int start, bool isBigEndian) public static long ReadUInt16(this Span<byte> data, int start, bool isBigEndian)
{
return SpanToUInt16(data, start, isBigEndian);
}
public static uint SpanToUInt16(Span<byte> data, int start, bool isBigEndian)
{ {
return isBigEndian return isBigEndian
? BinaryPrimitives.ReadUInt16BigEndian(data.Slice(start)) ? BinaryPrimitives.ReadUInt16BigEndian(data.Slice(start))
: BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(start)); : BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(start));
} }
public static long ReadInt64(this Span<byte> data, int start, bool isBigEndian)
{
return SpanToInt64(data, start, isBigEndian);
}
public static long SpanToInt64(Span<byte> data, int start, bool isBigEndian) public static long SpanToInt64(Span<byte> data, int start, bool isBigEndian)
{ {
return isBigEndian return isBigEndian
? BinaryPrimitives.ReadInt64BigEndian(data.Slice(start)) ? BinaryPrimitives.ReadInt64BigEndian(data.Slice(start))
: BinaryPrimitives.ReadInt64LittleEndian(data.Slice(start)); : BinaryPrimitives.ReadInt64LittleEndian(data.Slice(start));
} }
public static float ReadSingle(this Span<byte> data, int start, bool isBigEndian)
{
return SpanToSingle(data, start, isBigEndian);
}
#if NETFRAMEWORK
public static float SpanToSingle(Span<byte> data, int start, bool isBigEndian)
{
var bytes = data.Slice(start, 4);
if ((isBigEndian && BitConverter.IsLittleEndian) || (!isBigEndian && !BitConverter.IsLittleEndian))
bytes.Reverse();
return BitConverter.ToSingle(bytes.ToArray(), 0);
}
#else
public static float SpanToSingle(Span<byte> data, int start, bool isBigEndian)
{
return isBigEndian
? BinaryPrimitives.ReadSingleBigEndian(data[start..])
: BinaryPrimitives.ReadSingleLittleEndian(data[start..]);
}
#endif
public static string ReadStringToNull(this Span<byte> data, int maxLength = 32767)
{
Span<byte> bytes = stackalloc byte[maxLength];
var count = 0;
while (count != data.Length && count < maxLength)
{
var b = data[count];
if (b == 0)
{
break;
}
bytes[count] = b;
count++;
}
bytes = bytes.Slice(0, count);
#if NETFRAMEWORK
return Encoding.UTF8.GetString(bytes.ToArray());
#else
return Encoding.UTF8.GetString(bytes);
#endif
}
} }
} }

View File

@ -53,7 +53,11 @@ namespace AssetStudio
count++; count++;
} }
bytes = bytes.Slice(0, count); bytes = bytes.Slice(0, count);
#if NETFRAMEWORK
return encoding?.GetString(bytes.ToArray()) ?? Encoding.UTF8.GetString(bytes.ToArray()); return encoding?.GetString(bytes.ToArray()) ?? Encoding.UTF8.GetString(bytes.ToArray());
#else
return encoding?.GetString(bytes) ?? Encoding.UTF8.GetString(bytes);
#endif
} }
private static string ReadUnicodeStringToNull(this BinaryReader reader, int maxLength) private static string ReadUnicodeStringToNull(this BinaryReader reader, int maxLength)

View File

@ -1,6 +1,5 @@
using System; using System;
using System.IO; using System.IO;
using static AssetStudio.EndianSpanReader;
namespace AssetStudio namespace AssetStudio
{ {
@ -14,6 +13,9 @@ namespace AssetStudio
private static readonly byte[] brotliMagic = { 0x62, 0x72, 0x6F, 0x74, 0x6C, 0x69 }; private static readonly byte[] brotliMagic = { 0x62, 0x72, 0x6F, 0x74, 0x6C, 0x69 };
private static readonly byte[] zipMagic = { 0x50, 0x4B, 0x03, 0x04 }; private static readonly byte[] zipMagic = { 0x50, 0x4B, 0x03, 0x04 };
private static readonly byte[] zipSpannedMagic = { 0x50, 0x4B, 0x07, 0x08 }; private static readonly byte[] zipSpannedMagic = { 0x50, 0x4B, 0x07, 0x08 };
private static readonly byte[] unityFsMagic = {0x55, 0x6E, 0x69, 0x74, 0x79, 0x46, 0x53};
private static readonly int headerBuffLen = 1152;
private static byte[] headerBuff = new byte[headerBuffLen];
public FileReader(string path) : this(path, File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { } public FileReader(string path) : this(path, File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { }
@ -26,14 +28,19 @@ namespace AssetStudio
private FileType CheckFileType() private FileType CheckFileType()
{ {
var signature = this.ReadStringToNull(20); var buff = headerBuff.AsSpan();
buff.Clear();
var dataLen = Read(headerBuff, 0, headerBuffLen);
Position = 0; Position = 0;
var signature = buff.ReadStringToNull(20);
switch (signature) switch (signature)
{ {
case "UnityWeb": case "UnityWeb":
case "UnityRaw": case "UnityRaw":
case "UnityArchive": case "UnityArchive":
case "UnityFS": case "UnityFS":
CheckBundleDataOffset(buff);
return FileType.BundleFile; return FileType.BundleFile;
case "UnityWebData1.0": case "UnityWebData1.0":
return FileType.WebFile; return FileType.WebFile;
@ -41,17 +48,15 @@ namespace AssetStudio
return FileType.WebFile; return FileType.WebFile;
default: default:
{ {
var buff = ReadBytes(40).AsSpan();
var magic = Span<byte>.Empty; var magic = Span<byte>.Empty;
Position = 0;
magic = buff.Length > 2 ? buff.Slice(0, 2) : magic; magic = dataLen > 2 ? buff.Slice(0, 2) : magic;
if (magic.SequenceEqual(gzipMagic)) if (magic.SequenceEqual(gzipMagic))
{ {
return FileType.GZipFile; return FileType.GZipFile;
} }
magic = buff.Length > 38 ? buff.Slice(32, 6) : magic; magic = dataLen > 38 ? buff.Slice(32, 6) : magic;
if (magic.SequenceEqual(brotliMagic)) if (magic.SequenceEqual(brotliMagic))
{ {
return FileType.BrotliFile; return FileType.BrotliFile;
@ -62,12 +67,17 @@ namespace AssetStudio
return FileType.AssetsFile; return FileType.AssetsFile;
} }
magic = buff.Length > 4 ? buff.Slice(0, 4): magic; magic = dataLen > 4 ? buff.Slice(0, 4): magic;
if (magic.SequenceEqual(zipMagic) || magic.SequenceEqual(zipSpannedMagic)) if (magic.SequenceEqual(zipMagic) || magic.SequenceEqual(zipSpannedMagic))
{ {
return FileType.ZipFile; return FileType.ZipFile;
} }
if (CheckBundleDataOffset(buff))
{
return FileType.BundleFile;
}
return FileType.ResourceFile; return FileType.ResourceFile;
} }
} }
@ -82,10 +92,10 @@ namespace AssetStudio
} }
var isBigEndian = Endian == EndianType.BigEndian; var isBigEndian = Endian == EndianType.BigEndian;
//var m_MetadataSize = SpanToUint32(buff, 0, isBigEndian); //var m_MetadataSize = buff.ReadUInt32(0, isBigEndian);
long m_FileSize = SpanToUint32(buff, 4, isBigEndian); long m_FileSize = buff.ReadUInt32(4, isBigEndian);
var m_Version = SpanToUint32(buff, 8, isBigEndian); var m_Version = buff.ReadUInt32(8, isBigEndian);
long m_DataOffset = SpanToUint32(buff, 12, isBigEndian); long m_DataOffset = buff.ReadUInt32(12, isBigEndian);
//var m_Endianess = buff[16]; //var m_Endianess = buff[16];
//var m_Reserved = buff.Slice(17, 3); //var m_Reserved = buff.Slice(17, 3);
if (m_Version >= 22) if (m_Version >= 22)
@ -94,15 +104,44 @@ namespace AssetStudio
{ {
return false; return false;
} }
//m_MetadataSize = SpanToUint32(buff, 20, isBigEndian); //m_MetadataSize = buff.ReadUInt32(20, isBigEndian);
m_FileSize = SpanToInt64(buff, 24, isBigEndian); m_FileSize = buff.ReadInt64(24, isBigEndian);
m_DataOffset = SpanToInt64(buff, 32, isBigEndian); m_DataOffset = buff.ReadInt64(32, isBigEndian);
} }
if (m_FileSize != fileSize || m_DataOffset > fileSize) if (m_FileSize != fileSize || m_DataOffset > fileSize)
{ {
return false; return false;
} }
return true;
}
private bool CheckBundleDataOffset(ReadOnlySpan<byte> buff)
{
var lastOffset = buff.LastIndexOf(unityFsMagic);
if (lastOffset <= 0)
return false;
var firstOffset = buff.IndexOf(unityFsMagic);
if (firstOffset == lastOffset || lastOffset - firstOffset < 200)
{
Position = lastOffset;
return true;
}
Position = firstOffset;
_ = this.ReadStringToNull();
_ = this.ReadUInt32();
_ = this.ReadStringToNull();
_ = this.ReadStringToNull();
var bundleSize = this.ReadInt64();
if (bundleSize > 200 && firstOffset + bundleSize < lastOffset)
{
Position = firstOffset;
return true;
}
Position = lastOffset;
return true; return true;
} }
} }

View File

@ -0,0 +1,92 @@
using System.IO;
namespace AssetStudio
{
public class OffsetStream : Stream
{
private readonly Stream _baseStream;
private long _offset;
public override bool CanRead => _baseStream.CanRead;
public override bool CanSeek => _baseStream.CanSeek;
public override bool CanWrite => false;
public override long Length => _baseStream.Length - _offset;
public override long Position
{
get => _baseStream.Position - _offset;
set => Seek(value, SeekOrigin.Begin);
}
public long BasePosition => _baseStream.Position;
public long Offset
{
get => _offset;
set
{
if (value < 0 || value > _baseStream.Length)
{
throw new IOException($"{nameof(Offset)} is out of stream bound");
}
_offset = value;
Seek(0, SeekOrigin.Begin);
}
}
public OffsetStream(FileReader reader)
{
_baseStream = reader.BaseStream;
Offset = reader.Position;
}
public override void Flush() { }
public override int Read(byte[] buffer, int offset, int count)
{
return _baseStream.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
if (offset > _baseStream.Length)
{
throw new IOException("Unable to seek beyond stream bound");
}
switch (origin)
{
case SeekOrigin.Begin:
_baseStream.Seek(offset + _offset, SeekOrigin.Begin);
break;
case SeekOrigin.Current:
_baseStream.Seek(offset + Position, SeekOrigin.Begin);
break;
case SeekOrigin.End:
_baseStream.Seek(offset + _baseStream.Length, SeekOrigin.Begin);
break;
}
return Position;
}
public override void SetLength(long value)
{
throw new System.NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new System.NotImplementedException();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_baseStream.Dispose();
}
base.Dispose(disposing);
}
}
}