Ejemplo n.º 1
0
static HRESULT WriteVolumeHeader(COutArchive &archive, CFileItem &file, const CUpdateOptions &options)
{
  CCoderInfo coder;
  coder.NumInStreams = coder.NumOutStreams = 1;
  coder.MethodID = k_Copy;
  
  CFolder folder;
  folder.Coders.Add(coder);
  folder.PackStreams.Add(0);
  
  CNum numUnPackStreams = 0;
  if (file.UnPackSize != 0)
  {
    file.IsFileCRCDefined = true;
    file.HasStream = true;
    numUnPackStreams++;
  }
  else
  {
    throw 1;
    file.IsFileCRCDefined = false;
    file.HasStream = false;
  }
  folder.UnPackSizes.Add(file.UnPackSize);
  
  CArchiveDatabase newDatabase;
  newDatabase.Files.Add(file);
  newDatabase.Folders.Add(folder);
  newDatabase.NumUnPackStreamsVector.Add(numUnPackStreams);
  newDatabase.PackSizes.Add(file.UnPackSize);
  newDatabase.PackCRCsDefined.Add(false);
  newDatabase.PackCRCs.Add(file.FileCRC);
  
  return archive.WriteDatabase(newDatabase, 
      options.HeaderMethod, 
      false, 
      false);
}
Ejemplo n.º 2
0
STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
    IArchiveUpdateCallback *updateCallback)
{
  COM_TRY_BEGIN

  const CArchiveDatabaseEx *db = 0;
  #ifdef _7Z_VOL
  if (_volumes.Size() > 1)
    return E_FAIL;
  const CVolume *volume = 0;
  if (_volumes.Size() == 1)
  {
    volume = &_volumes.Front();
    db = &volume->Database;
  }
  #else
  if (_inStream != 0)
    db = &_db;
  #endif

  CObjectVector<CUpdateItem> updateItems;
  
  for (UInt32 i = 0; i < numItems; i++)
  {
    Int32 newData, newProps;
    UInt32 indexInArchive;
    if (!updateCallback)
      return E_FAIL;
    RINOK(updateCallback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArchive));
    CUpdateItem ui;
    ui.NewProps = IntToBool(newProps);
    ui.NewData = IntToBool(newData);
    ui.IndexInArchive = indexInArchive;
    ui.IndexInClient = i;
    ui.IsAnti = false;
    ui.Size = 0;

    if (ui.IndexInArchive != -1)
    {
      if (db == 0 || ui.IndexInArchive >= db->Files.Size())
        return E_INVALIDARG;
      const CFileItem &fi = db->Files[ui.IndexInArchive];
      ui.Name = fi.Name;
      ui.IsDir = fi.IsDir;
      ui.Size = fi.Size;
      ui.IsAnti = db->IsItemAnti(ui.IndexInArchive);
      
      ui.CTimeDefined = db->CTime.GetItem(ui.IndexInArchive, ui.CTime);
      ui.ATimeDefined = db->ATime.GetItem(ui.IndexInArchive, ui.ATime);
      ui.MTimeDefined = db->MTime.GetItem(ui.IndexInArchive, ui.MTime);
    }

    if (ui.NewProps)
    {
      bool nameIsDefined;
      bool folderStatusIsDefined;
      {
        NCOM::CPropVariant prop;
        RINOK(updateCallback->GetProperty(i, kpidAttrib, &prop));
        if (prop.vt == VT_EMPTY)
          ui.AttribDefined = false;
        else if (prop.vt != VT_UI4)
          return E_INVALIDARG;
        else
        {
          ui.Attrib = prop.ulVal;
          ui.AttribDefined = true;
        }
      }
      
      // we need MTime to sort files.
      RINOK(GetTime(updateCallback, i, WriteCTime, kpidCTime, ui.CTime, ui.CTimeDefined));
      RINOK(GetTime(updateCallback, i, WriteATime, kpidATime, ui.ATime, ui.ATimeDefined));
      RINOK(GetTime(updateCallback, i, true,       kpidMTime, ui.MTime, ui.MTimeDefined));

      {
        NCOM::CPropVariant prop;
        RINOK(updateCallback->GetProperty(i, kpidPath, &prop));
        if (prop.vt == VT_EMPTY)
          nameIsDefined = false;
        else if (prop.vt != VT_BSTR)
          return E_INVALIDARG;
        else
        {
          ui.Name = NItemName::MakeLegalName(prop.bstrVal);
          nameIsDefined = true;
        }
      }
      {
        NCOM::CPropVariant prop;
        RINOK(updateCallback->GetProperty(i, kpidIsDir, &prop));
        if (prop.vt == VT_EMPTY)
          folderStatusIsDefined = false;
        else if (prop.vt != VT_BOOL)
          return E_INVALIDARG;
        else
        {
          ui.IsDir = (prop.boolVal != VARIANT_FALSE);
          folderStatusIsDefined = true;
        }
      }

      {
        NCOM::CPropVariant prop;
        RINOK(updateCallback->GetProperty(i, kpidIsAnti, &prop));
        if (prop.vt == VT_EMPTY)
          ui.IsAnti = false;
        else if (prop.vt != VT_BOOL)
          return E_INVALIDARG;
        else
          ui.IsAnti = (prop.boolVal != VARIANT_FALSE);
      }

      if (ui.IsAnti)
      {
        ui.AttribDefined = false;

        ui.CTimeDefined = false;
        ui.ATimeDefined = false;
        ui.MTimeDefined = false;
        
        ui.Size = 0;
      }

      if (!folderStatusIsDefined && ui.AttribDefined)
        ui.SetDirStatusFromAttrib();
    }

    if (ui.NewData)
    {
      NCOM::CPropVariant prop;
      RINOK(updateCallback->GetProperty(i, kpidSize, &prop));
      if (prop.vt != VT_UI8)
        return E_INVALIDARG;
      ui.Size = (UInt64)prop.uhVal.QuadPart;
      if (ui.Size != 0 && ui.IsAnti)
        return E_INVALIDARG;
    }
    updateItems.Add(ui);
  }

  CCompressionMethodMode methodMode, headerMethod;
  RINOK(SetCompressionMethod(methodMode, headerMethod));
  #ifdef COMPRESS_MT
  methodMode.NumThreads = _numThreads;
  headerMethod.NumThreads = 1;
  #endif

  CMyComPtr<ICryptoGetTextPassword2> getPassword2;
  updateCallback->QueryInterface(IID_ICryptoGetTextPassword2, (void **)&getPassword2);

  if (getPassword2)
  {
    CMyComBSTR password;
    Int32 passwordIsDefined;
    RINOK(getPassword2->CryptoGetTextPassword2(&passwordIsDefined, &password));
    methodMode.PasswordIsDefined = IntToBool(passwordIsDefined);
    if (methodMode.PasswordIsDefined)
      methodMode.Password = password;
  }
  else
    methodMode.PasswordIsDefined = false;

  bool compressMainHeader = _compressHeaders;  // check it

  bool encryptHeaders = false;

  if (methodMode.PasswordIsDefined)
  {
    if (_encryptHeadersSpecified)
      encryptHeaders = _encryptHeaders;
    #ifndef _NO_CRYPTO
    else
      encryptHeaders = _passwordIsDefined;
    #endif
    compressMainHeader = true;
    if (encryptHeaders)
    {
      headerMethod.PasswordIsDefined = methodMode.PasswordIsDefined;
      headerMethod.Password = methodMode.Password;
    }
  }

  if (numItems < 2)
    compressMainHeader = false;

  CUpdateOptions options;
  options.Method = &methodMode;
  options.HeaderMethod = (_compressHeaders || encryptHeaders) ? &headerMethod : 0;
  options.UseFilters = _level != 0 && _autoFilter;
  options.MaxFilter = _level >= 8;

  options.HeaderOptions.CompressMainHeader = compressMainHeader;
  options.HeaderOptions.WriteCTime = WriteCTime;
  options.HeaderOptions.WriteATime = WriteATime;
  options.HeaderOptions.WriteMTime = WriteMTime;
  
  options.NumSolidFiles = _numSolidFiles;
  options.NumSolidBytes = _numSolidBytes;
  options.SolidExtension = _solidExtension;
  options.RemoveSfxBlock = _removeSfxBlock;
  options.VolumeMode = _volumeMode;

  COutArchive archive;
  CArchiveDatabase newDatabase;

  CMyComPtr<ICryptoGetTextPassword> getPassword;
  updateCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getPassword);
  
  HRESULT res = Update(
      EXTERNAL_CODECS_VARS
      #ifdef _7Z_VOL
      volume ? volume->Stream: 0,
      volume ? db : 0,
      #else
      _inStream,
      db,
      #endif
      updateItems,
      archive, newDatabase, outStream, updateCallback, options
      #ifndef _NO_CRYPTO
      , getPassword
      #endif
      );

  RINOK(res);

  updateItems.ClearAndFree();

  return archive.WriteDatabase(EXTERNAL_CODECS_VARS
      newDatabase, options.HeaderMethod, options.HeaderOptions);

  COM_TRY_END
}
Ejemplo n.º 3
0
static HRESULT Update2(
    DECL_EXTERNAL_CODECS_LOC_VARS
    IInStream *inStream,
    const CArchiveDatabaseEx *database,
    const CObjectVector<CUpdateItem> &updateItems,
    ISequentialOutStream *seqOutStream,
    IArchiveUpdateCallback *updateCallback,
    const CUpdateOptions &options)
{
  UInt64 numSolidFiles = options.NumSolidFiles;
  if (numSolidFiles == 0)
    numSolidFiles = 1;
  /*
  CMyComPtr<IOutStream> outStream;
  RINOK(seqOutStream->QueryInterface(IID_IOutStream, (void **)&outStream));
  if (!outStream)
    return E_NOTIMPL;
  */

  UInt64 startBlockSize = database != 0 ? database->ArchiveInfo.StartPosition: 0;
  if (startBlockSize > 0 && !options.RemoveSfxBlock)
  {
    RINOK(WriteRange(inStream, seqOutStream, 0, startBlockSize, NULL));
  }

  CRecordVector<int> fileIndexToUpdateIndexMap;
  if (database != 0)
  {
    fileIndexToUpdateIndexMap.Reserve(database->Files.Size());
    for (int i = 0; i < database->Files.Size(); i++)
      fileIndexToUpdateIndexMap.Add(-1);
  }
  int i;
  for(i = 0; i < updateItems.Size(); i++)
  {
    int index = updateItems[i].IndexInArchive;
    if (index != -1)
      fileIndexToUpdateIndexMap[index] = i;
  }

  CRecordVector<int> folderRefs;
  if (database != 0)
  {
    for(i = 0; i < database->Folders.Size(); i++)
    {
      CNum indexInFolder = 0;
      CNum numCopyItems = 0;
      CNum numUnPackStreams = database->NumUnPackStreamsVector[i];
      for (CNum fileIndex = database->FolderStartFileIndex[i];
      indexInFolder < numUnPackStreams; fileIndex++)
      {
        if (database->Files[fileIndex].HasStream)
        {
          indexInFolder++;
          int updateIndex = fileIndexToUpdateIndexMap[fileIndex];
          if (updateIndex >= 0)
            if (!updateItems[updateIndex].NewData)
              numCopyItems++;
        }
      }
      if (numCopyItems != numUnPackStreams && numCopyItems != 0)
        return E_NOTIMPL; // It needs repacking !!!
      if (numCopyItems > 0)
        folderRefs.Add(i);
    }
    folderRefs.Sort(CompareFolderRefs, (void *)database);
  }

  CArchiveDatabase newDatabase;

  ////////////////////////////

  COutArchive archive;
  RINOK(archive.Create(seqOutStream, false));
  RINOK(archive.SkeepPrefixArchiveHeader());
  UInt64 complexity = 0;
  for(i = 0; i < folderRefs.Size(); i++)
    complexity += database->GetFolderFullPackSize(folderRefs[i]);
  UInt64 inSizeForReduce = 0;
  for(i = 0; i < updateItems.Size(); i++)
  {
    const CUpdateItem &updateItem = updateItems[i];
    if (updateItem.NewData)
    {
      complexity += updateItem.Size;
      if (numSolidFiles == 1)
      {
        if (updateItem.Size > inSizeForReduce)
          inSizeForReduce = updateItem.Size;
      }
      else
        inSizeForReduce += updateItem.Size;
    }
  }
  RINOK(updateCallback->SetTotal(complexity));
  complexity = 0;
  RINOK(updateCallback->SetCompleted(&complexity));


  CLocalProgress *lps = new CLocalProgress;
  CMyComPtr<ICompressProgressInfo> progress = lps;
  lps->Init(updateCallback, true);

  /////////////////////////////////////////
  // Write Copy Items

  for(i = 0; i < folderRefs.Size(); i++)
  {
    int folderIndex = folderRefs[i];
    
    lps->ProgressOffset = complexity;
    UInt64 packSize = database->GetFolderFullPackSize(folderIndex);
    RINOK(WriteRange(inStream, archive.SeqStream,
        database->GetFolderStreamPos(folderIndex, 0), packSize, progress));
    complexity += packSize;
    
    const CFolder &folder = database->Folders[folderIndex];
    CNum startIndex = database->FolderStartPackStreamIndex[folderIndex];
    for (int j = 0; j < folder.PackStreams.Size(); j++)
    {
      newDatabase.PackSizes.Add(database->PackSizes[startIndex + j]);
      // newDatabase.PackCRCsDefined.Add(database.PackCRCsDefined[startIndex + j]);
      // newDatabase.PackCRCs.Add(database.PackCRCs[startIndex + j]);
    }
    newDatabase.Folders.Add(folder);

    CNum numUnPackStreams = database->NumUnPackStreamsVector[folderIndex];
    newDatabase.NumUnPackStreamsVector.Add(numUnPackStreams);

    CNum indexInFolder = 0;
    for (CNum fi = database->FolderStartFileIndex[folderIndex];
        indexInFolder < numUnPackStreams; fi++)
    {
      CFileItem file = database->Files[fi];
      if (file.HasStream)
      {
        indexInFolder++;
        int updateIndex = fileIndexToUpdateIndexMap[fi];
        if (updateIndex >= 0)
        {
          const CUpdateItem &updateItem = updateItems[updateIndex];
          if (updateItem.NewProperties)
          {
            CFileItem file2;
            FromUpdateItemToFileItem(updateItem, file2);
            file2.UnPackSize = file.UnPackSize;
            file2.FileCRC = file.FileCRC;
            file2.IsFileCRCDefined = file.IsFileCRCDefined;
            file2.HasStream = file.HasStream;
            file = file2;
          }
        }
        newDatabase.Files.Add(file);
      }
    }
  }

  /////////////////////////////////////////
  // Compress New Files

  CObjectVector<CSolidGroup> groups;
  SplitFilesToGroups(*options.Method, options.UseFilters, options.MaxFilter, 
      updateItems, groups);

  const UInt32 kMinReduceSize = (1 << 16);
  if (inSizeForReduce < kMinReduceSize)
    inSizeForReduce = kMinReduceSize;

  for (int groupIndex = 0; groupIndex < groups.Size(); groupIndex++)
  {
    const CSolidGroup &group = groups[groupIndex];
    int numFiles = group.Indices.Size();
    if (numFiles == 0)
      continue;
    CRecordVector<CRefItem> refItems;
    refItems.Reserve(numFiles);
    bool sortByType = (numSolidFiles > 1);
    for (i = 0; i < numFiles; i++)
      refItems.Add(CRefItem(group.Indices[i], updateItems[group.Indices[i]], sortByType));
    refItems.Sort(CompareUpdateItems, (void *)&sortByType);
    
    CRecordVector<UInt32> indices;
    indices.Reserve(numFiles);

    for (i = 0; i < numFiles; i++)
    {
      UInt32 index = refItems[i].Index;
      indices.Add(index);
      /*
      const CUpdateItem &updateItem = updateItems[index];
      CFileItem file;
      if (updateItem.NewProperties)
        FromUpdateItemToFileItem(updateItem, file);
      else
        file = database.Files[updateItem.IndexInArchive];
      if (file.IsAnti || file.IsDirectory)
        return E_FAIL;
      newDatabase.Files.Add(file);
      */
    }
    
    CEncoder encoder(group.Method);

    for (i = 0; i < numFiles;)
    {
      UInt64 totalSize = 0;
      int numSubFiles;
      UString prevExtension;
      for (numSubFiles = 0; i + numSubFiles < numFiles && 
          numSubFiles < numSolidFiles; numSubFiles++)
      {
        const CUpdateItem &updateItem = updateItems[indices[i + numSubFiles]];
        totalSize += updateItem.Size;
        if (totalSize > options.NumSolidBytes)
          break;
        if (options.SolidExtension)
        {
          UString ext = updateItem.GetExtension();
          if (numSubFiles == 0)
            prevExtension = ext;
          else
            if (ext.CompareNoCase(prevExtension) != 0)
              break;
        }
      }
      if (numSubFiles < 1)
        numSubFiles = 1;

      CFolderInStream *inStreamSpec = new CFolderInStream;
      CMyComPtr<ISequentialInStream> solidInStream(inStreamSpec);
      inStreamSpec->Init(updateCallback, &indices[i], numSubFiles);
      
      CFolder folderItem;

      int startPackIndex = newDatabase.PackSizes.Size();
      RINOK(encoder.Encode(
          EXTERNAL_CODECS_LOC_VARS
          solidInStream, NULL, &inSizeForReduce, folderItem, 
          archive.SeqStream, newDatabase.PackSizes, progress));

      for (; startPackIndex < newDatabase.PackSizes.Size(); startPackIndex++)
        lps->OutSize += newDatabase.PackSizes[startPackIndex];

      lps->InSize += folderItem.GetUnPackSize();
      // for()
      // newDatabase.PackCRCsDefined.Add(false);
      // newDatabase.PackCRCs.Add(0);
      
      newDatabase.Folders.Add(folderItem);
      
      CNum numUnPackStreams = 0;
      for (int subIndex = 0; subIndex < numSubFiles; subIndex++)
      {
        const CUpdateItem &updateItem = updateItems[indices[i + subIndex]];
        CFileItem file;
        if (updateItem.NewProperties)
          FromUpdateItemToFileItem(updateItem, file);
        else
          file = database->Files[updateItem.IndexInArchive];
        if (file.IsAnti || file.IsDirectory)
          return E_FAIL;
        
        /*
        CFileItem &file = newDatabase.Files[
              startFileIndexInDatabase + i + subIndex];
        */
        if (!inStreamSpec->Processed[subIndex])
        {
          continue;
          // file.Name += L".locked";
        }

        file.FileCRC = inStreamSpec->CRCs[subIndex];
        file.UnPackSize = inStreamSpec->Sizes[subIndex];
        if (file.UnPackSize != 0)
        {
          file.IsFileCRCDefined = true;
          file.HasStream = true;
          numUnPackStreams++;
        }
        else
        {
          file.IsFileCRCDefined = false;
          file.HasStream = false;
        }
        newDatabase.Files.Add(file);
      }
      // numUnPackStreams = 0 is very bad case for locked files
      // v3.13 doesn't understand it.
      newDatabase.NumUnPackStreamsVector.Add(numUnPackStreams);
      i += numSubFiles;
    }
  }

  {
    /////////////////////////////////////////
    // Write Empty Files & Folders
    
    CRecordVector<int> emptyRefs;
    for(i = 0; i < updateItems.Size(); i++)
    {
      const CUpdateItem &updateItem = updateItems[i];
      if (updateItem.NewData)
      {
        if (updateItem.HasStream())
          continue;
      }
      else
        if (updateItem.IndexInArchive != -1)
          if (database->Files[updateItem.IndexInArchive].HasStream)
            continue;
      emptyRefs.Add(i);
    }
    emptyRefs.Sort(CompareEmptyItems, (void *)&updateItems);
    for(i = 0; i < emptyRefs.Size(); i++)
    {
      const CUpdateItem &updateItem = updateItems[emptyRefs[i]];
      CFileItem file;
      if (updateItem.NewProperties)
        FromUpdateItemToFileItem(updateItem, file);
      else
        file = database->Files[updateItem.IndexInArchive];
      newDatabase.Files.Add(file);
    }
  }
    
  /*
  if (newDatabase.Files.Size() != updateItems.Size())
    return E_FAIL;
  */

  return archive.WriteDatabase(EXTERNAL_CODECS_LOC_VARS
      newDatabase, options.HeaderMethod, options.HeaderOptions);
}
Ejemplo n.º 4
0
// Create a new 7z archive for each folder contained in the archive to be
// exploded.
HRESULT ExplodeArchives(CCodecs *codecs, const CIntVector &formatIndices,
	bool stdInMode,
	UStringVector &arcPaths, UStringVector &arcPathsFull,
	UString& outputPath, UInt64 maxDepth, UInt64 &numErrors)
{
	int numArcs = arcPaths.Size();
	for (int i = 0; i < numArcs; i++)
	{
		UString archivePath = arcPaths[i];
		
		/*UString outputPath = arcPaths[i];
		outputPath.Replace(L'\\', L'/'); // linux and windows consistent
		const UString archiveName = StripFile(outputPath);
		outputPath.Empty();*/
		archivePath.Replace(L'\\', L'/'); // linux, windows and archive consistent
		outputPath.Replace(L'\\', L'/'); 
		FixPathFormat(outputPath);
		const UString archiveName = GetFileFromPath(archivePath);

		g_StdOut << "Outputting into : " << outputPath << endl;

		UInt64 arcPackSize = 0;
		if (!stdInMode)
		{
			NFile::NFind::CFileInfoW fi;
			if (!fi.Find(archivePath) || fi.IsDir())
			{
				SHOW_ERROR("is not a file.");
				continue;
			}
			arcPackSize = fi.Size;
		}

		g_StdOut << endl << "Exploding : " << archivePath << endl << endl;

		CArchiveLink archiveLink;

		COpenCallbackConsole openCallback;
		openCallback.OutStream = &g_StdOut;

#ifndef _NO_CRYPTO
		openCallback.PasswordIsDefined = false;
#endif

		HRESULT result = archiveLink.Open2(codecs, formatIndices, stdInMode, NULL, archivePath, &openCallback);
	
		if (result != S_OK)
		{
			if (result == E_ABORT)
				return result;
			g_StdOut << endl << "Error: " << archivePath << ": ";
			if (result == S_FALSE)
				g_StdOut << "Can not open file as archive";
			else if (result == E_OUTOFMEMORY)
				g_StdOut << "Can't allocate required memory";
			else
				g_StdOut << NError::MyFormatMessage(result);
			g_StdOut << endl;
			numErrors++;
			continue;
		}

		// remove other files names if multi-volume
		if (!stdInMode) {
			for (int v = 0; v < archiveLink.VolumePaths.Size(); v++)
			{
				int index = arcPathsFull.FindInSorted(archiveLink.VolumePaths[v]);
				if (index >= 0 && index > i)
				{
					arcPaths.Delete(index);
					arcPathsFull.Delete(index);
					numArcs = arcPaths.Size();
				}
			}
		}

		// don't support multi volume because i have to reopen the stream
		if (archiveLink.VolumePaths.Size() != 1) {
			SHOW_ERROR("Exploding multi-volume archives isn't supported.");
			continue;
		}

		bool szArchive = true;
		for (int x = 0; x < archiveLink.Arcs.Size(); x++)
		{
			const UString szName = L"7z";
			const CArc &arc = archiveLink.Arcs[x];
			if (codecs->Formats[arc.FormatIndex].Name != szName) {
				szArchive = false;
				break;
			}
		}

		if (!szArchive) {
			SHOW_ERROR("Only 7z archives can be exploded.");
			continue;
		}

		// Only 7z is supported, and it's been checked
		using namespace NArchive::N7z;
		IInArchive* inArc = archiveLink.GetArchive();
		CHandler* szHandler = (CHandler*)inArc;

		// not a fan of having to reopen the file..
		CInFileStream* _inStream = new CInFileStream;
		CMyComPtr<CInFileStream> inStream(_inStream);
		if (!inStream->Open(archivePath)) {
			SHOW_ERROR("Cannot be opened for reading.");
			continue;
		}	

		// Explode the archive into each folder
		CObjectVector<szExplodeData> exploded;
		szHandler->Explode(exploded, maxDepth);
	
		if (exploded.Size() == 0) {
			SHOW_ERROR("Empty archive!");
			continue;
		}

		// should make a struct that gets passed to Explode.. 
		// something like
		/*
		 * struct a {
		 *	CArchiveDatabase newDatabase;
		 *	CRecordVector<UInt64> folderSizes, folderPositions	
		 * };
		 * CObjectVector<a> exploded;
		 * szHandler->Explode(exploded);
		 */
		
		// Save each folder as a new 7z archive
		for (int x = 0; x < exploded.Size(); x++) {		
			UString relativeFilePath; // relative to archive
			UString fileName;
			CArchiveDatabase& newDatabase = exploded[x].newDatabase;
			szExplodeData& explodeData = exploded[x];

			// each exploded archive will only have a single folder.
			// no longer true. need to make sure the selected file
			// is the highest in the dir tree. could make 7zhandler
			// give us this info i guess.
			if (newDatabase.Files.Size() > 0) {
				relativeFilePath = newDatabase.Files[0].Name;
				if (!newDatabase.Files[0].IsDir) {
					fileName = GetFileFromPath(relativeFilePath);
					StripFile(relativeFilePath);
				}
			}

			//g_StdOut << "Relative path " << relativeFilePath << endl;
			//g_StdOut << "Archive " << archivePath << endl;

			UString folderOutPath = outputPath + relativeFilePath;
			if (relativeFilePath.Length() != 0) {
				bool b = NWindows::NFile::NDirectory::CreateComplexDirectory(folderOutPath);
				if (!b) g_StdOut << "Couldn't create directory " << folderOutPath << endl;
				//relativeFilePath.Insert(folderOutPath.Length(), L'/');
			}

			std::wstringstream sstream;
			sstream << folderOutPath.GetBuffer();
			
			if (newDatabase.Files.Size() == 1) // can use file names
				sstream << fileName.GetBuffer();
			else // use folder as name 
				sstream << archiveName.GetBuffer() << L"_folder_" << x;
			sstream << ".7z";
			
			g_StdOut << "Saving as '" << sstream.str().c_str() << "'" << endl;

			COutFileStream* _outstream = new COutFileStream;
			CMyComPtr<COutFileStream> outstream(_outstream);
			outstream->Create(sstream.str().c_str(), true);

			COutArchive out;
			out.Create(outstream, false);
			out.SkipPrefixArchiveHeader();
	
			for (int folderIndex = 0; folderIndex < newDatabase.Folders.Size(); 
				folderIndex++)
			{
				UInt64 folderLen = explodeData.folderSizes[folderIndex];
				UInt64 folderStartPackPos = explodeData.folderPositions[folderIndex];

				// write actual data
				RINOK(WriteRange(inStream, out.SeqStream, 
					folderStartPackPos, folderLen, NULL));
			}			

			CCompressionMethodMode method, headerMethod;
			szHandler->SetCompressionMethod(method, headerMethod);

			CHeaderOptions headerOptions;
			headerOptions.CompressMainHeader = true;

			out.WriteDatabase(newDatabase, &headerMethod, headerOptions);
			out.Close();

/*#ifdef ENV_UNIX
			// Create a symlink for each file in the folder.
			// This makes it seem as though each file is individually accessible.
			for (int fileIndex = 0; fileIndex < newDatabase.Files.Size(); fileIndex++) {
				AString oldfile, newfile;
				UString woldfile = sstream.str().c_str();
				UString wnewfile = outputPath + relativeFilePath + newDatabase.Files[fileIndex].Name + L".7z";
				ConvertUnicodeToUTF8(woldfile, oldfile);
				ConvertUnicodeToUTF8(wnewfile, newfile);
				const char* link_to = oldfile.GetBuffer();
				const char* link_name = newfile.GetBuffer();
				unlink(link_name);// should ask user
				//g_StdOut << "Creating symlink to '" << link_to << "' called '" << link_name << "'" << endl;
				int status = symlink(link_to, link_name);
				if (status == -1) {
					AString error = "Couldn't create symlink for '";
					error += newfile;
					error += "'";
					SHOW_ERROR(error);
					g_StdOut << "Error: " << errno << endl;
					
				}
			}
#endif*/
		}		

		archiveLink.Close(); // not needed but oh well
	}

	return S_OK;
}