Index: /trunk/include/VBox/err.h
===================================================================
--- /trunk/include/VBox/err.h	(revision 23751)
+++ /trunk/include/VBox/err.h	(revision 23752)
@@ -600,7 +600,13 @@
 /** Complex SSM field fed to SSMR3PutStruct or SSMR3GetStruct.  Use the
  * extended API. */
-#define VERR_SSM_COMPLEX_FIELD                  (-1864)
+#define VERR_SSM_FIELD_COMPLEX                  (-1864)
 /** Invalid size of a SSM field with the specified transformation. */
-#define VERR_SSM_INVALID_FIELD_SIZE             (-1865)
+#define VERR_SSM_FIELD_INVALID_SIZE             (-1865)
+/** The specified field is outside the structure.  */
+#define VERR_SSM_FIELD_OUT_OF_BOUNDS            (-1866)
+/** The field does not follow immediately the previous one. */
+#define VERR_SSM_FIELD_NOT_CONSECUTIVE          (-1867)
+/** The field contains an invalid callback or transformation index. */
+#define VERR_SSM_FIELD_INVALID_CALLBACK         (-1868)
 /** @} */
 
Index: /trunk/include/VBox/ssm.h
===================================================================
--- /trunk/include/VBox/ssm.h	(revision 23751)
+++ /trunk/include/VBox/ssm.h	(revision 23752)
@@ -176,7 +176,6 @@
 #define SSMFIELD_ENTRY_HCPTR(Type, Field)   SSMFIELD_ENTRY_INT(Type, Field, SSMFIELDTRANS_HCPTR)
 /** Emit a SSMFIELD array entry for a field that can be ignored.
- * It is stored if SSMSTRUCT_FLAGS_DONT_IGNORE is specified to SSMR3PutStructEx.
- * It is skipped if SSMSTRUCT_FLAGS_DONT_IGNORE is specified to
- * SSMR3GetStructEx, the structure member is never touched on restore. */
+ * It is stored as zeros if SSMSTRUCT_FLAGS_DONT_IGNORE is specified to
+ * SSMR3PutStructEx.  The member is never touched upon restore. */
 #define SSMFIELD_ENTRY_IGNORE(Type, Field)  SSMFIELD_ENTRY_INT(Type, Field, SSMFIELDTRANS_IGNORE)
 /** Emit a SSMFIELD array entry for a field with a custom callback. */
@@ -191,8 +190,12 @@
 /** The field descriptors must exactly cover the entire struct, A to Z. */
 #define SSMSTRUCT_FLAGS_FULL_STRUCT         RT_BIT_32(0)
+/** No start and end markers, just the raw bits. */
+#define SSMSTRUCT_FLAGS_NO_MARKERS          RT_BIT_32(1)
 /** Do not ignore any ignorable fields. */
-#define SSMSTRUCT_FLAGS_DONT_IGNORE         RT_BIT_32(1)
+#define SSMSTRUCT_FLAGS_DONT_IGNORE         RT_BIT_32(2)
 /** Band-aid for old SSMR3PutMem/SSMR3GetMem of structurs with host pointers. */
-#define SSMSTRUCT_FLAGS_MEM_BAND_AID        (SSMSTRUCT_FLAGS_DONT_IGNORE | SSMSTRUCT_FLAGS_FULL_STRUCT)
+#define SSMSTRUCT_FLAGS_MEM_BAND_AID        (SSMSTRUCT_FLAGS_DONT_IGNORE | SSMSTRUCT_FLAGS_FULL_STRUCT | SSMSTRUCT_FLAGS_NO_MARKERS)
+/** Mask of the valid bits. */
+#define SSMSTRUCT_FLAGS_VALID_MASK          UINT32_C(0x00000007)
 /** @} */
 
Index: /trunk/src/VBox/VMM/SSM.cpp
===================================================================
--- /trunk/src/VBox/VMM/SSM.cpp	(revision 23751)
+++ /trunk/src/VBox/VMM/SSM.cpp	(revision 23752)
@@ -3171,20 +3171,141 @@
 
             case SSMFIELDTRANS_GCPTR:
-                AssertMsgReturn(pCur->cb == sizeof(RTGCPTR), ("%#x\n", pCur->cb), VERR_SSM_INVALID_FIELD_SIZE);
+                AssertMsgReturn(pCur->cb == sizeof(RTGCPTR), ("%#x\n", pCur->cb), VERR_SSM_FIELD_INVALID_SIZE);
                 rc = SSMR3PutGCPtr(pSSM, *(PRTGCPTR)pbField);
                 break;
 
             case SSMFIELDTRANS_GCPHYS:
