CNA-BUG-022: ReflectiveTypeReader has no TypeVersion, so an XNA .xnb for a [ContentSerializerTypeVersion] type is rejected

CNA snapshot 009d40f5  ·  Known Issues › Current bugs  ·  source links pinned to 009d40f5

✓

Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.

Reader versions must match exactly, as in XNA, but a reflective reader always reports version 0 and ReflectiveTypeReaderBuilder cannot declare the version XNA's ReflectiveReader takes from the attribute.

Identifier
CNA-BUG-022
Category
Bug
Subsystem
Content & XNB/CNB/CNJ
Status
Open
Verified against
CNA 009d40f5 (009d40f5dd085c4e674d3479675fac84b12b3e0a)
Severity
Low (a triage suggestion, not a project priority)
Evidence basis
Source-verified: read at TARGET, not executed
Tests touching this area
Yes: see Current tests
Affected contract
ReflectiveTypeReader and ReflectiveTypeReaderBuilder (ContentTypeReaderBase::getTypeVersionProperty / SupportsVersion); XNA 4.0 ReflectiveReader.TypeVersion

Expected behaviour

XNA 4.0's ContentTypeReaderManager.ReadTypeManifest rejects a type-reader table entry whose version differs from the reader's TypeVersion, and ReflectiveReader<T>.TypeVersion is the value of the type's [ContentSerializerTypeVersion] attribute, which XNA's automatic writer records beside the reader name (CNA's own ContentSerializerTypeVersionAttribute.hpp says the same). A CNA port that declares the reflective reader for such a type should load assets built with that version.

Actual behaviour at TARGET

ContentReader::InitializeTypeReaders (ContentReader.cpp) calls SupportsVersion(entry.version), which by default is an exact match against getTypeVersionProperty(), which by default is 0 (ContentTypeReader.hpp). ReflectiveTypeReader<T>, ReflectiveSharedTypeReader, AbstractReflectiveTypeReader and EnumTypeReader override neither, and ReflectiveTypeReaderBuilder<T> (ReflectiveTypeReader.hpp, no version identifier anywhere in the file) has no way to set one. An XNA-built asset whose reflective entry carries version 2 fails with ContentLoadException "… at an unsupported version (2)".

The broader statement that every nonzero reader version is refused is true but, for the built-in readers, XNA-faithful: XNA's built-in readers are all version 0 and XNA enforces the same equality, so a nonzero version on a built-in reader name is not content XNA would load either. That part is not the defect.

Source locations

Evidence

Checked by reading at 009d40f5; not executed. Compared with the decompiled XNA 4.0 ContentTypeReaderManager.ReadTypeManifest and ReflectiveReader constructor. Hand-written readers can override getTypeVersionProperty() (the C API readers in CnaCApiContentReaders.cpp and the content-pipeline compiler tests do), so the gap is confined to the reflective route. This entry narrows the original report's wording from "any nonzero version" to the reflective case by reading; no code changed.

Focused reproduction

Illustrative; not compiled.

// XNA 4.0 game type, built by XNA's pipeline:
//   [ContentSerializerTypeVersion(2)] public class LevelData { public int Width; }
struct LevelData { int Width = 0; };

Content::ReflectiveTypeReaderBuilder<LevelData>("MyGame.LevelData")
    .Field(&LevelData::Width)
    .Register();
LevelData level = content.Load<LevelData>("level1");
// throws ContentLoadException: "... at an unsupported version (2)"

Current tests

ContentReaderTests.cpp (ReaderVersionMismatchThrowsContentLoadException) pins the strict rule with a hand-written reader. No reflective-reader test uses a nonzero version.

Regression test

Give ReflectiveTypeReaderBuilder a way to declare the version (for example a TypeVersion(int) step), then a test that builds an in-memory XNB whose reflective entry says version 2, loads it with a builder declaring 2, and refuses it with a builder declaring 0.

Blast radius

Only XNA content whose game types carry [ContentSerializerTypeVersion] and are loaded through the reflective builder. Built-in readers, hand-written ContentTypeReader<T> subclasses and unversioned reflective types are unaffected.

Workaround

Register a hand-written ContentTypeReader<T> (or a subclass of ReflectiveTypeReader<T>) that overrides getTypeVersionProperty(), with ContentTypeReaderManager::AddTypeCreator under the same canonical reader name, instead of calling the builder's Register() (the first registration for a name is the one that stays).

The same subject is explained at several altitudes. These are the neighbouring pages at each one.

Known issues
Bug index