#include "netpacket.h"
#include "SyEmbeddedHash.h"

cNetPositionPacket::cNetPositionPacket()
: cNetPacket(NET_POSITION),
  mLocation(0.0f, 0.0f, 0.0f),
  mVelocity(0.0f, 0.0f, 0.0f),
  mHPR(0.0f, 0.0f, 0.0f),
  mSpeedRequest(0.0f),
  mHeadingRequest(0.0f),
  mbTeleport(false)
{
}

void cNetPacket::Pack(SyPack::DataStream *stream)
{
    stream->Pack8u(&mType);
}

void cNetPositionPacket::Pack(SyPack::DataStream *stream)
{
    cNetPacket::Pack(stream);
    stream->PackFloat(&mLocation.X);
    stream->PackFloat(&mLocation.Y);
    stream->PackFloat(&mLocation.Z);
    stream->PackFloat(&mVelocity.X);
    stream->PackFloat(&mVelocity.Y);
    stream->PackFloat(&mVelocity.Z);
    stream->PackFloat(&mHPR.X);
    stream->PackFloat(&mHPR.Y);
    stream->PackFloat(&mHPR.Z);
    stream->PackFloat(&mSpeedRequest);
    stream->PackFloat(&mHeadingRequest);
    stream->PackBool(&mbTeleport);
}

void cNetAnimStatePacket::Pack(SyPack::DataStream *stream)
{
    cNetPacket::Pack(stream);
    stream->Pack32s(&mAnimState);
    stream->Pack32s(&mTarget);
    stream->Pack32s(&mActionTarget);
    stream->PackFloat(&mTargetHeading);
    stream->PackFloat(&mTargetRange);
    stream->Pack32s(&mScriptAnimID);
    stream->PackBool(&mScriptAnimAllowHitReactions);
    stream->Pack32s(&mCastSpellID);
    stream->Pack32s(&mCastSpellID);
    stream->Pack32s(&mCastAnimSettingOverride);
}


void cNetDamagePacket::Pack(SyPack::DataStream *stream)
{
    cNetPacket::Pack(stream);
    stream->Pack32s(&mTotalDamage);
    stream->Pack32s(&mAttacker);
    stream->PackBool(&mDeath);
    stream->PackBool(&mHitReact);
    stream->PackFloat(&mHitReactTime);
    stream->Pack32s(&mDamageType);
}

void cNetActivateRequestPacket::Pack(SyPack::DataStream *stream)
{
    cNetPacket::Pack(stream);
    stream->Pack32s(&mActor);
}

void cNetActivateConfirmPacket::Pack(SyPack::DataStream *stream)
{
    cNetPacket::Pack(stream);
    stream->Pack32s(&mActor);
}

void cNetDropPacket::Pack(SyPack::DataStream *stream)
{
    cNetPacket::Pack(stream);
    stream->Pack32s(&mObject);
}

void cNetAIBlackboardPacket::Pack(SyPack::DataStream *stream)
{
  cNetPacket::Pack(stream);
  stream->Pack8s(&mRecordType);
  stream->Pack32s(&mSourceID);
  stream->Pack32s(&mTargetID);
  stream->Pack32u(&mData);
  stream->PackFloat(&mLocation.X);
  stream->PackFloat(&mLocation.Y);
  stream->PackFloat(&mLocation.Z);
  stream->PackBool(&mbRemoveIfExists);
}

void cNetScriptFuncPacket::Pack(SyPack::DataStream *stream)
{
  cNetPacket::Pack(stream);
  stream->Pack32s(&mIDENTIFIER);
  stream->Pack32s(&mFuncID);
  stream->Pack32s(&mNumParams);
  stream->Pack32s(&mNumChars);
  for (int ii=0;ii<MAX_NUM_PARAMS;++ii)
  {
    stream->Pack64s(&mParams[ii]);
  }
  for (int ii=0;ii<MAX_STRING;++ii)
  {
    stream->Pack8s(&mStrings[ii]);
  }
};

cNetGlobalScriptStatePacket::cNetGlobalScriptStatePacket()
: cNetPacket(NET_GLOBAL_SCRIPT_STATE),
  mbStartup(false),  
  mCutsceneSkipTime(0.0f),
  mCutsceneSkipping(false),
  mCutsceneEndFadeIn(false),
  mNumScripts(0)
{
  memset(mCutsceneEndHandler, 0, MAX_CUTSCENEENDHANDLER_NAME_LEN);
}

