Auditing the XNA API surface: the census, xna4-spec and two worked audits
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. Counts computed from the generated census JSON at 009d40f5; XNA behaviour read from IL of the genuine XNA 4.0 Windows assemblies. The census was not re-run and nothing was executed; the census asserts representation, not behaviour.
CNA's generated census reports every documented XNA 4.0 runtime type and member as represented in its public C++ headers: 331 of 331 types and 3,627 of 3,627 members. This page explains how to audit that surface yourself for one type, which references decide which question, what the converted xna4-spec documentation is good for, and why a perfect surface row still says nothing about behaviour. Two worked audits, Ray and Viewport, show the method on real headers at 009d40f5. It is for maintainers changing public API and for anyone quoting a compatibility figure; the reader-facing summary is on XNA 4.0 compatibility.
Which reference decides which question
Four sources describe "the XNA API", and they are not interchangeable:
| Source | What it is at this snapshot | Authority |
|---|---|---|
| Microsoft's XNA 4.0 XML documentation plus the assemblies' metadata | The census input: ten runtime XML files, with member shapes from the matching DLL metadata; the report records each XML file's SHA-256 | API surface: which types and members exist, with which signatures |
| The genuine XNA 4.0 assemblies' IL | What the shipped Windows code actually does | Decisive for "does XNA do this?" on the Windows profile |
| FNA | An independent reimplementation targeting what desktop games call | Behaviour, as CNA's CHECKLIST.md assigns it; where measured XNA and FNA disagree, CNA's project rules follow XNA |
libcna/xna4-spec (fedc17aa) | An offline, machine-readable conversion of Microsoft's reference pages: 544 XML type files in 19 namespace directories, with kinds, syntax, members, overloads, parameters, returns, remarks and platform lists | None by itself: a searchable inventory and a source of review questions |
CHECKLIST.md states the split outright: "FNA is authoritative for BEHAVIOR. The XNA 4.0 reference assemblies are authoritative for API SURFACE", because an audit of CNA against FNA alone can come back clean while real API surface is missing (its example is Song's Album, Artist, Genre and ToString(), absent from FNA and found only by diffing against Microsoft's assemblies). No file in CNA references xna4-spec, and a clean comparison against it is not a CNA gate. Its own README says 550 types in one line while its index, coverage table and files say 544 (the file count at fedc17aa is 545 including index.xml); even a machine-readable conversion is data to validate, not an executable specification. The converted text remains Microsoft's documentation, offered by that repository for informational and educational use.
What the census measures
audit_xna_runtime_surface.py reads the runtime XML files (the Content.Pipeline assembly is excluded as a build-time tool), counts every runtime T: entry including nested and generic types, substitutes two arity-bearing CLR names with their C++ spellings (ContentTypeReader as ContentTypeReaderBase, IPackedVector`1 as IPackedVectorT), and matches members against CNA's public headers through Clang ASTs. Its output, xna-4-runtime-member-coverage.json, is the source of every number below; the script has its own tests in test_audit_xna_runtime_surface.py, and it is run by hand, not by CI. Its inputs live outside the CNA tree: by default the ten runtime XML files and the matching Microsoft DLLs are read from xna4-decomp/dlls beside the CNA checkout (--reference-xml-dir overrides it; the sibling xna4-decomp, not xna4-spec, holds them). DLL metadata is read by compiling tools/xna_runtime_metadata.cs with mcs and running it under mono, and the header side needs clang++. The generated report records reference_xml_dir and a SHA-256 for every XML file and DLL, so an audit should quote those hashes; CHECKLIST.md's manual per-type grep example names yet another local copy of the reference XML.
| Category | Documented | Exact equivalent | Semantic equivalent | Host-language substitution |
|---|---|---|---|---|
| Constructors | 253 | 168 | 84 | 1 |
| Methods | 1,518 | 829 | 633 | 56 |
| Properties | 1,040 | 0 | 1,039 | 1 |
| Fields | 753 | 749 | 4 | 0 |
| Events | 63 | 0 | 63 | 0 |
| Total | 3,627 | 1,746 | 1,823 | 58 |
The report also counts 145 operators, 29 indexers and 661 enum values, all represented, and records zero rows as missing, not applicable or needing review. Two properties of the table are easy to misread. First, "semantic equivalent" is mostly language: a property can never be an exact equivalent because C++ spells it as a getXProperty()/setXProperty() pair, and an event becomes a System::EventHandler<T> field. Second, the 58 substitutions are concentrated where C# idioms have no C++ counterpart: 15 GetEnumerator and 15 explicit IEnumerable.GetEnumerator rows, 4 generic IEnumerable<T>.GetEnumerator rows, 11 Finalize rows, and the generic GetData/SetData families. Every one of the 3,627 rows carries "behavior_assessed": false. The census answers "does a declaration with the documented shape exist?", nothing more.
Read the closure through five layers
A perfect surface score leaves four further questions open, and each needs a different instrument:
- Representation in public C++ headers: the census.
- Classification as exact, semantic or host-language substitution: the census's classification column, which is a statement about spelling.
- Implemented behaviour behind the declaration: reading the implementation, which may be complete, a bounded refusal (a video call without a decoding backend throws
NotSupportedException) or a documented no-op. - Tested behaviour for named values, errors and lifetimes: unit tests, the FNA value harness, CNA's recorded real-XNA probes.
- Renderer- and platform-dependent behaviour once the call reaches a host subsystem:
Effect(GraphicsDevice&, bytes)is represented perfectly and still refused on the 11 identities without a compiled-effect runtime.
A method can be represented perfectly and still contain a bug, a bounded refusal or an untested renderer route. The evidence vocabulary for the later layers is on compatibility levels and the evidence vector.
A reproducible audit of one type
For one type, a disciplined comparison has six steps:
- Pin the revisions: CNA, the reference XML and assemblies, FNA and (if used)
xna4-spec. - Extract fields, properties, constructors, methods, events and overload signatures from the structured records, not from prose mentions.
- Map C# idioms explicitly, using the table below.
- Compare CNA's public header and classify each row as exact surface, accepted language adaptation, marked extension (
CNAEXT), unexplained extra or missing. - Read the remarks and the implementation of every hand-written accessor or behaviour-bearing method.
- Attach an executable oracle for the semantics, and record the cases the documentation cannot decide.
| C# form | CNA form | Classify as |
|---|---|---|
Property P | getPProperty() / setPProperty() | Language adaptation (semantic) |
ref/out parameters | Reference-parameter overloads (std::optional<float>& result) | Language adaptation |
Equals(object) | Equals(const std::any&) | Language adaptation |
int GetHashCode() | std::size_t GetHashCode() const | Language adaptation |
| Operators | Free or member C++ operators | Exact |
Indexer Item | operator[] | Language adaptation |
Iterators, Finalize | C++ iteration marked CNAEXT; destructors | Host-language substitution |
| Implicit struct default | An explicit default constructor | Language adaptation, not an extension |
CNA's checklist gives the mechanical first pass per type: grep the reference XML for the type's P:, M: and F: entries (grep -E 'name="[PMF]:Microsoft\.Xna\.Framework\.<Ns>\.<Type>\.') and map P: to the accessor pair, M: to the same method name and F: to a field, enum value or constant. It also names the idiom equivalents that must not be reported as gaps, including GetEnumerator as begin()/end() and the explicit non-generic IEnumerable enumerator as having no C++ equivalent.
The mapping table is part of the result. Without it, Equals(object) looks missing on a value type with no universal boxed root, and a default constructor or an iterator looks like an unmarked XNA extension although it exists only for the target language's ordinary value and container idioms. Unmarked additions are what the strict build catches: CNA_STRICT_XNA_API turns CNAEXT into [[deprecated]] and CNA's harness StrictXnaApiSurfaceCheck.cpp compiles against it.
Normalising the converted documentation
xna4-spec's records need normalising before a script compares them. In Microsoft.Xna.Framework/Ray.xml each output-parameter Intersects overload appears twice, because the original reference pages were per platform: one record lists Windows Phone and spells the result parameter Nullable<Single>, the other lists Xbox 360 and Windows and spells it Nullable<float>, with identical signatures otherwise. The equality operators are recorded with the signature op_Equality() and no parameter list, although their parameter elements name a and b. A script that compares signature strings blindly invents duplicates and gaps; cross-check against the reference member identifiers (for example Microsoft.Xna.Framework.Ray.Intersects(Microsoft.Xna.Framework.BoundingBox@,System.Nullable{System.Single}@) in the census) whenever converted fields disagree.
Worked audit: Ray
The census counts 16 documented members for Ray: the two fields, the two-vector constructor, Equals(Ray), Equals(Object), GetHashCode, ToString, seven Intersects overloads and the two equality operators. Ray.hpp declares Position and Direction as public fields, Ray() = default and Ray(Vector3, Vector3), Equals(const std::any&) and Equals(Ray), std::size_t GetHashCode(), value-returning Intersects for BoundingBox, BoundingSphere, Plane and BoundingFrustum (each returning std::optional<float>), output-reference forms for box, sphere and plane (XNA has none for the frustum), ToString, and free operator==/operator!=. The census classifies 11 rows exact and 5 semantic: Equals(Object), GetHashCode and the three output-reference overloads.
The audit's classification work is in the adaptations. The default constructor mirrors C#'s implicit struct default. Equals(Object) is present as Equals(const std::any&), which matches only when the std::any holds a Ray (Ray.cpp); it was added in XNA-MISSING-002 (2026-09-20), so audits written before then list it as omitted. GetHashCode returns std::size_t rather than int, and reading both implementations shows a value difference the signature hides: XNA's IL adds the two vector hashes, CNA combines them with ^. Hash values are not part of XNA's documented contract, so this is a recorded adaptation rather than a defect, but it is exactly the kind of fact that only step 5 finds. The intersection semantics themselves are a behaviour question with their own evidence (rays and their four intersection rules).
Worked audit: Viewport
The census counts 14 documented Viewport members: the (int, int, int, int) and (Rectangle) constructors, nine properties (AspectRatio, Bounds, Height, MaxDepth, MinDepth, TitleSafeArea, Width, X, Y), and Project, Unproject and ToString; 5 exact, 9 semantic. Viewport.hpp provides six of the properties as generated DEF_PROP accessors and AspectRatio, Bounds and TitleSafeArea as hand-written ones, which is where step 5 applies.
Reading Viewport.cpp against the genuine XNA 4.0 Windows graphics assembly's IL:
| Member | CNA at 009d40f5 | XNA 4.0 (Windows assembly IL) |
|---|---|---|
| Default construction | Viewport(): every field zero, including MaxDepth | The implicit struct default: every field zero |
Four-integer and Rectangle constructors | MinDepth 0, MaxDepth 1 | Same |
AspectRatio | 0 when width or height is 0, else width divided by height | Same guard and division |
TitleSafeArea | Returns Bounds unchanged | Calls an internal GetTitleSafeArea(x, y, w, h) that returns new Rectangle(x, y, w, h): the bounds unchanged |
TitleSafeArea is the instructive row. The documentation describes a sub-rectangle guaranteed visible on lower-quality displays, and a surface comparison cannot tell whether an implementation that returns the full bounds honours it. CNA pins its choice with ViewportTest.TitleSafeAreaEqualsBounds (ViewportTests.cpp), which proves only that the behaviour is stable. What settles it is the Windows assembly's own code, which returns the bounds unchanged too; the Xbox 360 runtime, where a real safe area mattered, is not decided by that assembly and remains outside what CNA's references can show. The right outcome of such an audit is therefore a recorded, sourced behaviour ("matches the Windows XNA 4.0 assembly"), not a surface percentage. The same reading also corrects an easy assumption about the default constructor: it yields a 0-to-0 depth range, as XNA's struct default does, not 0 to 1.
What inventories cannot prove
An API inventory cannot establish default values produced at run time, floating-point edge behaviour, exception type and timing, lifetime and disposal order, GPU state, wire compatibility, content decoding or platform substitution. It cannot tell whether a nominally present method is a stub, a safe refusal or a complete implementation. Use each source for what it can decide:
xna4-specto generate questions and completeness rows;- the strict
CNAEXTbuild to detect unmarked additions; - the reference XML and assemblies to settle surface, and their IL to settle what the Windows runtime does;
- FNA or real XNA runs to settle semantics, and the oracle hierarchy (choosing the authority before the tolerance) to produce verdicts.
The most trustworthy audit is not the one with the highest percentage but the one that keeps these authorities separate instead of collapsing them into one number. When an audit finds a real behavioural difference, the change procedure is changing public XNA behaviour.
Evidence and limits
Checked by reading, at 009d40f5: the census script and its generated JSON (all counts on this page are computed from that file), CHECKLIST.md, Ray.hpp/Ray.cpp, Viewport.hpp/Viewport.cpp and ViewportTests.cpp. XNA behaviour was read from IL disassembly of the genuine XNA 4.0 Windows assemblies (Ray.GetHashCode, Viewport's constructors, get_AspectRatio and get_TitleSafeArea). xna4-spec was read at fedc17aa (2026-09-13); only its README and one image differ from the older 8f61207. Nothing was built or executed; the census was not re-run.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- User guide
- What 331/331 and 3,627/3,627 mean
- Architecture
- Architecture overview
- Maintainer workflow
- Change public XNA behaviour · Using the XNA oracle as evidence
- Reference
- Public headers