// AgentOut.cpp

#include "StdAfx.h"

#include "../../../Common/Wildcard.h"

#include "../../../Windows/FileDir.h"
#include "../../../Windows/FileName.h"
#include "../../../Windows/TimeUtils.h"

#include "../../Compress/CopyCoder.h"

#include "../../Common/FileStreams.h"

#include "../../Archive/Common/ItemNameUtils.h"

#include "Agent.h"
#include "UpdateCallbackAgent.h"

using namespace NWindows;
using namespace NCOM;

Z7_COM7F_IMF(CAgent::SetFolder(IFolderFolder *folder))
{
  _updatePathPrefix.Empty();
  _updatePathPrefix_is_AltFolder = false;
  _agentFolder = NULL;

  if (!folder)
    return S_OK;

  {
    Z7_DECL_CMyComPtr_QI_FROM(
        IArchiveFolderInternal,
        afi, folder)
    if (afi)
    {
      RINOK(afi->GetAgentFolder(&_agentFolder))
    }
    if (!_agentFolder)
      return E_FAIL;
  }

  if (_proxy2)
    _updatePathPrefix = _proxy2->GetDirPath_as_Prefix(_agentFolder->_proxyDirIndex, _updatePathPrefix_is_AltFolder);
  else
    _updatePathPrefix = _proxy->GetDirPath_as_Prefix(_agentFolder->_proxyDirIndex);
  return S_OK;
}

Z7_COM7F_IMF(CAgent::SetFiles(const wchar_t *folderPrefix,
    const wchar_t * const *names, UInt32 numNames))
{
  _folderPrefix = us2fs(folderPrefix);
  _names.ClearAndReserve(numNames);
  for (UInt32 i = 0; i < numNames; i++)
    _names.AddInReserved(us2fs(names[i]));
  return S_OK;
}

static HRESULT EnumerateArchiveItems(CAgent *agent,
    const CProxyDir &item,
    const UString &prefix,
    CObjectVector<CArcItem> &arcItems)
{
  unsigned i;
  
  for (i = 0; i < item.SubFiles.Size(); i++)
  {
    unsigned arcIndex = item.SubFiles[i];
    const CProxyFile &fileItem = agent->_proxy->Files[arcIndex];
    CArcItem ai;
    RINOK(agent->GetArc().GetItem_MTime(arcIndex, ai.MTime))
    RINOK(agent->GetArc().GetItem_Size(arcIndex, ai.Size, ai.Size_Defined))
    ai.IsDir = false;
    ai.Name = prefix + fileItem.Name;
    ai.Censored = true; // test it
    ai.IndexInServer = arcIndex;
    arcItems.Add(ai);
  }
  
  for (i = 0; i < item.SubDirs.Size(); i++)
  {
    const CProxyDir &dirItem = agent->_proxy->Dirs[item.SubDirs[i]];
    UString fullName = prefix + dirItem.Name;
    if (dirItem.IsLeaf())
    {
      CArcItem ai;
      RINOK(agent->GetArc().GetItem_MTime((unsigned)dirItem.ArcIndex, ai.MTime))
      ai.IsDir = true;
      ai.Size_Defined = false;
      ai.Name = fullName;
      ai.Censored = true; // test it
      ai.IndexInServer = (unsigned)dirItem.ArcIndex;
      arcItems.Add(ai);
    }
    RINOK(EnumerateArchiveItems(agent, dirItem, fullName + WCHAR_PATH_SEPARATOR, arcItems))
  }
  
  return S_OK;
}

static HRESULT EnumerateArchiveItems2(const CAgent *agent,
    unsigned dirIndex,
    const UString &prefix,
    CObjectVector<CArcItem> &arcItems)
{
  const CProxyDir2 &dir = agent->_proxy2->Dirs[dirIndex];
  FOR_VECTOR (i, dir.Items)
  {
    unsigned arcIndex = dir.Items[i];
    const CProxyFile2 &file = agent->_proxy2->Files[arcIndex];
    CArcItem ai;
    ai.IndexInServer = arcIndex;
    ai.Name = prefix + file.Name;
    ai.Censored = true; // test it
    RINOK(agent->GetArc().GetItem_MTime(arcIndex, ai.MTime))
    ai.IsDir = file.IsDir();
    ai.Size_Defined = false;
    ai.IsAltStream = file.IsAltStream;
    if (!ai.IsDir)
    {
      RINOK(agent->GetArc().GetItem_Size(arcIndex, ai.Size, ai.Size_Defined))
      ai.IsDir = false;
    }
    arcItems.Add(ai);
    
    if (file.AltDirIndex != -1)
    {
      RINOK(EnumerateArchiveItems2(agent, (unsigned)file.AltDirIndex, ai.Name + L':', arcItems))
    }
    
    if (ai.IsDir)
    {
      RINOK(EnumerateArchiveItems2(agent, (unsigned)file.DirIndex, ai.Name + WCHAR_PATH_SEPARATOR, arcItems))
    }
  }
  return S_OK;
}

struct CAgUpCallbackImp Z7_final: public IUpdateProduceCallback
{
  const CObjectVector<CArcItem> *_arcItems;
  IFolderArchiveUpdateCallback *_callback;
  
  CAgUpCallbackImp(const CObjectVector<CArcItem> *a,
      IFolderArchiveUpdateCallback *callback): _arcItems(a), _callback(callback) {}
  HRESULT ShowDeleteFile(unsigned arcIndex) Z7_override;
};

HRESULT CAgUpCallbackImp::ShowDeleteFile(unsigned arcIndex)
{
  return _callback->DeleteOperation((*_arcItems)[arcIndex].Name);
}


static void SetInArchiveInterfaces(CAgent *agent, CArchiveUpdateCallback *upd)
{
  if (agent->_archiveLink.Arcs.IsEmpty())
    return;
  const CArc &arc = agent->GetArc();
  upd->Arc = &arc;
  upd->Archive = arc.Archive;

  upd->ArcFileName = ExtractFileNameFromPath(arc.Path);
}

struct CDirItemsCallback_AgentOut Z7_final: public IDirItemsCallback
{
  CMyComPtr<IFolderScanProgress> FolderScanProgress;
  IFolderArchiveUpdateCallback *FolderArchiveUpdateCallback;
  HRESULT ErrorCode;
  
  CDirItemsCallback_AgentOut(): FolderArchiveUpdateCallback(NULL), ErrorCode(S_OK) {}

  HRESULT ScanError(const FString &name, DWORD systemError) Z7_override
  {
    const HRESULT hres = HRESULT_FROM_WIN32(systemError);
    if (FolderArchiveUpdateCallback)
      return FolderScanProgress->ScanError(fs2us(name), hres);
    ErrorCode = hres;
    return ErrorCode;
  }

