123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544 |
- // Buffer size for all volumes involved.
- static const size_t TotalBufferSize=0x4000000;
- class RSEncode // Encode or decode data area, one object per one thread.
- {
- private:
- RSCoder RSC;
- public:
- void EncodeBuf();
- void DecodeBuf();
- void Init(int RecVolNumber) {RSC.Init(RecVolNumber);}
- byte *Buf;
- byte *OutBuf;
- int BufStart;
- int BufEnd;
- int FileNumber;
- int RecVolNumber;
- size_t RecBufferSize;
- int *Erasures;
- int EraSize;
- };
- #ifdef RAR_SMP
- THREAD_PROC(RSEncodeThread)
- {
- RSEncode *rs=(RSEncode *)Data;
- rs->EncodeBuf();
- }
- THREAD_PROC(RSDecodeThread)
- {
- RSEncode *rs=(RSEncode *)Data;
- rs->DecodeBuf();
- }
- #endif
- RecVolumes3::RecVolumes3(RAROptions *Cmd,bool TestOnly)
- {
- memset(SrcFile,0,sizeof(SrcFile));
- if (TestOnly)
- {
- #ifdef RAR_SMP
- RSThreadPool=NULL;
- #endif
- }
- else
- {
- Buf.Alloc(TotalBufferSize);
- memset(SrcFile,0,sizeof(SrcFile));
- #ifdef RAR_SMP
- RSThreadPool=new ThreadPool(Cmd->Threads);
- #endif
- }
- }
- RecVolumes3::~RecVolumes3()
- {
- for (size_t I=0;I<ASIZE(SrcFile);I++)
- delete SrcFile[I];
- #ifdef RAR_SMP
- delete RSThreadPool;
- #endif
- }
- void RSEncode::EncodeBuf()
- {
- for (int BufPos=BufStart;BufPos<BufEnd;BufPos++)
- {
- byte Data[256],Code[256];
- for (int I=0;I<FileNumber;I++)
- Data[I]=Buf[I*RecBufferSize+BufPos];
- RSC.Encode(Data,FileNumber,Code);
- for (int I=0;I<RecVolNumber;I++)
- OutBuf[I*RecBufferSize+BufPos]=Code[I];
- }
- }
- // Check for names like arc5_3_1.rev created by RAR 3.0.
- static bool IsNewStyleRev(const wchar *Name)
- {
- wchar *Ext=GetExt(Name);
- if (Ext==NULL)
- return true;
- int DigitGroup=0;
- for (Ext--;Ext>Name;Ext--)
- if (!IsDigit(*Ext))
- if (*Ext=='_' && IsDigit(*(Ext-1)))
- DigitGroup++;
- else
- break;
- return DigitGroup<2;
- }
- bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
- {
- wchar ArcName[NM];
- wcsncpyz(ArcName,Name,ASIZE(ArcName));
- wchar *Ext=GetExt(ArcName);
- bool NewStyle=false; // New style .rev volumes are supported since RAR 3.10.
- bool RevName=Ext!=NULL && wcsicomp(Ext,L".rev")==0;
- if (RevName)
- {
- NewStyle=IsNewStyleRev(ArcName);
- while (Ext>ArcName+1 && (IsDigit(*(Ext-1)) || *(Ext-1)=='_'))
- Ext--;
- wcsncpyz(Ext,L"*.*",ASIZE(ArcName)-(Ext-ArcName));
-
- FindFile Find;
- Find.SetMask(ArcName);
- FindData fd;
- while (Find.Next(&fd))
- {
- Archive Arc(Cmd);
- if (Arc.WOpen(fd.Name) && Arc.IsArchive(true))
- {
- wcsncpyz(ArcName,fd.Name,ASIZE(ArcName));
- break;
- }
- }
- }
- Archive Arc(Cmd);
- if (!Arc.WCheckOpen(ArcName))
- return false;
- if (!Arc.Volume)
- {
- uiMsg(UIERROR_NOTVOLUME,ArcName);
- return false;
- }
- bool NewNumbering=Arc.NewNumbering;
- Arc.Close();
- wchar *VolNumStart=VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),NewNumbering);
- wchar RecVolMask[NM];
- wcsncpyz(RecVolMask,ArcName,ASIZE(RecVolMask));
- size_t BaseNamePartLength=VolNumStart-ArcName;
- wcsncpyz(RecVolMask+BaseNamePartLength,L"*.rev",ASIZE(RecVolMask)-BaseNamePartLength);
- int64 RecFileSize=0;
- // We cannot display "Calculating CRC..." message here, because we do not
- // know if we'll find any recovery volumes. We'll display it after finding
- // the first recovery volume.
- bool CalcCRCMessageDone=false;
- FindFile Find;
- Find.SetMask(RecVolMask);
- FindData RecData;
- int FileNumber=0,RecVolNumber=0,FoundRecVolumes=0,MissingVolumes=0;
- wchar PrevName[NM];
- while (Find.Next(&RecData))
- {
- wchar *CurName=RecData.Name;
- int P[3];
- if (!RevName && !NewStyle)
- {
- NewStyle=true;
- wchar *Dot=GetExt(CurName);
- if (Dot!=NULL)
- {
- int LineCount=0;
- Dot--;
- while (Dot>CurName && *Dot!='.')
- {
- if (*Dot=='_')
- LineCount++;
- Dot--;
- }
- if (LineCount==2)
- NewStyle=false;
- }
- }
- if (NewStyle)
- {
- if (!CalcCRCMessageDone)
- {
- uiMsg(UIMSG_RECVOLCALCCHECKSUM);
- CalcCRCMessageDone=true;
- }
-
- uiMsg(UIMSG_STRING,CurName);
- File CurFile;
- CurFile.TOpen(CurName);
- CurFile.Seek(0,SEEK_END);
- int64 Length=CurFile.Tell();
- CurFile.Seek(Length-7,SEEK_SET);
- for (int I=0;I<3;I++)
- P[2-I]=CurFile.GetByte()+1;
- uint FileCRC=0;
- for (int I=0;I<4;I++)
- FileCRC|=CurFile.GetByte()<<(I*8);
- uint CalcCRC;
- CalcFileSum(&CurFile,&CalcCRC,NULL,Cmd->Threads,Length-4);
- if (FileCRC!=CalcCRC)
- {
- uiMsg(UIMSG_CHECKSUM,CurName);
- continue;
- }
- }
- else
- {
- wchar *Dot=GetExt(CurName);
- if (Dot==NULL)
- continue;
- bool WrongParam=false;
- for (size_t I=0;I<ASIZE(P);I++)
- {
- do
- {
- Dot--;
- } while (IsDigit(*Dot) && Dot>=CurName+BaseNamePartLength);
- P[I]=atoiw(Dot+1);
- if (P[I]==0 || P[I]>255)
- WrongParam=true;
- }
- if (WrongParam)
- continue;
- }
- if (P[1]+P[2]>255)
- continue;
- if (RecVolNumber!=0 && RecVolNumber!=P[1] || FileNumber!=0 && FileNumber!=P[2])
- {
- uiMsg(UIERROR_RECVOLDIFFSETS,CurName,PrevName);
- return false;
- }
- RecVolNumber=P[1];
- FileNumber=P[2];
- wcsncpyz(PrevName,CurName,ASIZE(PrevName));
- File *NewFile=new File;
- NewFile->TOpen(CurName);
- SrcFile[FileNumber+P[0]-1]=NewFile;
- FoundRecVolumes++;
- if (RecFileSize==0)
- RecFileSize=NewFile->FileLength();
- }
- if (!Silent || FoundRecVolumes!=0)
- uiMsg(UIMSG_RECVOLFOUND,FoundRecVolumes);
- if (FoundRecVolumes==0)
- return false;
- bool WriteFlags[256];
- memset(WriteFlags,0,sizeof(WriteFlags));
- wchar LastVolName[NM];
- *LastVolName=0;
- for (int CurArcNum=0;CurArcNum<FileNumber;CurArcNum++)
- {
- Archive *NewFile=new Archive(Cmd);
- bool ValidVolume=FileExist(ArcName);
- if (ValidVolume)
- {
- NewFile->TOpen(ArcName);
- ValidVolume=NewFile->IsArchive(false);
- if (ValidVolume)
- {
- while (NewFile->ReadHeader()!=0)
- {
- if (NewFile->GetHeaderType()==HEAD_ENDARC)
- {
- uiMsg(UIMSG_STRING,ArcName);
- if (NewFile->EndArcHead.DataCRC)
- {
- uint CalcCRC;
- CalcFileSum(NewFile,&CalcCRC,NULL,Cmd->Threads,NewFile->CurBlockPos);
- if (NewFile->EndArcHead.ArcDataCRC!=CalcCRC)
- {
- ValidVolume=false;
- uiMsg(UIMSG_CHECKSUM,ArcName);
- }
- }
- break;
- }
- NewFile->SeekToNext();
- }
- }
- if (!ValidVolume)
- {
- NewFile->Close();
- wchar NewName[NM];
- wcsncpyz(NewName,ArcName,ASIZE(NewName));
- wcsncatz(NewName,L".bad",ASIZE(NewName));
- uiMsg(UIMSG_BADARCHIVE,ArcName);
- uiMsg(UIMSG_RENAMING,ArcName,NewName);
- RenameFile(ArcName,NewName);
- }
- NewFile->Seek(0,SEEK_SET);
- }
- if (!ValidVolume)
- {
- // It is important to return 'false' instead of aborting here,
- // so if we are called from extraction, we will be able to continue
- // extracting. It may happen if .rar and .rev are on read-only disks
- // like CDs.
- if (!NewFile->Create(ArcName,FMF_WRITE|FMF_SHAREREAD))
- {
- // We need to display the title of operation before the error message,
- // to make clear for user that create error is related to recovery
- // volumes. This is why we cannot use WCreate call here. Title must be
- // before create error, not after that.
- uiMsg(UIERROR_RECVOLFOUND,FoundRecVolumes); // Intentionally not displayed in console mode.
- uiMsg(UIERROR_RECONSTRUCTING);
- ErrHandler.CreateErrorMsg(ArcName);
- return false;
- }
- WriteFlags[CurArcNum]=true;
- MissingVolumes++;
- if (CurArcNum==FileNumber-1)
- wcsncpyz(LastVolName,ArcName,ASIZE(LastVolName));
- uiMsg(UIMSG_MISSINGVOL,ArcName);
- uiMsg(UIEVENT_NEWARCHIVE,ArcName);
- }
- SrcFile[CurArcNum]=(File*)NewFile;
- NextVolumeName(ArcName,ASIZE(ArcName),!NewNumbering);
- }
- 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);
- int TotalFiles=FileNumber+RecVolNumber;
- int Erasures[256],EraSize=0;
- for (int I=0;I<TotalFiles;I++)
- if (WriteFlags[I] || SrcFile[I]==NULL)
- Erasures[EraSize++]=I;
- int64 ProcessedSize=0;
- int LastPercent=-1;
- mprintf(L" ");
- // Size of per file buffer.
- size_t RecBufferSize=TotalBufferSize/TotalFiles;
- #ifdef RAR_SMP
- uint ThreadNumber=Cmd->Threads;
- #else
- uint ThreadNumber=1;
- #endif
- RSEncode *rse=new RSEncode[ThreadNumber];
- for (uint I=0;I<ThreadNumber;I++)
- rse[I].Init(RecVolNumber);
- while (true)
- {
- Wait();
- int MaxRead=0;
- for (int I=0;I<TotalFiles;I++)
- if (WriteFlags[I] || SrcFile[I]==NULL)
- memset(&Buf[I*RecBufferSize],0,RecBufferSize);
- else
- {
- int ReadSize=SrcFile[I]->Read(&Buf[I*RecBufferSize],RecBufferSize);
- if ((size_t)ReadSize!=RecBufferSize)
- memset(&Buf[I*RecBufferSize+ReadSize],0,RecBufferSize-ReadSize);
- if (ReadSize>MaxRead)
- MaxRead=ReadSize;
- }
- if (MaxRead==0)
- break;
- int CurPercent=ToPercent(ProcessedSize,RecFileSize);
- if (!Cmd->DisablePercentage && CurPercent!=LastPercent)
- {
- uiProcessProgress("RC",ProcessedSize,RecFileSize);
- LastPercent=CurPercent;
- }
- ProcessedSize+=MaxRead;
- int BlockStart=0;
- int BlockSize=MaxRead/ThreadNumber;
- if (BlockSize<0x100)
- BlockSize=MaxRead;
-
- for (uint CurThread=0;BlockStart<MaxRead;CurThread++)
- {
- // Last thread processes all left data including increasement
- // from rounding error.
- if (CurThread==ThreadNumber-1)
- BlockSize=MaxRead-BlockStart;
- RSEncode *curenc=rse+CurThread;
- curenc->Buf=&Buf[0];
- curenc->BufStart=BlockStart;
- curenc->BufEnd=BlockStart+BlockSize;
- curenc->FileNumber=TotalFiles;
- curenc->RecBufferSize=RecBufferSize;
- curenc->Erasures=Erasures;
- curenc->EraSize=EraSize;
- #ifdef RAR_SMP
- if (ThreadNumber>1)
- RSThreadPool->AddTask(RSDecodeThread,(void*)curenc);
- else
- curenc->DecodeBuf();
- #else
- curenc->DecodeBuf();
- #endif
- BlockStart+=BlockSize;
- }
- #ifdef RAR_SMP
- RSThreadPool->WaitDone();
- #endif // RAR_SMP
-
- for (int I=0;I<FileNumber;I++)
- if (WriteFlags[I])
- SrcFile[I]->Write(&Buf[I*RecBufferSize],MaxRead);
- }
- delete[] rse;
- for (int I=0;I<RecVolNumber+FileNumber;I++)
- if (SrcFile[I]!=NULL)
- {
- File *CurFile=SrcFile[I];
- if (NewStyle && WriteFlags[I])
- {
- int64 Length=CurFile->Tell();
- CurFile->Seek(Length-7,SEEK_SET);
- for (int J=0;J<7;J++)
- CurFile->PutByte(0);
- }
- CurFile->Close();
- SrcFile[I]=NULL;
- }
- if (*LastVolName!=0)
- {
- // Truncate the last volume to its real size.
- Archive Arc(Cmd);
- if (Arc.Open(LastVolName,FMF_UPDATE) && Arc.IsArchive(true) &&
- Arc.SearchBlock(HEAD_ENDARC))
- {
- Arc.Seek(Arc.NextBlockPos,SEEK_SET);
- char Buf[8192];
- int ReadSize=Arc.Read(Buf,sizeof(Buf));
- int ZeroCount=0;
- while (ZeroCount<ReadSize && Buf[ZeroCount]==0)
- ZeroCount++;
- if (ZeroCount==ReadSize)
- {
- Arc.Seek(Arc.NextBlockPos,SEEK_SET);
- Arc.Truncate();
- }
- }
- }
- #if !defined(SILENT)
- if (!Cmd->DisablePercentage)
- mprintf(L"\b\b\b\b100%%");
- if (!Silent && !Cmd->DisableDone)
- mprintf(St(MDone));
- #endif
- return true;
- }
- void RSEncode::DecodeBuf()
- {
- for (int BufPos=BufStart;BufPos<BufEnd;BufPos++)
- {
- byte Data[256];
- for (int I=0;I<FileNumber;I++)
- Data[I]=Buf[I*RecBufferSize+BufPos];
- RSC.Decode(Data,FileNumber,Erasures,EraSize);
- for (int I=0;I<EraSize;I++)
- Buf[Erasures[I]*RecBufferSize+BufPos]=Data[Erasures[I]];
- }
- }
- void RecVolumes3::Test(RAROptions *Cmd,const wchar *Name)
- {
- if (!IsNewStyleRev(Name)) // RAR 3.0 name#_#_#.rev do not include CRC32.
- {
- ErrHandler.UnknownMethodMsg(Name,Name);
- return;
- }
- wchar VolName[NM];
- wcsncpyz(VolName,Name,ASIZE(VolName));
- while (FileExist(VolName))
- {
- File CurFile;
- if (!CurFile.Open(VolName))
- {
- ErrHandler.OpenErrorMsg(VolName); // It also sets RARX_OPEN.
- continue;
- }
- if (!uiStartFileExtract(VolName,false,true,false))
- return;
- mprintf(St(MExtrTestFile),VolName);
- mprintf(L" ");
- CurFile.Seek(0,SEEK_END);
- int64 Length=CurFile.Tell();
- CurFile.Seek(Length-4,SEEK_SET);
- uint FileCRC=0;
- for (int I=0;I<4;I++)
- FileCRC|=CurFile.GetByte()<<(I*8);
- uint CalcCRC;
- CalcFileSum(&CurFile,&CalcCRC,NULL,1,Length-4,Cmd->DisablePercentage ? 0 : CALCFSUM_SHOWPROGRESS);
- if (FileCRC==CalcCRC)
- {
- mprintf(L"%s%s ",L"\b\b\b\b\b ",St(MOk));
- }
- else
- {
- uiMsg(UIERROR_CHECKSUM,VolName,VolName);
- ErrHandler.SetErrorCode(RARX_CRC);
- }
- NextVolumeName(VolName,ASIZE(VolName),false);
- }
- }
|