cNetGlobalScriptStatePacket::~cNetGlobalScriptStatePacket()
{
  for (int i=0; i<mScripts.Size(); ++i)
  {
    delete mScripts(i);
  }

  mScripts.Clear();
}

void cNetGlobalScriptStatePacket::Pack(SyPack::DataStream *stream)
{
  cNetPacket::Pack(stream);

  stream->Pack32s(&mNumScripts);

  for (int i=0; i<mNumScripts; ++i)
  {
    while (i >= mScripts.Size())
    {
      mScripts.Add(new ScriptInstanceData());
    }

    stream->Pack32s(&(mScripts(i)->mParentIndex));
    stream->Pack32s(&(mScripts(i)->mThisID));
    stream->Pack32s(&(mScripts(i)->mOtherID));
    stream->Pack32s(&(mScripts(i)->mDeltaSize));

    if (!stream->IsPacking() && mScripts(i)->mpScriptDelta == NULL)
    {
      mScripts(i)->mpScriptDelta = new char[mScripts(i)->mDeltaSize];
    }

    if (mScripts(i)->mpScriptDelta)
    {
      for (int j=0; j<mScripts(i)->mDeltaSize; ++j)
      {
        stream->Pack8s((char*)&(mScripts(i)->mpScriptDelta[j]));
      }
    }

    stream->Pack32s(&(mScripts(i)->mSleepNotifyType));
    stream->PackFloat(&(mScripts(i)->mSleepTime));
    stream->Pack32s(&(mScripts(i)->mSleepingID));
    stream->PackBool(&(mScripts(i)->mbCutscene));

    stream->Pack32s(&(mScripts(i)->mAMX_cip));
    stream->Pack32s(&(mScripts(i)->mAMX_frm));
    stream->Pack32s(&(mScripts(i)->mAMX_hea));
    stream->Pack32s(&(mScripts(i)->mAMX_hlw));
    stream->Pack32s(&(mScripts(i)->mAMX_stk));
    stream->Pack32s(&(mScripts(i)->mAMX_stp));
  }

  stream->PackBool(&mbStartup);

  for (int iC=0; iC<MAX_CUTSCENEENDHANDLER_NAME_LEN; ++iC)
  {
    stream->Pack8s(&(mCutsceneEndHandler[iC]));
  }

  stream->PackFloat(&mCutsceneSkipTime);
  stream->PackBool(&mCutsceneSkipping);
  stream->PackBool(&mCutsceneEndFadeIn);
}

void cNetGlobalScriptStatePacket::AddScript(int parentIndex,
                                            tGameObjectID thisID, tGameObjectID otherID,
                                            char* pSourceScript, char* pCurScript, int scriptSize,
                                            int sleepNotifyType, float sleepTime,
                                            tGameObjectID sleepingID, bool bCutscene,
                                            int cip, int frm, int hea, int hlw, int stk, int stp)
{
  ScriptInstanceData* pInfo = new ScriptInstanceData();

  pInfo->mParentIndex = parentIndex;
  pInfo->mThisID = thisID;
  pInfo->mOtherID = otherID;

  if (scriptSize > 0 && pSourceScript && pCurScript)
  {
    pInfo->mDeltaSize = scriptSize;
    pInfo->mpScriptDelta = new char [scriptSize];
    
    if (!cDeltaCompression::CreateDelta(pSourceScript, scriptSize, pCurScript, scriptSize, pInfo->mpScriptDelta, scriptSize, pInfo->mDeltaSize))
    {
      pInfo->mDeltaSize = 0;
      delete [] pInfo->mpScriptDelta;
      pInfo->mpScriptDelta = NULL;
      SyAssertf(false, "Failed to create global script state packet - delta buffer too small");
    }
//#ifdef _DEBUG
//    else
//    {
//      char* test = new char [scriptSize];
//      int outLen = 0;
//      cDeltaCompression::ApplyDelta(pSourceScript, scriptSize, pInfo->mpScriptDelta, pInfo->mDeltaSize, test, scriptSize, outLen);
//
//      int index = 0;
//      while (index < scriptSize && pCurScript[index] == test[index])
//      {
//        ++index;
//      }
//
//      SyAssert(index == scriptSize);
//      SyAssert(outLen == scriptSize);
//      delete [] test;
//    }
//#endif
  }
  else
  {
    pInfo->mDeltaSize = 0;
    pInfo->mpScriptDelta = NULL;
  }

  pInfo->mSleepNotifyType = sleepNotifyType;
  pInfo->mSleepTime = sleepTime;
  pInfo->mSleepingID = sleepingID;
  pInfo->mbCutscene = bCutscene;

  pInfo->mAMX_cip = cip;
  pInfo->mAMX_frm = frm;
  pInfo->mAMX_hea = hea;
  pInfo->mAMX_hlw = hlw;
  pInfo->mAMX_stk = stk;
  pInfo->mAMX_stp = stp;

  mScripts.Add(pInfo);
  ++mNumScripts;
}