  HRESULT ScanProgress(const CDirItemsStat &st, const FString &path, bool isDir) Z7_override
  {
    if (FolderScanProgress)
      return FolderScanProgress->ScanProgress(st.NumDirs, st.NumFiles + st.NumAltStreams,
          st.GetTotalBytes(), fs2us(path), BoolToInt(isDir));
    if (FolderArchiveUpdateCallback)
      return FolderArchiveUpdateCallback->SetNumFiles(st.NumFiles);
    return S_OK;
  }
};

  
Z7_COM7F_IMF(CAgent::DoOperation(
    FStringVector *requestedPaths,
    FStringVector *processedPaths,
    CCodecs *codecs,
    int formatIndex,
    ISequentialOutStream *outArchiveStream,
    const Byte *stateActions,
    const wchar_t *sfxModule,
    IFolderArchiveUpdateCallback *updateCallback100))
{
  if (!CanUpdate())
    return E_NOTIMPL;
  
  NUpdateArchive::CActionSet actionSet;
  {
    for (unsigned i = 0; i < NUpdateArchive::NPairState::kNumValues; i++)
      actionSet.StateActions[i] = (NUpdateArchive::NPairAction::EEnum)stateActions[i];
  }

  CDirItemsCallback_AgentOut enumCallback;
  if (updateCallback100)
  {
    enumCallback.FolderArchiveUpdateCallback = updateCallback100;
    updateCallback100->QueryInterface(IID_IFolderScanProgress, (void **)&enumCallback.FolderScanProgress);
  }
  
  CDirItems dirItems;
  dirItems.Callback = &enumCallback;

  {
    FString folderPrefix = _folderPrefix;
    bool isAltStreamPrefix = false;
    #if defined(_WIN32) && !defined(UNDER_CE)
    isAltStreamPrefix = NFile::NName::IsAltStreamPrefixWithColon(fs2us(folderPrefix));
    #endif
    if (!isAltStreamPrefix)
      NFile::NName::NormalizeDirPathPrefix(folderPrefix);
    
    RINOK(dirItems.EnumerateItems2(folderPrefix, _updatePathPrefix, _names, requestedPaths))

    if (_updatePathPrefix_is_AltFolder)
    {
      #ifndef _WIN32
      return E_NOTIMPL;
      #endif
      FOR_VECTOR(i, dirItems.Items)
      {
        CDirItem &item = dirItems.Items[i];
        if (item.IsDir())
          return E_NOTIMPL;
        #ifdef _WIN32
        item.IsAltStream = true;
        #endif
      }
    }
  }

  CMyComPtr<IOutArchive> outArchive;
  
  if (GetArchive())
  {
    RINOK(GetArchive()->QueryInterface(IID_IOutArchive, (void **)&outArchive))
  }
  else
  {
    if (formatIndex < 0)
      return E_FAIL;
    RINOK(codecs->CreateOutArchive((unsigned)formatIndex, outArchive))
    
    #ifdef Z7_EXTERNAL_CODECS
    {
      CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
      outArchive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
      if (setCompressCodecsInfo)
      {
        RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs))
      }
    }
    #endif
  }

  NFileTimeType::EEnum fileTimeType = NFileTimeType::kNotDefined;
  UInt32 value;
  RINOK(outArchive->GetFileTimeType(&value))
  // we support any future fileType here.
  // 22.00:
  fileTimeType = (NFileTimeType::EEnum)value;
  /*
  switch (value)
  {
    case NFileTimeType::kWindows:
    case NFileTimeType::kDOS:
    case NFileTimeType::kUnix:
      fileTimeType = NFileTimeType::EEnum(value);
      break;
    default:
    {
      return E_FAIL;
    }
  }
  */


  CObjectVector<CArcItem> arcItems;
  if (GetArchive())
  {
    RINOK(ReadItems())
    if (_proxy2)
    {
      RINOK(EnumerateArchiveItems2(this, k_Proxy2_RootDirIndex, L"", arcItems))
      RINOK(EnumerateArchiveItems2(this, k_Proxy2_AltRootDirIndex, L":", arcItems))
    }
    else
    {
      RINOK(EnumerateArchiveItems(this, _proxy->Dirs[0], L"", arcItems))
    }
  }

  CRecordVector<CUpdatePair2> updatePairs2;

  {
    CRecordVector<CUpdatePair> updatePairs;
    GetUpdatePairInfoList(dirItems, arcItems, fileTimeType, updatePairs);
    CAgUpCallbackImp upCallback(&arcItems, updateCallback100);
    UpdateProduce(updatePairs, actionSet, updatePairs2, &upCallback);
  }

  UInt32 numFiles = 0;
  {
    FOR_VECTOR (i, updatePairs2)
      if (updatePairs2[i].NewData)
        numFiles++;
  }
  
  if (updateCallback100)
  {
    RINOK(updateCallback100->SetNumFiles(numFiles))
  }
  
  CUpdateCallbackAgent updateCallbackAgent;
  updateCallbackAgent.SetCallback(updateCallback100);
  CMyComPtr2_Create<IArchiveUpdateCallback, CArchiveUpdateCallback> updateCallback;

  updateCallback->DirItems = &dirItems;
  updateCallback->ArcItems = &arcItems;
  updateCallback->UpdatePairs = &updatePairs2;
  
  SetInArchiveInterfaces(this, updateCallback.ClsPtr());
  
  updateCallback->Callback = &updateCallbackAgent;

  CByteBuffer processedItems;
  if (processedPaths)
  {
    unsigned num = dirItems.Items.Size();
    processedItems.Alloc(num);
    for (unsigned i = 0; i < num; i++)
      processedItems[i] = 0;
    updateCallback->ProcessedItemsStatuses = processedItems;
  }

  Z7_DECL_CMyComPtr_QI_FROM(
      ISetProperties,
      setProperties, outArchive)
  if (setProperties)
  {
    if (m_PropNames.Size() == 0)
    {
      RINOK(setProperties->SetProperties(NULL, NULL, 0))
    }
    else
    {
      CRecordVector<const wchar_t *> names;
      FOR_VECTOR (i, m_PropNames)
        names.Add((const wchar_t *)m_PropNames[i]);

      CPropVariant *propValues = new CPropVariant[m_PropValues.Size()];
      try
      {
        FOR_VECTOR (i, m_PropValues)
          propValues[i] = m_PropValues[i];
        RINOK(setProperties->SetProperties(names.ConstData(), propValues, names.Size()))
      }
      catch(...)
      {
        delete []propValues;
        return E_FAIL;
      }
      delete []propValues;
    }
  }
  m_PropNames.Clear();
  m_PropValues.Clear();

  if (sfxModule != NULL)
  {
    CMyComPtr2_Create<IInStream, CInFileStream> sfxStream;
    if (!sfxStream->Open(us2fs(sfxModule)))
      return E_FAIL;
      // throw "Can't open sfx module";
    RINOK(NCompress::CopyStream(sfxStream, outArchiveStream, NULL))
  }

  const HRESULT res = outArchive->UpdateItems(outArchiveStream, updatePairs2.Size(), updateCallback);
  if (res == S_OK && processedPaths)
  {
    {
      /* OutHandler for 7z archives doesn't report compression operation for empty files.
         So we must include these files manually */
      FOR_VECTOR(i, updatePairs2)
      {
        const CUpdatePair2 &up = updatePairs2[i];
        if (up.DirIndex != -1 && up.NewData)
        {
          const CDirItem &di = dirItems.Items[(unsigned)up.DirIndex];
          if (!di.IsDir() && di.Size == 0)
            processedItems[(unsigned)up.DirIndex] = 1;
        }
      }
    }

    FOR_VECTOR (i, dirItems.Items)
      if (processedItems[i] != 0)
        processedPaths->Add(dirItems.GetPhyPath(i));
  }
  return res;
}

