#region Using Statements using System; using System.Collections.Generic; using System.IO; using System.Reflection; using ANX.Framework.Graphics; using ANX.Framework.NonXNA.Reflection; using ANX.Framework.NonXNA.Development; #endregion // Using Statements // This file is part of the ANX.Framework created by the // "ANX.Framework developer group" and released under the Ms-PL license. // For details see: http://anxframework.codeplex.com/license namespace ANX.Framework.Content { [Developer("GinieDP")] [TestState(TestStateAttribute.TestState.Untested)] public sealed class ContentReader : BinaryReader { private GraphicsProfile graphicsProfile; private List>[] sharedResourceFixups; private ContentTypeReader[] typeReaders; public ContentManager ContentManager { get; private set; } public bool AnxExtensions { get; private set; } public string AssetName { get; private set; } private string assetDirectory; private ContentReader(ContentManager contentManager, Stream input, string assetName, GraphicsProfile graphicsProfile, bool anxExtensions) : base(input) { this.AnxExtensions = anxExtensions; this.ContentManager = contentManager; this.AssetName = assetName; this.graphicsProfile = graphicsProfile; this.assetDirectory = ""; int separatorIndex = assetName.LastIndexOfAny(new char[] { '\\', '/' }); if (separatorIndex >= 0) { this.assetDirectory = assetName.Substring(0, separatorIndex); } } public static ContentReader Create( ContentManager contentManager, Stream input, string assetName) { GraphicsProfile profile; bool anxExtensions; input = ContentReader.ReadXnbHeader(input, assetName, out profile, out anxExtensions); return new ContentReader(contentManager, input, assetName, profile, anxExtensions); } public static T ReadAsset(ContentManager contentManager, Stream input, string assetName) { GraphicsProfile profile; bool anxExtensions; input = ContentReader.ReadXnbHeader(input, assetName, out profile, out anxExtensions); return new ContentReader(contentManager, input, assetName, profile, anxExtensions).ReadAsset(); } private static Stream ReadXnbHeader(Stream input, string assetName, out GraphicsProfile graphicsProfile, out bool anxExtensions) { // read the XNB file information // // | Type | Description | example/value // |--------|----------------------|-------------------------------- // | Byte | Format identifier | X (88) // |--------|----------------------|-------------------------------- // | Byte | Format identifier | N (78) // |--------|----------------------|-------------------------------- // | Byte | Format identifier | B (66) // |--------|----------------------|-------------------------------- // | Byte | Target platform | w = Microsoft Windows // | | | m = Windows Phone 7 // | | | x = Xbox 360 // |--------|----------------------|-------------------------------- // | Byte | XNB format version | 5 = XNA Game Studio 4.0 // | | | 6 = future XNA version // | | | OR anx version if the next three bytes are ANX // |--------|----------------------|-------------------------------- // | Byte | Flag bits | Bit 0x01 = content is for HiDef profile (otherwise Reach) // | | | Bit 0x80 = asset data is compressed // |--------|----------------------|-------------------------------- // | UInt32 | Compressed file size | Total size of the (optionally compressed) // | | | .xnb file as stored on disk (including this header block) BinaryReader reader = new BinaryReader(input); anxExtensions = false; byte magicX = reader.ReadByte(); byte magicN = reader.ReadByte(); byte magicB = reader.ReadByte(); // The first three bytes must be the characters XNB if (magicX != 'X' || magicN != 'N' || magicB != 'B') { throw new ContentLoadException("Not an XNB file."); } byte targetPlattform = reader.ReadByte(); switch ((char)targetPlattform) { case 'w': // windows plattform OK break; case 'm': case 'x': default: throw new ContentLoadException("Invalid or unknown target plattform."); } byte formatVersion = reader.ReadByte(); if (formatVersion == 6) { byte magicVA = reader.ReadByte(); // A byte magicVN = reader.ReadByte(); // N byte magicVX = reader.ReadByte(); // X if (magicVA != 'A' || magicVN != 'N' || magicVX != 'X') { throw new ContentLoadException("Not an ANX.Framework version 1.0 XNB file or this is an unsupported past XNA 4.0 XNB file."); } anxExtensions = true; } else if (formatVersion != 5) { throw new ContentLoadException("Not an XNA Game Studio version 4.0 XNB file."); } byte flags = reader.ReadByte(); graphicsProfile = ((flags & 0x01) == 0x01 ? GraphicsProfile.HiDef : GraphicsProfile.Reach); bool isCompressed = (flags & 0x80) != 0; int sizeOnDisk = reader.ReadInt32(); if (input.CanSeek && ((sizeOnDisk - (anxExtensions ? 13 : 10)) > (input.Length - input.Position))) { throw new ContentLoadException("Bad XNB file size."); } if (isCompressed) { return Decompressor.DecompressStream(reader, input, sizeOnDisk); } else { return input; } } private int ReadXnbResourceManifest() { int numTypes = base.Read7BitEncodedInt(); this.typeReaders = ContentTypeReaderManager.ReadXnbTypeManifest(numTypes, this); int numSharedResources = base.Read7BitEncodedInt(); if (numSharedResources > 0) { this.sharedResourceFixups = new List>[numSharedResources]; for (int i = 0; i < numSharedResources; i++) { this.sharedResourceFixups[i] = new List>(); } } return numSharedResources; } internal T ReadAsset() { T asset; try { int numSharedResources = this.ReadXnbResourceManifest(); asset = this.ReadObject(); if (numSharedResources > 0) { object[] sharedResources = new object[numSharedResources]; for (int i = 0; i < numSharedResources; i++) { sharedResources[i] = this.ReadObject(); } for (int j = 0; j < numSharedResources; j++) { foreach (Action action in this.sharedResourceFixups[j]) { action.Invoke(sharedResources[j]); } } } } catch (IOException e) { throw new ContentLoadException("Bad Xnb", e); } return asset; } public T ReadObject() { int index = base.Read7BitEncodedInt(); if (index == 0) { return default(T); } index -= 1; if (index >= this.typeReaders.Length) { throw new ContentLoadException("Bad Xnb"); } ContentTypeReader reader = this.typeReaders[index]; return this.ReadWithTypeReader(reader, null); } public T ReadObject(T existingInstance) { int index = base.Read7BitEncodedInt(); if (index == 0) { return default(T); } if (index > this.typeReaders.Length) { throw new ContentLoadException("Bad Xnb"); } ContentTypeReader reader = this.typeReaders[index-1]; return this.ReadWithTypeReader(reader, existingInstance); } public T ReadObject(ContentTypeReader typeReader) { if (typeReader == null) { throw new ArgumentNullException("typeReader"); } if (TypeHelper.IsValueType(typeReader.TargetType)) { return this.ReadWithTypeReader(typeReader, null); } return this.ReadObject(null); } public T ReadObject(ContentTypeReader typeReader, T existingInstance) { if (typeReader == null) { throw new ArgumentNullException("typeReader"); } if (TypeHelper.IsValueType(typeReader.TargetType)) { return this.ReadWithTypeReader(typeReader, existingInstance); } return this.ReadObject(existingInstance); } public T ReadRawObject() { ContentTypeReader typeReader = ContentTypeReaderManager.GetTypeReader(typeof(T), this); return this.ReadWithTypeReader(typeReader, null); } public T ReadRawObject(T existingInstance) { ContentTypeReader typeReader = ContentTypeReaderManager.GetTypeReader(typeof(T), this); return this.ReadWithTypeReader(typeReader, existingInstance); } public T ReadRawObject(ContentTypeReader typeReader) { if (typeReader == null) { throw new ArgumentNullException("typeReader"); } return this.ReadWithTypeReader(typeReader, null); } public T ReadRawObject(ContentTypeReader typeReader, T existingInstance) { if (typeReader == null) { throw new ArgumentNullException("typeReader"); } return this.ReadWithTypeReader(typeReader, existingInstance); } private T ReadWithTypeReader(ContentTypeReader reader, object existingInstance) { ContentTypeReader contentTypeReader = reader as ContentTypeReader; T asset; if (contentTypeReader != null) { existingInstance = existingInstance ?? default(T); asset = contentTypeReader.Read(this, (T)existingInstance); } else { object obj = reader.Read(this, existingInstance) ?? default(T); if (obj != null && !(obj is T)) { throw new ContentLoadException("Bad xnb, wrong type"); } asset = (T)obj; } if (existingInstance != null && TypeHelper.IsValueType(existingInstance.GetType()) == false && object.ReferenceEquals(existingInstance, asset) == false) { throw new ContentLoadException("Reader constructed new instance"); } return asset; } public void ReadSharedResource(Action fixup) { if (fixup == null) { throw new ArgumentNullException("fixup"); } int resourceNumber = base.Read7BitEncodedInt(); if (resourceNumber == 0) { return; } int resourceIndex = resourceNumber - 1; if (resourceIndex >= this.sharedResourceFixups.Length) { throw new ContentLoadException("Bad XNB"); } this.sharedResourceFixups[resourceIndex].Add((value) => { if (value is T) { fixup((T)value); } else { throw new ContentLoadException("Bad XNB"); } }); } public T ReadExternalReference() { string assetReference = base.ReadString(); if (String.IsNullOrEmpty(assetReference)) { return default(T); } string assetLocation = Path.Combine(assetDirectory, assetReference); return this.ContentManager.Load(assetLocation); } public override float ReadSingle() { // This is handeled with unsafe code in the original implementation. // The original implementation reads as UInt32 and does some pointer magic to copy the UInt bits into the float. // The same "pointer magic" is done by the ReadSingle method of the binary reader already. return base.ReadSingle(); } public override double ReadDouble() { // This is handeled with unsafe code in the original implementation. // The original implementation reads as UInt64 and does some pointer magic to copy the UInt bits into the float. // The same "pointer magic" is done by the ReadDouble method of the binary reader already. return base.ReadDouble(); } public Color ReadColor() { var result = new Color(); result.PackedValue = base.ReadUInt32(); return result; } public Matrix ReadMatrix() { var result = new Matrix(); result.M11 = this.ReadSingle(); result.M12 = this.ReadSingle(); result.M13 = this.ReadSingle(); result.M14 = this.ReadSingle(); result.M21 = this.ReadSingle(); result.M22 = this.ReadSingle(); result.M23 = this.ReadSingle(); result.M24 = this.ReadSingle(); result.M31 = this.ReadSingle(); result.M32 = this.ReadSingle(); result.M33 = this.ReadSingle(); result.M34 = this.ReadSingle(); result.M41 = this.ReadSingle(); result.M42 = this.ReadSingle(); result.M43 = this.ReadSingle(); result.M44 = this.ReadSingle(); return result; } public Vector2 ReadVector2() { var result = new Vector2(); result.X = this.ReadSingle(); result.Y = this.ReadSingle(); return result; } public Vector3 ReadVector3() { var result = new Vector3(); result.X = this.ReadSingle(); result.Y = this.ReadSingle(); result.Z = this.ReadSingle(); return result; } public Vector4 ReadVector4() { var result = new Vector4(); result.X = this.ReadSingle(); result.Y = this.ReadSingle(); result.Z = this.ReadSingle(); result.W = this.ReadSingle(); return result; } public Quaternion ReadQuaternion() { var result = new Quaternion(); result.X = this.ReadSingle(); result.Y = this.ReadSingle(); result.Z = this.ReadSingle(); result.W = this.ReadSingle(); return result; } internal Graphics.GraphicsDevice ResolveGraphicsDevice() { var service = this.ContentManager.ServiceProvider.GetService(typeof(IGraphicsDeviceService)) as IGraphicsDeviceService; if (service == null) { var device2 = ContentManager.ServiceProvider.GetService(typeof (GraphicsDevice)) as GraphicsDevice; if (device2 == null) throw new ContentLoadException("Service not found: IGraphicsDeviceService"); return device2; } var device = service.GraphicsDevice; if (device == null) { throw new ContentLoadException("Graphics device missing"); } return device; } internal string GetAbsolutePathToReference(string referenceName) { referenceName = GetPathToReference(referenceName); referenceName = Path.Combine(ContentManager.RootDirectory, referenceName); Assembly assembly = null; string titleLocationPath = null; #if !WINDOWSMETRO assembly = Assembly.GetEntryAssembly(); if (assembly == null) { assembly = Assembly.GetCallingAssembly(); } titleLocationPath = Path.GetDirectoryName(assembly.Location); #else throw new NotImplementedException(); //TODO: find solution for metro #endif referenceName = Path.Combine(titleLocationPath, referenceName); return TitleContainer.GetCleanPath(referenceName); } private string GetPathToReference(string referenceName) { return Path.Combine(Path.GetDirectoryName(AssetName), referenceName); } } }