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   wcscpy(NextName,Arc.FileName);
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(GUI) && !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         wcscpy(AltNextName,Arc.FileName);
71         NextVolumeName(AltNextName,ASIZE(AltNextName),true);
72         OldSchemeTested=true;
73         if (Arc.Open(AltNextName,OpenMode))
74         {
75           wcscpy(NextName,AltNextName);
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 #ifndef GUI
97       if (!Cmd->VolumePause && !IsRemovable(NextName))
98       {
99         FailedOpen=true;
100         break;
101       }
102 #endif
103 #ifndef SILENT
104       if (Cmd->AllYes || !uiAskNextVolume(NextName,ASIZE(NextName)))
105 #endif
106       {
107         FailedOpen=true;
108         break;
109       }
110 
111 #endif // RARDLL
112     }
113 
114   if (FailedOpen)
115   {
116     uiMsg(UIERROR_MISSINGVOL,NextName);
117     Arc.Open(Arc.FileName,OpenMode);
118     Arc.Seek(PosBeforeClose,SEEK_SET);
119     return false;
120   }
121 
122   if (Command=='T' || Command=='X' || Command=='E')
123     mprintf(St(Command=='T' ? MTestVol:MExtrVol),Arc.FileName);
124 
125 
126   Arc.CheckArc(true);
127 #ifdef RARDLL
128   if (!DllVolNotify(Cmd,NextName))
129     return false;
130 #endif
131 
132   if (SplitHeader)
133     Arc.SearchBlock(HeaderType);
134   else
135     Arc.ReadHeader();
136   if (Arc.GetHeaderType()==HEAD_FILE)
137   {
138     Arc.ConvertAttributes();
139     Arc.Seek(Arc.NextBlockPos-Arc.FileHead.PackSize,SEEK_SET);
140   }
141 #ifndef GUI
142   if (ShowFileName)
143   {
144     mprintf(St(MExtrPoints),Arc.FileHead.FileName);
145     if (!Cmd->DisablePercentage)
146       mprintf(L"     ");
147   }
148 #endif
149   if (DataIO!=NULL)
150   {
151     if (HeaderType==HEAD_ENDARC)
152       DataIO->UnpVolume=false;
153     else
154     {
155       DataIO->UnpVolume=hd->SplitAfter;
156       DataIO->SetPackedSizeToRead(hd->PackSize);
157     }
158 #ifdef SFX_MODULE
159     DataIO->UnpArcSize=Arc.FileLength();
160 #endif
161 
162     // Reset the size of packed data read from current volume. It is used
163     // to display the total progress and preceding volumes are already
164     // compensated with ProcessedArcSize, so we need to reset this variable.
165     DataIO->CurUnpRead=0;
166 
167     DataIO->PackedDataHash.Init(hd->FileHash.Type,Cmd->Threads);
168   }
169   return true;
170 }
171 
172 
173 
174 
175 
176 
177 #ifdef RARDLL
178 #if defined(RARDLL) && defined(_MSC_VER) && !defined(_WIN_64)
179 // Disable the run time stack check for unrar.dll, so we can manipulate
180 // with ChangeVolProc call type below. Run time check would intercept
181 // a wrong ESP before we restore it.
182 #pragma runtime_checks( "s", off )
183 #endif
184 
DllVolChange(RAROptions * Cmd,wchar * NextName,size_t NameSize)185 bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize)
186 {
187   bool DllVolChanged=false,DllVolAborted=false;
188 
189   if (Cmd->Callback!=NULL)
190   {
191     wchar OrgNextName[NM];
192     wcscpy(OrgNextName,NextName);
193     if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NextName,RAR_VOL_ASK)==-1)
194       DllVolAborted=true;
195     else
196       if (wcscmp(OrgNextName,NextName)!=0)
197         DllVolChanged=true;
198       else
199       {
200         char NextNameA[NM],OrgNextNameA[NM];
201         WideToChar(NextName,NextNameA,ASIZE(NextNameA));
202         strcpy(OrgNextNameA,NextNameA);
203         if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextNameA,RAR_VOL_ASK)==-1)
204           DllVolAborted=true;
205         else
206           if (strcmp(OrgNextNameA,NextNameA)!=0)
207           {
208             // We can damage some Unicode characters by U->A->U conversion,
209             // so set Unicode name only if we see that ANSI name is changed.
210             CharToWide(NextNameA,NextName,NameSize);
211             DllVolChanged=true;
212           }
213       }
214   }
215   if (!DllVolChanged && Cmd->ChangeVolProc!=NULL)
216   {
217     char NextNameA[NM];
218     WideToChar(NextName,NextNameA,ASIZE(NextNameA));
219     // Here we preserve ESP value. It is necessary for those developers,
220     // who still define ChangeVolProc callback as "C" type function,
221     // even though in year 2001 we announced in unrar.dll whatsnew.txt
222     // that it will be PASCAL type (for compatibility with Visual Basic).
223 #if defined(_MSC_VER)
224 #ifndef _WIN_64
225     __asm mov ebx,esp
226 #endif
227 #elif defined(_WIN_ALL) && defined(__BORLANDC__)
228     _EBX=_ESP;
229 #endif
230     int RetCode=Cmd->ChangeVolProc(NextNameA,RAR_VOL_ASK);
231 
232     // Restore ESP after ChangeVolProc with wrongly defined calling
233     // convention broken it.
234 #if defined(_MSC_VER)
235 #ifndef _WIN_64
236     __asm mov esp,ebx
237 #endif
238 #elif defined(_WIN_ALL) && defined(__BORLANDC__)
239     _ESP=_EBX;
240 #endif
241     if (RetCode==0)
242       DllVolAborted=true;
243     else
244       CharToWide(NextNameA,NextName,NameSize);
245   }
246 
247   // We quit only on 'abort' condition, but not on 'name not changed'.
248   // It is legitimate for program to return the same name when waiting
249   // for currently non-existent volume.
250   if (DllVolAborted)
251   {
252     Cmd->DllError=ERAR_EOPEN;
253     return false;
254   }
255   return true;
256 }
257 #endif
258 
259 
260 #ifdef RARDLL
DllVolNotify(RAROptions * Cmd,wchar * NextName)261 bool DllVolNotify(RAROptions *Cmd,wchar *NextName)
262 {
263   char NextNameA[NM];
264   WideToChar(NextName,NextNameA,ASIZE(NextNameA));
265   if (Cmd->Callback!=NULL)
266   {
267     if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NextName,RAR_VOL_NOTIFY)==-1)
268       return false;
269     if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextNameA,RAR_VOL_NOTIFY)==-1)
270       return false;
271   }
272   if (Cmd->ChangeVolProc!=NULL)
273   {
274 #if defined(_WIN_ALL) && !defined(_MSC_VER) && !defined(__MINGW32__)
275     _EBX=_ESP;
276 #endif
277     int RetCode=Cmd->ChangeVolProc(NextNameA,RAR_VOL_NOTIFY);
278 #if defined(_WIN_ALL) && !defined(_MSC_VER) && !defined(__MINGW32__)
279     _ESP=_EBX;
280 #endif
281     if (RetCode==0)
282       return false;
283   }
284   return true;
285 }
286 
287 #if defined(RARDLL) && defined(_MSC_VER) && !defined(_WIN_64)
288 // Restore the run time stack check for unrar.dll.
289 #pragma runtime_checks( "s", restore )
290 #endif
291 #endif
292