Z7_COM7F_IMF(CAgent::DoOperation2(
    FStringVector *requestedPaths,
    FStringVector *processedPaths,
    ISequentialOutStream *outArchiveStream,
    const Byte *stateActions, const wchar_t *sfxModule, IFolderArchiveUpdateCallback *updateCallback100))
{
  return DoOperation(requestedPaths, processedPaths, g_CodecsObj, -1, outArchiveStream, stateActions, sfxModule, updateCallback100);
}

HRESULT CAgent::CommonUpdate(ISequentialOutStream *outArchiveStream,
    unsigned numUpdateItems, IArchiveUpdateCallback *updateCallback)
{
  if (!CanUpdate())
    return E_NOTIMPL;
  CMyComPtr<IOutArchive> outArchive;
  RINOK(GetArchive()->QueryInterface(IID_IOutArchive, (void **)&outArchive))
  return outArchive->UpdateItems(outArchiveStream, numUpdateItems, updateCallback);
}

Z7_COM7F_IMF(CAgent::DeleteItems(ISequentialOutStream *outArchiveStream,
    const UInt32 *indices, UInt32 numItems,
    IFolderArchiveUpdateCallback *updateCallback100))
{
  if (!CanUpdate())
    return E_NOTIMPL;
  CRecordVector<CUpdatePair2> updatePairs;
  CUpdateCallbackAgent updateCallbackAgent;
  updateCallbackAgent.SetCallback(updateCallback100);
  CMyComPtr2_Create<IArchiveUpdateCallback, CArchiveUpdateCallback> updateCallback;
  
  CUIntVector realIndices;
  _agentFolder->GetRealIndices(indices, numItems,
      true, // includeAltStreams
      false, // includeFolderSubItemsInFlatMode, we don't want to delete subItems in Flat Mode
      realIndices);
  unsigned curIndex = 0;
  UInt32 numItemsInArchive;
  RINOK(GetArchive()->GetNumberOfItems(&numItemsInArchive))

  UString deletePath;

  for (UInt32 i = 0; i < numItemsInArchive; i++)
  {
    if (curIndex < realIndices.Size())
      if (realIndices[curIndex] == i)
      {
        RINOK(GetArc().GetItem_Path2(i, deletePath))
        RINOK(updateCallback100->DeleteOperation(deletePath))
        
        curIndex++;
        continue;
      }
    CUpdatePair2 up2;
    up2.SetAs_NoChangeArcItem(i);
    updatePairs.Add(up2);
  }
  updateCallback->UpdatePairs = &updatePairs;

  SetInArchiveInterfaces(this, updateCallback.ClsPtr());

  updateCallback->Callback = &updateCallbackAgent;
  return CommonUpdate(outArchiveStream, updatePairs.Size(), updateCallback);
}

