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