diff --git a/UEFIExtract/CMakeLists.txt b/UEFIExtract/CMakeLists.txt
index 9077669..4db5457 100644
--- a/UEFIExtract/CMakeLists.txt
+++ b/UEFIExtract/CMakeLists.txt
@@ -33,6 +33,7 @@ SET(PROJECT_SOURCES
  ../common/ustring.cpp
  ../common/bstrlib/bstrlib.c
  ../common/bstrlib/bstrwrap.cpp
+ ../common/generated/ami_nvar.cpp
  ../common/generated/intel_acbp_v1.cpp
  ../common/generated/intel_acbp_v2.cpp
  ../common/generated/intel_keym_v1.cpp
diff --git a/UEFIFind/CMakeLists.txt b/UEFIFind/CMakeLists.txt
index b1b39e2..b508422 100644
--- a/UEFIFind/CMakeLists.txt
+++ b/UEFIFind/CMakeLists.txt
@@ -30,6 +30,7 @@ SET(PROJECT_SOURCES
  ../common/ustring.cpp
  ../common/bstrlib/bstrlib.c
  ../common/bstrlib/bstrwrap.cpp
+ ../common/generated/ami_nvar.cpp
  ../common/generated/intel_acbp_v1.cpp
  ../common/generated/intel_acbp_v2.cpp
  ../common/generated/intel_keym_v1.cpp
diff --git a/UEFITool/CMakeLists.txt b/UEFITool/CMakeLists.txt
index 9c2cfd0..8df2aa3 100644
--- a/UEFITool/CMakeLists.txt
+++ b/UEFITool/CMakeLists.txt
@@ -70,6 +70,7 @@ SET(PROJECT_SOURCES
  ../common/digest/sha256.c 
  ../common/digest/sha512.c 
  ../common/digest/sm3.c 
+ ../common/generated/ami_nvar.cpp
  ../common/generated/intel_acbp_v1.cpp 
  ../common/generated/intel_acbp_v2.cpp 
  ../common/generated/intel_keym_v1.cpp 
diff --git a/UEFITool/uefitool.cpp b/UEFITool/uefitool.cpp
index e335596..3ede8ee 100644
--- a/UEFITool/uefitool.cpp
+++ b/UEFITool/uefitool.cpp
@@ -243,6 +243,7 @@ void UEFITool::populateUi(const QModelIndex &current)
                                      || type == Types::EvsaStore
                                      || type == Types::FtwStore
                                      || type == Types::FlashMapStore
+                                     || type == Types::NvarGuidStore
                                      || type == Types::CmdbStore
                                      || type == Types::FptStore
                                      || type == Types::BpdtStore
@@ -407,9 +408,8 @@ void UEFITool::goToData()
         
         UByteArray rdata = model->parsingData(index);
         const NVAR_ENTRY_PARSING_DATA* pdata = (const NVAR_ENTRY_PARSING_DATA*)rdata.constData();
-        UINT32 lastVariableFlag = pdata->emptyByte ? 0xFFFFFF : 0;
         UINT32 offset = model->offset(index);
-        if (pdata->next == lastVariableFlag) {
+        if (pdata->next == 0xFFFFFF) {
             ui->structureTreeView->scrollTo(index, QAbstractItemView::PositionAtCenter);
             ui->structureTreeView->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows | QItemSelectionModel::Clear);
         }
@@ -783,13 +783,7 @@ void UEFITool::showParserMessages()
     
     std::vector<std::pair<QString, QModelIndex> > messages = ffsParser->getMessages();
     
-#if QT_VERSION_MAJOR < 6
-    std::pair<QString, QModelIndex> msg;
-    foreach (msg, messages)
-#else
-    for (const auto &msg : messages)
-#endif
-    {
+    for (const auto &msg : messages) {
         QListWidgetItem* item = new QListWidgetItem(msg.first, NULL, 0);
         item->setData(Qt::UserRole, QByteArray((const char*)&msg.second, sizeof(msg.second)));
         ui->parserMessagesListWidget->addItem(item);
@@ -807,13 +801,7 @@ void UEFITool::showFinderMessages()
     
     std::vector<std::pair<QString, QModelIndex> > messages = ffsFinder->getMessages();
     
-#if QT_VERSION_MAJOR < 6
-    std::pair<QString, QModelIndex> msg;
-    foreach (msg, messages)
-#else
-    for (const auto &msg : messages)
-#endif
-    {
+    for (const auto &msg : messages) {
         QListWidgetItem* item = new QListWidgetItem(msg.first, NULL, 0);
         item->setData(Qt::UserRole, QByteArray((const char*)&msg.second, sizeof(msg.second)));;
         ui->finderMessagesListWidget->addItem(item);
@@ -832,13 +820,7 @@ void UEFITool::showBuilderMessages()
     
     std::vector<std::pair<QString, QModelIndex> > messages = ffsBuilder->getMessages();
     
-#if QT_VERSION_MAJOR < 6
-    std::pair<QString, QModelIndex> msg;
-    foreach (msg, messages)
-#else
-    for (const auto &msg : messages)
-#endif
-    {
+    for (const auto &msg : messages) {
         QListWidgetItem* item = new QListWidgetItem(msg.first, NULL, 0);
         item->setData(Qt::UserRole, QByteArray((const char*)&msg.second, sizeof(msg.second)));
         ui->builderMessagesListWidget->addItem(item);
@@ -891,8 +873,7 @@ void UEFITool::contextMenuEvent(QContextMenuEvent* event)
         return;
     }
     
-    switch (model->type(index))
-    {
+    switch (model->type(index)) {
         case Types::Capsule:        ui->menuCapsuleActions->exec(event->globalPos());      break;
         case Types::Image:          ui->menuImageActions->exec(event->globalPos());        break;
         case Types::Region:         ui->menuRegionActions->exec(event->globalPos());       break;
@@ -907,6 +888,7 @@ void UEFITool::contextMenuEvent(QContextMenuEvent* event)
         case Types::EvsaStore:
         case Types::FtwStore:
         case Types::FlashMapStore:
+        case Types::NvarGuidStore:
         case Types::CmdbStore:
         case Types::FptStore:
         case Types::CpdStore:
diff --git a/UEFITool/uefitool.pro b/UEFITool/uefitool.pro
index 2dc54a4..cd890c6 100644
--- a/UEFITool/uefitool.pro
+++ b/UEFITool/uefitool.pro
@@ -46,9 +46,11 @@ HEADERS += uefitool.h \
  ../common/Tiano/EfiTianoCompress.h \
  ../common/ustring.h \
  ../common/ubytearray.h \
+ ../common/umemstream.h \
  ../common/digest/sha1.h \
  ../common/digest/sha2.h \
  ../common/digest/sm3.h \
+ ../common/generated/ami_nvar.h \
  ../common/generated/intel_acbp_v1.h \
  ../common/generated/intel_acbp_v2.h \
  ../common/generated/intel_keym_v1.h \
@@ -103,6 +105,7 @@ SOURCES += uefitool_main.cpp \
  ../common/digest/sha256.c \
  ../common/digest/sha512.c \
  ../common/digest/sm3.c \
+ ../common/generated/ami_nvar.cpp \
  ../common/generated/intel_acbp_v1.cpp \
  ../common/generated/intel_acbp_v2.cpp \
  ../common/generated/intel_keym_v1.cpp \
diff --git a/common/basetypes.h b/common/basetypes.h
index 9e93f27..99bbb0e 100644
--- a/common/basetypes.h
+++ b/common/basetypes.h
@@ -18,6 +18,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 #include <stdint.h>
 #include <stddef.h>
 
+// TODO: improve
 typedef size_t USTATUS;
 #define U_SUCCESS                         0
 #define U_INVALID_PARAMETER               1
@@ -76,6 +77,7 @@ typedef size_t USTATUS;
 #define U_PATCH_OFFSET_OUT_OF_BOUNDS      54
 #define U_INVALID_SYMBOL                  55
 #define U_ZLIB_DECOMPRESSION_FAILED       56
+#define U_INVALID_STORE                   57
 
 #define U_INVALID_MANIFEST                251
 #define U_UNKNOWN_MANIFEST_HEADER_VERSION 252
diff --git a/common/fitparser.cpp b/common/fitparser.cpp
index 8852b5e..322a381 100644
--- a/common/fitparser.cpp
+++ b/common/fitparser.cpp
@@ -21,7 +21,7 @@
 #include "utility.h"
 #include "digest/sha2.h"
 
-#include <sstream>
+#include "umemstream.h"
 #include "kaitai/kaitaistream.h"
 #include "generated/intel_acbp_v1.h"
 #include "generated/intel_acbp_v2.h"
@@ -29,45 +29,6 @@
 #include "generated/intel_keym_v2.h"
 #include "generated/intel_acm.h"
 
-// TODO: put into separate H/CPP when we start using Kaitai for other parsers
-// TODO: this implementation is certainly not a valid replacement to std::stringstream
-// TODO: because it only supports getting through the buffer once
-// TODO: however, we already do it this way, so it's enough for practical purposes of this file
-class membuf : public std::streambuf {
-public:
-    membuf(const char *p, size_t l) {
-        setg((char*)p, (char*)p, (char*)p + l);
-    }
-
-    pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which = std::ios_base::in) override
-    {
-        (void)which;
-        if (dir == std::ios_base::cur)
-            gbump((int)off);
-        else if (dir == std::ios_base::end)
-            setg(eback(), egptr() + off, egptr());
-        else if (dir == std::ios_base::beg)
-            setg(eback(), eback() + off, egptr());
-        return gptr() - eback();
-    }
-
-    pos_type seekpos(pos_type sp, std::ios_base::openmode which) override
-    {
-        return seekoff(sp - pos_type(off_type(0)), std::ios_base::beg, which);
-    }
-};
-
-class memstream : public std::istream {
-public:
-  memstream(const char *p, size_t l) : std::istream(&buffer_),
-    buffer_(p, l) {
-    rdbuf(&buffer_);
-  }
-
-private:
-  membuf buffer_;
-};
-
 USTATUS FitParser::parseFit(const UModelIndex & index)
 {
     // Reset parser state
@@ -318,7 +279,7 @@ USTATUS FitParser::parseFitEntryMicrocode(const UByteArray & microcode, const UI
 USTATUS FitParser::parseFitEntryAcm(const UByteArray & acm, const UINT32 localOffset, const UModelIndex & parent, UString & info, UINT32 &realSize)
 {
     try {
-        memstream is(acm.constData(), acm.size());
+        umemstream is(acm.constData(), acm.size());
         is.seekg(localOffset, is.beg);
         kaitai::kstream ks(&is);
         intel_acm_t parsed(&ks);
@@ -436,7 +397,7 @@ USTATUS FitParser::parseFitEntryBootGuardKeyManifest(const UByteArray & keyManif
     
     // v1
     try {
-        memstream is(keyManifest.constData(), keyManifest.size());
+        umemstream is(keyManifest.constData(), keyManifest.size());
         is.seekg(localOffset, is.beg);
         kaitai::kstream ks(&is);
         intel_keym_v1_t parsed(&ks);
@@ -539,7 +500,7 @@ USTATUS FitParser::parseFitEntryBootGuardKeyManifest(const UByteArray & keyManif
     
     // v2
     try {
-        memstream is(keyManifest.constData(), keyManifest.size());
+        umemstream is(keyManifest.constData(), keyManifest.size());
         is.seekg(localOffset, is.beg);
         kaitai::kstream ks(&is);
         intel_keym_v2_t parsed(&ks);
@@ -671,7 +632,7 @@ USTATUS FitParser::parseFitEntryBootGuardBootPolicy(const UByteArray & bootPolic
     
     // v1
     try {
-        memstream is(bootPolicy.constData(), bootPolicy.size());
+        umemstream is(bootPolicy.constData(), bootPolicy.size());
         is.seekg(localOffset, is.beg);
         kaitai::kstream ks(&is);
         intel_acbp_v1_t parsed(&ks);
@@ -935,7 +896,7 @@ USTATUS FitParser::parseFitEntryBootGuardBootPolicy(const UByteArray & bootPolic
     
     // v2
     try {
-        memstream is(bootPolicy.constData(), bootPolicy.size());
+        umemstream is(bootPolicy.constData(), bootPolicy.size());
         is.seekg(localOffset, is.beg);
         kaitai::kstream ks(&is);
         intel_acbp_v2_t parsed(&ks); // This already verified the version to be >= 0x20
diff --git a/common/generated/ami_nvar.cpp b/common/generated/ami_nvar.cpp
new file mode 100644
index 0000000..28441a2
--- /dev/null
+++ b/common/generated/ami_nvar.cpp
@@ -0,0 +1,442 @@
+// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+
+#include "ami_nvar.h"
+#include "../kaitai/exceptions.h"
+
+ami_nvar_t::ami_nvar_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, ami_nvar_t* p__root) : kaitai::kstruct(p__io) {
+    m__parent = p__parent;
+    m__root = this; (void)p__root;
+    m_entries = 0;
+
+    try {
+        _read();
+    } catch(...) {
+        _clean_up();
+        throw;
+    }
+}
+
+void ami_nvar_t::_read() {
+    m_entries = new std::vector<nvar_entry_t*>();
+    {
+        int i = 0;
+        nvar_entry_t* _;
+        do {
+            _ = new nvar_entry_t(m__io, this, m__root);
+            m_entries->push_back(_);
+            i++;
+        } while (!( ((_->signature_first() != 78) || (_io()->is_eof())) ));
+    }
+}
+
+ami_nvar_t::~ami_nvar_t() {
+    _clean_up();
+}
+
+void ami_nvar_t::_clean_up() {
+    if (m_entries) {
+        for (std::vector<nvar_entry_t*>::iterator it = m_entries->begin(); it != m_entries->end(); ++it) {
+            delete *it;
+        }
+        delete m_entries; m_entries = 0;
+    }
+}
+
+ami_nvar_t::nvar_attributes_t::nvar_attributes_t(kaitai::kstream* p__io, ami_nvar_t::nvar_entry_t* p__parent, ami_nvar_t* p__root) : kaitai::kstruct(p__io) {
+    m__parent = p__parent;
+    m__root = p__root;
+
+    try {
+        _read();
+    } catch(...) {
+        _clean_up();
+        throw;
+    }
+}
+
+void ami_nvar_t::nvar_attributes_t::_read() {
+    m_valid = m__io->read_bits_int_be(1);
+    m_auth_write = m__io->read_bits_int_be(1);
+    m_hw_error_record = m__io->read_bits_int_be(1);
+    m_extended_header = m__io->read_bits_int_be(1);
+    m_data_only = m__io->read_bits_int_be(1);
+    m_local_guid = m__io->read_bits_int_be(1);
+    m_ascii_name = m__io->read_bits_int_be(1);
+    m_runtime = m__io->read_bits_int_be(1);
+}
+
+ami_nvar_t::nvar_attributes_t::~nvar_attributes_t() {
+    _clean_up();
+}
+
+void ami_nvar_t::nvar_attributes_t::_clean_up() {
+}
+
+ami_nvar_t::ucs2_string_t::ucs2_string_t(kaitai::kstream* p__io, ami_nvar_t::nvar_entry_body_t* p__parent, ami_nvar_t* p__root) : kaitai::kstruct(p__io) {
+    m__parent = p__parent;
+    m__root = p__root;
+    m_ucs2_chars = 0;
+
+    try {
+        _read();
+    } catch(...) {
+        _clean_up();
+        throw;
+    }
+}
+
+void ami_nvar_t::ucs2_string_t::_read() {
+    m_ucs2_chars = new std::vector<uint16_t>();
+    {
+        int i = 0;
+        uint16_t _;
+        do {
+            _ = m__io->read_u2le();
+            m_ucs2_chars->push_back(_);
+            i++;
+        } while (!(_ == 0));
+    }
+}
+
+ami_nvar_t::ucs2_string_t::~ucs2_string_t() {
+    _clean_up();
+}
+
+void ami_nvar_t::ucs2_string_t::_clean_up() {
+    if (m_ucs2_chars) {
+        delete m_ucs2_chars; m_ucs2_chars = 0;
+    }
+}
+
+ami_nvar_t::nvar_extended_attributes_t::nvar_extended_attributes_t(kaitai::kstream* p__io, ami_nvar_t::nvar_entry_body_t* p__parent, ami_nvar_t* p__root) : kaitai::kstruct(p__io) {
+    m__parent = p__parent;
+    m__root = p__root;
+
+    try {
+        _read();
+    } catch(...) {
+        _clean_up();
+        throw;
+    }
+}
+
+void ami_nvar_t::nvar_extended_attributes_t::_read() {
+    m_reserved_high = m__io->read_bits_int_be(2);
+    m_time_based_auth = m__io->read_bits_int_be(1);
+    m_auth_write = m__io->read_bits_int_be(1);
+    m_reserved_low = m__io->read_bits_int_be(3);
+    m_checksum = m__io->read_bits_int_be(1);
+}
+
+ami_nvar_t::nvar_extended_attributes_t::~nvar_extended_attributes_t() {
+    _clean_up();
+}
+
+void ami_nvar_t::nvar_extended_attributes_t::_clean_up() {
+}
+
+ami_nvar_t::nvar_entry_t::nvar_entry_t(kaitai::kstream* p__io, ami_nvar_t* p__parent, ami_nvar_t* p__root) : kaitai::kstruct(p__io) {
+    m__parent = p__parent;
+    m__root = p__root;
+    m_attributes = 0;
+    m_body = 0;
+    m__io__raw_body = 0;
+    f_offset = false;
+    f_end_offset = false;
+
+    try {
+        _read();
+    } catch(...) {
+        _clean_up();
+        throw;
+    }
+}
+
+void ami_nvar_t::nvar_entry_t::_read() {
+    n_invoke_offset = true;
+    if (offset() >= 0) {
+        n_invoke_offset = false;
+        m_invoke_offset = m__io->read_bytes(0);
+    }
+    m_signature_first = m__io->read_u1();
+    n_signature_rest = true;
+    if (signature_first() == 78) {
+        n_signature_rest = false;
+        m_signature_rest = m__io->read_bytes(3);
+        if (!(signature_rest() == std::string("\x56\x41\x52", 3))) {
+            throw kaitai::validation_not_equal_error<std::string>(std::string("\x56\x41\x52", 3), signature_rest(), _io(), std::string("/types/nvar_entry/seq/2"));
+        }
+    }
+    n_size = true;
+    if (signature_first() == 78) {
+        n_size = false;
+        m_size = m__io->read_u2le();
+        {
+            uint16_t _ = size();
+            if (!(_ > ((4 + 2) + 4))) {
+                throw kaitai::validation_expr_error<uint16_t>(size(), _io(), std::string("/types/nvar_entry/seq/3"));
+            }
+        }
+    }
+    n_next = true;
+    if (signature_first() == 78) {
+        n_next = false;
+        m_next = m__io->read_bits_int_le(24);
+    }
+    m__io->align_to_byte();
+    n_attributes = true;
+    if (signature_first() == 78) {
+        n_attributes = false;
+        m_attributes = new nvar_attributes_t(m__io, this, m__root);
+    }
+    n_body = true;
+    if (signature_first() == 78) {
+        n_body = false;
+        m__raw_body = m__io->read_bytes((size() - ((4 + 2) + 4)));
+        m__io__raw_body = new kaitai::kstream(m__raw_body);
+        m_body = new nvar_entry_body_t(m__io__raw_body, this, m__root);
+    }
+    n_invoke_end_offset = true;
+    if ( ((signature_first() == 78) && (end_offset() >= 0)) ) {
+        n_invoke_end_offset = false;
+        m_invoke_end_offset = m__io->read_bytes(0);
+    }
+}
+
+ami_nvar_t::nvar_entry_t::~nvar_entry_t() {
+    _clean_up();
+}
+
+void ami_nvar_t::nvar_entry_t::_clean_up() {
+    if (!n_invoke_offset) {
+    }
+    if (!n_signature_rest) {
+    }
+    if (!n_size) {
+    }
+    if (!n_next) {
+    }
+    if (!n_attributes) {
+        if (m_attributes) {
+            delete m_attributes; m_attributes = 0;
+        }
+    }
+    if (!n_body) {
+        if (m__io__raw_body) {
+            delete m__io__raw_body; m__io__raw_body = 0;
+        }
+        if (m_body) {
+            delete m_body; m_body = 0;
+        }
+    }
+    if (!n_invoke_end_offset) {
+    }
+}
+
+int32_t ami_nvar_t::nvar_entry_t::offset() {
+    if (f_offset)
+        return m_offset;
+    m_offset = _io()->pos();
+    f_offset = true;
+    return m_offset;
+}
+
+int32_t ami_nvar_t::nvar_entry_t::end_offset() {
+    if (f_end_offset)
+        return m_end_offset;
+    m_end_offset = _io()->pos();
+    f_end_offset = true;
+    return m_end_offset;
+}
+
+ami_nvar_t::nvar_entry_body_t::nvar_entry_body_t(kaitai::kstream* p__io, ami_nvar_t::nvar_entry_t* p__parent, ami_nvar_t* p__root) : kaitai::kstruct(p__io) {
+    m__parent = p__parent;
+    m__root = p__root;
+    m_ucs2_name = 0;
+    m_extended_header_attributes = 0;
+    f_extended_header_attributes = false;
+    f_data_start_offset = false;
+    f_extended_header_size_field = false;
+    f_extended_header_timestamp = false;
+    f_data_size = false;
+    f_extended_header_checksum = false;
+    f_data_end_offset = false;
+    f_extended_header_size = false;
+    f_extended_header_hash = false;
+
+    try {
+        _read();
+    } catch(...) {
+        _clean_up();
+        throw;
+    }
+}
+
+void ami_nvar_t::nvar_entry_body_t::_read() {
+    n_guid_index = true;
+    if ( ((!(_parent()->attributes()->local_guid())) && (!(_parent()->attributes()->data_only())) && (_parent()->attributes()->valid())) ) {
+        n_guid_index = false;
+        m_guid_index = m__io->read_u1();
+    }
+    n_guid = true;
+    if ( ((_parent()->attributes()->local_guid()) && (!(_parent()->attributes()->data_only())) && (_parent()->attributes()->valid())) ) {
+        n_guid = false;
+        m_guid = m__io->read_bytes(16);
+    }
+    n_ascii_name = true;
+    if ( ((_parent()->attributes()->ascii_name()) && (!(_parent()->attributes()->data_only())) && (_parent()->attributes()->valid())) ) {
+        n_ascii_name = false;
+        m_ascii_name = kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), std::string("ASCII"));
+    }
+    n_ucs2_name = true;
+    if ( ((!(_parent()->attributes()->ascii_name())) && (!(_parent()->attributes()->data_only())) && (_parent()->attributes()->valid())) ) {
+        n_ucs2_name = false;
+        m_ucs2_name = new ucs2_string_t(m__io, this, m__root);
+    }
+    n_invoke_data_start = true;
+    if (data_start_offset() >= 0) {
+        n_invoke_data_start = false;
+        m_invoke_data_start = m__io->read_bytes(0);
+    }
+    m_data = m__io->read_bytes_full();
+}
+
+ami_nvar_t::nvar_entry_body_t::~nvar_entry_body_t() {
+    _clean_up();
+}
+
+void ami_nvar_t::nvar_entry_body_t::_clean_up() {
+    if (!n_guid_index) {
+    }
+    if (!n_guid) {
+    }
+    if (!n_ascii_name) {
+    }
+    if (!n_ucs2_name) {
+        if (m_ucs2_name) {
+            delete m_ucs2_name; m_ucs2_name = 0;
+        }
+    }
+    if (!n_invoke_data_start) {
+    }
+    if (f_extended_header_attributes && !n_extended_header_attributes) {
+        if (m_extended_header_attributes) {
+            delete m_extended_header_attributes; m_extended_header_attributes = 0;
+        }
+    }
+    if (f_extended_header_size_field && !n_extended_header_size_field) {
+    }
+    if (f_extended_header_timestamp && !n_extended_header_timestamp) {
+    }
+    if (f_extended_header_checksum && !n_extended_header_checksum) {
+    }
+    if (f_extended_header_hash && !n_extended_header_hash) {
+    }
+}
+
+ami_nvar_t::nvar_extended_attributes_t* ami_nvar_t::nvar_entry_body_t::extended_header_attributes() {
+    if (f_extended_header_attributes)
+        return m_extended_header_attributes;
+    n_extended_header_attributes = true;
+    if ( ((_parent()->attributes()->valid()) && (_parent()->attributes()->extended_header()) && (extended_header_size() >= (1 + 2))) ) {
+        n_extended_header_attributes = false;
+        std::streampos _pos = m__io->pos();
+        m__io->seek((_io()->pos() - extended_header_size()));
+        m_extended_header_attributes = new nvar_extended_attributes_t(m__io, this, m__root);
+        m__io->seek(_pos);
+        f_extended_header_attributes = true;
+    }
+    return m_extended_header_attributes;
+}
+
+int32_t ami_nvar_t::nvar_entry_body_t::data_start_offset() {
+    if (f_data_start_offset)
+        return m_data_start_offset;
+    m_data_start_offset = _io()->pos();
+    f_data_start_offset = true;
+    return m_data_start_offset;
+}
+
+uint16_t ami_nvar_t::nvar_entry_body_t::extended_header_size_field() {
+    if (f_extended_header_size_field)
+        return m_extended_header_size_field;
+    n_extended_header_size_field = true;
+    if ( ((_parent()->attributes()->valid()) && (_parent()->attributes()->extended_header()) && (_parent()->size() > (((4 + 2) + 4) + 2))) ) {
+        n_extended_header_size_field = false;
+        std::streampos _pos = m__io->pos();
+        m__io->seek((_io()->pos() - 2));
+        m_extended_header_size_field = m__io->read_u2le();
+        m__io->seek(_pos);
+        f_extended_header_size_field = true;
+    }
+    return m_extended_header_size_field;
+}
+
+uint64_t ami_nvar_t::nvar_entry_body_t::extended_header_timestamp() {
+    if (f_extended_header_timestamp)
+        return m_extended_header_timestamp;
+    n_extended_header_timestamp = true;
+    if ( ((_parent()->attributes()->valid()) && (_parent()->attributes()->extended_header()) && (extended_header_size() >= ((1 + 8) + 2)) && (extended_header_attributes()->time_based_auth())) ) {
+        n_extended_header_timestamp = false;
+        std::streampos _pos = m__io->pos();
+        m__io->seek(((_io()->pos() - extended_header_size()) + 1));
+        m_extended_header_timestamp = m__io->read_u8le();
+        m__io->seek(_pos);
+        f_extended_header_timestamp = true;
+    }
+    return m_extended_header_timestamp;
+}
+
+int32_t ami_nvar_t::nvar_entry_body_t::data_size() {
+    if (f_data_size)
+        return m_data_size;
+    m_data_size = ((data_end_offset() - data_start_offset()) - extended_header_size());
+    f_data_size = true;
+    return m_data_size;
+}
+
+uint8_t ami_nvar_t::nvar_entry_body_t::extended_header_checksum() {
+    if (f_extended_header_checksum)
+        return m_extended_header_checksum;
+    n_extended_header_checksum = true;
+    if ( ((_parent()->attributes()->valid()) && (_parent()->attributes()->extended_header()) && (extended_header_size() >= ((1 + 1) + 2)) && (extended_header_attributes()->checksum())) ) {
+        n_extended_header_checksum = false;
+        std::streampos _pos = m__io->pos();
+        m__io->seek(((_io()->pos() - 2) - 1));
+        m_extended_header_checksum = m__io->read_u1();
+        m__io->seek(_pos);
+        f_extended_header_checksum = true;
+    }
+    return m_extended_header_checksum;
+}
+
+int32_t ami_nvar_t::nvar_entry_body_t::data_end_offset() {
+    if (f_data_end_offset)
+        return m_data_end_offset;
+    m_data_end_offset = _io()->pos();
+    f_data_end_offset = true;
+    return m_data_end_offset;
+}
+
+uint16_t ami_nvar_t::nvar_entry_body_t::extended_header_size() {
+    if (f_extended_header_size)
+        return m_extended_header_size;
+    m_extended_header_size = ((_parent()->attributes()->extended_header()) ? (((extended_header_size_field() >= (1 + 2)) ? (extended_header_size_field()) : (0))) : (0));
+    f_extended_header_size = true;
+    return m_extended_header_size;
+}
+
+std::string ami_nvar_t::nvar_entry_body_t::extended_header_hash() {
+    if (f_extended_header_hash)
+        return m_extended_header_hash;
+    n_extended_header_hash = true;
+    if ( ((_parent()->attributes()->valid()) && (_parent()->attributes()->extended_header()) && (extended_header_size() >= (((1 + 8) + 32) + 2)) && (extended_header_attributes()->time_based_auth()) && (!(_parent()->attributes()->data_only()))) ) {
+        n_extended_header_hash = false;
+        std::streampos _pos = m__io->pos();
+        m__io->seek((((_io()->pos() - extended_header_size()) + 1) + 8));
+        m_extended_header_hash = m__io->read_bytes(32);
+        m__io->seek(_pos);
+        f_extended_header_hash = true;
+    }
+    return m_extended_header_hash;
+}
diff --git a/common/generated/ami_nvar.h b/common/generated/ami_nvar.h
new file mode 100644
index 0000000..b372835
--- /dev/null
+++ b/common/generated/ami_nvar.h
@@ -0,0 +1,398 @@
+#ifndef AMI_NVAR_H_
+#define AMI_NVAR_H_
+
+// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+
+#include "../kaitai/kaitaistruct.h"
+#include <stdint.h>
+#include <vector>
+
+#if KAITAI_STRUCT_VERSION < 9000L
+#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
+#endif
+
+class ami_nvar_t : public kaitai::kstruct {
+
+public:
+    class nvar_attributes_t;
+    class ucs2_string_t;
+    class nvar_extended_attributes_t;
+    class nvar_entry_t;
+    class nvar_entry_body_t;
+
+    ami_nvar_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, ami_nvar_t* p__root = 0);
+
+private:
+    void _read();
+    void _clean_up();
+
+public:
+    ~ami_nvar_t();
+
+    class nvar_attributes_t : public kaitai::kstruct {
+
+    public:
+
+        nvar_attributes_t(kaitai::kstream* p__io, ami_nvar_t::nvar_entry_t* p__parent = 0, ami_nvar_t* p__root = 0);
+
+    private:
+        void _read();
+        void _clean_up();
+
+    public:
+        ~nvar_attributes_t();
+
+    private:
+        bool m_valid;
+        bool m_auth_write;
+        bool m_hw_error_record;
+        bool m_extended_header;
+        bool m_data_only;
+        bool m_local_guid;
+        bool m_ascii_name;
+        bool m_runtime;
+        ami_nvar_t* m__root;
+        ami_nvar_t::nvar_entry_t* m__parent;
+
+    public:
+        bool valid() const { return m_valid; }
+        bool auth_write() const { return m_auth_write; }
+        bool hw_error_record() const { return m_hw_error_record; }
+        bool extended_header() const { return m_extended_header; }
+        bool data_only() const { return m_data_only; }
+        bool local_guid() const { return m_local_guid; }
+        bool ascii_name() const { return m_ascii_name; }
+        bool runtime() const { return m_runtime; }
+        ami_nvar_t* _root() const { return m__root; }
+        ami_nvar_t::nvar_entry_t* _parent() const { return m__parent; }
+    };
+
+    class ucs2_string_t : public kaitai::kstruct {
+
+    public:
+
+        ucs2_string_t(kaitai::kstream* p__io, ami_nvar_t::nvar_entry_body_t* p__parent = 0, ami_nvar_t* p__root = 0);
+
+    private:
+        void _read();
+        void _clean_up();
+
+    public:
+        ~ucs2_string_t();
+
+    private:
+        std::vector<uint16_t>* m_ucs2_chars;
+        ami_nvar_t* m__root;
+        ami_nvar_t::nvar_entry_body_t* m__parent;
+
+    public:
+        std::vector<uint16_t>* ucs2_chars() const { return m_ucs2_chars; }
+        ami_nvar_t* _root() const { return m__root; }
+        ami_nvar_t::nvar_entry_body_t* _parent() const { return m__parent; }
+    };
+
+    class nvar_extended_attributes_t : public kaitai::kstruct {
+
+    public:
+
+        nvar_extended_attributes_t(kaitai::kstream* p__io, ami_nvar_t::nvar_entry_body_t* p__parent = 0, ami_nvar_t* p__root = 0);
+
+    private:
+        void _read();
+        void _clean_up();
+
+    public:
+        ~nvar_extended_attributes_t();
+
+    private:
+        uint64_t m_reserved_high;
+        bool m_time_based_auth;
+        bool m_auth_write;
+        uint64_t m_reserved_low;
+        bool m_checksum;
+        ami_nvar_t* m__root;
+        ami_nvar_t::nvar_entry_body_t* m__parent;
+
+    public:
+        uint64_t reserved_high() const { return m_reserved_high; }
+        bool time_based_auth() const { return m_time_based_auth; }
+        bool auth_write() const { return m_auth_write; }
+        uint64_t reserved_low() const { return m_reserved_low; }
+        bool checksum() const { return m_checksum; }
+        ami_nvar_t* _root() const { return m__root; }
+        ami_nvar_t::nvar_entry_body_t* _parent() const { return m__parent; }
+    };
+
+    class nvar_entry_t : public kaitai::kstruct {
+
+    public:
+
+        nvar_entry_t(kaitai::kstream* p__io, ami_nvar_t* p__parent = 0, ami_nvar_t* p__root = 0);
+
+    private:
+        void _read();
+        void _clean_up();
+
+    public:
+        ~nvar_entry_t();
+
+    private:
+        bool f_offset;
+        int32_t m_offset;
+
+    public:
+        int32_t offset();
+
+    private:
+        bool f_end_offset;
+        int32_t m_end_offset;
+
+    public:
+        int32_t end_offset();
+
+    private:
+        std::string m_invoke_offset;
+        bool n_invoke_offset;
+
+    public:
+        bool _is_null_invoke_offset() { invoke_offset(); return n_invoke_offset; };
+
+    private:
+        uint8_t m_signature_first;
+        std::string m_signature_rest;
+        bool n_signature_rest;
+
+    public:
+        bool _is_null_signature_rest() { signature_rest(); return n_signature_rest; };
+
+    private:
+        uint16_t m_size;
+        bool n_size;
+
+    public:
+        bool _is_null_size() { size(); return n_size; };
+
+    private:
+        uint64_t m_next;
+        bool n_next;
+
+    public:
+        bool _is_null_next() { next(); return n_next; };
+
+    private:
+        nvar_attributes_t* m_attributes;
+        bool n_attributes;
+
+    public:
+        bool _is_null_attributes() { attributes(); return n_attributes; };
+
+    private:
+        nvar_entry_body_t* m_body;
+        bool n_body;
+
+    public:
+        bool _is_null_body() { body(); return n_body; };
+
+    private:
+        std::string m_invoke_end_offset;
+        bool n_invoke_end_offset;
+
+    public:
+        bool _is_null_invoke_end_offset() { invoke_end_offset(); return n_invoke_end_offset; };
+
+    private:
+        ami_nvar_t* m__root;
+        ami_nvar_t* m__parent;
+        std::string m__raw_body;
+        bool n__raw_body;
+
+    public:
+        bool _is_null__raw_body() { _raw_body(); return n__raw_body; };
+
+    private:
+        kaitai::kstream* m__io__raw_body;
+
+    public:
+        std::string invoke_offset() const { return m_invoke_offset; }
+        uint8_t signature_first() const { return m_signature_first; }
+        std::string signature_rest() const { return m_signature_rest; }
+        uint16_t size() const { return m_size; }
+        uint64_t next() const { return m_next; }
+        nvar_attributes_t* attributes() const { return m_attributes; }
+        nvar_entry_body_t* body() const { return m_body; }
+        std::string invoke_end_offset() const { return m_invoke_end_offset; }
+        ami_nvar_t* _root() const { return m__root; }
+        ami_nvar_t* _parent() const { return m__parent; }
+        std::string _raw_body() const { return m__raw_body; }
+        kaitai::kstream* _io__raw_body() const { return m__io__raw_body; }
+    };
+
+    class nvar_entry_body_t : public kaitai::kstruct {
+
+    public:
+
+        nvar_entry_body_t(kaitai::kstream* p__io, ami_nvar_t::nvar_entry_t* p__parent = 0, ami_nvar_t* p__root = 0);
+
+    private:
+        void _read();
+        void _clean_up();
+
+    public:
+        ~nvar_entry_body_t();
+
+    private:
+        bool f_extended_header_attributes;
+        nvar_extended_attributes_t* m_extended_header_attributes;
+        bool n_extended_header_attributes;
+
+    public:
+        bool _is_null_extended_header_attributes() { extended_header_attributes(); return n_extended_header_attributes; };
+
+    private:
+
+    public:
+        nvar_extended_attributes_t* extended_header_attributes();
+
+    private:
+        bool f_data_start_offset;
+        int32_t m_data_start_offset;
+
+    public:
+        int32_t data_start_offset();
+
+    private:
+        bool f_extended_header_size_field;
+        uint16_t m_extended_header_size_field;
+        bool n_extended_header_size_field;
+
+    public:
+        bool _is_null_extended_header_size_field() { extended_header_size_field(); return n_extended_header_size_field; };
+
+    private:
+
+    public:
+        uint16_t extended_header_size_field();
+
+    private:
+        bool f_extended_header_timestamp;
+        uint64_t m_extended_header_timestamp;
+        bool n_extended_header_timestamp;
+
+    public:
+        bool _is_null_extended_header_timestamp() { extended_header_timestamp(); return n_extended_header_timestamp; };
+
+    private:
+
+    public:
+        uint64_t extended_header_timestamp();
+
+    private:
+        bool f_data_size;
+        int32_t m_data_size;
+
+    public:
+        int32_t data_size();
+
+    private:
+        bool f_extended_header_checksum;
+        uint8_t m_extended_header_checksum;
+        bool n_extended_header_checksum;
+
+    public:
+        bool _is_null_extended_header_checksum() { extended_header_checksum(); return n_extended_header_checksum; };
+
+    private:
+
+    public:
+        uint8_t extended_header_checksum();
+
+    private:
+        bool f_data_end_offset;
+        int32_t m_data_end_offset;
+
+    public:
+        int32_t data_end_offset();
+
+    private:
+        bool f_extended_header_size;
+        uint16_t m_extended_header_size;
+
+    public:
+        uint16_t extended_header_size();
+
+    private:
+        bool f_extended_header_hash;
+        std::string m_extended_header_hash;
+        bool n_extended_header_hash;
+
+    public:
+        bool _is_null_extended_header_hash() { extended_header_hash(); return n_extended_header_hash; };
+
+    private:
+
+    public:
+        std::string extended_header_hash();
+
+    private:
+        uint8_t m_guid_index;
+        bool n_guid_index;
+
+    public:
+        bool _is_null_guid_index() { guid_index(); return n_guid_index; };
+
+    private:
+        std::string m_guid;
+        bool n_guid;
+
+    public:
+        bool _is_null_guid() { guid(); return n_guid; };
+
+    private:
+        std::string m_ascii_name;
+        bool n_ascii_name;
+
+    public:
+        bool _is_null_ascii_name() { ascii_name(); return n_ascii_name; };
+
+    private:
+        ucs2_string_t* m_ucs2_name;
+        bool n_ucs2_name;
+
+    public:
+        bool _is_null_ucs2_name() { ucs2_name(); return n_ucs2_name; };
+
+    private:
+        std::string m_invoke_data_start;
+        bool n_invoke_data_start;
+
+    public:
+        bool _is_null_invoke_data_start() { invoke_data_start(); return n_invoke_data_start; };
+
+    private:
+        std::string m_data;
+        ami_nvar_t* m__root;
+        ami_nvar_t::nvar_entry_t* m__parent;
+
+    public:
+        uint8_t guid_index() const { return m_guid_index; }
+        std::string guid() const { return m_guid; }
+        std::string ascii_name() const { return m_ascii_name; }
+        ucs2_string_t* ucs2_name() const { return m_ucs2_name; }
+        std::string invoke_data_start() const { return m_invoke_data_start; }
+        std::string data() const { return m_data; }
+        ami_nvar_t* _root() const { return m__root; }
+        ami_nvar_t::nvar_entry_t* _parent() const { return m__parent; }
+    };
+
+private:
+    std::vector<nvar_entry_t*>* m_entries;
+    ami_nvar_t* m__root;
+    kaitai::kstruct* m__parent;
+
+public:
+    std::vector<nvar_entry_t*>* entries() const { return m_entries; }
+    ami_nvar_t* _root() const { return m__root; }
+    kaitai::kstruct* _parent() const { return m__parent; }
+};
+
+#endif  // AMI_NVAR_H_
diff --git a/common/ksy/ami_nvar.ksy b/common/ksy/ami_nvar.ksy
new file mode 100644
index 0000000..25e63de
--- /dev/null
+++ b/common/ksy/ami_nvar.ksy
@@ -0,0 +1,162 @@
+meta:
+  id: ami_nvar
+  title: AMI Aptio NVRAM Storage
+  application: AMI Aptio-based UEFI firmware
+  file-extension: nvar
+  tags:
+    - firmware
+  license: CC0-1.0
+  ks-version: 0.9
+  endian: le
+
+seq:
+- id: entries
+  type: nvar_entry
+  repeat: until
+  repeat-until: _.signature_first != 0x4e or _io.eof
+
+types:
+  nvar_entry:
+   seq:
+   - id: invoke_offset
+     size: 0
+     if: offset >= 0
+   - id: signature_first
+     type: u1
+   - id: signature_rest
+     contents: [VAR]
+     if: signature_first == 0x4e
+   - id: size
+     type: u2
+     valid:
+      expr: _ > sizeof<u4> + sizeof<u2> + sizeof<u4>
+     if: signature_first == 0x4e
+   - id: next
+     type: b24le
+     if: signature_first == 0x4e
+   - id: attributes
+     type: nvar_attributes
+     if: signature_first == 0x4e
+   - id: body
+     type: nvar_entry_body
+     size: size - (sizeof<u4> + sizeof<u2> + sizeof<u4>)
+     if: signature_first == 0x4e
+   - id: invoke_end_offset
+     size: 0
+     if: signature_first == 0x4e and end_offset >= 0
+   instances:
+    offset:
+     value: _io.pos
+    end_offset:
+     value: _io.pos
+
+  nvar_attributes:
+   seq:
+    - id: valid
+      type: b1
+    - id: auth_write
+      type: b1
+    - id: hw_error_record
+      type: b1
+    - id: extended_header
+      type: b1
+    - id: data_only
+      type: b1
+    - id: local_guid
+      type: b1
+    - id: ascii_name
+      type: b1
+    - id: runtime
+      type: b1
+
+  nvar_extended_attributes:
+   seq:
+    - id: reserved_high
+      type: b2
+    - id: time_based_auth
+      type: b1
+    - id: auth_write
+      type: b1
+    - id: reserved_low
+      type: b3
+    - id: checksum
+      type: b1
+
+  ucs2_string:
+   seq:
+   - id: ucs2_chars
+     type: u2
+     repeat: until
+     repeat-until: _ == 0
+
+  nvar_entry_body:
+   seq:
+   - id: guid_index
+     type: u1
+     if: (not _parent.attributes.local_guid)
+      and (not _parent.attributes.data_only)
+      and (_parent.attributes.valid)
+   - id: guid
+     size: 16
+     if: (_parent.attributes.local_guid)
+      and (not _parent.attributes.data_only)
+      and (_parent.attributes.valid)
+   - id: ascii_name
+     type: strz
+     encoding: ASCII
+     if: (_parent.attributes.ascii_name)
+      and (not _parent.attributes.data_only)
+      and (_parent.attributes.valid)
+   - id: ucs2_name
+     type: ucs2_string
+     if: (not _parent.attributes.ascii_name)
+      and (not _parent.attributes.data_only)
+      and (_parent.attributes.valid)
+   - id: invoke_data_start
+     size: 0
+     if: data_start_offset >= 0
+   - id: data
+     size-eos: true
+   instances:
+    extended_header_size_field:
+     pos: _io.pos - sizeof<u2>
+     type: u2
+     if: _parent.attributes.valid
+      and _parent.attributes.extended_header
+      and _parent.size > sizeof<u4> + sizeof<u2> + sizeof<u4> + sizeof<u2>
+    extended_header_size:
+     value: '_parent.attributes.extended_header ? (extended_header_size_field >= sizeof<nvar_extended_attributes> + sizeof<u2> ? extended_header_size_field : 0) : 0'
+    extended_header_attributes:
+     pos: _io.pos - extended_header_size
+     type: nvar_extended_attributes
+     if: _parent.attributes.valid
+      and _parent.attributes.extended_header
+      and (extended_header_size >= sizeof<nvar_extended_attributes> + sizeof<u2>)
+    extended_header_timestamp:
+     pos: _io.pos - extended_header_size + sizeof<nvar_extended_attributes>
+     type: u8
+     if: _parent.attributes.valid
+      and _parent.attributes.extended_header
+      and (extended_header_size >= sizeof<nvar_extended_attributes> + sizeof<u8> + sizeof<u2>)
+      and extended_header_attributes.time_based_auth
+    extended_header_hash:
+     pos: _io.pos - extended_header_size + sizeof<nvar_extended_attributes> + sizeof<u8>
+     size: 32
+     if: _parent.attributes.valid
+      and _parent.attributes.extended_header
+      and (extended_header_size >= sizeof<nvar_extended_attributes> + sizeof<u8> + 32 + sizeof<u2>)
+      and extended_header_attributes.time_based_auth
+      and (not _parent.attributes.data_only)
+    extended_header_checksum:
+     pos: _io.pos - sizeof<u2> - sizeof<u1>
+     type: u1
+     if: _parent.attributes.valid
+      and _parent.attributes.extended_header
+      and (extended_header_size >= sizeof<nvar_extended_attributes> + sizeof<u1> + sizeof<u2>)
+      and extended_header_attributes.checksum
+    data_start_offset:
+     value: _io.pos
+    data_end_offset:
+     value: _io.pos
+    data_size:
+     value: (data_end_offset - data_start_offset) - extended_header_size
diff --git a/common/nvramparser.cpp b/common/nvramparser.cpp
index 404e2a2..4fd3377 100755
--- a/common/nvramparser.cpp
+++ b/common/nvramparser.cpp
@@ -12,358 +12,272 @@
  
  */
 
+#ifdef U_ENABLE_NVRAM_PARSING_SUPPORT
 #include <map>
 
 #include "nvramparser.h"
 #include "parsingdata.h"
+#include "ustring.h"
 #include "utility.h"
 #include "nvram.h"
 #include "ffs.h"
 #include "intel_microcode.h"
 
-#ifdef U_ENABLE_NVRAM_PARSING_SUPPORT
+#include "umemstream.h"
+#include "kaitai/kaitaistream.h"
+#include "generated/ami_nvar.h"
+
 USTATUS NvramParser::parseNvarStore(const UModelIndex & index)
 {
     // Sanity check
     if (!index.isValid())
         return U_INVALID_PARAMETER;
-    
-    // Obtain required information from parent file
-    UINT8 emptyByte = 0xFF;
-    UModelIndex parentFileIndex = model->findParentOfType(index, Types::File);
-    if (parentFileIndex.isValid() && model->hasEmptyParsingData(parentFileIndex) == false) {
-        UByteArray data = model->parsingData(parentFileIndex);
-        const FILE_PARSING_DATA* pdata = (const FILE_PARSING_DATA*)data.constData();
-        emptyByte = readUnaligned(pdata).emptyByte;
-    }
-    
-    // Get local offset
-    UINT32 localOffset = (UINT32)model->header(index).size();
-    
-    // Get item data
-    const UByteArray data = model->body(index);
-    
-    // Parse all entries
-    UINT32 offset = 0;
-    UINT32 guidsInStore = 0;
-    while (1) {
-        bool msgUnknownExtDataFormat = false;
-        bool msgExtHeaderTooLong = false;
-        bool msgExtDataTooShort = false;
-        
-        bool isInvalid = false;
-        bool isInvalidLink = false;
-        bool hasExtendedHeader = false;
-        bool hasChecksum = false;
-        bool hasTimestamp = false;
-        bool hasHash = false;
-        bool hasGuidIndex = false;
-        
-        UINT32 guidIndex = 0;
-        UINT8  storedChecksum = 0;
-        UINT8  calculatedChecksum = 0;
-        UINT32 extendedHeaderSize = 0;
-        UINT8  extendedAttributes = 0;
-        UINT64 timestamp = 0;
-        UByteArray hash;
-        
-        UINT8 subtype = Subtypes::FullNvarEntry;
-        UString name;
-        UString guid;
-        UString text;
-        UByteArray header;
-        UByteArray body;
-        UByteArray tail;
-        
-        UINT32 guidAreaSize = guidsInStore * sizeof(EFI_GUID);
-        UINT32 unparsedSize = (UINT32)data.size() - offset - guidAreaSize;
-        
-        // Get entry header
-        const NVAR_ENTRY_HEADER* entryHeader = (const NVAR_ENTRY_HEADER*)(data.constData() + offset);
-        
-        // Check header size and signature
-        if (unparsedSize < sizeof(NVAR_ENTRY_HEADER) ||
-            entryHeader->Signature != NVRAM_NVAR_ENTRY_SIGNATURE ||
-            unparsedSize < entryHeader->Size) {
-            // Check if the data left is a free space or a padding
-            UByteArray padding = data.mid(offset, unparsedSize);
-            
-            // Get info
-            UString info = usprintf("Full size: %Xh (%u)", (UINT32)padding.size(), (UINT32)padding.size());
-            
-            if ((UINT32)padding.count(emptyByte) == unparsedSize) { // Free space
+
+    UByteArray nvar = model->body(index);
+
+    // Nothing to parse in an empty store
+    if (nvar.isEmpty())
+        return U_SUCCESS;
+
+    try {
+        const UINT32 localOffset = (UINT32)model->header(index).size();
+        umemstream is(nvar.constData(), nvar.size());
+        kaitai::kstream ks(&is);
+        ami_nvar_t parsed(&ks);
+
+        UINT16 guidsInStore = 0;
+        UINT32 currentEntryIndex = 0;
+        for (const auto & entry : *parsed.entries()) {
+            UINT8 subtype = Subtypes::FullNvarEntry;
+            UString name;
+            UString text;
+            UString info;
+            UString guid;
+            UByteArray header;
+            UByteArray body;
+            UByteArray tail;
+
+            // This is a terminating entry, needs special processing
+            if (entry->_is_null_signature_rest()) {
+                UINT32 guidAreaSize = guidsInStore * sizeof(EFI_GUID);
+                UINT32 unparsedSize = (UINT32)nvar.size() - entry->offset() - guidAreaSize;
+
+                // Check if the data left is a free space or a padding
+                UByteArray padding = nvar.mid(entry->offset(), unparsedSize);
+
+                // Get info
+                UString info = usprintf("Full size: %Xh (%u)", (UINT32)padding.size(), (UINT32)padding.size());
+
+                if ((UINT32)padding.count(0xFF) == unparsedSize) { // Free space
+                    // Add tree item
+                    model->addItem(localOffset + entry->offset(), Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
+                }
+                else {
+                    // Nothing is parsed yet, but the file is not empty
+                    if (entry->offset() == 0) {
+                        msg(usprintf("%s: file can't be parsed as NVAR variable store", __FUNCTION__), index);
+                        return U_SUCCESS;
+                    }
+
+                    // Add tree item
+                    model->addItem(localOffset + entry->offset(), Types::Padding, getPaddingType(padding), UString("Padding"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
+                }
+
+                // Add GUID store area
+                UByteArray guidArea = nvar.right(guidAreaSize);
+                // Get info
+                name = UString("GUID store");
+                info = usprintf("Full size: %Xh (%u)\nGUIDs in store: %u",
+                                (UINT32)guidArea.size(), (UINT32)guidArea.size(),
+                                guidsInStore);
                 // Add tree item
-                model->addItem(localOffset + offset, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
+                model->addItem((UINT32)(localOffset + entry->offset() + padding.size()), Types::NvarGuidStore, 0, name, UString(), info, UByteArray(), guidArea, UByteArray(), Fixed, index);
+
+                return U_SUCCESS;
             }
-            else {
-                // Nothing is parsed yet, but the file is not empty
-                if (!offset) {
-                    msg(usprintf("%s: file can't be parsed as NVAR variables store", __FUNCTION__), index);
-                    return U_SUCCESS;
-                }
-                
-                // Add tree item
-                model->addItem(localOffset + offset, Types::Padding, getPaddingType(padding), UString("Padding"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
+
+            // This is a normal entry
+            const auto entry_body = entry->body();
+
+            // Set default next to predefined last value
+            NVAR_ENTRY_PARSING_DATA pdata = {};
+            pdata.emptyByte = 0xFF;
+            pdata.next = 0xFFFFFF;
+            pdata.isValid = TRUE;
+
+            // Check for invalid entry
+            if (!entry->attributes()->valid()) {
+                subtype = Subtypes::InvalidNvarEntry;
+                name = UString("Invalid");
+                pdata.isValid = FALSE;
+                goto processing_done;
             }
-            
-            // Add GUID store area
-            UByteArray guidArea = data.right(guidAreaSize);
-            // Get info
-            name = UString("GUID store");
-            info = usprintf("Full size: %Xh (%u)\nGUIDs in store: %u",
-                            (UINT32)guidArea.size(), (UINT32)guidArea.size(),
-                            guidsInStore);
-            // Add tree item
-            model->addItem((UINT32)(localOffset + offset + padding.size()), Types::Padding, getPaddingType(guidArea), name, UString(), info, UByteArray(), guidArea, UByteArray(), Fixed, index);
-            
-            return U_SUCCESS;
-        }
-        
-        // Contruct generic header and body
-        header = data.mid(offset, sizeof(NVAR_ENTRY_HEADER));
-        body = data.mid(offset + sizeof(NVAR_ENTRY_HEADER), entryHeader->Size - sizeof(NVAR_ENTRY_HEADER));
-        
-        UINT32 lastVariableFlag = emptyByte ? 0xFFFFFF : 0;
-        
-        // Set default next to predefined last value
-        NVAR_ENTRY_PARSING_DATA pdata = {};
-        pdata.emptyByte = emptyByte;
-        pdata.next = lastVariableFlag;
-        
-        // Entry is marked as invalid
-        if ((entryHeader->Attributes & NVRAM_NVAR_ENTRY_VALID) == 0) { // Valid attribute is not set
-            isInvalid = true;
-            // Do not parse further
-            goto parsing_done;
-        }
-        
-        // Add next node information to parsing data
-        if (entryHeader->Next != lastVariableFlag) {
-            subtype = Subtypes::LinkNvarEntry;
-            pdata.next = entryHeader->Next;
-        }
-        
-        // Entry with extended header
-        if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_EXT_HEADER) {
-            hasExtendedHeader = true;
-            msgUnknownExtDataFormat = true;
-            
-            extendedHeaderSize = readUnaligned((UINT16*)(body.constData() + body.size() - sizeof(UINT16)));
-            if (extendedHeaderSize > (UINT32)body.size()) {
-                msgExtHeaderTooLong = true;
-                isInvalid = true;
-                // Do not parse further
-                goto parsing_done;
+
+            // Check for link entry
+            if (entry->next() != 0xFFFFFF) {
+                subtype = Subtypes::LinkNvarEntry;
+                pdata.next = (UINT32)entry->next();
             }
-            
-            extendedAttributes = *(UINT8*)(body.constData() + body.size() - extendedHeaderSize);
-            
-            // Variable with checksum
-            if (extendedAttributes & NVRAM_NVAR_ENTRY_EXT_CHECKSUM) {
-                // Get stored checksum
-                storedChecksum = *(UINT8*)(body.constData() + body.size() - sizeof(UINT16) - sizeof(UINT8));
-                
-                // Recalculate checksum for the variable
-                calculatedChecksum = 0;
-                // Include entry data
-                UINT8* start = (UINT8*)(entryHeader + 1);
-                for (UINT8* p = start; p < start + entryHeader->Size - sizeof(NVAR_ENTRY_HEADER); p++) {
-                    calculatedChecksum += *p;
-                }
-                // Include entry size and flags
-                start = (UINT8*)&entryHeader->Size;
-                for (UINT8*p = start; p < start + sizeof(UINT16); p++) {
-                    calculatedChecksum += *p;
-                }
-                // Include entry attributes
-                calculatedChecksum += entryHeader->Attributes;
-                
-                hasChecksum = true;
-                msgUnknownExtDataFormat = false;
-            }
-            
-            tail = body.mid(body.size() - extendedHeaderSize);
-            body = body.left(body.size() - extendedHeaderSize);
-            
-            // Entry with authenticated write (for SecureBoot)
-            if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_AUTH_WRITE) {
-                if ((entryHeader->Attributes & NVRAM_NVAR_ENTRY_DATA_ONLY)) {// Data only auth. variables has no hash
-                    if ((UINT32)tail.size() < sizeof(UINT64)) {
-                        msgExtDataTooShort = true;
-                        isInvalid = true;
-                        // Do not parse further
-                        goto parsing_done;
-                    }
-                    
-                    timestamp = readUnaligned(tail.constData() + sizeof(UINT8));
-                    hasTimestamp = true;
-                    msgUnknownExtDataFormat = false;
-                }
-                else { // Full or link variable have hash
-                    if ((UINT32)tail.size() < sizeof(UINT64) + SHA256_HASH_SIZE) {
-                        msgExtDataTooShort = true;
-                        isInvalid = true;
-                        // Do not parse further
-                        goto parsing_done;
-                    }
-                    
-                    timestamp = readUnaligned((UINT64*)(tail.constData() + sizeof(UINT8)));
-                    hash = tail.mid(sizeof(UINT64) + sizeof(UINT8), SHA256_HASH_SIZE);
-                    hasTimestamp = true;
-                    hasHash = true;
-                    msgUnknownExtDataFormat = false;
-                }
-            }
-        }
-        
-        // Entry is data-only (nameless and GUIDless entry or link)
-        if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_DATA_ONLY) { // Data-only attribute is set
-            isInvalidLink = true;
-            UModelIndex nvarIndex;
-            // Search previously added entries for a link to this variable
-            // WARNING: O(n^2), may be very slow
-            for (int i = model->rowCount(index) - 1; i >= 0; i--) {
-                nvarIndex = index.model()->index(i, 0, index);
-                
-                if (model->hasEmptyParsingData(nvarIndex) == false) {
-                    UByteArray nvarData = model->parsingData(nvarIndex);
-                    const NVAR_ENTRY_PARSING_DATA nvarPdata = readUnaligned((const NVAR_ENTRY_PARSING_DATA*)nvarData.constData());
-                    if (nvarPdata.isValid && nvarPdata.next + model->offset(nvarIndex) - localOffset == offset) { // Previous link is present and valid
-                        isInvalidLink = false;
-                        break;
+
+            // Check for data-only entry (nameless and GUIDless entry or link)
+            if (entry->attributes()->data_only()) {
+                // Search backwards for a previous entry with a link to this variable
+                UModelIndex prevEntryIndex;
+                if (currentEntryIndex > 0) {
+                    for (UINT32 i = currentEntryIndex - 1; i > 0; i--) {
+                        const auto previousEntry = parsed.entries()->at(i);
+
+                        if (previousEntry == entry)
+                            break;
+
+                        if (previousEntry->next() + previousEntry->offset() == entry->offset()) { // Previous link is present and valid
+                            prevEntryIndex = index.model()->index(i, 0, index);
+                            // Make sure that we are linking to a valid entry
+                            NVAR_ENTRY_PARSING_DATA pd = readUnaligned((NVAR_ENTRY_PARSING_DATA*)model->parsingData(prevEntryIndex).constData());
+                            if (!pd.isValid) {
+                                prevEntryIndex = UModelIndex();
+                            }
+                            break;
+                        }
                     }
                 }
+                // Check if the link is valid
+                if (prevEntryIndex.isValid()) {
+                    // Use the name and text of the previous entry
+                    name = model->name(prevEntryIndex);
+                    text = model->text(prevEntryIndex);
+
+                    if (entry->next() == 0xFFFFFF)
+                        subtype = Subtypes::DataNvarEntry;
+                }
+                else {
+                    subtype = Subtypes::InvalidLinkNvarEntry;
+                    name = UString("InvalidLink");
+                    pdata.isValid = FALSE;
+                }
+                goto processing_done;
             }
-            // Check if the link is valid
-            if (!isInvalidLink) {
-                // Use the name and text of the previous link
-                name = model->name(nvarIndex);
-                text = model->text(nvarIndex);
-                
-                if (entryHeader->Next == lastVariableFlag)
-                    subtype = Subtypes::DataNvarEntry;
+
+            // Obtain text
+            if (!entry_body->_is_null_ascii_name()) {
+                text = entry_body->ascii_name().c_str();
             }
-            
-            // Do not parse further
-            goto parsing_done;
-        }
-        
-        // Get entry name
-        {
-            UINT32 nameOffset = (entryHeader->Attributes & NVRAM_NVAR_ENTRY_GUID) ? sizeof(EFI_GUID) : sizeof(UINT8); // GUID can be stored with the variable or in a separate store, so there will only be an index of it
-            CHAR8* namePtr = (CHAR8*)(entryHeader + 1) + nameOffset;
-            UINT32 nameSize = 0;
-            if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_ASCII_NAME) { // Name is stored as ASCII string of CHAR8s
-                text = UString(namePtr);
-                nameSize = (UINT32)(text.length() + 1);
+            else if (!entry_body->_is_null_ucs2_name()) {
+                UByteArray temp;
+                for (const auto & ch : *entry_body->ucs2_name()->ucs2_chars()) {
+                    temp += UByteArray((const char*)&ch, sizeof(ch));
+                }
+                text = uFromUcs2(temp.constData());
             }
-            else { // Name is stored as UCS2 string of CHAR16s
-                text = uFromUcs2(namePtr);
-                nameSize = (UINT32)((text.length() + 1) * 2);
+
+            // Obtain GUID
+            if (!entry_body->_is_null_guid()) { // GUID is stored in the entry itself
+                const EFI_GUID g = readUnaligned((EFI_GUID*)entry_body->guid().c_str());
+                name = guidToUString(g);
+                guid = guidToUString(g, false);
             }
-            
-            // Get entry GUID
-            if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_GUID) { // GUID is strored in the variable itself
-                name = guidToUString(readUnaligned((EFI_GUID*)(entryHeader + 1)));
-                guid = guidToUString(readUnaligned((EFI_GUID*)(entryHeader + 1)), false);
-            }
-            // GUID is stored in GUID list at the end of the store
-            else {
-                guidIndex = *(UINT8*)(entryHeader + 1);
-                if (guidsInStore < guidIndex + 1)
-                    guidsInStore = guidIndex + 1;
-                
+            else { // GUID is stored in GUID store at the end of the NVAR store
+                // Grow the GUID store if needed
+                if (guidsInStore < entry_body->guid_index() + 1)
+                    guidsInStore = entry_body->guid_index() + 1;
+
                 // The list begins at the end of the store and goes backwards
-                const EFI_GUID* guidPtr = (const EFI_GUID*)(data.constData() + data.size()) - 1 - guidIndex;
-                name = guidToUString(readUnaligned(guidPtr));
-                guid = guidToUString(readUnaligned(guidPtr), false);
-                hasGuidIndex = true;
+                const EFI_GUID g = readUnaligned((EFI_GUID*)(nvar.constData() + nvar.size()) - (entry_body->guid_index() + 1));
+                name = guidToUString(g);
+                guid = guidToUString(g, false);
             }
-            
-            // Include name and GUID into the header and remove them from body
-            header = data.mid(offset, sizeof(NVAR_ENTRY_HEADER) + nameOffset + nameSize);
-            body = body.mid(nameOffset + nameSize);
+
+processing_done:
+            // This feels hacky, but I haven't found a way to ask Kaitai for raw bytes
+            header = nvar.mid(entry->offset(), sizeof(NVAR_ENTRY_HEADER) + entry_body->data_start_offset());
+            body = nvar.mid(entry->offset() + sizeof(NVAR_ENTRY_HEADER) + entry_body->data_start_offset(), entry_body->data_size());
+            tail = nvar.mid(entry->end_offset() - entry_body->extended_header_size(), entry_body->extended_header_size());
+
+            // Add GUID info for valid entries
+            if (!guid.isEmpty())
+                info += UString("Variable GUID: ") + guid + "\n";
+
+            // Add GUID index information
+            if (!entry_body->_is_null_guid_index())
+                info += usprintf("GUID index: %u\n", entry_body->guid_index());
+
+            // Add header, body and extended data info
+            info += usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nTail size: %Xh (%u)",
+                             entry->size(), entry->size(),
+                             (UINT32)header.size(), (UINT32)header.size(),
+                             (UINT32)body.size(), (UINT32)body.size(),
+                             (UINT32)tail.size(), (UINT32)tail.size());
+
+            // Add attributes info
+            const NVAR_ENTRY_HEADER entryHeader = readUnaligned((NVAR_ENTRY_HEADER*)header.constData());
+            info += usprintf("\nAttributes: %02Xh", entryHeader.Attributes);
+
+            // Translate attributes to text
+            if (entryHeader.Attributes != 0x00 && entryHeader.Attributes != 0xFF)
+                info += UString(" (") + nvarAttributesToUString(entryHeader.Attributes) + UString(")");
+
+            // Add next node info
+            if (entry->next() != 0xFFFFFF)
+                info += usprintf("\nNext node at offset: %Xh", localOffset + entry->offset() + (UINT32)entry->next());
+
+            // Add extended header info
+            if (entry_body->extended_header_size() > 0) {
+                info += usprintf("\nExtended header size: %Xh (%u)",
+                                 entry_body->extended_header_size(), entry_body->extended_header_size());
+
+                const UINT8 extendedAttributes = *tail.constData();
+                info += usprintf("\nExtended attributes: %02Xh (", extendedAttributes) + nvarExtendedAttributesToUString(extendedAttributes) + UString(")");
+
+                // Add checksum
+                if (!entry_body->_is_null_extended_header_checksum()) {
+                    UINT8 calculatedChecksum = 0;
+                    UByteArray wholeBody = body + tail;
+
+                    // Include entry body
+                    UINT8* start = (UINT8*)wholeBody.constData();
+                    for (UINT8* p = start; p < start + wholeBody.size(); p++) {
+                        calculatedChecksum += *p;
+                    }
+                    // Include entry size and flags
+                    start = (UINT8*)&entryHeader.Size;
+                    for (UINT8*p = start; p < start + sizeof(UINT16); p++) {
+                        calculatedChecksum += *p;
+                    }
+                    // Include entry attributes
+                    calculatedChecksum += entryHeader.Attributes;
+                    info += usprintf("\nChecksum: %02Xh, ", entry_body->extended_header_checksum())
+                     + (calculatedChecksum ? usprintf(", invalid, should be %02Xh", 0x100 - calculatedChecksum) : UString(", valid"));
+                }
+
+                // Add timestamp
+                if (!entry_body->_is_null_extended_header_timestamp())
+                    info += usprintf("\nTimestamp: %" PRIX64 "h", entry_body->extended_header_timestamp());
+
+                // Add hash
+                if (!entry_body->_is_null_extended_header_hash()) {
+                    UByteArray hash = UByteArray(entry_body->extended_header_hash().c_str(), entry_body->extended_header_hash().size());
+                    info += UString("\nHash: ") + UString(hash.toHex().constData());
+                }
+            }
+
+            // Add tree item
+            UModelIndex varIndex = model->addItem(localOffset + entry->offset(), Types::NvarEntry, subtype, name, text, info, header, body, tail, Fixed, index);
+            currentEntryIndex++;
+
+            // Set parsing data
+            model->setParsingData(varIndex, UByteArray((const char*)&pdata, sizeof(pdata)));
+
+            // Try parsing the entry data as NVAR storage if it begins with NVAR signature
+            if ((subtype == Subtypes::DataNvarEntry || subtype == Subtypes::FullNvarEntry)
+                && body.size() >= 4 && readUnaligned((const UINT32*)body.constData()) == NVRAM_NVAR_ENTRY_SIGNATURE)
+                (void)parseNvarStore(varIndex);
         }
-    parsing_done:
-        UString info;
-        
-        // Rename invalid entries according to their types
-        pdata.isValid = TRUE;
-        if (isInvalid) {
-            name = UString("Invalid");
-            subtype = Subtypes::InvalidNvarEntry;
-            pdata.isValid = FALSE;
-        }
-        else if (isInvalidLink) {
-            name = UString("Invalid link");
-            subtype = Subtypes::InvalidLinkNvarEntry;
-            pdata.isValid = FALSE;
-        }
-        else // Add GUID info for valid entries
-            info += UString("Variable GUID: ") + guid + "\n";
-        
-        // Add GUID index information
-        if (hasGuidIndex)
-            info += usprintf("GUID index: %u\n", guidIndex);
-        
-        // Add header, body and extended data info
-        info += usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)",
-                         entryHeader->Size, entryHeader->Size,
-                         (UINT32)header.size(), (UINT32)header.size(),
-                         (UINT32)body.size(), (UINT32)body.size());
-        
-        // Add attributes info
-        info += usprintf("\nAttributes: %02Xh", entryHeader->Attributes);
-        // Translate attributes to text
-        if (entryHeader->Attributes && entryHeader->Attributes != 0xFF)
-            info += UString(" (") + nvarAttributesToUString(entryHeader->Attributes) + UString(")");
-        
-        // Add next node info
-        if (!isInvalid && entryHeader->Next != lastVariableFlag)
-            info += usprintf("\nNext node at offset: %Xh", localOffset + offset + entryHeader->Next);
-        
-        // Add extended header info
-        if (hasExtendedHeader) {
-            info += usprintf("\nExtended header size: %Xh (%u)\nExtended attributes: %Xh (",
-                             extendedHeaderSize, extendedHeaderSize,
-                             extendedAttributes) + nvarExtendedAttributesToUString(extendedAttributes) + UString(")");
-            
-            // Add checksum
-            if (hasChecksum)
-                info += usprintf("\nChecksum: %02Xh", storedChecksum) +
-                (calculatedChecksum ? usprintf(", invalid, should be %02Xh", 0x100 - calculatedChecksum) : UString(", valid"));
-            
-            // Add timestamp
-            if (hasTimestamp)
-                info += usprintf("\nTimestamp: %" PRIX64 "h", timestamp);
-            
-            // Add hash
-            if (hasHash)
-                info += UString("\nHash: ") + UString(hash.toHex().constData());
-        }
-        
-        // Add tree item
-        UModelIndex varIndex = model->addItem(localOffset + offset, Types::NvarEntry, subtype, name, text, info, header, body, tail, Fixed, index);
-        
-        // Set parsing data for created entry
-        model->setParsingData(varIndex, UByteArray((const char*)&pdata, sizeof(pdata)));
-        
-        // Show messages
-        if (msgUnknownExtDataFormat) msg(usprintf("%s: unknown extended data format", __FUNCTION__), varIndex);
-        if (msgExtHeaderTooLong)     msg(usprintf("%s: extended header size (%Xh) is greater than body size (%Xh)", __FUNCTION__,
-                                                  extendedHeaderSize, (UINT32)body.size()), varIndex);
-        if (msgExtDataTooShort)      msg(usprintf("%s: extended header size (%Xh) is too small for timestamp and hash", __FUNCTION__,
-                                                  (UINT32)tail.size()), varIndex);
-        
-        // Try parsing the entry data as NVAR storage if it begins with NVAR signature
-        if ((subtype == Subtypes::DataNvarEntry || subtype == Subtypes::FullNvarEntry)
-            && body.size() >= 4 && readUnaligned((const UINT32*)body.constData()) == NVRAM_NVAR_ENTRY_SIGNATURE)
-            parseNvarStore(varIndex);
-        
-        // Move to next exntry
-        offset += entryHeader->Size;
     }
-    
+    catch (...) {
+        msg(usprintf("%s: unable to parse AMI NVAR storage", __FUNCTION__), index);
+        return U_INVALID_STORE;
+    }
+
     return U_SUCCESS;
 }
 
diff --git a/common/types.cpp b/common/types.cpp
index f397316..32e3791 100755
--- a/common/types.cpp
+++ b/common/types.cpp
@@ -59,6 +59,7 @@ UString itemTypeToUString(const UINT8 type)
         case Types::EvsaStore:             return UString("EVSA store");
         case Types::CmdbStore:             return UString("CMDB store");
         case Types::FlashMapStore:         return UString("FlashMap store");
+        case Types::NvarGuidStore:         return UString("NVAR GUID store");
         case Types::NvarEntry:             return UString("NVAR entry");
         case Types::VssEntry:              return UString("VSS entry");
         case Types::FsysEntry:             return UString("Fsys entry");
diff --git a/common/types.h b/common/types.h
index 62c8929..5785a96 100755
--- a/common/types.h
+++ b/common/types.h
@@ -51,6 +51,7 @@ namespace Types {
         EvsaStore,
         FlashMapStore,
         CmdbStore,
+        NvarGuidStore,
         NvarEntry,
         VssEntry,
         FsysEntry,
diff --git a/common/umemstream.h b/common/umemstream.h
new file mode 100644
index 0000000..243b5c2
--- /dev/null
+++ b/common/umemstream.h
@@ -0,0 +1,58 @@
+/* umemstream.h
+
+ Copyright (c) 2023, Nikolaj Schlej. All rights reserved.
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution.  The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+ */
+
+#ifndef UMEMSTREAM_H
+#define UMEMSTREAM_H
+
+#include <sstream>
+
+// NOTE: this implementation is certainly not a valid replacement to std::stringstream
+// NOTE: because it only supports getting through the buffer once
+// NOTE: however, we already do it this way, so it's enough for practical purposes
+
+class umembuf : public std::streambuf {
+public:
+    umembuf(const char *p, size_t l) {
+        setg((char*)p, (char*)p, (char*)p + l);
+    }
+
+    pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which = std::ios_base::in) override
+    {
+        (void)which;
+        if (dir == std::ios_base::cur)
+            gbump((int)off);
+        else if (dir == std::ios_base::end)
+            setg(eback(), egptr() + off, egptr());
+        else if (dir == std::ios_base::beg)
+            setg(eback(), eback() + off, egptr());
+        return gptr() - eback();
+    }
+
+    pos_type seekpos(pos_type sp, std::ios_base::openmode which) override
+    {
+        return seekoff(sp - pos_type(off_type(0)), std::ios_base::beg, which);
+    }
+};
+
+class umemstream : public std::istream {
+public:
+  umemstream(const char *p, size_t l) : std::istream(&buffer_),
+    buffer_(p, l) {
+    rdbuf(&buffer_);
+  }
+
+private:
+  umembuf buffer_;
+};
+
+#endif // UMEMSTREAM_H
diff --git a/common/ustring.cpp b/common/ustring.cpp
index 1786a60..2493a41 100644
--- a/common/ustring.cpp
+++ b/common/ustring.cpp
@@ -113,12 +113,10 @@ UString uFromUcs2(const char* str, size_t max_len)
     UString msg;
     const char *str8 = str;
     size_t rest = (max_len == 0) ? SIZE_MAX : max_len;
-    if (max_len == 0) {
-        while (str8[0] && rest) {
-            msg += str8[0];
-            str8 += 2;
-            rest--;
-        }
+    while (str8[0] && rest) {
+        msg += str8[0];
+        str8 += 2;
+        rest--;
     }
     return msg;
-}
\ No newline at end of file
+}
diff --git a/common/utility.cpp b/common/utility.cpp
index fcd37a7..336b7c7 100755
--- a/common/utility.cpp
+++ b/common/utility.cpp
@@ -146,6 +146,7 @@ void fixFileName(UString &name, bool replaceSpaces)
 // Returns text representation of error code
 UString errorCodeToUString(USTATUS errorCode)
 {
+    // TODO: improve
     switch (errorCode) {
         case U_SUCCESS:                         return UString("Success");
         case U_NOT_IMPLEMENTED:                 return UString("Not implemented");
@@ -165,7 +166,6 @@ UString errorCodeToUString(USTATUS errorCode)
         case U_VOLUMES_NOT_FOUND:               return UString("UEFI volumes not found");
         case U_INVALID_VOLUME:                  return UString("Invalid UEFI volume");
         case U_VOLUME_REVISION_NOT_SUPPORTED:   return UString("Volume revision not supported");
-            //case U_VOLUME_GROW_FAILED:              return UString("Volume grow failed");
         case U_UNKNOWN_FFS:                     return UString("Unknown file system");
         case U_INVALID_FILE:                    return UString("Invalid file");
         case U_INVALID_SECTION:                 return UString("Invalid section");
@@ -177,26 +177,19 @@ UString errorCodeToUString(USTATUS errorCode)
         case U_UNKNOWN_COMPRESSION_TYPE:        return UString("Unknown compression type");
         case U_UNKNOWN_EXTRACT_MODE:            return UString("Unknown extract mode");
         case U_UNKNOWN_REPLACE_MODE:            return UString("Unknown replace mode");
-            //case U_UNKNOWN_INSERT_MODE:             return UString("Unknown insert mode");
         case U_UNKNOWN_IMAGE_TYPE:              return UString("Unknown executable image type");
         case U_UNKNOWN_PE_OPTIONAL_HEADER_TYPE: return UString("Unknown PE optional header type");
         case U_UNKNOWN_RELOCATION_TYPE:         return UString("Unknown relocation type");
-            //case U_GENERIC_CALL_NOT_SUPPORTED:      return UString("Generic call not supported");
-            //case U_VOLUME_BASE_NOT_FOUND:           return UString("Volume base address not found");
-            //case U_PEI_CORE_ENTRY_POINT_NOT_FOUND:  return UString("PEI core entry point not found");
         case U_COMPLEX_BLOCK_MAP:               return UString("Block map structure too complex for correct analysis");
         case U_DIR_ALREADY_EXIST:               return UString("Directory already exists");
         case U_DIR_CREATE:                      return UString("Directory can't be created");
         case U_DIR_CHANGE:                      return UString("Change directory failed");
-            //case U_UNKNOWN_PATCH_TYPE:              return UString("Unknown patch type");
-            //case U_PATCH_OFFSET_OUT_OF_BOUNDS:      return UString("Patch offset out of bounds");
-            //case U_INVALID_SYMBOL:                  return UString("Invalid symbol");
-            //case U_NOTHING_TO_PATCH:                return UString("Nothing to patch");
         case U_DEPEX_PARSE_FAILED:              return UString("Dependency expression parsing failed");
         case U_TRUNCATED_IMAGE:                 return UString("Image is truncated");
         case U_INVALID_CAPSULE:                 return UString("Invalid capsule");
         case U_STORES_NOT_FOUND:                return UString("Stores not found");
         case U_INVALID_STORE_SIZE:              return UString("Invalid store size");
+        case U_INVALID_STORE:                   return UString("Invalid store");
         default:                                return usprintf("Unknown error %02lX", errorCode);
     }
 }
diff --git a/fuzzing/CMakeLists.txt b/fuzzing/CMakeLists.txt
index eb99ad3..5a9f9a4 100644
--- a/fuzzing/CMakeLists.txt
+++ b/fuzzing/CMakeLists.txt
@@ -31,6 +31,7 @@ SET(PROJECT_SOURCES
  ../common/LZMA/SDK/C/LzmaDec.c
  ../common/Tiano/EfiTianoDecompress.c
  ../common/ustring.cpp
+ ../common/generated/ami_nvar.cpp
  ../common/generated/intel_acbp_v1.cpp
  ../common/generated/intel_acbp_v2.cpp
  ../common/generated/intel_keym_v1.cpp
@@ -86,7 +87,7 @@ ADD_DEFINITIONS(
 ADD_EXECUTABLE(ffsparser_fuzzer ${PROJECT_SOURCES})
 
 
-IF(USE_AFL_DRIVER)
+IF(NOT USE_AFL_DRIVER)
 TARGET_COMPILE_OPTIONS(ffsparser_fuzzer PRIVATE -O1 -fno-omit-frame-pointer -g -ggdb3 -fsanitize=fuzzer,address,undefined -fsanitize-address-use-after-scope -fno-sanitize-recover=undefined)
 TARGET_LINK_LIBRARIES(ffsparser_fuzzer PRIVATE -fsanitize=fuzzer,address,undefined)
 ELSE()