HRESULT CAgent::CreateFolder(ISequentialOutStream *outArchiveStream,
    const wchar_t *folderName, IFolderArchiveUpdateCallback *updateCallback100)
{
  if (!CanUpdate())
    return E_NOTIMPL;
  CRecordVector<CUpdatePair2> updatePairs;
  CDirItems dirItems;
  CUpdateCallbackAgent updateCallbackAgent;
  updateCallbackAgent.SetCallback(updateCallback100);
  CMyComPtr2_Create<IArchiveUpdateCallback, CArchiveUpdateCallback> updateCallback;

  UInt32 numItemsInArchive;
  RINOK(GetArchive()->GetNumberOfItems(&numItemsInArchive))
  for (UInt32 i = 0; i < numItemsInArchive; i++)
  {
    CUpdatePair2 up2;
    up2.SetAs_NoChangeArcItem(i);
    updatePairs.Add(up2);
  }
  CUpdatePair2 up2;
  up2.NewData = up2.NewProps = true;
  up2.UseArcProps = false;
  up2.DirIndex = 0;

  updatePairs.Add(up2);

  updatePairs.ReserveDown();

  CDirItem di;

  di.SetAsDir();
  di.Size = 0;
  bool isAltStreamFolder = false;
  if (_proxy2)
    di.Name = _proxy2->GetDirPath_as_Prefix(_agentFolder->_proxyDirIndex, isAltStreamFolder);
  else
    di.Name = _proxy->GetDirPath_as_Prefix(_agentFolder->_proxyDirIndex);
  di.Name += folderName;

  NTime::GetCurUtc_FiTime(di.MTime);
  di.CTime = di.ATime = di.MTime;

  dirItems.Items.Add(di);

  updateCallback->Callback = &updateCallbackAgent;
  updateCallback->DirItems = &dirItems;
  updateCallback->UpdatePairs = &updatePairs;
  
  SetInArchiveInterfaces(this, updateCallback.ClsPtr());
  
  return CommonUpdate(outArchiveStream, updatePairs.Size(), updateCallback);
}


HRESULT CAgent::RenameItem(ISequentialOutStream *outArchiveStream,
    const UInt32 *indices, UInt32 numItems, const wchar_t *newItemName,
    IFolderArchiveUpdateCallback *updateCallback100)
{
  if (!CanUpdate())
    return E_NOTIMPL;
  if (numItems != 1)
    return E_INVALIDARG;
  if (!_archiveLink.IsOpen)
    return E_FAIL;
  CRecordVector<CUpdatePair2> updatePairs;
  CUpdateCallbackAgent updateCallbackAgent;
  updateCallbackAgent.SetCallback(updateCallback100);
  CMyComPtr2_Create<IArchiveUpdateCallback, CArchiveUpdateCallback> updateCallback;
  
  CUIntVector realIndices;
  _agentFolder->GetRealIndices(indices, numItems,
      true, // includeAltStreams
      true, // includeFolderSubItemsInFlatMode
      realIndices);

  const UInt32 ind0 = indices[0];
  const int mainRealIndex = _agentFolder->GetRealIndex(ind0);
  const UString fullPrefix = _agentFolder->GetFullPrefix(ind0);
  UString name = _agentFolder->GetName(ind0);
  // 22.00 : we normalize name
  NArchive::NItemName::NormalizeSlashes_in_FileName_for_OsPath(name);
  const UString oldItemPath = fullPrefix + name;
  const UString newItemPath = fullPrefix + newItemName;

  UStringVector newNames;

  unsigned curIndex = 0;
  UInt32 numItemsInArchive;
  RINOK(GetArchive()->GetNumberOfItems(&numItemsInArchive))
  
  for (UInt32 i = 0; i < numItemsInArchive; i++)
  {
    CUpdatePair2 up2;
    up2.SetAs_NoChangeArcItem(i);
    if (curIndex < realIndices.Size())
      if (realIndices[curIndex] == i)
      {
        up2.NewProps = true;
        RINOK(GetArc().IsItem_Anti(i, up2.IsAnti)) // it must work without that line too.

        UString oldFullPath;
        RINOK(GetArc().GetItem_Path2(i, oldFullPath))

        if (!IsPath1PrefixedByPath2(oldFullPath, oldItemPath))
          return E_INVALIDARG;

        up2.NewNameIndex = (int)newNames.Add(newItemPath + oldFullPath.Ptr(oldItemPath.Len()));
        up2.IsMainRenameItem = (mainRealIndex == (int)i);
        curIndex++;
      }
    updatePairs.Add(up2);
  }
  
  updateCallback->Callback = &updateCallbackAgent;
  updateCallback->UpdatePairs = &updatePairs;
  updateCallback->NewNames = &newNames;

  SetInArchiveInterfaces(this, updateCallback.ClsPtr());

  return CommonUpdate(outArchiveStream, updatePairs.Size(), updateCallback);
}


