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