int ParseVersionFileName(wchar *Name,bool Truncate) { int Version=0; wchar *VerText=wcsrchr(Name,';'); if (VerText!=NULL) { if (Version==0) Version=atoiw(VerText+1); if (Truncate) *VerText=0; } return Version; }
int ParseVersionFileName(char *Name,wchar *NameW,bool Truncate) { int Version=0; char *VerText=strrchrd(Name,';'); if (VerText!=NULL) { Version=atoi(VerText+1); if (Truncate) *VerText=0; } if (NameW!=NULL) { wchar *VerTextW=strrchrw(NameW,';'); if (VerTextW!=NULL) { if (Version==0) Version=atoiw(VerTextW+1); if (Truncate) *VerTextW=0; } } return(Version); }
void CommandData::ProcessSwitch(const wchar *Switch) { switch(toupperw(Switch[0])) { case '@': ListMode=Switch[1]=='+' ? RCLM_ACCEPT_LISTS:RCLM_REJECT_LISTS; break; case 'A': switch(toupperw(Switch[1])) { case 'C': ClearArc=true; break; case 'D': AppendArcNameToPath=true; break; #ifndef SFX_MODULE case 'G': if (Switch[2]=='-' && Switch[3]==0) GenerateArcName=0; else { GenerateArcName=true; wcsncpyz(GenerateMask,Switch+2,ASIZE(GenerateMask)); } break; #endif case 'I': IgnoreGeneralAttr=true; break; case 'N': // Reserved for archive name. break; case 'O': AddArcOnly=true; break; case 'P': wcscpy(ArcPath,Switch+2); break; case 'S': SyncFiles=true; break; default: BadSwitch(Switch); break; } break; case 'C': if (Switch[2]==0) switch(toupperw(Switch[1])) { case '-': DisableComment=true; break; case 'U': ConvertNames=NAMES_UPPERCASE; break; case 'L': ConvertNames=NAMES_LOWERCASE; break; } break; case 'D': if (Switch[2]==0) switch(toupperw(Switch[1])) { case 'S': DisableSortSolid=true; break; case 'H': OpenShared=true; break; case 'F': DeleteFiles=true; break; } break; case 'E': switch(toupperw(Switch[1])) { case 'P': switch(Switch[2]) { case 0: ExclPath=EXCL_SKIPWHOLEPATH; break; case '1': ExclPath=EXCL_BASEPATH; break; case '2': ExclPath=EXCL_SAVEFULLPATH; break; case '3': ExclPath=EXCL_ABSPATH; break; } break; default: if (Switch[1]=='+') { InclFileAttr|=GetExclAttr(Switch+2); InclAttrSet=true; } else ExclFileAttr|=GetExclAttr(Switch+1); break; } break; case 'F': if (Switch[1]==0) FreshFiles=true; else BadSwitch(Switch); break; case 'H': switch (toupperw(Switch[1])) { case 'P': EncryptHeaders=true; if (Switch[2]!=0) { Password.Set(Switch+2); cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0])); } else if (!Password.IsSet()) { uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password); eprintf(L"\n"); } break; default : BadSwitch(Switch); break; } break; case 'I': if (wcsnicomp(Switch+1,L"LOG",3)==0) { wcsncpyz(LogName,Switch[4]!=0 ? Switch+4:DefLogName,ASIZE(LogName)); break; } if (wcsicomp(Switch+1,L"SND")==0) { Sound=true; break; } if (wcsicomp(Switch+1,L"ERR")==0) { MsgStream=MSG_STDERR; // Set it immediately when parsing the command line, so it also // affects messages issued while parsing the command line. SetConsoleMsgStream(MSG_STDERR); break; } if (wcsnicomp(Switch+1,L"EML",3)==0) { wcsncpyz(EmailTo,Switch[4]!=0 ? Switch+4:L"@",ASIZE(EmailTo)); break; } if (wcsicomp(Switch+1,L"M")==0) { MoreInfo=true; break; } if (wcsicomp(Switch+1,L"NUL")==0) { MsgStream=MSG_NULL; SetConsoleMsgStream(MSG_NULL); break; } if (toupperw(Switch[1])=='D') { for (uint I=2;Switch[I]!=0;I++) switch(toupperw(Switch[I])) { case 'Q': MsgStream=MSG_ERRONLY; SetConsoleMsgStream(MSG_ERRONLY); break; case 'C': DisableCopyright=true; break; case 'D': DisableDone=true; break; case 'P': DisablePercentage=true; break; } break; } if (wcsnicomp(Switch+1,L"OFF",3)==0) { switch(Switch[4]) { case 0: case '1': Shutdown=POWERMODE_OFF; break; case '2': Shutdown=POWERMODE_HIBERNATE; break; case '3': Shutdown=POWERMODE_SLEEP; break; case '4': Shutdown=POWERMODE_RESTART; break; } break; } if (wcsicomp(Switch+1,L"VER")==0) { PrintVersion=true; break; } break; case 'K': switch(toupperw(Switch[1])) { case 'B': KeepBroken=true; break; case 0: Lock=true; break; } break; case 'M': switch(toupperw(Switch[1])) { case 'C': { const wchar *Str=Switch+2; if (*Str=='-') for (uint I=0;I<ASIZE(FilterModes);I++) FilterModes[I].State=FILTER_DISABLE; else while (*Str!=0) { int Param1=0,Param2=0; FilterState State=FILTER_AUTO; FilterType Type=FILTER_NONE; if (IsDigit(*Str)) { Param1=atoiw(Str); while (IsDigit(*Str)) Str++; } if (*Str==':' && IsDigit(Str[1])) { Param2=atoiw(++Str); while (IsDigit(*Str)) Str++; } switch(toupperw(*(Str++))) { case 'T': Type=FILTER_PPM; break; case 'E': Type=FILTER_E8; break; case 'D': Type=FILTER_DELTA; break; case 'A': Type=FILTER_AUDIO; break; case 'C': Type=FILTER_RGB; break; case 'I': Type=FILTER_ITANIUM; break; case 'R': Type=FILTER_ARM; break; } if (*Str=='+' || *Str=='-') State=*(Str++)=='+' ? FILTER_FORCE:FILTER_DISABLE; FilterModes[Type].State=State; FilterModes[Type].Param1=Param1; FilterModes[Type].Param2=Param2; } } break; case 'M': break; case 'D': break; case 'S': { wchar StoreNames[1024]; wcsncpyz(StoreNames,(Switch[2]==0 ? DefaultStoreList:Switch+2),ASIZE(StoreNames)); wchar *Names=StoreNames; while (*Names!=0) { wchar *End=wcschr(Names,';'); if (End!=NULL) *End=0; if (*Names=='.') Names++; wchar Mask[NM]; if (wcspbrk(Names,L"*?.")==NULL) swprintf(Mask,ASIZE(Mask),L"*.%ls",Names); else wcsncpyz(Mask,Names,ASIZE(Mask)); StoreArgs.AddString(Mask); if (End==NULL) break; Names=End+1; } } break; #ifdef RAR_SMP case 'T': Threads=atoiw(Switch+2); if (Threads>MaxPoolThreads || Threads<1) BadSwitch(Switch); else { } break; #endif default: Method=Switch[1]-'0'; if (Method>5 || Method<0) BadSwitch(Switch); break; } break; case 'N': case 'X': if (Switch[1]!=0) { StringList *Args=toupperw(Switch[0])=='N' ? &InclArgs:&ExclArgs; if (Switch[1]=='@' && !IsWildcard(Switch)) ReadTextFile(Switch+2,Args,false,true,FilelistCharset,true,true,true); else Args->AddString(Switch+1); } break; case 'O': switch(toupperw(Switch[1])) { case '+': Overwrite=OVERWRITE_ALL; break; case '-': Overwrite=OVERWRITE_NONE; break; case 0: Overwrite=OVERWRITE_FORCE_ASK; break; #ifdef _WIN_ALL case 'C': SetCompressedAttr=true; break; #endif case 'H': SaveHardLinks=true; break; #ifdef SAVE_LINKS case 'L': SaveSymLinks=true; if (toupperw(Switch[2])=='A') AbsoluteLinks=true; break; #endif #ifdef _WIN_ALL case 'N': if (toupperw(Switch[2])=='I') AllowIncompatNames=true; break; #endif case 'R': Overwrite=OVERWRITE_AUTORENAME; break; #ifdef _WIN_ALL case 'S': SaveStreams=true; break; #endif case 'W': ProcessOwners=true; break; default : BadSwitch(Switch); break; } break; case 'P': if (Switch[1]==0) { uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password); eprintf(L"\n"); } else { Password.Set(Switch+1); cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0])); } break; #ifndef SFX_MODULE case 'Q': if (toupperw(Switch[1])=='O') switch(toupperw(Switch[2])) { case 0: QOpenMode=QOPEN_AUTO; break; case '-': QOpenMode=QOPEN_NONE; break; case '+': QOpenMode=QOPEN_ALWAYS; break; default: BadSwitch(Switch); break; } else BadSwitch(Switch); break; #endif case 'R': switch(toupperw(Switch[1])) { case 0: Recurse=RECURSE_ALWAYS; break; case '-': Recurse=RECURSE_DISABLE; break; case '0': Recurse=RECURSE_WILDCARDS; break; case 'I': { Priority=atoiw(Switch+2); if (Priority<0 || Priority>15) BadSwitch(Switch); const wchar *ChPtr=wcschr(Switch+2,':'); if (ChPtr!=NULL) { SleepTime=atoiw(ChPtr+1); if (SleepTime>1000) BadSwitch(Switch); InitSystemOptions(SleepTime); } SetPriority(Priority); } break; } break; case 'S': if (IsDigit(Switch[1])) { Solid|=SOLID_COUNT; SolidCount=atoiw(&Switch[1]); } else switch(toupperw(Switch[1])) { case 0: Solid|=SOLID_NORMAL; break; case '-': Solid=SOLID_NONE; break; case 'E': Solid|=SOLID_FILEEXT; break; case 'V': Solid|=Switch[2]=='-' ? SOLID_VOLUME_DEPENDENT:SOLID_VOLUME_INDEPENDENT; break; case 'D': Solid|=SOLID_VOLUME_DEPENDENT; break; case 'L': if (IsDigit(Switch[2])) FileSizeLess=atoilw(Switch+2); break; case 'M': if (IsDigit(Switch[2])) FileSizeMore=atoilw(Switch+2); break; case 'C': { bool AlreadyBad=false; // Avoid reporting "bad switch" several times. RAR_CHARSET rch=RCH_DEFAULT; switch(toupperw(Switch[2])) { case 'A': rch=RCH_ANSI; break; case 'O': rch=RCH_OEM; break; case 'U': rch=RCH_UNICODE; break; case 'F': rch=RCH_UTF8; break; default : BadSwitch(Switch); AlreadyBad=true; break; }; if (!AlreadyBad) if (Switch[3]==0) CommentCharset=FilelistCharset=ErrlogCharset=RedirectCharset=rch; else for (uint I=3;Switch[I]!=0 && !AlreadyBad;I++) switch(toupperw(Switch[I])) { case 'C': CommentCharset=rch; break; case 'L': FilelistCharset=rch; break; case 'R': RedirectCharset=rch; break; default: BadSwitch(Switch); AlreadyBad=true; break; } // Set it immediately when parsing the command line, so it also // affects messages issued while parsing the command line. SetConsoleRedirectCharset(RedirectCharset); } break; } break; case 'T': switch(toupperw(Switch[1])) { case 'K': ArcTime=ARCTIME_KEEP; break; case 'L': ArcTime=ARCTIME_LATEST; break; case 'O': FileTimeBefore.SetAgeText(Switch+2); break; case 'N': FileTimeAfter.SetAgeText(Switch+2); break; case 'B': FileTimeBefore.SetIsoText(Switch+2); break; case 'A': FileTimeAfter.SetIsoText(Switch+2); break; case 'S': { EXTTIME_MODE Mode=EXTTIME_HIGH3; bool CommonMode=Switch[2]>='0' && Switch[2]<='4'; if (CommonMode) Mode=(EXTTIME_MODE)(Switch[2]-'0'); if (Mode==EXTTIME_HIGH1 || Mode==EXTTIME_HIGH2) // '2' and '3' not supported anymore. Mode=EXTTIME_HIGH3; if (Switch[2]=='-') Mode=EXTTIME_NONE; if (CommonMode || Switch[2]=='-' || Switch[2]=='+' || Switch[2]==0) xmtime=xctime=xatime=Mode; else { if (Switch[3]>='0' && Switch[3]<='4') Mode=(EXTTIME_MODE)(Switch[3]-'0'); if (Mode==EXTTIME_HIGH1 || Mode==EXTTIME_HIGH2) // '2' and '3' not supported anymore. Mode=EXTTIME_HIGH3; if (Switch[3]=='-') Mode=EXTTIME_NONE; switch(toupperw(Switch[2])) { case 'M': xmtime=Mode; break; case 'C': xctime=Mode; break; case 'A': xatime=Mode; break; } } } break; case '-': Test=false; break; case 0: Test=true; break; default: BadSwitch(Switch); break; } break; case 'U': if (Switch[1]==0) UpdateFiles=true; else BadSwitch(Switch); break; case 'V': switch(toupperw(Switch[1])) { case 'P': VolumePause=true; break; case 'E': if (toupperw(Switch[2])=='R') VersionControl=atoiw(Switch+3)+1; break; case '-': VolSize=0; break; default: VolSize=VOLSIZE_AUTO; // UnRAR -v switch for list command. break; } break; case 'W': wcsncpyz(TempPath,Switch+1,ASIZE(TempPath)); AddEndSlash(TempPath,ASIZE(TempPath)); break; case 'Y': AllYes=true; break; case 'Z': if (Switch[1]==0) { // If comment file is not specified, we read data from stdin. wcscpy(CommentFile,L"stdin"); } else wcsncpyz(CommentFile,Switch+1,ASIZE(CommentFile)); break; case '?' : OutHelp(RARX_SUCCESS); break; default : BadSwitch(Switch); break; } }
bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent) { wchar ArcName[NM]; wcscpy(ArcName,Name); wchar *Ext=GetExt(ArcName); bool NewStyle=false; bool RevName=Ext!=NULL && wcsicomp(Ext,L".rev")==0; if (RevName) { for (int DigitGroup=0;Ext>ArcName && DigitGroup<3;Ext--) if (!IsDigit(*Ext)) if (IsDigit(*(Ext-1)) && (*Ext=='_' || DigitGroup<2)) DigitGroup++; else if (DigitGroup<2) { NewStyle=true; break; } while (IsDigit(*Ext) && Ext>ArcName+1) Ext--; wcscpy(Ext,L"*.*"); FindFile Find; Find.SetMask(ArcName); FindData fd; while (Find.Next(&fd)) { Archive Arc(Cmd); if (Arc.WOpen(fd.Name) && Arc.IsArchive(true)) { wcscpy(ArcName,fd.Name); break; } } } Archive Arc(Cmd); if (!Arc.WCheckOpen(ArcName)) return false; if (!Arc.Volume) { #ifndef SILENT Log(ArcName,St(MNotVolume),ArcName); #endif return false; } bool NewNumbering=Arc.NewNumbering; Arc.Close(); wchar *VolNumStart=VolNameToFirstName(ArcName,ArcName,NewNumbering); wchar RecVolMask[NM]; wcscpy(RecVolMask,ArcName); size_t BaseNamePartLength=VolNumStart-ArcName; wcscpy(RecVolMask+BaseNamePartLength,L"*.rev"); #ifndef SILENT int64 RecFileSize=0; #endif // 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) { #ifndef SILENT mprintf(St(MCalcCRCAllVol)); #endif CalcCRCMessageDone=true; } #ifndef SILENT mprintf(L"\n%s",CurName); #endif 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) { #ifndef SILENT mprintf(St(MCRCFailed),CurName); #endif 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]) { #ifndef SILENT Log(NULL,St(MRecVolDiffSets),CurName,PrevName); #endif return(false); } RecVolNumber=P[1]; FileNumber=P[2]; wcscpy(PrevName,CurName); File *NewFile=new File; NewFile->TOpen(CurName); SrcFile[FileNumber+P[0]-1]=NewFile; FoundRecVolumes++; #ifndef SILENT if (RecFileSize==0) RecFileSize=NewFile->FileLength(); #endif } #ifndef SILENT if (!Silent || FoundRecVolumes!=0) { mprintf(St(MRecVolFound),FoundRecVolumes); } #endif 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) { #ifndef SILENT mprintf(L"\n%s",ArcName); #endif if (NewFile->EndArcHead.DataCRC) { uint CalcCRC; CalcFileSum(NewFile,&CalcCRC,NULL,Cmd->Threads,NewFile->CurBlockPos); if (NewFile->EndArcHead.ArcDataCRC!=CalcCRC) { ValidVolume=false; #ifndef SILENT mprintf(St(MCRCFailed),ArcName); #endif } } break; } NewFile->SeekToNext(); } } if (!ValidVolume) { NewFile->Close(); wchar NewName[NM]; wcscpy(NewName,ArcName); wcscat(NewName,L".bad"); #ifndef SILENT mprintf(St(MBadArc),ArcName); mprintf(St(MRenaming),ArcName,NewName); #endif 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)) { // 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. #ifndef SILENT mprintf(St(MReconstructing)); #endif ErrHandler.CreateErrorMsg(NULL,ArcName); return false; } WriteFlags[CurArcNum]=true; MissingVolumes++; if (CurArcNum==FileNumber-1) wcscpy(LastVolName,ArcName); #ifndef SILENT mprintf(St(MAbsNextVol),ArcName); #endif } SrcFile[CurArcNum]=(File*)NewFile; NextVolumeName(ArcName,ASIZE(ArcName),!NewNumbering); } #ifndef SILENT mprintf(St(MRecVolMissing),MissingVolumes); #endif if (MissingVolumes==0) { #ifndef SILENT mprintf(St(MRecVolAllExist)); #endif return false; } if (MissingVolumes>FoundRecVolumes) { #ifndef SILENT mprintf(St(MRecVolCannotFix)); #endif return false; } #ifndef SILENT mprintf(St(MReconstructing)); #endif 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; #ifndef SILENT int64 ProcessedSize=0; #ifndef GUI int LastPercent=-1; mprintf(L" "); #endif #endif // Size of per file buffer. size_t RecBufferSize=TotalBufferSize/TotalFiles; #ifdef RAR_SMP uint ThreadNumber=Cmd->Threads; RSEncode rse[MaxPoolThreads]; #else uint ThreadNumber=1; RSEncode rse[1]; #endif 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; #ifndef SILENT int CurPercent=ToPercent(ProcessedSize,RecFileSize); if (!Cmd->DisablePercentage && CurPercent!=LastPercent) { mprintf(L"\b\b\b\b%3d%%",CurPercent); LastPercent=CurPercent; } ProcessedSize+=MaxRead; #endif 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); } 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(GUI) && !defined(SILENT) if (!Cmd->DisablePercentage) mprintf(L"\b\b\b\b100%%"); if (!Silent && !Cmd->DisableDone) mprintf(St(MDone)); #endif return true; }