mirror of
https://github.com/ReneLergner/WPinternals.git
synced 2026-06-17 04:40:12 +10:00
Initial commit - WPinternals 2.6
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
// 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.
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class AboutViewModel: ContextViewModel
|
||||
{
|
||||
internal AboutViewModel() : base() { }
|
||||
|
||||
public int MajorVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Major;
|
||||
}
|
||||
}
|
||||
|
||||
public int MinorVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Minor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
// 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.Threading;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class BackupTargetSelectionViewModel: ContextViewModel
|
||||
{
|
||||
private PhoneNotifierViewModel PhoneNotifier;
|
||||
private Action<string, string, string> BackupCallback;
|
||||
private Action<string> BackupArchiveCallback;
|
||||
internal Action SwitchToUnlockBoot;
|
||||
|
||||
internal BackupTargetSelectionViewModel(PhoneNotifierViewModel PhoneNotifier, Action SwitchToUnlockBoot, Action<string> BackupArchiveCallback, Action<string, string, string> BackupCallback)
|
||||
: base()
|
||||
{
|
||||
this.PhoneNotifier = PhoneNotifier;
|
||||
this.BackupCallback = BackupCallback;
|
||||
this.BackupArchiveCallback = BackupArchiveCallback;
|
||||
this.SwitchToUnlockBoot = SwitchToUnlockBoot;
|
||||
|
||||
this.PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
||||
this.PhoneNotifier.DeviceRemoved += DeviceRemoved;
|
||||
|
||||
new Thread(() => EvaluateViewState()).Start();
|
||||
}
|
||||
|
||||
private string _ArchivePath;
|
||||
public string ArchivePath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _ArchivePath;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _ArchivePath)
|
||||
{
|
||||
_ArchivePath = value;
|
||||
OnPropertyChanged("ArchivePath");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _EFIESPPath;
|
||||
public string EFIESPPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _EFIESPPath;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _EFIESPPath)
|
||||
{
|
||||
_EFIESPPath = value;
|
||||
OnPropertyChanged("EFIESPPath");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _MainOSPath;
|
||||
public string MainOSPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _MainOSPath;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _MainOSPath)
|
||||
{
|
||||
_MainOSPath = value;
|
||||
OnPropertyChanged("MainOSPath");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _DataPath;
|
||||
public string DataPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _DataPath;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _DataPath)
|
||||
{
|
||||
_DataPath = value;
|
||||
OnPropertyChanged("DataPath");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _IsPhoneDisconnected;
|
||||
public bool IsPhoneDisconnected
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IsPhoneDisconnected;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _IsPhoneDisconnected)
|
||||
{
|
||||
_IsPhoneDisconnected = value;
|
||||
OnPropertyChanged("IsPhoneDisconnected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _IsPhoneInMassStorage;
|
||||
public bool IsPhoneInMassStorage
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IsPhoneInMassStorage;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _IsPhoneInMassStorage)
|
||||
{
|
||||
_IsPhoneInMassStorage = value;
|
||||
OnPropertyChanged("IsPhoneInMassStorage");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _IsPhoneInOtherMode;
|
||||
public bool IsPhoneInOtherMode
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IsPhoneInOtherMode;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _IsPhoneInOtherMode)
|
||||
{
|
||||
_IsPhoneInOtherMode = value;
|
||||
OnPropertyChanged("IsPhoneInOtherMode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DelegateCommand _BackupArchiveCommand;
|
||||
public DelegateCommand BackupArchiveCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_BackupArchiveCommand == null)
|
||||
{
|
||||
_BackupArchiveCommand = new DelegateCommand(() => { BackupArchiveCallback(ArchivePath); }, () => ((ArchivePath != null) && (PhoneNotifier.CurrentInterface != null)));
|
||||
}
|
||||
return _BackupArchiveCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private DelegateCommand _BackupCommand;
|
||||
public DelegateCommand BackupCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_BackupCommand == null)
|
||||
{
|
||||
_BackupCommand = new DelegateCommand(() => { BackupCallback(EFIESPPath, MainOSPath, DataPath); }, () => (((EFIESPPath != null) || (MainOSPath != null) || (DataPath != null)) && (PhoneNotifier.CurrentInterface != null)));
|
||||
}
|
||||
return _BackupCommand;
|
||||
}
|
||||
}
|
||||
|
||||
~BackupTargetSelectionViewModel()
|
||||
{
|
||||
PhoneNotifier.NewDeviceArrived -= NewDeviceArrived;
|
||||
}
|
||||
|
||||
void NewDeviceArrived(ArrivalEventArgs Args)
|
||||
{
|
||||
new Thread(() => EvaluateViewState()).Start();
|
||||
}
|
||||
|
||||
void DeviceRemoved()
|
||||
{
|
||||
new Thread(() => EvaluateViewState()).Start();
|
||||
}
|
||||
|
||||
internal override void EvaluateViewState()
|
||||
{
|
||||
IsPhoneDisconnected = PhoneNotifier.CurrentInterface == null;
|
||||
IsPhoneInMassStorage = PhoneNotifier.CurrentInterface == PhoneInterfaces.Lumia_MassStorage;
|
||||
IsPhoneInOtherMode = (!IsPhoneDisconnected && !IsPhoneInMassStorage);
|
||||
BackupCommand.RaiseCanExecuteChanged();
|
||||
BackupArchiveCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
// 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.Threading;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class BusyViewModel : ContextViewModel
|
||||
{
|
||||
private ulong MaxProgressValue = 0;
|
||||
internal ProgressUpdater ProgressUpdater = null;
|
||||
|
||||
// UIContext can be passed to BusyViewModel, when it needs to update progress-controls and it is created on a worker-thread.
|
||||
internal BusyViewModel(string Message, string SubMessage = null, ulong? MaxProgressValue = null, SynchronizationContext UIContext = null, bool ShowAnimation = true)
|
||||
{
|
||||
LogFile.Log(Message);
|
||||
|
||||
if (UIContext == null)
|
||||
this.UIContext = SynchronizationContext.Current;
|
||||
else
|
||||
this.UIContext = UIContext;
|
||||
|
||||
this.Message = Message;
|
||||
this.SubMessage = SubMessage;
|
||||
this.ShowAnimation = ShowAnimation;
|
||||
if (MaxProgressValue != null)
|
||||
{
|
||||
ProgressPercentage = 0;
|
||||
this.MaxProgressValue = (ulong)MaxProgressValue;
|
||||
ProgressUpdater = new ProgressUpdater((ulong)MaxProgressValue, (p, t) =>
|
||||
{
|
||||
if ((this.UIContext == null) || (this.UIContext == SynchronizationContext.Current))
|
||||
{
|
||||
ProgressPercentage = p;
|
||||
TimeRemaining = t;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.UIContext.Post((s) =>
|
||||
{
|
||||
ProgressPercentage = p;
|
||||
TimeRemaining = t;
|
||||
}, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
internal void SetProgress(ulong Value)
|
||||
{
|
||||
if (ProgressUpdater != null)
|
||||
UIContext.Post((s) => { ProgressUpdater.SetProgress(Value); }, null);
|
||||
}
|
||||
|
||||
private string _Message = null;
|
||||
public string Message
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Message;
|
||||
}
|
||||
set
|
||||
{
|
||||
_Message = value;
|
||||
OnPropertyChanged("Message");
|
||||
}
|
||||
}
|
||||
|
||||
private string _SubMessage = null;
|
||||
public string SubMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return _SubMessage;
|
||||
}
|
||||
set
|
||||
{
|
||||
_SubMessage = value;
|
||||
OnPropertyChanged("SubMessage");
|
||||
}
|
||||
}
|
||||
|
||||
private int? _ProgressPercentage = null;
|
||||
public int? ProgressPercentage
|
||||
{
|
||||
get
|
||||
{
|
||||
return _ProgressPercentage;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_ProgressPercentage != value)
|
||||
{
|
||||
_ProgressPercentage = value;
|
||||
OnPropertyChanged("ProgressPercentage");
|
||||
OnPropertyChanged("ShowAnimation");
|
||||
UpdateProgressText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TimeSpan? _TimeRemaining = null;
|
||||
public TimeSpan? TimeRemaining
|
||||
{
|
||||
get
|
||||
{
|
||||
return _TimeRemaining;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_TimeRemaining != value)
|
||||
{
|
||||
_TimeRemaining = value;
|
||||
OnPropertyChanged("TimeRemaining");
|
||||
UpdateProgressText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateProgressText()
|
||||
{
|
||||
string NewText = null;
|
||||
if (ProgressPercentage != null)
|
||||
{
|
||||
NewText = "Progress: " + ((int)ProgressPercentage).ToString() + "%";
|
||||
}
|
||||
if (TimeRemaining != null)
|
||||
{
|
||||
if (NewText == null)
|
||||
NewText = "";
|
||||
else
|
||||
NewText += " - ";
|
||||
|
||||
NewText += "Estimated time remaining: " + ((TimeSpan)TimeRemaining).ToString(@"h\:mm\:ss");
|
||||
}
|
||||
ProgressText = NewText;
|
||||
}
|
||||
|
||||
private string _ProgressText = null;
|
||||
public string ProgressText
|
||||
{
|
||||
get
|
||||
{
|
||||
return _ProgressText;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_ProgressText != value)
|
||||
{
|
||||
_ProgressText = value;
|
||||
OnPropertyChanged("ProgressText");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _ShowAnimation = true;
|
||||
public bool ShowAnimation
|
||||
{
|
||||
get
|
||||
{
|
||||
return (_ProgressPercentage == null) && _ShowAnimation;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_ShowAnimation != value)
|
||||
{
|
||||
_ShowAnimation = value;
|
||||
OnPropertyChanged("ShowAnimation");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
// 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.ComponentModel;
|
||||
using System.Threading;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class ContextViewModel : INotifyPropertyChanged
|
||||
{
|
||||
protected SynchronizationContext UIContext;
|
||||
|
||||
public bool IsSwitchingInterface = false;
|
||||
public bool IsFlashModeOperation = false;
|
||||
private bool _IsActive = false;
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged = delegate { };
|
||||
|
||||
protected void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
if ((UIContext == null) && (SynchronizationContext.Current != null))
|
||||
UIContext = SynchronizationContext.Current;
|
||||
|
||||
if (this.PropertyChanged != null)
|
||||
{
|
||||
if (SynchronizationContext.Current == UIContext)
|
||||
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
|
||||
else
|
||||
{
|
||||
UIContext.Post((s) => PropertyChanged(this, new PropertyChangedEventArgs(propertyName)), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ContextViewModel _SubContextViewModel;
|
||||
public ContextViewModel SubContextViewModel
|
||||
{
|
||||
get
|
||||
{
|
||||
return _SubContextViewModel;
|
||||
}
|
||||
private set
|
||||
{
|
||||
if (_SubContextViewModel != null)
|
||||
_SubContextViewModel.IsActive = false;
|
||||
_SubContextViewModel = value;
|
||||
if (_SubContextViewModel != null)
|
||||
_SubContextViewModel.IsActive = IsActive;
|
||||
OnPropertyChanged("SubContextViewModel");
|
||||
}
|
||||
}
|
||||
|
||||
internal ContextViewModel()
|
||||
{
|
||||
UIContext = SynchronizationContext.Current;
|
||||
}
|
||||
|
||||
internal ContextViewModel(MainViewModel Main): this()
|
||||
{
|
||||
}
|
||||
|
||||
internal ContextViewModel(MainViewModel Main, ContextViewModel SubContext): this(Main)
|
||||
{
|
||||
SubContextViewModel = SubContext;
|
||||
}
|
||||
|
||||
internal bool IsActive
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IsActive;
|
||||
}
|
||||
set
|
||||
{
|
||||
_IsActive = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual void EvaluateViewState()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
internal void Activate()
|
||||
{
|
||||
IsActive = true;
|
||||
EvaluateViewState();
|
||||
if (SubContextViewModel != null)
|
||||
SubContextViewModel.Activate();
|
||||
}
|
||||
|
||||
internal void ActivateSubContext(ContextViewModel NewSubContext)
|
||||
{
|
||||
if (_SubContextViewModel != null)
|
||||
_SubContextViewModel.IsActive = false;
|
||||
if (NewSubContext != null)
|
||||
{
|
||||
if (IsActive)
|
||||
NewSubContext.Activate();
|
||||
else
|
||||
NewSubContext.IsActive = false;
|
||||
}
|
||||
SubContextViewModel = NewSubContext;
|
||||
}
|
||||
|
||||
internal void SetWorkingStatus(string Message, string SubMessage, ulong? MaxProgressValue, bool ShowAnimation = true, WPinternalsStatus Status = WPinternalsStatus.Undefined)
|
||||
{
|
||||
ActivateSubContext(new BusyViewModel(Message, SubMessage, MaxProgressValue, UIContext: UIContext, ShowAnimation: ShowAnimation));
|
||||
}
|
||||
|
||||
internal void UpdateWorkingStatus(string Message, string SubMessage, ulong? CurrentProgressValue, WPinternalsStatus Status = WPinternalsStatus.Undefined)
|
||||
{
|
||||
if (SubContextViewModel is BusyViewModel)
|
||||
{
|
||||
BusyViewModel Busy = (BusyViewModel)SubContextViewModel;
|
||||
if (Message != null)
|
||||
{
|
||||
Busy.Message = Message;
|
||||
Busy.SubMessage = SubMessage;
|
||||
}
|
||||
if ((CurrentProgressValue != null) && (Busy.ProgressUpdater != null))
|
||||
{
|
||||
try
|
||||
{
|
||||
Busy.ProgressUpdater.SetProgress((ulong)CurrentProgressValue);
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// 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 Microsoft.Win32;
|
||||
using System;
|
||||
using System.Windows;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class DisclaimerAndNdaViewModel : ContextViewModel
|
||||
{
|
||||
Action Accepted;
|
||||
|
||||
internal DisclaimerAndNdaViewModel(Action Accepted)
|
||||
: base()
|
||||
{
|
||||
this.Accepted = Accepted;
|
||||
}
|
||||
|
||||
private DelegateCommand _ExitCommand = null;
|
||||
public DelegateCommand ExitCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_ExitCommand == null)
|
||||
{
|
||||
_ExitCommand = new DelegateCommand(() =>
|
||||
{
|
||||
Application.Current.Shutdown();
|
||||
});
|
||||
}
|
||||
return _ExitCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private DelegateCommand _ContinueCommand = null;
|
||||
public DelegateCommand ContinueCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_ContinueCommand == null)
|
||||
{
|
||||
_ContinueCommand = new DelegateCommand(() =>
|
||||
{
|
||||
Registry.CurrentUser.OpenSubKey("Software\\WPInternals", true).SetValue("DisclaimerAccepted", 1, RegistryValueKind.DWord);
|
||||
Registry.CurrentUser.OpenSubKey("Software\\WPInternals", true).SetValue("NdaAccepted", 1, RegistryValueKind.DWord);
|
||||
Accepted();
|
||||
});
|
||||
}
|
||||
return _ContinueCommand;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// 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 Microsoft.Win32;
|
||||
using System;
|
||||
using System.Windows;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal delegate void DisclaimerAcceptedHandler();
|
||||
|
||||
internal class DisclaimerViewModel : ContextViewModel
|
||||
{
|
||||
Action Accepted;
|
||||
|
||||
internal DisclaimerViewModel(Action Accepted)
|
||||
: base()
|
||||
{
|
||||
this.Accepted = Accepted;
|
||||
}
|
||||
|
||||
private DelegateCommand _ExitCommand = null;
|
||||
public DelegateCommand ExitCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_ExitCommand == null)
|
||||
{
|
||||
_ExitCommand = new DelegateCommand(() =>
|
||||
{
|
||||
Application.Current.Shutdown();
|
||||
});
|
||||
}
|
||||
return _ExitCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private DelegateCommand _ContinueCommand = null;
|
||||
public DelegateCommand ContinueCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_ContinueCommand == null)
|
||||
{
|
||||
_ContinueCommand = new DelegateCommand(() =>
|
||||
{
|
||||
Registry.CurrentUser.OpenSubKey("Software\\WPInternals", true).SetValue("DisclaimerAccepted", 1, RegistryValueKind.DWord);
|
||||
Accepted();
|
||||
});
|
||||
}
|
||||
return _ContinueCommand;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,871 @@
|
||||
// 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 Microsoft.Win32;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class DownloadsViewModel: ContextViewModel
|
||||
{
|
||||
private PhoneNotifierViewModel Notifier;
|
||||
private Timer SpeedTimer;
|
||||
|
||||
internal DownloadsViewModel(PhoneNotifierViewModel Notifier)
|
||||
{
|
||||
IsSwitchingInterface = false;
|
||||
IsFlashModeOperation = false;
|
||||
this.Notifier = Notifier;
|
||||
Notifier.NewDeviceArrived += Notifier_NewDeviceArrived;
|
||||
|
||||
RegistryKey Key = Registry.CurrentUser.OpenSubKey(@"Software\WPInternals", true);
|
||||
if (Key == null)
|
||||
Key = Registry.CurrentUser.CreateSubKey(@"Software\WPInternals");
|
||||
DownloadFolder = (string)Key.GetValue("DownloadFolder", @"C:\ProgramData\WPinternals\Repository");
|
||||
Key.Close();
|
||||
|
||||
SpeedTimer = new Timer(TimerCallback, this, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
|
||||
|
||||
AddFFUCommand = new DelegateCommand(() =>
|
||||
{
|
||||
string FFUPath = null;
|
||||
|
||||
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
|
||||
dlg.DefaultExt = ".ffu"; // Default file extension
|
||||
dlg.Filter = "ROM images (.ffu)|*.ffu"; // Filter files by extension
|
||||
|
||||
bool? result = dlg.ShowDialog();
|
||||
|
||||
if (result == true)
|
||||
{
|
||||
FFUPath = dlg.FileName;
|
||||
string FFUFile = System.IO.Path.GetFileName(FFUPath);
|
||||
|
||||
try
|
||||
{
|
||||
App.Config.AddFfuToRepository(FFUPath);
|
||||
App.Config.WriteConfig();
|
||||
LastStatusText = "File \"" + FFUFile + "\" was added to the repository.";
|
||||
}
|
||||
catch (WPinternalsException Ex)
|
||||
{
|
||||
LastStatusText = "Error: " + Ex.Message + ". File \"" + FFUFile + "\" was not added.";
|
||||
}
|
||||
catch
|
||||
{
|
||||
LastStatusText = "Error: File \"" + FFUFile + "\" was not added.";
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
LastStatusText = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private string _LastStatusText = null;
|
||||
public string LastStatusText
|
||||
{
|
||||
get
|
||||
{
|
||||
return _LastStatusText;
|
||||
}
|
||||
set
|
||||
{
|
||||
_LastStatusText = value;
|
||||
OnPropertyChanged("LastStatusText");
|
||||
}
|
||||
}
|
||||
|
||||
internal static void TimerCallback(object State)
|
||||
{
|
||||
foreach (DownloadEntry Entry in App.DownloadManager.DownloadList)
|
||||
{
|
||||
if (Entry.SpeedIndex >= 0)
|
||||
{
|
||||
int ArrayIndex = (int)(Entry.SpeedIndex % 10);
|
||||
Entry.Speeds[ArrayIndex] = Entry.BytesReceived - Entry.LastBytesReceived;
|
||||
int Count = (int)((Entry.SpeedIndex + 1) > 10 ? 10 : (Entry.SpeedIndex + 1));
|
||||
long Sum = 0;
|
||||
for (int i = 0; i < Count; i++)
|
||||
Sum += Entry.Speeds[i];
|
||||
Entry.Speed = Sum / Count;
|
||||
if (Entry.Speed < 1000)
|
||||
Entry.TimeLeft = Timeout.InfiniteTimeSpan;
|
||||
else
|
||||
Entry.TimeLeft = TimeSpan.FromSeconds((Entry.Size - Entry.BytesReceived) / Entry.Speed);
|
||||
}
|
||||
Entry.LastBytesReceived = Entry.BytesReceived;
|
||||
Entry.SpeedIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
private void Notifier_NewDeviceArrived(ArrivalEventArgs Args)
|
||||
{
|
||||
EvaluateViewState();
|
||||
}
|
||||
|
||||
internal static long GetFileLengthFromURL(string URL)
|
||||
{
|
||||
long Length = 0;
|
||||
HttpWebRequest req = (HttpWebRequest)System.Net.HttpWebRequest.Create(URL);
|
||||
req.Method = "HEAD";
|
||||
req.ServicePoint.ConnectionLimit = 10;
|
||||
using (System.Net.WebResponse resp = req.GetResponse())
|
||||
{
|
||||
long.TryParse(resp.Headers.Get("Content-Length"), out Length);
|
||||
}
|
||||
return Length;
|
||||
}
|
||||
|
||||
internal static string GetFileNameFromURL(string URL)
|
||||
{
|
||||
string FileName = System.IO.Path.GetFileName(URL);
|
||||
int End = FileName.IndexOf('?');
|
||||
if (End >= 0)
|
||||
FileName = FileName.Substring(0, End);
|
||||
return FileName;
|
||||
}
|
||||
|
||||
private void Search()
|
||||
{
|
||||
SynchronizationContext UIContext = SynchronizationContext.Current;
|
||||
SearchResultList.Clear();
|
||||
|
||||
new Thread(() =>
|
||||
{
|
||||
string FFUURL = null;
|
||||
string[] EmergencyURLs = null;
|
||||
try
|
||||
{
|
||||
string TempProductType = ProductType.ToUpper();
|
||||
if ((TempProductType != null) && TempProductType.StartsWith("RM") && !TempProductType.StartsWith("RM-"))
|
||||
TempProductType = "RM-" + TempProductType.Substring(2);
|
||||
ProductType = TempProductType;
|
||||
FFUURL = LumiaDownloadModel.SearchFFU(ProductType, ProductCode, OperatorCode, out TempProductType);
|
||||
if (TempProductType != null)
|
||||
ProductType = TempProductType;
|
||||
if (ProductType != null)
|
||||
EmergencyURLs = LumiaDownloadModel.SearchEmergencyFiles(ProductType);
|
||||
}
|
||||
catch { }
|
||||
|
||||
UIContext.Post(s =>
|
||||
{
|
||||
if (FFUURL != null)
|
||||
SearchResultList.Add(new SearchResult(FFUURL, ProductType, FFUDownloaded, null));
|
||||
if (EmergencyURLs != null)
|
||||
SearchResultList.Add(new SearchResult(ProductType + " emergency-files", EmergencyURLs, ProductType, EmergencyDownloaded, ProductType));
|
||||
}, null);
|
||||
}).Start();
|
||||
}
|
||||
|
||||
internal void Download(string URL, string Category, Action<string[], object> Callback, object State = null)
|
||||
{
|
||||
string Folder;
|
||||
if (Category == null)
|
||||
Folder = DownloadFolder;
|
||||
else
|
||||
Folder = Path.Combine(DownloadFolder, Category);
|
||||
DownloadList.Add(new DownloadEntry(URL, Folder, null, Callback, State));
|
||||
}
|
||||
|
||||
internal void Download(string[] URLs, string Category, Action<string[], object> Callback, object State = null)
|
||||
{
|
||||
string Folder;
|
||||
if (Category == null)
|
||||
Folder = DownloadFolder;
|
||||
else
|
||||
Folder = Path.Combine(DownloadFolder, Category);
|
||||
foreach (string URL in URLs)
|
||||
DownloadList.Add(new DownloadEntry(URL, Folder, URLs, Callback, State));
|
||||
}
|
||||
|
||||
private void DownloadAll()
|
||||
{
|
||||
SynchronizationContext UIContext = SynchronizationContext.Current;
|
||||
|
||||
new Thread(() =>
|
||||
{
|
||||
string FFUURL = null;
|
||||
string[] EmergencyURLs = null;
|
||||
try
|
||||
{
|
||||
string TempProductType = ProductType.ToUpper();
|
||||
if ((TempProductType != null) && TempProductType.StartsWith("RM") && !TempProductType.StartsWith("RM-"))
|
||||
TempProductType = "RM-" + TempProductType.Substring(2);
|
||||
ProductType = TempProductType;
|
||||
FFUURL = LumiaDownloadModel.SearchFFU(ProductType, ProductCode, OperatorCode, out TempProductType);
|
||||
if (TempProductType != null)
|
||||
ProductType = TempProductType;
|
||||
if (ProductType != null)
|
||||
EmergencyURLs = LumiaDownloadModel.SearchEmergencyFiles(ProductType);
|
||||
}
|
||||
catch { }
|
||||
|
||||
UIContext.Post(s =>
|
||||
{
|
||||
if (FFUURL != null)
|
||||
Download(FFUURL, ProductType, FFUDownloadedAndCheckSupported, null);
|
||||
if (EmergencyURLs != null)
|
||||
Download(EmergencyURLs, ProductType, EmergencyDownloaded, ProductType);
|
||||
}, null);
|
||||
}).Start();
|
||||
}
|
||||
|
||||
private void DownloadSelected()
|
||||
{
|
||||
IEnumerable<SearchResult> Selection = SearchResultList.Where(r => r.IsSelected);
|
||||
foreach (SearchResult Result in Selection)
|
||||
App.DownloadManager.Download(Result.URLs, Result.Category, Result.Callback, Result.State);
|
||||
}
|
||||
|
||||
private void FFUDownloaded(string[] Files, object State)
|
||||
{
|
||||
App.Config.AddFfuToRepository(Files[0]);
|
||||
}
|
||||
|
||||
private void FFUDownloadedAndCheckSupported(string[] Files, object State)
|
||||
{
|
||||
App.Config.AddFfuToRepository(Files[0]);
|
||||
|
||||
if (App.Config.FFURepository.Where(e => App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == e.OSVersion)).Count() == 0)
|
||||
{
|
||||
string ProductType2 = "RM-1085";
|
||||
string URL = LumiaDownloadModel.SearchFFU(ProductType2, null, null);
|
||||
Download(URL, ProductType2, FFUDownloaded, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void EmergencyDownloaded(string[] Files, object State)
|
||||
{
|
||||
string Type = (string)State;
|
||||
string ProgrammerPath = null;
|
||||
string PayloadPath = null;
|
||||
|
||||
for (int i = 0; i < Files.Length; i++)
|
||||
{
|
||||
if (Files[i].EndsWith(".ede", StringComparison.OrdinalIgnoreCase))
|
||||
ProgrammerPath = Files[i];
|
||||
if (Files[i].EndsWith(".edp", StringComparison.OrdinalIgnoreCase))
|
||||
PayloadPath = Files[i];
|
||||
}
|
||||
|
||||
if ((Type != null) && (ProgrammerPath != null) && (PayloadPath != null))
|
||||
App.Config.AddEmergencyToRepository(Type, ProgrammerPath, PayloadPath);
|
||||
}
|
||||
|
||||
private ObservableCollection<DownloadEntry> _DownloadList = new ObservableCollection<DownloadEntry>();
|
||||
public ObservableCollection<DownloadEntry> DownloadList
|
||||
{
|
||||
get
|
||||
{
|
||||
return _DownloadList;
|
||||
}
|
||||
}
|
||||
|
||||
private ObservableCollection<SearchResult> _SearchResultList = new ObservableCollection<SearchResult>();
|
||||
public ObservableCollection<SearchResult> SearchResultList
|
||||
{
|
||||
get
|
||||
{
|
||||
return _SearchResultList;
|
||||
}
|
||||
}
|
||||
|
||||
private DelegateCommand _DownloadSelectedCommand = null;
|
||||
public DelegateCommand DownloadSelectedCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_DownloadSelectedCommand == null)
|
||||
{
|
||||
_DownloadSelectedCommand = new DelegateCommand(() =>
|
||||
{
|
||||
DownloadSelected();
|
||||
});
|
||||
}
|
||||
return _DownloadSelectedCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private DelegateCommand _SearchCommand = null;
|
||||
public DelegateCommand SearchCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_SearchCommand == null)
|
||||
{
|
||||
_SearchCommand = new DelegateCommand(() =>
|
||||
{
|
||||
Search();
|
||||
});
|
||||
}
|
||||
return _SearchCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private DelegateCommand _DownloadAllCommand = null;
|
||||
public DelegateCommand DownloadAllCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_DownloadAllCommand == null)
|
||||
{
|
||||
_DownloadAllCommand = new DelegateCommand(() =>
|
||||
{
|
||||
DownloadAll();
|
||||
});
|
||||
}
|
||||
return _DownloadAllCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private string _DownloadFolder = null;
|
||||
public string DownloadFolder
|
||||
{
|
||||
get
|
||||
{
|
||||
return _DownloadFolder;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_DownloadFolder != value)
|
||||
{
|
||||
_DownloadFolder = value;
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(_DownloadFolder);
|
||||
}
|
||||
catch { }
|
||||
if (!Directory.Exists(_DownloadFolder))
|
||||
{
|
||||
_DownloadFolder = @"C:\ProgramData\WPinternals\Repository";
|
||||
Directory.CreateDirectory(_DownloadFolder);
|
||||
}
|
||||
|
||||
RegistryKey Key = Registry.CurrentUser.OpenSubKey(@"Software\WPInternals", true);
|
||||
|
||||
if (_DownloadFolder == null)
|
||||
{
|
||||
if (Key.GetValue("DownloadFolder") != null)
|
||||
Key.DeleteValue("DownloadFolder");
|
||||
}
|
||||
else
|
||||
Key.SetValue("DownloadFolder", _DownloadFolder);
|
||||
|
||||
Key.Close();
|
||||
|
||||
OnPropertyChanged("DownloadFolder");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _ProductCode = null;
|
||||
public string ProductCode
|
||||
{
|
||||
get
|
||||
{
|
||||
return _ProductCode;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_ProductCode != value)
|
||||
{
|
||||
_ProductCode = value;
|
||||
|
||||
OnPropertyChanged("ProductCode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _ProductType = null;
|
||||
public string ProductType
|
||||
{
|
||||
get
|
||||
{
|
||||
return _ProductType;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_ProductType != value)
|
||||
{
|
||||
_ProductType = value;
|
||||
|
||||
OnPropertyChanged("ProductType");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _OperatorCode = null;
|
||||
public string OperatorCode
|
||||
{
|
||||
get
|
||||
{
|
||||
return _OperatorCode;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_OperatorCode != value)
|
||||
{
|
||||
_OperatorCode = value;
|
||||
|
||||
OnPropertyChanged("OperatorCode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal override void EvaluateViewState()
|
||||
{
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if ((Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash))
|
||||
{
|
||||
NokiaFlashModel LumiaFlashModel = (NokiaFlashModel)Notifier.CurrentModel;
|
||||
PhoneInfo Info = LumiaFlashModel.ReadPhoneInfo();
|
||||
ProductType = Info.Type;
|
||||
OperatorCode = "";
|
||||
ProductCode = Info.ProductCode;
|
||||
}
|
||||
else if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Normal)
|
||||
{
|
||||
NokiaPhoneModel LumiaNormalModel = (NokiaPhoneModel)Notifier.CurrentModel;
|
||||
OperatorCode = LumiaNormalModel.ExecuteJsonMethodAsString("ReadOperatorName", "OperatorName"); // Example: 000-NL
|
||||
string TempProductType = LumiaNormalModel.ExecuteJsonMethodAsString("ReadManufacturerModelName", "ManufacturerModelName"); // RM-821_eu_denmark_251
|
||||
if (TempProductType.IndexOf('_') >= 0) TempProductType = TempProductType.Substring(0, TempProductType.IndexOf('_'));
|
||||
ProductType = TempProductType;
|
||||
ProductCode = LumiaNormalModel.ExecuteJsonMethodAsString("ReadProductCode", "ProductCode"); // 059Q9D7
|
||||
}
|
||||
}
|
||||
|
||||
private DelegateCommand _AddFFUCommand = null;
|
||||
public DelegateCommand AddFFUCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
return _AddFFUCommand;
|
||||
}
|
||||
private set
|
||||
{
|
||||
_AddFFUCommand = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal enum DownloadStatus
|
||||
{
|
||||
Downloading,
|
||||
Ready,
|
||||
Failed
|
||||
};
|
||||
|
||||
internal class DownloadEntry: INotifyPropertyChanged
|
||||
{
|
||||
private SynchronizationContext UIContext;
|
||||
public event PropertyChangedEventHandler PropertyChanged = delegate { };
|
||||
internal Action<string[], object> Callback;
|
||||
internal object State;
|
||||
internal string URL;
|
||||
internal string[] URLCollection;
|
||||
internal string Folder;
|
||||
internal MyWebClient Client;
|
||||
internal long SpeedIndex = -1;
|
||||
internal long[] Speeds = new long[10];
|
||||
internal long LastBytesReceived;
|
||||
internal long BytesReceived;
|
||||
|
||||
internal DownloadEntry(string URL, string Folder, string[] URLCollection, Action<string[], object> Callback, object State)
|
||||
{
|
||||
UIContext = SynchronizationContext.Current;
|
||||
this.URL = URL;
|
||||
this.Callback = Callback;
|
||||
this.State = State;
|
||||
this.URLCollection = URLCollection;
|
||||
this.Folder = Folder;
|
||||
Directory.CreateDirectory(Folder);
|
||||
Name = DownloadsViewModel.GetFileNameFromURL(URL);
|
||||
Uri Uri = new Uri(URL);
|
||||
Status = DownloadStatus.Downloading;
|
||||
new Thread(() =>
|
||||
{
|
||||
Size = DownloadsViewModel.GetFileLengthFromURL(URL);
|
||||
|
||||
Client = new MyWebClient();
|
||||
Client.DownloadFileCompleted += Client_DownloadFileCompleted;
|
||||
Client.DownloadProgressChanged += Client_DownloadProgressChanged;
|
||||
Client.DownloadFileAsync(Uri, Path.Combine(Folder, DownloadsViewModel.GetFileNameFromURL(Uri.LocalPath)), null);
|
||||
}).Start();
|
||||
}
|
||||
|
||||
private void Client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
|
||||
{
|
||||
|
||||
Action Finish = () =>
|
||||
{
|
||||
Status = e.Error == null ? DownloadStatus.Ready : DownloadStatus.Failed;
|
||||
App.DownloadManager.DownloadList.Remove(this);
|
||||
if (Status == DownloadStatus.Ready)
|
||||
{
|
||||
if ((URLCollection == null) || (!URLCollection.Any(c => App.DownloadManager.DownloadList.Any(d => d.URL == c)))) // if there are no files left to download from this collection, then call the callback-function.
|
||||
{
|
||||
string[] Files;
|
||||
if (URLCollection == null)
|
||||
{
|
||||
Files = new string[1];
|
||||
Files[0] = System.IO.Path.Combine(Folder, DownloadsViewModel.GetFileNameFromURL(URL));
|
||||
}
|
||||
else
|
||||
{
|
||||
Files = new string[URLCollection.Length];
|
||||
for (int i = 0; i < URLCollection.Length; i++)
|
||||
Files[i] = System.IO.Path.Combine(Folder, DownloadsViewModel.GetFileNameFromURL(URLCollection[i]));
|
||||
}
|
||||
|
||||
Callback(Files, State);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (UIContext != null)
|
||||
UIContext.Post(d => Finish(), null);
|
||||
}
|
||||
|
||||
private void Client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
|
||||
{
|
||||
BytesReceived = e.BytesReceived;
|
||||
Progress = e.ProgressPercentage;
|
||||
}
|
||||
|
||||
protected void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
if (this.PropertyChanged != null)
|
||||
{
|
||||
if (SynchronizationContext.Current == UIContext)
|
||||
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
|
||||
else
|
||||
{
|
||||
UIContext.Post((s) => PropertyChanged(this, new PropertyChangedEventArgs(propertyName)), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DownloadStatus _Status;
|
||||
public DownloadStatus Status
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Status;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_Status != value)
|
||||
{
|
||||
_Status = value;
|
||||
OnPropertyChanged("Status");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _Name;
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Name;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_Name != value)
|
||||
{
|
||||
_Name = value;
|
||||
OnPropertyChanged("Name");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long _Size;
|
||||
public long Size
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Size;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_Size != value)
|
||||
{
|
||||
_Size = value;
|
||||
OnPropertyChanged("Size");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TimeSpan _TimeLeft;
|
||||
public TimeSpan TimeLeft
|
||||
{
|
||||
get
|
||||
{
|
||||
return _TimeLeft;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_TimeLeft != value)
|
||||
{
|
||||
_TimeLeft = value;
|
||||
OnPropertyChanged("TimeLeft");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double _Speed;
|
||||
public double Speed
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Speed;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_Speed != value)
|
||||
{
|
||||
_Speed = value;
|
||||
OnPropertyChanged("Speed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int _Progress;
|
||||
public int Progress
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Progress;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_Progress != value)
|
||||
{
|
||||
_Progress = value;
|
||||
OnPropertyChanged("Progress");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class SearchResult : INotifyPropertyChanged
|
||||
{
|
||||
private SynchronizationContext UIContext;
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
internal string[] URLs;
|
||||
internal Action<string[], object> Callback;
|
||||
internal object State;
|
||||
internal string Category;
|
||||
|
||||
internal SearchResult(string URL, string Category, Action<string[], object> Callback, object State)
|
||||
{
|
||||
UIContext = SynchronizationContext.Current;
|
||||
URLs = new string[1];
|
||||
URLs[0] = URL;
|
||||
Name = DownloadsViewModel.GetFileNameFromURL(URL);
|
||||
this.Callback = Callback;
|
||||
this.State = State;
|
||||
this.Category = Category;
|
||||
GetSize();
|
||||
}
|
||||
|
||||
internal SearchResult(string Name, string[] URLs, string Category, Action<string[], object> Callback, object State)
|
||||
{
|
||||
UIContext = SynchronizationContext.Current;
|
||||
this.URLs = URLs;
|
||||
this.Name = Name;
|
||||
this.Callback = Callback;
|
||||
this.State = State;
|
||||
this.Category = Category;
|
||||
GetSize();
|
||||
}
|
||||
|
||||
private void GetSize()
|
||||
{
|
||||
new Thread(() =>
|
||||
{
|
||||
long CalcSize = 0;
|
||||
foreach (string URL in URLs)
|
||||
{
|
||||
CalcSize += DownloadsViewModel.GetFileLengthFromURL(URL);
|
||||
}
|
||||
Size = CalcSize;
|
||||
}).Start();
|
||||
}
|
||||
|
||||
protected void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
if (this.PropertyChanged != null)
|
||||
{
|
||||
if (SynchronizationContext.Current == UIContext)
|
||||
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
|
||||
else
|
||||
{
|
||||
UIContext.Post((s) => PropertyChanged(this, new PropertyChangedEventArgs(propertyName)), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _Name;
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Name;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_Name != value)
|
||||
{
|
||||
_Name = value;
|
||||
OnPropertyChanged("Name");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long _Size;
|
||||
public long Size
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Size;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_Size != value)
|
||||
{
|
||||
_Size = value;
|
||||
OnPropertyChanged("Size");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _IsSelected;
|
||||
public bool IsSelected
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IsSelected;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_IsSelected != value)
|
||||
{
|
||||
_IsSelected = value;
|
||||
OnPropertyChanged("IsSelected");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class MyWebClient : WebClient
|
||||
{
|
||||
protected override WebRequest GetWebRequest(Uri address)
|
||||
{
|
||||
HttpWebRequest req = (HttpWebRequest)base.GetWebRequest(address);
|
||||
req.ServicePoint.ConnectionLimit = 10;
|
||||
return (WebRequest)req;
|
||||
}
|
||||
}
|
||||
|
||||
public class DownloaderNameConvertor : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType,
|
||||
object parameter, CultureInfo culture)
|
||||
{
|
||||
return System.IO.Path.GetFileNameWithoutExtension((string)value);
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType,
|
||||
object parameter, CultureInfo culture)
|
||||
{
|
||||
return Binding.DoNothing;
|
||||
}
|
||||
}
|
||||
|
||||
public class DownloaderSizeConvertor : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType,
|
||||
object parameter, CultureInfo culture)
|
||||
{
|
||||
long? Size = value as long?;
|
||||
if (Size < 1024)
|
||||
return Size + " B";
|
||||
if (Size < (1024 * 1024))
|
||||
return Math.Round(((double)Size / 1024), 0) + " KB";
|
||||
return Math.Round(((double)Size / 1024 / 1024), 0) + " MB";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType,
|
||||
object parameter, CultureInfo culture)
|
||||
{
|
||||
return Binding.DoNothing;
|
||||
}
|
||||
}
|
||||
|
||||
public class DownloaderSpeedConvertor : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType,
|
||||
object parameter, CultureInfo culture)
|
||||
{
|
||||
return ((int)((double)value / 1024)).ToString() + " KB/s";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType,
|
||||
object parameter, CultureInfo culture)
|
||||
{
|
||||
return Binding.DoNothing;
|
||||
}
|
||||
}
|
||||
|
||||
public class DownloaderTimeRemainingConvertor : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType,
|
||||
object parameter, CultureInfo culture)
|
||||
{
|
||||
TimeSpan TimeLeft = (TimeSpan)value;
|
||||
if (TimeLeft == Timeout.InfiniteTimeSpan)
|
||||
return "";
|
||||
return TimeLeft.ToString(@"h\:mm\:ss");
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType,
|
||||
object parameter, CultureInfo culture)
|
||||
{
|
||||
return Binding.DoNothing;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
// 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.Threading;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class DumpRomTargetSelectionViewModel: ContextViewModel
|
||||
{
|
||||
private Action<string, string, bool, string, bool, string, bool> DumpCallback;
|
||||
internal Action SwitchToUnlockBoot;
|
||||
internal Action SwitchToUnlockRoot;
|
||||
internal Action SwitchToFlashRom;
|
||||
|
||||
internal DumpRomTargetSelectionViewModel(Action SwitchToUnlockBoot, Action SwitchToUnlockRoot, Action SwitchToFlashRom, Action<string, string, bool, string, bool, string, bool> DumpCallback)
|
||||
: base()
|
||||
{
|
||||
this.SwitchToUnlockBoot = SwitchToUnlockBoot;
|
||||
this.SwitchToUnlockRoot = SwitchToUnlockRoot;
|
||||
this.SwitchToFlashRom = SwitchToFlashRom;
|
||||
this.DumpCallback = DumpCallback;
|
||||
|
||||
new Thread(() => EvaluateViewState()).Start();
|
||||
}
|
||||
|
||||
private string _FFUPath;
|
||||
public string FFUPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _FFUPath;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _FFUPath)
|
||||
{
|
||||
_FFUPath = value;
|
||||
OnPropertyChanged("FFUPath");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _EFIESPPath;
|
||||
public string EFIESPPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _EFIESPPath;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _EFIESPPath)
|
||||
{
|
||||
_EFIESPPath = value;
|
||||
OnPropertyChanged("EFIESPPath");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _CompressEFIESP;
|
||||
public bool CompressEFIESP
|
||||
{
|
||||
get
|
||||
{
|
||||
return _CompressEFIESP;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _CompressEFIESP)
|
||||
{
|
||||
_CompressEFIESP = value;
|
||||
OnPropertyChanged("CompressEFIESP");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _MainOSPath;
|
||||
public string MainOSPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _MainOSPath;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _MainOSPath)
|
||||
{
|
||||
_MainOSPath = value;
|
||||
OnPropertyChanged("MainOSPath");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _CompressMainOS;
|
||||
public bool CompressMainOS
|
||||
{
|
||||
get
|
||||
{
|
||||
return _CompressMainOS;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _CompressMainOS)
|
||||
{
|
||||
_CompressMainOS = value;
|
||||
OnPropertyChanged("CompressMainOS");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _DataPath;
|
||||
public string DataPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _DataPath;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _DataPath)
|
||||
{
|
||||
_DataPath = value;
|
||||
OnPropertyChanged("DataPath");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _CompressData = true;
|
||||
public bool CompressData
|
||||
{
|
||||
get
|
||||
{
|
||||
return _CompressData;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _CompressData)
|
||||
{
|
||||
_CompressData = value;
|
||||
OnPropertyChanged("CompressData");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _IsPhoneDisconnected;
|
||||
public bool IsPhoneDisconnected
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IsPhoneDisconnected;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _IsPhoneDisconnected)
|
||||
{
|
||||
_IsPhoneDisconnected = value;
|
||||
OnPropertyChanged("IsPhoneDisconnected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _IsPhoneInMassStorage;
|
||||
public bool IsPhoneInMassStorage
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IsPhoneInMassStorage;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _IsPhoneInMassStorage)
|
||||
{
|
||||
_IsPhoneInMassStorage = value;
|
||||
OnPropertyChanged("IsPhoneInMassStorage");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _IsPhoneInOtherMode;
|
||||
public bool IsPhoneInOtherMode
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IsPhoneInOtherMode;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _IsPhoneInOtherMode)
|
||||
{
|
||||
_IsPhoneInOtherMode = value;
|
||||
OnPropertyChanged("IsPhoneInOtherMode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DelegateCommand _DumpCommand;
|
||||
public DelegateCommand DumpCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_DumpCommand == null)
|
||||
{
|
||||
_DumpCommand = new DelegateCommand(() => { DumpCallback(FFUPath, EFIESPPath, CompressEFIESP, MainOSPath, CompressMainOS, DataPath, CompressData); }, () => ((FFUPath != null) && ((EFIESPPath != null) || (MainOSPath != null) || (DataPath != null))));
|
||||
}
|
||||
return _DumpCommand;
|
||||
}
|
||||
}
|
||||
|
||||
internal override void EvaluateViewState()
|
||||
{
|
||||
DumpCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
// 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.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class DumpRomViewModel: ContextViewModel
|
||||
{
|
||||
private Action SwitchToUnlockBoot;
|
||||
private Action SwitchToUnlockRoot;
|
||||
private Action SwitchToFlashRom;
|
||||
|
||||
internal DumpRomViewModel(Action SwitchToUnlockBoot, Action SwitchToUnlockRoot, Action SwitchToFlashRom)
|
||||
: base()
|
||||
{
|
||||
this.SwitchToUnlockBoot = SwitchToUnlockBoot;
|
||||
this.SwitchToUnlockRoot = SwitchToUnlockRoot;
|
||||
this.SwitchToFlashRom = SwitchToFlashRom;
|
||||
}
|
||||
|
||||
internal override void EvaluateViewState()
|
||||
{
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (SubContextViewModel == null)
|
||||
ActivateSubContext(new DumpRomTargetSelectionViewModel(SwitchToUnlockBoot, SwitchToUnlockRoot, SwitchToFlashRom, DoDumpRom));
|
||||
}
|
||||
|
||||
internal void DoDumpRom(string FFUPath, string EFIESPPath, bool CompressEFIESP, string MainOSPath, bool CompressMainOS, string DataPath, bool CompressData)
|
||||
{
|
||||
new Thread(() =>
|
||||
{
|
||||
bool Result = true;
|
||||
|
||||
ActivateSubContext(new BusyViewModel("Initializing ROM dump..."));
|
||||
|
||||
ulong TotalSizeSectors = 0;
|
||||
int PartitionCount = 0;
|
||||
Partition Partition;
|
||||
FFU FFU = null;
|
||||
try
|
||||
{
|
||||
FFU = new FFU(FFUPath);
|
||||
|
||||
if (EFIESPPath != null)
|
||||
{
|
||||
Partition = FFU.GPT.Partitions.Where(p => p.Name == "EFIESP").First();
|
||||
TotalSizeSectors += Partition.SizeInSectors;
|
||||
PartitionCount++;
|
||||
}
|
||||
|
||||
if (MainOSPath != null)
|
||||
{
|
||||
Partition = FFU.GPT.Partitions.Where(p => p.Name == "MainOS").First();
|
||||
TotalSizeSectors += Partition.SizeInSectors;
|
||||
PartitionCount++;
|
||||
}
|
||||
|
||||
if (DataPath != null)
|
||||
{
|
||||
Partition = FFU.GPT.Partitions.Where(p => p.Name == "Data").First();
|
||||
TotalSizeSectors += Partition.SizeInSectors;
|
||||
PartitionCount++;
|
||||
}
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
Result = false;
|
||||
}
|
||||
|
||||
// We are on a worker thread!
|
||||
// So we must pass the SynchronizationContext of the UI thread
|
||||
BusyViewModel Busy = new BusyViewModel("Dumping ROM...", MaxProgressValue: TotalSizeSectors, UIContext: UIContext);
|
||||
ProgressUpdater Updater = Busy.ProgressUpdater;
|
||||
ActivateSubContext(Busy);
|
||||
|
||||
int i = 0;
|
||||
if (Result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (EFIESPPath != null)
|
||||
{
|
||||
i++;
|
||||
Busy.Message = "Dumping partition EFIESP (" + i.ToString() + @"/" + PartitionCount.ToString() + ")";
|
||||
FFU.WritePartition("EFIESP", EFIESPPath, Updater, CompressEFIESP);
|
||||
}
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
Result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (MainOSPath != null)
|
||||
{
|
||||
i++;
|
||||
Busy.Message = "Dumping partition MainOS (" + i.ToString() + @"/" + PartitionCount.ToString() + ")";
|
||||
FFU.WritePartition("MainOS", MainOSPath, Updater, CompressMainOS);
|
||||
}
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
Result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (DataPath != null)
|
||||
{
|
||||
i++;
|
||||
Busy.Message = "Dumping partition Data (" + i.ToString() + @"/" + PartitionCount.ToString() + ")";
|
||||
FFU.WritePartition("Data", DataPath, Updater, CompressData);
|
||||
}
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
Result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Result)
|
||||
{
|
||||
ActivateSubContext(new MessageViewModel("Failed to dump ROM partitions!", Restart));
|
||||
return;
|
||||
}
|
||||
|
||||
ActivateSubContext(new MessageViewModel("Successfully dumped ROM partitions!", Restart));
|
||||
}).Start();
|
||||
}
|
||||
|
||||
internal void Restart()
|
||||
{
|
||||
ActivateSubContext(new DumpRomTargetSelectionViewModel(SwitchToUnlockBoot, SwitchToUnlockRoot, SwitchToFlashRom, DoDumpRom));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class GettingStartedViewModel: ContextViewModel
|
||||
{
|
||||
internal Action ShowDisclaimer;
|
||||
internal Action SwitchToUnlockBoot;
|
||||
internal Action SwitchToUnlockRoot;
|
||||
internal Action SwitchToBackup;
|
||||
internal Action SwitchToDumpRom;
|
||||
internal Action SwitchToFlashRom;
|
||||
internal Action SwitchToDownload;
|
||||
|
||||
internal GettingStartedViewModel(Action ShowDisclaimer, Action SwitchToUnlockBoot, Action SwitchToUnlockRoot, Action SwitchToBackup, Action SwitchToDumpRom, Action SwitchToFlashRom, Action SwitchToDownload)
|
||||
: base()
|
||||
{
|
||||
this.ShowDisclaimer = ShowDisclaimer;
|
||||
this.SwitchToUnlockBoot = SwitchToUnlockBoot;
|
||||
this.SwitchToUnlockRoot = SwitchToUnlockRoot;
|
||||
this.SwitchToBackup = SwitchToBackup;
|
||||
this.SwitchToFlashRom = SwitchToFlashRom;
|
||||
this.SwitchToDumpRom = SwitchToDumpRom;
|
||||
this.SwitchToDownload = SwitchToDownload;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
// 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.Threading;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class LumiaFlashRomSourceSelectionViewModel: ContextViewModel
|
||||
{
|
||||
private PhoneNotifierViewModel PhoneNotifier;
|
||||
private Action<string, string, string> FlashPartitionsCallback;
|
||||
private Action<string> FlashFFUCallback;
|
||||
private Action<string> FlashArchiveCallback;
|
||||
internal Action SwitchToUnlockBoot;
|
||||
internal Action SwitchToUnlockRoot;
|
||||
internal Action SwitchToDumpFFU;
|
||||
internal Action SwitchToBackup;
|
||||
|
||||
internal LumiaFlashRomSourceSelectionViewModel(PhoneNotifierViewModel PhoneNotifier, Action SwitchToUnlockBoot, Action SwitchToUnlockRoot, Action SwitchToDumpFFU, Action SwitchToBackup, Action<string, string, string> FlashPartitionsCallback, Action<string> FlashArchiveCallback, Action<string> FlashFFUCallback)
|
||||
: base()
|
||||
{
|
||||
this.PhoneNotifier = PhoneNotifier;
|
||||
this.SwitchToUnlockBoot = SwitchToUnlockBoot;
|
||||
this.SwitchToUnlockRoot = SwitchToUnlockRoot;
|
||||
this.SwitchToDumpFFU = SwitchToDumpFFU;
|
||||
this.SwitchToBackup = SwitchToBackup;
|
||||
this.FlashPartitionsCallback = FlashPartitionsCallback;
|
||||
this.FlashArchiveCallback = FlashArchiveCallback;
|
||||
this.FlashFFUCallback = FlashFFUCallback;
|
||||
|
||||
this.PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
||||
this.PhoneNotifier.DeviceRemoved += DeviceRemoved;
|
||||
|
||||
new Thread(() => EvaluateViewState()).Start();
|
||||
}
|
||||
|
||||
private string _EFIESPPath;
|
||||
public string EFIESPPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _EFIESPPath;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _EFIESPPath)
|
||||
{
|
||||
_EFIESPPath = value;
|
||||
OnPropertyChanged("EFIESPPath");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _MainOSPath;
|
||||
public string MainOSPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _MainOSPath;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _MainOSPath)
|
||||
{
|
||||
_MainOSPath = value;
|
||||
OnPropertyChanged("MainOSPath");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _DataPath;
|
||||
public string DataPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _DataPath;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _DataPath)
|
||||
{
|
||||
_DataPath = value;
|
||||
OnPropertyChanged("DataPath");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _ArchivePath;
|
||||
public string ArchivePath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _ArchivePath;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _ArchivePath)
|
||||
{
|
||||
_ArchivePath = value;
|
||||
OnPropertyChanged("ArchivePath");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _FFUPath;
|
||||
public string FFUPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _FFUPath;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _FFUPath)
|
||||
{
|
||||
_FFUPath = value;
|
||||
OnPropertyChanged("FFUPath");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _IsPhoneDisconnected;
|
||||
public bool IsPhoneDisconnected
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IsPhoneDisconnected;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _IsPhoneDisconnected)
|
||||
{
|
||||
_IsPhoneDisconnected = value;
|
||||
OnPropertyChanged("IsPhoneDisconnected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _IsPhoneInFlashMode;
|
||||
public bool IsPhoneInFlashMode
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IsPhoneInFlashMode;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _IsPhoneInFlashMode)
|
||||
{
|
||||
_IsPhoneInFlashMode = value;
|
||||
OnPropertyChanged("IsPhoneInFlashMode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _IsPhoneInOtherMode;
|
||||
public bool IsPhoneInOtherMode
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IsPhoneInOtherMode;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _IsPhoneInOtherMode)
|
||||
{
|
||||
_IsPhoneInOtherMode = value;
|
||||
OnPropertyChanged("IsPhoneInOtherMode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DelegateCommand _FlashPartitionsCommand;
|
||||
public DelegateCommand FlashPartitionsCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_FlashPartitionsCommand == null)
|
||||
{
|
||||
_FlashPartitionsCommand = new DelegateCommand(() => { FlashPartitionsCallback(EFIESPPath, MainOSPath, DataPath); }, () => (((EFIESPPath != null) || (MainOSPath != null) || (DataPath != null)) && (PhoneNotifier.CurrentInterface != null)));
|
||||
}
|
||||
return _FlashPartitionsCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private DelegateCommand _FlashFFUCommand;
|
||||
public DelegateCommand FlashFFUCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_FlashFFUCommand == null)
|
||||
{
|
||||
_FlashFFUCommand = new DelegateCommand(() => { FlashFFUCallback(FFUPath); }, () => ((FFUPath != null) && (PhoneNotifier.CurrentInterface != null)));
|
||||
}
|
||||
return _FlashFFUCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private DelegateCommand _FlashArchiveCommand;
|
||||
public DelegateCommand FlashArchiveCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_FlashArchiveCommand == null)
|
||||
{
|
||||
_FlashArchiveCommand = new DelegateCommand(() => { FlashArchiveCallback(ArchivePath); }, () => ((ArchivePath != null) && (PhoneNotifier.CurrentInterface != null)));
|
||||
}
|
||||
return _FlashArchiveCommand;
|
||||
}
|
||||
}
|
||||
|
||||
~LumiaFlashRomSourceSelectionViewModel()
|
||||
{
|
||||
PhoneNotifier.NewDeviceArrived -= NewDeviceArrived;
|
||||
}
|
||||
|
||||
void NewDeviceArrived(ArrivalEventArgs Args)
|
||||
{
|
||||
new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
EvaluateViewState();
|
||||
}
|
||||
catch { }
|
||||
}).Start();
|
||||
}
|
||||
|
||||
void DeviceRemoved()
|
||||
{
|
||||
new Thread(() => EvaluateViewState()).Start();
|
||||
}
|
||||
|
||||
internal override void EvaluateViewState()
|
||||
{
|
||||
IsPhoneDisconnected = PhoneNotifier.CurrentInterface == null;
|
||||
IsPhoneInFlashMode = PhoneNotifier.CurrentInterface == PhoneInterfaces.Lumia_Flash;
|
||||
IsPhoneInOtherMode = (!IsPhoneDisconnected && !IsPhoneInFlashMode);
|
||||
FlashPartitionsCommand.RaiseCanExecuteChanged();
|
||||
FlashArchiveCommand.RaiseCanExecuteChanged();
|
||||
FlashFFUCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,657 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class LumiaFlashRomViewModel: ContextViewModel
|
||||
{
|
||||
private PhoneNotifierViewModel PhoneNotifier;
|
||||
internal Action SwitchToUnlockBoot;
|
||||
internal Action SwitchToUnlockRoot;
|
||||
internal Action SwitchToDumpFFU;
|
||||
internal Action SwitchToBackup;
|
||||
private Action Callback;
|
||||
|
||||
internal LumiaFlashRomViewModel(PhoneNotifierViewModel PhoneNotifier, Action SwitchToUnlockBoot, Action SwitchToUnlockRoot, Action SwitchToDumpFFU, Action SwitchToBackup, Action Callback)
|
||||
: base()
|
||||
{
|
||||
IsSwitchingInterface = false;
|
||||
IsFlashModeOperation = true;
|
||||
|
||||
this.PhoneNotifier = PhoneNotifier;
|
||||
this.SwitchToUnlockBoot = SwitchToUnlockBoot;
|
||||
this.SwitchToUnlockRoot = SwitchToUnlockRoot;
|
||||
this.SwitchToDumpFFU = SwitchToDumpFFU;
|
||||
this.SwitchToBackup = SwitchToBackup;
|
||||
this.Callback = Callback;
|
||||
}
|
||||
|
||||
internal override void EvaluateViewState()
|
||||
{
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (SubContextViewModel == null)
|
||||
ActivateSubContext(new LumiaFlashRomSourceSelectionViewModel(PhoneNotifier, SwitchToUnlockBoot, SwitchToUnlockRoot, SwitchToDumpFFU, SwitchToBackup, FlashPartitions, FlashArchive, FlashFFU));
|
||||
}
|
||||
|
||||
// Called from an event-handler. So, "async void" is valid here.
|
||||
internal async void FlashPartitions(string EFIESPPath, string MainOSPath, string DataPath)
|
||||
{
|
||||
IsSwitchingInterface = true; // Prevents that a device is forced to Flash mode on this screen which is meant for flashing
|
||||
try
|
||||
{
|
||||
await SwitchModeViewModel.SwitchToWithProgress(PhoneNotifier, PhoneInterfaces.Lumia_Flash,
|
||||
(msg, sub) =>
|
||||
ActivateSubContext(new BusyViewModel(msg, sub)));
|
||||
if (((NokiaFlashModel)PhoneNotifier.CurrentModel).ReadPhoneInfo(ExtendedInfo: false).FlashAppProtocolVersionMajor < 2)
|
||||
FlashPartitionsTask(EFIESPPath, MainOSPath, DataPath);
|
||||
else
|
||||
await Task.Run(async () => await LumiaV2UnlockBootViewModel.LumiaV2FlashPartitions(PhoneNotifier, EFIESPPath, MainOSPath, DataPath, SetWorkingStatus, UpdateWorkingStatus, ExitSuccess, ExitFailure));
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
ActivateSubContext(new MessageViewModel(Ex.Message, Callback));
|
||||
}
|
||||
}
|
||||
|
||||
internal void FlashPartitionsTask(string EFIESPPath, string MainOSPath, string DataPath)
|
||||
{
|
||||
new Thread(() =>
|
||||
{
|
||||
bool Result = true;
|
||||
|
||||
ActivateSubContext(new BusyViewModel("Initializing flash..."));
|
||||
|
||||
NokiaFlashModel Phone = (NokiaFlashModel)PhoneNotifier.CurrentModel;
|
||||
|
||||
GPT GPT = Phone.ReadGPT();
|
||||
|
||||
ulong TotalSizeSectors = 0;
|
||||
int PartitionCount = 0;
|
||||
ulong MainOSOldSectorCount = 0;
|
||||
ulong MainOSNewSectorCount = 0;
|
||||
ulong DataOldSectorCount = 0;
|
||||
ulong DataNewSectorCount = 0;
|
||||
ulong FirstMainOSSector = 0;
|
||||
|
||||
try
|
||||
{
|
||||
if (EFIESPPath != null)
|
||||
{
|
||||
using (Stream Stream = new DecompressedStream(File.Open(EFIESPPath, FileMode.Open)))
|
||||
{
|
||||
ulong StreamLengthInSectors = (ulong)Stream.Length / 0x200;
|
||||
TotalSizeSectors += StreamLengthInSectors;
|
||||
PartitionCount++;
|
||||
Partition Partition = GPT.Partitions.Where(p => string.Compare(p.Name, "EFIESP", true) == 0).FirstOrDefault();
|
||||
if (StreamLengthInSectors > Partition.SizeInSectors)
|
||||
{
|
||||
LogFile.Log("Flash failed! Size of partition 'EFIESP' is too big.");
|
||||
ExitFailure("Flash failed!", "Size of partition 'EFIESP' is too big.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (MainOSPath != null)
|
||||
{
|
||||
using (Stream Stream = new DecompressedStream(File.Open(MainOSPath, FileMode.Open)))
|
||||
{
|
||||
ulong StreamLengthInSectors = (ulong)Stream.Length / 0x200;
|
||||
TotalSizeSectors += StreamLengthInSectors;
|
||||
PartitionCount++;
|
||||
Partition Partition = GPT.Partitions.Where(p => string.Compare(p.Name, "MainOS", true) == 0).FirstOrDefault();
|
||||
MainOSOldSectorCount = Partition.SizeInSectors;
|
||||
MainOSNewSectorCount = StreamLengthInSectors;
|
||||
FirstMainOSSector = Partition.FirstSector;
|
||||
}
|
||||
}
|
||||
|
||||
if (DataPath != null)
|
||||
{
|
||||
using (Stream Stream = new DecompressedStream(File.Open(DataPath, FileMode.Open)))
|
||||
{
|
||||
ulong StreamLengthInSectors = (ulong)Stream.Length / 0x200;
|
||||
TotalSizeSectors += StreamLengthInSectors;
|
||||
PartitionCount++;
|
||||
Partition Partition = GPT.Partitions.Where(p => string.Compare(p.Name, "Data", true) == 0).FirstOrDefault();
|
||||
DataOldSectorCount = Partition.SizeInSectors;
|
||||
DataNewSectorCount = StreamLengthInSectors;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
Result = false;
|
||||
}
|
||||
|
||||
if ((MainOSNewSectorCount > 0) && (DataNewSectorCount > 0))
|
||||
{
|
||||
if ((MainOSNewSectorCount > MainOSOldSectorCount) || (DataNewSectorCount > DataOldSectorCount))
|
||||
{
|
||||
UInt64 OSSpace = GPT.LastUsableSector - FirstMainOSSector + 1;
|
||||
if ((MainOSNewSectorCount + DataNewSectorCount) <= OSSpace)
|
||||
{
|
||||
// MainOS and Data partitions need to be re-aligned!
|
||||
Partition MainOSPartition = GPT.Partitions.Where(p => string.Compare(p.Name, "MainOS", true) == 0).Single();
|
||||
Partition DataPartition = GPT.Partitions.Where(p => string.Compare(p.Name, "Data", true) == 0).Single();
|
||||
MainOSPartition.LastSector = MainOSPartition.FirstSector + MainOSNewSectorCount - 1;
|
||||
DataPartition.FirstSector = MainOSPartition.LastSector + 1;
|
||||
DataPartition.LastSector = DataPartition.FirstSector + DataNewSectorCount - 1;
|
||||
Phone.WriteGPT(GPT);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.Log("Flash failed! Size of partitions 'MainOS' and 'Data' together are too big.");
|
||||
ExitFailure("Flash failed!", "Sizes of partitions 'MainOS' and 'Data' together are too big.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((MainOSNewSectorCount > 0) && (MainOSNewSectorCount > MainOSOldSectorCount))
|
||||
{
|
||||
LogFile.Log("Flash failed! Size of partition 'MainOS' is too big.");
|
||||
ExitFailure("Flash failed!", "Size of partition 'MainOS' is too big.");
|
||||
return;
|
||||
}
|
||||
else if ((DataNewSectorCount > 0) && (DataNewSectorCount > DataOldSectorCount))
|
||||
{
|
||||
LogFile.Log("Flash failed! Size of partition 'Data' is too big.");
|
||||
ExitFailure("Flash failed!", "Size of partition 'Data' together is too big.");
|
||||
return;
|
||||
}
|
||||
|
||||
BusyViewModel Busy = new BusyViewModel("Flashing...", MaxProgressValue: TotalSizeSectors, UIContext: UIContext);
|
||||
ProgressUpdater Updater = Busy.ProgressUpdater;
|
||||
ActivateSubContext(Busy);
|
||||
|
||||
int i = 0;
|
||||
if (Result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (EFIESPPath != null)
|
||||
{
|
||||
i++;
|
||||
Busy.Message = "Flashing partition EFIESP (" + i.ToString() + @"/" + PartitionCount.ToString() + ")";
|
||||
Phone.FlashRawPartition(EFIESPPath, "EFIESP", Updater);
|
||||
}
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
Result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (MainOSPath != null)
|
||||
{
|
||||
i++;
|
||||
Busy.Message = "Flashing partition MainOS (" + i.ToString() + @"/" + PartitionCount.ToString() + ")";
|
||||
Phone.FlashRawPartition(MainOSPath, "MainOS", Updater);
|
||||
}
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
Result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (DataPath != null)
|
||||
{
|
||||
i++;
|
||||
Busy.Message = "Flashing partition Data (" + i.ToString() + @"/" + PartitionCount.ToString() + ")";
|
||||
Phone.FlashRawPartition(DataPath, "Data", Updater);
|
||||
}
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
Result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Result)
|
||||
{
|
||||
ExitFailure("Flash failed!", null);
|
||||
return;
|
||||
}
|
||||
|
||||
ExitSuccess("Flash successful! Make sure you disable Windows Update on the phone!", null);
|
||||
}).Start();
|
||||
}
|
||||
|
||||
// Called from an event-handler. So, "async void" is valid here.
|
||||
internal async void FlashArchive(string ArchivePath)
|
||||
{
|
||||
IsSwitchingInterface = true; // Prevents that a device is forced to Flash mode on this screen which is meant for flashing
|
||||
try
|
||||
{
|
||||
await SwitchModeViewModel.SwitchToWithProgress(PhoneNotifier, PhoneInterfaces.Lumia_Flash,
|
||||
(msg, sub) =>
|
||||
ActivateSubContext(new BusyViewModel(msg, sub)));
|
||||
if (((NokiaFlashModel)PhoneNotifier.CurrentModel).ReadPhoneInfo(ExtendedInfo: false).FlashAppProtocolVersionMajor < 2)
|
||||
FlashArchiveTask(ArchivePath);
|
||||
else
|
||||
await Task.Run(async () => await LumiaV2UnlockBootViewModel.LumiaV2FlashArchive(PhoneNotifier, ArchivePath, SetWorkingStatus, UpdateWorkingStatus, ExitSuccess, ExitFailure));
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
ActivateSubContext(new MessageViewModel(Ex.Message, Callback));
|
||||
}
|
||||
}
|
||||
|
||||
internal void FlashArchiveTask(string ArchivePath)
|
||||
{
|
||||
new Thread(() =>
|
||||
{
|
||||
ActivateSubContext(new BusyViewModel("Initializing flash..."));
|
||||
|
||||
NokiaFlashModel Phone = (NokiaFlashModel)PhoneNotifier.CurrentModel;
|
||||
|
||||
ulong TotalSizeSectors = 0;
|
||||
int PartitionCount = 0;
|
||||
ulong MainOSOldSectorCount = 0;
|
||||
ulong MainOSNewSectorCount = 0;
|
||||
ulong DataOldSectorCount = 0;
|
||||
ulong DataNewSectorCount = 0;
|
||||
ulong FirstMainOSSector = 0;
|
||||
bool GPTChanged = false;
|
||||
|
||||
try
|
||||
{
|
||||
GPT GPT = Phone.ReadGPT();
|
||||
|
||||
using (FileStream FileStream = new FileStream(ArchivePath, FileMode.Open))
|
||||
{
|
||||
using (ZipArchive Archive = new ZipArchive(FileStream, ZipArchiveMode.Read))
|
||||
{
|
||||
foreach (ZipArchiveEntry Entry in Archive.Entries)
|
||||
{
|
||||
// Determine if there is a partition layout present
|
||||
ZipArchiveEntry PartitionEntry = Archive.GetEntry("Partitions.xml");
|
||||
if (PartitionEntry == null)
|
||||
{
|
||||
GPT.MergePartitions(null, false, Archive);
|
||||
GPTChanged |= GPT.HasChanged;
|
||||
}
|
||||
else
|
||||
{
|
||||
using (Stream ZipStream = PartitionEntry.Open())
|
||||
{
|
||||
using (StreamReader ZipReader = new StreamReader(ZipStream))
|
||||
{
|
||||
string PartitionXml = ZipReader.ReadToEnd();
|
||||
GPT.MergePartitions(PartitionXml, false, Archive);
|
||||
GPTChanged |= GPT.HasChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// First determine if we need a new GPT!
|
||||
if (!Entry.FullName.Contains("/")) // No subfolders
|
||||
{
|
||||
string PartitionName = System.IO.Path.GetFileNameWithoutExtension(Entry.Name);
|
||||
int P = PartitionName.IndexOf('.');
|
||||
if (P >= 0)
|
||||
PartitionName = PartitionName.Substring(0, P); // Example: Data.bin.gz -> Data
|
||||
Partition Partition = GPT.Partitions.Where(p => string.Compare(p.Name, PartitionName, true) == 0).FirstOrDefault();
|
||||
if (Partition != null)
|
||||
{
|
||||
DecompressedStream DecompressedStream = new DecompressedStream(Entry.Open());
|
||||
ulong StreamLengthInSectors = (ulong)Entry.Length / 0x200;
|
||||
try
|
||||
{
|
||||
StreamLengthInSectors = (ulong)DecompressedStream.Length / 0x200;
|
||||
}
|
||||
catch { }
|
||||
|
||||
TotalSizeSectors += StreamLengthInSectors;
|
||||
PartitionCount++;
|
||||
|
||||
if (string.Compare(PartitionName, "MainOS", true) == 0)
|
||||
{
|
||||
MainOSOldSectorCount = Partition.SizeInSectors;
|
||||
MainOSNewSectorCount = StreamLengthInSectors;
|
||||
FirstMainOSSector = Partition.FirstSector;
|
||||
}
|
||||
else if (string.Compare(PartitionName, "Data", true) == 0)
|
||||
{
|
||||
DataOldSectorCount = Partition.SizeInSectors;
|
||||
DataNewSectorCount = StreamLengthInSectors;
|
||||
}
|
||||
else if (StreamLengthInSectors > Partition.SizeInSectors)
|
||||
{
|
||||
LogFile.Log("Flash failed! Size of partition '" + PartitionName + "' is too big.");
|
||||
ExitFailure("Flash failed!", "Size of partition '" + PartitionName + "' is too big.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((MainOSNewSectorCount > 0) && (DataNewSectorCount > 0))
|
||||
{
|
||||
if ((MainOSNewSectorCount > MainOSOldSectorCount) || (DataNewSectorCount > DataOldSectorCount))
|
||||
{
|
||||
UInt64 OSSpace = GPT.LastUsableSector - FirstMainOSSector + 1;
|
||||
if ((MainOSNewSectorCount + DataNewSectorCount) <= OSSpace)
|
||||
{
|
||||
// MainOS and Data partitions need to be re-aligned!
|
||||
Partition MainOSPartition = GPT.Partitions.Where(p => string.Compare(p.Name, "MainOS", true) == 0).Single();
|
||||
Partition DataPartition = GPT.Partitions.Where(p => string.Compare(p.Name, "Data", true) == 0).Single();
|
||||
MainOSPartition.LastSector = MainOSPartition.FirstSector + MainOSNewSectorCount - 1;
|
||||
DataPartition.FirstSector = MainOSPartition.LastSector + 1;
|
||||
DataPartition.LastSector = DataPartition.FirstSector + DataNewSectorCount - 1;
|
||||
|
||||
GPTChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.Log("Flash failed! Size of partitions 'MainOS' and 'Data' together are too big.");
|
||||
ExitFailure("Flash failed!", "Sizes of partitions 'MainOS' and 'Data' together are too big.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((MainOSNewSectorCount > 0) && (MainOSNewSectorCount > MainOSOldSectorCount))
|
||||
{
|
||||
LogFile.Log("Flash failed! Size of partition 'MainOS' is too big.");
|
||||
ExitFailure("Flash failed!", "Size of partition 'MainOS' is too big.");
|
||||
return;
|
||||
}
|
||||
else if ((DataNewSectorCount > 0) && (DataNewSectorCount > DataOldSectorCount))
|
||||
{
|
||||
LogFile.Log("Flash failed! Size of partition 'Data' is too big.");
|
||||
ExitFailure("Flash failed!", "Size of partition 'Data' is too big.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (GPTChanged)
|
||||
Phone.WriteGPT(GPT);
|
||||
|
||||
if (PartitionCount > 0)
|
||||
{
|
||||
BusyViewModel Busy = new BusyViewModel("Flashing...", MaxProgressValue: TotalSizeSectors, UIContext: UIContext);
|
||||
ProgressUpdater Updater = Busy.ProgressUpdater;
|
||||
ActivateSubContext(Busy);
|
||||
|
||||
int i = 0;
|
||||
|
||||
foreach (ZipArchiveEntry Entry in Archive.Entries)
|
||||
{
|
||||
// "MainOS.bin.gz" => "MainOS"
|
||||
string PartitionName = Entry.Name;
|
||||
int Pos = PartitionName.IndexOf('.');
|
||||
if (Pos >= 0)
|
||||
PartitionName = PartitionName.Substring(0, Pos);
|
||||
|
||||
Partition Partition = GPT.Partitions.Where(p => string.Compare(p.Name, PartitionName, true) == 0).FirstOrDefault();
|
||||
if (Partition != null)
|
||||
{
|
||||
Stream DecompressedStream = new DecompressedStream(Entry.Open());
|
||||
ulong StreamLengthInSectors = (ulong)Entry.Length / 0x200;
|
||||
try
|
||||
{
|
||||
StreamLengthInSectors = (ulong)DecompressedStream.Length / 0x200;
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (StreamLengthInSectors <= Partition.SizeInSectors)
|
||||
{
|
||||
i++;
|
||||
Busy.Message = "Flashing partition " + Partition.Name + " (" + i.ToString() + @"/" + PartitionCount.ToString() + ")";
|
||||
Phone.FlashRawPartition(DecompressedStream, Partition.Name, Updater);
|
||||
}
|
||||
DecompressedStream.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.Log("Flash failed! No valid partitions found in the archive.");
|
||||
ExitFailure("Flash failed!", "No valid partitions found in the archive");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
if (Ex is WPinternalsException)
|
||||
{
|
||||
ExitFailure("Flash failed!", ((WPinternalsException)Ex).SubMessage);
|
||||
}
|
||||
else
|
||||
ExitFailure("Flash failed!", null);
|
||||
return;
|
||||
}
|
||||
|
||||
ExitSuccess("Flash successful! Make sure you disable Windows Update on the phone!", null);
|
||||
}).Start();
|
||||
}
|
||||
|
||||
// Called from an event-handler. So, "async void" is valid here.
|
||||
internal async void FlashFFU(string FFUPath)
|
||||
{
|
||||
IsSwitchingInterface = true; // Prevents that a device is forced to Flash mode on this screen which is meant for flashing
|
||||
try
|
||||
{
|
||||
await SwitchModeViewModel.SwitchToWithProgress(PhoneNotifier, PhoneInterfaces.Lumia_Flash,
|
||||
(msg, sub) =>
|
||||
ActivateSubContext(new BusyViewModel(msg, sub)));
|
||||
FlashFFUTask(FFUPath);
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
ActivateSubContext(new MessageViewModel(Ex.Message, Callback));
|
||||
}
|
||||
}
|
||||
|
||||
internal void FlashFFUTask(string FFUPath)
|
||||
{
|
||||
new Thread(() =>
|
||||
{
|
||||
bool Result = true;
|
||||
|
||||
NokiaFlashModel Phone = (NokiaFlashModel)PhoneNotifier.CurrentModel;
|
||||
PhoneInfo Info = Phone.ReadPhoneInfo(false);
|
||||
|
||||
#region Remove bootloader changes
|
||||
|
||||
// If necessary remove bootloader changes
|
||||
// In case the NV vars were redirected, and a stock FFU is flashed, then the IsFlashing flag will be cleared in the redirected NV vars
|
||||
// And after a reboot the original NV vars are active again, but the IsFlashing flag is still set from when the bootloader was unlocked
|
||||
// So we will first restore the GPT, so the original vars are active again.
|
||||
// Then IsFlashing is true and the phone boots forcibly to FlashApp again.
|
||||
// Then we start normal FFU flasing and at the end the IsFlashing flag is cleared in the original vars.
|
||||
|
||||
if (Info.FlashAppProtocolVersionMajor >= 2)
|
||||
{
|
||||
byte[] GPTChunk = LumiaV2UnlockBootViewModel.GetGptChunk(Phone, 0x20000); // TODO: Get proper profile FFU and get ChunkSizeInBytes
|
||||
GPT GPT = new GPT(GPTChunk);
|
||||
FlashPart Part;
|
||||
List<FlashPart> FlashParts = new List<FlashPart>();
|
||||
|
||||
Partition NvBackupPartition = GPT.GetPartition("BACKUP_BS_NV");
|
||||
if (NvBackupPartition != null)
|
||||
{
|
||||
// This must be a left over of a half unlocked bootloader
|
||||
Partition NvPartition = GPT.GetPartition("UEFI_BS_NV");
|
||||
NvBackupPartition.Name = "UEFI_BS_NV";
|
||||
NvBackupPartition.PartitionGuid = NvPartition.PartitionGuid;
|
||||
NvBackupPartition.PartitionTypeGuid = NvPartition.PartitionTypeGuid;
|
||||
GPT.Partitions.Remove(NvPartition);
|
||||
|
||||
GPT.Rebuild();
|
||||
Part = new FlashPart();
|
||||
Part.StartSector = 0;
|
||||
Part.Stream = new MemoryStream(GPTChunk);
|
||||
FlashParts.Add(Part);
|
||||
}
|
||||
|
||||
// We should only clear NV if there was no backup NV to be restored and the current NV contains the SB unlock.
|
||||
if ((NvBackupPartition == null) && !Info.UefiSecureBootEnabled)
|
||||
{
|
||||
// ClearNV
|
||||
Part = new FlashPart();
|
||||
Partition Target = GPT.GetPartition("UEFI_BS_NV");
|
||||
Part.StartSector = (UInt32)Target.FirstSector;
|
||||
Part.Stream = new MemoryStream(new byte[0x40000]);
|
||||
FlashParts.Add(Part);
|
||||
}
|
||||
|
||||
if (FlashParts.Count > 0)
|
||||
{
|
||||
ActivateSubContext(new BusyViewModel("Restoring bootloader..."));
|
||||
WPinternalsStatus LastStatus = WPinternalsStatus.Undefined;
|
||||
LumiaV2UnlockBootViewModel.LumiaV2CustomFlash(PhoneNotifier, FFUPath, false, false, FlashParts, true, ClearFlashingStatusAtEnd: false,
|
||||
SetWorkingStatus: (m, s, v, a, st) =>
|
||||
{
|
||||
if ((st == WPinternalsStatus.Scanning) || (st == WPinternalsStatus.WaitingForManualReset))
|
||||
SetWorkingStatus(m, s, v, a, st);
|
||||
else if ((LastStatus == WPinternalsStatus.Scanning) || (LastStatus == WPinternalsStatus.WaitingForManualReset))
|
||||
SetWorkingStatus("Restoring bootloader...", null, null, Status: WPinternalsStatus.Flashing);
|
||||
LastStatus = st;
|
||||
},
|
||||
UpdateWorkingStatus: (m, s, v, st) =>
|
||||
{
|
||||
if ((st == WPinternalsStatus.Scanning) || (st == WPinternalsStatus.WaitingForManualReset))
|
||||
UpdateWorkingStatus(m, s, v, st);
|
||||
else if ((LastStatus == WPinternalsStatus.Scanning) || (LastStatus == WPinternalsStatus.WaitingForManualReset))
|
||||
SetWorkingStatus("Restoring bootloader...", null, null, Status: WPinternalsStatus.Flashing);
|
||||
LastStatus = st;
|
||||
}
|
||||
).Wait();
|
||||
|
||||
if ((PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) && (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_Flash))
|
||||
PhoneNotifier.WaitForArrival().Wait();
|
||||
|
||||
if (PhoneNotifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader)
|
||||
((NokiaFlashModel)PhoneNotifier.CurrentModel).SwitchToFlashAppContext();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
Phone = (NokiaFlashModel)PhoneNotifier.CurrentModel;
|
||||
|
||||
ActivateSubContext(new BusyViewModel("Initializing flash..."));
|
||||
|
||||
string ErrorSubMessage = null;
|
||||
|
||||
try
|
||||
{
|
||||
FFU FFU = new FFU(FFUPath);
|
||||
BusyViewModel Busy = new BusyViewModel("Flashing original FFU...", MaxProgressValue: FFU.TotalChunkCount, UIContext: UIContext);
|
||||
ActivateSubContext(Busy);
|
||||
byte Options = 0;
|
||||
if (!Info.SecureFfuEnabled || Info.Authenticated || Info.RdcPresent)
|
||||
Options = (byte)((FlashOptions)Options | FlashOptions.SkipSignatureCheck);
|
||||
Phone.FlashFFU(FFU, Busy.ProgressUpdater, true, Options);
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
if (Ex is WPinternalsException)
|
||||
ErrorSubMessage = ((WPinternalsException)Ex).SubMessage;
|
||||
Result = false;
|
||||
}
|
||||
|
||||
|
||||
if (!Result)
|
||||
{
|
||||
ExitFailure("Flash failed!", ErrorSubMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
ExitSuccess("Flash successful!", null);
|
||||
}).Start();
|
||||
}
|
||||
|
||||
// Called from an event-handler. So, "async void" is valid here.
|
||||
internal async void Exit()
|
||||
{
|
||||
try
|
||||
{
|
||||
await SwitchModeViewModel.SwitchToWithProgress(PhoneNotifier, PhoneInterfaces.Lumia_Normal,
|
||||
(msg, sub) =>
|
||||
ActivateSubContext(new BusyViewModel(msg, sub)));
|
||||
IsSwitchingInterface = false;
|
||||
Callback();
|
||||
ActivateSubContext(null);
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
ActivateSubContext(new MessageViewModel(Ex.Message, () =>
|
||||
{
|
||||
IsSwitchingInterface = false;
|
||||
Callback();
|
||||
ActivateSubContext(null);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
internal void ExitSuccess(string Message, string SubMessage)
|
||||
{
|
||||
MessageViewModel SuccessMessageViewModel = new MessageViewModel(Message, () =>
|
||||
{
|
||||
// No need to call Exit() to go to normal mode, because it already switches to normal mode automatically.
|
||||
IsSwitchingInterface = false; // From here on a device will be forced to Flash mode again on this screen which is meant for flashing
|
||||
Callback();
|
||||
ActivateSubContext(null);
|
||||
});
|
||||
SuccessMessageViewModel.SubMessage = SubMessage;
|
||||
ActivateSubContext(SuccessMessageViewModel);
|
||||
}
|
||||
|
||||
internal void ExitFailure(string Message, string SubMessage)
|
||||
{
|
||||
MessageViewModel ErrorMessageViewModel = new MessageViewModel(Message, () =>
|
||||
{
|
||||
IsSwitchingInterface = false;
|
||||
Callback();
|
||||
ActivateSubContext(null);
|
||||
});
|
||||
ErrorMessageViewModel.SubMessage = SubMessage;
|
||||
ActivateSubContext(ErrorMessageViewModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// 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;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class LumiaInfoViewModel: ContextViewModel
|
||||
{
|
||||
internal PhoneInterfaces? CurrentInterface;
|
||||
internal IDisposable CurrentModel;
|
||||
internal PhoneNotifierViewModel PhoneNotifier;
|
||||
private Action<PhoneInterfaces> ModeSwitchRequestCallback;
|
||||
private Action SwitchToGettingStarted;
|
||||
|
||||
internal LumiaInfoViewModel(PhoneNotifierViewModel PhoneNotifier, Action<PhoneInterfaces> ModeSwitchRequestCallback, Action SwitchToGettingStarted)
|
||||
: base()
|
||||
{
|
||||
this.PhoneNotifier = PhoneNotifier;
|
||||
this.ModeSwitchRequestCallback = ModeSwitchRequestCallback;
|
||||
this.SwitchToGettingStarted = SwitchToGettingStarted;
|
||||
|
||||
CurrentInterface = PhoneNotifier.CurrentInterface;
|
||||
CurrentModel = PhoneNotifier.CurrentModel;
|
||||
|
||||
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
||||
PhoneNotifier.DeviceRemoved += DeviceRemoved;
|
||||
}
|
||||
|
||||
~LumiaInfoViewModel()
|
||||
{
|
||||
PhoneNotifier.NewDeviceArrived -= NewDeviceArrived;
|
||||
PhoneNotifier.DeviceRemoved -= DeviceRemoved;
|
||||
}
|
||||
|
||||
void DeviceRemoved()
|
||||
{
|
||||
CurrentInterface = null;
|
||||
CurrentModel = null;
|
||||
ActivateSubContext(null);
|
||||
}
|
||||
|
||||
void NewDeviceArrived(ArrivalEventArgs Args)
|
||||
{
|
||||
CurrentInterface = Args.NewInterface;
|
||||
CurrentModel = Args.NewModel;
|
||||
|
||||
// Determine SubcontextViewModel
|
||||
switch (CurrentInterface)
|
||||
{
|
||||
case null:
|
||||
case PhoneInterfaces.Lumia_Bootloader:
|
||||
ActivateSubContext(null);
|
||||
break;
|
||||
case PhoneInterfaces.Lumia_Normal:
|
||||
ActivateSubContext(new NokiaNormalViewModel((NokiaPhoneModel)CurrentModel, ModeSwitchRequestCallback));
|
||||
break;
|
||||
case PhoneInterfaces.Lumia_Flash:
|
||||
ActivateSubContext(new NokiaFlashViewModel((NokiaFlashModel)CurrentModel, ModeSwitchRequestCallback, SwitchToGettingStarted));
|
||||
break;
|
||||
case PhoneInterfaces.Lumia_Label:
|
||||
ActivateSubContext(new NokiaLabelViewModel((NokiaPhoneModel)CurrentModel, ModeSwitchRequestCallback));
|
||||
break;
|
||||
case PhoneInterfaces.Lumia_MassStorage:
|
||||
ActivateSubContext(new NokiaMassStorageViewModel((MassStorage)CurrentModel));
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
// 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;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class LumiaModeViewModel : ContextViewModel
|
||||
{
|
||||
internal PhoneInterfaces? CurrentInterface;
|
||||
internal IDisposable CurrentModel;
|
||||
internal PhoneNotifierViewModel PhoneNotifier;
|
||||
private Action Callback;
|
||||
|
||||
internal LumiaModeViewModel(PhoneNotifierViewModel PhoneNotifier, Action Callback)
|
||||
: base()
|
||||
{
|
||||
this.PhoneNotifier = PhoneNotifier;
|
||||
this.Callback = Callback;
|
||||
|
||||
CurrentInterface = PhoneNotifier.CurrentInterface;
|
||||
CurrentModel = PhoneNotifier.CurrentModel;
|
||||
|
||||
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
||||
PhoneNotifier.DeviceRemoved += DeviceRemoved;
|
||||
}
|
||||
|
||||
~LumiaModeViewModel()
|
||||
{
|
||||
PhoneNotifier.NewDeviceArrived -= NewDeviceArrived;
|
||||
PhoneNotifier.DeviceRemoved -= DeviceRemoved;
|
||||
}
|
||||
|
||||
void DeviceRemoved()
|
||||
{
|
||||
CurrentInterface = null;
|
||||
CurrentModel = null;
|
||||
|
||||
if (!IsSwitchingInterface)
|
||||
ActivateSubContext(null);
|
||||
}
|
||||
|
||||
void NewDeviceArrived(ArrivalEventArgs Args)
|
||||
{
|
||||
CurrentInterface = Args.NewInterface;
|
||||
CurrentModel = Args.NewModel;
|
||||
|
||||
if (!IsSwitchingInterface && IsActive)
|
||||
Refresh();
|
||||
}
|
||||
|
||||
internal override void EvaluateViewState()
|
||||
{
|
||||
if (!IsSwitchingInterface && IsActive)
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void Refresh()
|
||||
{
|
||||
// Determine SubcontextViewModel
|
||||
switch (CurrentInterface)
|
||||
{
|
||||
case null:
|
||||
case PhoneInterfaces.Lumia_Bootloader:
|
||||
break;
|
||||
case PhoneInterfaces.Lumia_Normal:
|
||||
ActivateSubContext(new NokiaModeNormalViewModel((NokiaPhoneModel)CurrentModel, OnModeSwitchRequested));
|
||||
break;
|
||||
case PhoneInterfaces.Lumia_Flash:
|
||||
ActivateSubContext(new NokiaModeFlashViewModel((NokiaFlashModel)CurrentModel, OnModeSwitchRequested));
|
||||
break;
|
||||
case PhoneInterfaces.Lumia_Label:
|
||||
ActivateSubContext(new NokiaModeLabelViewModel((NokiaPhoneModel)CurrentModel, OnModeSwitchRequested));
|
||||
break;
|
||||
case PhoneInterfaces.Lumia_MassStorage:
|
||||
ActivateSubContext(new NokiaModeMassStorageViewModel(null));
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
// Called from eventhandler, so "async void" is valid here.
|
||||
internal async void OnModeSwitchRequested(PhoneInterfaces? TargetInterface)
|
||||
{
|
||||
IsSwitchingInterface = true;
|
||||
|
||||
try
|
||||
{
|
||||
await SwitchModeViewModel.SwitchToWithStatus(PhoneNotifier, TargetInterface, SetWorkingStatus, UpdateWorkingStatus, null); // This is a manual switch. We don't care about which volume arrives.
|
||||
|
||||
IsSwitchingInterface = false;
|
||||
Callback();
|
||||
Refresh();
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
IsSwitchingInterface = false;
|
||||
ActivateSubContext(new MessageViewModel(Ex.Message, () => {
|
||||
Callback();
|
||||
Refresh();
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,198 @@
|
||||
// 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.Threading;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class LumiaUnlockRootTargetSelectionViewModel: LumiaRootAccessTargetSelectionViewModel
|
||||
{
|
||||
public LumiaUnlockRootTargetSelectionViewModel(PhoneNotifierViewModel PhoneNotifier, Action SwitchToUnlockBoot, Action SwitchToDumpRom, Action SwitchToFlashRom, Action UnlockPhoneCallback, Action<string, string> UnlockImageCallback)
|
||||
: base(PhoneNotifier, SwitchToUnlockBoot, SwitchToDumpRom, SwitchToFlashRom, UnlockPhoneCallback, UnlockImageCallback) { }
|
||||
}
|
||||
|
||||
internal class LumiaUndoRootTargetSelectionViewModel: LumiaRootAccessTargetSelectionViewModel
|
||||
{
|
||||
public LumiaUndoRootTargetSelectionViewModel(PhoneNotifierViewModel PhoneNotifier, Action SwitchToUnlockBoot, Action SwitchToDumpRom, Action SwitchToFlashRom, Action UnlockPhoneCallback, Action<string, string> UnlockImageCallback)
|
||||
: base(PhoneNotifier, SwitchToUnlockBoot, SwitchToDumpRom, SwitchToFlashRom, UnlockPhoneCallback, UnlockImageCallback) { }
|
||||
}
|
||||
|
||||
internal class LumiaRootAccessTargetSelectionViewModel: ContextViewModel
|
||||
{
|
||||
private PhoneNotifierViewModel PhoneNotifier;
|
||||
internal Action SwitchToUnlockBoot;
|
||||
internal Action SwitchToDumpRom;
|
||||
internal Action SwitchToFlashRom;
|
||||
private Action UnlockPhoneCallback;
|
||||
private Action<string, string> UnlockImageCallback;
|
||||
|
||||
internal LumiaRootAccessTargetSelectionViewModel(PhoneNotifierViewModel PhoneNotifier, Action SwitchToUnlockBoot, Action SwitchToDumpRom, Action SwitchToFlashRom, Action UnlockPhoneCallback, Action<string, string> UnlockImageCallback)
|
||||
: base()
|
||||
{
|
||||
this.PhoneNotifier = PhoneNotifier;
|
||||
this.SwitchToDumpRom = SwitchToDumpRom;
|
||||
this.SwitchToFlashRom = SwitchToFlashRom;
|
||||
this.SwitchToUnlockBoot = SwitchToUnlockBoot;
|
||||
this.UnlockPhoneCallback = UnlockPhoneCallback;
|
||||
this.UnlockImageCallback = UnlockImageCallback;
|
||||
|
||||
this.PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
||||
this.PhoneNotifier.DeviceRemoved += DeviceRemoved;
|
||||
|
||||
new Thread(() => EvaluateViewState()).Start();
|
||||
}
|
||||
|
||||
private string _EFIESPMountPoint;
|
||||
public string EFIESPMountPoint
|
||||
{
|
||||
get
|
||||
{
|
||||
return _EFIESPMountPoint;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _EFIESPMountPoint)
|
||||
{
|
||||
_EFIESPMountPoint = value;
|
||||
OnPropertyChanged("EFIESPMountPoint");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _MainOSMountPoint;
|
||||
public string MainOSMountPoint
|
||||
{
|
||||
get
|
||||
{
|
||||
return _MainOSMountPoint;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _MainOSMountPoint)
|
||||
{
|
||||
_MainOSMountPoint = value;
|
||||
OnPropertyChanged("MainOSMountPoint");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _IsPhoneDisconnected;
|
||||
public bool IsPhoneDisconnected
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IsPhoneDisconnected;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _IsPhoneDisconnected)
|
||||
{
|
||||
_IsPhoneDisconnected = value;
|
||||
OnPropertyChanged("IsPhoneDisconnected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _IsPhoneInMassStorage;
|
||||
public bool IsPhoneInMassStorage
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IsPhoneInMassStorage;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _IsPhoneInMassStorage)
|
||||
{
|
||||
_IsPhoneInMassStorage = value;
|
||||
OnPropertyChanged("IsPhoneInMassStorage");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _IsPhoneInOtherMode;
|
||||
public bool IsPhoneInOtherMode
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IsPhoneInOtherMode;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _IsPhoneInOtherMode)
|
||||
{
|
||||
_IsPhoneInOtherMode = value;
|
||||
OnPropertyChanged("IsPhoneInOtherMode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DelegateCommand _UnlockPhoneCommand;
|
||||
public DelegateCommand UnlockPhoneCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_UnlockPhoneCommand == null)
|
||||
{
|
||||
_UnlockPhoneCommand = new DelegateCommand(() => { UnlockPhoneCallback(); }, () => !IsPhoneDisconnected);
|
||||
}
|
||||
return _UnlockPhoneCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private DelegateCommand _UnlockImageCommand;
|
||||
public DelegateCommand UnlockImageCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_UnlockImageCommand == null)
|
||||
{
|
||||
_UnlockImageCommand = new DelegateCommand(() => { UnlockImageCallback(EFIESPMountPoint, MainOSMountPoint); }, () => ((EFIESPMountPoint != null) || (MainOSMountPoint != null)));
|
||||
}
|
||||
return _UnlockImageCommand;
|
||||
}
|
||||
}
|
||||
|
||||
~LumiaRootAccessTargetSelectionViewModel()
|
||||
{
|
||||
PhoneNotifier.NewDeviceArrived -= NewDeviceArrived;
|
||||
}
|
||||
|
||||
void NewDeviceArrived(ArrivalEventArgs Args)
|
||||
{
|
||||
new Thread(() => EvaluateViewState()).Start();
|
||||
}
|
||||
|
||||
void DeviceRemoved()
|
||||
{
|
||||
new Thread(() => EvaluateViewState()).Start();
|
||||
}
|
||||
|
||||
internal override void EvaluateViewState()
|
||||
{
|
||||
IsPhoneDisconnected = PhoneNotifier.CurrentInterface == null;
|
||||
IsPhoneInMassStorage = PhoneNotifier.CurrentInterface == PhoneInterfaces.Lumia_MassStorage;
|
||||
IsPhoneInOtherMode = (!IsPhoneDisconnected && !IsPhoneInMassStorage);
|
||||
UnlockPhoneCommand.RaiseCanExecuteChanged();
|
||||
UnlockImageCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
// 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.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class LumiaUnlockRootViewModel: ContextViewModel
|
||||
{
|
||||
private PhoneNotifierViewModel PhoneNotifier;
|
||||
private Action SwitchToUnlockBoot;
|
||||
private Action SwitchToDumpRom;
|
||||
private Action SwitchToFlashRom;
|
||||
private Action Callback;
|
||||
private bool DoUnlock;
|
||||
|
||||
internal LumiaUnlockRootViewModel(PhoneNotifierViewModel PhoneNotifier, Action SwitchToUnlockBoot, Action SwitchToDumpRom, Action SwitchToFlashRom, bool DoUnlock, Action Callback)
|
||||
: base()
|
||||
{
|
||||
IsSwitchingInterface = false;
|
||||
IsFlashModeOperation = true;
|
||||
|
||||
this.PhoneNotifier = PhoneNotifier;
|
||||
this.SwitchToDumpRom = SwitchToDumpRom;
|
||||
this.SwitchToFlashRom = SwitchToFlashRom;
|
||||
this.SwitchToUnlockBoot = SwitchToUnlockBoot;
|
||||
this.DoUnlock = DoUnlock;
|
||||
this.Callback = Callback;
|
||||
}
|
||||
|
||||
internal override void EvaluateViewState()
|
||||
{
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (DoUnlock)
|
||||
{
|
||||
if ((SubContextViewModel == null) || (SubContextViewModel is LumiaUndoRootTargetSelectionViewModel))
|
||||
ActivateSubContext(new LumiaUnlockRootTargetSelectionViewModel(PhoneNotifier, SwitchToUnlockBoot, SwitchToDumpRom, SwitchToFlashRom, DoUnlockPhone, DoUnlockImage));
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((SubContextViewModel == null) || (SubContextViewModel is LumiaUnlockRootTargetSelectionViewModel))
|
||||
ActivateSubContext(new LumiaUndoRootTargetSelectionViewModel(PhoneNotifier, SwitchToUnlockBoot, SwitchToDumpRom, SwitchToFlashRom, DoUnlockPhone, DoUnlockImage));
|
||||
}
|
||||
}
|
||||
|
||||
internal async void DoUnlockPhone()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsSwitchingInterface = true;
|
||||
await SwitchModeViewModel.SwitchToWithProgress(PhoneNotifier, PhoneInterfaces.Lumia_MassStorage,
|
||||
(msg, sub) =>
|
||||
ActivateSubContext(new BusyViewModel(msg, sub)));
|
||||
bool HasNewBootloader = HasNewBootloaderFromMassStorage();
|
||||
string EFIESPPath = HasNewBootloader ? null : ((MassStorage)PhoneNotifier.CurrentModel).Drive + @"\EFIESP\";
|
||||
string MainOSPath = ((MassStorage)PhoneNotifier.CurrentModel).Drive + @"\";
|
||||
StartPatch(EFIESPPath, MainOSPath, HasNewBootloader);
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
ActivateSubContext(new MessageViewModel(Ex.Message, () => {
|
||||
Callback();
|
||||
ActivateSubContext(null);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
internal void DoUnlockImage(string EFIESPMountPoint, string MainOSMountPoint)
|
||||
{
|
||||
StartPatch(EFIESPMountPoint, MainOSMountPoint, false); // Unlock image is only supported for Lumia's with bootloader Spec A. Due to complexity of Spec B bootloader hack, it cannot be applied on a mounted image.
|
||||
}
|
||||
|
||||
// Magic!
|
||||
// Apply patches for Root Access
|
||||
private void StartPatch(string EFIESP, string MainOS, bool HasNewBootloader)
|
||||
{
|
||||
IsSwitchingInterface = false;
|
||||
new Thread(() =>
|
||||
{
|
||||
if (DoUnlock)
|
||||
LogFile.BeginAction("EnableRootAccess");
|
||||
else
|
||||
LogFile.BeginAction("DisableRootAccess");
|
||||
|
||||
bool Result = false;
|
||||
|
||||
if (EFIESP != null)
|
||||
{
|
||||
if (DoUnlock)
|
||||
ActivateSubContext(new BusyViewModel("Enable Root Access on EFIESP..."));
|
||||
else
|
||||
ActivateSubContext(new BusyViewModel("Disable Root Access on EFIESP..."));
|
||||
|
||||
try
|
||||
{
|
||||
App.PatchEngine.TargetPath = EFIESP;
|
||||
if (DoUnlock)
|
||||
{
|
||||
Result = App.PatchEngine.Patch("SecureBootHack-V1-EFIESP");
|
||||
|
||||
if (!Result)
|
||||
{
|
||||
ActivateSubContext(new MessageViewModel("Failed to enable Root Access on EFIESP! Check the OS version on the phone and verify the compatibility-list in the \"Getting started\" section.", Exit));
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
App.PatchEngine.Restore("SecureBootHack-V1-EFIESP");
|
||||
Result = true;
|
||||
}
|
||||
}
|
||||
catch (UnauthorizedAccessException Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
ActivateSubContext(new MessageViewModel("Failed to enable Root Access on EFIESP! Not enough privileges to perform action. Try to logon to Windows with an administrator account.", Exit));
|
||||
return;
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
Result = false;
|
||||
}
|
||||
|
||||
if (!Result)
|
||||
{
|
||||
ActivateSubContext(new MessageViewModel("Failed to enable Root Access on EFIESP!", Exit));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (MainOS != null)
|
||||
{
|
||||
if (DoUnlock)
|
||||
ActivateSubContext(new BusyViewModel("Enable Root Access on MainOS..."));
|
||||
else
|
||||
ActivateSubContext(new BusyViewModel("Disable Root Access on MainOS..."));
|
||||
|
||||
try
|
||||
{
|
||||
App.PatchEngine.TargetPath = MainOS;
|
||||
if (DoUnlock)
|
||||
{
|
||||
Result = App.PatchEngine.Patch("RootAccess-MainOS");
|
||||
|
||||
if (Result)
|
||||
Result = App.PatchEngine.Patch("SecureBootHack-MainOS");
|
||||
|
||||
if (!Result)
|
||||
{
|
||||
ActivateSubContext(new MessageViewModel("Failed to enable Root Access on MainOS! Check the OS version on the phone and verify the compatibility-list in the \"Getting started\" section.", Exit));
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
App.PatchEngine.Restore("RootAccess-MainOS");
|
||||
|
||||
if (!HasNewBootloader)
|
||||
App.PatchEngine.Restore("SecureBootHack-MainOS");
|
||||
|
||||
Result = true;
|
||||
}
|
||||
}
|
||||
catch (UnauthorizedAccessException Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
ActivateSubContext(new MessageViewModel("Failed to enable Root Access on MainOS! Not enough privileges to perform action. Try to logon to Windows with an administrator account.", Exit));
|
||||
Result = false;
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
Result = false;
|
||||
}
|
||||
|
||||
if (!Result)
|
||||
{
|
||||
ActivateSubContext(new MessageViewModel("Failed to enable Root Access on MainOS!", Exit));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DoUnlock)
|
||||
ActivateSubContext(new MessageViewModel("Root Access successfully enabled!", Exit));
|
||||
else
|
||||
ActivateSubContext(new MessageViewModel("Root Access successfully disabled!", Exit));
|
||||
}
|
||||
}
|
||||
|
||||
if (DoUnlock)
|
||||
LogFile.EndAction("EnableRootAccess");
|
||||
else
|
||||
LogFile.EndAction("DisableRootAccess");
|
||||
}).Start();
|
||||
}
|
||||
|
||||
private void Exit()
|
||||
{
|
||||
IsSwitchingInterface = false;
|
||||
Callback();
|
||||
ActivateSubContext(null);
|
||||
}
|
||||
|
||||
private bool HasNewBootloaderFromMassStorage()
|
||||
{
|
||||
bool Result = false;
|
||||
MassStorage Phone = (MassStorage)PhoneNotifier.CurrentModel;
|
||||
Phone.OpenVolume(false);
|
||||
byte[] GPTBuffer = Phone.ReadSectors(1, 33);
|
||||
GPT GPT = new WPinternals.GPT(GPTBuffer);
|
||||
Partition Partition = GPT.GetPartition("UEFI");
|
||||
byte[] UefiBuffer = Phone.ReadSectors(Partition.FirstSector, Partition.LastSector - Partition.FirstSector + 1);
|
||||
UEFI UEFI = new UEFI(UefiBuffer);
|
||||
string BootMgrName = UEFI.EFIs.Where(efi => ((efi.Name != null) && ((efi.Name.Contains("BootMgrApp")) || (efi.Name.Contains("FlashApp"))))).First().Name;
|
||||
byte[] BootMgr = UEFI.GetFile(BootMgrName);
|
||||
// "Header V2"
|
||||
Result = (ByteOperations.FindAscii(BootMgr, "Header V2") != null);
|
||||
Phone.CloseVolume();
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,580 @@
|
||||
// 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 Microsoft.Win32;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
public enum NavigationSubject
|
||||
{
|
||||
Info,
|
||||
Mode,
|
||||
Install,
|
||||
About
|
||||
};
|
||||
|
||||
public enum PhoneInterfaces
|
||||
{
|
||||
Lumia_Normal,
|
||||
Lumia_Flash,
|
||||
Lumia_Label,
|
||||
Lumia_MassStorage,
|
||||
Lumia_Bootloader,
|
||||
Qualcomm_Download,
|
||||
Qualcomm_Flash,
|
||||
Lumia_BadMassStorage
|
||||
};
|
||||
|
||||
// Create this class on the UI thread, after the main-window of the application is initialized.
|
||||
// It is necessary to create the object on the UI thread, because notification events to the View need to be fired on that thread.
|
||||
// The Model for this ViewModel communicates over USB and for that it uses the hWnd of the main window.
|
||||
// Therefore the main window must be created before the ViewModel is created.
|
||||
internal class MainViewModel : INotifyPropertyChanged
|
||||
{
|
||||
public PhoneInterfaces? CurrentInterface = null;
|
||||
public PhoneInterfaces? LastInterface = null;
|
||||
public NokiaPhoneModel CurrentModel = null;
|
||||
public PhoneNotifierViewModel PhoneNotifier;
|
||||
public LumiaInfoViewModel InfoViewModel;
|
||||
public LumiaModeViewModel ModeViewModel;
|
||||
public LumiaUnlockBootViewModel BootUnlockViewModel;
|
||||
public LumiaUnlockBootViewModel BootRestoreViewModel;
|
||||
public LumiaUnlockRootViewModel RootUnlockViewModel;
|
||||
public LumiaUnlockRootViewModel RootRestoreViewModel;
|
||||
public BackupViewModel BackupViewModel;
|
||||
public RestoreViewModel RestoreViewModel;
|
||||
public LumiaFlashRomViewModel LumiaFlashRomViewModel;
|
||||
public DumpRomViewModel DumpRomViewModel;
|
||||
public DownloadsViewModel DownloadsViewModel;
|
||||
private GettingStartedViewModel _GettingStartedViewModel = null;
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
private void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
if (this.PropertyChanged != null)
|
||||
{
|
||||
if (MainSyncContext == SynchronizationContext.Current)
|
||||
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
|
||||
else
|
||||
MainSyncContext.Post(s => PropertyChanged(this, new PropertyChangedEventArgs(propertyName)), null);
|
||||
}
|
||||
}
|
||||
|
||||
private ContextViewModel _ContextViewModel;
|
||||
public ContextViewModel ContextViewModel
|
||||
{
|
||||
get
|
||||
{
|
||||
return _ContextViewModel;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_ContextViewModel != value)
|
||||
{
|
||||
if (_ContextViewModel != null)
|
||||
_ContextViewModel.IsActive = false;
|
||||
_ContextViewModel = value;
|
||||
if (_ContextViewModel != null)
|
||||
{
|
||||
_ContextViewModel.Activate();
|
||||
}
|
||||
OnPropertyChanged("ContextViewModel");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private SynchronizationContext MainSyncContext;
|
||||
|
||||
public MainViewModel()
|
||||
{
|
||||
MainSyncContext = SynchronizationContext.Current;
|
||||
|
||||
LogFile.LogApplicationVersion();
|
||||
|
||||
// Set global callback for cases where Dependency Injection is not possible.
|
||||
App.NavigateToGettingStarted = () => { GettingStartedCommand.Execute(null); };
|
||||
App.NavigateToUnlockBoot = () => { BootUnlockCommand.Execute(null); };
|
||||
|
||||
if (Registry.CurrentUser.OpenSubKey("Software\\WPInternals") == null)
|
||||
Registry.CurrentUser.OpenSubKey("Software", true).CreateSubKey("WPInternals");
|
||||
|
||||
if ((Registration.IsPrerelease) && (Registry.CurrentUser.OpenSubKey("Software\\WPInternals").GetValue("NdaAccepted") == null))
|
||||
{
|
||||
this.ContextViewModel = new DisclaimerAndNdaViewModel(Disclaimer_Accepted);
|
||||
}
|
||||
else if (Registry.CurrentUser.OpenSubKey("Software\\WPInternals").GetValue("DisclaimerAccepted") == null)
|
||||
{
|
||||
this.ContextViewModel = new DisclaimerViewModel(Disclaimer_Accepted);
|
||||
}
|
||||
else if ((Registration.IsPrerelease) && !Registration.IsRegistered())
|
||||
{
|
||||
ContextViewModel = new RegistrationViewModel(Registration_Completed, Registration_Failed);
|
||||
}
|
||||
else
|
||||
StartOperation();
|
||||
}
|
||||
|
||||
void Disclaimer_Accepted()
|
||||
{
|
||||
ContextViewModel = null;
|
||||
|
||||
if ((Registration.IsPrerelease) && !Registration.IsRegistered())
|
||||
{
|
||||
ContextViewModel = new RegistrationViewModel(Registration_Completed, Registration_Failed);
|
||||
}
|
||||
else
|
||||
StartOperation();
|
||||
}
|
||||
|
||||
void Registration_Completed()
|
||||
{
|
||||
ContextViewModel = null;
|
||||
StartOperation();
|
||||
}
|
||||
|
||||
void Registration_Failed()
|
||||
{
|
||||
ContextViewModel = new MessageViewModel("Registration failed", () => Environment.Exit(0));
|
||||
((MessageViewModel)ContextViewModel).SubMessage = "Check your filewall settings";
|
||||
}
|
||||
|
||||
public void StartOperation()
|
||||
{
|
||||
IsMenuEnabled = true;
|
||||
|
||||
_GettingStartedViewModel = new GettingStartedViewModel(
|
||||
() =>
|
||||
{
|
||||
ContextViewModel = new DisclaimerViewModel(
|
||||
() => ContextViewModel = _GettingStartedViewModel
|
||||
);
|
||||
},
|
||||
SwitchToUnlockBoot,
|
||||
SwitchToUnlockRoot,
|
||||
SwitchToBackup,
|
||||
SwitchToDumpFFU,
|
||||
SwitchToFlashRom,
|
||||
SwitchToDownload
|
||||
);
|
||||
this.ContextViewModel = _GettingStartedViewModel;
|
||||
|
||||
PhoneNotifier = new PhoneNotifierViewModel();
|
||||
PhoneNotifier.NewDeviceArrived += PhoneNotifier_NewDeviceArrived;
|
||||
PhoneNotifier.DeviceRemoved += PhoneNotifier_DeviceRemoved;
|
||||
|
||||
InfoViewModel = new LumiaInfoViewModel(PhoneNotifier, (TargetInterface) =>
|
||||
{
|
||||
ModeViewModel.OnModeSwitchRequested(TargetInterface);
|
||||
ContextViewModel = ModeViewModel;
|
||||
},
|
||||
() =>
|
||||
{
|
||||
ContextViewModel = _GettingStartedViewModel;
|
||||
});
|
||||
InfoViewModel.ActivateSubContext(null);
|
||||
|
||||
ModeViewModel = new LumiaModeViewModel(PhoneNotifier, SwitchToInfoViewModel);
|
||||
ModeViewModel.ActivateSubContext(null);
|
||||
|
||||
BootUnlockViewModel = new LumiaUnlockBootViewModel(PhoneNotifier, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, true, SwitchToInfoViewModel);
|
||||
BootUnlockViewModel.ActivateSubContext(null);
|
||||
|
||||
BootRestoreViewModel = new LumiaUnlockBootViewModel(PhoneNotifier, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, false, SwitchToInfoViewModel);
|
||||
BootRestoreViewModel.ActivateSubContext(null);
|
||||
|
||||
RootUnlockViewModel = new LumiaUnlockRootViewModel(PhoneNotifier, SwitchToUnlockBoot, SwitchToDumpFFU, SwitchToFlashRom, true, SwitchToInfoViewModel);
|
||||
RootUnlockViewModel.ActivateSubContext(null);
|
||||
|
||||
RootRestoreViewModel = new LumiaUnlockRootViewModel(PhoneNotifier, SwitchToUnlockBoot, SwitchToDumpFFU, SwitchToFlashRom, false, SwitchToInfoViewModel);
|
||||
RootRestoreViewModel.ActivateSubContext(null);
|
||||
|
||||
BackupViewModel = new BackupViewModel(PhoneNotifier, SwitchToUnlockBoot, SwitchToInfoViewModel);
|
||||
BackupViewModel.ActivateSubContext(null);
|
||||
|
||||
RestoreViewModel = new RestoreViewModel(PhoneNotifier, SwitchToDifferentInterface, SwitchToUnlockBoot, SwitchToFlashRom, SwitchToInfoViewModel);
|
||||
RestoreViewModel.ActivateSubContext(null);
|
||||
|
||||
LumiaFlashRomViewModel = new LumiaFlashRomViewModel(PhoneNotifier, SwitchToUnlockBoot, SwitchToUnlockRoot, SwitchToDumpFFU, SwitchToBackup, SwitchToInfoViewModel);
|
||||
LumiaFlashRomViewModel.ActivateSubContext(null);
|
||||
|
||||
DumpRomViewModel = new DumpRomViewModel(SwitchToUnlockBoot, SwitchToUnlockRoot, SwitchToFlashRom);
|
||||
DumpRomViewModel.ActivateSubContext(null);
|
||||
|
||||
DownloadsViewModel = new DownloadsViewModel(PhoneNotifier);
|
||||
App.DownloadManager = DownloadsViewModel;
|
||||
|
||||
PhoneNotifier.Start();
|
||||
}
|
||||
|
||||
internal void SwitchToInfoViewModel()
|
||||
{
|
||||
ContextViewModel = InfoViewModel;
|
||||
}
|
||||
|
||||
internal void SwitchToUnlockBoot()
|
||||
{
|
||||
ContextViewModel = BootUnlockViewModel;
|
||||
}
|
||||
|
||||
internal void SwitchToRestoreBoot()
|
||||
{
|
||||
ContextViewModel = BootRestoreViewModel;
|
||||
}
|
||||
|
||||
internal void SwitchToUnlockRoot()
|
||||
{
|
||||
ContextViewModel = RootUnlockViewModel;
|
||||
}
|
||||
|
||||
internal void SwitchToUndoRoot()
|
||||
{
|
||||
ContextViewModel = RootRestoreViewModel;
|
||||
}
|
||||
|
||||
internal void SwitchToBackup()
|
||||
{
|
||||
ContextViewModel = BackupViewModel;
|
||||
}
|
||||
|
||||
internal void SwitchToFlashRom()
|
||||
{
|
||||
ContextViewModel = LumiaFlashRomViewModel;
|
||||
}
|
||||
|
||||
internal void SwitchToDumpFFU()
|
||||
{
|
||||
ContextViewModel = DumpRomViewModel;
|
||||
}
|
||||
|
||||
internal void SwitchToDownload()
|
||||
{
|
||||
ContextViewModel = DownloadsViewModel;
|
||||
}
|
||||
|
||||
internal void SwitchToDifferentInterface(PhoneInterfaces TargetInterface)
|
||||
{
|
||||
ModeViewModel.OnModeSwitchRequested(TargetInterface);
|
||||
ContextViewModel = ModeViewModel;
|
||||
}
|
||||
|
||||
void PhoneNotifier_DeviceRemoved()
|
||||
{
|
||||
InfoViewModel.ActivateSubContext(null);
|
||||
}
|
||||
|
||||
void PhoneNotifier_NewDeviceArrived(ArrivalEventArgs Args)
|
||||
{
|
||||
PhoneInterfaces? PreviousInterface = LastInterface;
|
||||
LastInterface = Args.NewInterface;
|
||||
|
||||
if (App.InterruptBoot && (Args.NewInterface == PhoneInterfaces.Lumia_Bootloader))
|
||||
{
|
||||
App.InterruptBoot = false;
|
||||
LogFile.Log("Found Lumia BootMgr and user forced to interrupt the boot process. Force to Flash-mode.");
|
||||
Task.Run(() => SwitchModeViewModel.SwitchTo(PhoneNotifier, PhoneInterfaces.Lumia_Flash));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Args.NewInterface != PhoneInterfaces.Qualcomm_Download)
|
||||
App.InterruptBoot = false;
|
||||
|
||||
if (ContextViewModel == null)
|
||||
ContextViewModel = InfoViewModel;
|
||||
else if (ContextViewModel.IsFlashModeOperation)
|
||||
{
|
||||
if ((!ContextViewModel.IsSwitchingInterface) && (Args.NewInterface == PhoneInterfaces.Lumia_Bootloader))
|
||||
{
|
||||
// The current screen is marked as "Flash operation".
|
||||
// When the bootloader is detected at this stage, it means a phone is booting and
|
||||
// it is possible that the phone is in a non-booting stage (not possible to boot past UEFI).
|
||||
// We will try to boot straight to Flash-mode, so that it will be possible to flash a new ROM.
|
||||
LogFile.Log("Found Lumia BootMgr while mode is not being switched. Screen is marked as Flash Operation. Force to Flash-mode.");
|
||||
Task.Run(() => SwitchModeViewModel.SwitchTo(PhoneNotifier, PhoneInterfaces.Lumia_Flash));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((!ContextViewModel.IsSwitchingInterface) && (Args.NewInterface != PhoneInterfaces.Lumia_Bootloader))
|
||||
ContextViewModel = InfoViewModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ICommand _InfoCommand = null;
|
||||
public ICommand InfoCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_InfoCommand == null)
|
||||
{
|
||||
_InfoCommand = new DelegateCommand(() =>
|
||||
{
|
||||
ContextViewModel = InfoViewModel;
|
||||
});
|
||||
}
|
||||
return _InfoCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private ICommand _ModeCommand = null;
|
||||
public ICommand ModeCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_ModeCommand == null)
|
||||
{
|
||||
_ModeCommand = new DelegateCommand(() =>
|
||||
{
|
||||
ContextViewModel = ModeViewModel;
|
||||
});
|
||||
}
|
||||
return _ModeCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private ICommand _BootUnlockCommand = null;
|
||||
public ICommand BootUnlockCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_BootUnlockCommand == null)
|
||||
{
|
||||
_BootUnlockCommand = new DelegateCommand(() =>
|
||||
{
|
||||
ContextViewModel = BootUnlockViewModel;
|
||||
});
|
||||
}
|
||||
return _BootUnlockCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private ICommand _BootRestoreCommand = null;
|
||||
public ICommand BootRestoreCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_BootRestoreCommand == null)
|
||||
{
|
||||
_BootRestoreCommand = new DelegateCommand(() =>
|
||||
{
|
||||
ContextViewModel = BootRestoreViewModel;
|
||||
});
|
||||
}
|
||||
return _BootRestoreCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private ICommand _RootUnlockCommand = null;
|
||||
public ICommand RootUnlockCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_RootUnlockCommand == null)
|
||||
{
|
||||
_RootUnlockCommand = new DelegateCommand(() =>
|
||||
{
|
||||
ContextViewModel = RootUnlockViewModel;
|
||||
});
|
||||
}
|
||||
return _RootUnlockCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private ICommand _RootUndoCommand = null;
|
||||
public ICommand RootUndoCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_RootUndoCommand == null)
|
||||
{
|
||||
_RootUndoCommand = new DelegateCommand(() =>
|
||||
{
|
||||
ContextViewModel = RootRestoreViewModel;
|
||||
});
|
||||
}
|
||||
return _RootUndoCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private ICommand _BackupCommand = null;
|
||||
public ICommand BackupCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_BackupCommand == null)
|
||||
{
|
||||
_BackupCommand = new DelegateCommand(() =>
|
||||
{
|
||||
ContextViewModel = BackupViewModel;
|
||||
});
|
||||
}
|
||||
return _BackupCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private ICommand _RestoreCommand = null;
|
||||
public ICommand RestoreCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_RestoreCommand == null)
|
||||
{
|
||||
_RestoreCommand = new DelegateCommand(() =>
|
||||
{
|
||||
ContextViewModel = RestoreViewModel;
|
||||
});
|
||||
}
|
||||
return _RestoreCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private ICommand _LumiaFlashRomCommand = null;
|
||||
public ICommand LumiaFlashRomCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_LumiaFlashRomCommand == null)
|
||||
{
|
||||
_LumiaFlashRomCommand = new DelegateCommand(() =>
|
||||
{
|
||||
ContextViewModel = LumiaFlashRomViewModel;
|
||||
});
|
||||
}
|
||||
return _LumiaFlashRomCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private ICommand _DumpRomCommand = null;
|
||||
public ICommand DumpRomCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_DumpRomCommand == null)
|
||||
{
|
||||
_DumpRomCommand = new DelegateCommand(() =>
|
||||
{
|
||||
ContextViewModel = DumpRomViewModel;
|
||||
});
|
||||
}
|
||||
return _DumpRomCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private ICommand _AboutCommand = null;
|
||||
public ICommand AboutCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_AboutCommand == null)
|
||||
{
|
||||
_AboutCommand = new DelegateCommand(() =>
|
||||
{
|
||||
ContextViewModel = new AboutViewModel();
|
||||
});
|
||||
}
|
||||
return _AboutCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private ICommand _OpenWebSiteCommand = null;
|
||||
public ICommand OpenWebSiteCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_OpenWebSiteCommand == null)
|
||||
{
|
||||
_OpenWebSiteCommand = new DelegateCommand(() =>
|
||||
{
|
||||
Process.Start("www.wpinternals.net");
|
||||
});
|
||||
}
|
||||
return _OpenWebSiteCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private ICommand _DonateCommand = null;
|
||||
public ICommand DonateCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_DonateCommand == null)
|
||||
{
|
||||
_DonateCommand = new DelegateCommand(() =>
|
||||
{
|
||||
Process.Start("https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=VY8N7BCBT9CS4");
|
||||
});
|
||||
}
|
||||
return _DonateCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private ICommand _GettingStartedCommand = null;
|
||||
public ICommand GettingStartedCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_GettingStartedCommand == null)
|
||||
{
|
||||
_GettingStartedCommand = new DelegateCommand(() =>
|
||||
{
|
||||
ContextViewModel = _GettingStartedViewModel;
|
||||
});
|
||||
}
|
||||
return _GettingStartedCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private ICommand _DownloadCommand = null;
|
||||
public ICommand DownloadCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_DownloadCommand == null)
|
||||
{
|
||||
_DownloadCommand = new DelegateCommand(() =>
|
||||
{
|
||||
ContextViewModel = DownloadsViewModel;
|
||||
});
|
||||
}
|
||||
return _DownloadCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private bool _IsMenuEnabled = false;
|
||||
public bool IsMenuEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IsMenuEnabled;
|
||||
}
|
||||
set
|
||||
{
|
||||
_IsMenuEnabled = value;
|
||||
OnPropertyChanged("IsMenuEnabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// 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;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class MessageViewModel: ContextViewModel
|
||||
{
|
||||
internal MessageViewModel(string Message, Action OkAction = null, Action CancelAction = null)
|
||||
: base()
|
||||
{
|
||||
LogFile.Log(Message);
|
||||
|
||||
if (OkAction != null)
|
||||
this.OkCommand = new DelegateCommand(OkAction);
|
||||
if (CancelAction != null)
|
||||
this.CancelCommand = new DelegateCommand(CancelAction);
|
||||
this.Message = Message;
|
||||
}
|
||||
|
||||
private string _Message = null;
|
||||
public string Message
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Message;
|
||||
}
|
||||
set
|
||||
{
|
||||
_Message = value;
|
||||
OnPropertyChanged("Message");
|
||||
}
|
||||
}
|
||||
|
||||
private string _SubMessage = null;
|
||||
public string SubMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return _SubMessage;
|
||||
}
|
||||
set
|
||||
{
|
||||
_SubMessage = value;
|
||||
OnPropertyChanged("SubMessage");
|
||||
}
|
||||
}
|
||||
|
||||
private DelegateCommand _OkCommand = null;
|
||||
public DelegateCommand OkCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
return _OkCommand;
|
||||
}
|
||||
private set
|
||||
{
|
||||
_OkCommand = value;
|
||||
}
|
||||
}
|
||||
|
||||
private DelegateCommand _CancelCommand = null;
|
||||
public DelegateCommand CancelCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
return _CancelCommand;
|
||||
}
|
||||
private set
|
||||
{
|
||||
_CancelCommand = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,440 @@
|
||||
// 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.Threading;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
// Create this class on the UI thread, after the main-window of the application is initialized.
|
||||
// It is necessary to create the object on the UI thread, because notification events to the View need to be fired on that thread.
|
||||
// The Model for this ViewModel communicates over USB and for that it uses the hWnd of the main window.
|
||||
// Therefore the main window must be created before the ViewModel is created.
|
||||
|
||||
internal class NokiaFlashViewModel : ContextViewModel
|
||||
{
|
||||
private NokiaFlashModel CurrentModel;
|
||||
private Action<PhoneInterfaces> RequestModeSwitch;
|
||||
internal Action SwitchToGettingStarted;
|
||||
private object LockDeviceInfo = new object();
|
||||
bool DeviceInfoLoaded = false;
|
||||
|
||||
internal NokiaFlashViewModel(NokiaPhoneModel CurrentModel, Action<PhoneInterfaces> RequestModeSwitch, Action SwitchToGettingStarted)
|
||||
: base()
|
||||
{
|
||||
this.CurrentModel = (NokiaFlashModel)CurrentModel;
|
||||
this.RequestModeSwitch = RequestModeSwitch;
|
||||
this.SwitchToGettingStarted = SwitchToGettingStarted;
|
||||
}
|
||||
|
||||
// Device info should be loaded only one time and only when the ViewModel is active
|
||||
internal override void EvaluateViewState()
|
||||
{
|
||||
if (IsActive)
|
||||
new Thread(() => StartLoadDeviceInfo()).Start();
|
||||
}
|
||||
|
||||
private void StartLoadDeviceInfo()
|
||||
{
|
||||
lock (LockDeviceInfo)
|
||||
{
|
||||
if (!DeviceInfoLoaded)
|
||||
{
|
||||
try
|
||||
{
|
||||
//byte[] Imsi = CurrentModel.ExecuteJsonMethodAsBytes("ReadImsi", "Imsi"); // 9 bytes: 08 29 40 40 ...
|
||||
//string BatteryLevel = CurrentModel.ExecuteJsonMethodAsString("ReadBatteryLevel", "BatteryLevel");
|
||||
//string SystemAsicVersion = CurrentModel.ExecuteJsonMethodAsString("ReadSystemAsicVersion", "SystemAsicVersion"); // 8960 -> Chip SOC version
|
||||
//string OperatorName = CurrentModel.ExecuteJsonMethodAsString("ReadOperatorName", "OperatorName"); // 000-DK
|
||||
//string ManufacturerModelName = CurrentModel.ExecuteJsonMethodAsString("ReadManufacturerModelName", "ManufacturerModelName"); // RM-821_eu_denmark_251
|
||||
//string AkVersion = CurrentModel.ExecuteJsonMethodAsString("ReadAkVersion", "AkVersion"); // 9200.10521
|
||||
//string BspVersion = CurrentModel.ExecuteJsonMethodAsString("ReadBspVersion", "BspVersion"); // 3051.40000
|
||||
//string ProductCode = CurrentModel.ExecuteJsonMethodAsString("ReadProductCode", "ProductCode"); // 059Q9D7
|
||||
//string SecurityMode = CurrentModel.ExecuteJsonMethodAsString("GetSecurityMode", "SecMode"); // Restricted
|
||||
//string SerialNumber = CurrentModel.ExecuteJsonMethodAsString("ReadSerialNumber", "SerialNumber"); // 356355051883955 = IMEI
|
||||
//string SwVersion = CurrentModel.ExecuteJsonMethodAsString("ReadSwVersion", "SwVersion"); // 3051.40000.1349.0007
|
||||
//string ModuleCode = CurrentModel.ExecuteJsonMethodAsString("ReadModuleCode", "ModuleCode"); // 0205137
|
||||
//byte[] PublicId = CurrentModel.ExecuteJsonMethodAsBytes("ReadPublicId", "PublicId"); // 0x14 bytes: a5 e5 ...
|
||||
//string Psn = CurrentModel.ExecuteJsonMethodAsString("ReadPsn", "Psn"); // CEP737370
|
||||
//string HwVersion = CurrentModel.ExecuteJsonMethodAsString("ReadHwVersion", "HWVersion"); // 6504 = 6.5.0.4
|
||||
//byte[] BtId = CurrentModel.ExecuteJsonMethodAsBytes("ReadBtId", "BtId"); // 6 bytes: bc c6 ...
|
||||
//byte[] WlanMacAddress1 = CurrentModel.ExecuteJsonMethodAsBytes("ReadWlanMacAddress", "WlanMacAddress1"); // 6 bytes
|
||||
//byte[] WlanMacAddress2 = CurrentModel.ExecuteJsonMethodAsBytes("ReadWlanMacAddress", "WlanMacAddress2"); // same
|
||||
//byte[] WlanMacAddress3 = CurrentModel.ExecuteJsonMethodAsBytes("ReadWlanMacAddress", "WlanMacAddress3"); // same
|
||||
//bool SimlockActive = CurrentModel.ExecuteJsonMethodAsBoolean("ReadSimlockActive", "SimLockActive"); // false
|
||||
//string ServiceTag = CurrentModel.ExecuteJsonMethodAsString("ReadServiceTag", "ServiceTag"); // error
|
||||
//byte[] RfChipsetVersion = CurrentModel.ExecuteJsonMethodAsBytes("ReadRfChipsetVersion", "RfChipsetVersion"); // error
|
||||
//byte[] Meid = CurrentModel.ExecuteJsonMethodAsBytes("ReadMeid", "Meid"); // error
|
||||
//string Test = CurrentModel.ExecuteJsonMethodAsString("ReadManufacturingData", ""); -> This method is only possible in Label-mode.
|
||||
|
||||
UefiSecurityStatusResponse SecurityStatus = CurrentModel.ReadSecurityStatus();
|
||||
|
||||
UInt32? FlagsResult = CurrentModel.ReadSecurityFlags();
|
||||
UInt32 SecurityFlags = 0;
|
||||
if (FlagsResult != null)
|
||||
{
|
||||
SecurityFlags = (UInt32)CurrentModel.ReadSecurityFlags();
|
||||
LogFile.Log("Security flags: 0x" + SecurityFlags.ToString("X8"));
|
||||
}
|
||||
else
|
||||
LogFile.Log("Security flags could not be read");
|
||||
|
||||
PlatformName = CurrentModel.ReadStringParam("DPI");
|
||||
LogFile.Log("Platform Name: " + PlatformName);
|
||||
|
||||
// Some phones do not support the Terminal interface! (928 verizon)
|
||||
// Instead read param RRKH to get the RKH.
|
||||
PublicID = null;
|
||||
byte[] RawPublicID = CurrentModel.ReadParam("PID");
|
||||
if ((RawPublicID != null) && (RawPublicID.Length > 4))
|
||||
{
|
||||
PublicID = new byte[RawPublicID.Length - 4];
|
||||
Array.Copy(RawPublicID, 4, PublicID, 0, RawPublicID.Length - 4);
|
||||
LogFile.Log("Public ID: " + Converter.ConvertHexToString(PublicID, " "));
|
||||
}
|
||||
RootKeyHash = CurrentModel.ReadParam("RRKH");
|
||||
if (RootKeyHash != null)
|
||||
LogFile.Log("Root Key Hash: " + Converter.ConvertHexToString(RootKeyHash, " "));
|
||||
|
||||
if (SecurityStatus != null)
|
||||
{
|
||||
PlatformSecureBootStatus = SecurityStatus.PlatformSecureBootStatus;
|
||||
LogFile.Log("Platform Secure Boot Status: " + PlatformSecureBootStatus.ToString());
|
||||
UefiSecureBootStatus = SecurityStatus.UefiSecureBootStatus;
|
||||
LogFile.Log("Uefi Secure Boot Status: " + UefiSecureBootStatus.ToString());
|
||||
EffectiveSecureBootStatus = SecurityStatus.PlatformSecureBootStatus && SecurityStatus.UefiSecureBootStatus;
|
||||
LogFile.Log("Effective Secure Boot Status: " + EffectiveSecureBootStatus.ToString());
|
||||
|
||||
BootloaderSecurityQfuseStatus = SecurityStatus.SecureFfuEfuseStatus;
|
||||
LogFile.Log("Bootloader Security Qfuse Status: " + BootloaderSecurityQfuseStatus.ToString());
|
||||
BootloaderSecurityAuthenticationStatus = SecurityStatus.AuthenticationStatus;
|
||||
LogFile.Log("Bootloader Security Authentication Status: " + BootloaderSecurityAuthenticationStatus.ToString());
|
||||
BootloaderSecurityRdcStatus = SecurityStatus.RdcStatus;
|
||||
LogFile.Log("Bootloader Security Rdc Status: " + BootloaderSecurityRdcStatus.ToString());
|
||||
EffectiveBootloaderSecurityStatus = SecurityStatus.SecureFfuEfuseStatus && !SecurityStatus.AuthenticationStatus && !SecurityStatus.RdcStatus;
|
||||
LogFile.Log("Effective Bootloader Security Status: " + EffectiveBootloaderSecurityStatus.ToString());
|
||||
|
||||
NativeDebugStatus = !SecurityStatus.DebugStatus;
|
||||
LogFile.Log("Native Debug Status: " + NativeDebugStatus.ToString());
|
||||
}
|
||||
|
||||
byte[] CID = CurrentModel.ReadParam("CID");
|
||||
byte[] EMS = CurrentModel.ReadParam("EMS");
|
||||
UInt16 MID = (UInt16)(((UInt16)CID[0] << 8) + CID[1]);
|
||||
UInt64 MemSize = (UInt64)(((UInt32)EMS[0] << 24) + ((UInt32)EMS[1] << 16) + ((UInt32)EMS[2] << 8) + EMS[3]) * 0x200;
|
||||
double MemSizeDouble = (double)MemSize / 1024 / 1024 / 1024;
|
||||
MemSizeDouble = (double)(int)(MemSizeDouble * 10) / 10;
|
||||
string Manufacturer = null;
|
||||
switch (MID)
|
||||
{
|
||||
case 0x0002:
|
||||
case 0x0045:
|
||||
Manufacturer = "SanDisk";
|
||||
break;
|
||||
case 0x0011:
|
||||
Manufacturer = "Toshiba";
|
||||
break;
|
||||
case 0x0013:
|
||||
Manufacturer = "Micron";
|
||||
break;
|
||||
case 0x0015:
|
||||
Manufacturer = "Samsung";
|
||||
break;
|
||||
case 0x0090:
|
||||
Manufacturer = "Hynix";
|
||||
break;
|
||||
case 0x0070:
|
||||
Manufacturer = "Kingston";
|
||||
break;
|
||||
case 0x00EC:
|
||||
Manufacturer = "GigaDevice";
|
||||
break;
|
||||
}
|
||||
if (Manufacturer == null)
|
||||
eMMC = MemSizeDouble.ToString() + " GB";
|
||||
else
|
||||
eMMC = Manufacturer + " " + MemSizeDouble.ToString() + " GB";
|
||||
SamsungWarningVisible = (MID == 0x0015);
|
||||
|
||||
PhoneInfo Info = CurrentModel.ReadPhoneInfo(true);
|
||||
if (Info.FlashAppProtocolVersionMajor < 2)
|
||||
BootloaderDescription = "Lumia Bootloader Spec A";
|
||||
else
|
||||
BootloaderDescription = "Lumia Bootloader Spec B";
|
||||
LogFile.Log("Bootloader: " + BootloaderDescription);
|
||||
|
||||
ProductCode = Info.ProductCode;
|
||||
LogFile.Log("ProductCode: " + ProductCode);
|
||||
|
||||
ProductType = Info.Type;
|
||||
LogFile.Log("ProductType: " + ProductType);
|
||||
}
|
||||
catch
|
||||
{
|
||||
LogFile.Log("Reading status from Flash interface was aborted.");
|
||||
}
|
||||
DeviceInfoLoaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] _PublicID = null;
|
||||
public byte[] PublicID
|
||||
{
|
||||
get
|
||||
{
|
||||
return _PublicID;
|
||||
}
|
||||
set
|
||||
{
|
||||
_PublicID = value;
|
||||
OnPropertyChanged("PublicID");
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] _RootKeyHash = null;
|
||||
public byte[] RootKeyHash
|
||||
{
|
||||
get
|
||||
{
|
||||
return _RootKeyHash;
|
||||
}
|
||||
set
|
||||
{
|
||||
_RootKeyHash = value;
|
||||
OnPropertyChanged("RootKeyHash");
|
||||
}
|
||||
}
|
||||
|
||||
private string _PlatformName = null;
|
||||
public string PlatformName
|
||||
{
|
||||
get
|
||||
{
|
||||
return _PlatformName;
|
||||
}
|
||||
set
|
||||
{
|
||||
_PlatformName = value;
|
||||
OnPropertyChanged("PlatformName");
|
||||
}
|
||||
}
|
||||
|
||||
private string _ProductType = null;
|
||||
public string ProductType
|
||||
{
|
||||
get
|
||||
{
|
||||
return _ProductType;
|
||||
}
|
||||
set
|
||||
{
|
||||
_ProductType = value;
|
||||
OnPropertyChanged("ProductType");
|
||||
}
|
||||
}
|
||||
|
||||
private string _ProductCode = null;
|
||||
public string ProductCode
|
||||
{
|
||||
get
|
||||
{
|
||||
return _ProductCode;
|
||||
}
|
||||
set
|
||||
{
|
||||
_ProductCode = value;
|
||||
OnPropertyChanged("ProductCode");
|
||||
}
|
||||
}
|
||||
|
||||
private string _eMMC = null;
|
||||
public string eMMC
|
||||
{
|
||||
get
|
||||
{
|
||||
return _eMMC;
|
||||
}
|
||||
set
|
||||
{
|
||||
_eMMC = value;
|
||||
OnPropertyChanged("eMMC");
|
||||
}
|
||||
}
|
||||
|
||||
private string _BootloaderDescription = null;
|
||||
public string BootloaderDescription
|
||||
{
|
||||
get
|
||||
{
|
||||
return _BootloaderDescription;
|
||||
}
|
||||
set
|
||||
{
|
||||
_BootloaderDescription = value;
|
||||
OnPropertyChanged("BootloaderDescription");
|
||||
}
|
||||
}
|
||||
|
||||
private bool _SamsungWarningVisible = false;
|
||||
public bool SamsungWarningVisible
|
||||
{
|
||||
get
|
||||
{
|
||||
return _SamsungWarningVisible;
|
||||
}
|
||||
set
|
||||
{
|
||||
_SamsungWarningVisible = value;
|
||||
OnPropertyChanged("SamsungWarningVisible");
|
||||
}
|
||||
}
|
||||
|
||||
private bool? _PlatformSecureBootStatus = null;
|
||||
public bool? PlatformSecureBootStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
return _PlatformSecureBootStatus;
|
||||
}
|
||||
set
|
||||
{
|
||||
_PlatformSecureBootStatus = value;
|
||||
OnPropertyChanged("PlatformSecureBootStatus");
|
||||
}
|
||||
}
|
||||
|
||||
private bool? _BootloaderSecurityQfuseStatus = null;
|
||||
public bool? BootloaderSecurityQfuseStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
return _BootloaderSecurityQfuseStatus;
|
||||
}
|
||||
set
|
||||
{
|
||||
_BootloaderSecurityQfuseStatus = value;
|
||||
OnPropertyChanged("BootloaderSecurityQfuseStatus");
|
||||
}
|
||||
}
|
||||
|
||||
private bool? _BootloaderSecurityRdcStatus = null;
|
||||
public bool? BootloaderSecurityRdcStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
return _BootloaderSecurityRdcStatus;
|
||||
}
|
||||
set
|
||||
{
|
||||
_BootloaderSecurityRdcStatus = value;
|
||||
OnPropertyChanged("BootloaderSecurityRdcStatus");
|
||||
}
|
||||
}
|
||||
|
||||
private bool? _BootloaderSecurityAuthenticationStatus = null;
|
||||
public bool? BootloaderSecurityAuthenticationStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
return _BootloaderSecurityAuthenticationStatus;
|
||||
}
|
||||
set
|
||||
{
|
||||
_BootloaderSecurityAuthenticationStatus = value;
|
||||
OnPropertyChanged("BootloaderSecurityAuthenticationStatus");
|
||||
}
|
||||
}
|
||||
|
||||
private bool? _UefiSecureBootStatus = null;
|
||||
public bool? UefiSecureBootStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
return _UefiSecureBootStatus;
|
||||
}
|
||||
set
|
||||
{
|
||||
_UefiSecureBootStatus = value;
|
||||
OnPropertyChanged("UefiSecureBootStatus");
|
||||
}
|
||||
}
|
||||
|
||||
private bool? _EffectiveSecureBootStatus = null;
|
||||
public bool? EffectiveSecureBootStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
return _EffectiveSecureBootStatus;
|
||||
}
|
||||
set
|
||||
{
|
||||
_EffectiveSecureBootStatus = value;
|
||||
OnPropertyChanged("EffectiveSecureBootStatus");
|
||||
}
|
||||
}
|
||||
|
||||
private bool? _EffectiveBootloaderSecurityStatus = null;
|
||||
public bool? EffectiveBootloaderSecurityStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
return _EffectiveBootloaderSecurityStatus;
|
||||
}
|
||||
set
|
||||
{
|
||||
_EffectiveBootloaderSecurityStatus = value;
|
||||
OnPropertyChanged("EffectiveBootloaderSecurityStatus");
|
||||
}
|
||||
}
|
||||
|
||||
private bool? _NativeDebugStatus = null;
|
||||
public bool? NativeDebugStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
return _NativeDebugStatus;
|
||||
}
|
||||
set
|
||||
{
|
||||
_NativeDebugStatus = value;
|
||||
OnPropertyChanged("NativeDebugStatus");
|
||||
}
|
||||
}
|
||||
|
||||
internal void RebootTo(string Mode)
|
||||
{
|
||||
switch (Mode)
|
||||
{
|
||||
case "Normal":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_Normal);
|
||||
break;
|
||||
case "Label":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_Label);
|
||||
break;
|
||||
case "MassStorage":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_MassStorage);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
// Create this class on the UI thread, after the main-window of the application is initialized.
|
||||
// It is necessary to create the object on the UI thread, because notification events to the View need to be fired on that thread.
|
||||
// The Model for this ViewModel communicates over USB and for that it uses the hWnd of the main window.
|
||||
// Therefore the main window must be created before the ViewModel is created.
|
||||
|
||||
internal class NokiaLabelViewModel : ContextViewModel
|
||||
{
|
||||
private NokiaPhoneModel CurrentModel;
|
||||
private Action<PhoneInterfaces> RequestModeSwitch;
|
||||
|
||||
internal NokiaLabelViewModel(NokiaPhoneModel CurrentModel, Action<PhoneInterfaces> RequestModeSwitch)
|
||||
: base()
|
||||
{
|
||||
this.RequestModeSwitch = RequestModeSwitch;
|
||||
|
||||
this.CurrentModel = CurrentModel;
|
||||
|
||||
new Thread(() => StartLoadDeviceInfo()).Start();
|
||||
}
|
||||
|
||||
private void StartLoadDeviceInfo()
|
||||
{
|
||||
//byte[] Imsi = CurrentModel.ExecuteJsonMethodAsBytes("ReadImsi", "Imsi"); // 9 bytes: 08 29 40 40 ...
|
||||
//string BatteryLevel = CurrentModel.ExecuteJsonMethodAsString("ReadBatteryLevel", "BatteryLevel");
|
||||
//string SystemAsicVersion = CurrentModel.ExecuteJsonMethodAsString("ReadSystemAsicVersion", "SystemAsicVersion"); // 8960 -> Chip SOC version
|
||||
//string OperatorName = CurrentModel.ExecuteJsonMethodAsString("ReadOperatorName", "OperatorName"); // 000-DK
|
||||
//string ManufacturerModelName = CurrentModel.ExecuteJsonMethodAsString("ReadManufacturerModelName", "ManufacturerModelName"); // RM-821_eu_denmark_251
|
||||
//string AkVersion = CurrentModel.ExecuteJsonMethodAsString("ReadAkVersion", "AkVersion"); // 9200.10521
|
||||
//string BspVersion = CurrentModel.ExecuteJsonMethodAsString("ReadBspVersion", "BspVersion"); // 3051.40000
|
||||
//string ProductCode = CurrentModel.ExecuteJsonMethodAsString("ReadProductCode", "ProductCode"); // 059Q9D7
|
||||
//string SecurityMode = CurrentModel.ExecuteJsonMethodAsString("GetSecurityMode", "SecMode"); // Restricted
|
||||
//string SerialNumber = CurrentModel.ExecuteJsonMethodAsString("ReadSerialNumber", "SerialNumber"); // 356355051883955 = IMEI
|
||||
//string SwVersion = CurrentModel.ExecuteJsonMethodAsString("ReadSwVersion", "SwVersion"); // 3051.40000.1349.0007
|
||||
//string ModuleCode = CurrentModel.ExecuteJsonMethodAsString("ReadModuleCode", "ModuleCode"); // 0205137
|
||||
//byte[] PublicId = CurrentModel.ExecuteJsonMethodAsBytes("ReadPublicId", "PublicId"); // 0x14 bytes: a5 e5 ...
|
||||
//string Psn = CurrentModel.ExecuteJsonMethodAsString("ReadPsn", "Psn"); // CEP737370
|
||||
//string HwVersion = CurrentModel.ExecuteJsonMethodAsString("ReadHwVersion", "HWVersion"); // 6504 = 6.5.0.4
|
||||
//byte[] BtId = CurrentModel.ExecuteJsonMethodAsBytes("ReadBtId", "BtId"); // 6 bytes: bc c6 ...
|
||||
//byte[] WlanMacAddress1 = CurrentModel.ExecuteJsonMethodAsBytes("ReadWlanMacAddress", "WlanMacAddress1"); // 6 bytes
|
||||
//byte[] WlanMacAddress2 = CurrentModel.ExecuteJsonMethodAsBytes("ReadWlanMacAddress", "WlanMacAddress2"); // same
|
||||
//byte[] WlanMacAddress3 = CurrentModel.ExecuteJsonMethodAsBytes("ReadWlanMacAddress", "WlanMacAddress3"); // same
|
||||
//bool SimlockActive = CurrentModel.ExecuteJsonMethodAsBoolean("ReadSimlockActive", "SimLockActive"); // false
|
||||
//string Version = CurrentModel.ExecuteJsonMethodAsString("GetVersion", "HelloString"); // Resultvars: HelloString Version BuildDate BuildType
|
||||
//string ServiceTag = CurrentModel.ExecuteJsonMethodAsString("ReadServiceTag", "ServiceTag"); // error
|
||||
//byte[] RfChipsetVersion = CurrentModel.ExecuteJsonMethodAsBytes("ReadRfChipsetVersion", "RfChipsetVersion"); // error
|
||||
//byte[] Meid = CurrentModel.ExecuteJsonMethodAsBytes("ReadMeid", "Meid"); // error
|
||||
//string Test = CurrentModel.ExecuteJsonMethodAsString("ReadManufacturingData", ""); -> This method is only possible in Label-mode.
|
||||
|
||||
byte[] AsskMask = new byte[0x10] { 1, 0, 16, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64 };
|
||||
byte[] Challenge = new byte[0x88];
|
||||
Dictionary<string, object> Params = new Dictionary<string, object>();
|
||||
Params.Add("AsskMask", AsskMask);
|
||||
Params.Add("Challenge", Challenge);
|
||||
Params.Add("AsicIndex", 0);
|
||||
byte[] TerminalResponseBytes = CurrentModel.ExecuteJsonMethodAsBytes("TerminalChallenge", Params, "TerminalResponse");
|
||||
TerminalResponse TerminalResponse = Terminal.Parse(TerminalResponseBytes, 0);
|
||||
if (TerminalResponse != null)
|
||||
{
|
||||
PublicID = TerminalResponse.PublicId;
|
||||
LogFile.Log("Public ID: " + Converter.ConvertHexToString(PublicID, " "));
|
||||
RootKeyHash = TerminalResponse.RootKeyHash;
|
||||
LogFile.Log("RootKeyHash: " + Converter.ConvertHexToString(RootKeyHash, " "));
|
||||
}
|
||||
|
||||
ManufacturerModelName = CurrentModel.ExecuteJsonMethodAsString("ReadManufacturerModelName", "ManufacturerModelName"); // RM-821_eu_denmark_251
|
||||
LogFile.Log("Manufacturer Model Name: " + ManufacturerModelName);
|
||||
ProductCode = CurrentModel.ExecuteJsonMethodAsString("ReadProductCode", "ProductCode"); // 059Q9D7
|
||||
LogFile.Log("Product Code: " + ProductCode);
|
||||
Firmware = CurrentModel.ExecuteJsonMethodAsString("ReadSwVersion", "SwVersion"); // 3051.40000.1349.0007
|
||||
LogFile.Log("Firmware: " + Firmware);
|
||||
|
||||
IMEI = CurrentModel.ExecuteJsonMethodAsString("ReadSerialNumber", "SerialNumber"); // IMEI
|
||||
LogFile.Log("IMEI: " + IMEI);
|
||||
BluetoothMac = CurrentModel.ExecuteJsonMethodAsBytes("ReadBtId", "BtId"); // 6 bytes: bc c6 ...
|
||||
LogFile.Log("Bluetooth MAC: " + Converter.ConvertHexToString(BluetoothMac, " "));
|
||||
WlanMac = CurrentModel.ExecuteJsonMethodAsBytes("ReadWlanMacAddress", "WlanMacAddress1"); // 6 bytes
|
||||
LogFile.Log("WLAN MAC: " + Converter.ConvertHexToString(WlanMac, " "));
|
||||
|
||||
IsBootloaderSecurityEnabled = (bool)CurrentModel.ExecuteJsonMethodAsBoolean("ReadProductionDoneState", "ProductionDone");
|
||||
LogFile.Log("Bootloader Security: " + ((bool)IsBootloaderSecurityEnabled ? "Enabled" : "Disabled"));
|
||||
|
||||
Params = new Dictionary<string, object>();
|
||||
Params.Add("ID", 3534);
|
||||
Params.Add("NVData", new byte[] { 0 });
|
||||
CurrentModel.ExecuteJsonMethod("WriteNVData", Params); // Error: 150
|
||||
|
||||
Params = new Dictionary<string, object>();
|
||||
Params.Add("ID", 3534);
|
||||
byte[] NV3534 = CurrentModel.ExecuteJsonMethodAsBytes("ReadNVData", Params, "NVData"); // Error: value not written
|
||||
}
|
||||
|
||||
private string _ProductCode = null;
|
||||
public string ProductCode
|
||||
{
|
||||
get
|
||||
{
|
||||
return _ProductCode;
|
||||
}
|
||||
set
|
||||
{
|
||||
_ProductCode = value;
|
||||
OnPropertyChanged("ProductCode");
|
||||
}
|
||||
}
|
||||
|
||||
private string _ManufacturerModelName = null;
|
||||
public string ManufacturerModelName
|
||||
{
|
||||
get
|
||||
{
|
||||
return _ManufacturerModelName;
|
||||
}
|
||||
set
|
||||
{
|
||||
_ManufacturerModelName = value;
|
||||
OnPropertyChanged("ManufacturerModelName");
|
||||
}
|
||||
}
|
||||
|
||||
private string _Operator = null;
|
||||
public string Operator
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Operator;
|
||||
}
|
||||
set
|
||||
{
|
||||
_Operator = value;
|
||||
OnPropertyChanged("Operator");
|
||||
}
|
||||
}
|
||||
|
||||
private string _Firmware = null;
|
||||
public string Firmware
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Firmware;
|
||||
}
|
||||
set
|
||||
{
|
||||
_Firmware = value;
|
||||
OnPropertyChanged("Firmware");
|
||||
}
|
||||
}
|
||||
|
||||
private string _IMEI = null;
|
||||
public string IMEI
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IMEI;
|
||||
}
|
||||
set
|
||||
{
|
||||
_IMEI = value;
|
||||
OnPropertyChanged("IMEI");
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] _PublicID = null;
|
||||
public byte[] PublicID
|
||||
{
|
||||
get
|
||||
{
|
||||
return _PublicID;
|
||||
}
|
||||
set
|
||||
{
|
||||
_PublicID = value;
|
||||
OnPropertyChanged("PublicID");
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] _RootKeyHash = null;
|
||||
public byte[] RootKeyHash
|
||||
{
|
||||
get
|
||||
{
|
||||
return _RootKeyHash;
|
||||
}
|
||||
set
|
||||
{
|
||||
_RootKeyHash = value;
|
||||
OnPropertyChanged("RootKeyHash");
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] _WlanMac = null;
|
||||
public byte[] WlanMac
|
||||
{
|
||||
get
|
||||
{
|
||||
return _WlanMac;
|
||||
}
|
||||
set
|
||||
{
|
||||
_WlanMac = value;
|
||||
OnPropertyChanged("WlanMac");
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] _BluetoothMac = null;
|
||||
public byte[] BluetoothMac
|
||||
{
|
||||
get
|
||||
{
|
||||
return _BluetoothMac;
|
||||
}
|
||||
set
|
||||
{
|
||||
_BluetoothMac = value;
|
||||
OnPropertyChanged("BluetoothMac");
|
||||
}
|
||||
}
|
||||
|
||||
private bool? _IsBootloaderSecurityEnabled = null;
|
||||
public bool? IsBootloaderSecurityEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IsBootloaderSecurityEnabled;
|
||||
}
|
||||
set
|
||||
{
|
||||
_IsBootloaderSecurityEnabled = value;
|
||||
OnPropertyChanged("IsBootloaderSecurityEnabled");
|
||||
}
|
||||
}
|
||||
|
||||
private bool? _IsSimLocked = null;
|
||||
public bool? IsSimLocked
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IsSimLocked;
|
||||
}
|
||||
set
|
||||
{
|
||||
_IsSimLocked = value;
|
||||
OnPropertyChanged("IsSimLocked");
|
||||
}
|
||||
}
|
||||
|
||||
public void RebootTo(string Mode)
|
||||
{
|
||||
switch (Mode)
|
||||
{
|
||||
case "Flash":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_Flash);
|
||||
break;
|
||||
case "Label":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_Label);
|
||||
break;
|
||||
case "MassStorage":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_MassStorage);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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.Threading;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class NokiaMassStorageViewModel: ContextViewModel
|
||||
{
|
||||
private MassStorage CurrentModel;
|
||||
|
||||
internal NokiaMassStorageViewModel(MassStorage CurrentModel)
|
||||
: base()
|
||||
{
|
||||
this.CurrentModel = CurrentModel;
|
||||
_Drive = CurrentModel.Drive;
|
||||
}
|
||||
|
||||
private string _Drive = null;
|
||||
public string Drive
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Drive;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class NokiaModeFlashViewModel : ContextViewModel
|
||||
{
|
||||
private NokiaFlashModel CurrentModel;
|
||||
Action<PhoneInterfaces?> RequestModeSwitch;
|
||||
private object LockDeviceInfo = new object();
|
||||
bool DeviceInfoLoaded = false;
|
||||
|
||||
internal NokiaModeFlashViewModel(NokiaPhoneModel CurrentModel, Action<PhoneInterfaces?> RequestModeSwitch)
|
||||
: base()
|
||||
{
|
||||
this.CurrentModel = (NokiaFlashModel)CurrentModel;
|
||||
this.RequestModeSwitch = RequestModeSwitch;
|
||||
}
|
||||
|
||||
internal override void EvaluateViewState()
|
||||
{
|
||||
if (IsActive)
|
||||
new Thread(() => StartLoadDeviceInfo()).Start();
|
||||
}
|
||||
|
||||
private bool? _EffectiveBootloaderSecurityStatus = null;
|
||||
public bool? EffectiveBootloaderSecurityStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
return _EffectiveBootloaderSecurityStatus;
|
||||
}
|
||||
set
|
||||
{
|
||||
_EffectiveBootloaderSecurityStatus = value;
|
||||
OnPropertyChanged("EffectiveBootloaderSecurityStatus");
|
||||
}
|
||||
}
|
||||
|
||||
internal void StartLoadDeviceInfo()
|
||||
{
|
||||
lock (LockDeviceInfo)
|
||||
{
|
||||
if (!DeviceInfoLoaded)
|
||||
{
|
||||
try
|
||||
{
|
||||
PhoneInfo Info = CurrentModel.ReadPhoneInfo();
|
||||
|
||||
if (Info.FlashAppProtocolVersionMajor < 2)
|
||||
{
|
||||
UefiSecurityStatusResponse SecurityStatus = CurrentModel.ReadSecurityStatus();
|
||||
|
||||
if (SecurityStatus != null)
|
||||
{
|
||||
EffectiveBootloaderSecurityStatus = SecurityStatus.SecureFfuEfuseStatus && !SecurityStatus.AuthenticationStatus && !SecurityStatus.RdcStatus;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EffectiveBootloaderSecurityStatus = Info.UefiSecureBootEnabled;
|
||||
}
|
||||
|
||||
LogFile.Log("Effective Bootloader Security Status: " + EffectiveBootloaderSecurityStatus.ToString());
|
||||
}
|
||||
catch
|
||||
{
|
||||
LogFile.Log("Reading status from Flash interface was aborted.");
|
||||
}
|
||||
DeviceInfoLoaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void RebootTo(string Mode)
|
||||
{
|
||||
switch (Mode)
|
||||
{
|
||||
case "Normal":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_Normal);
|
||||
break;
|
||||
case "Flash":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_Flash);
|
||||
break;
|
||||
case "Label":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_Label);
|
||||
break;
|
||||
case "MassStorage":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_MassStorage);
|
||||
break;
|
||||
case "Shutdown":
|
||||
RequestModeSwitch(null);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// 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;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class NokiaModeLabelViewModel : ContextViewModel
|
||||
{
|
||||
private NokiaPhoneModel CurrentModel;
|
||||
private Action<PhoneInterfaces?> RequestModeSwitch;
|
||||
|
||||
internal NokiaModeLabelViewModel(NokiaPhoneModel CurrentModel, Action<PhoneInterfaces?> RequestModeSwitch)
|
||||
: base()
|
||||
{
|
||||
this.CurrentModel = CurrentModel;
|
||||
this.RequestModeSwitch = RequestModeSwitch;
|
||||
}
|
||||
|
||||
public void RebootTo(string Mode)
|
||||
{
|
||||
switch (Mode)
|
||||
{
|
||||
case "Normal":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_Normal);
|
||||
break;
|
||||
case "Flash":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_Flash);
|
||||
break;
|
||||
case "Label":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_Label);
|
||||
break;
|
||||
case "MassStorage":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_MassStorage);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// 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.Collections.Generic;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class NokiaModeMassStorageViewModel : ContextViewModel
|
||||
{
|
||||
private NokiaPhoneModel CurrentModel;
|
||||
|
||||
internal NokiaModeMassStorageViewModel(NokiaPhoneModel CurrentModel)
|
||||
: base()
|
||||
{
|
||||
this.CurrentModel = CurrentModel;
|
||||
}
|
||||
|
||||
internal void RebootTo(string Mode)
|
||||
{
|
||||
string DeviceMode;
|
||||
|
||||
switch (Mode)
|
||||
{
|
||||
case "Flash":
|
||||
DeviceMode = "Flash";
|
||||
LogFile.Log("Reboot to Flash");
|
||||
break;
|
||||
case "Label":
|
||||
DeviceMode = "Test";
|
||||
LogFile.Log("Reboot to Label");
|
||||
break;
|
||||
case "MassStorage":
|
||||
DeviceMode = "Flash"; // TODO: implement folow-up
|
||||
LogFile.Log("Reboot to Mass Storage");
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<string, object> Params = new Dictionary<string, object>();
|
||||
Params.Add("DeviceMode", DeviceMode);
|
||||
Params.Add("ResetMethod", "HwReset");
|
||||
CurrentModel.ExecuteJsonMethodAsync("SetDeviceMode", Params);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// 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;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class NokiaModeNormalViewModel: ContextViewModel
|
||||
{
|
||||
private NokiaPhoneModel CurrentModel;
|
||||
private Action<PhoneInterfaces?> RequestModeSwitch;
|
||||
|
||||
internal NokiaModeNormalViewModel(NokiaPhoneModel CurrentModel, Action<PhoneInterfaces?> RequestModeSwitch)
|
||||
: base()
|
||||
{
|
||||
this.CurrentModel = CurrentModel;
|
||||
this.RequestModeSwitch = RequestModeSwitch;
|
||||
}
|
||||
|
||||
public void RebootTo(string Mode)
|
||||
{
|
||||
switch (Mode)
|
||||
{
|
||||
case "Flash":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_Flash);
|
||||
break;
|
||||
case "Label":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_Label);
|
||||
break;
|
||||
case "MassStorage":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_MassStorage);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
// 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.Threading;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
// Create this class on the UI thread, after the main-window of the application is initialized.
|
||||
// It is necessary to create the object on the UI thread, because notification events to the View need to be fired on that thread.
|
||||
// The Model for this ViewModel communicates over USB and for that it uses the hWnd of the main window.
|
||||
// Therefore the main window must be created before the ViewModel is created.
|
||||
|
||||
internal class NokiaNormalViewModel : ContextViewModel
|
||||
{
|
||||
private NokiaPhoneModel CurrentModel;
|
||||
private Action<PhoneInterfaces> RequestModeSwitch;
|
||||
|
||||
internal NokiaNormalViewModel(NokiaPhoneModel CurrentModel, Action<PhoneInterfaces> RequestModeSwitch)
|
||||
: base()
|
||||
{
|
||||
this.CurrentModel = CurrentModel;
|
||||
this.RequestModeSwitch = RequestModeSwitch;
|
||||
|
||||
new Thread(() => StartLoadDeviceInfo()).Start();
|
||||
}
|
||||
|
||||
private void StartLoadDeviceInfo()
|
||||
{
|
||||
try
|
||||
{
|
||||
Operator = CurrentModel.ExecuteJsonMethodAsString("ReadOperatorName", "OperatorName"); // 000-NL
|
||||
LogFile.Log("Operator: " + Operator);
|
||||
ManufacturerModelName = CurrentModel.ExecuteJsonMethodAsString("ReadManufacturerModelName", "ManufacturerModelName"); // RM-821_eu_denmark_251
|
||||
LogFile.Log("Manufacturer Model Name: " + ManufacturerModelName);
|
||||
ProductCode = CurrentModel.ExecuteJsonMethodAsString("ReadProductCode", "ProductCode"); // 059Q9D7
|
||||
LogFile.Log("Product Code: " + ProductCode);
|
||||
Firmware = CurrentModel.ExecuteJsonMethodAsString("ReadSwVersion", "SwVersion"); // 3051.40000.1349.0007
|
||||
LogFile.Log("Firmware: " + Firmware);
|
||||
|
||||
IMEI = CurrentModel.ExecuteJsonMethodAsString("ReadSerialNumber", "SerialNumber"); // IMEI
|
||||
LogFile.Log("IMEI: " + IMEI);
|
||||
PublicID = CurrentModel.ExecuteJsonMethodAsBytes("ReadPublicId", "PublicId"); // 0x14 bytes: a5 e5 ...
|
||||
LogFile.Log("Public ID: " + Converter.ConvertHexToString(PublicID, " "));
|
||||
BluetoothMac = CurrentModel.ExecuteJsonMethodAsBytes("ReadBtId", "BtId"); // 6 bytes: bc c6 ...
|
||||
LogFile.Log("Bluetooth MAC: " + Converter.ConvertHexToString(BluetoothMac, " "));
|
||||
WlanMac = CurrentModel.ExecuteJsonMethodAsBytes("ReadWlanMacAddress", "WlanMacAddress1"); // 6 bytes
|
||||
LogFile.Log("WLAN MAC: " + Converter.ConvertHexToString(WlanMac, " "));
|
||||
|
||||
bool? ProductionDone = CurrentModel.ExecuteJsonMethodAsBoolean("ReadProductionDoneState", "ProductionDone");
|
||||
if (ProductionDone == null)
|
||||
IsBootloaderSecurityEnabled = (CurrentModel.ExecuteJsonMethodAsString("GetSecurityMode", "SecMode").IndexOf("Restricted", StringComparison.OrdinalIgnoreCase) >= 0);
|
||||
else
|
||||
IsBootloaderSecurityEnabled = ((CurrentModel.ExecuteJsonMethodAsString("GetSecurityMode", "SecMode").IndexOf("Restricted", StringComparison.OrdinalIgnoreCase) >= 0) && (bool)CurrentModel.ExecuteJsonMethodAsBoolean("ReadProductionDoneState", "ProductionDone"));
|
||||
LogFile.Log("Bootloader Security: " + ((bool)IsBootloaderSecurityEnabled ? "Enabled" : "Disabled"));
|
||||
IsSimLocked = CurrentModel.ExecuteJsonMethodAsBoolean("ReadSimlockActive", "SimLockActive");
|
||||
LogFile.Log("Simlock: " + ((bool)IsSimLocked ? "Active" : "Unlocked"));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private string _ProductCode = null;
|
||||
public string ProductCode
|
||||
{
|
||||
get
|
||||
{
|
||||
return _ProductCode;
|
||||
}
|
||||
set
|
||||
{
|
||||
_ProductCode = value;
|
||||
OnPropertyChanged("ProductCode");
|
||||
}
|
||||
}
|
||||
|
||||
private string _ManufacturerModelName = null;
|
||||
public string ManufacturerModelName
|
||||
{
|
||||
get
|
||||
{
|
||||
return _ManufacturerModelName;
|
||||
}
|
||||
set
|
||||
{
|
||||
_ManufacturerModelName = value;
|
||||
OnPropertyChanged("ManufacturerModelName");
|
||||
}
|
||||
}
|
||||
|
||||
private string _Operator = null;
|
||||
public string Operator
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Operator;
|
||||
}
|
||||
set
|
||||
{
|
||||
_Operator = value;
|
||||
OnPropertyChanged("Operator");
|
||||
}
|
||||
}
|
||||
|
||||
private string _Firmware = null;
|
||||
public string Firmware
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Firmware;
|
||||
}
|
||||
set
|
||||
{
|
||||
_Firmware = value;
|
||||
OnPropertyChanged("Firmware");
|
||||
}
|
||||
}
|
||||
|
||||
private string _IMEI = null;
|
||||
public string IMEI
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IMEI;
|
||||
}
|
||||
set
|
||||
{
|
||||
_IMEI = value;
|
||||
OnPropertyChanged("IMEI");
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] _PublicID = null;
|
||||
public byte[] PublicID
|
||||
{
|
||||
get
|
||||
{
|
||||
return _PublicID;
|
||||
}
|
||||
set
|
||||
{
|
||||
_PublicID = value;
|
||||
OnPropertyChanged("PublicID");
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] _WlanMac = null;
|
||||
public byte[] WlanMac
|
||||
{
|
||||
get
|
||||
{
|
||||
return _WlanMac;
|
||||
}
|
||||
set
|
||||
{
|
||||
_WlanMac = value;
|
||||
OnPropertyChanged("WlanMac");
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] _BluetoothMac = null;
|
||||
public byte[] BluetoothMac
|
||||
{
|
||||
get
|
||||
{
|
||||
return _BluetoothMac;
|
||||
}
|
||||
set
|
||||
{
|
||||
_BluetoothMac = value;
|
||||
OnPropertyChanged("BluetoothMac");
|
||||
}
|
||||
}
|
||||
|
||||
private bool? _IsBootloaderSecurityEnabled = null;
|
||||
public bool? IsBootloaderSecurityEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IsBootloaderSecurityEnabled;
|
||||
}
|
||||
set
|
||||
{
|
||||
_IsBootloaderSecurityEnabled = value;
|
||||
OnPropertyChanged("IsBootloaderSecurityEnabled");
|
||||
}
|
||||
}
|
||||
|
||||
private bool? _IsSimLocked = null;
|
||||
public bool? IsSimLocked
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IsSimLocked;
|
||||
}
|
||||
set
|
||||
{
|
||||
_IsSimLocked = value;
|
||||
OnPropertyChanged("IsSimLocked");
|
||||
}
|
||||
}
|
||||
|
||||
public void RebootTo(string Mode)
|
||||
{
|
||||
switch (Mode)
|
||||
{
|
||||
case "Flash":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_Flash);
|
||||
break;
|
||||
case "Label":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_Label);
|
||||
break;
|
||||
case "MassStorage":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_MassStorage);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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.
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class NotImplementedViewModel: ContextViewModel
|
||||
{
|
||||
internal string Message { get; set; }
|
||||
|
||||
internal NotImplementedViewModel(MainViewModel Main)
|
||||
: base(Main)
|
||||
{
|
||||
Message = "Not implemented yet...";
|
||||
}
|
||||
|
||||
internal NotImplementedViewModel(MainViewModel Main, string Message)
|
||||
: base(Main)
|
||||
{
|
||||
this.Message = Message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
// 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 MadWizard.WinUSBNet;
|
||||
using System;
|
||||
using System.Diagnostics.Eventing.Reader;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal delegate void NewDeviceArrivedEvent(ArrivalEventArgs Args);
|
||||
internal delegate void DeviceRemovedEvent();
|
||||
|
||||
internal class PhoneNotifierViewModel
|
||||
{
|
||||
private USBNotifier LumiaOldCombiNotifier;
|
||||
private USBNotifier LumiaNewCombiNotifier;
|
||||
private USBNotifier LumiaNormalNotifier;
|
||||
private USBNotifier LumiaFlashNotifier;
|
||||
private USBNotifier StorageNotifier;
|
||||
private USBNotifier ComPortNotifier;
|
||||
private USBNotifier LumiaEmergencyNotifier;
|
||||
|
||||
public PhoneInterfaces? CurrentInterface = null;
|
||||
private PhoneInterfaces? LastInterface = null;
|
||||
public IDisposable CurrentModel = null;
|
||||
|
||||
public event NewDeviceArrivedEvent NewDeviceArrived = delegate { };
|
||||
public event DeviceRemovedEvent DeviceRemoved = delegate { };
|
||||
|
||||
private Guid OldCombiInterfaceGuid = new Guid("{0FD3B15C-D457-45d8-A779-C2B2C9F9D0FD}");
|
||||
private Guid NewCombiInterfaceGuid = new Guid("{7eaff726-34cc-4204-b09d-f95471b873cf}");
|
||||
private Guid MassStorageInterfaceGuid = new Guid("{53F56307-B6BF-11D0-94F2-00A0C91EFB8B}");
|
||||
private Guid LumiaNormalInterfaceGuid = new Guid("{08324F9C-B621-435C-859B-AE4652481B7C}");
|
||||
private Guid LumiaFlashInterfaceGuid = new Guid("{9e3bd5f7-9690-4fcc-8810-3e2650cd6ecc}");
|
||||
private Guid ComPortInterfaceGuid = new Guid("{86E0D1E0-8089-11D0-9CE4-08003E301F73}");
|
||||
private Guid LumiaEmergencyInterfaceGuid = new Guid("{71DE994D-8B7C-43DB-A27E-2AE7CD579A0C}");
|
||||
|
||||
private object ModelLock = new object();
|
||||
|
||||
private EventWaitHandle NewInterfaceWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
|
||||
|
||||
private EventLogWatcher LogWatcher;
|
||||
|
||||
internal void Start()
|
||||
{
|
||||
LumiaOldCombiNotifier = new USBNotifier(OldCombiInterfaceGuid);
|
||||
LumiaOldCombiNotifier.Arrival += LumiaNotifier_Arrival;
|
||||
LumiaOldCombiNotifier.Removal += LumiaNotifier_Removal;
|
||||
|
||||
LumiaNewCombiNotifier = new USBNotifier(NewCombiInterfaceGuid);
|
||||
LumiaNewCombiNotifier.Arrival += LumiaNotifier_Arrival;
|
||||
LumiaNewCombiNotifier.Removal += LumiaNotifier_Removal;
|
||||
|
||||
LumiaNormalNotifier = new USBNotifier(LumiaNormalInterfaceGuid);
|
||||
LumiaNormalNotifier.Arrival += LumiaNotifier_Arrival;
|
||||
LumiaNormalNotifier.Removal += LumiaNotifier_Removal;
|
||||
|
||||
LumiaFlashNotifier = new USBNotifier(LumiaFlashInterfaceGuid);
|
||||
LumiaFlashNotifier.Arrival += LumiaNotifier_Arrival;
|
||||
LumiaFlashNotifier.Removal += LumiaNotifier_Removal;
|
||||
|
||||
StorageNotifier = new USBNotifier(MassStorageInterfaceGuid);
|
||||
StorageNotifier.Arrival += LumiaNotifier_Arrival;
|
||||
StorageNotifier.Removal += LumiaNotifier_Removal;
|
||||
|
||||
ComPortNotifier = new USBNotifier(ComPortInterfaceGuid);
|
||||
ComPortNotifier.Arrival += LumiaNotifier_Arrival;
|
||||
ComPortNotifier.Removal += LumiaNotifier_Removal;
|
||||
|
||||
LumiaEmergencyNotifier = new USBNotifier(LumiaEmergencyInterfaceGuid);
|
||||
LumiaEmergencyNotifier.Arrival += LumiaNotifier_Arrival;
|
||||
LumiaEmergencyNotifier.Removal += LumiaNotifier_Removal;
|
||||
|
||||
try
|
||||
{
|
||||
EventLogQuery LogQuery = new EventLogQuery("Microsoft-Windows-Kernel-PnP/Configuration", PathType.LogName, "*[System[(EventID = 411)]]");
|
||||
LogWatcher = new EventLogWatcher(LogQuery);
|
||||
LogWatcher.EventRecordWritten += new EventHandler<EventRecordWrittenEventArgs>(PnPEventWritten);
|
||||
LogWatcher.Enabled = true;
|
||||
App.IsPnPEventLogMissing = false;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private void PnPEventWritten(Object obj, EventRecordWrittenEventArgs arg)
|
||||
{
|
||||
string Description = arg.EventRecord.FormatDescription();
|
||||
if (Description.IndexOf("VID_045E&PID_9006", 0, StringComparison.OrdinalIgnoreCase) >= 0)
|
||||
{
|
||||
LogFile.Log("Event " + arg.EventRecord.Id.ToString() + ": " + Description, LogType.FileOnly);
|
||||
LogFile.Log("Phone switched to Mass Storage mode, but the driver on the PC did not start correctly", LogType.FileAndConsole);
|
||||
CurrentInterface = PhoneInterfaces.Lumia_BadMassStorage;
|
||||
CurrentModel = null;
|
||||
NewDeviceArrived(new ArrivalEventArgs((PhoneInterfaces)CurrentInterface, CurrentModel));
|
||||
}
|
||||
}
|
||||
|
||||
internal void Stop()
|
||||
{
|
||||
LumiaOldCombiNotifier.Dispose();
|
||||
LumiaNewCombiNotifier.Dispose();
|
||||
LumiaNormalNotifier.Dispose();
|
||||
LumiaFlashNotifier.Dispose();
|
||||
StorageNotifier.Dispose();
|
||||
ComPortNotifier.Dispose();
|
||||
LumiaEmergencyNotifier.Dispose();
|
||||
LogWatcher.Dispose();
|
||||
}
|
||||
|
||||
internal async Task WaitForNextNodeChange()
|
||||
{
|
||||
// Node change events are on all USBnotifiers, so we just pick one
|
||||
await LumiaEmergencyNotifier.WaitForNextNodeChange();
|
||||
}
|
||||
|
||||
internal void NotifyArrival()
|
||||
{
|
||||
if (CurrentInterface != null)
|
||||
NewDeviceArrived(new ArrivalEventArgs((PhoneInterfaces)CurrentInterface, CurrentModel));
|
||||
}
|
||||
|
||||
void LumiaNotifier_Arrival(object sender, USBEvent e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (e.DevicePath.IndexOf("VID_0421&PID_0660&MI_04", StringComparison.OrdinalIgnoreCase) >= 0)
|
||||
{
|
||||
CurrentInterface = PhoneInterfaces.Lumia_Label;
|
||||
CurrentModel = new NokiaPhoneModel(e.DevicePath);
|
||||
LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly);
|
||||
LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly);
|
||||
LogFile.Log("Connected device: Lumia", LogType.FileAndConsole);
|
||||
LogFile.Log("Mode: Label", LogType.FileAndConsole);
|
||||
NewDeviceArrived(new ArrivalEventArgs((PhoneInterfaces)CurrentInterface, CurrentModel));
|
||||
}
|
||||
else if ((e.DevicePath.IndexOf("VID_0421&PID_0661", StringComparison.OrdinalIgnoreCase) >= 0) ||
|
||||
(e.DevicePath.IndexOf("VID_0421&PID_06FC", StringComparison.OrdinalIgnoreCase) >= 0) || // VID_0421&PID_06FC is for Lumia 930
|
||||
(e.DevicePath.IndexOf("vid_045e&pid_0a00", StringComparison.OrdinalIgnoreCase) >= 0)) // vid_045e & pid_0a00 & mi_03 = Lumia 950 XL normal mode
|
||||
{
|
||||
if (((USBNotifier)sender).Guid == OldCombiInterfaceGuid)
|
||||
{
|
||||
NewInterfaceWaitHandle.Reset();
|
||||
if (USBDevice.GetDevices(NewCombiInterfaceGuid).Count() > 0)
|
||||
return;
|
||||
else
|
||||
{
|
||||
// Old combi-interface was detected, but new combi-interface was not detected.
|
||||
// This could mean 2 things:
|
||||
// - It is a WP80 phone, which has only this old combi-interface to talk to.
|
||||
// - It is a WP81 / W10M phone, which has an unresponsive old combi-interface and we need to wait for the new combi-interface to arrive.
|
||||
// We will wait maximum 1 sec for the new interface. If it doesn't arrive we will start talking on this old interface.
|
||||
// We will start a new thread, because if this thread is blocked, no new devices will arrive.
|
||||
string DevicePath = e.DevicePath;
|
||||
ThreadPool.QueueUserWorkItem(s =>
|
||||
{
|
||||
if (!NewInterfaceWaitHandle.WaitOne(1000))
|
||||
{
|
||||
// Waithandle not set.
|
||||
// So new interface did not arrive.
|
||||
// So we assume we need to talk to this old interface.
|
||||
|
||||
CurrentInterface = PhoneInterfaces.Lumia_Normal;
|
||||
CurrentModel = new NokiaPhoneModel(DevicePath);
|
||||
LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly);
|
||||
LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly);
|
||||
LogFile.Log("Connected device: Lumia", LogType.FileAndConsole);
|
||||
LogFile.Log("Mode: Normal", LogType.FileAndConsole);
|
||||
NewDeviceArrived(new ArrivalEventArgs((PhoneInterfaces)CurrentInterface, CurrentModel));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NewInterfaceWaitHandle.Set();
|
||||
|
||||
CurrentInterface = PhoneInterfaces.Lumia_Normal;
|
||||
CurrentModel = new NokiaPhoneModel(e.DevicePath);
|
||||
LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly);
|
||||
LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly);
|
||||
LogFile.Log("Connected device: Lumia", LogType.FileAndConsole);
|
||||
LogFile.Log("Mode: Normal", LogType.FileAndConsole);
|
||||
NewDeviceArrived(new ArrivalEventArgs((PhoneInterfaces)CurrentInterface, CurrentModel));
|
||||
}
|
||||
}
|
||||
else if ((e.DevicePath.IndexOf("VID_0421&PID_066E", StringComparison.OrdinalIgnoreCase) >= 0) ||
|
||||
(e.DevicePath.IndexOf("VID_0421&PID_0714", StringComparison.OrdinalIgnoreCase) >= 0) || // VID_0421&PID_0714 is for Lumia 930
|
||||
(e.DevicePath.IndexOf("VID_045E&PID_0A02", StringComparison.OrdinalIgnoreCase) >= 0)) // VID_045E&PID_0A02 is for Lumia 950
|
||||
{
|
||||
CurrentModel = new NokiaFlashModel(e.DevicePath);
|
||||
((NokiaFlashModel)CurrentModel).InterfaceChanged += InterfaceChanged;
|
||||
|
||||
// Attempt to request a param.
|
||||
// When it succeeds we have full Flash mode.
|
||||
// When it fails we are in a limited Flash mode, like Bootmanager or Hardreset-screen.
|
||||
// Limited Flash mode only supports boot-commands; not querying info.
|
||||
byte[] QueryResult = ((NokiaFlashModel)CurrentModel).ReadParam("SS");
|
||||
if (QueryResult == null)
|
||||
{
|
||||
CurrentInterface = PhoneInterfaces.Lumia_Bootloader;
|
||||
LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly);
|
||||
LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly);
|
||||
LogFile.Log("Connected device: Lumia", LogType.FileAndConsole);
|
||||
LogFile.Log("Mode: Bootloader", LogType.FileAndConsole);
|
||||
NewDeviceArrived(new ArrivalEventArgs((PhoneInterfaces)CurrentInterface, CurrentModel));
|
||||
}
|
||||
else
|
||||
{
|
||||
((NokiaFlashModel)CurrentModel).DisableRebootTimeOut();
|
||||
CurrentInterface = PhoneInterfaces.Lumia_Flash;
|
||||
LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly);
|
||||
LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly);
|
||||
LogFile.Log("Connected device: Lumia", LogType.FileAndConsole);
|
||||
LogFile.Log("Mode: Flash", LogType.FileAndConsole);
|
||||
NewDeviceArrived(new ArrivalEventArgs((PhoneInterfaces)CurrentInterface, CurrentModel));
|
||||
}
|
||||
}
|
||||
else if ((e.DevicePath.IndexOf(@"disk&ven_qualcomm&prod_mmc_storage", StringComparison.OrdinalIgnoreCase) >= 0) ||
|
||||
(e.DevicePath.IndexOf(@"DISK&VEN_MSFT&PROD_PHONE_MMC_STOR", StringComparison.OrdinalIgnoreCase) >= 0))
|
||||
{
|
||||
#if DEBUG
|
||||
LogFile.Log("Mass storage arrived: " + e.DevicePath, LogType.FileOnly);
|
||||
LogFile.Log("Start new thread for getting metadata.", LogType.FileOnly);
|
||||
#endif
|
||||
|
||||
// This function is possibly called by an USB notification WndProc.
|
||||
// It is not possible to invoke COM objects from a WndProc.
|
||||
// MassStorage uses ManagementObjectSearcher, which is a COM object.
|
||||
// Therefore we use a new thread.
|
||||
ThreadPool.QueueUserWorkItem(s => {
|
||||
lock (ModelLock)
|
||||
{
|
||||
if (!(CurrentModel is MassStorage))
|
||||
{
|
||||
MassStorage NewModel = new MassStorage(e.DevicePath);
|
||||
if (NewModel.Drive != null) // When logical drive is already known, we use this model. Or else we wait for the logical drive to arrive.
|
||||
{
|
||||
CurrentInterface = PhoneInterfaces.Lumia_MassStorage;
|
||||
CurrentModel = NewModel;
|
||||
LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly);
|
||||
LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly);
|
||||
LogFile.Log("Connected device: Lumia", LogType.FileAndConsole);
|
||||
LogFile.Log("Mode: Mass storage mode", LogType.FileAndConsole);
|
||||
NewDeviceArrived(new ArrivalEventArgs((PhoneInterfaces)CurrentInterface, CurrentModel));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else if ((e.DevicePath.Length == @"\\.\E:".Length) && (e.DevicePath.StartsWith(@"\\.\")) && (e.DevicePath.EndsWith(":")))
|
||||
{
|
||||
#if DEBUG
|
||||
LogFile.Log("Mass storage arrived: " + e.DevicePath, LogType.FileOnly);
|
||||
LogFile.Log("Start new thread for getting metadata.", LogType.FileOnly);
|
||||
#endif
|
||||
// This function is possibly called by an USB notification WndProc.
|
||||
// It is not possible to invoke COM objects from a WndProc.
|
||||
// MassStorage uses ManagementObjectSearcher, which is a COM object.
|
||||
// Therefore we use a new thread.
|
||||
ThreadPool.QueueUserWorkItem(s =>
|
||||
{
|
||||
lock (ModelLock)
|
||||
{
|
||||
if (!(CurrentModel is MassStorage))
|
||||
{
|
||||
MassStorage NewModel = new MassStorage(e.DevicePath);
|
||||
if (NewModel.Drive != null) // When logical drive is already known, we use this model. Or else we wait for the logical drive to arrive.
|
||||
{
|
||||
CurrentInterface = PhoneInterfaces.Lumia_MassStorage;
|
||||
CurrentModel = NewModel;
|
||||
LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly);
|
||||
LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly);
|
||||
LogFile.Log("Connected device: Lumia", LogType.FileAndConsole);
|
||||
LogFile.Log("Mode: Mass storage mode", LogType.FileAndConsole);
|
||||
NewDeviceArrived(new ArrivalEventArgs((PhoneInterfaces)CurrentInterface, CurrentModel));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (e.DevicePath.IndexOf("VID_05C6&PID_9008", StringComparison.OrdinalIgnoreCase) >= 0)
|
||||
{
|
||||
USBDeviceInfo DeviceInfo = USBDevice.GetDevices(((USBNotifier)sender).Guid).Where((d) => string.Compare(d.DevicePath, e.DevicePath, true) == 0).FirstOrDefault();
|
||||
|
||||
if ((DeviceInfo.BusName == "QHSUSB_DLOAD") || (DeviceInfo.BusName == "QHSUSB__BULK") || ((DeviceInfo.BusName == "") && (LastInterface != PhoneInterfaces.Qualcomm_Download))) // TODO: Separate for Sahara!
|
||||
{
|
||||
CurrentInterface = PhoneInterfaces.Qualcomm_Download;
|
||||
CurrentModel = new QualcommSerial(e.DevicePath);
|
||||
NewDeviceArrived(new ArrivalEventArgs((PhoneInterfaces)CurrentInterface, CurrentModel));
|
||||
LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly);
|
||||
LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly);
|
||||
LogFile.Log("Connected device: Lumia", LogType.FileAndConsole);
|
||||
if (DeviceInfo.BusName == "")
|
||||
LogFile.Log("Driver does not show busname, assume mode: Qualcomm Emergency Download 9008", LogType.FileAndConsole);
|
||||
else
|
||||
LogFile.Log("Mode: Qualcomm Emergency Download 9008", LogType.FileAndConsole);
|
||||
}
|
||||
else if ((DeviceInfo.BusName == "QHSUSB_ARMPRG") || ((DeviceInfo.BusName == "") && (LastInterface == PhoneInterfaces.Qualcomm_Download)))
|
||||
{
|
||||
CurrentInterface = PhoneInterfaces.Qualcomm_Flash;
|
||||
CurrentModel = new QualcommSerial(e.DevicePath);
|
||||
NewDeviceArrived(new ArrivalEventArgs((PhoneInterfaces)CurrentInterface, CurrentModel));
|
||||
LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly);
|
||||
LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly);
|
||||
LogFile.Log("Connected device: Lumia", LogType.FileAndConsole);
|
||||
if (DeviceInfo.BusName == "")
|
||||
LogFile.Log("Driver does not show busname, assume mode: Qualcomm Emergency Flash 9008", LogType.FileAndConsole);
|
||||
else
|
||||
LogFile.Log("Mode: Qualcomm Emergency Flash 9008", LogType.FileAndConsole);
|
||||
}
|
||||
}
|
||||
else if (e.DevicePath.IndexOf("VID_05C6&PID_9006", StringComparison.OrdinalIgnoreCase) >= 0)
|
||||
{
|
||||
// This is part of the Mass Storage inteface.
|
||||
// It is a slightly different version of the Qualcomm Emergency interface, which is implemented in SBL3.
|
||||
// One important difference is that the base address for sending a loader is not 0x2A000000, but it is 0x82F00000.
|
||||
|
||||
LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly);
|
||||
LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly);
|
||||
LogFile.Log("Connected device: Lumia", LogType.FileAndConsole);
|
||||
LogFile.Log("Mode: Qualcomm Emergency 9006", LogType.FileAndConsole);
|
||||
}
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
CurrentModel = null;
|
||||
CurrentInterface = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void InterfaceChanged(PhoneInterfaces NewInterface)
|
||||
{
|
||||
CurrentInterface = NewInterface;
|
||||
}
|
||||
|
||||
void LumiaNotifier_Removal(object sender, USBEvent e)
|
||||
{
|
||||
if (
|
||||
(e.DevicePath.IndexOf("VID_0421&PID_0660&MI_04", StringComparison.OrdinalIgnoreCase) >= 0) ||
|
||||
(e.DevicePath.IndexOf("VID_0421&PID_0661", StringComparison.OrdinalIgnoreCase) >= 0) ||
|
||||
(e.DevicePath.IndexOf("VID_0421&PID_06FC", StringComparison.OrdinalIgnoreCase) >= 0) ||
|
||||
(e.DevicePath.IndexOf("VID_0421&PID_066E", StringComparison.OrdinalIgnoreCase) >= 0) ||
|
||||
(e.DevicePath.IndexOf("VID_0421&PID_0714", StringComparison.OrdinalIgnoreCase) >= 0) ||
|
||||
(e.DevicePath.IndexOf("vid_045e&pid_0a00", StringComparison.OrdinalIgnoreCase) >= 0) ||
|
||||
(e.DevicePath.IndexOf("VID_045E&PID_0A02", StringComparison.OrdinalIgnoreCase) >= 0) ||
|
||||
(e.DevicePath.IndexOf("VID_05C6&PID_9008", StringComparison.OrdinalIgnoreCase) >= 0) ||
|
||||
(e.DevicePath.IndexOf(@"disk&ven_qualcomm&prod_mmc_storage", StringComparison.OrdinalIgnoreCase) >= 0) ||
|
||||
(e.DevicePath.IndexOf(@"DISK&VEN_MSFT&PROD_PHONE_MMC_STOR", StringComparison.OrdinalIgnoreCase) >= 0)
|
||||
) {
|
||||
if (CurrentInterface != null)
|
||||
LastInterface = CurrentInterface;
|
||||
CurrentInterface = null;
|
||||
if (CurrentModel != null)
|
||||
{
|
||||
CurrentModel.Dispose();
|
||||
CurrentModel = null;
|
||||
LogFile.Log("Lumia disconnected", LogType.FileAndConsole);
|
||||
}
|
||||
DeviceRemoved();
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<IDisposable> WaitForArrival()
|
||||
{
|
||||
IDisposable Result = null;
|
||||
|
||||
if (CurrentInterface == null)
|
||||
LogFile.Log("Waiting for phone to connect...", LogType.FileOnly);
|
||||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
System.Threading.AutoResetEvent e = new System.Threading.AutoResetEvent(false);
|
||||
NewDeviceArrivedEvent Arrived = (a) =>
|
||||
{
|
||||
e.Set();
|
||||
Result = a.NewModel;
|
||||
};
|
||||
NewDeviceArrived += Arrived;
|
||||
e.WaitOne();
|
||||
NewDeviceArrived -= Arrived;
|
||||
});
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal async Task WaitForRemoval()
|
||||
{
|
||||
LogFile.Log("Waiting for phone to disconnect...", LogType.FileOnly);
|
||||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
System.Threading.AutoResetEvent e = new System.Threading.AutoResetEvent(false);
|
||||
DeviceRemovedEvent Removed = () =>
|
||||
{
|
||||
e.Set();
|
||||
};
|
||||
DeviceRemoved += Removed;
|
||||
e.WaitOne();
|
||||
DeviceRemoved -= Removed;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
// 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.Windows;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class RegistrationViewModel : ContextViewModel
|
||||
{
|
||||
Action Completed;
|
||||
Action Failed;
|
||||
|
||||
internal RegistrationViewModel(Action Completed, Action Failed)
|
||||
: base()
|
||||
{
|
||||
this.Completed = Completed;
|
||||
this.Failed = Failed;
|
||||
}
|
||||
|
||||
private DelegateCommand _ExitCommand = null;
|
||||
public DelegateCommand ExitCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_ExitCommand == null)
|
||||
{
|
||||
_ExitCommand = new DelegateCommand(() =>
|
||||
{
|
||||
Application.Current.Shutdown();
|
||||
});
|
||||
}
|
||||
return _ExitCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private DelegateCommand _ContinueCommand = null;
|
||||
public DelegateCommand ContinueCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_ContinueCommand == null)
|
||||
{
|
||||
_ContinueCommand = new DelegateCommand(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
App.Config.RegistrationName = _Name;
|
||||
App.Config.RegistrationEmail = _Email;
|
||||
App.Config.RegistrationSkypeID = _SkypeID;
|
||||
App.Config.RegistrationTelegramID = _TelegramID;
|
||||
App.Config.RegistrationKey = Registration.CalcRegKey();
|
||||
|
||||
LogFile.BeginAction("Registration");
|
||||
LogFile.EndAction("Registration");
|
||||
|
||||
App.Config.WriteConfig();
|
||||
|
||||
Completed();
|
||||
}
|
||||
catch
|
||||
{
|
||||
Failed();
|
||||
}
|
||||
});
|
||||
}
|
||||
return _ContinueCommand;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsRegistrationComplete
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((_Name != null) && (_Name.Length >= 2) && (_Email != null) && (_Email.Length >= 5));
|
||||
}
|
||||
}
|
||||
|
||||
private string _Name = null;
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Name;
|
||||
}
|
||||
set
|
||||
{
|
||||
_Name = value;
|
||||
OnPropertyChanged("Name");
|
||||
OnPropertyChanged("IsRegistrationComplete");
|
||||
}
|
||||
}
|
||||
|
||||
private string _Email = null;
|
||||
public string Email
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Email;
|
||||
}
|
||||
set
|
||||
{
|
||||
_Email = value;
|
||||
OnPropertyChanged("Email");
|
||||
OnPropertyChanged("IsRegistrationComplete");
|
||||
}
|
||||
}
|
||||
|
||||
private string _SkypeID = null;
|
||||
public string SkypeID
|
||||
{
|
||||
get
|
||||
{
|
||||
return _SkypeID;
|
||||
}
|
||||
set
|
||||
{
|
||||
_SkypeID = value;
|
||||
OnPropertyChanged("SkypeID");
|
||||
}
|
||||
}
|
||||
|
||||
private string _TelegramID = null;
|
||||
public string TelegramID
|
||||
{
|
||||
get
|
||||
{
|
||||
return _TelegramID;
|
||||
}
|
||||
set
|
||||
{
|
||||
_TelegramID = value;
|
||||
OnPropertyChanged("TelegramID");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
// 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.Threading;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class RestoreSourceSelectionViewModel: ContextViewModel
|
||||
{
|
||||
private PhoneNotifierViewModel PhoneNotifier;
|
||||
private Action<string, string, string> RestoreCallback;
|
||||
private Action<PhoneInterfaces> RequestModeSwitch;
|
||||
internal Action SwitchToUnlockBoot;
|
||||
internal Action SwitchToFlashRom;
|
||||
|
||||
internal RestoreSourceSelectionViewModel(PhoneNotifierViewModel PhoneNotifier, Action<PhoneInterfaces> RequestModeSwitch, Action SwitchToUnlockBoot, Action SwitchToFlashRom, Action<string, string, string> RestoreCallback)
|
||||
: base()
|
||||
{
|
||||
this.PhoneNotifier = PhoneNotifier;
|
||||
this.RestoreCallback = RestoreCallback;
|
||||
this.RequestModeSwitch = RequestModeSwitch;
|
||||
this.SwitchToUnlockBoot = SwitchToUnlockBoot;
|
||||
this.SwitchToFlashRom = SwitchToFlashRom;
|
||||
|
||||
this.PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
||||
this.PhoneNotifier.DeviceRemoved += DeviceRemoved;
|
||||
|
||||
new Thread(() => EvaluateViewState()).Start();
|
||||
}
|
||||
|
||||
private string _EFIESPPath;
|
||||
public string EFIESPPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _EFIESPPath;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _EFIESPPath)
|
||||
{
|
||||
_EFIESPPath = value;
|
||||
OnPropertyChanged("EFIESPPath");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _MainOSPath;
|
||||
public string MainOSPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _MainOSPath;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _MainOSPath)
|
||||
{
|
||||
_MainOSPath = value;
|
||||
OnPropertyChanged("MainOSPath");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _DataPath;
|
||||
public string DataPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _DataPath;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _DataPath)
|
||||
{
|
||||
_DataPath = value;
|
||||
OnPropertyChanged("DataPath");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _IsPhoneDisconnected;
|
||||
public bool IsPhoneDisconnected
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IsPhoneDisconnected;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _IsPhoneDisconnected)
|
||||
{
|
||||
_IsPhoneDisconnected = value;
|
||||
OnPropertyChanged("IsPhoneDisconnected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _IsPhoneInFlashMode;
|
||||
public bool IsPhoneInFlashMode
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IsPhoneInFlashMode;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _IsPhoneInFlashMode)
|
||||
{
|
||||
_IsPhoneInFlashMode = value;
|
||||
OnPropertyChanged("IsPhoneInFlashMode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _IsPhoneInOtherMode;
|
||||
public bool IsPhoneInOtherMode
|
||||
{
|
||||
get
|
||||
{
|
||||
return _IsPhoneInOtherMode;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _IsPhoneInOtherMode)
|
||||
{
|
||||
_IsPhoneInOtherMode = value;
|
||||
OnPropertyChanged("IsPhoneInOtherMode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DelegateCommand _RestoreCommand;
|
||||
public DelegateCommand RestoreCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_RestoreCommand == null)
|
||||
{
|
||||
_RestoreCommand = new DelegateCommand(() => { RestoreCallback(EFIESPPath, MainOSPath, DataPath); }, () => (((EFIESPPath != null) || (MainOSPath != null) || (DataPath != null)) && (PhoneNotifier.CurrentInterface != null)));
|
||||
}
|
||||
return _RestoreCommand;
|
||||
}
|
||||
}
|
||||
|
||||
~RestoreSourceSelectionViewModel()
|
||||
{
|
||||
PhoneNotifier.NewDeviceArrived -= NewDeviceArrived;
|
||||
}
|
||||
|
||||
void NewDeviceArrived(ArrivalEventArgs Args)
|
||||
{
|
||||
new Thread(() => EvaluateViewState()).Start();
|
||||
}
|
||||
|
||||
void DeviceRemoved()
|
||||
{
|
||||
new Thread(() => EvaluateViewState()).Start();
|
||||
}
|
||||
|
||||
internal override void EvaluateViewState()
|
||||
{
|
||||
IsPhoneDisconnected = PhoneNotifier.CurrentInterface == null;
|
||||
IsPhoneInFlashMode = PhoneNotifier.CurrentInterface == PhoneInterfaces.Lumia_Flash;
|
||||
IsPhoneInOtherMode = (!IsPhoneDisconnected && !IsPhoneInFlashMode);
|
||||
RestoreCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
internal void RebootTo(string Mode)
|
||||
{
|
||||
switch (Mode)
|
||||
{
|
||||
case "Normal":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_Normal);
|
||||
break;
|
||||
case "Flash":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_Flash);
|
||||
break;
|
||||
case "Label":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_Label);
|
||||
break;
|
||||
case "MassStorage":
|
||||
RequestModeSwitch(PhoneInterfaces.Lumia_MassStorage);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
// 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.Threading;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class RestoreViewModel: ContextViewModel
|
||||
{
|
||||
private PhoneNotifierViewModel PhoneNotifier;
|
||||
private Action Callback;
|
||||
private Action<PhoneInterfaces> RequestModeSwitch;
|
||||
private Action SwitchToUnlockBoot;
|
||||
private Action SwitchToFlashRom;
|
||||
|
||||
internal RestoreViewModel(PhoneNotifierViewModel PhoneNotifier, Action<PhoneInterfaces> RequestModeSwitch, Action SwitchToUnlockBoot, Action SwitchToFlashRom, Action Callback)
|
||||
: base()
|
||||
{
|
||||
IsSwitchingInterface = true;
|
||||
|
||||
this.PhoneNotifier = PhoneNotifier;
|
||||
this.Callback = Callback;
|
||||
this.RequestModeSwitch = RequestModeSwitch;
|
||||
this.SwitchToUnlockBoot = SwitchToUnlockBoot;
|
||||
this.SwitchToFlashRom = SwitchToFlashRom;
|
||||
}
|
||||
|
||||
internal override void EvaluateViewState()
|
||||
{
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (SubContextViewModel == null)
|
||||
ActivateSubContext(new RestoreSourceSelectionViewModel(PhoneNotifier, RequestModeSwitch, SwitchToUnlockBoot, SwitchToFlashRom, DoRestore));
|
||||
|
||||
if (SubContextViewModel is RestoreSourceSelectionViewModel)
|
||||
((RestoreSourceSelectionViewModel)SubContextViewModel).EvaluateViewState();
|
||||
}
|
||||
|
||||
internal async void DoRestore(string EFIESPPath, string MainOSPath, string DataPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
await SwitchModeViewModel.SwitchToWithProgress(PhoneNotifier, PhoneInterfaces.Lumia_Flash,
|
||||
(msg, sub) =>
|
||||
ActivateSubContext(new BusyViewModel(msg, sub)));
|
||||
RestoreTask(EFIESPPath, MainOSPath, DataPath);
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
ActivateSubContext(new MessageViewModel(Ex.Message, Callback));
|
||||
}
|
||||
}
|
||||
|
||||
internal void RestoreTask(string EFIESPPath, string MainOSPath, string DataPath)
|
||||
{
|
||||
new Thread(() =>
|
||||
{
|
||||
bool Result = true;
|
||||
|
||||
ActivateSubContext(new BusyViewModel("Initializing restore..."));
|
||||
|
||||
ulong TotalSizeSectors = 0;
|
||||
int PartitionCount = 0;
|
||||
try
|
||||
{
|
||||
if (EFIESPPath != null)
|
||||
{
|
||||
TotalSizeSectors += (ulong)new FileInfo(EFIESPPath).Length / 0x200;
|
||||
PartitionCount++;
|
||||
}
|
||||
|
||||
if (MainOSPath != null)
|
||||
{
|
||||
TotalSizeSectors += (ulong)new FileInfo(MainOSPath).Length / 0x200;
|
||||
PartitionCount++;
|
||||
}
|
||||
|
||||
if (DataPath != null)
|
||||
{
|
||||
TotalSizeSectors += (ulong)new FileInfo(DataPath).Length / 0x200;
|
||||
PartitionCount++;
|
||||
}
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
Result = false;
|
||||
}
|
||||
|
||||
NokiaFlashModel Phone = (NokiaFlashModel)PhoneNotifier.CurrentModel;
|
||||
|
||||
BusyViewModel Busy = new BusyViewModel("Restoring...", MaxProgressValue: TotalSizeSectors, UIContext: UIContext);
|
||||
ProgressUpdater Updater = Busy.ProgressUpdater;
|
||||
ActivateSubContext(Busy);
|
||||
|
||||
int i = 0;
|
||||
if (Result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (EFIESPPath != null)
|
||||
{
|
||||
i++;
|
||||
Busy.Message = "Restoring partition EFIESP (" + i.ToString() + @"/" + PartitionCount.ToString() + ")";
|
||||
Phone.FlashRawPartition(EFIESPPath, "EFIESP", Updater);
|
||||
}
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
Result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (MainOSPath != null)
|
||||
{
|
||||
i++;
|
||||
Busy.Message = "Restoring partition MainOS (" + i.ToString() + @"/" + PartitionCount.ToString() + ")";
|
||||
Phone.FlashRawPartition(EFIESPPath, "MainOS", Updater);
|
||||
}
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
Result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (DataPath != null)
|
||||
{
|
||||
i++;
|
||||
Busy.Message = "Restoring partition Data (" + i.ToString() + @"/" + PartitionCount.ToString() + ")";
|
||||
Phone.FlashRawPartition(EFIESPPath, "Data", Updater);
|
||||
}
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
Result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Result)
|
||||
{
|
||||
ActivateSubContext(new MessageViewModel("Failed to restore!", Exit));
|
||||
return;
|
||||
}
|
||||
|
||||
ActivateSubContext(new MessageViewModel("Successfully restored!", Exit));
|
||||
}).Start();
|
||||
}
|
||||
|
||||
internal async void Exit()
|
||||
{
|
||||
IsSwitchingInterface = false;
|
||||
try
|
||||
{
|
||||
await SwitchModeViewModel.SwitchToWithProgress(PhoneNotifier, PhoneInterfaces.Lumia_Normal,
|
||||
(msg, sub) =>
|
||||
ActivateSubContext(new BusyViewModel(msg, sub)));
|
||||
Callback();
|
||||
ActivateSubContext(null);
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
ActivateSubContext(new MessageViewModel(Ex.Message, Callback));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,669 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal delegate void ModeSwitchProgressHandler(string Message, string SubMessage);
|
||||
internal delegate void ModeSwitchErrorHandler(string Message);
|
||||
internal delegate void ModeSwitchSuccessHandler(IDisposable NewModel, PhoneInterfaces NewInterface);
|
||||
|
||||
internal class SwitchModeViewModel: ContextViewModel
|
||||
{
|
||||
protected PhoneNotifierViewModel PhoneNotifier;
|
||||
protected IDisposable CurrentModel;
|
||||
protected PhoneInterfaces? CurrentMode;
|
||||
protected PhoneInterfaces? TargetMode;
|
||||
protected bool IsSwitching = false;
|
||||
internal event ModeSwitchProgressHandler ModeSwitchProgress = delegate { };
|
||||
internal event ModeSwitchErrorHandler ModeSwitchError = delegate { };
|
||||
internal event ModeSwitchSuccessHandler ModeSwitchSuccess = delegate { };
|
||||
internal new SetWorkingStatus SetWorkingStatus = (m, s, v, a, st) => { };
|
||||
internal new UpdateWorkingStatus UpdateWorkingStatus = (m, s, v, st) => { };
|
||||
private string MassStorageWarning = null;
|
||||
|
||||
internal SwitchModeViewModel(PhoneNotifierViewModel PhoneNotifier, PhoneInterfaces? TargetMode)
|
||||
: base()
|
||||
{
|
||||
if ((PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_Flash) && (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) && (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_Label) && (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_Normal))
|
||||
throw new ArgumentException();
|
||||
|
||||
this.PhoneNotifier = PhoneNotifier;
|
||||
this.CurrentModel = (NokiaPhoneModel)PhoneNotifier.CurrentModel;
|
||||
this.CurrentMode = PhoneNotifier.CurrentInterface;
|
||||
this.TargetMode = TargetMode;
|
||||
|
||||
if (this.CurrentMode == null)
|
||||
{
|
||||
LogFile.Log("Waiting for phone to connect...", LogType.FileAndConsole);
|
||||
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make sure this ViewModel has its View loaded before we continue,
|
||||
// or else loading of Views can get mixed up.
|
||||
if (SynchronizationContext.Current == null)
|
||||
StartSwitch();
|
||||
else
|
||||
{
|
||||
SynchronizationContext.Current.Post((s) =>
|
||||
{
|
||||
((SwitchModeViewModel)s).StartSwitch();
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal SwitchModeViewModel(PhoneNotifierViewModel PhoneNotifier, PhoneInterfaces? TargetMode, ModeSwitchProgressHandler ModeSwitchProgress, ModeSwitchErrorHandler ModeSwitchError, ModeSwitchSuccessHandler ModeSwitchSuccess, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null)
|
||||
: base()
|
||||
{
|
||||
if ((PhoneNotifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) && (TargetMode == PhoneInterfaces.Lumia_Flash))
|
||||
{
|
||||
PhoneInfo Info = ((NokiaFlashModel)PhoneNotifier.CurrentModel).ReadPhoneInfo(false);
|
||||
if (Info.BootManagerProtocolVersionMajor >= 2)
|
||||
{
|
||||
try
|
||||
{
|
||||
// The implementation of SwitchToFlashAppContext() is improved
|
||||
// SwitchToFlashAppContext() should only be used with BootMgr v2
|
||||
// For switching from BootMgr to FlashApp, it will use NOKS
|
||||
// That will switch to a charging state, whereas a normal context switch will not start charging
|
||||
// The implementation of NOKS in BootMgr mode has changed in BootMgr v2
|
||||
// It does not disconnect / reconnect anymore and the apptype is changed immediately
|
||||
// NOKS still doesnt return a status
|
||||
// BootMgr v1 uses normal NOKS and waits for arrival of FlashApp
|
||||
((NokiaFlashModel)PhoneNotifier.CurrentModel).SwitchToFlashAppContext();
|
||||
|
||||
// But this was called as a real switch, so we will raise an arrival event.
|
||||
PhoneNotifier.CurrentInterface = PhoneInterfaces.Lumia_Flash;
|
||||
PhoneNotifier.NotifyArrival();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
if (PhoneNotifier.CurrentInterface == TargetMode)
|
||||
ModeSwitchSuccess(PhoneNotifier.CurrentModel, (PhoneInterfaces)PhoneNotifier.CurrentInterface);
|
||||
else
|
||||
{
|
||||
this.PhoneNotifier = PhoneNotifier;
|
||||
this.CurrentModel = (NokiaPhoneModel)PhoneNotifier.CurrentModel;
|
||||
this.CurrentMode = PhoneNotifier.CurrentInterface;
|
||||
this.TargetMode = TargetMode;
|
||||
if (ModeSwitchProgress != null) this.ModeSwitchProgress += ModeSwitchProgress;
|
||||
if (ModeSwitchError != null) this.ModeSwitchError += ModeSwitchError;
|
||||
if (ModeSwitchSuccess != null) this.ModeSwitchSuccess += ModeSwitchSuccess;
|
||||
if (SetWorkingStatus != null) this.SetWorkingStatus = SetWorkingStatus;
|
||||
if (UpdateWorkingStatus != null) this.UpdateWorkingStatus = UpdateWorkingStatus;
|
||||
|
||||
if (this.CurrentMode == null)
|
||||
{
|
||||
LogFile.Log("Waiting for phone to connect...", LogType.FileAndConsole);
|
||||
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make sure this ViewModel has its View loaded before we continue,
|
||||
// or else loading of Views can get mixed up.
|
||||
if (SynchronizationContext.Current == null)
|
||||
StartSwitch();
|
||||
else
|
||||
{
|
||||
SynchronizationContext.Current.Post((s) =>
|
||||
{
|
||||
((SwitchModeViewModel)s).StartSwitch();
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ModeSwitchProgressWrapper(string Message, string SubMessage)
|
||||
{
|
||||
if ((UIContext == null) || (SynchronizationContext.Current == UIContext))
|
||||
{
|
||||
ModeSwitchProgress(Message, SubMessage);
|
||||
SetWorkingStatus(Message, SubMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
UIContext.Post(s => {
|
||||
ModeSwitchProgress(Message, SubMessage);
|
||||
SetWorkingStatus(Message, SubMessage);
|
||||
}, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void ModeSwitchErrorWrapper(string Message)
|
||||
{
|
||||
IsSwitching = false;
|
||||
if ((UIContext == null) || (SynchronizationContext.Current == UIContext))
|
||||
ModeSwitchError(Message);
|
||||
else
|
||||
UIContext.Post(s => { ModeSwitchError(Message); }, null);
|
||||
}
|
||||
|
||||
private void ModeSwitchSuccessWrapper()
|
||||
{
|
||||
IsSwitching = false;
|
||||
if ((UIContext == null) || (SynchronizationContext.Current == UIContext))
|
||||
ModeSwitchSuccess((IDisposable)PhoneNotifier.CurrentModel, (PhoneInterfaces)PhoneNotifier.CurrentInterface);
|
||||
else
|
||||
UIContext.Post(s => { ModeSwitchSuccess((IDisposable)PhoneNotifier.CurrentModel, (PhoneInterfaces)PhoneNotifier.CurrentInterface); }, null);
|
||||
}
|
||||
|
||||
~SwitchModeViewModel()
|
||||
{
|
||||
if (PhoneNotifier != null)
|
||||
PhoneNotifier.NewDeviceArrived -= NewDeviceArrived;
|
||||
}
|
||||
|
||||
internal void StartSwitch()
|
||||
{
|
||||
IsSwitching = true;
|
||||
|
||||
// Make switch and set message or navigate to error
|
||||
switch (CurrentMode)
|
||||
{
|
||||
case PhoneInterfaces.Lumia_Normal:
|
||||
case PhoneInterfaces.Lumia_Label:
|
||||
string DeviceMode;
|
||||
|
||||
switch (TargetMode)
|
||||
{
|
||||
case PhoneInterfaces.Lumia_Normal:
|
||||
DeviceMode = "Normal";
|
||||
IsSwitchingInterface = true;
|
||||
ModeSwitchProgressWrapper("Rebooting phone to Normal mode...", null);
|
||||
LogFile.Log("Rebooting phone to Normal mode", LogType.FileAndConsole);
|
||||
break;
|
||||
case PhoneInterfaces.Lumia_Bootloader:
|
||||
DeviceMode = "Normal";
|
||||
IsSwitchingInterface = true;
|
||||
ModeSwitchProgressWrapper("Rebooting phone to Bootloader mode...", null);
|
||||
LogFile.Log("Rebooting phone to Bootloader mode", LogType.FileAndConsole);
|
||||
break;
|
||||
case PhoneInterfaces.Lumia_Flash:
|
||||
DeviceMode = "Flash";
|
||||
IsSwitchingInterface = true;
|
||||
ModeSwitchProgressWrapper("Rebooting phone to Flash mode...", null);
|
||||
LogFile.Log("Rebooting phone to Flash mode", LogType.FileAndConsole);
|
||||
break;
|
||||
case PhoneInterfaces.Lumia_Label:
|
||||
DeviceMode = "Test";
|
||||
IsSwitchingInterface = true;
|
||||
ModeSwitchProgressWrapper("Rebooting phone to Label mode...", null);
|
||||
LogFile.Log("Rebooting phone to Label mode", LogType.FileAndConsole);
|
||||
break;
|
||||
case PhoneInterfaces.Lumia_MassStorage:
|
||||
DeviceMode = "Flash";
|
||||
IsSwitchingInterface = true;
|
||||
ModeSwitchProgressWrapper("First rebooting phone to Flash mode...", null);
|
||||
LogFile.Log("First rebooting phone to Flash mode (then attempt Mass Storage mode)", LogType.FileAndConsole);
|
||||
break;
|
||||
case PhoneInterfaces.Qualcomm_Download: // Only available in new Lumia's
|
||||
DeviceMode = "Flash";
|
||||
IsSwitchingInterface = true;
|
||||
ModeSwitchProgressWrapper("First rebooting phone to Flash mode...", null);
|
||||
LogFile.Log("First rebooting phone to Flash mode (then attempt Qualcomm Download mode", LogType.FileAndConsole);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<string, object> Params = new Dictionary<string, object>();
|
||||
Params.Add("DeviceMode", DeviceMode);
|
||||
Params.Add("ResetMethod", "HwReset");
|
||||
try
|
||||
{
|
||||
((NokiaPhoneModel)CurrentModel).ExecuteJsonMethodAsync("SetDeviceMode", Params);
|
||||
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
ModeSwitchErrorWrapper("Failed to switch to Qualcomm Download mode");
|
||||
IsSwitchingInterface = false;
|
||||
}
|
||||
break;
|
||||
case PhoneInterfaces.Lumia_Flash:
|
||||
case PhoneInterfaces.Lumia_Bootloader:
|
||||
byte[] BootModeFlagCommand = new byte[] { 0x4E, 0x4F, 0x4B, 0x58, 0x46, 0x57, 0x00, 0x55, 0x42, 0x46, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }; // NOKFW UBF
|
||||
byte[] RebootCommand = new byte[] { 0x4E, 0x4F, 0x4B, 0x52 }; // NOKR
|
||||
byte[] RebootCommandResult;
|
||||
IsSwitchingInterface = true;
|
||||
switch (TargetMode)
|
||||
{
|
||||
case null:
|
||||
((NokiaFlashModel)CurrentModel).Shutdown();
|
||||
ModeSwitchProgressWrapper("Shutting down phone...", null);
|
||||
LogFile.Log("Shutting down phone", LogType.FileAndConsole);
|
||||
PhoneNotifier.WaitForRemoval().Wait();
|
||||
ModeSwitchSuccessWrapper();
|
||||
break;
|
||||
case PhoneInterfaces.Lumia_Normal:
|
||||
((NokiaPhoneModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand);
|
||||
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
||||
ModeSwitchProgressWrapper("Rebooting phone to Normal mode...", null);
|
||||
LogFile.Log("Rebooting phone to Normal mode", LogType.FileAndConsole);
|
||||
break;
|
||||
case PhoneInterfaces.Lumia_Bootloader:
|
||||
((NokiaPhoneModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand);
|
||||
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
||||
ModeSwitchProgressWrapper("Rebooting phone to Bootloader mode...", null);
|
||||
LogFile.Log("Rebooting phone to Bootloader mode", LogType.FileAndConsole);
|
||||
break;
|
||||
case PhoneInterfaces.Lumia_Label:
|
||||
BootModeFlagCommand[0x0F] = 0x59;
|
||||
((NokiaPhoneModel)CurrentModel).ExecuteRawMethod(BootModeFlagCommand);
|
||||
((NokiaPhoneModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand);
|
||||
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
||||
ModeSwitchProgressWrapper("Rebooting phone to Label mode...", null);
|
||||
LogFile.Log("Rebooting phone to Label mode", LogType.FileAndConsole);
|
||||
break;
|
||||
case PhoneInterfaces.Lumia_Flash: // attempt to boot from limited flash to full flash
|
||||
byte[] RebootToFlashCommand = new byte[] { 0x4E, 0x4F, 0x4B, 0x53 }; // NOKS
|
||||
((NokiaPhoneModel)CurrentModel).ExecuteRawVoidMethod(RebootToFlashCommand);
|
||||
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
||||
ModeSwitchProgressWrapper("Rebooting phone to Flash mode...", null);
|
||||
LogFile.Log("Rebooting phone to Flash mode", LogType.FileAndConsole);
|
||||
break;
|
||||
case PhoneInterfaces.Lumia_MassStorage:
|
||||
SwitchFromFlashToMassStorageMode();
|
||||
break;
|
||||
case PhoneInterfaces.Qualcomm_Download:
|
||||
byte[] RebootToQualcommDownloadCommand = new byte[] { 0x4E, 0x4F, 0x4B, 0x58, 0x43, 0x42, 0x45 }; // NOKXCBE
|
||||
RebootCommandResult = ((NokiaPhoneModel)CurrentModel).ExecuteRawMethod(RebootToQualcommDownloadCommand);
|
||||
if ((RebootCommandResult != null) && (RebootCommandResult.Length == 4)) // This means fail: NOKU (unknow command)
|
||||
{
|
||||
IsSwitchingInterface = false;
|
||||
ModeSwitchErrorWrapper("Failed to switch to Qualcomm Download mode");
|
||||
}
|
||||
else
|
||||
{
|
||||
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
||||
ModeSwitchProgressWrapper("Rebooting phone to Qualcomm Download mode...", null);
|
||||
LogFile.Log("Rebooting phone to Qualcomm Download mode", LogType.FileAndConsole);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case PhoneInterfaces.Lumia_MassStorage:
|
||||
// TODO: don't know how to switch from Mass Storage to other mode
|
||||
break;
|
||||
case PhoneInterfaces.Qualcomm_Download:
|
||||
// TODO: don't know how to switch from Qualcomm Download mode to other mode
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void NewDeviceArrived(ArrivalEventArgs Args)
|
||||
{
|
||||
PhoneNotifier.NewDeviceArrived -= NewDeviceArrived;
|
||||
|
||||
CurrentModel = (IDisposable)Args.NewModel;
|
||||
CurrentMode = Args.NewInterface;
|
||||
|
||||
if ((CurrentMode == PhoneInterfaces.Lumia_Bootloader) && (TargetMode == PhoneInterfaces.Lumia_Flash))
|
||||
{
|
||||
try
|
||||
{
|
||||
// Going from BootMgr to FlashApp
|
||||
// SwitchToFlashAppContext() will only switch context. Phone will not charge.
|
||||
// ResetPhoneToFlashMode() reboots to real flash app. Phone will charge. Works when in BootMgrApp, not when already in FlashApp.
|
||||
|
||||
((NokiaFlashModel)CurrentModel).ResetPhoneToFlashMode();
|
||||
CurrentMode = PhoneInterfaces.Lumia_Flash;
|
||||
PhoneNotifier.NotifyArrival();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
if (CurrentMode == TargetMode)
|
||||
{
|
||||
if (TargetMode == PhoneInterfaces.Lumia_Bootloader)
|
||||
((NokiaFlashModel)CurrentModel).DisableRebootTimeOut();
|
||||
|
||||
ModeSwitchSuccessWrapper();
|
||||
}
|
||||
else if (!IsSwitching)
|
||||
{
|
||||
StartSwitch();
|
||||
}
|
||||
else if ((CurrentMode == PhoneInterfaces.Lumia_Bootloader) && (TargetMode == PhoneInterfaces.Lumia_Normal))
|
||||
{
|
||||
// Do nothing, because booting to Normal shortly goes into Flash mode too.
|
||||
// Just wait to arrive in Normal mode;
|
||||
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
||||
}
|
||||
else if ((CurrentMode == PhoneInterfaces.Lumia_Flash) && (TargetMode == PhoneInterfaces.Lumia_MassStorage))
|
||||
{
|
||||
SwitchFromFlashToMassStorageMode(Continuation: true);
|
||||
}
|
||||
else if ((CurrentMode == PhoneInterfaces.Lumia_Flash) && (TargetMode == PhoneInterfaces.Qualcomm_Download))
|
||||
{
|
||||
byte[] RebootCommand = new byte[] { 0x4E, 0x4F, 0x4B, 0x52 };
|
||||
byte[] RebootToQualcommDownloadCommand = new byte[] { 0x4E, 0x4F, 0x4B, 0x58, 0x43, 0x42, 0x45 }; // NOKXCBE
|
||||
IsSwitchingInterface = true;
|
||||
LogFile.Log("Sending command for rebooting to Emergency Download mode");
|
||||
byte[] RebootCommandResult = ((NokiaPhoneModel)CurrentModel).ExecuteRawMethod(RebootToQualcommDownloadCommand);
|
||||
if ((RebootCommandResult != null) && (RebootCommandResult.Length >= 8))
|
||||
{
|
||||
int ResultCode = (RebootCommandResult[6] << 8) + RebootCommandResult[7];
|
||||
LogFile.Log("Resultcode: 0x" + ResultCode.ToString("X4"));
|
||||
}
|
||||
if ((RebootCommandResult != null) && (RebootCommandResult.Length == 4)) // This means fail: NOKU (unknow command)
|
||||
{
|
||||
ModeSwitchErrorWrapper("Failed to switch to Qualcomm Download mode");
|
||||
IsSwitchingInterface = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
||||
ModeSwitchProgressWrapper("And now rebooting phone to Qualcomm Download mode...", null);
|
||||
LogFile.Log("Rebooting phone to Qualcomm Download mode");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (TargetMode)
|
||||
{
|
||||
case PhoneInterfaces.Lumia_Normal:
|
||||
ModeSwitchErrorWrapper("Failed to switch to Normal mode");
|
||||
break;
|
||||
case PhoneInterfaces.Lumia_Flash:
|
||||
ModeSwitchErrorWrapper("Failed to switch to Flash mode");
|
||||
break;
|
||||
case PhoneInterfaces.Lumia_Label:
|
||||
ModeSwitchErrorWrapper("Failed to switch to Label mode");
|
||||
break;
|
||||
case PhoneInterfaces.Lumia_MassStorage:
|
||||
ModeSwitchErrorWrapper("Failed to switch to Mass Storage mode");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SwitchFromFlashToMassStorageMode(bool Continuation = false)
|
||||
{
|
||||
string ProgressText;
|
||||
if (Continuation)
|
||||
ProgressText = "And now rebooting phone to Mass Storage mode...";
|
||||
else
|
||||
ProgressText = "Rebooting phone to Mass Storage mode...";
|
||||
|
||||
NokiaFlashModel FlashModel = (NokiaFlashModel)CurrentModel;
|
||||
if (CurrentMode == PhoneInterfaces.Lumia_Bootloader)
|
||||
{
|
||||
try
|
||||
{
|
||||
FlashModel.SwitchToFlashAppContext();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
PhoneInfo Info = FlashModel.ReadPhoneInfo(ExtendedInfo: false);
|
||||
|
||||
MassStorageWarning = null;
|
||||
if (Info.FlashAppProtocolVersionMajor < 2)
|
||||
MassStorageWarning = "Switching to Mass Storage mode should take about 10 seconds. The phone should be unlocked using an Engineering SBL3 to enable Mass Storage mode. When you unlocked the bootloader, but you did not use an Engineering SBL3, an attempt to boot to Mass Storage mode may result in an unresponsive state. Installing drivers for this interface may also cause to hang the PC. So when this switch is taking too long, you should reboot both the PC and the phone. To reboot the phone, you have to perform a soft-reset. Press and hold the volume-down-button and the power-button at the same time for at least 10 seconds. This will trigger a power-cycle and the phone will reboot. Once fully booted, the phone may show strange behavior, like complaining about mail-accounts, showing old text-messages, inability to load https-websites, etc. This is expected behavior, because the time-settings of the phone are incorrect. Just wait a few seconds for the phone to get a data-connection and have the date/time synced. After that the strange behavior will stop automatically and normal operation is resumed.";
|
||||
else
|
||||
{
|
||||
MassStorageWarning = "When the screen of the phone is black for a while, it could be that the phone is already in Mass Storage Mode, but there is no drive-letter assigned. To resolve this issue, open Device Manager and manually assign a drive-letter to the MainOS partition of your phone, or open a command-prompt and type: diskpart automount enable.";
|
||||
if (App.IsPnPEventLogMissing)
|
||||
MassStorageWarning += " It is also possible that the phone is in Mass Storage mode, but the Mass Storage driver on this PC failed. Your PC does not have an eventlog to detect this misbehaviour. But in this case you will see a device with an exclamation mark in Device Manager and then you need to manually reset the phone by pressing and holding the power-button for at least 10 seconds until it vibrates and reboots. After that Windows Phone Internals will revert the changes. After the phone has rebooted to the OS, you can retry to unlock the bootloader.";
|
||||
}
|
||||
|
||||
bool IsOldLumia = (Info.FlashAppProtocolVersionMajor < 2);
|
||||
bool IsNewLumia = (Info.FlashAppProtocolVersionMajor >= 2);
|
||||
bool IsUnlockedNew = false;
|
||||
if (IsNewLumia)
|
||||
{
|
||||
GPT GPT = FlashModel.ReadGPT();
|
||||
IsUnlockedNew = ((GPT.GetPartition("IS_UNLOCKED") != null) || (GPT.GetPartition("BACKUP_EFIESP") != null));
|
||||
}
|
||||
bool IsOriginalEngineeringLumia = ((!Info.SecureFfuEnabled || Info.Authenticated || Info.RdcPresent) && !IsUnlockedNew);
|
||||
|
||||
if (IsOldLumia || IsOriginalEngineeringLumia)
|
||||
{
|
||||
byte[] BootModeFlagCommand = new byte[] { 0x4E, 0x4F, 0x4B, 0x58, 0x46, 0x57, 0x00, 0x55, 0x42, 0x46, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }; // NOKFW UBF
|
||||
byte[] RebootCommand = new byte[] { 0x4E, 0x4F, 0x4B, 0x52 };
|
||||
byte[] RebootToMassStorageCommand = new byte[] { 0x4E, 0x4F, 0x4B, 0x4D }; // NOKM
|
||||
IsSwitchingInterface = true;
|
||||
byte[] RebootCommandResult = ((NokiaPhoneModel)CurrentModel).ExecuteRawMethod(RebootToMassStorageCommand);
|
||||
if ((RebootCommandResult != null) && (RebootCommandResult.Length == 4)) // This means fail: NOKU (unknow command)
|
||||
{
|
||||
BootModeFlagCommand[0x0F] = 0x4D;
|
||||
byte[] BootFlagResult = ((NokiaPhoneModel)CurrentModel).ExecuteRawMethod(BootModeFlagCommand);
|
||||
UInt16 ResultCode = BitConverter.ToUInt16(BootFlagResult, 6);
|
||||
if (ResultCode == 0)
|
||||
{
|
||||
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
||||
((NokiaPhoneModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand);
|
||||
ModeSwitchProgressWrapper(ProgressText, MassStorageWarning);
|
||||
LogFile.Log("Rebooting phone to Mass Storage mode");
|
||||
}
|
||||
else
|
||||
{
|
||||
ModeSwitchErrorWrapper("Failed to switch to Mass Storage mode");
|
||||
IsSwitchingInterface = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
||||
ModeSwitchProgressWrapper(ProgressText, MassStorageWarning);
|
||||
LogFile.Log("Rebooting phone to Mass Storage mode");
|
||||
}
|
||||
}
|
||||
else if (IsUnlockedNew)
|
||||
{
|
||||
new Thread(async () =>
|
||||
{
|
||||
LogFile.BeginAction("SwitchToMassStorageMode");
|
||||
|
||||
try
|
||||
{
|
||||
// Implementation of writing a partition with SecureBoot variable to the phone
|
||||
ModeSwitchProgressWrapper(ProgressText, MassStorageWarning);
|
||||
LogFile.Log("Preparing phone for Mass Storage Mode", LogType.FileAndConsole);
|
||||
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
|
||||
|
||||
// Magic!
|
||||
// The SBMSM resource is a compressed version of a raw NV-variable-partition.
|
||||
// In this partition the SecureBoot variable is disabled and an extra variable is added which triggers Mass Storage Mode on next reboot.
|
||||
// It overwrites the variable in a different NV-partition than where this variable is stored usually.
|
||||
// This normally leads to endless-loops when the NV-variables are enumerated.
|
||||
// But the partition contains an extra hack to break out the endless loops.
|
||||
using (var stream = assembly.GetManifestResourceStream("WPinternals.SBMSM"))
|
||||
{
|
||||
using (DecompressedStream dec = new DecompressedStream(stream))
|
||||
{
|
||||
using (System.IO.MemoryStream SB = new System.IO.MemoryStream()) // Must be a seekable stream!
|
||||
{
|
||||
dec.CopyTo(SB);
|
||||
|
||||
// We don't need to check for the BACKUP_BS_NV partition here,
|
||||
// because the SecureBoot flag is disabled here.
|
||||
// So either the NV was already backupped or already overwritten.
|
||||
|
||||
GPT GPT = FlashModel.ReadGPT();
|
||||
Partition Target = GPT.GetPartition("UEFI_BS_NV");
|
||||
|
||||
// We've been reading the GPT, so we let the phone reset once more to be sure that memory maps are the same
|
||||
WPinternalsStatus LastStatus = WPinternalsStatus.Undefined;
|
||||
List<FlashPart> Parts = new List<FlashPart>();
|
||||
FlashPart Part = new FlashPart();
|
||||
Part.StartSector = (uint)Target.FirstSector;
|
||||
Part.Stream = SB;
|
||||
Parts.Add(Part);
|
||||
await LumiaV2UnlockBootViewModel.LumiaV2CustomFlash(PhoneNotifier, null, false, false, Parts, DoResetFirst: true, ClearFlashingStatusAtEnd: false, ShowProgress: false,
|
||||
SetWorkingStatus: (m, s, v, a, st) =>
|
||||
{
|
||||
if (SetWorkingStatus != null)
|
||||
{
|
||||
if ((st == WPinternalsStatus.Scanning) || (st == WPinternalsStatus.WaitingForManualReset))
|
||||
SetWorkingStatus(m, s, v, a, st);
|
||||
else if ((LastStatus == WPinternalsStatus.Scanning) || (LastStatus == WPinternalsStatus.WaitingForManualReset))
|
||||
SetWorkingStatus(ProgressText, MassStorageWarning);
|
||||
LastStatus = st;
|
||||
}
|
||||
},
|
||||
UpdateWorkingStatus: (m, s, v, st) =>
|
||||
{
|
||||
if (UpdateWorkingStatus != null)
|
||||
{
|
||||
if ((st == WPinternalsStatus.Scanning) || (st == WPinternalsStatus.WaitingForManualReset))
|
||||
UpdateWorkingStatus(m, s, v, st);
|
||||
else if ((LastStatus == WPinternalsStatus.Scanning) || (LastStatus == WPinternalsStatus.WaitingForManualReset))
|
||||
SetWorkingStatus(ProgressText, MassStorageWarning);
|
||||
LastStatus = st;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (PhoneNotifier.CurrentInterface == PhoneInterfaces.Lumia_BadMassStorage)
|
||||
throw new WPinternalsException("Phone is in Mass Storage mode, but the driver on PC failed to start");
|
||||
|
||||
// Wait for bootloader
|
||||
if (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_MassStorage)
|
||||
{
|
||||
LogFile.Log("Waiting for Mass Storage Mode (1)...", LogType.FileOnly);
|
||||
await PhoneNotifier.WaitForArrival();
|
||||
}
|
||||
|
||||
if (PhoneNotifier.CurrentInterface == PhoneInterfaces.Lumia_BadMassStorage)
|
||||
throw new WPinternalsException("Phone is in Mass Storage mode, but the driver on PC failed to start");
|
||||
|
||||
// Wait for mass storage mode
|
||||
if (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_MassStorage)
|
||||
{
|
||||
LogFile.Log("Waiting for Mass Storage Mode (2)...", LogType.FileOnly);
|
||||
await PhoneNotifier.WaitForArrival();
|
||||
}
|
||||
|
||||
if (PhoneNotifier.CurrentInterface == PhoneInterfaces.Lumia_BadMassStorage)
|
||||
throw new WPinternalsException("Phone is in Mass Storage mode, but the driver on PC failed to start");
|
||||
|
||||
MassStorage Storage = null;
|
||||
if (PhoneNotifier.CurrentModel is MassStorage)
|
||||
Storage = (MassStorage)PhoneNotifier.CurrentModel;
|
||||
|
||||
if (Storage == null)
|
||||
ModeSwitchErrorWrapper("Failed to switch to Mass Storage Mode");
|
||||
else
|
||||
ModeSwitchSuccessWrapper();
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
ModeSwitchErrorWrapper(Ex.Message);
|
||||
}
|
||||
|
||||
LogFile.EndAction("SwitchToMassStorageMode");
|
||||
}).Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
ModeSwitchErrorWrapper("Bootloader was not unlocked. First unlock bootloader before you try to switch to Mass Storage Mode.");
|
||||
}
|
||||
}
|
||||
|
||||
internal async static Task<IDisposable> SwitchTo(PhoneNotifierViewModel Notifier, PhoneInterfaces? TargetMode, string RequestedVolumeForMassStorage = "MainOS")
|
||||
{
|
||||
return await SwitchToWithProgress(Notifier, TargetMode, null, RequestedVolumeForMassStorage);
|
||||
}
|
||||
|
||||
internal async static Task<IDisposable> SwitchToWithProgress(PhoneNotifierViewModel Notifier, PhoneInterfaces? TargetMode, ModeSwitchProgressHandler ModeSwitchProgress, string RequestedVolumeForMassStorage = "MainOS")
|
||||
{
|
||||
if (Notifier.CurrentInterface == TargetMode)
|
||||
return Notifier.CurrentModel;
|
||||
|
||||
IDisposable Result = null;
|
||||
string LocalErrorMessage = null;
|
||||
|
||||
AsyncAutoResetEvent Event = new AsyncAutoResetEvent(false);
|
||||
|
||||
SwitchModeViewModel Switch = new SwitchModeViewModel(
|
||||
Notifier,
|
||||
TargetMode,
|
||||
ModeSwitchProgress,
|
||||
(string ErrorMessage) =>
|
||||
{
|
||||
LocalErrorMessage = ErrorMessage;
|
||||
Event.Set();
|
||||
},
|
||||
(IDisposable NewModel, PhoneInterfaces NewInterface) =>
|
||||
{
|
||||
Result = NewModel;
|
||||
Event.Set();
|
||||
}
|
||||
);
|
||||
|
||||
await Event.WaitAsync(Timeout.InfiniteTimeSpan);
|
||||
|
||||
if (LocalErrorMessage != null)
|
||||
throw new WPinternalsException(LocalErrorMessage);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal async static Task<IDisposable> SwitchToWithStatus(PhoneNotifierViewModel Notifier, PhoneInterfaces? TargetMode, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null, string RequestedVolumeForMassStorage = "MainOS")
|
||||
{
|
||||
if (Notifier.CurrentInterface == TargetMode)
|
||||
return Notifier.CurrentModel;
|
||||
|
||||
IDisposable Result = null;
|
||||
string LocalErrorMessage = null;
|
||||
|
||||
AsyncAutoResetEvent Event = new AsyncAutoResetEvent(false);
|
||||
|
||||
SwitchModeViewModel Switch = new SwitchModeViewModel(
|
||||
Notifier,
|
||||
TargetMode,
|
||||
null,
|
||||
(string ErrorMessage) =>
|
||||
{
|
||||
LocalErrorMessage = ErrorMessage;
|
||||
Event.Set();
|
||||
},
|
||||
(IDisposable NewModel, PhoneInterfaces NewInterface) =>
|
||||
{
|
||||
Result = NewModel;
|
||||
Event.Set();
|
||||
}, SetWorkingStatus, UpdateWorkingStatus
|
||||
);
|
||||
|
||||
await Event.WaitAsync(Timeout.InfiniteTimeSpan);
|
||||
|
||||
if (LocalErrorMessage != null)
|
||||
throw new WPinternalsException(LocalErrorMessage);
|
||||
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user