int cNetGlobalScriptStatePacket::GetNumScripts()
{
  return mScripts.Size();
}

const cNetGlobalScriptStatePacket::ScriptInstanceData* cNetGlobalScriptStatePacket::GetScriptData(int index)
{
  if (index < mScripts.Size())
  {
    return mScripts(index);
  }

  return NULL;
}

cNetGlobalScriptStatePacket::ScriptInstanceData::ScriptInstanceData()
{
  mParentIndex = -1;
  mThisID = ID_NONE;
  mOtherID = ID_NONE;
  mpScriptDelta = NULL; 
  mDeltaSize = 0;
  mSleepNotifyType = 0;
  mSleepTime = 0.0f;
  mSleepingID = ID_NONE;
  mbCutscene = false;
  mAMX_cip = 0;
  mAMX_frm = 0;
  mAMX_hea = 0;
  mAMX_hlw = 0;
  mAMX_stk = 0;
  mAMX_stp = 0;
}

cNetGlobalScriptStatePacket::ScriptInstanceData::~ScriptInstanceData()
{
  delete [] mpScriptDelta;
  mpScriptDelta = NULL;
}

bool 
cNetScriptFuncPacket::GetAddr(int32 address,int32 **dest)
{
  if (address < 0 || address >= MAX_STRING)
  {
    return false;
  }
  *dest = (int32*)&mStrings[address];
  return true;
};

bool 
cNetScriptFuncPacket::GetString(char *buffer, int32 src, int max_string_len)
{
  if (src < 0 || src >= MAX_STRING)
  {
    return false;
  }
  char *start = &mStrings[src];
  int index = 0;
  while (start < &mStrings[MAX_STRING] && *start != '\0' && index < max_string_len-1)
  {
    buffer[index] = *start;
    ++index;
    ++start;
  }
  buffer[index]= '\0';
  return true;
};

void
cNetScriptFuncPacket::Init(SyResourceID func_id,int32 *params, int numParams)
{
  mFuncID = func_id;
  SyAssert(numParams < MAX_NUM_PARAMS);

  for (int ii=0;ii<numParams+1;++ii)
  {
    mParams[ii] = params[ii];
  }

  mNumParams = numParams;
  mNumChars = 0;
};

void 
cNetScriptFuncPacket::AddString(int cell_index, char *string)
{
  SyAssert (cell_index <= mNumParams);
  mParams[cell_index] = mNumChars;
  while (mNumChars < MAX_STRING-1 && *string != '\0')
  {
    mStrings[mNumChars++] = *string++;
  }
  mStrings[mNumChars++] = '\0';
};


void cNetPushRequestPacket::Pack(SyPack::DataStream *stream)
{
  cNetPacket::Pack(stream);
  stream->PackFloat(&mLocation.X);
  stream->PackFloat(&mLocation.Y);
  stream->PackFloat(&mLocation.Z);
}


void cNetTransitionLevelPacket::Pack(SyPack::DataStream *stream)
{
  cNetPacket::Pack(stream);

  for (int ii=0;ii<MAX_STRING;++ii)
  {
    stream->Pack8s(&mLevelName[ii]);
  }
  for (int ii=0;ii<MAX_STRING;++ii)
  {
    stream->Pack8s(&mSpawnPointName[ii]);
  }
}


void 
cNetTransitionLevelPacket::Init(const char *levelname,const char *spawnpoint)
{
  strncpy(mLevelName,levelname,MAX_STRING);
  strncpy(mSpawnPointName,spawnpoint,MAX_STRING);
}

