Merge pull request 'bugfix/decompresing' (#27) from bugfix/decompresing into develop
Reviewed-on: https://git.brew.monster/Unity/perfect-world-unity/pulls/27
This commit is contained in:
@@ -14,6 +14,7 @@ namespace CSNetwork
|
||||
private TcpClient? _client;
|
||||
private NetworkStream? _stream;
|
||||
private readonly Octets _receiveOctets; // Underlying buffer for receiving
|
||||
private readonly Octets _decryptedOctets;
|
||||
private readonly OctetsStream _receiveBufferStream; // Stream wrapper (less used now)
|
||||
private BaseSecurity? _inputSecurity = null; // Use abstract Security class
|
||||
private BaseSecurity? _outputSecurity = null; // Use abstract Security class
|
||||
@@ -42,6 +43,7 @@ namespace CSNetwork
|
||||
{
|
||||
// Removed TcpClient initialization here, do it in ConnectAsync
|
||||
_receiveOctets = new Octets(8192); // Initial buffer size
|
||||
_decryptedOctets = new Octets(8192); // initial the decrypted buffer
|
||||
_receiveBufferStream = new OctetsStream(_receiveOctets); // Keep for reference maybe?
|
||||
// Initialize security using the factory
|
||||
_inputSecurity = BaseSecurity.Create(SecurityType.NULLSECURITY);
|
||||
@@ -298,15 +300,22 @@ namespace CSNetwork
|
||||
try
|
||||
{
|
||||
if (token.IsCancellationRequested) break;
|
||||
// Read data from client-server stream into the receive buffer
|
||||
bytesRead = await stream.ReadAsync(
|
||||
_receiveOctets.RawBuffer,
|
||||
currentBufferLength,
|
||||
_receiveOctets.Capacity - currentBufferLength,
|
||||
0,
|
||||
_receiveOctets.Capacity,
|
||||
token
|
||||
);
|
||||
|
||||
|
||||
// _logger.Log(LogType.Info, $"ProcessReceivedData:: Buffer remaining data size: {currentBufferLength} -- Raw first byte: {_receiveOctets.RawBuffer[0]}");
|
||||
|
||||
// if (bytesRead > 0)
|
||||
// {
|
||||
// _logger.Log(LogType.Info, $"ProcessReceivedData:: Last Byte: {_receiveOctets.RawBuffer[currentBufferLength + bytesRead - 1]}");
|
||||
// if (_previousLength > 0)
|
||||
// _logger.Log(LogType.Info, $"ProcessReceivedData:: Buffer remaining data size from {_previousLength} to {currentBufferLength + bytesRead} --- Last byte: {_receiveOctets.RawBuffer[_previousLength - 1]}");
|
||||
// }
|
||||
//
|
||||
// _previousLength = currentBufferLength + bytesRead; // cache to check if the buffer is growing
|
||||
}
|
||||
catch (IOException ex)
|
||||
when (ex.InnerException is SocketException se
|
||||
@@ -336,14 +345,12 @@ namespace CSNetwork
|
||||
}
|
||||
|
||||
currentBufferLength += bytesRead;
|
||||
_receiveOctets.SetSize(currentBufferLength);
|
||||
_receiveOctets.SetSize(bytesRead);
|
||||
|
||||
_logger.Log(LogType.Info, $"BF Process Buffer:: Read {bytesRead} bytes -- Total size: {currentBufferLength}");
|
||||
// Process the data currently in the buffer
|
||||
ProcessBuffer();
|
||||
_logger.Log(LogType.Info, $"AF Process Buffer:: Read {bytesRead} bytes -- Total size: {currentBufferLength}");
|
||||
// After processing, the buffer might have been compacted, update length
|
||||
currentBufferLength = _receiveOctets.Length;
|
||||
// currentBufferLength = _receiveOctets.Length;
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
@@ -376,7 +383,7 @@ namespace CSNetwork
|
||||
bool securityApplied =
|
||||
currentIsec != null && currentIsec.GetType() != typeof(NullSecurity);
|
||||
Octets dataToProcess; // This will hold the data block we decode from
|
||||
int originalBlockLength = _receiveOctets.Length; // Length before security
|
||||
int originalBlockLength = _decryptedOctets.Length; // Length before security
|
||||
int bytesConsumedFromOriginal = 0; // How many bytes of _receiveOctets are processed
|
||||
|
||||
// 1. Apply Input Security (if active)
|
||||
@@ -384,22 +391,22 @@ namespace CSNetwork
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create a temporary Octets with the current buffer content
|
||||
// Create a temporary Octets with the current raw buffer content
|
||||
Octets currentData = new Octets(
|
||||
_receiveOctets.RawBuffer,
|
||||
0,
|
||||
originalBlockLength
|
||||
_receiveOctets.Size
|
||||
);
|
||||
_logger.Log(LogType.Info, $"ProcessBuffer:: raw first byte {currentData.RawBuffer[0]} - Length: {currentData.Length}");
|
||||
// Update returns a NEW Octets object with processed data
|
||||
dataToProcess = currentIsec!.Update(currentData);
|
||||
_logger.Log(LogType.Info, $"ProcessBuffer:: decompressed first byte {dataToProcess.RawBuffer[0]} - Length: {dataToProcess.Length}");
|
||||
_decryptedOctets.Insert(_decryptedOctets.Size, dataToProcess.ByteArray);
|
||||
dataToProcess = _decryptedOctets;
|
||||
// _logger.Log(LogType.Info, $"Input security applied. Original size: {originalBlockLength}, Processed size: {dataToProcess.Length}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Log(LogType.Info,
|
||||
$"Input security update error: {ex.Message} - Clearing receive buffer."
|
||||
$"Input security update error: {ex.Message} - {ex.StackTrace} - Clearing receive buffer."
|
||||
);
|
||||
OnErrorOccurred($"Input security error: {ex.Message}");
|
||||
_receiveOctets.SetSize(0);
|
||||
@@ -466,7 +473,7 @@ namespace CSNetwork
|
||||
{
|
||||
// If security was applied and we processed/consumed *any* bytes from the processed stream,
|
||||
// assume the whole original block was consumed.
|
||||
bytesConsumedFromOriginal = processedAnyProtocols ? originalBlockLength : 0;
|
||||
bytesConsumedFromOriginal = totalConsumedFromProcessedStream;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -476,14 +483,15 @@ namespace CSNetwork
|
||||
}
|
||||
|
||||
EndProcessing:
|
||||
_receiveOctets.SetSize(0);
|
||||
// 4. Compact the *original* _receiveOctets buffer
|
||||
CompactOriginalBuffer(bytesConsumedFromOriginal, originalBlockLength);
|
||||
CompactDecryptedBuffer(bytesConsumedFromOriginal, originalBlockLength);
|
||||
}
|
||||
|
||||
// *** Helper to compact the original receive buffer ***
|
||||
private void CompactOriginalBuffer(int bytesToConsume, int originalLength)
|
||||
private void CompactDecryptedBuffer(int bytesToConsume, int originalLength)
|
||||
{
|
||||
if (bytesToConsume <= 0 || _receiveOctets == null) // Add null check
|
||||
if (bytesToConsume <= 0 || _decryptedOctets == null) // Add null check
|
||||
{
|
||||
return; // Nothing to consume/compact
|
||||
}
|
||||
@@ -496,21 +504,20 @@ namespace CSNetwork
|
||||
int remaining = originalLength - bytesToConsume;
|
||||
// _logger.Log(LogType.Info, $"Compacting original buffer: Consumed {bytesToConsume}, Moving {remaining} bytes from pos {bytesToConsume}");
|
||||
Buffer.BlockCopy(
|
||||
_receiveOctets.RawBuffer,
|
||||
_decryptedOctets.RawBuffer,
|
||||
bytesToConsume,
|
||||
_receiveOctets.RawBuffer,
|
||||
_decryptedOctets.RawBuffer,
|
||||
0,
|
||||
remaining
|
||||
);
|
||||
_receiveOctets.SetSize(remaining);
|
||||
_decryptedOctets.SetSize(remaining);
|
||||
}
|
||||
else // Consumed all
|
||||
{
|
||||
// All data processed or skipped, clear buffer
|
||||
// _logger.Log(LogType.Info, $"Clearing original buffer: Consumed {bytesToConsume} >= Original {originalLength}");
|
||||
_receiveOctets.SetSize(0);
|
||||
_decryptedOctets.SetSize(0);
|
||||
}
|
||||
_receiveBufferStream.Position = _receiveOctets.Length; // Reset stream pos just in case
|
||||
}
|
||||
|
||||
// Helper to raise ErrorOccurred event
|
||||
|
||||
@@ -80,8 +80,7 @@ namespace CSNetwork.Security
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
_index1++; // Note: byte overflows wrap around from 255 to 0 automatically
|
||||
_index2 = (byte)(_index2 + _perm[_index1]);
|
||||
_index2 += _perm[++_index1];
|
||||
|
||||
// Swap perm[index1] and perm[index2]
|
||||
byte temp = _perm[_index1];
|
||||
@@ -89,9 +88,8 @@ namespace CSNetwork.Security
|
||||
_perm[_index2] = temp;
|
||||
|
||||
byte j = (byte)(_perm[_index1] + _perm[_index2]);
|
||||
byte keystreamByte = _perm[j];
|
||||
|
||||
buffer[offset + i] ^= keystreamByte;
|
||||
buffer[offset + i] ^= _perm[j];
|
||||
}
|
||||
|
||||
return data; // Return the modified Octets
|
||||
|
||||
@@ -45,8 +45,6 @@ namespace CSNetwork.Security
|
||||
{
|
||||
if (data == null || data.Length == 0)
|
||||
{
|
||||
_logger.Log(LogType.Debug,$"HoangDev: AF _arcFour data{data.RawBuffer[0]} - Length: {data.Length}");
|
||||
|
||||
return new Octets(); // Return empty if input is empty
|
||||
}
|
||||
// 1. Decrypt using ARCFour
|
||||
@@ -57,8 +55,8 @@ namespace CSNetwork.Security
|
||||
// or just to be safe. Ensure _arcFour.Update returns a *new* Octets.
|
||||
// *** If ARCFourSecurity.Update modified the input Octets in-place, this would be wrong. ***
|
||||
// *** Assuming ARCFourSecurity.Update follows the abstract Security pattern and returns new Octets ***
|
||||
UnityEngine.Debug.Log($"ENCRYPTED: {data.RawBuffer[0]}");
|
||||
decryptedData = _arcFour.Update(data);
|
||||
_logger.Log(LogType.Debug,$"HoangDev: AF _arcFour data{decryptedData.RawBuffer[0]} - Length: {decryptedData.Length}");
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -78,8 +76,8 @@ namespace CSNetwork.Security
|
||||
Octets decompressedData;
|
||||
try
|
||||
{
|
||||
UnityEngine.Debug.Log($"DECRYPTED: {decryptedData.RawBuffer[0]}");
|
||||
decompressedData = decompressor.Update(decryptedData);
|
||||
_logger.Log(LogType.Debug, $"Decompressed {decryptedData.Length} bytes to {decompressedData.Length} bytes. Decompressed Data: {decompressedData.ToString()}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -10,43 +10,43 @@ namespace CSNetwork.Security
|
||||
private const int MPPC_HIST_LEN = 8192;
|
||||
|
||||
private byte[] history = new byte[MPPC_HIST_LEN];
|
||||
private int historyPos;
|
||||
private uint bitPos;
|
||||
private uint adjust_bitPos;
|
||||
private uint bitsProcessed;
|
||||
private uint totalBits;
|
||||
private int histptr;
|
||||
private uint l;
|
||||
private uint adjust_l;
|
||||
private uint blen;
|
||||
private uint blen_total;
|
||||
private List<byte> legacyInput = new List<byte>();
|
||||
private int readPos;
|
||||
private int adjust_readPos;
|
||||
private int rptr;
|
||||
private int adjust_rptr;
|
||||
|
||||
public Decompress()
|
||||
{
|
||||
historyPos = 0;
|
||||
bitPos = 0;
|
||||
histptr = 0;
|
||||
l = 0;
|
||||
}
|
||||
|
||||
public Decompress(Decompress source)
|
||||
{
|
||||
Array.Copy(source.history, history, MPPC_HIST_LEN);
|
||||
historyPos = source.historyPos;
|
||||
bitPos = source.bitPos;
|
||||
adjust_bitPos = source.adjust_bitPos;
|
||||
bitsProcessed = source.bitsProcessed;
|
||||
totalBits = source.totalBits;
|
||||
histptr = source.histptr;
|
||||
l = source.l;
|
||||
adjust_l = source.adjust_l;
|
||||
blen = source.blen;
|
||||
blen_total = source.blen_total;
|
||||
legacyInput = new List<byte>(source.legacyInput);
|
||||
readPos = source.readPos;
|
||||
adjust_readPos = source.adjust_readPos;
|
||||
rptr = source.rptr;
|
||||
adjust_rptr = source.adjust_rptr;
|
||||
}
|
||||
|
||||
private bool PassBits(uint n)
|
||||
private bool passbits(uint n)
|
||||
{
|
||||
bitPos += n;
|
||||
bitsProcessed += n;
|
||||
if (bitsProcessed < totalBits)
|
||||
l += n;
|
||||
blen += n;
|
||||
if (blen < blen_total)
|
||||
return true;
|
||||
|
||||
bitPos = adjust_bitPos;
|
||||
readPos = adjust_readPos;
|
||||
l = adjust_l;
|
||||
rptr = adjust_rptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -58,19 +58,20 @@ namespace CSNetwork.Security
|
||||
| ((value & 0xFF000000) >> 24);
|
||||
}
|
||||
|
||||
private uint Fetch()
|
||||
private uint fetch()
|
||||
{
|
||||
readPos += (int)(bitPos >> 3);
|
||||
bitPos &= 7;
|
||||
rptr += (int)(l >> 3);
|
||||
l &= 7;
|
||||
|
||||
byte[] fourBytes = new byte[4];
|
||||
for (int i = 0; i < 4 && readPos + i < legacyInput.Count; i++)
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
fourBytes[i] = legacyInput[readPos + i];
|
||||
if (rptr + i < legacyInput.Count) fourBytes[i] = legacyInput[rptr + i];
|
||||
else fourBytes[i] = 0;
|
||||
}
|
||||
|
||||
uint value = BitConverter.ToUInt32(fourBytes, 0);
|
||||
return ByteOrder32(value) << (int)bitPos;
|
||||
return ByteOrder32(value) << (int)l;
|
||||
}
|
||||
|
||||
private void LameCopy(int dstPos, int srcPos, int len)
|
||||
@@ -93,34 +94,6 @@ namespace CSNetwork.Security
|
||||
}
|
||||
}
|
||||
|
||||
// Copy bytes from history using MPPC's 8 KB circular window semantics.
|
||||
// Source starts at (historyPos - offset) modulo window size and may overlap
|
||||
// destination; freshly written bytes must be visible to subsequent reads.
|
||||
private void CopyFromHistoryCircular(int offset, int length)
|
||||
{
|
||||
int srcPos = historyPos - offset;
|
||||
if (srcPos < 0)
|
||||
srcPos += MPPC_HIST_LEN; // wrap source into window
|
||||
|
||||
int dstPos = historyPos;
|
||||
|
||||
while (length-- > 0)
|
||||
{
|
||||
history[dstPos] = history[srcPos];
|
||||
|
||||
dstPos++;
|
||||
if (dstPos == MPPC_HIST_LEN)
|
||||
dstPos = 0; // wrap destination
|
||||
|
||||
srcPos++;
|
||||
if (srcPos == MPPC_HIST_LEN)
|
||||
srcPos = 0; // wrap source
|
||||
}
|
||||
|
||||
// Advance history head to the end of the copied sequence
|
||||
historyPos = dstPos;
|
||||
}
|
||||
|
||||
public Octets Update(Octets input)
|
||||
{
|
||||
// Add input to legacy buffer
|
||||
@@ -129,253 +102,163 @@ namespace CSNetwork.Security
|
||||
legacyInput.Add(b);
|
||||
}
|
||||
|
||||
totalBits = (uint)(legacyInput.Count * 8 - bitPos);
|
||||
readPos = 0;
|
||||
bitsProcessed = 7;
|
||||
blen_total = (uint)(legacyInput.Count * 8 - l);
|
||||
rptr = 0;
|
||||
blen = 7;
|
||||
|
||||
Octets output = new Octets();
|
||||
int histHead = historyPos;
|
||||
int histhead = histptr;
|
||||
|
||||
while (totalBits > bitsProcessed)
|
||||
while (blen_total > blen)
|
||||
{
|
||||
adjust_bitPos = bitPos;
|
||||
adjust_readPos = readPos;
|
||||
uint val = Fetch();
|
||||
|
||||
adjust_l = l;
|
||||
adjust_rptr = rptr;
|
||||
|
||||
uint val = fetch();
|
||||
if (val < 0x80000000)
|
||||
{
|
||||
if (!PassBits(8))
|
||||
if (!passbits(8))
|
||||
break;
|
||||
// Defer if writing this literal would cross the end of the linear window.
|
||||
// We only wrap at EOB to match the original C++ semantics.
|
||||
if (historyPos + 1 > MPPC_HIST_LEN)
|
||||
{
|
||||
bitPos = adjust_bitPos;
|
||||
readPos = adjust_readPos;
|
||||
break;
|
||||
}
|
||||
history[historyPos++] = (byte)(val >> 24);
|
||||
// Do not wrap here; wrapping is handled only at EOB.
|
||||
|
||||
history[histptr++] = (byte)(val >> 24);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (val < 0xC0000000)
|
||||
{
|
||||
if (!PassBits(9))
|
||||
if (!passbits(9))
|
||||
break;
|
||||
// Defer if writing this literal would cross the end of the linear window.
|
||||
if (historyPos + 1 > MPPC_HIST_LEN)
|
||||
{
|
||||
bitPos = adjust_bitPos;
|
||||
readPos = adjust_readPos;
|
||||
break;
|
||||
}
|
||||
history[historyPos++] = (byte)(((val >> 23) | 0x80) & 0xFF);
|
||||
// Do not wrap here; wrapping is handled only at EOB.
|
||||
history[histptr++] = (byte)(((val >> 23) | 0x80) & 0xFF);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint offset = 0,
|
||||
length = 0;
|
||||
uint off = 0, len = 0;
|
||||
if (val >= 0xF0000000)
|
||||
{
|
||||
if (!PassBits(10))
|
||||
if (!passbits(10))
|
||||
break;
|
||||
offset = (val >> 22) & 0x3F;
|
||||
if (offset == CTRL_OFF_EOB)
|
||||
off = (val >> 22) & 0x3F;
|
||||
if (off == CTRL_OFF_EOB)
|
||||
{
|
||||
uint advance = 8 - (bitPos & 7);
|
||||
uint advance = (uint)(8 - (l & 7));
|
||||
if (advance < 8)
|
||||
if (!PassBits(advance))
|
||||
if (!passbits(advance))
|
||||
break;
|
||||
|
||||
// Copy current history segment to output
|
||||
if (historyPos >= histHead)
|
||||
{
|
||||
byte[] segment = new byte[historyPos - histHead];
|
||||
Array.Copy(history, histHead, segment, 0, segment.Length);
|
||||
byte[] segment = new byte[histptr - histhead];
|
||||
Array.Copy(history, histhead, segment, 0, segment.Length);
|
||||
output.Insert(output.Size, segment);
|
||||
|
||||
// If output is empty, replace it, otherwise insert at the end
|
||||
if (output.Size == 0)
|
||||
output.Replace(segment);
|
||||
else
|
||||
output.Insert(output.Size, segment);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wrap around case - two segments
|
||||
byte[] segment1 = new byte[MPPC_HIST_LEN - histHead];
|
||||
Array.Copy(history, histHead, segment1, 0, segment1.Length);
|
||||
if (histptr - histhead == MPPC_HIST_LEN)
|
||||
histptr = 0;
|
||||
|
||||
// Add first segment (from histHead to end of buffer)
|
||||
if (output.Size == 0)
|
||||
output.Replace(segment1);
|
||||
else
|
||||
output.Insert(output.Size, segment1);
|
||||
|
||||
// Add second segment (from beginning to historyPos)
|
||||
if (historyPos > 0)
|
||||
{
|
||||
byte[] segment2 = new byte[historyPos];
|
||||
Array.Copy(history, 0, segment2, 0, historyPos);
|
||||
output.Insert(output.Size, segment2);
|
||||
}
|
||||
}
|
||||
|
||||
if (historyPos == MPPC_HIST_LEN)
|
||||
historyPos = 0;
|
||||
|
||||
histHead = historyPos;
|
||||
histhead = histptr;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (val >= 0xE0000000)
|
||||
{
|
||||
if (!PassBits(12))
|
||||
if (!passbits(12))
|
||||
break;
|
||||
offset = ((val >> 20) & 0xFF) + 64;
|
||||
off = ((val >> 20) & 0xFF) + 64;
|
||||
}
|
||||
else if (val >= 0xC0000000)
|
||||
{
|
||||
if (!PassBits(16))
|
||||
if (!passbits(16))
|
||||
break;
|
||||
offset = ((val >> 16) & 0x1FFF) + 320;
|
||||
off = ((val >> 16) & 0x1FFF) + 320;
|
||||
}
|
||||
|
||||
val = fetch();
|
||||
if ( val < 0x80000000 )
|
||||
{
|
||||
if ( !passbits(1) )
|
||||
break;
|
||||
len = 3;
|
||||
}
|
||||
else if ( val < 0xc0000000 )
|
||||
{
|
||||
if ( !passbits(4) )
|
||||
break;
|
||||
len = 4|((val>>28)&3);
|
||||
}
|
||||
else if ( val < 0xe0000000 )
|
||||
{
|
||||
if ( !passbits(6) )
|
||||
break;
|
||||
len = 8|((val>>26)&7);
|
||||
}
|
||||
else if ( val < 0xf0000000 )
|
||||
{
|
||||
if ( !passbits(8) )
|
||||
break;
|
||||
len = 16|((val>>24)&15);
|
||||
}
|
||||
else if ( val < 0xf8000000 )
|
||||
{
|
||||
if ( !passbits(10) )
|
||||
break;
|
||||
len = 32|((val>>22)&0x1f);
|
||||
}
|
||||
else if ( val < 0xfc000000 )
|
||||
{
|
||||
if ( !passbits(12) )
|
||||
break;
|
||||
len = 64|((val>>20)&0x3f);
|
||||
}
|
||||
else if ( val < 0xfe000000 )
|
||||
{
|
||||
if ( !passbits(14) )
|
||||
break;
|
||||
len = 128|((val>>18)&0x7f);
|
||||
}
|
||||
else if ( val < 0xff000000 )
|
||||
{
|
||||
if ( !passbits(16) )
|
||||
break;
|
||||
len = 256|((val>>16)&0xff);
|
||||
}
|
||||
else if ( val < 0xff800000 )
|
||||
{
|
||||
if ( !passbits(18) )
|
||||
break;
|
||||
len = 0x200|((val>>14)&0x1ff);
|
||||
}
|
||||
else if ( val < 0xffc00000 )
|
||||
{
|
||||
if ( !passbits(20) )
|
||||
break;
|
||||
len = 0x400|((val>>12)&0x3ff);
|
||||
}
|
||||
else if ( val < 0xffe00000 )
|
||||
{
|
||||
if ( !passbits(22) )
|
||||
break;
|
||||
len = 0x800|((val>>10)&0x7ff);
|
||||
}
|
||||
else if ( val < 0xfff00000 )
|
||||
{
|
||||
if ( !passbits(24) )
|
||||
break;
|
||||
len = 0x1000|((val>>8)&0xfff);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Invalid data
|
||||
bitPos = adjust_bitPos;
|
||||
readPos = adjust_readPos;
|
||||
l = adjust_l;
|
||||
rptr = adjust_rptr;
|
||||
break;
|
||||
}
|
||||
|
||||
val = Fetch();
|
||||
if (val < 0x80000000)
|
||||
{
|
||||
if (!PassBits(1))
|
||||
break;
|
||||
length = 3;
|
||||
}
|
||||
else if (val < 0xC0000000)
|
||||
{
|
||||
if (!PassBits(4))
|
||||
break;
|
||||
length = 4 | ((val >> 28) & 3);
|
||||
}
|
||||
else if (val < 0xE0000000)
|
||||
{
|
||||
if (!PassBits(6))
|
||||
break;
|
||||
length = 8 | ((val >> 26) & 7);
|
||||
}
|
||||
else if (val < 0xF0000000)
|
||||
{
|
||||
if (!PassBits(8))
|
||||
break;
|
||||
length = 16 | ((val >> 24) & 15);
|
||||
}
|
||||
else if (val < 0xF8000000)
|
||||
{
|
||||
if (!PassBits(10))
|
||||
break;
|
||||
length = 32 | ((val >> 22) & 0x1F);
|
||||
}
|
||||
else if (val < 0xFC000000)
|
||||
{
|
||||
if (!PassBits(12))
|
||||
break;
|
||||
length = 64 | ((val >> 20) & 0x3F);
|
||||
}
|
||||
else if (val < 0xFE000000)
|
||||
{
|
||||
if (!PassBits(14))
|
||||
break;
|
||||
length = 128 | ((val >> 18) & 0x7F);
|
||||
}
|
||||
else if (val < 0xFF000000)
|
||||
{
|
||||
if (!PassBits(16))
|
||||
break;
|
||||
length = 256 | ((val >> 16) & 0xFF);
|
||||
}
|
||||
else if (val < 0xFF800000)
|
||||
{
|
||||
if (!PassBits(18))
|
||||
break;
|
||||
length = 0x200 | ((val >> 14) & 0x1FF);
|
||||
}
|
||||
else if (val < 0xFFC00000)
|
||||
{
|
||||
if (!PassBits(20))
|
||||
break;
|
||||
length = 0x400 | ((val >> 12) & 0x3FF);
|
||||
}
|
||||
else if (val < 0xFFE00000)
|
||||
{
|
||||
if (!PassBits(22))
|
||||
break;
|
||||
length = 0x800 | ((val >> 10) & 0x7FF);
|
||||
}
|
||||
else if (val < 0xFFF00000)
|
||||
{
|
||||
if (!PassBits(24))
|
||||
break;
|
||||
length = 0x1000 | ((val >> 8) & 0xFFF);
|
||||
}
|
||||
else
|
||||
{
|
||||
bitPos = adjust_bitPos;
|
||||
readPos = adjust_readPos;
|
||||
if (histptr < off || histptr + len > MPPC_HIST_LEN)
|
||||
break;
|
||||
}
|
||||
|
||||
// Validate match boundaries against the current linear window. Defer if it would cross.
|
||||
// This mirrors the C++ check: if (histptr - off < history || histptr + len > history + MPPC_HIST_LEN) break;
|
||||
if (offset == 0 || offset > MPPC_HIST_LEN || historyPos - (int)offset < 0 || historyPos + (int)length > MPPC_HIST_LEN)
|
||||
{
|
||||
// Not enough room in the current linear window; rollback and wait for more data.
|
||||
bitPos = adjust_bitPos;
|
||||
readPos = adjust_readPos;
|
||||
break;
|
||||
}
|
||||
|
||||
// Linear, overlap-safe copy within current window. No mid-block wrap.
|
||||
LameCopy(historyPos, historyPos - (int)offset, (int)length);
|
||||
historyPos += (int)length;
|
||||
}
|
||||
|
||||
// Copy remaining history segment to output
|
||||
if (historyPos >= histHead)
|
||||
{
|
||||
byte[] result = new byte[historyPos - histHead];
|
||||
Array.Copy(history, histHead, result, 0, result.Length);
|
||||
if (output.Size == 0)
|
||||
output.Replace(result);
|
||||
else
|
||||
output.Insert(output.Size, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (histHead < MPPC_HIST_LEN)
|
||||
{
|
||||
byte[] segment1 = new byte[MPPC_HIST_LEN - histHead];
|
||||
Array.Copy(history, histHead, segment1, 0, segment1.Length);
|
||||
if (output.Size == 0)
|
||||
output.Replace(segment1);
|
||||
else
|
||||
output.Insert(output.Size, segment1);
|
||||
}
|
||||
|
||||
if (historyPos > 0)
|
||||
{
|
||||
byte[] segment2 = new byte[historyPos];
|
||||
Array.Copy(history, 0, segment2, 0, historyPos);
|
||||
output.Insert(output.Size, segment2);
|
||||
}
|
||||
LameCopy(histptr, histptr - (int)off, (int)len);
|
||||
histptr += (int)len;
|
||||
}
|
||||
byte[] result = new byte[histptr - histhead];
|
||||
Array.Copy(history, histhead, result, 0, result.Length);
|
||||
output.Insert(output.Size, result);
|
||||
|
||||
// Remove processed bytes from legacy input
|
||||
legacyInput.RemoveRange(0, readPos);
|
||||
legacyInput.RemoveRange(0, rptr);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user