// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; using System.IO; using System.IO.Compression; using System.Linq; using System.Threading; namespace WPinternals { internal class BackupViewModel: ContextViewModel { private PhoneNotifierViewModel PhoneNotifier; private Action Callback; private Action SwitchToUnlockBoot; internal BackupViewModel(PhoneNotifierViewModel PhoneNotifier, Action SwitchToUnlockBoot, Action Callback) : base() { IsFlashModeOperation = true; this.PhoneNotifier = PhoneNotifier; this.SwitchToUnlockBoot = SwitchToUnlockBoot; this.Callback = Callback; } internal override void EvaluateViewState() { if (!IsActive) return; if (SubContextViewModel == null) { ActivateSubContext(new BackupTargetSelectionViewModel(PhoneNotifier, SwitchToUnlockBoot, DoBackupArchive, DoBackup)); IsSwitchingInterface = false; } if (SubContextViewModel is BackupTargetSelectionViewModel) ((BackupTargetSelectionViewModel)SubContextViewModel).EvaluateViewState(); } internal async void DoBackup(string EFIESPPath, string MainOSPath, string DataPath) { try { IsSwitchingInterface = true; await SwitchModeViewModel.SwitchToWithProgress(PhoneNotifier, PhoneInterfaces.Lumia_MassStorage, (msg, sub) => ActivateSubContext(new BusyViewModel(msg, sub))); BackupTask(EFIESPPath, MainOSPath, DataPath); } catch (Exception Ex) { ActivateSubContext(new MessageViewModel(Ex.Message, Callback)); } } internal async void DoBackupArchive(string ArchivePath) { try { IsSwitchingInterface = true; await SwitchModeViewModel.SwitchToWithProgress(PhoneNotifier, PhoneInterfaces.Lumia_MassStorage, (msg, sub) => ActivateSubContext(new BusyViewModel(msg, sub))); BackupArchiveTask(ArchivePath); } catch (Exception Ex) { ActivateSubContext(new MessageViewModel(Ex.Message, Callback)); } } internal void BackupTask(string EFIESPPath, string MainOSPath, string DataPath) { IsSwitchingInterface = false; new Thread(() => { bool Result = true; ActivateSubContext(new BusyViewModel("Initializing backup...")); ulong TotalSizeSectors = 0; int PartitionCount = 0; MassStorage Phone = (MassStorage)PhoneNotifier.CurrentModel; Phone.OpenVolume(false); byte[] GPTBuffer = Phone.ReadSectors(1, 33); GPT GPT = new WPinternals.GPT(GPTBuffer); Partition Partition; try { if (EFIESPPath != null) { Partition = GPT.Partitions.Where(p => p.Name == "EFIESP").First(); TotalSizeSectors += Partition.SizeInSectors; PartitionCount++; } if (MainOSPath != null) { Partition = GPT.Partitions.Where(p => p.Name == "MainOS").First(); TotalSizeSectors += Partition.SizeInSectors; PartitionCount++; } if (DataPath != null) { Partition = GPT.Partitions.Where(p => p.Name == "Data").First(); TotalSizeSectors += Partition.SizeInSectors; PartitionCount++; } } catch (Exception Ex) { LogFile.LogException(Ex); Result = false; } BusyViewModel Busy = new BusyViewModel("Create backup...", MaxProgressValue: TotalSizeSectors, UIContext: UIContext); ProgressUpdater Updater = Busy.ProgressUpdater; ActivateSubContext(Busy); int i = 0; if (Result) { try { if (EFIESPPath != null) { i++; Busy.Message = "Create backup of partition EFIESP (" + i.ToString() + @"/" + PartitionCount.ToString() + ")"; Phone.BackupPartition("EFIESP", EFIESPPath, Updater); } } catch (Exception Ex) { LogFile.LogException(Ex); Result = false; } } if (Result) { try { if (MainOSPath != null) { i++; Busy.Message = "Create backup of partition MainOS (" + i.ToString() + @"/" + PartitionCount.ToString() + ")"; Phone.BackupPartition("MainOS", MainOSPath, Updater); } } catch (Exception Ex) { LogFile.LogException(Ex); Result = false; } } if (Result) { try { if (DataPath != null) { i++; Busy.Message = "Create backup of partition Data (" + i.ToString() + @"/" + PartitionCount.ToString() + ")"; Phone.BackupPartition("Data", DataPath, Updater); } } catch (Exception Ex) { LogFile.LogException(Ex); Result = false; } } Phone.CloseVolume(); if (!Result) { ActivateSubContext(new MessageViewModel("Failed to create backup!", Exit)); return; } ActivateSubContext(new MessageViewModel("Successfully created a backup!", Exit)); }).Start(); } internal void BackupArchiveTask(string ArchivePath) { IsSwitchingInterface = false; new Thread(() => { bool Result = true; ActivateSubContext(new BusyViewModel("Initializing backup...")); ulong TotalSizeSectors = 0; int PartitionCount = 3; MassStorage Phone = (MassStorage)PhoneNotifier.CurrentModel; try { Phone.OpenVolume(false); byte[] GPTBuffer = Phone.ReadSectors(1, 33); GPT GPT = new WPinternals.GPT(GPTBuffer); Partition Partition; try { Partition = GPT.Partitions.Where(p => p.Name == "EFIESP").First(); TotalSizeSectors += Partition.SizeInSectors; Partition = GPT.Partitions.Where(p => p.Name == "MainOS").First(); TotalSizeSectors += Partition.SizeInSectors; Partition = GPT.Partitions.Where(p => p.Name == "Data").First(); TotalSizeSectors += Partition.SizeInSectors; } catch (Exception Ex) { LogFile.LogException(Ex); Result = false; } BusyViewModel Busy = new BusyViewModel("Create backup...", MaxProgressValue: TotalSizeSectors, UIContext: UIContext); ProgressUpdater Updater = Busy.ProgressUpdater; ActivateSubContext(Busy); ZipArchiveEntry Entry; Stream EntryStream = null; using (FileStream FileStream = new FileStream(ArchivePath, FileMode.Create)) { using (ZipArchive Archive = new ZipArchive(FileStream, ZipArchiveMode.Create)) { int i = 0; if (Result) { try { Entry = Archive.CreateEntry("EFIESP.bin", CompressionLevel.Optimal); EntryStream = Entry.Open(); i++; Busy.Message = "Create backup of partition EFIESP (" + i.ToString() + @"/" + PartitionCount.ToString() + ")"; Phone.BackupPartition("EFIESP", EntryStream, Updater); } catch (Exception Ex) { LogFile.LogException(Ex); Result = false; } finally { if (EntryStream != null) EntryStream.Close(); EntryStream = null; } } if (Result) { try { Entry = Archive.CreateEntry("MainOS.bin", CompressionLevel.Optimal); EntryStream = Entry.Open(); i++; Busy.Message = "Create backup of partition MainOS (" + i.ToString() + @"/" + PartitionCount.ToString() + ")"; Phone.BackupPartition("MainOS", EntryStream, Updater); } catch (Exception Ex) { LogFile.LogException(Ex); Result = false; } finally { if (EntryStream != null) EntryStream.Close(); EntryStream = null; } } if (Result) { try { Entry = Archive.CreateEntry("Data.bin", CompressionLevel.Optimal); EntryStream = Entry.Open(); i++; Busy.Message = "Create backup of partition Data (" + i.ToString() + @"/" + PartitionCount.ToString() + ")"; Phone.BackupPartition("Data", EntryStream, Updater); } catch (Exception Ex) { LogFile.LogException(Ex); Result = false; } finally { if (EntryStream != null) EntryStream.Close(); EntryStream = null; } } } } } catch { } finally { Phone.CloseVolume(); } if (!Result) { ActivateSubContext(new MessageViewModel("Failed to create backup!", Exit)); return; } ActivateSubContext(new MessageViewModel("Successfully created a backup!", Exit)); }).Start(); } private void Exit() { IsSwitchingInterface = false; ActivateSubContext(null); Callback(); } } }