CNA-BUG-022: ReflectiveTypeReader has no TypeVersion, so an XNA .xnb for a [ContentSerializerTypeVersion] type is rejected
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
modules/content/src/Xna/ContentReader.cpp— ContentReader::InitializeTypeReaders - SupportsVersion enforcementmodules/content/include/Microsoft/Xna/Framework/Content/ContentTypeReader.hpp— ContentTypeReaderBase::SupportsVersion / getTypeVersionProperty defaultsmodules/content/include/Microsoft/Xna/Framework/Content/ReflectiveTypeReader.hpp— ReflectiveTypeReader and ReflectiveTypeReaderBuilder::Register - no version
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).
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- Known issues
- Bug index