EXTRACT_ARC_CODE CmdExtract::ExtractArchive() { Archive Arc(Cmd); if (!Arc.WOpen(ArcName)) return EXTRACT_ARC_NEXT; if (!Arc.IsArchive(true)) { #ifndef GUI mprintf(St(MNotRAR),ArcName); #endif if (CmpExt(ArcName,L"rar")) ErrHandler.SetErrorCode(RARX_WARNING); return EXTRACT_ARC_NEXT; } if (Arc.FailedHeaderDecryption) // Bad archive password. return EXTRACT_ARC_NEXT; #ifndef SFX_MODULE if (Arc.Volume && !Arc.FirstVolume) { wchar FirstVolName[NM]; VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),Arc.NewNumbering); // If several volume names from same volume set are specified // and current volume is not first in set and first volume is present // and specified too, let's skip the current volume. if (wcsicomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) && Cmd->ArcNames.Search(FirstVolName,false)) return EXTRACT_ARC_NEXT; } #endif int64 VolumeSetSize=0; // Total size of volumes after the current volume. if (Arc.Volume) { // Calculate the total size of all accessible volumes. // This size is necessary to display the correct total progress indicator. wchar NextName[NM]; wcscpy(NextName,Arc.FileName); while (true) { // First volume is already added to DataIO.TotalArcSize // in initial TotalArcSize calculation in DoExtract. // So we skip it and start from second volume. NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); FindData FD; if (FindFile::FastFind(NextName,&FD)) VolumeSetSize+=FD.Size; else break; } DataIO.TotalArcSize+=VolumeSetSize; } ExtractArchiveInit(Arc); if (*Cmd->Command=='T' || *Cmd->Command=='I') Cmd->Test=true; if (*Cmd->Command=='I') { #ifndef GUI Cmd->DisablePercentage=true; #endif } else uiStartArchiveExtract(!Cmd->Test,ArcName); Arc.ViewComment(); while (1) { size_t Size=Arc.ReadHeader(); bool Repeat=false; if (!ExtractCurrentFile(Arc,Size,Repeat)) { if (Repeat) { // If we started extraction from not first volume and need to // restart it from first, we must correct DataIO.TotalArcSize // for correct total progress display. We subtract the size // of current volume and all volumes after it and add the size // of new (first) volume. FindData OldArc,NewArc; if (FindFile::FastFind(Arc.FileName,&OldArc) && FindFile::FastFind(ArcName,&NewArc)) DataIO.TotalArcSize-=VolumeSetSize+OldArc.Size-NewArc.Size; return EXTRACT_ARC_REPEAT; } else break; } } return EXTRACT_ARC_NEXT; }
EXTRACT_ARC_CODE CmdExtract::ExtractArchive(CommandData *Cmd) { Archive Arc(Cmd); if (!Arc.WOpen(ArcName,ArcNameW)) { ErrHandler.SetErrorCode(OPEN_ERROR); return(EXTRACT_ARC_NEXT); } if (!Arc.IsArchive(true)) { #ifndef GUI mprintf(St(MNotRAR),ArcName); #endif if (CmpExt(ArcName,"rar")) ErrHandler.SetErrorCode(WARNING); return(EXTRACT_ARC_NEXT); } // archive with corrupt encrypted header can be closed in IsArchive() call if (!Arc.IsOpened()) return(EXTRACT_ARC_NEXT); #ifndef SFX_MODULE if (Arc.Volume && Arc.NotFirstVolume) { char FirstVolName[NM]; VolNameToFirstName(ArcName,FirstVolName,(Arc.NewMhd.Flags & MHD_NEWNUMBERING)!=0); // If several volume names from same volume set are specified // and current volume is not first in set and first volume is present // and specified too, let's skip the current volume. if (stricomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) && Cmd->ArcNames->Search(FirstVolName,NULL,false)) return(EXTRACT_ARC_NEXT); } #endif int64 VolumeSetSize=0; // Total size of volumes after the current volume. if (Arc.Volume) { // Calculate the total size of all accessible volumes. // This size is necessary to display the correct total progress indicator. char NextName[NM]; wchar NextNameW[NM]; strcpy(NextName,Arc.FileName); strcpyw(NextNameW,Arc.FileNameW); while (true) { // First volume is already added to DataIO.TotalArcSize // in initial TotalArcSize calculation in DoExtract. // So we skip it and start from second volume. NextVolumeName(NextName,NextNameW,ASIZE(NextName),(Arc.NewMhd.Flags & MHD_NEWNUMBERING)==0 || Arc.OldFormat); struct FindData FD; if (FindFile::FastFind(NextName,NextNameW,&FD)) VolumeSetSize+=FD.Size; else break; } DataIO.TotalArcSize+=VolumeSetSize; } ExtractArchiveInit(Cmd,Arc); if (*Cmd->Command=='T' || *Cmd->Command=='I') Cmd->Test=true; #ifndef GUI if (*Cmd->Command=='I') Cmd->DisablePercentage=true; else if (Cmd->Test) mprintf(St(MExtrTest),ArcName); else mprintf(St(MExtracting),ArcName); #endif Arc.ViewComment(); // RAR can close a corrupt encrypted archive if (!Arc.IsOpened()) return(EXTRACT_ARC_NEXT); while (1) { size_t Size=Arc.ReadHeader(); bool Repeat=false; if (!ExtractCurrentFile(Cmd,Arc,Size,Repeat)) if (Repeat) { // If we started extraction from not first volume and need to // restart it from first, we must correct DataIO.TotalArcSize // for correct total progress display. We subtract the size // of current volume and all volumes after it and add the size // of new (first) volume. struct FindData OldArc,NewArc; if (FindFile::FastFind(Arc.FileName,Arc.FileNameW,&OldArc) && FindFile::FastFind(ArcName,ArcNameW,&NewArc)) DataIO.TotalArcSize-=VolumeSetSize+OldArc.Size-NewArc.Size; return(EXTRACT_ARC_REPEAT); } else break; } return(EXTRACT_ARC_NEXT); }
bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent) { wchar ArcName[NM]; wcscpy(ArcName,Name); wchar *Num=GetVolNumPart(ArcName); while (Num>ArcName && IsDigit(*(Num-1))) Num--; wcsncpyz(Num,L"*.*",ASIZE(ArcName)-(Num-ArcName)); wchar FirstVolName[NM]; *FirstVolName=0; int64 RecFileSize=0; FindFile VolFind; VolFind.SetMask(ArcName); FindData fd; uint FoundRecVolumes=0; while (VolFind.Next(&fd)) { Wait(); Archive *Vol=new Archive(Cmd); int ItemPos=-1; if (Vol->WOpen(fd.Name)) { if (CmpExt(fd.Name,L"rev")) { uint RecNum=ReadHeader(Vol,FoundRecVolumes==0); if (RecNum!=0) { if (FoundRecVolumes==0) RecFileSize=Vol->FileLength(); ItemPos=RecNum; FoundRecVolumes++; } } else if (Vol->IsArchive(true) && (Vol->SFXSize>0 || CmpExt(fd.Name,L"rar"))) { if (!Vol->Volume && !Vol->BrokenHeader) { uiMsg(UIERROR_NOTVOLUME,ArcName); return false; } // We work with archive as with raw data file, so we do not want // to spend time to QOpen I/O redirection. Vol->QOpenUnload(); Vol->Seek(0,SEEK_SET); // RAR volume found. Get its number, store the handle in appropriate // array slot, clean slots in between if we had to grow the array. wchar *Num=GetVolNumPart(fd.Name); uint VolNum=0; for (uint K=1;Num>=fd.Name && IsDigit(*Num);K*=10,Num--) VolNum+=(*Num-'0')*K; if (VolNum==0 || VolNum>MaxVolumes) continue; size_t CurSize=RecItems.Size(); if (VolNum>CurSize) { RecItems.Alloc(VolNum); for (size_t I=CurSize;I<VolNum;I++) RecItems[I].f=NULL; } ItemPos=VolNum-1; if (*FirstVolName==0) VolNameToFirstName(fd.Name,FirstVolName,ASIZE(FirstVolName),true); } } if (ItemPos==-1) delete Vol; // Skip found file, it is not RAR or REV volume. else if ((uint)ItemPos<RecItems.Size()) // Check if found more REV than needed. { // Store found RAR or REV volume. RecVolItem *Item=RecItems+ItemPos; Item->f=Vol; Item->New=false; wcsncpyz(Item->Name,fd.Name,ASIZE(Item->Name)); } } if (!Silent || FoundRecVolumes!=0) uiMsg(UIMSG_RECVOLFOUND,FoundRecVolumes); if (FoundRecVolumes==0) return false; uiMsg(UIMSG_RECVOLCALCCHECKSUM); MissingVolumes=0; for (uint I=0;I<TotalCount;I++) { RecVolItem *Item=&RecItems[I]; if (Item->f!=NULL) { uiMsg(UIMSG_STRING,Item->Name); uint RevCRC; CalcFileSum(Item->f,&RevCRC,NULL,Cmd->Threads,INT64NDF,CALCFSUM_CURPOS); Item->Valid=RevCRC==Item->CRC; if (!Item->Valid) { uiMsg(UIMSG_CHECKSUM,Item->Name); // Close only corrupt REV volumes here. We'll close and rename corrupt // RAR volumes later, if we'll know that recovery is possible. if (I>=DataCount) { Item->f->Close(); Item->f=NULL; FoundRecVolumes--; } } } if (I<DataCount && (Item->f==NULL || !Item->Valid)) MissingVolumes++; } uiMsg(UIMSG_RECVOLMISSING,MissingVolumes); if (MissingVolumes==0) { uiMsg(UIERROR_RECVOLALLEXIST); return false; } if (MissingVolumes>FoundRecVolumes) { uiMsg(UIERROR_RECVOLFOUND,FoundRecVolumes); // Intentionally not displayed in console mode. uiMsg(UIERROR_RECVOLCANNOTFIX); return false; } uiMsg(UIMSG_RECONSTRUCTING); // Create missing and rename bad volumes. uint64 MaxVolSize=0; for (uint I=0;I<DataCount;I++) { RecVolItem *Item=&RecItems[I]; if (Item->FileSize>MaxVolSize) MaxVolSize=Item->FileSize; if (Item->f!=NULL && !Item->Valid) { Item->f->Close(); wchar NewName[NM]; wcscpy(NewName,Item->Name); wcscat(NewName,L".bad"); uiMsg(UIMSG_BADARCHIVE,Item->Name); uiMsg(UIMSG_RENAMING,Item->Name,NewName); RenameFile(Item->Name,NewName); delete Item->f; Item->f=NULL; } if (Item->New=(Item->f==NULL)) { wcsncpyz(Item->Name,FirstVolName,ASIZE(Item->Name)); uiMsg(UIMSG_CREATING,Item->Name); uiMsg(UIEVENT_NEWARCHIVE,Item->Name); File *NewVol=new File; bool UserReject; if (!FileCreate(Cmd,NewVol,Item->Name,ASIZE(Item->Name),&UserReject)) { if (!UserReject) ErrHandler.CreateErrorMsg(Item->Name); ErrHandler.Exit(UserReject ? RARX_USERBREAK:RARX_CREATE); } NewVol->Prealloc(Item->FileSize); Item->f=NewVol; Item->New=true; } NextVolumeName(FirstVolName,ASIZE(FirstVolName),false); } int64 ProcessedSize=0; #ifndef GUI int LastPercent=-1; mprintf(L" "); #endif // Even though we already preliminary calculated missing volume number, // let's do it again now, when we have the final and exact information. MissingVolumes=0; ValidFlags=new bool[TotalCount]; for (uint I=0;I<TotalCount;I++) { ValidFlags[I]=RecItems[I].f!=NULL && !RecItems[I].New; if (I<DataCount && !ValidFlags[I]) MissingVolumes++; } // Size of per file buffer. RecBufferSize=TotalBufferSize/MissingVolumes; if ((RecBufferSize&1)==1) // Must be even for our RS16 codec. RecBufferSize--; #ifdef USE_SSE RecBufferSize&=~(SSE_ALIGNMENT-1); // Align for SSE. #endif uint *Data=new uint[TotalCount]; RSCoder16 RS; if (!RS.Init(DataCount,RecCount,ValidFlags)) return false; // Should not happen, we check parameter validity above. RealReadBuffer=new byte[RecBufferSize+SSE_ALIGNMENT]; byte *ReadBuf=(byte *)ALIGN_VALUE(RealReadBuffer,SSE_ALIGNMENT); while (true) { Wait(); int MaxRead=0; for (uint I=0,J=DataCount;I<DataCount;I++) { uint VolNum=I; if (!ValidFlags[I]) // If next RAR volume is missing or invalid. { while (!ValidFlags[J]) // Find next valid REV volume. J++; VolNum=J++; // Use next valid REV volume data instead of RAR. } RecVolItem *Item=RecItems+VolNum; byte *B=&ReadBuf[0]; int ReadSize=0; if (Item->f!=NULL && !Item->New) ReadSize=Item->f->Read(B,RecBufferSize); if (ReadSize!=RecBufferSize) memset(B+ReadSize,0,RecBufferSize-ReadSize); if (ReadSize>MaxRead) MaxRead=ReadSize; // We can have volumes of different size. Let's use data chunk // for largest volume size. uint DataToProcess=(uint)Min(RecBufferSize,MaxVolSize-ProcessedSize); ProcessRS(Cmd,I,B,DataToProcess,false); } if (MaxRead==0) break; for (uint I=0,J=0;I<DataCount;I++) if (!ValidFlags[I]) { RecVolItem *Item=RecItems+I; size_t WriteSize=(size_t)Min(MaxRead,Item->FileSize); Item->f->Write(Buf+(J++)*RecBufferSize,WriteSize); Item->FileSize-=WriteSize; } int CurPercent=ToPercent(ProcessedSize,RecFileSize); if (!Cmd->DisablePercentage && CurPercent!=LastPercent) { uiProcessProgress("RV",ProcessedSize,RecFileSize); LastPercent=CurPercent; } ProcessedSize+=MaxRead; } for (uint I=0;I<TotalCount;I++) if (RecItems[I].f!=NULL) RecItems[I].f->Close(); delete[] ValidFlags; delete[] Data; #if !defined(GUI) && !defined(SILENT) if (!Cmd->DisablePercentage) mprintf(L"\b\b\b\b100%%"); if (!Silent && !Cmd->DisableDone) mprintf(St(MDone)); #endif return true; }
EXTRACT_ARC_CODE CmdExtract::ExtractArchive(CommandData *Cmd) { Archive Arc(Cmd); if (!Arc.WOpen(ArcName,ArcNameW)) { ErrHandler.SetErrorCode(OPEN_ERROR); return(EXTRACT_ARC_NEXT); } if (!Arc.IsArchive(true)) { #ifndef GUI mprintf(St(MNotRAR),ArcName); #endif if (CmpExt(ArcName,"rar")) ErrHandler.SetErrorCode(WARNING); return(EXTRACT_ARC_NEXT); } if (!Arc.IsOpened()) return(EXTRACT_ARC_NEXT); #ifndef SFX_MODULE if (Arc.Volume && Arc.NotFirstVolume) { char FirstVolName[NM]; VolNameToFirstName(ArcName,FirstVolName,(Arc.NewMhd.Flags & MHD_NEWNUMBERING)); if (stricomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) && Cmd->ArcNames->Search(FirstVolName,NULL,false)) return(EXTRACT_ARC_NEXT); } #endif ExtractArchiveInit(Cmd,Arc); if (*Cmd->Command=='T' || *Cmd->Command=='I') Cmd->Test=true; #ifndef GUI if (*Cmd->Command=='I') Cmd->DisablePercentage=true; else if (Cmd->Test) mprintf(St(MExtrTest),ArcName); else mprintf(St(MExtracting),ArcName); #endif Arc.ViewComment(); while (1) { int Size=Arc.ReadHeader(); bool Repeat=false; if (!ExtractCurrentFile(Cmd,Arc,Size,Repeat)) if (Repeat) { return(EXTRACT_ARC_REPEAT); } else break; } return(EXTRACT_ARC_NEXT); }