1 #include "rar.hpp"
2 
3 #ifdef RARDLL
4 static bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize);
5 static bool DllVolNotify(RAROptions *Cmd,wchar *NextName);
6 #endif
7 
8 
9 
MergeArchive(Archive & Arc,ComprDataIO * DataIO,bool ShowFileName,wchar Command)10 bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Command)
11 {
12   RAROptions *Cmd=Arc.GetRAROptions();
13 
14   HEADER_TYPE HeaderType=Arc.GetHeaderType();
15   FileHeader *hd=HeaderType==HEAD_SERVICE ? &Arc.SubHead:&Arc.FileHead;
16   bool SplitHeader=(HeaderType==HEAD_FILE || HeaderType==HEAD_SERVICE) &&
17                    hd->SplitAfter;
18 
19   if (DataIO!=NULL && SplitHeader)
20   {
21     bool PackedHashPresent=Arc.Format==RARFMT50 ||
22          hd->UnpVer>=20 && hd->FileHash.CRC32!=0xffffffff;
23     if (PackedHashPresent &&
24         !DataIO->PackedDataHash.Cmp(&hd->FileHash,hd->UseHashKey ? hd->HashKey:NULL))
25       uiMsg(UIERROR_CHECKSUMPACKED, Arc.FileName, hd->FileName);
26   }
27 
28   int64 PosBeforeClose=Arc.Tell();
29 
30   if (DataIO!=NULL)
31     DataIO->ProcessedArcSize+=Arc.FileLength();
32 
33 
34   Arc.Close();
35 
36   wchar NextName[NM];
37   wcsncpyz(NextName,Arc.FileName,ASIZE(NextName));
38   NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering);
39 
40 #if !defined(SFX_MODULE) && !defined(RARDLL)
41   bool RecoveryDone=false;
42 #endif
43   bool FailedOpen=false,OldSchemeTested=false;
44 
45 #if !defined(SILENT)
46   // In -vp mode we force the pause before next volume even if it is present
47   // and even if we are on the hard disk. It is important when user does not
48   // want to process partially downloaded volumes preliminary.
49   if (Cmd->VolumePause && !uiAskNextVolume(NextName,ASIZE(NextName)))
50     FailedOpen=true;
51 #endif
52 
53   uint OpenMode = Cmd->OpenShared ? FMF_OPENSHARED : 0;
54 
55   if (!FailedOpen)
56     while (!Arc.Open(NextName,OpenMode))
57     {
58       // We need to open a new volume which size was not calculated
59       // in total size before, so we cannot calculate the total progress
60       // anymore. Let's reset the total size to zero and stop
61       // the total progress.
62       if (DataIO!=NULL)
63         DataIO->TotalArcSize=0;
64 
65       if (!OldSchemeTested)
66       {
67         // Checking for new style volumes renamed by user to old style
68         // name format. Some users did it for unknown reason.
69         wchar AltNextName[NM];
70         wcsncpyz(AltNextName,Arc.FileName,ASIZE(AltNextName));
71         NextVolumeName(AltNextName,ASIZE(AltNextName),true);
72         OldSchemeTested=true;
73         if (Arc.Open(AltNextName,OpenMode))
74         {
75           wcsncpyz(NextName,AltNextName,ASIZE(NextName));
76           break;
77         }
78       }
79 #ifdef RARDLL
80       if (!DllVolChange(Cmd,NextName,ASIZE(NextName)))
81       {
82         FailedOpen=true;
83         break;
84       }
85 #else // !RARDLL
86 
87 #ifndef SFX_MODULE
88       if (!RecoveryDone)
89       {
90         RecVolumesRestore(Cmd,Arc.FileName,true);
91         RecoveryDone=true;
92         continue;
93       }
94 #endif
95 
96       if (!Cmd->VolumePause && !IsRemovable(NextName))
97       {
98         FailedOpen=true;
99         break;
100       }
101 #ifndef SILENT
102       if (Cmd->AllYes || !uiAskNextVolume(NextName,ASIZE(NextName)))
103 #endif
104       {
105         FailedOpen=true;
106         break;
107       }
108 
109 #endif // RARDLL
110     }
111 
112   if (FailedOpen)
113   {
114     uiMsg(UIERROR_MISSINGVOL,NextName);
115     Arc.Open(Arc.FileName,OpenMode);
116     Arc.Seek(PosBeforeClose,SEEK_SET);
117     return false;
118   }
119 
120   if (Command=='T' || Command=='X' || Command=='E')
121     mprintf(St(Command=='T' ? MTestVol:MExtrVol),Arc.FileName);
122 
123 
124   Arc.CheckArc(true);
125 #ifdef RARDLL
126   if (!DllVolNotify(Cmd,NextName))
127     return false;
128 #endif
129 
130   if (SplitHeader)
131     Arc.SearchBlock(HeaderType);
132   else
133     Arc.ReadHeader();
134   if (Arc.GetHeaderType()==HEAD_FILE)
135   {
136     Arc.ConvertAttributes();
137     Arc.Seek(Arc.NextBlockPos-Arc.FileHead.PackSize,SEEK_SET);
138   }
139   if (ShowFileName && !Cmd->DisableNames)
140   {
141     mprintf(St(MExtrPoints),Arc.FileHead.FileName);
142     if (!Cmd->DisablePercentage)
143       mprintf(L"     ");
144   }
145   if (DataIO!=NULL)
146   {
147     if (HeaderType==HEAD_ENDARC)
148       DataIO->UnpVolume=false;
149     else
150     {
151       DataIO->UnpVolume=hd->SplitAfter;
152       DataIO->SetPackedSizeToRead(hd->PackSize);
153     }
154 #ifdef SFX_MODULE
155     DataIO->UnpArcSize=Arc.FileLength();
156 #endif
157 
158     // Reset the size of packed data read from current volume. It is used
159     // to display the total progress and preceding volumes are already
160     // compensated with ProcessedArcSize, so we need to reset this variable.
161     DataIO->CurUnpRead=0;
162 
163     DataIO->PackedDataHash.Init(hd->FileHash.Type,Cmd->Threads);
164   }
165   return true;
166 }
167 
168 
169 
170 
171 
172 
173 #ifdef RARDLL
174 #if defined(RARDLL) && defined(_MSC_VER) && !defined(_WIN_64)
175 // Disable the run time stack check for unrar.dll, so we can manipulate
176 // with ChangeVolProc call type below. Run time check would intercept
177 // a wrong ESP before we restore it.
178 #pragma runtime_checks( "s", off )
179 #endif
180 
DllVolChange(RAROptions * Cmd,wchar * NextName,size_t NameSize)181 bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize)
182 {
183   bool DllVolChanged=false,DllVolAborted=false;
184 
185   if (Cmd->Callback!=NULL)
186   {
187     wchar OrgNextName[NM];
188     wcsncpyz(OrgNextName,NextName,ASIZE(OrgNextName));
189     if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NextName,RAR_VOL_ASK)==-1)
190       DllVolAborted=true;
191     else
192       if (wcscmp(OrgNextName,NextName)!=0)
193         DllVolChanged=true;
194       else
195       {
196         char NextNameA[NM],OrgNextNameA[NM];
197         WideToChar(NextName,NextNameA,ASIZE(NextNameA));
198         strncpyz(OrgNextNameA,NextNameA,ASIZE(OrgNextNameA));
199         if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextNameA,RAR_VOL_ASK)==-1)
200           DllVolAborted=true;
201         else
202           if (strcmp(OrgNextNameA,NextNameA)!=0)
203           {
204             // We can damage some Unicode characters by U->A->U conversion,
205             // so set Unicode name only if we see that ANSI name is changed.
206             CharToWide(NextNameA,NextName,NameSize);
207             DllVolChanged=true;
208           }
209       }
210   }
211   if (!DllVolChanged && Cmd->ChangeVolProc!=NULL)
212   {
213     char NextNameA[NM];
214     WideToChar(NextName,NextNameA,ASIZE(NextNameA));
215     // Here we preserve ESP value. It is necessary for those developers,
216     // who still define ChangeVolProc callback as "C" type function,
217     // even though in year 2001 we announced in unrar.dll whatsnew.txt
218     // that it will be PASCAL type (for compatibility with Visual Basic).
219 #if defined(_MSC_VER)
220 #ifndef _WIN_64
221     __asm mov ebx,esp
222 #endif
223 #elif defined(_WIN_ALL) && defined(__BORLANDC__)
224     _EBX=_ESP;
225 #endif
226     int RetCode=Cmd->ChangeVolProc(NextNameA,RAR_VOL_ASK);
227 
228     // Restore ESP after ChangeVolProc with wrongly defined calling
229     // convention broken it.
230 #if defined(_MSC_VER)
231 #ifndef _WIN_64
232     __asm mov esp,ebx
233 #endif
234 #elif defined(_WIN_ALL) && defined(__BORLANDC__)
235     _ESP=_EBX;
236 #endif
237     if (RetCode==0)
238       DllVolAborted=true;
239     else
240       CharToWide(NextNameA,NextName,NameSize);
241   }
242 
243   // We quit only on 'abort' condition, but not on 'name not changed'.
244   // It is legitimate for program to return the same name when waiting
245   // for currently non-existent volume.
246   // Also we quit to prevent an infinite loop if no callback is defined.
247   if (DllVolAborted || Cmd->Callback==NULL && Cmd->ChangeVolProc==NULL)
248   {
249     Cmd->DllError=ERAR_EOPEN;
250     return false;
251   }
252   return true;
253 }
254 #endif
255 
256 
257 #ifdef RARDLL
DllVolNotify(RAROptions * Cmd,wchar * NextName)258 bool DllVolNotify(RAROptions *Cmd,wchar *NextName)
259 {
260   char NextNameA[NM];
261   WideToChar(NextName,NextNameA,ASIZE(NextNameA));
262   if (Cmd->Callback!=NULL)
263   {
264     if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NextName,RAR_VOL_NOTIFY)==-1)
265       return false;
266     if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextNameA,RAR_VOL_NOTIFY)==-1)
267       return false;
268   }
269   if (Cmd->ChangeVolProc!=NULL)
270   {
271 #if defined(_WIN_ALL) && !defined(_MSC_VER) && !defined(__MINGW32__)
272     _EBX=_ESP;
273 #endif
274     int RetCode=Cmd->ChangeVolProc(NextNameA,RAR_VOL_NOTIFY);
275 #if defined(_WIN_ALL) && !defined(_MSC_VER) && !defined(__MINGW32__)
276     _ESP=_EBX;
277 #endif
278     if (RetCode==0)
279       return false;
280   }
281   return true;
282 }
283 
284 #if defined(RARDLL) && defined(_MSC_VER) && !defined(_WIN_64)
285 // Restore the run time stack check for unrar.dll.
286 #pragma runtime_checks( "s", restore )
287 #endif
288 #endif
289