// Get the name of first volume. Return the leftmost digit of volume number. wchar* VolNameToFirstName(const wchar *VolName,wchar *FirstName,size_t MaxSize,bool NewNumbering) { if (FirstName!=VolName) wcsncpyz(FirstName,VolName,MaxSize); wchar *VolNumStart=FirstName; if (NewNumbering) { wchar N='1'; // From the rightmost digit of volume number to the left. for (wchar *ChPtr=GetVolNumPart(FirstName);ChPtr>FirstName;ChPtr--) if (IsDigit(*ChPtr)) { *ChPtr=N; // Set the rightmost digit to '1' and others to '0'. N='0'; } else if (N=='0') { VolNumStart=ChPtr+1; // Store the position of leftmost digit in volume number. break; } } else { // Old volume numbering scheme. Just set the extension to ".rar". SetExt(FirstName,L"rar",MaxSize); VolNumStart=GetExt(FirstName); } if (!FileExist(FirstName)) { // If the first volume, which name we just generated, is not exist, // check if volume with same name and any other extension is available. // It can help in case of *.exe or *.sfx first volume. wchar Mask[NM]; wcsncpyz(Mask,FirstName,ASIZE(Mask)); SetExt(Mask,L"*",ASIZE(Mask)); FindFile Find; Find.SetMask(Mask); FindData FD; while (Find.Next(&FD)) { Archive Arc; if (Arc.Open(FD.Name,0) && Arc.IsArchive(true) && Arc.FirstVolume) { wcsncpyz(FirstName,FD.Name,MaxSize); break; } } } return VolNumStart; }
void NextVolumeName(char *ArcName,bool OldNumbering) { char *ChPtr; if ((ChPtr=GetExt(ArcName))==NULL) { strcat(ArcName,".rar"); ChPtr=GetExt(ArcName); } else if (ChPtr[1]==0 || stricomp(ChPtr+1,"exe")==0 || stricomp(ChPtr+1,"sfx")==0) strcpy(ChPtr+1,"rar"); if (!OldNumbering) { ChPtr=GetVolNumPart(ArcName); while ((++(*ChPtr))=='9'+1) { *ChPtr='0'; ChPtr--; if (ChPtr<ArcName || !isdigit(*ChPtr)) { for (char *EndPtr=ArcName+strlen(ArcName);EndPtr!=ChPtr;EndPtr--) *(EndPtr+1)=*EndPtr; *(ChPtr+1)='1'; break; } } } else if (!isdigit(*(ChPtr+2)) || !isdigit(*(ChPtr+3))) strcpy(ChPtr+2,"00"); else { ChPtr+=3; while ((++(*ChPtr))=='9'+1) if (*(ChPtr-1)=='.') { *ChPtr='A'; break; } else { *ChPtr='0'; ChPtr--; } } }
void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering) { wchar *ChPtr; if ((ChPtr=GetExt(ArcName))==NULL) { wcsncatz(ArcName,L".rar",MaxLength); ChPtr=GetExt(ArcName); } else if (ChPtr[1]==0 && wcslen(ArcName)<MaxLength-3 || wcsicomp(ChPtr+1,L"exe")==0 || wcsicomp(ChPtr+1,L"sfx")==0) wcscpy(ChPtr+1,L"rar"); if (!OldNumbering) { ChPtr=GetVolNumPart(ArcName); while ((++(*ChPtr))=='9'+1) { *ChPtr='0'; ChPtr--; if (ChPtr<ArcName || !IsDigit(*ChPtr)) { for (wchar *EndPtr=ArcName+wcslen(ArcName);EndPtr!=ChPtr;EndPtr--) *(EndPtr+1)=*EndPtr; *(ChPtr+1)='1'; break; } } } else if (!IsDigit(*(ChPtr+2)) || !IsDigit(*(ChPtr+3))) wcscpy(ChPtr+2,L"00"); else { ChPtr+=3; while ((++(*ChPtr))=='9'+1) if (*(ChPtr-1)=='.') { *ChPtr='A'; break; } else { *ChPtr='0'; ChPtr--; } } }
char* VolNameToFirstName(const char *VolName,char *FirstName,bool NewNumbering) { if (FirstName!=VolName) strcpy(FirstName,VolName); char *VolNumStart=FirstName; if (NewNumbering) { int N='1'; for (char *ChPtr=GetVolNumPart(FirstName);ChPtr>FirstName;ChPtr--) if (isdigit(*ChPtr)) { *ChPtr=N; N='0'; } else if (N=='0') { VolNumStart=ChPtr+1; break; } } else { SetExt(FirstName,"rar"); VolNumStart=GetExt(FirstName); } if (!FileExist(FirstName)) { char Mask[NM]; strcpy(Mask,FirstName); SetExt(Mask,"*"); FindFile Find; Find.SetMask(Mask); struct FindData FD; while (Find.Next(&FD)) { Archive Arc; if (Arc.Open(FD.Name,FD.NameW) && Arc.IsArchive(true) && !Arc.NotFirstVolume) { strcpy(FirstName,FD.Name); break; } } } return(VolNumStart); }
void RecVolumesTest(RAROptions *Cmd,Archive *Arc,const wchar *Name) { wchar RevName[NM]; *RevName=0; if (Arc!=NULL) { // We received .rar or .exe volume as a parameter, trying to find // the matching .rev file number 1. bool NewNumbering=Arc->NewNumbering; wchar ArcName[NM]; wcsncpyz(ArcName,Name,ASIZE(ArcName)); 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); FindFile Find; Find.SetMask(RecVolMask); FindData RecData; while (Find.Next(&RecData)) { wchar *Num=GetVolNumPart(RecData.Name); if (*Num!='1') // Name must have "0...01" numeric part. continue; bool FirstVol=true; while (--Num>=RecData.Name && IsDigit(*Num)) if (*Num!='0') { FirstVol=false; break; } if (FirstVol) { wcsncpyz(RevName,RecData.Name,ASIZE(RevName)); Name=RevName; break; } } if (*RevName==0) // First .rev file not found. return; } File RevFile; if (!RevFile.Open(Name)) { ErrHandler.OpenErrorMsg(Name); // It also sets RARX_OPEN. return; } #ifndef GUI mprintf(L"\n"); #endif byte Sign[REV5_SIGN_SIZE]; bool Rev5=RevFile.Read(Sign,REV5_SIGN_SIZE)==REV5_SIGN_SIZE && memcmp(Sign,REV5_SIGN,REV5_SIGN_SIZE)==0; RevFile.Close(); if (Rev5) { RecVolumes5 RecVol(true); RecVol.Test(Cmd,Name); } else { RecVolumes3 RecVol(true); RecVol.Test(Cmd,Name); } }
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; }
bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,char Command) { RAROptions *Cmd=Arc.GetRAROptions(); int HeaderType=Arc.GetHeaderType(); FileHeader *hd=HeaderType==NEWSUB_HEAD ? &Arc.SubHead:&Arc.NewLhd; bool SplitHeader=(HeaderType==FILE_HEAD || HeaderType==NEWSUB_HEAD) && (hd->Flags & LHD_SPLIT_AFTER)!=0; if (DataIO!=NULL && SplitHeader && hd->UnpVer>=20 && hd->FileCRC!=0xffffffff && DataIO->PackedCRC!=~hd->FileCRC) { Log(Arc.FileName,St(MDataBadCRC),hd->FileName,Arc.FileName); } Int64 PosBeforeClose=Arc.Tell(); Arc.Close(); char NextName[NM]; wchar NextNameW[NM]; *NextNameW=0; strcpy(NextName,Arc.FileName); NextVolumeName(NextName,(Arc.NewMhd.Flags & MHD_NEWNUMBERING)==0 || Arc.OldFormat); if (*Arc.FileNameW!=0) { // Copy incremented trailing low ASCII volume name part to Unicode name. // It is simpler than also implementing Unicode version of NextVolumeName. strcpyw(NextNameW,Arc.FileNameW); char *NumPtr=GetVolNumPart(NextName); // moving to first digit in volume number while (NumPtr>NextName && isdigit(*NumPtr) && isdigit(*(NumPtr-1))) NumPtr--; // also copy the first character before volume number, // because it can be changed when going from .r99 to .s00 if (NumPtr>NextName) NumPtr--; int CharsToCopy=strlen(NextName)-(NumPtr-NextName); int DestPos=strlenw(NextNameW)-CharsToCopy; if (DestPos>0) { CharToWide(NumPtr,NextNameW+DestPos,ASIZE(NextNameW)-DestPos-1); NextNameW[ASIZE(NextNameW)-1]=0; } } #if !defined(SFX_MODULE) && !defined(RARDLL) bool RecoveryDone=false; #endif bool FailedOpen=false,OldSchemeTested=false; while (!Arc.Open(NextName,NextNameW)) { if (!OldSchemeTested) { char AltNextName[NM]; strcpy(AltNextName,Arc.FileName); NextVolumeName(AltNextName,true); OldSchemeTested=true; if (Arc.Open(AltNextName)) { strcpy(NextName,AltNextName); *NextNameW=0; break; } } #ifdef RARDLL if (Cmd->Callback==NULL && Cmd->ChangeVolProc==NULL || Cmd->Callback!=NULL && Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LONG)NextName,RAR_VOL_ASK)==-1) { Cmd->DllError=ERAR_EOPEN; FailedOpen=true; break; } if (Cmd->ChangeVolProc!=NULL) { #if defined(_WIN_32) && !defined(_MSC_VER) && !defined(__MINGW32__) _EBX=_ESP; #endif int RetCode=Cmd->ChangeVolProc(NextName,RAR_VOL_ASK); #if defined(_WIN_32) && !defined(_MSC_VER) && !defined(__MINGW32__) _ESP=_EBX; #endif if (RetCode==0) { Cmd->DllError=ERAR_EOPEN; FailedOpen=true; break; } } #else // RARDLL #if !defined(SFX_MODULE) && !defined(_WIN_CE) if (!RecoveryDone) { RecVolumes RecVol; RecVol.Restore(Cmd,Arc.FileName,Arc.FileNameW,true); RecoveryDone=true; continue; } #endif #ifndef GUI if (!Cmd->VolumePause && !IsRemovable(NextName)) { FailedOpen=true; break; } #endif #ifndef SILENT if (Cmd->AllYes || !AskNextVol(NextName)) #endif { FailedOpen=true; break; } #endif // RARDLL *NextNameW=0; } if (FailedOpen) { #if !defined(SILENT) && !defined(_WIN_CE) Log(Arc.FileName,St(MAbsNextVol),NextName); #endif Arc.Open(Arc.FileName,Arc.FileNameW); Arc.Seek(PosBeforeClose,SEEK_SET); return(false); } Arc.CheckArc(true); #ifdef RARDLL if (Cmd->Callback!=NULL && Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LONG)NextName,RAR_VOL_NOTIFY)==-1) return(false); if (Cmd->ChangeVolProc!=NULL) { #if defined(_WIN_32) && !defined(_MSC_VER) && !defined(__MINGW32__) _EBX=_ESP; #endif int RetCode=Cmd->ChangeVolProc(NextName,RAR_VOL_NOTIFY); #if defined(_WIN_32) && !defined(_MSC_VER) && !defined(__MINGW32__) _ESP=_EBX; #endif if (RetCode==0) return(false); } #endif if (Command=='T' || Command=='X' || Command=='E') mprintf(St(Command=='T' ? MTestVol:MExtrVol),Arc.FileName); if (SplitHeader) Arc.SearchBlock(HeaderType); else Arc.ReadHeader(); if (Arc.GetHeaderType()==FILE_HEAD) { Arc.ConvertAttributes(); Arc.Seek(Arc.NextBlockPos-Arc.NewLhd.FullPackSize,SEEK_SET); } #ifndef GUI if (ShowFileName) { char OutName[NM]; IntToExt(Arc.NewLhd.FileName,OutName); #ifdef UNICODE_SUPPORTED bool WideName=(Arc.NewLhd.Flags & LHD_UNICODE) && UnicodeEnabled(); if (WideName) { wchar NameW[NM]; ConvertPath(Arc.NewLhd.FileNameW,NameW); char Name[NM]; if (WideToChar(NameW,Name) && IsNameUsable(Name)) strcpy(OutName,Name); } #endif mprintf(St(MExtrPoints),OutName); if (!Cmd->DisablePercentage) mprintf(" "); } #endif if (DataIO!=NULL) { if (HeaderType==ENDARC_HEAD) DataIO->UnpVolume=false; else { DataIO->UnpVolume=(hd->Flags & LHD_SPLIT_AFTER); DataIO->SetPackedSizeToRead(hd->FullPackSize); } #ifdef SFX_MODULE DataIO->UnpArcSize=Arc.FileLength(); DataIO->CurUnpRead=0; #endif DataIO->PackedCRC=0xffffffff; // DataIO->SetFiles(&Arc,NULL); } return(true); }
void NextVolumeName(char *ArcName,wchar *ArcNameW,uint MaxLength,bool OldNumbering) { SVP_LogMsg3("ArcName0 %s %s" , ArcName , CStringA(ArcNameW)); if( CStringA(ArcName).IsEmpty()) WideToChar(ArcNameW, ArcName, MaxLength); char *ChPtr; if ((ChPtr=GetExt(ArcName))==NULL) { strcat(ArcName,".rar"); ChPtr=GetExt(ArcName); } else if (ChPtr[1]==0 || stricomp(ChPtr+1,"exe")==0 || stricomp(ChPtr+1,"sfx")==0) strcpy(ChPtr+1,"rar"); if (!OldNumbering) { ChPtr=GetVolNumPart(ArcName); while ((++(*ChPtr))=='9'+1) { *ChPtr='0'; ChPtr--; if (ChPtr<ArcName || !IsDigit(*ChPtr)) { for (char *EndPtr=ArcName+strlen(ArcName);EndPtr!=ChPtr;EndPtr--) *(EndPtr+1)=*EndPtr; *(ChPtr+1)='1'; break; } } } else if (!IsDigit(*(ChPtr+2)) || !IsDigit(*(ChPtr+3))) strcpy(ChPtr+2,"00"); else { ChPtr+=3; while ((++(*ChPtr))=='9'+1) if (*(ChPtr-1)=='.') { *ChPtr='A'; //or R ?? break; } else { *ChPtr='0'; ChPtr--; } } if (ArcNameW!=NULL && *ArcNameW!=0) { // Copy incremented trailing low ASCII volume name part to Unicode name. // It is simpler than implementing Unicode version of entire function. char *NumPtr=GetVolNumPart(ArcName); // moving to first digit in volume number while (NumPtr>ArcName && IsDigit(*NumPtr) && IsDigit(*(NumPtr-1))) NumPtr--; // also copy the first character before volume number, // because it can be changed when going from .r99 to .s00 if (NumPtr>ArcName) NumPtr--; int CharsToCopy=(int)(strlen(ArcName)-(NumPtr-ArcName)); int DestPos=(int)(strlenw(ArcNameW)-CharsToCopy); if (DestPos>0) // if (DestPos>=0) { CharToWide(NumPtr,ArcNameW+DestPos,MaxLength-DestPos-1); ArcNameW[MaxLength-1]=0; } } }