void
cNetKnockbackPacket::Pack(SyPack::DataStream *stream)
{
  cNetPacket::Pack(stream);
  stream->PackFloat(&mLocation.X);
  stream->PackFloat(&mLocation.Y);
  stream->PackFloat(&mLocation.Z);
  stream->PackFloat(&mXZAmount);
  stream->PackFloat(&mYAmount);
  stream->PackBool(&mbKnockdown);
  stream->PackFloat(&mKnockdownTime);
}

void
cNetAddConditionPacket::Pack(SyPack::DataStream *stream)
{
  cNetPacket::Pack(stream);

  stream->PackString(mConditionName, MAX_CONDITION_NAME_LEN);
  stream->Pack32u(&mSourceID);
  stream->Pack32u(&mSpellID);
  stream->Pack32u(&mItemMasterID);
  stream->PackFloat(&mDuration);
  stream->Pack32u(&mEffectID);
  stream->Pack32s(&mParam1);
  stream->Pack32s(&mParam2);
}

void
cNetRemoveConditionPacket::Pack(SyPack::DataStream *stream)
{
  cNetPacket::Pack(stream);

  stream->PackString(mConditionName, cNetAddConditionPacket::MAX_CONDITION_NAME_LEN);
  stream->Pack32u(&mSourceID);
  stream->Pack32u(&mSpellID);
  stream->Pack32u(&mItemMasterID);
}

void
cNetRemoveConditionsFromSourcePacket::Pack(SyPack::DataStream *stream)
{
  cNetPacket::Pack(stream);

  stream->Pack32u(&mSourceID);
  stream->Pack32u(&mSpellID);
  stream->Pack32u(&mItemMasterID);
}

void
cNetCastSpellPacket::Pack(SyPack::DataStream *stream)
{
  cNetPacket::Pack(stream);

  stream->Pack32u(&mTargetID);
  stream->Pack32u(&mSpellID);
  stream->PackFloat(&mTargetLoc.X);
  stream->PackFloat(&mTargetLoc.Y);
  stream->PackFloat(&mTargetLoc.Z);
  stream->Pack32s(&mCombo);
}


class cDeltaHashEntry
{
  public:
    cDeltaHashEntry(int offset, bool bFromBase): mOffset(offset), mbFromBase(bFromBase) {}

    SyEmbeddedHashLink<cDeltaHashEntry> mLink;
   
    int mOffset;
    bool mbFromBase;
};

typedef SyEmbeddedHash<cDeltaHashEntry, &cDeltaHashEntry::mLink, 1000> DeltaPrefixHashTable;

static void ClearDeltaPrefixHashTable(DeltaPrefixHashTable& hashes)
{
  cDeltaHashEntry* pCurEntry = hashes.WalkFirst();
  while (pCurEntry != NULL)
  {
    cDeltaHashEntry *pDeleteEntry = pCurEntry;
    pCurEntry = hashes.WalkNext(pCurEntry);
    hashes.Remove(pDeleteEntry);
    delete pDeleteEntry;
  }
}

