Jypeli  5
The simple game programming library
StorageFile.cs
Siirry tämän tiedoston dokumentaatioon.
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.IO;
6 using System.Xml.Serialization;
7 using System.Reflection;
8 using System.Xml;
9 using System.Collections;
10 using System.Globalization;
11 
12 namespace Jypeli
13 {
17  public class StorageFile : IDisposable
18  {
19  public string Name { get; set; }
20  public Stream Stream { get; private set; }
21 
22  internal StorageFile( string name, Stream stream )
23  {
24  Name = name;
25  Stream = stream;
26  if ( Stream == null ) Stream = new MemoryStream();
27  }
28 
29  public void Close()
30  {
31  Stream.Close();
32  }
33 
34  public void Dispose()
35  {
36  Stream.Close();
37  }
38 
39  internal static BindingFlags AllOfInstance = BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
40 
41  string GenToString( Type type, object obj )
42  {
43  if ( type == typeof( string ) )
44  return (string)obj;
45 
46  IFormatProvider provider = NumberFormatInfo.InvariantInfo;
47 
48  try
49  {
50  object[] args = new object[] { provider };
51  return (string)type.InvokeMember( "ToString", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, obj, args );
52  }
53  catch ( MissingMethodException )
54  {
55  object[] args = new object[] { };
56  return (string)type.InvokeMember( "ToString", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, obj, args );
57  }
58  }
59 
60  object GenParse( Type type, string str )
61  {
62  if ( type == typeof( string ) )
63  return str;
64 
65  if ( type == typeof(Type) )
66  {
67  return TypeHelper.Parse( str );
68  }
69 
70  try
71  {
72  object[] args = new object[] { str, NumberFormatInfo.InvariantInfo };
73  return type.InvokeMember( "Parse", BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public, null, null, args );
74  }
75  catch ( MissingMethodException )
76  {
77  object[] args = new object[] { str };
78  return type.InvokeMember( "Parse", BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public, null, null, args );
79  }
80  }
81 
82  internal object LoadData( XmlReader reader, Type type, object obj )
83  {
84  Dictionary<string, string> metadata = null;
85  int depth = reader.Depth;
86 
87  while ( reader.Read() )
88  {
89  if ( ( reader.NodeType == XmlNodeType.EndElement ) && ( reader.Depth <= depth ) )
90  break;
91 
92  if ( reader.NodeType != XmlNodeType.Element )
93  continue;
94 
95  if ( reader.Name == "Meta" )
96  {
97  if ( metadata == null ) metadata = new Dictionary<string, string>();
98 
99  reader.MoveToFirstAttribute();
100  do
101  {
102  metadata.Add( reader.Name, reader.ReadContentAsString() );
103  } while ( reader.MoveToNextAttribute() );
104  }
105 
106  if ( reader.IsEmptyElement )
107  continue;
108 
109  Destroyable dObj = obj as Destroyable;
110 
111  // A Type object has no reasonable default value.
112  if ( ( type != typeof( Type ) ) && ( obj == null || ( dObj != null && dObj.IsDestroyed ) ) )
113  {
114  object tag = obj is Tagged ? ( (Tagged)obj ).Tag : null;
115  string stag = tag as string;
116 
117  if ( !TryCreateObject( type, stag, out obj ) )
118  throw new NullReferenceException( "Object of type " + type.Name + " must be initialized before it can be loaded since there is no factory method or default constructor for it." );
119  }
120 
121  if ( reader.Name == "Field" || reader.Name == "Property" )
122  {
123  string mName = reader.GetAttribute( "Name" );
124  Type mType = TypeHelper.Parse( reader.GetAttribute( "Type" ) );
125 
126  if ( reader.Name == "Property" )
127  {
128  PropertyInfo propInfo = type.GetProperty( mName, BindingFlags.GetProperty | AllOfInstance );
129  if ( propInfo == null ) throw new ArgumentException( type.Name + " does not contain property " + mName );
130  object mValue = propInfo.GetValue( obj, null );
131  mValue = LoadData( reader, mType, mValue );
132  propInfo.SetValue( obj, mValue, null );
133  }
134  else
135  {
136  FieldInfo fieldInfo = type.GetField( mName, BindingFlags.GetField | AllOfInstance );
137  if ( fieldInfo == null ) throw new ArgumentException( type.Name + " does not contain field " + mName );
138  object mValue = fieldInfo.GetValue( obj );
139  mValue = LoadData( reader, mType, mValue );
140  fieldInfo.SetValue( obj, mValue );
141  }
142  }
143  else if ( reader.Name == "SerializedObject" )
144  {
145  // Used for legacy support only
146 
147  reader.Read();
148  object objValue = null;
149 
150  using ( MemoryStream dataStream = new MemoryStream( Encoding.Unicode.GetBytes( reader.Value ) ) )
151  {
152  XmlSerializer serializer = new XmlSerializer( type );
153  objValue = serializer.Deserialize( dataStream );
154  }
155 
156  while ( !reader.EOF && reader.NodeType != XmlNodeType.EndElement ) reader.Read();
157 
158  obj = objValue;
159  }
160  else if ( reader.Name == "Value" )
161  {
162  reader.Read();
163 
164  if ( type.IsEnum )
165  obj = Enum.Parse( type, reader.Value, false );
166  else
167  obj = GenParse( type, reader.Value );
168 
169  while ( !reader.EOF && reader.NodeType != XmlNodeType.EndElement ) reader.Read();
170  }
171  else if ( reader.Name == "Array" )
172  {
173  Type mType = TypeHelper.Parse( reader.GetAttribute( "Type" ) );
174  obj = LoadArray( reader, mType, obj );
175  }
176  else if ( reader.Name == "List" )
177  {
178  Type mType = TypeHelper.Parse( reader.GetAttribute( "Type" ) );
179  int itemCount = int.Parse( reader.GetAttribute( "Count" ) );
180  obj = LoadList( reader, mType, itemCount, obj );
181  }
182  }
183 
184  if ( metadata != null )
185  {
186  foreach ( KeyValuePair<string, string> entry in metadata )
187  applyMetadata( type, obj, entry.Key, entry.Value );
188  }
189 
190  return obj;
191  }
192 
193  private void applyMetadata( Type type, object obj, string tag, string value )
194  {
195 #if JYPELI
196  if ( TypeHelper.InheritsFrom( type, typeof( GameObject ) ) )
197  {
198  // Game object tags
199  GameObject gobj = (GameObject)obj;
200 
201  if ( tag == "AddedToGame" && tagTrue( value ) && !gobj.IsAddedToGame )
202  Game.Instance.Add( gobj );
203  }
204 #endif
205  }
206 
207  private bool tagTrue( string value )
208  {
209  return value == "1" || value.ToLower() == "true";
210  }
211 
212  internal void SaveData( XmlWriter writer, Type type, object obj, bool saveAllFields )
213  {
214  if ( obj is Array )
215  {
216  SaveArray( writer, type, obj );
217  return;
218  }
219 
220  if ( obj is IList )
221  {
222  SaveList( writer, type, obj );
223  return;
224  }
225 
226  if ( obj is Type )
227  {
228  writer.WriteElementString( "Value", ( obj as Type ).AssemblyQualifiedName );
229  }
230 
231  bool hasSaveAttrib = saveAllFields || type.GetCustomAttributes( typeof( SaveAttribute ), true ).Length > 0;
232  bool hasSaveAllFields = saveAllFields || type.GetCustomAttributes( typeof( SaveAllFieldsAttribute ), true ).Length > 0;
233 
234  if ( type.IsClass && type != typeof( string ) && !hasSaveAttrib )
235  return;
236 
237  if ( !hasSaveAttrib || ( type.IsPrimitive && type.IsEnum ) )
238  {
239  writer.WriteElementString( "Value", GenToString( type, obj ) );
240  return;
241  }
242 
243  writeMetadata( writer, type, obj );
244 
245  foreach ( PropertyInfo prop in type.GetProperties( BindingFlags.GetProperty | AllOfInstance ) )
246  {
247  if ( IsIllegalType( prop.PropertyType ) )
248  continue;
249 
250  object[] attribs = prop.GetCustomAttributes( true );
251  if ( prop.GetCustomAttributes( typeof( SaveAttribute ), true ).Length == 0 )
252  continue;
253 
254  object propValue = prop.GetValue( obj, null );
255 
256  if ( propValue == null )
257  continue;
258 
259  Type st = propValue.GetType();
260  SaveMember( writer, "Property", st, prop, propValue, saveAllFields );
261  }
262 
263  foreach ( FieldInfo field in type.GetFields( BindingFlags.GetField | AllOfInstance ) )
264  {
265  if ( IsIllegalType( field.FieldType ) )
266  continue;
267 
268  if ( !hasSaveAllFields && field.GetCustomAttributes( typeof( SaveAttribute ), true ).Length == 0 )
269  continue;
270 
271  object fieldValue = field.GetValue( obj );
272 
273  if ( fieldValue == null )
274  continue;
275 
276  Type st = fieldValue.GetType();
277  SaveMember( writer, "Field", st, field, fieldValue, saveAllFields );
278  }
279  }
280 
281  private bool IsIllegalType( Type type )
282  {
283  return ( type == typeof( IntPtr ) || type == typeof( UIntPtr ) );
284  }
285 
286  private void SaveMember( XmlWriter writer, string elementName, Type memberType, MemberInfo member, object memberValue, bool saveAllFields )
287  {
288  writer.WriteStartElement( elementName );
289  writer.WriteAttributeString( "Name", member.Name );
290  writer.WriteAttributeString( "Type", TypeHelper.ToString( memberType ) );
291  SaveData( writer, memberType, memberValue, saveAllFields || member.GetCustomAttributes( typeof( SaveAllFieldsAttribute ), true ).Length > 0 );
292  writer.WriteEndElement();
293  }
294 
295  private void writeMetadata( XmlWriter writer, Type type, object obj )
296  {
297 #if JYPELI
298  if ( TypeHelper.InheritsFrom( type, typeof( GameObject ) ) && ( (GameObject)obj ).IsAddedToGame )
299  {
300  writer.WriteStartElement( "Meta" );
301  writer.WriteAttributeString( "AddedToGame", "1" );
302  writer.WriteEndElement();
303  }
304 #endif
305  }
306 
307  internal object LoadArray( XmlReader reader, Type containerType, object obj )
308  {
309  Type designatedItemType = null;
310  Array array = (Array)obj;
311  int index = 0;
312 
313  for ( int readItems = 0;
314  readItems < array.GetLength( 0 ) &&
315  reader.Read() && !( reader.NodeType == XmlNodeType.EndElement && reader.Name == "Array" ); )
316  {
317  if ( reader.NodeType != XmlNodeType.Element )
318  continue;
319  if ( reader.Name != "Item" )
320  {
321  string name = reader.GetAttribute( "Name" );
322  if ( name == null ) name = "(undefined)";
323  throw new XmlException( "Unexpected subelement in List block: " + reader.Name + ", Name = " + name );
324  }
325 
326  index = Int16.Parse( reader.GetAttribute( "Index" ) );
327  designatedItemType = TypeHelper.Parse( reader.GetAttribute( "Type" ) );
328 
329  object item = array.GetValue( index );
330  if ( item != null )
331  {
332  Type realItemType = item.GetType();
333  if ( designatedItemType != realItemType )
334  throw new XmlException( "Array item type mismatch: expected " + designatedItemType.Name + ", got " + realItemType.Name + " at index " + index.ToString() );
335  }
336 
337  item = LoadData( reader, designatedItemType, item );
338  array.SetValue( item, index );
339 
340  readItems++;
341  }
342 
343  return array;
344  }
345 
346  private void FixListSize( ref IList list, int itemCount )
347  {
348  while ( list.Count < itemCount )
349  {
350  list.Add( null );
351  }
352 
353  while ( list.Count > itemCount )
354  {
355  list.RemoveAt( list.Count - 1 );
356  }
357  }
358 
359  internal object LoadList( XmlReader reader, Type containerType, int itemCount, object obj )
360  {
361  object[] listObjects = new object[itemCount];
362  IList list = (IList)obj;
363 
364  while ( list.Count > itemCount )
365  {
366  int removeIndex = list.Count - 1;
367  if ( list[removeIndex] is Destroyable )
368  {
369  ( (Destroyable)( list[removeIndex] ) ).Destroy();
370  }
371  list.RemoveAt( removeIndex );
372  }
373 
374  for ( int i = 0; i < Math.Min( itemCount, list.Count ); i++ )
375  listObjects[i] = list[i];
376  for ( int i = list.Count; i < itemCount; i++ )
377  listObjects[i] = null;
378 
379  while ( reader.Read() && !( reader.NodeType == XmlNodeType.EndElement && reader.Name == "List" ) )
380  {
381  if ( reader.NodeType != XmlNodeType.Element )
382  continue;
383  if ( reader.Name != "Item" )
384  {
385  string name = reader.GetAttribute( "Name" );
386  if ( name == null ) name = "(undefined)";
387  throw new XmlException( "Unexpected subelement in List block: " + reader.Name + ", Name = " + name );
388  }
389 
390  string tag = reader.GetAttribute( "Tag" );
391  Type itemType = TypeHelper.Parse( reader.GetAttribute( "Type" ) );
392  int itemindex = int.Parse( reader.GetAttribute( "Index" ) );
393 
394  if ( tag != null && listObjects[itemindex] == null )
395  {
396  if ( !TryCreateObject( itemType, tag, out listObjects[itemindex] ) )
397  throw new MissingMethodException( String.Format("No factory method for type {0}, tag {1}", itemType.Name, tag) );
398  }
399 
400  listObjects[itemindex] = LoadData( reader, itemType, listObjects[itemindex] );
401  }
402 
403  list.Clear();
404 
405  for ( int i = 0; i < listObjects.Length; i++ )
406  list.Add( listObjects[i] );
407 
408  return list;
409  }
410 
411  bool TryCreateObject( Type type, string tag, out object obj )
412  {
413  if ( type == typeof( string ) )
414  {
415  // Strings are immutable, and therefore have no default constructor
416  obj = "";
417  return true;
418  }
419  else
420  {
421  try
422  {
423  // Try to create using a factory method
424  obj = Factory.FactoryCreate( type, tag );
425  return true;
426  }
427  catch ( KeyNotFoundException )
428  {
429  try
430  {
431  // Try the default constructor
432  obj = Activator.CreateInstance( type, true );
433  return true;
434  }
435  catch ( MissingMethodException )
436  {
437  obj = null;
438  return false;
439  }
440  }
441  }
442  }
443 
444  internal void SaveArray( XmlWriter writer, Type containerType, object obj )
445  {
446  Array array = (Array)obj;
447 
448  writer.WriteStartElement( "Array" );
449  writer.WriteAttributeString( "Type", TypeHelper.ToString( containerType ) );
450 
451  for ( int i = 0; i < array.GetLength( 0 ); i++ )
452  {
453  object item = array.GetValue( i );
454  Type realItemType = item.GetType();
455  writer.WriteStartElement( "Item" );
456  writer.WriteAttributeString( "Index", i.ToString() );
457  writer.WriteAttributeString( "Type", TypeHelper.ToString( realItemType ) );
458  if ( item is Tagged ) writer.WriteAttributeString( "Tag", ( (Tagged)item ).Tag.ToString() );
459  SaveData( writer, realItemType, item, false );
460  writer.WriteEndElement();
461  }
462 
463  writer.WriteEndElement();
464  }
465 
466  internal void SaveList( XmlWriter writer, Type containerType, object obj )
467  {
468  IList list = (IList)obj;
469 
470  writer.WriteStartElement( "List" );
471  writer.WriteAttributeString( "Type", TypeHelper.ToString( containerType ) );
472  writer.WriteAttributeString( "Count", list.Count.ToString() );
473 
474  for ( int i = 0; i < list.Count; i++ )
475  {
476  Type realItemType = list[i].GetType();
477  writer.WriteStartElement( "Item" );
478  writer.WriteAttributeString( "Type", TypeHelper.ToString( realItemType ) );
479  writer.WriteAttributeString( "Index", i.ToString() );
480  if ( list[i] is Tagged ) writer.WriteAttributeString( "Tag", ( (Tagged)list[i] ).Tag.ToString() );
481  SaveData( writer, realItemType, list[i], false );
482  writer.WriteEndElement();
483  }
484 
485  writer.WriteEndElement();
486  }
487  }
488 }
bool IsAddedToGame
Onko olio lisätty peliin.
Rajapinta olioille, joilla on Tag-ominaisuus.
Definition: Tagged.cs:6
object Tag
Definition: Tagged.cs:8
static Game Instance
Definition: Game.cs:149
void Add(IGameObject o)
Lisää olion peliin. Tavalliset oliot tulevat automaattisesti kerrokselle 0 ja ruutuoliot päällimmäise...
Definition: Game.cs:691
Peliluokka reaaliaikaisille peleille.
Definition: DebugScreen.cs:10
static bool InheritsFrom(Type actual, Type expected)
Definition: TypeHelper.cs:10
static string ToString(Type type)
Definition: TypeHelper.cs:23
static Type Parse(string typeStr)
Definition: TypeHelper.cs:58
Pelialueella liikkuva olio. Käytä fysiikkapeleissä PhysicsObject-olioita.
Definition: __GameObject.cs:54
Rajapinta olioille, jotka ovat tuhottavissa.
Definition: Destroyable.cs:8