Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions EfsTools/CommandLineOptions/NvItemFormat.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace EfsTools.CommandLineOptions
{
internal enum NvItemFormat
{
Raw,
Hex,
Dec
}
}
17 changes: 17 additions & 0 deletions EfsTools/CommandLineOptions/ReadNvItemOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using CommandLine;

namespace EfsTools.CommandLineOptions
{
[Verb("readNvItem", HelpText = "Read single NV item.")]
internal class ReadNvItemOptions
{
[Option('i', "itemId", Required = true, HelpText = "NV item ID.")]
public ushort ItemId { get; set; }

[Option('o', "outComputerFilePath", Required = false, HelpText = "Output computer file path. If not specified, output will be sent to the console.")]
public string OutComputerFilePath { get; set; }

[Option('f', "format", Required = false, Default = NvItemFormat.Raw, HelpText = "Output format (Raw, Hex, Dec). Default is Raw. 'Raw' format is not supported for console output and will default to 'Hex'.")]
public NvItemFormat Format { get; set; }
}
}
20 changes: 20 additions & 0 deletions EfsTools/CommandLineOptions/WriteNvItemOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using CommandLine;

namespace EfsTools.CommandLineOptions
{
[Verb("writeNvItem", HelpText = "Write single NV item from a file or a string payload.")]
internal class WriteNvItemOptions
{
[Option('i', "itemId", Required = true, HelpText = "NV item ID.")]
public ushort ItemId { get; set; }

[Option('c', "inComputerFilePath", Required = false, HelpText = "Input computer file path.", SetName = "file")]
public string InComputerFilePath { get; set; }

[Option('p', "payload", Required = false, HelpText = "Input payload string (for Hex or Dec formats).", SetName = "payload")]
public string Payload { get; set; }

[Option('f', "format", Required = false, Default = NvItemFormat.Raw, HelpText = "Input format (Raw, Hex, Dec). Default is Raw. 'Raw' format is not supported for payload input.")]
public NvItemFormat Format { get; set; }
}
}
134 changes: 134 additions & 0 deletions EfsTools/EfsTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Reflection;
using System.Text;
using System.Threading;
using EfsTools.CommandLineOptions;
using EfsTools.Items;
using EfsTools.Mbn;
using EfsTools.Qualcomm;
Expand Down Expand Up @@ -365,6 +366,139 @@ public void EfsFixFileNames(string efsPath)
}
}

public void ReadNvItem(ushort itemId, string computerPath, NvItemFormat format)
{
using (var manager = OpenQcdmManager())
{
var data = manager.Nv.Read(itemId);
if (data == null || data.Length == 0)
{
_logger.LogInfo($"NV item {itemId} could not be read or is empty.");
return;
}

if (string.IsNullOrEmpty(computerPath))
{
var outputFormat = format;
if (format == NvItemFormat.Raw)
{
_logger.LogWarning("Raw format cannot be written to the console. Defaulting to Hex.");
outputFormat = NvItemFormat.Hex;
}

string output;
switch (outputFormat)
{
case NvItemFormat.Hex:
output = BitConverter.ToString(data).Replace("-", " ");
break;
case NvItemFormat.Dec:
output = string.Join(" ", data.Select(b => b.ToString()));
break;
default:
throw new InvalidOperationException("Unsupported format for console output.");
}
_logger.LogInfo($"NV item {itemId} [{outputFormat}]:");
_logger.LogInfo(output);
}
else
{
switch (format)
{
case NvItemFormat.Raw:
File.WriteAllBytes(computerPath, data);
break;
case NvItemFormat.Hex:
File.WriteAllText(computerPath, BitConverter.ToString(data).Replace("-", " "));
break;
case NvItemFormat.Dec:
File.WriteAllText(computerPath, string.Join(" ", data.Select(b => b.ToString())));
break;
}
_logger.LogInfo($"NV item {itemId} was read and saved to '{computerPath}' in {format} format.");
}
}
}