bool cDeltaCompression::CreateDelta(char* pBase, int baseSize,
                                    char* pTarget, int targetSize,
                                    char* pDelta, int deltaSize,
                                    int& deltaOutLen)
{
  static const int HASH_PREFIX_LENGTH = 3;

  DeltaPrefixHashTable hashes;

  bool bSuccessful = true;

  char* pOffsetBase = pBase;
  char* pOffsetTarget = pTarget;
  char* pOffsetDelta = pDelta;

  char* pStartBase = pBase;
  char* pStartTarget = pTarget;

  char* pEndBase = pBase + baseSize;
  char* pEndTarget = pTarget + targetSize;
  char* pEndDelta = pDelta + deltaSize;

  int hashBase = 0;
  int hashTarget = 0;

  int d = 1;

  int numSimilar, insertLen;
  char* pBaseCmp;
  char* pTargetCmp;

  cDeltaHashEntry* pFoundEntry;

  // Preprocessing - computes d = 2^(m-1) with the left-shift operator 
  for (int iPow = 1; iPow < HASH_PREFIX_LENGTH; ++iPow)
  {
    d = (d<<1);
  }

#define LARGE_PRIME 2146954937
#define HASH(a, h) h=0; for (char* pch=a; pch<a+HASH_PREFIX_LENGTH; ++pch) { h = ((h<<1) + *pch); }
#define REHASH(a, b, h) h = ((((h) - (a)*d) << 1) + (b))

  // compute starting hash values for base and target
  HASH(pBase, hashBase);
  HASH(pTarget, hashTarget);


  while (pOffsetBase < pEndBase - HASH_PREFIX_LENGTH &&
         pOffsetTarget < pEndTarget - HASH_PREFIX_LENGTH &&
         bSuccessful)
  {
    // check for base hash already found
    pFoundEntry = hashes.FindFirst(hashBase);

    if (!pFoundEntry)
    {
      cDeltaHashEntry* pNewEntry = new cDeltaHashEntry(pOffsetBase-pBase, true);
      hashes.Insert(pNewEntry, hashBase);
    }
    else if (!pFoundEntry->mbFromBase)
    {
      if (memcmp(pOffsetBase, pTarget+pFoundEntry->mOffset, HASH_PREFIX_LENGTH) == 0)
      {
        // found matching hash from target file
        numSimilar = 0;
        pBaseCmp = pOffsetBase;
        pTargetCmp = pTarget + pFoundEntry->mOffset;

        while (pBaseCmp < pEndBase && pTargetCmp < pEndTarget && *pBaseCmp == *pTargetCmp)
        {
          ++pBaseCmp;
          ++pTargetCmp;
          ++numSimilar;
        }

        if (pFoundEntry->mOffset > pStartTarget-pTarget)
        {
          insertLen = (pFoundEntry->mOffset-(pStartTarget-pTarget));
          // encode insertion if we have data before the match
          if (pOffsetDelta+9+insertLen <= pEndDelta)
          {
            *pOffsetDelta++ = DELTA_INSERT;
            *((int*)(pOffsetDelta)) = insertLen;
            pOffsetDelta += 4;
            memcpy(pOffsetDelta, pStartTarget, insertLen);
            pOffsetDelta += insertLen;
          }
          else
          {
            bSuccessful = false;
          }
        }

        // encode copy of matching data
        if (pOffsetDelta+9 <= pEndDelta)
        {
          *pOffsetDelta++ = DELTA_COPY;
          *((int*)(pOffsetDelta)) = pOffsetBase-pBase;
          pOffsetDelta += 4;
          *((int*)(pOffsetDelta)) = numSimilar;
          pOffsetDelta += 4;
        }
        else
        {
          bSuccessful = false;
        }

        pOffsetBase += numSimilar;
        pOffsetTarget = pTarget+pFoundEntry->mOffset+numSimilar;

        if (pOffsetBase+HASH_PREFIX_LENGTH < pEndBase)
        {
          HASH(pOffsetBase, hashBase);
        }

        if (pOffsetTarget+HASH_PREFIX_LENGTH < pEndTarget)
        {
          HASH(pOffsetTarget, hashTarget);
        }

        pStartBase = pOffsetBase;
        pStartTarget = pOffsetTarget;
        ClearDeltaPrefixHashTable(hashes);
        continue;
      }
    }

    // check for target hash already found
    pFoundEntry = hashes.FindFirst(hashTarget);

    if (!pFoundEntry)
    {
      cDeltaHashEntry* pNewEntry = new cDeltaHashEntry(pOffsetTarget-pTarget, false);
      hashes.Insert(pNewEntry, hashTarget);
    }
    else if (pFoundEntry->mbFromBase)
    {
      if (memcmp(pOffsetTarget, pBase+pFoundEntry->mOffset, HASH_PREFIX_LENGTH) == 0)
      {
        // found matching hash from base file
        numSimilar = 0;
        pBaseCmp = pBase + pFoundEntry->mOffset;
        pTargetCmp = pOffsetTarget;

        while (pBaseCmp < pEndBase && pTargetCmp < pEndTarget && *pBaseCmp == *pTargetCmp)
        {
          ++pBaseCmp;
          ++pTargetCmp;
          ++numSimilar;
        }

        if (pOffsetTarget > pStartTarget)
        {
          insertLen = (pOffsetTarget-pStartTarget);

          // encode insertion if we have data before the match
          if (pOffsetDelta+9+insertLen <= pEndDelta)
          {
            *pOffsetDelta++ = DELTA_INSERT;
            *((int*)(pOffsetDelta)) = insertLen;
            pOffsetDelta += 4;
            memcpy(pOffsetDelta, pStartTarget, insertLen);
            pOffsetDelta += insertLen;
          }
          else
          {
            bSuccessful = false;
          }
        }

        // encode copy of matching data
        if (pOffsetDelta+9 <= pEndDelta)
        {
          *pOffsetDelta++ = DELTA_COPY;
          *((int*)(pOffsetDelta)) = pFoundEntry->mOffset;
          pOffsetDelta += 4;
          *((int*)(pOffsetDelta)) = numSimilar;
          pOffsetDelta += 4;
        }
        else
        {
          bSuccessful = false;
        }

        pOffsetBase = pBase+pFoundEntry->mOffset+numSimilar;
        pOffsetTarget += numSimilar;

        if (pOffsetBase+HASH_PREFIX_LENGTH < pEndBase)
        {
          HASH(pOffsetBase, hashBase);
        }

        if (pOffsetTarget+HASH_PREFIX_LENGTH < pEndTarget)
        {
          HASH(pOffsetTarget, hashTarget);
        }

        pStartBase = pOffsetBase;
        pStartTarget = pOffsetTarget;
        ClearDeltaPrefixHashTable(hashes);
        continue;
      }
    }

    if (pOffsetBase+HASH_PREFIX_LENGTH < pEndBase)
    {
      REHASH(*pOffsetBase, *(char*)(pOffsetBase+HASH_PREFIX_LENGTH), hashBase);
    }

    if (pOffsetTarget+HASH_PREFIX_LENGTH < pEndTarget)
    {
      REHASH(*pOffsetTarget, *(char*)(pOffsetTarget+HASH_PREFIX_LENGTH), hashTarget);
    }

    ++pOffsetBase;
    ++pOffsetTarget;
  }

#undef REHASH
#undef HASH
#undef LARGE_PRIME

  // encode any remaining difference in the target as an insertion
  if (pStartTarget < pEndTarget)
  {
    int insertLen = (pEndTarget-pStartTarget);
    if (pOffsetDelta+9+insertLen <= pEndDelta)
    {
      *pOffsetDelta++ = DELTA_INSERT;
      *((int*)(pOffsetDelta)) = insertLen;
      pOffsetDelta += 4;
      memcpy(pOffsetDelta, pStartTarget, insertLen);
      pOffsetDelta += insertLen;
    }
    else
    {
      bSuccessful = false;
    }
  }

  ClearDeltaPrefixHashTable(hashes);

  deltaOutLen = pOffsetDelta-pDelta;
  return bSuccessful; 
}