-                AssertMsgReturn(pCur->cb == sizeof(RTGCPHYS), ("%#x\n", pCur->cb), VERR_SSM_INVALID_FIELD_SIZE);
+                AssertMsgReturn(pCur->cb == sizeof(RTGCPHYS), ("%#x\n", pCur->cb), VERR_SSM_FIELD_INVALID_SIZE);
                 rc = SSMR3PutGCPhys(pSSM, *(PRTGCPHYS)pbField);
                 break;
 
             default:
-                AssertMsgFailedReturn(("%#x\n", pCur->pfnGetPutOrTransformer), VERR_SSM_COMPLEX_FIELD);
-        }
+                AssertMsgFailedReturn(("%#x\n", pCur->pfnGetPutOrTransformer), VERR_SSM_FIELD_COMPLEX);
+        }
+        if (RT_FAILURE(rc))
+            return rc;
     }
 
     /* end marker */
     return SSMR3PutU32(pSSM, SSMR3STRUCT_END);
+}
+
+
+/**
+ * Puts a structure, extended API.
+ *
+ * @returns VBox status code.
+ * @param   pSSM            The saved state handle.
+ * @param   pvStruct        The structure address.
+ * @param   cbStruct        The size of the struct (use for validation only).
+ * @param   fFlags          Combination of SSMSTRUCT_FLAGS_XXX defines.
+ * @param   paFields        The array of structure fields descriptions. The
+ *                          array must be terminated by a SSMFIELD_ENTRY_TERM().
+ * @param   pvUser          User argument for any callbacks that paFields might
+ *                          contain.
+ */
+VMMR3DECL(int) SSMR3PutStructEx(PSSMHANDLE pSSM, const void *pvStruct, size_t cbStruct,
+                                uint32_t fFlags, PCSSMFIELD paFields, void *pvUser)
+{
+    static uint8_t const s_abZero[_1K] = {0};
+    int rc;
+
+    /*
+     * Validation.
+     */
+    AssertMsgReturn(!(fFlags & ~SSMSTRUCT_FLAGS_VALID_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+    AssertPtr(pvStruct);
+    AssertPtr(paFields);
+
+
+    /*
+     * Begin marker.
+     */
+    if (!(fFlags & SSMSTRUCT_FLAGS_NO_MARKERS))
+    {
+        rc = SSMR3PutU32(pSSM, SSMR3STRUCT_BEGIN);
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+
+    /*
+     * Put the fields
+     */
+    uint32_t off = 0;
+    for (PCSSMFIELD pCur = paFields;
+         pCur->cb != UINT32_MAX && pCur->off != UINT32_MAX;
+         pCur++)
+    {
+        AssertMsgReturn(   pCur->cb             <= cbStruct
+                        && pCur->off + pCur->cb <= cbStruct
+                        && pCur->off + pCur->cb >  pCur->off,
+                        ("off=%#x cb=%#x cbStruct=%#x\n", pCur->cb, pCur->off, cbStruct),
+                        VERR_SSM_FIELD_OUT_OF_BOUNDS);
+        AssertMsgReturn(    !(fFlags & SSMSTRUCT_FLAGS_FULL_STRUCT)
+                        || off == pCur->off,
+                        ("off=%#x pCur->off=%#x\n", off, pCur->off),
+                        VERR_SSM_FIELD_NOT_CONSECUTIVE);
+
+        uint8_t const *pbField = (uint8_t const *)pvStruct + pCur->off;
+        switch ((uintptr_t)pCur->pfnGetPutOrTransformer)
+        {
+            case SSMFIELDTRANS_NO_TRANSFORMATION:
+                rc = ssmR3DataWrite(pSSM, pbField, pCur->cb);
+                break;
+
+            case SSMFIELDTRANS_GCPTR:
+                AssertMsgReturn(pCur->cb == sizeof(RTGCPTR), ("%#x\n", pCur->cb), VERR_SSM_FIELD_INVALID_SIZE);
+                rc = SSMR3PutGCPtr(pSSM, *(PRTGCPTR)pbField);
+                break;
+
+            case SSMFIELDTRANS_GCPHYS:
+                AssertMsgReturn(pCur->cb == sizeof(RTGCPHYS), ("%#x\n", pCur->cb), VERR_SSM_FIELD_INVALID_SIZE);
+                rc = SSMR3PutGCPhys(pSSM, *(PRTGCPHYS)pbField);
+                break;
+
+            case SSMFIELDTRANS_HCPTR:
+                AssertMsgReturn(pCur->cb == sizeof(void *), ("%#x\n", pCur->cb), VERR_SSM_FIELD_INVALID_SIZE);
+                if (fFlags & SSMSTRUCT_FLAGS_DONT_IGNORE)
+                    rc = ssmR3DataWrite(pSSM, s_abZero, sizeof(void *));
+                break;
+
+            case SSMFIELDTRANS_IGNORE:
+                if (fFlags & SSMSTRUCT_FLAGS_DONT_IGNORE)
+                {
+                    uint32_t cb;
+                    for (uint32_t cbLeft = pCur->cb; cbLeft > 0 && RT_SUCCESS(rc); cbLeft -= cb)
+                    {
+                        cb = RT_MIN(sizeof(s_abZero), cbLeft);
+                        rc = ssmR3DataWrite(pSSM, s_abZero, cb);
+                    }
+                }
+                break;
+
+            default:
+                AssertPtrReturn(pCur->pfnGetPutOrTransformer, VERR_SSM_FIELD_INVALID_CALLBACK);
+                rc = pCur->pfnGetPutOrTransformer(pSSM, pCur, (void *)pvStruct, fFlags, false /*fGetOrPut*/, pvUser);
+                break;
+        }
+        if (RT_FAILURE(rc))
+            return rc;
+
+        off = pCur->off + pCur->cb;
+    }
+    AssertMsgReturn(    !(fFlags & SSMSTRUCT_FLAGS_FULL_STRUCT)
+                    || off == cbStruct,
+                    ("off=%#x cbStruct=%#x\n", off, cbStruct),
+                    VERR_SSM_FIELD_NOT_CONSECUTIVE);
+
+    /*
+     * End marker
+     */
+    if (!(fFlags & SSMSTRUCT_FLAGS_NO_MARKERS))
+    {
+        rc = SSMR3PutU32(pSSM, SSMR3STRUCT_END);
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+
+    return VINF_SUCCESS;
 }
 
@@ -5525,6 +5646,5 @@
     if (RT_FAILURE(rc))
         return rc;
-    if (u32Magic != SSMR3STRUCT_BEGIN)
-        AssertMsgFailedReturn(("u32Magic=%#RX32\n", u32Magic), VERR_SSM_STRUCTURE_MAGIC);
+    AssertMsgReturn(u32Magic == SSMR3STRUCT_BEGIN, ("u32Magic=%#RX32\n", u32Magic), VERR_SSM_STRUCTURE_MAGIC);
 
     /* get the fields */
@@ -5541,16 +5661,18 @@
 
             case SSMFIELDTRANS_GCPTR:
-                AssertMsgReturn(pCur->cb == sizeof(RTGCPTR), ("%#x\n", pCur->cb), VERR_SSM_INVALID_FIELD_SIZE);
+                AssertMsgReturn(pCur->cb == sizeof(RTGCPTR), ("%#x\n", pCur->cb), VERR_SSM_FIELD_INVALID_SIZE);
                 rc = SSMR3GetGCPtr(pSSM, (PRTGCPTR)pbField);
                 break;
 
             case SSMFIELDTRANS_GCPHYS:
-                AssertMsgReturn(pCur->cb == sizeof(RTGCPHYS), ("%#x\n", pCur->cb), VERR_SSM_INVALID_FIELD_SIZE);
+                AssertMsgReturn(pCur->cb == sizeof(RTGCPHYS), ("%#x\n", pCur->cb), VERR_SSM_FIELD_INVALID_SIZE);
                 rc = SSMR3GetGCPhys(pSSM, (PRTGCPHYS)pbField);
                 break;
 
             default:
-                AssertMsgFailedReturn(("%#x\n", pCur->pfnGetPutOrTransformer), VERR_SSM_COMPLEX_FIELD);
-        }
+                AssertMsgFailedReturn(("%#x\n", pCur->pfnGetPutOrTransformer), VERR_SSM_FIELD_COMPLEX);
+        }
+        if (RT_FAILURE(rc))
+            return rc;
     }
 
@@ -5559,7 +5681,120 @@
     if (RT_FAILURE(rc))
         return rc;
-    if (u32Magic != SSMR3STRUCT_END)
-        AssertMsgFailedReturn(("u32Magic=%#RX32\n", u32Magic), VERR_SSM_STRUCTURE_MAGIC);
+    AssertMsgReturn(u32Magic == SSMR3STRUCT_END, ("u32Magic=%#RX32\n", u32Magic), VERR_SSM_STRUCTURE_MAGIC);
     return rc;
+}
+
+
+/**
+ * Guts a structure, extended API.
+ *
+ * @returns VBox status code.
+ * @param   pSSM            The saved state handle.
+ * @param   pvStruct        The structure address.
+ * @param   cbStruct        The size of the struct (use for validation only).
+ * @param   fFlags          Combination of SSMSTRUCT_FLAGS_XXX defines.
+ * @param   paFields        The array of structure fields descriptions. The
+ *                          array must be terminated by a SSMFIELD_ENTRY_TERM().
+ * @param   pvUser          User argument for any callbacks that paFields might
+ *                          contain.
+ */
+VMMR3DECL(int) SSMR3GetStructEx(PSSMHANDLE pSSM, void *pvStruct, size_t cbStruct,
+                                uint32_t fFlags, PCSSMFIELD paFields, void *pvUser)
+{
+    int         rc;
+    uint32_t    u32Magic;
+
+    /*
+     * Validation.
+     */
+    AssertMsgReturn(!(fFlags & ~SSMSTRUCT_FLAGS_VALID_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+    AssertPtr(pvStruct);
+    AssertPtr(paFields);
+
+
+    /*
+     * Begin marker.
+     */
+    if (!(fFlags & SSMSTRUCT_FLAGS_NO_MARKERS))
+    {
+        rc = SSMR3GetU32(pSSM, &u32Magic);
+        if (RT_FAILURE(rc))
+            return rc;
+        AssertMsgReturn(u32Magic == SSMR3STRUCT_BEGIN, ("u32Magic=%#RX32\n", u32Magic), VERR_SSM_STRUCTURE_MAGIC);
+    }
+
+    /*
+     * Put the fields
+     */
+    uint32_t off = 0;
+    for (PCSSMFIELD pCur = paFields;
+         pCur->cb != UINT32_MAX && pCur->off != UINT32_MAX;
+         pCur++)
+    {
+        AssertMsgReturn(   pCur->cb             <= cbStruct
+                        && pCur->off + pCur->cb <= cbStruct
+                        && pCur->off + pCur->cb >  pCur->off,
+                        ("off=%#x cb=%#x cbStruct=%#x\n", pCur->cb, pCur->off, cbStruct),
+                        VERR_SSM_FIELD_OUT_OF_BOUNDS);
+        AssertMsgReturn(    !(fFlags & SSMSTRUCT_FLAGS_FULL_STRUCT)
+                        || off == pCur->off,
+                        ("off=%#x pCur->off=%#x\n", off, pCur->off),
+                        VERR_SSM_FIELD_NOT_CONSECUTIVE);
+
+        uint8_t *pbField = (uint8_t *)pvStruct + pCur->off;
+        switch ((uintptr_t)pCur->pfnGetPutOrTransformer)
+        {
+            case SSMFIELDTRANS_NO_TRANSFORMATION:
+                rc = ssmR3DataRead(pSSM, pbField, pCur->cb);
+                break;
+
+            case SSMFIELDTRANS_GCPTR:
+                AssertMsgReturn(pCur->cb == sizeof(RTGCPTR), ("%#x\n", pCur->cb), VERR_SSM_FIELD_INVALID_SIZE);
+                rc = SSMR3GetGCPtr(pSSM, (PRTGCPTR)pbField);
+                break;
+
+            case SSMFIELDTRANS_GCPHYS:
+                AssertMsgReturn(pCur->cb == sizeof(RTGCPHYS), ("%#x\n", pCur->cb), VERR_SSM_FIELD_INVALID_SIZE);
+                rc = SSMR3GetGCPhys(pSSM, (PRTGCPHYS)pbField);
+                break;
+
+            case SSMFIELDTRANS_HCPTR:
+                AssertMsgReturn(pCur->cb == sizeof(void *), ("%#x\n", pCur->cb), VERR_SSM_FIELD_INVALID_SIZE);
+                if (fFlags & SSMSTRUCT_FLAGS_DONT_IGNORE)
+                    rc = SSMR3Skip(pSSM, pSSM->u.Read.cHostBits / 8);
+                break;
+
+            case SSMFIELDTRANS_IGNORE:
+                if (fFlags & SSMSTRUCT_FLAGS_DONT_IGNORE)
+                    rc = SSMR3Skip(pSSM, pCur->cb);
+                break;
+
+            default:
+                AssertPtrReturn(pCur->pfnGetPutOrTransformer, VERR_SSM_FIELD_INVALID_CALLBACK);
+                rc = pCur->pfnGetPutOrTransformer(pSSM, pCur, pvStruct, fFlags, true /*fGetOrPut*/, pvUser);
+                break;
+        }
+        if (RT_FAILURE(rc))
+            return rc;
+
+        off = pCur->off + pCur->cb;
+    }
+    AssertMsgReturn(    !(fFlags & SSMSTRUCT_FLAGS_FULL_STRUCT)
+                    || off == cbStruct,
+                    ("off=%#x cbStruct=%#x\n", off, cbStruct),
+                    VERR_SSM_FIELD_NOT_CONSECUTIVE);
+
+    /*
+     * End marker
+     */
+    if (!(fFlags & SSMSTRUCT_FLAGS_NO_MARKERS))
+    {
+        rc = SSMR3GetU32(pSSM, &u32Magic);
+        if (RT_FAILURE(rc))
+            return rc;
+        AssertMsgReturn(u32Magic == SSMR3STRUCT_END, ("u32Magic=%#RX32\n", u32Magic), VERR_SSM_STRUCTURE_MAGIC);
+    }
+
+    return VINF_SUCCESS;
 }
 
