2 // System.Resources.ResourceReader.cs
5 // Duncan Mak <duncan@ximian.com>
6 // Nick Drochak <ndrochak@gol.com>
7 // Dick Porter <dick@ximian.com>
8 // Marek Safar <marek.safar@seznam.cz>
9 // Atsushi Enomoto <atsushi@ximian.com>
11 // (C) 2001, 2002 Ximian Inc, http://www.ximian.com
12 // Copyright (C) 2004-2005,2007 Novell, Inc (http://www.novell.com)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Collections;
35 using System.Resources;
38 using System.Runtime.InteropServices;
39 using System.Runtime.Serialization;
40 using System.Runtime.Serialization.Formatters.Binary;
41 using System.Security.Permissions;
43 using System.Collections.Generic;
46 namespace System.Resources
48 internal enum PredefinedResourceType
73 [System.Runtime.InteropServices.ComVisible (true)]
75 public sealed class ResourceReader : IResourceReader, IEnumerable, IDisposable
79 public readonly long ValuePosition;
80 public readonly string ResourceName;
81 public readonly int TypeIndex;
83 public ResourceInfo (string resourceName, long valuePosition, int type_index)
85 ValuePosition = valuePosition;
86 ResourceName = resourceName;
87 TypeIndex = type_index;
91 struct ResourceCacheItem
93 public readonly string ResourceName;
94 public readonly object ResourceValue;
96 public ResourceCacheItem (string name, object value)
99 ResourceValue = value;
104 object readerLock = new object ();
105 IFormatter formatter;
106 internal int resourceCount = 0;
110 ResourceInfo[] infos;
111 int dataSectionOffset;
112 long nameSectionOffset;
116 [SecurityPermission (SecurityAction.LinkDemand, SerializationFormatter = true)]
117 public ResourceReader (Stream stream)
120 throw new ArgumentNullException ("stream");
123 throw new ArgumentException ("Stream was not readable.");
125 reader = new BinaryReader(stream, Encoding.UTF8);
126 formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File|StreamingContextStates.Persistence));
131 public ResourceReader (string fileName)
133 reader = new BinaryReader (new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read));
134 formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File|StreamingContextStates.Persistence));
139 /* Read the ResourceManager header and the
140 * ResourceReader header.
142 private void ReadHeaders()
145 int manager_magic = reader.ReadInt32();
147 if(manager_magic != ResourceManager.MagicNumber)
148 throw new ArgumentException(String.Format ("Stream is not a valid .resources file, magic=0x{0:x}", manager_magic));
150 int manager_ver = reader.ReadInt32();
151 int manager_len = reader.ReadInt32();
153 /* We know how long the header is, even if
154 * the version number is too new
156 if(manager_ver > ResourceManager.HeaderVersionNumber) {
157 reader.BaseStream.Seek(manager_len, SeekOrigin.Current);
159 string reader_class=reader.ReadString();
160 if(!reader_class.StartsWith("System.Resources.ResourceReader")) {
161 throw new NotSupportedException("This .resources file requires reader class " + reader_class);
164 string set_class=reader.ReadString();
165 if(!set_class.StartsWith(typeof(ResourceSet).FullName) && !set_class.StartsWith("System.Resources.RuntimeResourceSet")) {
166 throw new NotSupportedException("This .resources file requires set class " + set_class);
170 /* Now read the ResourceReader header */
171 resource_ver = reader.ReadInt32();
178 throw new NotSupportedException("This .resources file requires unsupported set class version: " + resource_ver.ToString());
181 resourceCount = reader.ReadInt32();
182 typeCount = reader.ReadInt32();
184 typeNames=new string[typeCount];
186 for(int i=0; i<typeCount; i++) {
187 typeNames[i]=reader.ReadString();
190 /* There are between 0 and 7 bytes of
191 * padding here, consisting of the
192 * letters PAD. The next item (Hash
193 * values for each resource name) need
194 * to be aligned on an 8-byte
198 int pad_align=(int)(reader.BaseStream.Position & 7);
202 pad_chars=8-pad_align;
205 for(int i=0; i<pad_chars; i++) {
206 byte pad_byte=reader.ReadByte();
207 if(pad_byte!="PAD"[i%3]) {
208 throw new ArgumentException("Malformed .resources file (padding values incorrect)");
211 /* Read in the hash values for each
212 * resource name. These can be used
213 * by ResourceSet (calling internal
214 * methods) to do a fast compare on
215 * resource names without doing
216 * expensive string compares (but we
217 * dont do that yet, so far we only
218 * implement the Enumerator interface)
220 hashes=new int[resourceCount];
221 for(int i=0; i<resourceCount; i++) {
222 hashes[i]=reader.ReadInt32();
225 /* Read in the virtual offsets for
228 long[] positions = new long [resourceCount];
229 for(int i = 0; i < resourceCount; i++)
230 positions [i] = reader.ReadInt32();
232 dataSectionOffset = reader.ReadInt32();
233 nameSectionOffset = reader.BaseStream.Position;
235 long origPosition = reader.BaseStream.Position;
236 infos = new ResourceInfo [resourceCount];
237 for (int i = 0; i < resourceCount; i++)
238 infos [i] = CreateResourceInfo (positions [i]);
239 reader.BaseStream.Seek (origPosition, SeekOrigin.Begin);
242 } catch(EndOfStreamException e) {
243 throw new ArgumentException("Stream is not a valid .resources file! It was possibly truncated.", e);
247 ResourceInfo CreateResourceInfo (long position)
249 long pos = position + nameSectionOffset;
251 reader.BaseStream.Seek (pos, SeekOrigin.Begin);
254 int len = Read7BitEncodedInt ();
255 byte[] str = new byte [len];
257 reader.Read (str, 0, len);
258 string resourceName = Encoding.Unicode.GetString (str);
260 long data_offset = reader.ReadInt32 () + dataSectionOffset;
261 reader.BaseStream.Seek (data_offset, SeekOrigin.Begin);
262 int type_index = Read7BitEncodedInt ();
264 return new ResourceInfo (resourceName, reader.BaseStream.Position, type_index);
267 /* Cut and pasted from BinaryReader, because it's
270 private int Read7BitEncodedInt() {
276 b = reader.ReadByte();
278 ret = ret | ((b & 0x7f) << shift);
280 } while ((b & 0x80) == 0x80);
285 object ReadValueVer2 (int type_index)
287 switch ((PredefinedResourceType)type_index)
289 case PredefinedResourceType.Null:
292 case PredefinedResourceType.String:
293 return reader.ReadString();
295 case PredefinedResourceType.Bool:
296 return reader.ReadBoolean ();
298 case PredefinedResourceType.Char:
299 return (char)reader.ReadUInt16();
301 case PredefinedResourceType.Byte:
302 return reader.ReadByte();
304 case PredefinedResourceType.SByte:
305 return reader.ReadSByte();
307 case PredefinedResourceType.Int16:
308 return reader.ReadInt16();
310 case PredefinedResourceType.UInt16:
311 return reader.ReadUInt16();
313 case PredefinedResourceType.Int32:
314 return reader.ReadInt32();
316 case PredefinedResourceType.UInt32:
317 return reader.ReadUInt32();
319 case PredefinedResourceType.Int64:
320 return reader.ReadInt64();
322 case PredefinedResourceType.UInt64:
323 return reader.ReadUInt64();
325 case PredefinedResourceType.Single:
326 return reader.ReadSingle();
328 case PredefinedResourceType.Double:
329 return reader.ReadDouble();
331 case PredefinedResourceType.Decimal:
332 return reader.ReadDecimal();
334 case PredefinedResourceType.DateTime:
335 return new DateTime(reader.ReadInt64());
337 case PredefinedResourceType.TimeSpan:
338 return new TimeSpan(reader.ReadInt64());
340 case PredefinedResourceType.ByteArray:
341 return reader.ReadBytes (reader.ReadInt32 ());
343 case PredefinedResourceType.Stream:
344 // FIXME: create pinned UnmanagedMemoryStream for efficiency.
345 byte [] bytes = new byte [reader.ReadUInt32 ()];
346 reader.Read (bytes, 0, bytes.Length);
347 return new MemoryStream (bytes);
350 type_index -= (int)PredefinedResourceType.FistCustom;
351 return ReadNonPredefinedValue (Type.GetType (typeNames[type_index], true));
354 object ReadValueVer1 (Type type)
356 // The most common first
357 if (type==typeof(String))
358 return reader.ReadString();
359 if (type==typeof(Int32))
360 return reader.ReadInt32();
361 if (type==typeof(Byte))
362 return(reader.ReadByte());
363 if (type==typeof(Double))
364 return(reader.ReadDouble());
365 if (type==typeof(Int16))
366 return(reader.ReadInt16());
367 if (type==typeof(Int64))
368 return(reader.ReadInt64());
369 if (type==typeof(SByte))
370 return(reader.ReadSByte());
371 if (type==typeof(Single))
372 return(reader.ReadSingle());
373 if (type==typeof(TimeSpan))
374 return(new TimeSpan(reader.ReadInt64()));
375 if (type==typeof(UInt16))
376 return(reader.ReadUInt16());
377 if (type==typeof(UInt32))
378 return(reader.ReadUInt32());
379 if (type==typeof(UInt64))
380 return(reader.ReadUInt64());
381 if (type==typeof(Decimal))
382 return(reader.ReadDecimal());
383 if (type==typeof(DateTime))
384 return(new DateTime(reader.ReadInt64()));
386 return ReadNonPredefinedValue(type);
389 // TODO: Add security checks
390 object ReadNonPredefinedValue (Type exp_type)
392 object obj=formatter.Deserialize(reader.BaseStream);
393 if(obj.GetType() != exp_type) {
400 * BadImageFormatException,
407 throw new InvalidOperationException("Deserialized object is wrong type");
412 void LoadResourceValues (IList store)
415 ResourceCacheItem rci;
419 for (int i = 0; i < resourceCount; i++) {
421 if (ri.TypeIndex == -1) {
422 store.Add (new ResourceCacheItem (ri.ResourceName, null));
426 reader.BaseStream.Seek (ri.ValuePosition, SeekOrigin.Begin);
428 if (resource_ver == 2)
429 value = ReadValueVer2 (ri.TypeIndex);
432 value = ReadValueVer1 (Type.GetType (typeNames [ri.TypeIndex], true));
434 store.Add (new ResourceCacheItem (ri.ResourceName, value));
440 internal UnmanagedMemoryStream ResourceValueAsStream (string name, int index)
442 ResourceInfo ri = infos [index];
443 if ((PredefinedResourceType)ri.TypeIndex != PredefinedResourceType.Stream)
444 throw new InvalidOperationException (String.Format ("Resource '{0}' was not a Stream. Use GetObject() instead.", name));
447 reader.BaseStream.Seek (ri.ValuePosition, SeekOrigin.Begin);
449 // here we return a Stream from exactly
450 // current position so that the returned
451 // Stream represents a single object stream.
452 long slen = reader.ReadInt32();
453 IntPtrStream basePtrStream = reader.BaseStream as IntPtrStream;
455 if (basePtrStream != null) {
456 byte* addr = (byte*) basePtrStream.BaseAddress.ToPointer ();
457 addr += basePtrStream.Position;
458 return new UnmanagedMemoryStream ((byte*) (void*) addr, slen);
460 IntPtr ptr = Marshal.AllocHGlobal ((int) slen);
461 byte* addr = (byte*) ptr.ToPointer ();
462 UnmanagedMemoryStream ms = new UnmanagedMemoryStream (addr, slen, slen, FileAccess.ReadWrite);
463 // The memory resource must be freed
464 // when the stream is disposed.
465 ms.Closed += delegate (object o, EventArgs e) {
466 Marshal.FreeHGlobal (ptr);
468 byte [] bytes = new byte [slen < 1024 ? slen : 1024];
469 for (int i = 0; i < slen; i += bytes.Length) {
470 int x = reader.Read (bytes, 0, bytes.Length);
471 ms.Write (bytes, 0, x);
473 ms.Seek (0, SeekOrigin.Begin);
486 public IDictionaryEnumerator GetEnumerator () {
488 throw new InvalidOperationException("ResourceReader is closed.");
491 return new ResourceEnumerator (this);
495 IEnumerator IEnumerable.GetEnumerator ()
497 return ((IResourceReader) this).GetEnumerator();
500 public void GetResourceData (string resourceName, out string resourceType, out byte [] resourceData)
502 if (resourceName == null)
503 throw new ArgumentNullException ("resourceName");
504 ResourceEnumerator en = new ResourceEnumerator (this);
505 while (en.MoveNext ())
506 if ((string) en.Key == resourceName) {
507 GetResourceDataAt (en.Index, out resourceType, out resourceData);
510 throw new ArgumentException (String.Format ("Specified resource not found: {0}", resourceName));
513 private void GetResourceDataAt (int index, out string resourceType, out byte [] data)
515 ResourceInfo ri = infos [index];
516 int type_index = ri.TypeIndex;
517 if (type_index == -1)
518 throw new FormatException ("The resource data is corrupt");
521 reader.BaseStream.Seek (ri.ValuePosition, SeekOrigin.Begin);
522 long pos2 = reader.BaseStream.Position;
524 // Simply read data, and seek back to the original position
525 if (resource_ver == 2) {
526 if (type_index >= (int) PredefinedResourceType.FistCustom) {
527 int typenameidx = type_index - (int)PredefinedResourceType.FistCustom;
528 if (typenameidx >= typeNames.Length)
529 throw new FormatException ("The resource data is corrupt. Invalid index to types");
530 resourceType = typeNames[typenameidx];
533 resourceType = "ResourceTypeCode." + (PredefinedResourceType) type_index;
534 ReadValueVer2 (type_index);
536 // resource ver 1 == untyped
537 resourceType = "ResourceTypeCode.Null";
538 ReadValueVer1 (Type.GetType (typeNames [type_index], true));
541 // FIXME: the data size is wrong.
542 int datalen = (int) (reader.BaseStream.Position - pos2);
543 reader.BaseStream.Seek (-datalen, SeekOrigin.Current);
544 data = new byte [datalen];
545 reader.BaseStream.Read (data, 0, datalen);
549 void IDisposable.Dispose ()
554 private void Dispose (bool disposing)
568 internal class ResourceEnumerator : IDictionaryEnumerator
570 private ResourceReader reader;
571 private int index = -1;
572 private bool finished = false;
573 ResourceCacheItem[] cache;
575 internal ResourceEnumerator(ResourceReader readerToEnumerate)
577 reader = readerToEnumerate;
583 get { return index; }
586 public virtual DictionaryEntry Entry
589 if (reader.reader == null)
590 throw new InvalidOperationException("ResourceReader is closed.");
592 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
594 DictionaryEntry entry = new DictionaryEntry();
601 public virtual object Key
604 if (reader.reader == null)
605 throw new InvalidOperationException("ResourceReader is closed.");
607 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
609 return cache [index].ResourceName;
613 public virtual object Value
616 if (reader.reader == null)
617 throw new InvalidOperationException("ResourceReader is closed.");
619 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
620 return cache [index].ResourceValue;
625 public UnmanagedMemoryStream ValueAsStream
628 if (reader.reader == null)
629 throw new InvalidOperationException("ResourceReader is closed.");
631 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
632 return(reader.ResourceValueAsStream((string) Key, index));
637 public virtual object Current
640 /* Entry does the checking, no
641 * need to repeat it here
647 public virtual bool MoveNext ()
649 if (reader.reader == null)
650 throw new InvalidOperationException("ResourceReader is closed.");
655 if (++index < reader.resourceCount){
664 public void Reset () {
665 if (reader.reader == null)
666 throw new InvalidOperationException("ResourceReader is closed.");
673 if (reader.reader == null)
677 var resources = new List <ResourceCacheItem> (reader.resourceCount);
679 ArrayList resources = new ArrayList (reader.resourceCount);
682 reader.LoadResourceValues (resources);
684 cache = resources.ToArray ();
686 cache = (ResourceCacheItem[]) resources.ToArray (typeof (ResourceCacheItem));
689 } // internal class ResourceEnumerator
690 } // public sealed class ResourceReader
691 } // namespace System.Resources