From 47af96eb12c66ea07d9d5d1b2370b1ba6b88f8f2 Mon Sep 17 00:00:00 2001
From: Alexei Kotov <alexdobrohotov@yandex.ru>
Date: Wed, 16 Aug 2023 22:33:00 +0300
Subject: [PATCH] Generalize extended storage subrecord handling

---
 components/esm4/loadnavm.cpp | 31 +------------------------------
 components/esm4/loadwrld.cpp | 15 ---------------
 components/esm4/reader.cpp   | 17 ++++++++++++++---
 3 files changed, 15 insertions(+), 48 deletions(-)

diff --git a/components/esm4/loadnavm.cpp b/components/esm4/loadnavm.cpp
index bd982f4279..20fcf90982 100644
--- a/components/esm4/loadnavm.cpp
+++ b/components/esm4/loadnavm.cpp
@@ -196,7 +196,6 @@ void ESM4::NavMesh::load(ESM4::Reader& reader)
 
     std::uint32_t esmVer = reader.esmVersion();
     // std::cout << "NavMesh 0x" << std::hex << this << std::endl; // FIXME
-    std::uint32_t subSize = 0; // for XXXX sub record
 
 // FIXME: debugging only
 #if 0
@@ -214,18 +213,7 @@ void ESM4::NavMesh::load(ESM4::Reader& reader)
             {
                 if (esmVer == 0x3F800000)
                 {
-                    // TODO: check if any valid TES5 plugin prepends XXXX to NVNM
-                    if (subSize)
-                    {
-                        reader.skipSubRecordData(subSize);
-                        reader.updateRecordRead(subSize);
-                        subSize = 0;
-                    }
-                    else
-                    {
-                        // FIXME: FO4 appears to have a different format
-                        reader.skipSubRecordData();
-                    }
+                    reader.skipSubRecordData();
                     break;
                 }
 
@@ -237,23 +225,6 @@ void ESM4::NavMesh::load(ESM4::Reader& reader)
             case ESM4::SUB_ONAM:
             case ESM4::SUB_PNAM:
             case ESM4::SUB_NNAM:
-            {
-                if (subSize)
-                {
-                    reader.skipSubRecordData(subSize); // special post XXXX
-                    reader.updateRecordRead(subSize); // WARNING: manual update
-                    subSize = 0;
-                }
-                else
-                {
-                    Log(Debug::Verbose) << ESM::printName(subHdr.typeId) << " skipping...";
-                    reader.skipSubRecordData(); // FIXME: process the subrecord rather than skip
-                }
-                break;
-            }
-            case ESM4::SUB_XXXX:
-                reader.get(subSize);
-                break;
             case ESM4::SUB_NVER: // FO3
             case ESM4::SUB_DATA: // FO3
             case ESM4::SUB_NVVX: // FO3
diff --git a/components/esm4/loadwrld.cpp b/components/esm4/loadwrld.cpp
index 2df127c8d3..2a77833a44 100644
--- a/components/esm4/loadwrld.cpp
+++ b/components/esm4/loadwrld.cpp
@@ -45,8 +45,6 @@ void ESM4::World::load(ESM4::Reader& reader)
     // corrupted by the use of ignore flag (TODO: should check to verify).
     reader.setCurrWorld(mId); // save for CELL later
 
-    std::uint32_t subSize = 0; // for XXXX sub record
-
     std::uint32_t esmVer = reader.esmVersion();
     // bool isTES4 = (esmVer == ESM::VER_080 || esmVer == ESM::VER_100);
     // bool isFONV = (esmVer == ESM::VER_132 || esmVer == ESM::VER_133 || esmVer == ESM::VER_134);
@@ -144,19 +142,6 @@ void ESM4::World::load(ESM4::Reader& reader)
                 reader.get(mParentUseFlags);
                 break;
             case ESM4::SUB_OFST:
-                if (subSize)
-                {
-                    reader.skipSubRecordData(subSize); // special post XXXX
-                    reader.updateRecordRead(subSize); // WARNING: manually update
-                    subSize = 0;
-                }
-                else
-                    reader.skipSubRecordData(); // FIXME: process the subrecord rather than skip
-
-                break;
-            case ESM4::SUB_XXXX:
-                reader.get(subSize);
-                break;
             case ESM4::SUB_RNAM: // multiple
             case ESM4::SUB_MHDT:
             case ESM4::SUB_LTMP:
diff --git a/components/esm4/reader.cpp b/components/esm4/reader.cpp
index 02bd78116f..2e626cc557 100644
--- a/components/esm4/reader.cpp
+++ b/components/esm4/reader.cpp
@@ -558,9 +558,6 @@ namespace ESM4
     {
         bool result = false;
         // NOTE: some SubRecords have 0 dataSize (e.g. SUB_RDSD in one of REC_REGN records in Oblivion.esm).
-        // Also SUB_XXXX has zero dataSize and the following 4 bytes represent the actual dataSize
-        // - hence it require manual updtes to mCtx.recordRead via updateRecordRead()
-        // See ESM4::NavMesh and ESM4::World.
         if (mCtx.recordHeader.record.dataSize - mCtx.recordRead >= sizeof(mCtx.subRecordHeader))
         {
             result = getExact(mCtx.subRecordHeader);
@@ -582,6 +579,20 @@ namespace ESM4
             return false;
         }
 
+        // Extended storage subrecord redefines the following subrecord's size.
+        // Would need to redesign the loader to support that, so skip over both subrecords.
+        if (result && mCtx.subRecordHeader.typeId == ESM4::SUB_XXXX)
+        {
+            std::uint32_t extDataSize;
+            get(extDataSize);
+            if (!getSubRecordHeader())
+                return false;
+
+            skipSubRecordData(extDataSize);
+            mCtx.recordRead += extDataSize - mCtx.subRecordHeader.dataSize;
+            return getSubRecordHeader();
+        }
+
         return result;
     }