298 lines
9.7 KiB
C#
298 lines
9.7 KiB
C#
#region Using Statements
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.IO;
|
|
using ANX.Framework.Graphics;
|
|
|
|
#endregion
|
|
|
|
// 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.Pipeline.Serialization.Compiler
|
|
{
|
|
public sealed class ContentWriter : BinaryWriter
|
|
{
|
|
#region Private Members
|
|
private ContentCompiler compiler;
|
|
private Boolean compressContent;
|
|
private string rootDirectory;
|
|
private string referenceRelocationPath;
|
|
private Dictionary<Type, int> typeTable = new Dictionary<Type, int>();
|
|
private List<ContentTypeWriter> typeWriters = new List<ContentTypeWriter>();
|
|
private List<object> sharedResources = new List<object>();
|
|
|
|
private Stream outputStream;
|
|
private MemoryStream header = new MemoryStream();
|
|
private MemoryStream content = new MemoryStream();
|
|
|
|
#endregion
|
|
|
|
const byte xnbFormatVersion = (byte)6;
|
|
char[] xnbMagicWord = new char[] { 'X', 'N', 'B' };
|
|
|
|
internal ContentWriter(ContentCompiler compiler, Stream output, bool compressContent, string rootDirectory, string referenceRelocationPath)
|
|
{
|
|
this.compiler = compiler;
|
|
this.compressContent = compressContent;
|
|
this.rootDirectory = rootDirectory;
|
|
this.referenceRelocationPath = referenceRelocationPath;
|
|
|
|
this.outputStream = output;
|
|
this.OutStream = content;
|
|
}
|
|
|
|
#region Write value types
|
|
public void Write(Color value)
|
|
{
|
|
base.Write(value.PackedValue);
|
|
}
|
|
|
|
public void Write(Matrix value)
|
|
{
|
|
base.Write(value.M11);
|
|
base.Write(value.M12);
|
|
base.Write(value.M13);
|
|
base.Write(value.M14);
|
|
base.Write(value.M21);
|
|
base.Write(value.M22);
|
|
base.Write(value.M23);
|
|
base.Write(value.M24);
|
|
base.Write(value.M31);
|
|
base.Write(value.M32);
|
|
base.Write(value.M33);
|
|
base.Write(value.M34);
|
|
base.Write(value.M41);
|
|
base.Write(value.M42);
|
|
base.Write(value.M43);
|
|
base.Write(value.M44);
|
|
}
|
|
|
|
public void Write(Quaternion value)
|
|
{
|
|
base.Write(value.X);
|
|
base.Write(value.Y);
|
|
base.Write(value.Z);
|
|
base.Write(value.W);
|
|
}
|
|
|
|
public void Write(Vector2 value)
|
|
{
|
|
base.Write(value.X);
|
|
base.Write(value.Y);
|
|
}
|
|
|
|
public void Write(Vector3 value)
|
|
{
|
|
base.Write(value.X);
|
|
base.Write(value.Y);
|
|
base.Write(value.Z);
|
|
}
|
|
|
|
public void Write(Vector4 value)
|
|
{
|
|
base.Write(value.X);
|
|
base.Write(value.Y);
|
|
base.Write(value.Z);
|
|
base.Write(value.W);
|
|
}
|
|
|
|
#endregion
|
|
|
|
public void WriteExternalReference<T>(ExternalReference<T> reference)
|
|
{
|
|
if (reference == null || String.IsNullOrEmpty(reference.Filename))
|
|
{
|
|
Write(String.Empty);
|
|
return;
|
|
}
|
|
|
|
Write(Path.GetFileNameWithoutExtension(reference.Filename));
|
|
}
|
|
|
|
public void WriteObject<T>(T value)
|
|
{
|
|
if (value == null)
|
|
{
|
|
base.Write7BitEncodedInt(0);
|
|
return;
|
|
}
|
|
|
|
int typeIndex;
|
|
ContentTypeWriter typeWriter = this.GetTypeWriter(value.GetType(), out typeIndex);
|
|
if (typeWriter == null)
|
|
throw new InvalidOperationException(string.Format("Can't find a type writer for {0}.", value.GetType()));
|
|
|
|
base.Write7BitEncodedInt(typeIndex + 1);
|
|
|
|
//TODO: test for recursive cyclic calls
|
|
this.InvokeWriter<T>(value, typeWriter);
|
|
}
|
|
|
|
public void WriteObject<T>(T value, ContentTypeWriter typeWriter)
|
|
{
|
|
if (value == null)
|
|
{
|
|
base.Write7BitEncodedInt(0);
|
|
return;
|
|
}
|
|
|
|
if (typeWriter.TargetIsValueType)
|
|
{
|
|
this.InvokeWriter<T>(value, typeWriter);
|
|
return;
|
|
}
|
|
|
|
this.WriteObject<T>(value);
|
|
}
|
|
|
|
public void WriteRawObject<T>(T value)
|
|
{
|
|
if (value == null)
|
|
{
|
|
throw new ArgumentNullException("value");
|
|
}
|
|
|
|
int typeIndex;
|
|
ContentTypeWriter typeWriter = this.GetTypeWriter(typeof(T), out typeIndex);
|
|
this.InvokeWriter<T>(value, typeWriter);
|
|
}
|
|
|
|
public void WriteRawObject<T>(T value, ContentTypeWriter typeWriter)
|
|
{
|
|
if (value == null)
|
|
{
|
|
throw new ArgumentNullException("value");
|
|
}
|
|
if (typeWriter == null)
|
|
{
|
|
throw new ArgumentNullException("typeWriter");
|
|
}
|
|
this.InvokeWriter<T>(value, typeWriter);
|
|
}
|
|
|
|
public void WriteSharedResource<T>(T value)
|
|
{
|
|
if (value == null)
|
|
{
|
|
//Index 0 is reserved for null
|
|
Write7BitEncodedInt(0);
|
|
}
|
|
else
|
|
{
|
|
int sharedResourceIndex = sharedResources.IndexOf(value);
|
|
if (sharedResourceIndex == -1)
|
|
{
|
|
sharedResourceIndex = sharedResources.Count;
|
|
sharedResources.Add(value);
|
|
}
|
|
base.Write7BitEncodedInt(sharedResourceIndex + 1);
|
|
}
|
|
}
|
|
|
|
public TargetPlatform TargetPlatform
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public GraphicsProfile TargetProfile
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
WriteData();
|
|
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
private void WriteData()
|
|
{
|
|
OutStream = content;
|
|
|
|
//We wrote the main object previously in the content stream.
|
|
//We also have to execute the shared resources writer before we write the typeWriters list,
|
|
//because we may add additional type writers here.
|
|
foreach (var resource in sharedResources)
|
|
{
|
|
this.WriteObject(resource);
|
|
}
|
|
|
|
OutStream = header;
|
|
|
|
//Write type writers
|
|
Write7BitEncodedInt(this.typeWriters.Count);
|
|
foreach (ContentTypeWriter current in this.typeWriters)
|
|
{
|
|
Write(current.GetRuntimeReader(TargetPlatform));
|
|
Write(current.TypeVersion);
|
|
}
|
|
|
|
Write7BitEncodedInt(sharedResources.Count);
|
|
|
|
OutStream = outputStream;
|
|
|
|
Write(xnbMagicWord); // magic bytes for file recognition - 03 bytes
|
|
Write((byte)TargetPlatform); // target platform of content file - 01 byte
|
|
Write((byte)xnbFormatVersion); // version of this file - 01 byte
|
|
Write(new byte[] { (byte)'A', (byte)'N', (byte)'X' }); // ANX magic word - 03 byte
|
|
Write((byte)(TargetProfile == GraphicsProfile.HiDef ? 0x01 : 0x00)); // flags - 01 byte
|
|
Write((int)header.Length + (int)content.Length + 13); // size of file - 04 byte
|
|
if (compressContent)
|
|
{
|
|
//TODO: write compressed size
|
|
}
|
|
|
|
OutStream.Write(header.GetBuffer(), 0, (int)header.Length);
|
|
OutStream.Write(content.GetBuffer(), 0, (int)content.Length); //TODO: write compressed stream if compressedContent is true
|
|
}
|
|
|
|
private void InvokeWriter<T>(T value, ContentTypeWriter writer)
|
|
{
|
|
ContentTypeWriter<T> contentTypeWriter = writer as ContentTypeWriter<T>;
|
|
if (contentTypeWriter != null)
|
|
{
|
|
contentTypeWriter.Write(this, value);
|
|
return;
|
|
}
|
|
writer.Write(this, value);
|
|
}
|
|
|
|
private ContentTypeWriter GetTypeWriter(Type type)
|
|
{
|
|
int typeIndex;
|
|
return GetTypeWriter(type, out typeIndex);
|
|
}
|
|
|
|
private ContentTypeWriter GetTypeWriter(Type type, out int typeIndex)
|
|
{
|
|
if (this.typeTable.TryGetValue(type, out typeIndex))
|
|
{
|
|
return this.typeWriters[typeIndex];
|
|
}
|
|
|
|
IEnumerable<Type> dependencies = null;
|
|
ContentTypeWriter typeWriter = this.compiler.GetTypeWriter(type, out dependencies);
|
|
typeIndex = this.typeWriters.Count;
|
|
this.typeWriters.Add(typeWriter);
|
|
this.typeTable.Add(type, typeIndex);
|
|
|
|
foreach (Type dependentType in dependencies)
|
|
{
|
|
if (!(dependentType == typeof(object)))
|
|
{
|
|
this.GetTypeWriter(dependentType);
|
|
}
|
|
}
|
|
|
|
return typeWriter;
|
|
}
|
|
}
|
|
}
|