xref: /aosp_15_r20/external/lzma/CPP/7zip/UI/FileManager/PanelSplitFile.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // PanelSplitFile.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../Common/IntToString.h"
6 
7 #include "../../../Windows/ErrorMsg.h"
8 #include "../../../Windows/FileName.h"
9 
10 #include "../GUI/ExtractRes.h"
11 
12 #include "resource.h"
13 
14 #include "App.h"
15 #include "CopyDialog.h"
16 #include "FormatUtils.h"
17 #include "LangUtils.h"
18 #include "SplitDialog.h"
19 #include "SplitUtils.h"
20 
21 #include "PropertyNameRes.h"
22 
23 using namespace NWindows;
24 using namespace NFile;
25 using namespace NDir;
26 
27 static const char * const g_Message_FileWriteError = "File write error";
28 
29 struct CVolSeqName
30 {
31   UString UnchangedPart;
32   UString ChangedPart;
CVolSeqNameCVolSeqName33   CVolSeqName(): ChangedPart("000") {}
34 
SetNumDigitsCVolSeqName35   void SetNumDigits(UInt64 numVolumes)
36   {
37     ChangedPart = "000";
38     while (numVolumes > 999)
39     {
40       numVolumes /= 10;
41       ChangedPart.Add_Char('0');
42     }
43   }
44 
ParseNameCVolSeqName45   bool ParseName(const UString &name)
46   {
47     if (name.Len() < 2)
48       return false;
49     if (name.Back() != L'1' || name[name.Len() - 2] != L'0')
50       return false;
51 
52     unsigned pos = name.Len() - 2;
53     for (; pos > 0 && name[pos - 1] == '0'; pos--);
54     UnchangedPart.SetFrom(name, pos);
55     ChangedPart = name.Ptr(pos);
56     return true;
57   }
58 
59   UString GetNextName();
60 };
61 
62 
GetNextName()63 UString CVolSeqName::GetNextName()
64 {
65   for (int i = (int)ChangedPart.Len() - 1; i >= 0; i--)
66   {
67     const wchar_t c = ChangedPart[i];
68     if (c != L'9')
69     {
70       ChangedPart.ReplaceOneCharAtPos((unsigned)i, (wchar_t)(c + 1));
71       break;
72     }
73     ChangedPart.ReplaceOneCharAtPos((unsigned)i, L'0');
74     if (i == 0)
75       ChangedPart.InsertAtFront(L'1');
76   }
77   return UnchangedPart + ChangedPart;
78 }
79 
80 class CThreadSplit: public CProgressThreadVirt
81 {
82   HRESULT ProcessVirt() Z7_override;
83 public:
84   FString FilePath;
85   FString VolBasePath;
86   UInt64 NumVolumes;
87   CRecordVector<UInt64> VolumeSizes;
88 };
89 
90 
91 class CPreAllocOutFile
92 {
93   UInt64 _preAllocSize;
94 public:
95   NIO::COutFile File;
96   UInt64 Written;
97 
CPreAllocOutFile()98   CPreAllocOutFile(): _preAllocSize(0), Written(0) {}
99 
~CPreAllocOutFile()100   ~CPreAllocOutFile()
101   {
102     SetCorrectFileLength();
103   }
104 
PreAlloc(UInt64 preAllocSize)105   void PreAlloc(UInt64 preAllocSize)
106   {
107     _preAllocSize = 0;
108     if (File.SetLength(preAllocSize))
109       _preAllocSize = preAllocSize;
110     File.SeekToBegin();
111   }
112 
Write(const void * data,UInt32 size,UInt32 & processedSize)113   bool Write(const void *data, UInt32 size, UInt32 &processedSize) throw()
114   {
115     bool res = File.Write(data, size, processedSize);
116     Written += processedSize;
117     return res;
118   }
119 
Close()120   void Close()
121   {
122     SetCorrectFileLength();
123     Written = 0;
124     _preAllocSize = 0;
125     File.Close();
126   }
127 
SetCorrectFileLength()128   void SetCorrectFileLength()
129   {
130     if (Written < _preAllocSize)
131     {
132       File.SetLength(Written);
133       _preAllocSize = 0;
134     }
135   }
136 };
137 
138 
139 static const UInt32 kBufSize = (1 << 20);
140 
ProcessVirt()141 HRESULT CThreadSplit::ProcessVirt()
142 {
143   NIO::CInFile inFile;
144   if (!inFile.Open(FilePath))
145     return GetLastError_noZero_HRESULT();
146 
147   CPreAllocOutFile outFile;
148 
149   CMyBuffer buffer;
150   if (!buffer.Allocate(kBufSize))
151     return E_OUTOFMEMORY;
152 
153   CVolSeqName seqName;
154   seqName.SetNumDigits(NumVolumes);
155 
156   UInt64 length;
157   if (!inFile.GetLength(length))
158     return GetLastError_noZero_HRESULT();
159 
160   CProgressSync &sync = Sync;
161   sync.Set_NumBytesTotal(length);
162 
163   UInt64 pos = 0;
164   UInt64 prev = 0;
165   UInt64 numFiles = 0;
166   unsigned volIndex = 0;
167 
168   for (;;)
169   {
170     UInt64 volSize;
171     if (volIndex < VolumeSizes.Size())
172       volSize = VolumeSizes[volIndex];
173     else
174       volSize = VolumeSizes.Back();
175 
176     UInt32 needSize = kBufSize;
177     {
178       const UInt64 rem = volSize - outFile.Written;
179       if (needSize > rem)
180         needSize = (UInt32)rem;
181     }
182     UInt32 processedSize;
183     if (!inFile.Read(buffer, needSize, processedSize))
184       return GetLastError_noZero_HRESULT();
185     if (processedSize == 0)
186       return S_OK;
187     needSize = processedSize;
188 
189     if (outFile.Written == 0)
190     {
191       FString name = VolBasePath;
192       name.Add_Dot();
193       name += us2fs(seqName.GetNextName());
194       sync.Set_FilePath(fs2us(name));
195       if (!outFile.File.Create_NEW(name))
196       {
197         const HRESULT res = GetLastError_noZero_HRESULT();
198         AddErrorPath(name);
199         return res;
200       }
201       UInt64 expectSize = volSize;
202       if (pos < length)
203       {
204         const UInt64 rem = length - pos;
205         if (expectSize > rem)
206           expectSize = rem;
207       }
208       outFile.PreAlloc(expectSize);
209     }
210 
211     if (!outFile.Write(buffer, needSize, processedSize))
212       return GetLastError_noZero_HRESULT();
213     if (needSize != processedSize)
214       throw g_Message_FileWriteError;
215 
216     pos += processedSize;
217 
218     if (outFile.Written == volSize)
219     {
220       outFile.Close();
221       sync.Set_NumFilesCur(++numFiles);
222       if (volIndex < VolumeSizes.Size())
223         volIndex++;
224     }
225 
226     if (pos - prev >= ((UInt32)1 << 22) || outFile.Written == 0)
227     {
228       RINOK(sync.Set_NumBytesCur(pos))
229       prev = pos;
230     }
231   }
232 }
233 
234 
Split()235 void CApp::Split()
236 {
237   const unsigned srcPanelIndex = GetFocusedPanelIndex();
238   CPanel &srcPanel = Panels[srcPanelIndex];
239   if (!srcPanel.Is_IO_FS_Folder())
240   {
241     srcPanel.MessageBox_Error_UnsupportOperation();
242     return;
243   }
244   CRecordVector<UInt32> indices;
245   srcPanel.Get_ItemIndices_Operated(indices);
246   if (indices.IsEmpty())
247     return;
248   if (indices.Size() != 1)
249   {
250     srcPanel.MessageBox_Error_LangID(IDS_SELECT_ONE_FILE);
251     return;
252   }
253   const unsigned index = indices[0];
254   if (srcPanel.IsItem_Folder(index))
255   {
256     srcPanel.MessageBox_Error_LangID(IDS_SELECT_ONE_FILE);
257     return;
258   }
259   const UString itemName = srcPanel.GetItemName(index);
260 
261   const UString srcPath = srcPanel.GetFsPath() + srcPanel.GetItemPrefix(index);
262   UString path = srcPath;
263   unsigned destPanelIndex = (NumPanels <= 1) ? srcPanelIndex : (1 - srcPanelIndex);
264   CPanel &destPanel = Panels[destPanelIndex];
265   if (NumPanels > 1)
266     if (destPanel.IsFSFolder())
267       path = destPanel.GetFsPath();
268   CSplitDialog splitDialog;
269   splitDialog.FilePath = srcPanel.GetItemRelPath(index);
270   splitDialog.Path = path;
271   if (splitDialog.Create(srcPanel.GetParent()) != IDOK)
272     return;
273 
274   NFind::CFileInfo fileInfo;
275   if (!fileInfo.Find(us2fs(srcPath + itemName)))
276   {
277     srcPanel.MessageBox_Error(L"Cannot find file");
278     return;
279   }
280   if (fileInfo.Size <= splitDialog.VolumeSizes.FrontItem())
281   {
282     srcPanel.MessageBox_Error_LangID(IDS_SPLIT_VOL_MUST_BE_SMALLER);
283     return;
284   }
285   const UInt64 numVolumes = GetNumberOfVolumes(fileInfo.Size, splitDialog.VolumeSizes);
286   if (numVolumes >= 100)
287   {
288     wchar_t s[32];
289     ConvertUInt64ToString(numVolumes, s);
290     if (::MessageBoxW(srcPanel, MyFormatNew(IDS_SPLIT_CONFIRM_MESSAGE, s),
291         LangString(IDS_SPLIT_CONFIRM_TITLE),
292         MB_YESNOCANCEL | MB_ICONQUESTION) != IDYES)
293       return;
294   }
295 
296   path = splitDialog.Path;
297   NName::NormalizeDirPathPrefix(path);
298   if (!CreateComplexDir(us2fs(path)))
299   {
300     const HRESULT lastError = GetLastError_noZero_HRESULT();
301     srcPanel.MessageBox_Error_2Lines_Message_HRESULT(MyFormatNew(IDS_CANNOT_CREATE_FOLDER, path), lastError);
302     return;
303   }
304 
305   {
306   CThreadSplit spliter;
307   spliter.NumVolumes = numVolumes;
308 
309   CProgressDialog &progressDialog = spliter;
310 
311   const UString progressWindowTitle ("7-Zip"); // LangString(IDS_APP_TITLE, 0x03000000);
312   const UString title = LangString(IDS_SPLITTING);
313 
314   progressDialog.ShowCompressionInfo = false;
315 
316   progressDialog.MainWindow = _window;
317   progressDialog.MainTitle = progressWindowTitle;
318   progressDialog.MainAddTitle = title;
319   progressDialog.MainAddTitle.Add_Space();
320   progressDialog.Sync.Set_TitleFileName(itemName);
321 
322 
323   spliter.FilePath = us2fs(srcPath + itemName);
324   spliter.VolBasePath = us2fs(path + srcPanel.GetItemName_for_Copy(index));
325   spliter.VolumeSizes = splitDialog.VolumeSizes;
326 
327   // if (splitDialog.VolumeSizes.Size() == 0) return;
328 
329   // CPanel::CDisableTimerProcessing disableTimerProcessing1(srcPanel);
330   // CPanel::CDisableTimerProcessing disableTimerProcessing2(destPanel);
331 
332   if (spliter.Create(title, _window) != 0)
333     return;
334   }
335   RefreshTitleAlways();
336 
337 
338   // disableNotify.Restore();
339   // disableNotify.Restore();
340   // srcPanel.SetFocusToList();
341   // srcPanel.RefreshListCtrlSaveFocused();
342 }
343 
344 
345 class CThreadCombine: public CProgressThreadVirt
346 {
347   HRESULT ProcessVirt() Z7_override;
348 public:
349   FString InputDirPrefix;
350   FStringVector Names;
351   FString OutputPath;
352   UInt64 TotalSize;
353 };
354 
ProcessVirt()355 HRESULT CThreadCombine::ProcessVirt()
356 {
357   NIO::COutFile outFile;
358   if (!outFile.Create_NEW(OutputPath))
359   {
360     const HRESULT res = GetLastError_noZero_HRESULT();
361     AddErrorPath(OutputPath);
362     return res;
363   }
364 
365   CProgressSync &sync = Sync;
366   sync.Set_NumBytesTotal(TotalSize);
367 
368   CMyBuffer bufferObject;
369   if (!bufferObject.Allocate(kBufSize))
370     return E_OUTOFMEMORY;
371   Byte *buffer = (Byte *)(void *)bufferObject;
372   UInt64 pos = 0;
373   FOR_VECTOR (i, Names)
374   {
375     NIO::CInFile inFile;
376     const FString nextName = InputDirPrefix + Names[i];
377     if (!inFile.Open(nextName))
378     {
379       const HRESULT res = GetLastError_noZero_HRESULT();
380       AddErrorPath(nextName);
381       return res;
382     }
383     sync.Set_FilePath(fs2us(nextName));
384     for (;;)
385     {
386       UInt32 processedSize;
387       if (!inFile.Read(buffer, kBufSize, processedSize))
388       {
389         const HRESULT res = GetLastError_noZero_HRESULT();
390         AddErrorPath(nextName);
391         return res;
392       }
393       if (processedSize == 0)
394         break;
395       const UInt32 needSize = processedSize;
396       if (!outFile.Write(buffer, needSize, processedSize))
397       {
398         const HRESULT res = GetLastError_noZero_HRESULT();
399         AddErrorPath(OutputPath);
400         return res;
401       }
402       if (needSize != processedSize)
403         throw g_Message_FileWriteError;
404       pos += processedSize;
405       RINOK(sync.Set_NumBytesCur(pos))
406     }
407   }
408   return S_OK;
409 }
410 
411 extern void AddValuePair2(UString &s, UINT resourceID, UInt64 num, UInt64 size);
412 
AddInfoFileName(UString & dest,const UString & name)413 static void AddInfoFileName(UString &dest, const UString &name)
414 {
415   dest += "\n  ";
416   dest += name;
417 }
418 
Combine()419 void CApp::Combine()
420 {
421   const unsigned srcPanelIndex = GetFocusedPanelIndex();
422   CPanel &srcPanel = Panels[srcPanelIndex];
423   if (!srcPanel.IsFSFolder())
424   {
425     srcPanel.MessageBox_Error_LangID(IDS_OPERATION_IS_NOT_SUPPORTED);
426     return;
427   }
428   CRecordVector<UInt32> indices;
429   srcPanel.Get_ItemIndices_Operated(indices);
430   if (indices.IsEmpty())
431     return;
432   const unsigned index = indices[0];
433   if (indices.Size() != 1 || srcPanel.IsItem_Folder(index))
434   {
435     srcPanel.MessageBox_Error_LangID(IDS_COMBINE_SELECT_ONE_FILE);
436     return;
437   }
438   const UString itemName = srcPanel.GetItemName(index);
439 
440   UString srcPath = srcPanel.GetFsPath() + srcPanel.GetItemPrefix(index);
441   UString path = srcPath;
442   unsigned destPanelIndex = (NumPanels <= 1) ? srcPanelIndex : (1 - srcPanelIndex);
443   CPanel &destPanel = Panels[destPanelIndex];
444   if (NumPanels > 1)
445     if (destPanel.IsFSFolder())
446       path = destPanel.GetFsPath();
447 
448   CVolSeqName volSeqName;
449   if (!volSeqName.ParseName(itemName))
450   {
451     srcPanel.MessageBox_Error_LangID(IDS_COMBINE_CANT_DETECT_SPLIT_FILE);
452     return;
453   }
454 
455   {
456   CThreadCombine combiner;
457 
458   UString nextName = itemName;
459   combiner.TotalSize = 0;
460   for (;;)
461   {
462     NFind::CFileInfo fileInfo;
463     if (!fileInfo.Find(us2fs(srcPath + nextName)) || fileInfo.IsDir())
464       break;
465     combiner.Names.Add(us2fs(nextName));
466     combiner.TotalSize += fileInfo.Size;
467     nextName = volSeqName.GetNextName();
468   }
469   if (combiner.Names.Size() == 1)
470   {
471     srcPanel.MessageBox_Error_LangID(IDS_COMBINE_CANT_FIND_MORE_THAN_ONE_PART);
472     return;
473   }
474 
475   if (combiner.TotalSize == 0)
476   {
477     srcPanel.MessageBox_Error(L"No data");
478     return;
479   }
480 
481   UString info;
482   AddValuePair2(info, IDS_PROP_FILES, combiner.Names.Size(), combiner.TotalSize);
483 
484   info.Add_LF();
485   info += srcPath;
486 
487   unsigned i;
488   for (i = 0; i < combiner.Names.Size() && i < 2; i++)
489     AddInfoFileName(info, fs2us(combiner.Names[i]));
490   if (i != combiner.Names.Size())
491   {
492     if (i + 1 != combiner.Names.Size())
493       AddInfoFileName(info, L"...");
494     AddInfoFileName(info, fs2us(combiner.Names.Back()));
495   }
496 
497   {
498     CCopyDialog copyDialog;
499     copyDialog.Value = path;
500     LangString(IDS_COMBINE, copyDialog.Title);
501     copyDialog.Title.Add_Space();
502     copyDialog.Title += srcPanel.GetItemRelPath(index);
503     LangString(IDS_COMBINE_TO, copyDialog.Static);
504     copyDialog.Info = info;
505     if (copyDialog.Create(srcPanel.GetParent()) != IDOK)
506       return;
507     path = copyDialog.Value;
508   }
509 
510   NName::NormalizeDirPathPrefix(path);
511   if (!CreateComplexDir(us2fs(path)))
512   {
513     const HRESULT lastError = GetLastError_noZero_HRESULT();
514     srcPanel.MessageBox_Error_2Lines_Message_HRESULT(MyFormatNew(IDS_CANNOT_CREATE_FOLDER, path), lastError);
515     return;
516   }
517 
518   UString outName = volSeqName.UnchangedPart;
519   while (!outName.IsEmpty())
520   {
521     if (outName.Back() != L'.')
522       break;
523     outName.DeleteBack();
524   }
525   if (outName.IsEmpty())
526     outName = "file";
527 
528   NFind::CFileInfo fileInfo;
529   UString destFilePath = path + outName;
530   combiner.OutputPath = us2fs(destFilePath);
531   if (fileInfo.Find(combiner.OutputPath))
532   {
533     srcPanel.MessageBox_Error(MyFormatNew(IDS_FILE_EXIST, destFilePath));
534     return;
535   }
536 
537     CProgressDialog &progressDialog = combiner;
538     progressDialog.ShowCompressionInfo = false;
539 
540     const UString progressWindowTitle ("7-Zip"); // LangString(IDS_APP_TITLE, 0x03000000);
541     const UString title = LangString(IDS_COMBINING);
542 
543     progressDialog.MainWindow = _window;
544     progressDialog.MainTitle = progressWindowTitle;
545     progressDialog.MainAddTitle = title;
546     progressDialog.MainAddTitle.Add_Space();
547 
548     combiner.InputDirPrefix = us2fs(srcPath);
549 
550     // CPanel::CDisableTimerProcessing disableTimerProcessing1(srcPanel);
551     // CPanel::CDisableTimerProcessing disableTimerProcessing2(destPanel);
552 
553     if (combiner.Create(title, _window) != 0)
554       return;
555   }
556   RefreshTitleAlways();
557 
558   // disableNotify.Restore();
559   // disableNotify.Restore();
560   // srcPanel.SetFocusToList();
561   // srcPanel.RefreshListCtrlSaveFocused();
562 }
563