diff --git a/UEFIExtract/CMakeLists.txt b/UEFIExtract/CMakeLists.txt
index 2cf3c63..13dceed 100644
--- a/UEFIExtract/CMakeLists.txt
+++ b/UEFIExtract/CMakeLists.txt
@@ -37,6 +37,7 @@ SET(PROJECT_SOURCES
  ../common/generated/ami_nvar.cpp
  ../common/generated/edk2_vss.cpp
  ../common/generated/edk2_vss2.cpp
+ ../common/generated/edk2_ftw.cpp
  ../common/generated/insyde_fdc.cpp
  ../common/generated/intel_acbp_v1.cpp
  ../common/generated/intel_acbp_v2.cpp
diff --git a/UEFIFind/CMakeLists.txt b/UEFIFind/CMakeLists.txt
index c626d47..e3b8491 100644
--- a/UEFIFind/CMakeLists.txt
+++ b/UEFIFind/CMakeLists.txt
@@ -34,6 +34,7 @@ SET(PROJECT_SOURCES
  ../common/generated/ami_nvar.cpp
  ../common/generated/edk2_vss.cpp
  ../common/generated/edk2_vss2.cpp
+ ../common/generated/edk2_ftw.cpp
  ../common/generated/insyde_fdc.cpp
  ../common/generated/intel_acbp_v1.cpp
  ../common/generated/intel_acbp_v2.cpp
diff --git a/UEFITool/CMakeLists.txt b/UEFITool/CMakeLists.txt
index b38a1b3..1b57289 100644
--- a/UEFITool/CMakeLists.txt
+++ b/UEFITool/CMakeLists.txt
@@ -70,6 +70,7 @@ SET(PROJECT_SOURCES
  ../common/generated/ami_nvar.cpp
  ../common/generated/edk2_vss.cpp
  ../common/generated/edk2_vss2.cpp
+ ../common/generated/edk2_ftw.cpp
  ../common/generated/insyde_fdc.cpp
  ../common/generated/intel_acbp_v1.cpp
  ../common/generated/intel_acbp_v2.cpp
diff --git a/common/generated/edk2_ftw.cpp b/common/generated/edk2_ftw.cpp
new file mode 100644
index 0000000..ebc775b
--- /dev/null
+++ b/common/generated/edk2_ftw.cpp
@@ -0,0 +1,100 @@
+// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+
+#include "edk2_ftw.h"
+#include "../kaitai/exceptions.h"
+
+edk2_ftw_t::edk2_ftw_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, edk2_ftw_t* p__root) : kaitai::kstruct(p__io) {
+    m__parent = p__parent;
+    m__root = this; (void)p__root;
+    f_len_ftw_store_header_32 = false;
+    f_len_ftw_store_header_64 = false;
+    _read();
+}
+
+void edk2_ftw_t::_read() {
+    m_signature = m__io->read_u4le();
+    {
+        uint32_t _ = signature();
+        if (!( ((_ == 4293995405UL) || (_ == 2656577835UL)) )) {
+            throw kaitai::validation_expr_error<uint32_t>(signature(), _io(), std::string("/seq/0"));
+        }
+    }
+    n_signature_main = true;
+    if (signature() == 4293995405UL) {
+        n_signature_main = false;
+        m_signature_main = m__io->read_bytes(12);
+        if (!(signature_main() == std::string("\x96\x76\x8B\x4C\xA9\x85\x27\x47\x07\x5B\x4F\x50", 12))) {
+            throw kaitai::validation_not_equal_error<std::string>(std::string("\x96\x76\x8B\x4C\xA9\x85\x27\x47\x07\x5B\x4F\x50", 12), signature_main(), _io(), std::string("/seq/1"));
+        }
+    }
+    n_signature_edk2_working_block = true;
+    if (signature() == 2656577835UL) {
+        n_signature_edk2_working_block = false;
+        m_signature_edk2_working_block = m__io->read_bytes(12);
+        if (!(signature_edk2_working_block() == std::string("\x68\x7C\x7D\x49\x0A\xCE\x65\x00\xFD\x9F\x1B\x95", 12))) {
+            throw kaitai::validation_not_equal_error<std::string>(std::string("\x68\x7C\x7D\x49\x0A\xCE\x65\x00\xFD\x9F\x1B\x95", 12), signature_edk2_working_block(), _io(), std::string("/seq/2"));
+        }
+    }
+    n_signature_vss2_working_block = true;
+    if (signature() == 2656577835UL) {
+        n_signature_vss2_working_block = false;
+        m_signature_vss2_working_block = m__io->read_bytes(12);
+        if (!(signature_vss2_working_block() == std::string("\x68\x7C\x7D\x49\xA0\xCE\x65\x00\xFD\x9F\x1B\x95", 12))) {
+            throw kaitai::validation_not_equal_error<std::string>(std::string("\x68\x7C\x7D\x49\xA0\xCE\x65\x00\xFD\x9F\x1B\x95", 12), signature_vss2_working_block(), _io(), std::string("/seq/3"));
+        }
+    }
+    m_crc = m__io->read_u4le();
+    m_state = m__io->read_u1();
+    m_reserved = m__io->read_bytes(3);
+    m_len_write_queue_32 = m__io->read_u4le();
+    n_len_write_queue_64 = true;
+    if (kaitai::kstream::mod(len_write_queue_32(), 16) == 0) {
+        n_len_write_queue_64 = false;
+        m_len_write_queue_64 = m__io->read_u4le();
+    }
+    n_write_queue_32 = true;
+    if (kaitai::kstream::mod(len_write_queue_32(), 16) == 4) {
+        n_write_queue_32 = false;
+        m_write_queue_32 = m__io->read_bytes(len_write_queue_32());
+    }
+    n_write_queue_64 = true;
+    if (kaitai::kstream::mod(len_write_queue_32(), 16) == 0) {
+        n_write_queue_64 = false;
+        m_write_queue_64 = m__io->read_bytes(((static_cast<uint64_t>(len_write_queue_64()) << 32) + len_write_queue_32()));
+    }
+}
+
+edk2_ftw_t::~edk2_ftw_t() {
+    _clean_up();
+}
+
+void edk2_ftw_t::_clean_up() {
+    if (!n_signature_main) {
+    }
+    if (!n_signature_edk2_working_block) {
+    }
+    if (!n_signature_vss2_working_block) {
+    }
+    if (!n_len_write_queue_64) {
+    }
+    if (!n_write_queue_32) {
+    }
+    if (!n_write_queue_64) {
+    }
+}
+
+int8_t edk2_ftw_t::len_ftw_store_header_32() {
+    if (f_len_ftw_store_header_32)
+        return m_len_ftw_store_header_32;
+    m_len_ftw_store_header_32 = 28;
+    f_len_ftw_store_header_32 = true;
+    return m_len_ftw_store_header_32;
+}
+
+int8_t edk2_ftw_t::len_ftw_store_header_64() {
+    if (f_len_ftw_store_header_64)
+        return m_len_ftw_store_header_64;
+    m_len_ftw_store_header_64 = 32;
+    f_len_ftw_store_header_64 = true;
+    return m_len_ftw_store_header_64;
+}
diff --git a/common/generated/edk2_ftw.h b/common/generated/edk2_ftw.h
new file mode 100644
index 0000000..7399d3e
--- /dev/null
+++ b/common/generated/edk2_ftw.h
@@ -0,0 +1,105 @@
+#pragma once
+
+// 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 <memory>
+
+#if KAITAI_STRUCT_VERSION < 9000L
+#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
+#endif
+
+class edk2_ftw_t : public kaitai::kstruct {
+
+public:
+
+    edk2_ftw_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, edk2_ftw_t* p__root = nullptr);
+
+private:
+    void _read();
+    void _clean_up();
+
+public:
+    ~edk2_ftw_t();
+
+private:
+    bool f_len_ftw_store_header_32;
+    int8_t m_len_ftw_store_header_32;
+
+public:
+    int8_t len_ftw_store_header_32();
+
+private:
+    bool f_len_ftw_store_header_64;
+    int8_t m_len_ftw_store_header_64;
+
+public:
+    int8_t len_ftw_store_header_64();
+
+private:
+    uint32_t m_signature;
+    std::string m_signature_main;
+    bool n_signature_main;
+
+public:
+    bool _is_null_signature_main() { signature_main(); return n_signature_main; };
+
+private:
+    std::string m_signature_edk2_working_block;
+    bool n_signature_edk2_working_block;
+
+public:
+    bool _is_null_signature_edk2_working_block() { signature_edk2_working_block(); return n_signature_edk2_working_block; };
+
+private:
+    std::string m_signature_vss2_working_block;
+    bool n_signature_vss2_working_block;
+
+public:
+    bool _is_null_signature_vss2_working_block() { signature_vss2_working_block(); return n_signature_vss2_working_block; };
+
+private:
+    uint32_t m_crc;
+    uint8_t m_state;
+    std::string m_reserved;
+    uint32_t m_len_write_queue_32;
+    uint32_t m_len_write_queue_64;
+    bool n_len_write_queue_64;
+
+public:
+    bool _is_null_len_write_queue_64() { len_write_queue_64(); return n_len_write_queue_64; };
+
+private:
+    std::string m_write_queue_32;
+    bool n_write_queue_32;
+
+public:
+    bool _is_null_write_queue_32() { write_queue_32(); return n_write_queue_32; };
+
+private:
+    std::string m_write_queue_64;
+    bool n_write_queue_64;
+
+public:
+    bool _is_null_write_queue_64() { write_queue_64(); return n_write_queue_64; };
+
+private:
+    edk2_ftw_t* m__root;
+    kaitai::kstruct* m__parent;
+
+public:
+    uint32_t signature() const { return m_signature; }
+    std::string signature_main() const { return m_signature_main; }
+    std::string signature_edk2_working_block() const { return m_signature_edk2_working_block; }
+    std::string signature_vss2_working_block() const { return m_signature_vss2_working_block; }
+    uint32_t crc() const { return m_crc; }
+    uint8_t state() const { return m_state; }
+    std::string reserved() const { return m_reserved; }
+    uint32_t len_write_queue_32() const { return m_len_write_queue_32; }
+    uint32_t len_write_queue_64() const { return m_len_write_queue_64; }
+    std::string write_queue_32() const { return m_write_queue_32; }
+    std::string write_queue_64() const { return m_write_queue_64; }
+    edk2_ftw_t* _root() const { return m__root; }
+    kaitai::kstruct* _parent() const { return m__parent; }
+};
diff --git a/common/ksy/edk2_ftw.ksy b/common/ksy/edk2_ftw.ksy
new file mode 100644
index 0000000..e3f9fe7
--- /dev/null
+++ b/common/ksy/edk2_ftw.ksy
@@ -0,0 +1,48 @@
+meta:
+  id: edk2_ftw
+  title: EDK2 Fault Tolerant Write NVRAM store
+  application: EDK2-based UEFI firmware
+  file-extension: ftw
+  tags:
+    - firmware
+  license: CC0-1.0
+  ks-version: 0.9
+  endian: le
+
+seq:
+- id: signature
+  type: u4
+  valid:
+   expr: _ == 0xFFF12B8D or _ == 0x9E58292B
+- id: signature_main
+  contents: [0x96, 0x76, 0x8B, 0x4C, 0xA9, 0x85, 0x27, 0x47, 0x07, 0x5B, 0x4F, 0x50] # FF12B8D-7696-4C8B-A985-2747075B4F50
+  if: signature == 0xFFF12B8D
+- id: signature_edk2_working_block
+  contents: [0x68, 0x7C, 0x7D, 0x49, 0x0A, 0xCE, 0x65, 0x00, 0xFD, 0x9F, 0x1B, 0x95] # 9E58292B-7C68-497D-0ACE-6500FD9F1B95
+  if: signature ==  0x9E58292B
+- id: signature_vss2_working_block
+  contents: [0x68, 0x7C, 0x7D, 0x49, 0xA0, 0xCE, 0x65, 0x00, 0xFD, 0x9F, 0x1B, 0x95] # 9E58292B-7C68-497D-A0CE-6500FD9F1B95
+  if: signature ==  0x9E58292B
+- id: crc
+  type: u4
+- id: state
+  type: u1
+- id: reserved
+  size: 3
+- id: len_write_queue_32
+  type: u4
+- id: len_write_queue_64
+  type: u4
+  if: len_write_queue_32 % 0x10 == 0
+- id: write_queue_32
+  size: len_write_queue_32
+  if: len_write_queue_32 % 0x10 == 0x04
+- id: write_queue_64
+  size: ((len_write_queue_64.as<u8>) << 32) + len_write_queue_32
+  if: len_write_queue_32 % 0x10 == 0
+  
+instances:
+  len_ftw_store_header_32:
+   value: 28
+  len_ftw_store_header_64:
+   value: 32
diff --git a/common/nvram.h b/common/nvram.h
index 05323d1..fa3d165 100755
--- a/common/nvram.h
+++ b/common/nvram.h
@@ -204,8 +204,8 @@ typedef struct FDC_VOLUME_HEADER_ {
 //
 #define EFI_FAULT_TOLERANT_WORKING_BLOCK_VALID   0x1
 #define EFI_FAULT_TOLERANT_WORKING_BLOCK_INVALID 0x2
-extern const UByteArray EDKII_WORKING_BLOCK_SIGNATURE_GUID; // 9E58292B-7C68-497D-0ACE6500FD9F1B95
-extern const UByteArray VSS2_WORKING_BLOCK_SIGNATURE_GUID; // 9E58292B-7C68-497D-A0CE6500FD9F1B95
+extern const UByteArray EDKII_WORKING_BLOCK_SIGNATURE_GUID; // 9E58292B-7C68-497D-0ACE-6500FD9F1B95
+extern const UByteArray VSS2_WORKING_BLOCK_SIGNATURE_GUID; // 9E58292B-7C68-497D-A0CE-6500FD9F1B95
 
 #define NVRAM_MAIN_STORE_VOLUME_GUID_DATA1       0xFFF12B8D
 #define EDKII_WORKING_BLOCK_SIGNATURE_GUID_DATA1 0x9E58292B
diff --git a/common/nvramparser.cpp b/common/nvramparser.cpp
index 6c39c64..54f0704 100644
--- a/common/nvramparser.cpp
+++ b/common/nvramparser.cpp
@@ -28,6 +28,7 @@
 #include "generated/ami_nvar.h"
 #include "generated/edk2_vss.h"
 #include "generated/edk2_vss2.h"
+#include "generated/edk2_ftw.h"
 #include "generated/insyde_fdc.h"
 
 USTATUS NvramParser::parseNvarStore(const UModelIndex & index)
@@ -660,7 +661,76 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32
         // Do not try any other parsers if we are here for FDC store parsing
         if (fdcStoreSizeOverride == 0) {
             // FTW
-            
+            try {
+                if (volumeBodySize - storeOffset < sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32)) {
+                    // No need to parse further, the rest of the volume is too small
+                    throw 0;
+                }
+                
+                UByteArray ftw = volumeBody.mid(storeOffset);
+                umemstream is(ftw.constData(), ftw.size());
+                kaitai::kstream ks(&is);
+                edk2_ftw_t parsed(&ks);
+                UINT64 storeSize;
+                UINT64 headerSize;
+                UINT32 calculatedCrc;
+                UByteArray header;
+                if (parsed._is_null_len_write_queue_64()) {
+                    headerSize = parsed.len_ftw_store_header_32();
+                    storeSize = headerSize + parsed.len_write_queue_32();
+                    header = ftw.left(headerSize);
+                    
+                    // Check block header checksum
+                    UByteArray crcHeader = header;
+                    EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* crcFtwBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)crcHeader.data();
+                    crcFtwBlockHeader->Crc = emptyByte ? 0xFFFFFFFF : 0;
+                    crcFtwBlockHeader->State = emptyByte ? 0xFF : 0;
+                    calculatedCrc = (UINT32)crc32(0, (const UINT8*)crcFtwBlockHeader, (UINT32)headerSize);
+                }
+                else {
+                    headerSize = parsed.len_ftw_store_header_64();
+                    storeSize = headerSize + parsed.len_write_queue_32() + (((UINT64)parsed.len_write_queue_64()) << 32);
+                    header = ftw.left(headerSize);
+                    
+                    // Check block header checksum
+                    UByteArray crcHeader = header;
+                    EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64* crcFtwBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64*)crcHeader.data();
+                    crcFtwBlockHeader->Crc = emptyByte ? 0xFFFFFFFF : 0;
+                    crcFtwBlockHeader->State = emptyByte ? 0xFF : 0;
+                    calculatedCrc = (UINT32)crc32(0, (const UINT8*)crcFtwBlockHeader, (UINT32)headerSize);
+                }
+                
+                // FTW store at current offset parsed correctly
+                // Check if we need to add a padding before it
+                if (!outerPadding.isEmpty()) {
+                    UString info = usprintf("Full size: %Xh (%u)", (UINT32)outerPadding.size(), (UINT32)outerPadding.size());
+                    model->addItem(previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), UString("Padding"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index);
+                    outerPadding.clear();
+                }
+                
+                // Construct header and body
+                UByteArray body = ftw.mid(header.size(), storeSize - header.size());
+                
+                // Add info
+                const EFI_GUID* guid = (const EFI_GUID*)header.constData();
+                UString name = UString("FTW store");
+                UString info = UString("Signature: ") + guidToUString(*guid, false);
+                info += usprintf("\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nState: %02Xh\nHeader CRC32: %08Xh",
+                                 (UINT32)storeSize, (UINT32)storeSize,
+                                 (UINT32)header.size(), (UINT32)header.size(),
+                                 (UINT32)body.size(), (UINT32)body.size(),
+                                 parsed.state(),
+                                 parsed.crc()) + (parsed.crc() != calculatedCrc ? usprintf(", invalid, should be %08Xh", calculatedCrc) : UString(", valid"));
+                
+                // Add header tree item
+                UModelIndex headerIndex = model->addItem(localOffset + storeOffset, Types::FtwStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, index);
+                
+                storeFound = true;
+                storeOffset += storeSize;
+                previousStoreEndOffset = storeOffset;
+            } catch (...) {
+                // Parsing failed, try something else
+            }
             // Insyde FDC
             try {
                 if (volumeBodySize - storeOffset < sizeof(FDC_VOLUME_HEADER)) {
@@ -693,10 +763,10 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32
                                         (UINT32)header.size(), (UINT32)header.size(),
                                         (UINT32)body.size(), (UINT32)body.size());
 
-                // Add header tree item with modified body
+                // Add header tree item
                 UModelIndex headerIndex = model->addItem(localOffset + storeOffset, Types::FdcStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, index);
                 
-                // Parse modified FDC body as normal VSS/VSS2 storage
+                // Parse FDC body as normal VSS/VSS2 storage with size override
                 parseNvramVolumeBody(headerIndex, (UINT32)body.size());
                 
                 storeFound = true;
@@ -706,7 +776,6 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32
                 // Parsing failed, try something else
             }
             
-            
             // Apple Fsys/Gaid
             
             // Phoenix EVSA