#region License /* FNA - XNA4 Reimplementation for Desktop Platforms * Copyright 2009-2020 Ethan Lee and the MonoGame Team * * Released under the Microsoft Public License. * See LICENSE for details. */ /* Derived from code by the Mono.Xna Team (Copyright 2006). * Released under the MIT License. See monoxna.LICENSE for details. */ #endregion #region Using Statements using System; using System.Collections.Generic; using System.IO; #endregion namespace Microsoft.Xna.Framework.Content { public sealed class ContentReader : BinaryReader { #region Public Properties public ContentManager ContentManager { get { return contentManager; } } public string AssetName { get { return assetName; } } #endregion #region Internal Properties internal ContentTypeReader[] TypeReaders { get { return typeReaders; } } #endregion #region Internal Variables internal int version; internal char platform; #endregion #region Private Variables private ContentManager contentManager; private Action recordDisposableObject; private ContentTypeReaderManager typeReaderManager; private ContentTypeReader[] typeReaders; private string assetName; /* From what I can tell, shared resources work like this: * A list of shared resources is stored at the end of the file, * and while we're reading the whole object, the parts that ask * for "shared" objects store the 1-based index of the shared * resource in the list. For example, if there are two shared * resources, a ReadSharedResource function will ask for either * 1 or 2. For null references, the index will be 0. */ private int sharedResourceCount; private List>[] sharedResourceFixups; #endregion #region Internal Constructor internal ContentReader( ContentManager manager, Stream stream, string assetName, int version, char platform, Action recordDisposableObject ) : base(stream) { this.recordDisposableObject = recordDisposableObject; this.contentManager = manager; this.assetName = assetName; this.version = version; this.platform = platform; } #endregion #region Public Read Methods public T ReadExternalReference() { string externalReference = ReadString(); if (!String.IsNullOrEmpty(externalReference)) { return contentManager.Load( MonoGame.Utilities.FileHelpers.ResolveRelativePath(assetName, externalReference) ); } return default(T); } public Matrix ReadMatrix() { Matrix result = new Matrix(); result.M11 = ReadSingle(); result.M12 = ReadSingle(); result.M13 = ReadSingle(); result.M14 = ReadSingle(); result.M21 = ReadSingle(); result.M22 = ReadSingle(); result.M23 = ReadSingle(); result.M24 = ReadSingle(); result.M31 = ReadSingle(); result.M32 = ReadSingle(); result.M33 = ReadSingle(); result.M34 = ReadSingle(); result.M41 = ReadSingle(); result.M42 = ReadSingle(); result.M43 = ReadSingle(); result.M44 = ReadSingle(); return result; } public T ReadObject() { return ReadObject(default(T)); } public T ReadObject(ContentTypeReader typeReader) { T result = (T) typeReader.Read(this, default(T)); RecordDisposable(result); return result; } public T ReadObject(T existingInstance) { return InnerReadObject(existingInstance); } public T ReadObject(ContentTypeReader typeReader, T existingInstance) { if (!typeReader.TargetType.IsValueType) { return ReadObject(existingInstance); } T result = (T) typeReader.Read(this, existingInstance); RecordDisposable(result); return result; } public Quaternion ReadQuaternion() { Quaternion result = new Quaternion(); result.X = ReadSingle(); result.Y = ReadSingle(); result.Z = ReadSingle(); result.W = ReadSingle(); return result; } public T ReadRawObject() { return (T) ReadRawObject(default(T)); } public T ReadRawObject(ContentTypeReader typeReader) { return (T) ReadRawObject(typeReader, default(T)); } public T ReadRawObject(T existingInstance) { Type objectType = typeof(T); foreach (ContentTypeReader typeReader in typeReaders) { if (typeReader.TargetType == objectType) { return (T) ReadRawObject(typeReader,existingInstance); } } throw new NotSupportedException(); } public T ReadRawObject(ContentTypeReader typeReader, T existingInstance) { return (T) typeReader.Read(this, existingInstance); } public void ReadSharedResource(Action fixup) { int index = Read7BitEncodedInt(); if (index > 0) { sharedResourceFixups[index - 1].Add( delegate(object v) { if (!(v is T)) { throw new ContentLoadException( String.Format( "Error loading shared resource. Expected type {0}, received type {1}", typeof(T).Name, v.GetType().Name ) ); } fixup((T) v); } ); } } public Vector2 ReadVector2() { Vector2 result = new Vector2(); result.X = ReadSingle(); result.Y = ReadSingle(); return result; } public Vector3 ReadVector3() { Vector3 result = new Vector3(); result.X = ReadSingle(); result.Y = ReadSingle(); result.Z = ReadSingle(); return result; } public Vector4 ReadVector4() { Vector4 result = new Vector4(); result.X = ReadSingle(); result.Y = ReadSingle(); result.Z = ReadSingle(); result.W = ReadSingle(); return result; } public Color ReadColor() { Color result = new Color(); result.R = ReadByte(); result.G = ReadByte(); result.B = ReadByte(); result.A = ReadByte(); return result; } #endregion #region Internal Methods internal object ReadAsset() { InitializeTypeReaders(); // Read primary object object result = ReadObject(); // Read shared resources ReadSharedResources(); return result; } internal void InitializeTypeReaders() { typeReaderManager = new ContentTypeReaderManager(); typeReaders = typeReaderManager.LoadAssetReaders(this); sharedResourceCount = Read7BitEncodedInt(); sharedResourceFixups = new List>[sharedResourceCount]; for (int i = 0; i < sharedResourceCount; i += 1) { sharedResourceFixups[i] = new List>(); } } internal void ReadSharedResources() { for (int i = 0; i < sharedResourceCount; i += 1) { // Load the shared object... object sharedResource = InnerReadObject(null); // ... then send it to each ReadSharedResource caller foreach (Action fixup in sharedResourceFixups[i]) { fixup(sharedResource); } } } internal new int Read7BitEncodedInt() { return base.Read7BitEncodedInt(); } internal BoundingSphere ReadBoundingSphere() { Vector3 position = ReadVector3(); float radius = ReadSingle(); return new BoundingSphere(position, radius); } #endregion #region Private Methods private T InnerReadObject(T existingInstance) { int typeReaderIndex = Read7BitEncodedInt(); if (typeReaderIndex == 0) { return existingInstance; } if (typeReaderIndex > typeReaders.Length) { throw new ContentLoadException( "Incorrect type reader index found!" ); } ContentTypeReader typeReader = typeReaders[typeReaderIndex - 1]; T result = (T) typeReader.Read(this, default(T)); RecordDisposable(result); return result; } private void RecordDisposable(T result) { IDisposable disposable = result as IDisposable; if (disposable == null) { return; } if (recordDisposableObject != null) { recordDisposableObject(disposable); } else { contentManager.RecordDisposable(disposable); } } #endregion } }