@@ -40,6 +40,7 @@ struct PropInfo
40
40
{
41
41
// Name of the property, should exist in class's property table (BEGIN_PROP_TABLE...)
42
42
// If starts with '#', the property is ignored, and the following string is a property's type name.
43
+ // If starts with '!', the property is ignored, and PropIndex defines the engine version constant.
43
44
const char * Name;
44
45
// Index of the property.
45
46
int PropIndex;
@@ -52,6 +53,8 @@ struct PropInfo
52
53
#define MAP (name,index ) { #name, index, 0 }, // field specification
53
54
#define END { NULL , 0 , 0 }, // end of class - mark with NULL name
54
55
56
+ #define VERSION_BLOCK (version ) { " !" , version, 0 }, // begin engine-specific block
57
+
55
58
// Multi-property "drop" instructions
56
59
#if _MSC_VER
57
60
#define DROP_INT8 (index, ...) { " #int8" , index, MakeBitmaskWithOffset<index, __VA_ARGS__>() },
@@ -68,14 +71,18 @@ struct PropInfo
68
71
69
72
#define DROP_OBJ_ARRAY (index ) { " #arr_int32" , index, 0 }, // TArray<UObject*>
70
73
71
- /*
72
- * Class table. If class is missing here, it's assumed that its property list
73
- * matches property table (declared with BEGIN_PROP_TABLE).
74
- * Notes:
75
- * - Classes should go in order: child, parent, parent's parent ... If not, the parent
76
- * class will be captured before the child, and the property index won't match.
77
- * - If property is not listed, SerializeUnversionedProperties4 will skip the property as int32.
78
- */
74
+ // Class table. If class is missing here, it's assumed that its property list matches property table
75
+ // (declared with BEGIN_PROP_TABLE).
76
+ // Declaration rules:
77
+ // - Classes should go in order: child, parent, parent's parent ... If not, the parent
78
+ // class will be captured before the child, and the property index won't match.
79
+ // - If property is not listed, SerializeUnversionedProperties4 will assume the property is int32.
80
+ // - VERSION_BLOCK has strict syntax
81
+ // - no multiple fields in DROP_.. macros
82
+ // - VERSION_BLOCK with higher version (newer one) should go before lower (older) version
83
+ // - VERSION_BLOCK should be ended with constant 0
84
+ // - all properties inside of the VERSION_BLOCK should go in ascending order
85
+
79
86
static const PropInfo PropData[] =
80
87
{
81
88
BEGIN (" UStaticMesh4" )
@@ -260,7 +267,10 @@ struct ParentInfo
260
267
int MinEngineVersion = 0 ; // allow changing type information with newer engine versions
261
268
};
262
269
263
- // Note: parent classes should be defined after children, so the whole table could be iterated with a single pass
270
+ // Declaration rules:
271
+ // - Parent classes should be defined after children, so the whole table could be iterated with a single pass.
272
+ // - If there's engine-specific class declaration, newer engine declarations should go before the older ones.
273
+
264
274
static const ParentInfo ParentData[] =
265
275
{
266
276
// Texture classes
@@ -350,28 +360,60 @@ const char* CTypeInfo::FindUnversionedProp(int InPropIndex, int& OutArrayIndex,
350
360
p = PropData;
351
361
end = PropData + ARRAY_COUNT (PropData);
352
362
353
- bool bClassFound = false ;
354
363
while (p < end)
355
364
{
356
- // Note: StrucType could correspond to a few classes from the list about
357
- // because of inheritance, so don't "break" a loop when we've scanned some class, check
358
- // other classes too
359
- bool IsOurClass;
360
365
// Use exact name comparison to allow intermediate parents which aren't declared in PropData[].
361
366
// Doing the same in FindTypeForProperty().
362
- IsOurClass = stricmp (p->Name , FoundProp.TypeName ) == 0 ;
367
+ if (stricmp (p->Name , FoundProp.TypeName ) != 0 )
368
+ {
369
+ // A different class, skip its declaration.
370
+ while (++p < end && p->Name != NULL )
371
+ {}
372
+ // skip the END marker and continue with next type map
373
+ p++;
374
+ continue ;
375
+ }
376
+
377
+ bool bPatchingPropIndex = false ;
378
+ int NumInsertedProps = 0 ;
363
379
364
380
// Loop over PropInfo entries, either find a property or an end of the current class information.
365
- while (++p < end && p->Name )
381
+ while (++p < end && p->Name != NULL )
366
382
{
367
- if (!IsOurClass )
383
+ if (p-> Name [ 0 ] == ' ! ' )
368
384
{
369
- // We're just waiting for the end of class, to try matching type name again
385
+ // Adjust the property index so it will match previous engine version
386
+ FoundProp.PropIndex -= NumInsertedProps;
387
+ NumInsertedProps = 0 ;
388
+ // This is the engine version block
389
+ int RequiredVersion = p->PropIndex ;
390
+ bPatchingPropIndex = false ;
391
+ if (InGame < RequiredVersion)
392
+ {
393
+ // Skip the whole block for the engine which is newer than loaded asset use
394
+ while (++p < end && p->Name != NULL && p->Name [0 ] != ' !' )
395
+ {}
396
+ // A step back, so next '++p' in outer loop will check this entry again
397
+ p--;
398
+ }
399
+ else if (RequiredVersion != 0 )
400
+ {
401
+ // The engine version matches, so the following block will probably insert new properties
402
+ bPatchingPropIndex = true ;
403
+ }
370
404
continue ;
371
405
}
372
406
407
+ if (p->PropIndex <= FoundProp.PropIndex && bPatchingPropIndex)
408
+ {
409
+ // This property was inserted in this engine version, so we'll need to adjust
410
+ // property index at the end of version block to match previous engine version.
411
+ NumInsertedProps++;
412
+ }
413
+
373
414
if (p->PropMask )
374
415
{
416
+ assert (bPatchingPropIndex == false ); // no support for masks inside VERSION_BLOCK
375
417
// It is used only for DROP_... macros.
376
418
uint32 IndexWithOffset = FoundProp.PropIndex - p->PropIndex ;
377
419
if (IndexWithOffset > 32 )
@@ -388,26 +430,12 @@ const char* CTypeInfo::FindUnversionedProp(int InPropIndex, int& OutArrayIndex,
388
430
else if (p->PropIndex == FoundProp.PropIndex )
389
431
{
390
432
// Found a matching property.
391
- // todo: not supporting arrays here, arrays relies on property table to match layout of CTypeInfo declaration
433
+ // todo: we're not supporting arrays here, arrays relies on property table to match layout of CTypeInfo declaration
392
434
return p->Name ;
393
435
}
394
436
}
395
437
396
- if (IsOurClass)
397
- {
398
- // the class has been verified, and we didn't find a property
399
- bClassFound = true ;
400
- break ;
401
- }
402
-
403
- // skip the END marker and continue with next type map
404
- p++;
405
- }
406
-
407
- if (bClassFound)
408
- {
409
- // We have a declaration of the class, but didn't find a property. Don't fall to CTypeInfo PROP declarations.
410
- // todo: review later, actually can "return NULL" from inside the loop body
438
+ // The class has been verified, and we didn't find a property. Don't fall to CTypeInfo PROP declarations.
411
439
return NULL ;
412
440
}
413
441
0 commit comments