HRESULT CAgent::CommentItem(ISequentialOutStream *outArchiveStream,
    const UInt32 *indices, UInt32 numItems, const wchar_t *newItemName,
    IFolderArchiveUpdateCallback *updateCallback100)
{
  if (!CanUpdate())
    return E_NOTIMPL;
  if (numItems != 1)
    return E_INVALIDARG;
  if (!_archiveLink.IsOpen)
    return E_FAIL;
  
  CRecordVector<CUpdatePair2> updatePairs;
  CUpdateCallbackAgent updateCallbackAgent;
  updateCallbackAgent.SetCallback(updateCallback100);
  CMyComPtr2_Create<IArchiveUpdateCallback, CArchiveUpdateCallback> updateCallback;
  
  const int mainRealIndex = _agentFolder->GetRealIndex(indices[0]);

  if (mainRealIndex < 0)
    return E_NOTIMPL;

  UInt32 numItemsInArchive;
  RINOK(GetArchive()->GetNumberOfItems(&numItemsInArchive))

  UString newName = newItemName;
  
  for (UInt32 i = 0; i < numItemsInArchive; i++)
  {
    CUpdatePair2 up2;
    up2.SetAs_NoChangeArcItem(i);
    if ((int)i == mainRealIndex)
      up2.NewProps = true;
    updatePairs.Add(up2);
  }
  
  updateCallback->Callback = &updateCallbackAgent;
  updateCallback->UpdatePairs = &updatePairs;
  updateCallback->CommentIndex = mainRealIndex;
  updateCallback->Comment = &newName;

  SetInArchiveInterfaces(this, updateCallback.ClsPtr());

  return CommonUpdate(outArchiveStream, updatePairs.Size(), updateCallback);
}



HRESULT CAgent::UpdateOneFile(ISequentialOutStream *outArchiveStream,
    const UInt32 *indices, UInt32 numItems, const wchar_t *diskFilePath,
    IFolderArchiveUpdateCallback *updateCallback100)
{
  if (!CanUpdate())
    return E_NOTIMPL;
  CRecordVector<CUpdatePair2> updatePairs;
  CDirItems dirItems;
  CUpdateCallbackAgent updateCallbackAgent;
  updateCallbackAgent.SetCallback(updateCallback100);
  CMyComPtr2_Create<IArchiveUpdateCallback, CArchiveUpdateCallback> updateCallback;
  
  UInt32 realIndex;
  {
    CUIntVector realIndices;
    _agentFolder->GetRealIndices(indices, numItems,
        false, // includeAltStreams // we update only main stream of file
        false, // includeFolderSubItemsInFlatMode
        realIndices);
    if (realIndices.Size() != 1)
      return E_FAIL;
    realIndex = realIndices[0];
  }

  {
    FStringVector filePaths;
    filePaths.Add(us2fs(diskFilePath));
    dirItems.EnumerateItems2(FString(), UString(), filePaths, NULL);
    if (dirItems.Items.Size() != 1)
      return E_FAIL;
  }

  UInt32 numItemsInArchive;
  RINOK(GetArchive()->GetNumberOfItems(&numItemsInArchive))
  for (UInt32 i = 0; i < numItemsInArchive; i++)
  {
    CUpdatePair2 up2;
    up2.SetAs_NoChangeArcItem(i);
    if (realIndex == i)
    {
      up2.DirIndex = 0;
      up2.NewData = true;
      up2.NewProps = true;
      up2.UseArcProps = false;
    }
    updatePairs.Add(up2);
  }
  updateCallback->DirItems = &dirItems;
  updateCallback->Callback = &updateCallbackAgent;
  updateCallback->UpdatePairs = &updatePairs;
 
  SetInArchiveInterfaces(this, updateCallback.ClsPtr());
  
  updateCallback->KeepOriginalItemNames = true;
  return CommonUpdate(outArchiveStream, updatePairs.Size(), updateCallback);
}

Z7_COM7F_IMF(CAgent::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps))
{
  m_PropNames.Clear();
  m_PropValues.Clear();
  for (UInt32 i = 0; i < numProps; i++)
  {
    m_PropNames.Add(names[i]);
    m_PropValues.Add(values[i]);
  }
  return S_OK;
}
