Created
April 25, 2020 13:28
-
-
Save tslater2006/b7b976a53b48bad3cdd4d357af8c2dd5 to your computer and use it in GitHub Desktop.
ESP32 NVS Parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using System.IO; | |
| using System.Linq; | |
| using System.Text; | |
| namespace esp32_flash_to_elf | |
| { | |
| public class ESP32NVSKeyPair | |
| { | |
| public string Namespace; | |
| public string Key; | |
| public string Value; | |
| public ESP32NVSType Type; | |
| } | |
| public class ESP32NVSPartition | |
| { | |
| List<ESP32NVSPage> Pages = new List<ESP32NVSPage>(); | |
| public ESP32NVSPartition(byte[] data) | |
| { | |
| MemoryStream ms = new MemoryStream(data); | |
| while (ms.Position < ms.Length - 1) | |
| { | |
| byte[] pageData = new byte[4096]; | |
| ms.Read(pageData); | |
| Pages.Add(new ESP32NVSPage(pageData)); | |
| } | |
| } | |
| public ESP32NVSPage GetPageBySequence(uint sequence) | |
| { | |
| return Pages.Where(p => p.Header.Sequence == sequence).FirstOrDefault(); | |
| } | |
| public void SaveToCSV(string filepath) | |
| { | |
| var pairs = GetValuePairs(); | |
| StreamWriter sw = File.CreateText(filepath); | |
| sw.WriteLine("key,type,encoding,value"); | |
| List<ESP32NVSKeyPair> test = pairs.Where(p => p.Namespace == "diagnostics" && p.Key == "usage").ToList(); | |
| List<string> distinctNamespaces = pairs.Select(p => p.Namespace).Distinct().ToList(); | |
| foreach (var space in distinctNamespaces) | |
| { | |
| sw.Write(space); | |
| sw.Write(','); | |
| sw.WriteLine("namespace,,"); | |
| foreach (var item in pairs.Where(p => p.Namespace == space)) | |
| { | |
| sw.Write(item.Key); | |
| sw.Write(",data,"); | |
| switch (item.Type) | |
| { | |
| case ESP32NVSType.BLOB: | |
| sw.Write("base64,"); | |
| break; | |
| case ESP32NVSType.U8: | |
| sw.Write("u8,"); | |
| break; | |
| case ESP32NVSType.I8: | |
| sw.Write("i8,"); | |
| break; | |
| case ESP32NVSType.U16: | |
| sw.Write("u16,"); | |
| break; | |
| case ESP32NVSType.I16: | |
| sw.Write("i16,"); | |
| break; | |
| case ESP32NVSType.U32: | |
| sw.Write("u32,"); | |
| break; | |
| case ESP32NVSType.I32: | |
| sw.Write("i32,"); | |
| break; | |
| case ESP32NVSType.SZ: | |
| sw.Write("string,"); | |
| break; | |
| } | |
| sw.WriteLine(item.Value); | |
| } | |
| } | |
| sw.Close(); | |
| } | |
| public List<ESP32NVSKeyPair> GetValuePairs() | |
| { | |
| List<ESP32NVSKeyPair> pairs = new List<ESP32NVSKeyPair>(); | |
| var allWrittenEntries = Pages.SelectMany(p => p.Entries.Where(e => e.State == ESP32NVSEntryState.WRITTEN)).ToList(); | |
| Dictionary<byte, string> Namespaces = new Dictionary<byte, string>(); | |
| for (var x = 0; x < Pages.Count; x++) | |
| { | |
| for (var y = 0; y < Pages[x].Entries.Count; y++) | |
| { | |
| var e = Pages[x].Entries[y]; | |
| if (e.Namespace == 0 && e.Type != ESP32NVSType.ANY) | |
| { | |
| var namespaceIndex = e.Data[0]; | |
| var namespaceString = UTF8Encoding.UTF8.GetString(e.Key).Trim('\0'); | |
| Namespaces.Add(namespaceIndex, namespaceString); | |
| } | |
| } | |
| } | |
| for (var x = 0; x < Pages.Count; x++) | |
| { | |
| for (var y = 0; y < Pages[x].Entries.Count; y++) | |
| { | |
| var e = Pages[x].Entries[y]; | |
| if (e.State != ESP32NVSEntryState.WRITTEN) | |
| { | |
| continue; | |
| } | |
| if (e.Namespace != 0 && e.Type != ESP32NVSType.ANY) | |
| { | |
| ESP32NVSKeyPair kvp = new ESP32NVSKeyPair(); | |
| kvp.Key = UTF8Encoding.UTF8.GetString(e.Key).Trim('\0'); | |
| if (kvp.Key.Equals("usage")) | |
| { | |
| //Debugger.Break(); | |
| } | |
| kvp.Type = e.Type; | |
| kvp.Namespace = Namespaces[e.Namespace]; | |
| switch (e.Type) | |
| { | |
| case ESP32NVSType.U8: | |
| kvp.Value = e.Data[0].ToString(); | |
| break; | |
| case ESP32NVSType.I8: | |
| kvp.Value = ((sbyte)(e.Data[0])).ToString(); | |
| break; | |
| case ESP32NVSType.U16: | |
| kvp.Value = BitConverter.ToUInt16(e.Data).ToString(); | |
| break; | |
| case ESP32NVSType.I16: | |
| kvp.Value = BitConverter.ToInt16(e.Data).ToString(); | |
| break; | |
| case ESP32NVSType.U32: | |
| kvp.Value = BitConverter.ToUInt32(e.Data).ToString(); | |
| break; | |
| case ESP32NVSType.I32: | |
| kvp.Value = BitConverter.ToInt32(e.Data).ToString(); | |
| break; | |
| case ESP32NVSType.BLOB: | |
| kvp.Value = Convert.ToBase64String(e.Data); | |
| break; | |
| case ESP32NVSType.SZ: | |
| kvp.Value = UTF8Encoding.UTF8.GetString(e.Data).Trim('\0'); | |
| break; | |
| default: | |
| break; | |
| } | |
| pairs.Add(kvp); | |
| } | |
| } | |
| } | |
| return pairs; | |
| } | |
| } | |
| public enum ESP32NVSPageState : uint | |
| { | |
| ACTIVE = 0xFFFFFFFE, FULL = 0xFFFFFFFC | |
| } | |
| public class ESP32NVSPageHeader | |
| { | |
| public ESP32NVSPageState State; | |
| public uint Sequence; | |
| public ESP32NVSVersion Version; | |
| public byte[] Unused; | |
| public uint CRC32; | |
| public byte[] EntryStateBitmap; | |
| public List<ESP32NVSEntryState> EntryStates = new List<ESP32NVSEntryState>(); | |
| } | |
| public class ESP32NVSPageEntry | |
| { | |
| public ESP32NVSEntryState State; | |
| public byte Namespace; | |
| public ESP32NVSType Type; | |
| public byte Span; | |
| public byte ChunkIndex; | |
| public uint CRC32; | |
| public byte[] Key; | |
| public byte[] Data; | |
| } | |
| public enum ESP32NVSEntryState : byte | |
| { | |
| ERASED = 0x0, WRITTEN = 0x2, EMPTY = 0x3 | |
| } | |
| public enum ESP32NVSType : byte | |
| { | |
| U8 = 0x01, | |
| I8 = 0x11, | |
| U16 = 0x02, | |
| I16 = 0x12, | |
| U32 = 0x04, | |
| I32 = 0x14, | |
| U64 = 0x08, | |
| I64 = 0x18, | |
| SZ = 0x21, | |
| BLOB = 0x41, | |
| BLOB_DATA = 0x42, | |
| BLOB_IDX = 0x48, | |
| ANY = 0xFF | |
| } | |
| public enum ESP32NVSVersion : byte | |
| { | |
| VERSION1 = 0xFF, | |
| VERSION2 = 0xFE | |
| } | |
| public class ESP32NVSPage | |
| { | |
| public ESP32NVSPageHeader Header = new ESP32NVSPageHeader(); | |
| public List<ESP32NVSPageEntry> Entries = new List<ESP32NVSPageEntry>(); | |
| public ESP32NVSPage(byte[] data) | |
| { | |
| MemoryStream ms = new MemoryStream(data); | |
| BinaryReader br = new BinaryReader(ms); | |
| Header.State = (ESP32NVSPageState)BitConverter.ToUInt32(br.ReadBytes(4)); | |
| Header.Sequence = BitConverter.ToUInt32(br.ReadBytes(4)); | |
| Header.Version = (ESP32NVSVersion)br.ReadByte(); | |
| Header.Unused = br.ReadBytes(19); | |
| Header.CRC32 = BitConverter.ToUInt32(br.ReadBytes(4)); | |
| Header.EntryStateBitmap = br.ReadBytes(32); | |
| for (var entry_num = 0; entry_num < 126; entry_num++) | |
| { | |
| var bitnum = entry_num * 2; | |
| var byte_index = (int)(bitnum / 8); | |
| var temp = Header.EntryStateBitmap[byte_index]; | |
| temp = (byte)(temp >> (6 - (bitnum % 8))); | |
| temp = (byte)(temp & 3); | |
| Header.EntryStates.Add((ESP32NVSEntryState)(temp)); | |
| } | |
| for (var x = 0; x < 126; x++) | |
| { | |
| /* 126 entries per page... */ | |
| var entry = new ESP32NVSPageEntry(); | |
| entry.Namespace = br.ReadByte(); | |
| entry.Type = (ESP32NVSType)br.ReadByte(); | |
| entry.Span = br.ReadByte(); | |
| entry.ChunkIndex = br.ReadByte(); | |
| entry.CRC32 = BitConverter.ToUInt32(br.ReadBytes(4)); | |
| entry.Key = br.ReadBytes(16); | |
| entry.Data = br.ReadBytes(8); | |
| entry.State = Header.EntryStates[x]; | |
| if (entry.Type == ESP32NVSType.BLOB || entry.Type == ESP32NVSType.SZ) | |
| { | |
| var dataSize = BitConverter.ToUInt16(entry.Data, 0); | |
| var dataCRC32 = BitConverter.ToUInt32(entry.Data, 4); | |
| MemoryStream msd = new MemoryStream(); | |
| for (var y = 1; y < entry.Span; y++) | |
| { | |
| msd.Write(br.ReadBytes(32), 0, 32); | |
| x++; | |
| } | |
| msd.Seek(0, SeekOrigin.Begin); | |
| var br2 = new BinaryReader(msd); | |
| entry.Data = br2.ReadBytes(dataSize); | |
| br2.Close(); | |
| } | |
| Entries.Add(entry); | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment