2013-05-09 11:13:16 -04:00

903 lines
32 KiB
C#

//
// Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net). Distributed under GPL version 2.
//
//
// This is the netcode for turning SOE protocol packets into EQ Application Packets
//
using System;
using System.Text;
using System.IO;
using System.Collections.Generic;
using zlib;
using MyUtils;
using EQExtractor2.Patches;
using EQExtractor2.OpCodes;
using EQApplicationLayer;
namespace EQPacket
{
public enum PacketDirection {ClientToServer, ServerToClient, Unknown };
public class EQApplicationPacket
{
public EQApplicationPacket(System.Net.IPAddress srcIp, System.Net.IPAddress dstIp, ushort srcPort, ushort dstPort, int OpCode, int BufferSize, byte[] Source, int Offset, PacketDirection Direction, DateTime PacketTime)
{
this.OpCode = OpCode;
this.Direction = Direction;
this.PacketTime = PacketTime;
if (BufferSize < 0)
{
//Log("OPCode " + OpCode.ToString("x") + ", Buffer Size " + BufferSize + " Buffer Size < 0 !");
return;
}
Buffer = new byte[BufferSize];
Array.Copy(Source, Offset, Buffer, 0, BufferSize);
}
public byte[] Buffer;
public int OpCode;
public PacketDirection Direction;
public DateTime PacketTime;
public bool Locked = false;
}
public class PacketManager
{
bool DEBUG = false;
bool DUMPPACKETS = false;
private void Debug(string Message)
{
if (DebugLogger != null)
DebugLogger(Message);
}
public bool ErrorsInStream = false;
const UInt32 OP_SessionRequest = 0x0001;
const UInt32 OP_SessionResponse = 0x0002;
const UInt32 OP_Combined = 0x0003;
const UInt32 OP_Packet = 0x0009;
const UInt32 OP_Fragment = 0x000d;
const UInt32 OP_OutOfOrderAck = 0x0011;
const UInt32 OP_Ack = 0x0015;
private bool PermaLocked = false;
private bool Identified = false;
private int[] FragmentSeq = {-1, -1};
private int[] FragmentedPacketSize = {0, 0};
private int[] FragmentedBytesCollected = {0, 0};
private byte[][] Fragments = new byte [2][];
private System.Net.IPAddress ServerIP;
private ushort ServerPort = 0;
private int ExpectedServerSEQ = 0;
private System.Net.IPAddress ClientIP;
private ushort ClientPort = 0;
private int ExpectedClientSEQ = 0;
private ushort CryptoFlag = 0;
StreamIdentifier Identifier = null;
public DateTime LastPacketTime = DateTime.Now;
public List<EQApplicationPacket> PacketList = new List<EQApplicationPacket>();
LogHandler Logger = null;
LogHandler DebugLogger = null;
public void SetLogHandler(LogHandler Logger)
{
this.Logger = Logger;
}
public void SetDebugLogHandler(LogHandler Logger)
{
this.DebugLogger = Logger;
DEBUG = (Logger != null);
DUMPPACKETS = (Logger != null);
}
void Log(string Message)
{
if(Logger != null)
Logger(Message);
}
public delegate IdentificationStatus StreamIdentifier(int OPCode, int Size, PacketDirection Direction);
public void SetVersionIdentifierMethod(StreamIdentifier Identifier)
{
this.Identifier = Identifier;
}
public void AddPacket(EQApplicationPacket Packet)
{
#pragma warning disable 0162
if (DUMPPACKETS)
{
Debug("[OPCode: 0x" + Packet.OpCode.ToString("x") + "] [Size: " + Packet.Buffer.Length + "]");
Debug(Utils.HexDump(Packet.Buffer));
}
#pragma warning restore 0162
Packet.Locked = PermaLocked;
PacketList.Add(Packet);
}
private struct CacheEntry
{
public int Seq;
public PacketDirection Direction;
public byte[] Payload;
public DateTime PacketTime;
public bool SubPacket;
public CacheEntry(int inSeq, PacketDirection inDirection, byte[] inPayload, DateTime inPacketTime, bool inSubPacket)
{
Seq = inSeq;
Direction = inDirection;
Payload = inPayload;
PacketTime = inPacketTime;
SubPacket = inSubPacket;
}
}
private List<CacheEntry> Cache = new List<CacheEntry>();
private void AddToCache(int Seq, PacketDirection Direction, byte[] Payload, DateTime PacketTime, bool SubPacket)
{
if(DEBUG)
Debug("Adding packet with Seq " + Seq + " to cache.");
foreach(CacheEntry Existing in Cache)
{
if((Existing.Direction == Direction) && (Existing.Seq == Seq))
return;
}
CacheEntry ce = new CacheEntry(Seq, Direction, Payload, PacketTime, SubPacket);
Cache.Add(ce);
}
private bool CacheEmpty()
{
return (Cache.Count == 0);
}
public void DumpCache()
{
Log("Cache has " + Cache.Count + " elements.");
for (int i = 0; i < Cache.Count; ++i)
{
string Direction;
if (Cache[i].Direction == PacketDirection.ClientToServer)
Direction = "Client to Server";
else if (Cache[i].Direction == PacketDirection.ServerToClient)
Direction = "Server to Client";
else
Direction = "Unknown";
Log("Cache Entry Seq " + Cache[i].Seq + ", Direction:" + Direction);
}
}
private int FindCacheEntry(int Seq, PacketDirection Direction)
{
for(int i = 0; i < Cache.Count; ++i)
{
CacheEntry ce = Cache[i];
if ((ce.Seq == Seq) && (ce.Direction == Direction))
return i;
}
return -1;
}
private void ProcessCache()
{
int CacheElement;
CacheElement = FindCacheEntry(GetExpectedSeq(PacketDirection.ServerToClient), PacketDirection.ServerToClient);
while ( CacheElement >= 0)
{
if (DEBUG)
Debug("Processing packet with seq " + Cache[CacheElement].Seq + " from cache.");
ProcessPacket(ServerIP, ClientIP, ServerPort, ClientPort, Cache[CacheElement].Payload, Cache[CacheElement].PacketTime, Cache[CacheElement].SubPacket, true);
Cache.RemoveRange(CacheElement, 1);
if (DEBUG)
Debug("Cache now has " + Cache.Count + " elements.");
CacheElement = FindCacheEntry(GetExpectedSeq(PacketDirection.ServerToClient), PacketDirection.ServerToClient);
}
CacheElement = FindCacheEntry(GetExpectedSeq(PacketDirection.ClientToServer), PacketDirection.ClientToServer);
while (CacheElement >= 0)
{
ProcessPacket(ClientIP, ServerIP, ClientPort, ServerPort, Cache[CacheElement].Payload, Cache[CacheElement].PacketTime, Cache[CacheElement].SubPacket, true);
Cache.RemoveRange(CacheElement, 1);
CacheElement = FindCacheEntry(GetExpectedSeq(PacketDirection.ServerToClient), PacketDirection.ServerToClient);
}
}
private PacketDirection GetDirection(System.Net.IPAddress srcIp, System.Net.IPAddress dstIp, ushort srcPort, ushort dstPort)
{
if ((srcIp.Equals(ServerIP)) && (srcPort == ServerPort) && (dstIp.Equals(ClientIP)) && (dstPort == ClientPort))
return PacketDirection.ServerToClient;
if ((srcIp.Equals(ClientIP)) && (srcPort == ClientPort) && (dstIp.Equals(ServerIP)) && (dstPort == ServerPort))
return PacketDirection.ClientToServer;
return PacketDirection.Unknown;
}
private int GetExpectedSeq(PacketDirection Direction)
{
if (Direction == PacketDirection.ClientToServer)
return ExpectedClientSEQ;
else if (Direction == PacketDirection.ServerToClient)
return ExpectedServerSEQ;
return 0;
}
private void AdvanceSeq(PacketDirection Direction)
{
if (Direction == PacketDirection.ClientToServer)
++ExpectedClientSEQ;
else if (Direction == PacketDirection.ServerToClient)
++ExpectedServerSEQ;
}
public void ProcessPacket(System.Net.IPAddress srcIp, System.Net.IPAddress dstIp, ushort srcPort, ushort dstPort, byte[] Payload, DateTime PacketTime, bool SubPacket, bool Cached)
{
byte Flags = 0x00;
UInt32 OpCode = (UInt32)(Payload[0] * 256 + Payload[1]);
PacketDirection Direction = GetDirection(srcIp, dstIp, srcPort, dstPort);
if ((Direction == PacketDirection.Unknown) && (OpCode != OP_SessionRequest))
return;
// Check if this is a UCS connection and if so, skip packets until we see another OP_SessionRequest
if (((CryptoFlag & 4) > 0) && (OpCode != OP_SessionRequest))
return;
if (Direction == PacketDirection.ClientToServer)
Debug("Client -> Server");
else
Debug("Server -> Client");
Debug("Delta: " + (PacketTime - LastPacketTime));
TimeSpan Elapsed = PacketTime - LastPacketTime;
//if (Elapsed.Seconds > 1)
// Debug("*** More than 1 second elapsed ***");
LastPacketTime = PacketTime;
switch (OpCode)
{
case OP_SessionRequest:
{
if (PermaLocked)
break;
ClientIP = srcIp;
ClientPort = srcPort;
ServerIP = dstIp;
ServerPort = dstPort;
Log("-- Locked onto EQ Stream. Client IP " + srcIp + ":" + srcPort + " Server IP " + dstIp + ":" + dstPort);
ExpectedClientSEQ = 0;
ExpectedServerSEQ = 0;
CryptoFlag = 0;
break;
}
case OP_SessionResponse:
{
CryptoFlag = (ushort)(Payload[11] + (Payload[12] * 256));
Log("Stream Crypto Flag is 0x" + CryptoFlag.ToString("x4"));
break;
}
case OP_Combined:
{
if (DEBUG)
{
Debug("OP_Combined, Direction " + (Direction == PacketDirection.ClientToServer ? "Client->Server" : "Server->Client"));
Debug(Utils.HexDump(Payload));
}
byte[] Uncompressed;
if (!SubPacket && (Payload[2] == 0x5a))
{
Uncompressed = DecompressPacket(Payload, 3);
}
else if (!SubPacket && (Payload[2] == 0xa5))
{
Uncompressed = new byte[Payload.Length - 3];
Array.Copy(Payload, 3, Uncompressed, 0, Payload.Length - 3);
}
else
{
Uncompressed = new byte[Payload.Length - 2];
Array.Copy(Payload, 2, Uncompressed, 0, Payload.Length - 2);
}
int BufferPosition = 0;
while (BufferPosition < Uncompressed.Length - 2)
{
int SubPacketSize = Uncompressed[BufferPosition++];
byte[] NewPacket = new byte[SubPacketSize];
Array.Copy(Uncompressed, BufferPosition, NewPacket, 0, SubPacketSize);
BufferPosition += SubPacketSize;
ProcessPacket(srcIp, dstIp, srcPort, dstPort, NewPacket, PacketTime, true, Cached);
}
break;
}
case OP_Packet:
{
if (DEBUG)
Debug("OP_Packet, Subpacket = "+ SubPacket);
byte[] Uncompressed;
if (!SubPacket && (Payload[2] == 0x5a))
{
if (DEBUG)
Debug("Compressed");
Uncompressed = DecompressPacket(Payload, 3);
}
else if (!SubPacket && (Payload[2] == 0xa5))
{
if (DEBUG)
Debug("0xa5");
Uncompressed = new byte[Payload.Length - 5];
Array.Copy(Payload, 3, Uncompressed, 0, Payload.Length - 5);
}
else
{
if (DEBUG)
Debug("Uncompressed");
Uncompressed = new byte[Payload.Length - 2];
Array.Copy(Payload, 2, Uncompressed, 0, Payload.Length - 2);
}
if (DEBUG)
{
//Debug("Raw payload is:");
//Debug(Utils.HexDump(Payload));
//Debug("Uncompressed data is:");
//Debug(Utils.HexDump(Uncompressed));
}
int Seq = Uncompressed[0] * 256 + Uncompressed[1];
Debug("Seq is " + Seq + " Expected " + GetExpectedSeq(Direction));
if (Seq != GetExpectedSeq(Direction))
{
if (Seq > GetExpectedSeq(Direction))
{
if ((Seq - GetExpectedSeq(Direction) < 1000))
AddToCache(Seq, Direction, Payload, PacketTime, SubPacket);
else
{
Log("Giving up on seeing expected fragment.");
ErrorsInStream = true;
FragmentSeq[(int)Direction] = -1;
AdvanceSeq(Direction);
}
}
break;
}
else
AdvanceSeq(Direction);
bool Multi = ((Uncompressed[2] == 0x00) && (Uncompressed[3] == 0x19));
if (Multi)
{
int BufferPosition = 4;
while (BufferPosition < (Uncompressed.Length - 2))
{
int Size = 0;
if (Uncompressed[BufferPosition] == 0xff)
{
if (Uncompressed[BufferPosition + 1] == 0x01)
Size = 256 + Uncompressed[BufferPosition + 2];
else
Size = Uncompressed[BufferPosition + 2];
BufferPosition += 3;
}
else
Size = Uncompressed[BufferPosition++];
int OpCodeBytes = 2;
int AppOpCode = Uncompressed[BufferPosition++];
if (AppOpCode == 0)
{
++BufferPosition;
OpCodeBytes = 3;
}
AppOpCode += (Uncompressed[BufferPosition++] * 256);
ProcessAppPacket(srcIp, dstIp, srcPort, dstPort, AppOpCode, Size - OpCodeBytes, Uncompressed, BufferPosition, Direction, PacketTime);
BufferPosition = BufferPosition + (Size - OpCodeBytes);
}
}
else
{
int BufferPosition = 2;
int OpCodeBytes = 2;
int AppOpCode = Uncompressed[BufferPosition++];
if (AppOpCode == 0)
{
++BufferPosition;
OpCodeBytes = 3;
}
AppOpCode += (Uncompressed[BufferPosition++] * 256);
ProcessAppPacket(srcIp, dstIp, srcPort, dstPort, AppOpCode, Uncompressed.Length - (2 + OpCodeBytes), Uncompressed, BufferPosition, Direction, PacketTime);
}
break;
}
case OP_Fragment:
{
if (DEBUG)
{
Debug("OP_Fragment");
Debug("Raw Data");
Debug(Utils.HexDump(Payload));
}
byte[] Uncompressed;
if (!SubPacket && (Payload[2] == 0x5a))
{
if (DEBUG)
Debug("Compressed");
Uncompressed = DecompressPacket(Payload, 3);
}
else if (!SubPacket && (Payload[2] == 0xa5))
{
if (DEBUG)
Debug("0xa5");
Uncompressed = new byte[Payload.Length - 5];
Array.Copy(Payload, 3, Uncompressed, 0, Payload.Length - 5);
}
else
{
Uncompressed = new byte[Payload.Length - 2];
Array.Copy(Payload, 2, Uncompressed, 0, Payload.Length - 2);
}
if (DEBUG)
{
Debug("Uncompressed data.");
Debug(Utils.HexDump(Uncompressed));
}
if (FragmentSeq[(int)Direction] == -1)
{
if (DEBUG)
Debug("First fragment.");
FragmentSeq[(int)Direction] = (Uncompressed[0] * 256) + Uncompressed[1];
if (DEBUG)
Debug("FragmentSeq is " + FragmentSeq[(int)Direction] + " Expecting " + GetExpectedSeq(Direction));
if (FragmentSeq[(int)Direction] != GetExpectedSeq(Direction))
{
if (FragmentSeq[(int)Direction] > GetExpectedSeq(Direction))
{
if((FragmentSeq[(int)Direction] - GetExpectedSeq(Direction)) < 1000)
AddToCache(FragmentSeq[(int)Direction], Direction, Payload, PacketTime, SubPacket);
else
{
Log("Giving up on seeing expected fragment.");
ErrorsInStream = true;
FragmentSeq[(int)Direction] = -1;
AdvanceSeq(Direction);
}
}
FragmentSeq[(int)Direction] = -1;
break;
}
else
AdvanceSeq(Direction);
FragmentedPacketSize[(int)Direction] = Uncompressed[2] * 0x1000000 + Uncompressed[3] * 0x10000 + Uncompressed[4] * 0x100 + Uncompressed[5];
if((FragmentedPacketSize[(int)Direction] == 0) || (FragmentedPacketSize[(int)Direction] > 1000000))
{
if (DEBUG)
Debug("Got a fragmented packet of size " + FragmentedPacketSize[(int)Direction] + ". Discarding.");
ErrorsInStream = true;
FragmentSeq[(int)Direction] = -1;
break;
}
FragmentedBytesCollected[(int)Direction] = Uncompressed.Length - 6;
if (DEBUG)
Debug("Total packet size is " + FragmentedPacketSize[(int)Direction]);
if((Uncompressed.Length - 6) > FragmentedPacketSize[(int)Direction])
{
Log("Mangled fragment.");
ErrorsInStream = true;
FragmentSeq[(int)Direction] = -1;
break;
}
Fragments[(int)Direction] = new byte[FragmentedPacketSize[(int)Direction]];
if (DEBUG)
Debug("Copying " + (Uncompressed.Length - 6) + " bytes to Fragments starting at index 0");
Array.Copy(Uncompressed, 6, Fragments[(int)Direction], 0, Uncompressed.Length - 6);
}
else
{
int LastSeq = FragmentSeq[(int)Direction];
FragmentSeq[(int)Direction] = (Uncompressed[0] * 256) + Uncompressed[1];
if (DEBUG)
Debug("FragmentSeq is " + FragmentSeq[(int)Direction] + ". Expecting " + GetExpectedSeq(Direction));
if (FragmentSeq[(int)Direction] != GetExpectedSeq(Direction))
{
if (FragmentSeq[(int)Direction] > GetExpectedSeq(Direction))
{
if ((FragmentSeq[(int)Direction] - GetExpectedSeq(Direction)) < 1000)
AddToCache(FragmentSeq[(int)Direction], Direction, Payload, PacketTime, SubPacket);
else
{
Log("Giving up on seeing expected fragment.");
ErrorsInStream = true;
AdvanceSeq(Direction);
FragmentSeq[(int)Direction] = -1;
}
}
break;
}
else
AdvanceSeq(Direction);
if (DEBUG)
Debug("Copying " + (Uncompressed.Length - 2) + " bytes from Uncompressed to Fragments starting at " + FragmentedBytesCollected[(int)Direction]);
if ((Uncompressed.Length - 2) > (Fragments[(int)Direction].Length - FragmentedBytesCollected[(int)Direction]))
{
Log("Mangled fragment. Discarding.");
ErrorsInStream = true;
FragmentSeq[(int)Direction] = -1;
break;
}
Array.Copy(Uncompressed, 2, Fragments[(int)Direction], FragmentedBytesCollected[(int)Direction], Uncompressed.Length - 2);
FragmentedBytesCollected[(int)Direction] += Uncompressed.Length - 2;
if (FragmentedBytesCollected[(int)Direction] == FragmentedPacketSize[(int)Direction])
{
if (DEBUG)
Debug("Got whole packet.");
if ((Fragments[(int)Direction][0] == 0x00) && (Fragments[1][(int)Direction] == 0x019))
{
if (DEBUG)
Debug("Multi packet.");
int BufferPosition = 2;
while (BufferPosition < Fragments[(int)Direction].Length)
{
int Size = 0;
if (Fragments[(int)Direction][BufferPosition] == 0xff)
{
if (Fragments[(int)Direction][BufferPosition + 1] == 0x01)
Size = 256 + Fragments[(int)Direction][BufferPosition + 2];
else
Size = Fragments[(int)Direction][BufferPosition + 2];
BufferPosition += 3;
}
else
Size = Fragments[(int)Direction][BufferPosition++];
int OpCodeBytes = 2;
int AppOpCode = Fragments[(int)Direction][BufferPosition++];
if (AppOpCode == 0)
{
++BufferPosition;
OpCodeBytes = 3;
}
AppOpCode += (Fragments[(int)Direction][BufferPosition++] * 256);
ProcessAppPacket(srcIp, dstIp, srcPort, dstPort, AppOpCode, Size - OpCodeBytes, Fragments[(int)Direction], BufferPosition, Direction, PacketTime);
BufferPosition = BufferPosition + (Size - OpCodeBytes);
}
}
else
{
int BufferPosition = 0;
int OpCodeBytes = 2;
int AppOpCode = Fragments[(int)Direction][BufferPosition++];
if (AppOpCode == 0)
{
++BufferPosition;
OpCodeBytes = 3;
}
AppOpCode += (Fragments[(int)Direction][BufferPosition++] * 256);
byte[] NewPacket = new byte[Fragments[(int)Direction].Length - OpCodeBytes];
Array.Copy(Fragments[(int)Direction], BufferPosition, NewPacket, 0, Fragments[(int)Direction].Length - OpCodeBytes);
ProcessAppPacket(srcIp, dstIp, srcPort, dstPort, AppOpCode, NewPacket.Length, NewPacket, 0, Direction, PacketTime);
}
if (DEBUG)
Debug("Reseting FragmentSeq to -1");
FragmentSeq[(int)Direction] = -1;
}
}
break;
}
case OP_OutOfOrderAck:
{
int Seq = Payload[2] * 256 + Payload[3];
if (DEBUG)
Debug("OP_OutOfOrder " + Seq);
break;
}
case OP_Ack:
{
int Seq;
if(Payload.Length > 4)
Seq = Payload[3] * 256 + Payload[4];
else
Seq = Payload[2] * 256 + Payload[3];
string DirectionString;
if(Direction == PacketDirection.ClientToServer)
DirectionString = "Client to Server";
else
DirectionString = "Server to Client";
if (DEBUG)
{
Debug("OP_Ack, Seq " + Seq + " " + DirectionString);
Debug(Utils.HexDump(Payload));
}
break;
}
default:
if (OpCode > 0xff)
{
if (DEBUG)
{
Debug("Unencapsulated EQ Application OpCode. Subpacket is " + SubPacket.ToString());
Debug("--- Raw payload ---");
Debug(Utils.HexDump(Payload));
Debug("-------------------");
}
int AppOpCode;
byte[] NewPacket;
if (SubPacket)
{
AppOpCode = Payload[1] * 256 + Payload[0];
NewPacket = new byte[Payload.Length - 2];
Array.Copy(Payload, 2, NewPacket, 0, Payload.Length - 2);
}
else
{
// This packet has a flag byte between the first and second bytes of the opcode, and also a CRC
Flags = Payload[1];
if (Flags == 0x5a)
{
if(DEBUG)
Debug("Compressed unencapsulated packet.");
byte[] NewPayload = new byte[Payload.Length - 4];
Array.Copy(Payload, 2, NewPayload, 0, Payload.Length - 4);
byte[] Uncompressed = DecompressPacket(NewPayload, 0);
if (DEBUG)
{
Debug("Uncompressed Payload:");
Debug(Utils.HexDump(Uncompressed));
}
// Opcode is first byte of compressed payload and first byte of uncompressed data
AppOpCode = Uncompressed[0] * 256 + Payload[0];
NewPacket = new byte[Uncompressed.Length - 1];
Array.Copy(Uncompressed, 1, NewPacket, 0, Uncompressed.Length - 1);
}
else
{
AppOpCode = Payload[2] * 256 + Payload[0];
NewPacket = new byte[Payload.Length - 5];
Array.Copy(Payload, 3, NewPacket, 0, Payload.Length - 5);
}
}
ProcessAppPacket(srcIp, dstIp, srcPort, dstPort, AppOpCode, NewPacket.Length, NewPacket, 0, Direction, PacketTime);
}
else
{
if (DEBUG)
{
Debug("OP_Unknown (" + OpCode.ToString("x") + ")");
Debug(Utils.HexDump(Payload));
}
}
break;
}
if (!Cached && !CacheEmpty())
ProcessCache();
}
public void ProcessAppPacket(System.Net.IPAddress srcIp, System.Net.IPAddress dstIp, ushort srcPort, ushort dstPort, int InOpCode, int BufferSize, byte[] Source, int Offset, PacketDirection Direction, DateTime PacketTime)
{
EQApplicationPacket app = new EQApplicationPacket(srcIp, dstIp, srcPort, dstPort, InOpCode, BufferSize, Source, Offset, Direction, PacketTime);
if (!Identified)
{
IdentificationStatus TempStatus = Identifier(InOpCode, BufferSize, Direction);
if (!PermaLocked && (TempStatus > IdentificationStatus.No))
{
Log("-- Permalocking to this stream.");
PermaLocked = true;
}
Identified = (TempStatus == IdentificationStatus.Yes);
}
AddPacket(app);
}
public byte[] DecompressPacket(byte[] Payload, int Offset)
{
MemoryStream ms = new MemoryStream(Payload.GetUpperBound(0) - Offset);
ms.Write(Payload, Offset, Payload.GetUpperBound(0) - Offset);
ms.Seek(0, System.IO.SeekOrigin.Begin);
ZInputStream zs = new ZInputStream(ms);
int UncompressedSize;
byte[] Uncompressed = new byte[4096];
try
{
UncompressedSize = zs.read(Uncompressed, 0, 4096);
}
catch
{
if(DEBUG)
Debug("DECOMPRESSION FAILURE");
Array.Copy(Payload, Offset - 1, Uncompressed, 0, Payload.Length - (Offset - 1));
UncompressedSize = Payload.Length - (Offset - 1);
}
zs.Close();
zs.Dispose();
ms.Close();
ms.Dispose();
Array.Resize(ref Uncompressed, UncompressedSize);
return Uncompressed;
}
}
}