public void WriteNvItem(ushort itemId, string computerPath, string payload, NvItemFormat format)
{
if (string.IsNullOrEmpty(computerPath) && string.IsNullOrEmpty(payload))
{
throw new ArgumentException("Either a file path or a payload must be provided.");
}

if (!string.IsNullOrEmpty(computerPath) && !string.IsNullOrEmpty(payload))
{
throw new ArgumentException("Both file path and payload cannot be provided simultaneously.");
}

if (!string.IsNullOrEmpty(payload) && format == NvItemFormat.Raw)
{
throw new ArgumentException("Raw format is not supported for payload input. Please use Hex or Dec.");
}

byte[] dataToWrite;

try
{
if (!string.IsNullOrEmpty(payload))
{
switch (format)
{
case NvItemFormat.Hex:
dataToWrite = ParseUtils.ParseHexString(payload);
break;
case NvItemFormat.Dec:
dataToWrite = ParseUtils.ParseDecString(payload);
break;
default: // Raw is already handled
throw new InvalidOperationException("Unsupported format for payload input.");
}
}
else
{
if (!File.Exists(computerPath))
{
_logger.LogError($"File not found: {computerPath}");
return;
}

switch (format)
{
case NvItemFormat.Raw:
dataToWrite = File.ReadAllBytes(computerPath);
break;
case NvItemFormat.Hex:
{
var hexString = File.ReadAllText(computerPath);
dataToWrite = ParseUtils.ParseHexString(hexString);
break;
}
case NvItemFormat.Dec:
{
var decString = File.ReadAllText(computerPath);
dataToWrite = ParseUtils.ParseDecString(decString);
break;
}
default:
throw new InvalidOperationException("Unsupported format for file input.");
}
}
}
catch (Exception ex)
{
_logger.LogError($"Failed to parse input data: {ex.Message}");
return;
}

using (var manager = OpenQcdmManager())
{
manager.Nv.Write(itemId, dataToWrite);
var source = string.IsNullOrEmpty(computerPath) ? "payload" : $"'{computerPath}'";
_logger.LogInfo($"NV item {itemId} was written from {source} using {format} format.");
}
}

public void ExtractMbn(string inputMbnFilePath, string outputComputerDirectoryPath, bool noExtraData)
{
_logger.LogInfo(Strings.QcdmExtractingMbnFileFormat, inputMbnFilePath);
Expand Down
6 changes: 6 additions & 0 deletions EfsTools/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ private static void Main(string[] args)
typeof(GetModemConfigOptions),
typeof(SetModemConfigOptions),
typeof(ExtractMbnOptions),
typeof(ReadNvItemOptions),
typeof(WriteNvItemOptions),
typeof(StartWebDavServerOptions),
typeof(GetLogsOptions)
);
Expand Down Expand Up @@ -78,6 +80,10 @@ private static void Main(string[] args)
tools.ExtractMbn(opts.InputMbnFilePath, opts.OutputComputerDirectoryPath, opts.NoExtraData));
cmd.WithParsed<GetLogsOptions>(opts =>
tools.GetLog(opts.MessageMask, opts.LogMask, opts.Verbose));
cmd.WithParsed<ReadNvItemOptions>(opts =>
tools.ReadNvItem(opts.ItemId, opts.OutComputerFilePath, opts.Format));
cmd.WithParsed<WriteNvItemOptions>(opts =>
tools.WriteNvItem(opts.ItemId, opts.InComputerFilePath, opts.Payload, opts.Format));
cmd.WithParsed<StartWebDavServerOptions>(opts =>
tools.StartWebDavServer(opts.Port, opts.LogLevel, opts.ReadOnly != 0));
cmd.WithNotParsed(errors => { });
Expand Down
30 changes: 30 additions & 0 deletions EfsTools/Utils/ParseUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;

namespace EfsTools.Utils
{
internal static class ParseUtils
{
public static byte[] ParseHexString(string hex)
{
hex = Regex.Replace(hex, @"\s+", "");
if (hex.Length % 2 != 0)
{
throw new ArgumentException("Hex string must have an even number of characters.");
}

return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}

public static byte[] ParseDecString(string dec)
{
return dec.Split(new[] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(byte.Parse)
.ToArray();
}
}
}