bool cDeltaCompression::ApplyDelta(char* pBase, int baseSize,
                                   char* pDelta, int deltaSize,
                                   char* pTarget, int targetSize,
                                   int& targetOutLen)
{
  char* pOffsetTarget = pTarget;
  char* pOffsetDelta = pDelta;

  char* pEndTarget = pTarget + targetSize;
  char* pEndDelta = pDelta + deltaSize;

  int copyLength;
  int offsetBase;

  bool bSuccessful = true;


  while (pOffsetDelta < pEndDelta && bSuccessful)
  {
    if (*pOffsetDelta == DELTA_COPY)
    {
      offsetBase = *((int*)(pOffsetDelta+1));
      copyLength = *((int*)(pOffsetDelta+5));

      if (offsetBase+copyLength <= baseSize && 
          pOffsetTarget+copyLength <= pEndTarget)
      {
        memcpy(pOffsetTarget, pBase+offsetBase, copyLength);
        pOffsetTarget += copyLength;
        pOffsetDelta += 9;
      }
      else
      {
        bSuccessful = false;
      }
    }
    else if (*pOffsetDelta == DELTA_INSERT)
    {
      copyLength = *((int*)(pOffsetDelta+1));

      SyAssert(pOffsetDelta+5+copyLength <= pEndDelta);
      if (pOffsetDelta+5+copyLength <= pEndDelta && 
          pOffsetTarget+copyLength <= pEndTarget)
      {
        memcpy(pOffsetTarget, pOffsetDelta+5, copyLength);
        pOffsetTarget += copyLength;
        pOffsetDelta += 5 + copyLength;
      }
      else
      {
        bSuccessful = false;
      }
    }
    else 
    {
      SyAssertf(false, "Bad opcode in delta compression");
    }
  }

  SyAssert(pOffsetDelta == pEndDelta);
  targetOutLen = pOffsetTarget-pTarget;

  return bSuccessful;
}


// EOF
