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>
10 // (C) 2001, 2002 Ximian Inc, http://www.ximian.com
11 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Collections;
34 using System.Resources;
37 using System.Runtime.Serialization;
38 using System.Runtime.Serialization.Formatters.Binary;
39 using System.Security.Permissions;
41 namespace System.Resources
43 internal enum PredefinedResourceType
67 public sealed class ResourceReader : IResourceReader, IEnumerable, IDisposable
71 internal int resourceCount = 0;
76 int dataSectionOffset;
77 long nameSectionOffset;
81 [SecurityPermission (SecurityAction.LinkDemand, SerializationFormatter = true)]
82 public ResourceReader (Stream stream)
85 throw new ArgumentNullException ("Value cannot be null.");
88 throw new ArgumentException ("Stream was not readable.");
90 reader = new BinaryReader(stream, Encoding.UTF8);
91 formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File|StreamingContextStates.Persistence));
96 public ResourceReader (string fileName)
99 throw new ArgumentNullException ("Path cannot be null.");
101 if (!System.IO.File.Exists (fileName))
102 throw new FileNotFoundException ("Could not find file " + Path.GetFullPath(fileName));
104 reader = new BinaryReader (new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read));
105 formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File|StreamingContextStates.Persistence));
110 /* Read the ResourceManager header and the
111 * ResourceReader header.
113 private void ReadHeaders()
116 int manager_magic = reader.ReadInt32();
118 if(manager_magic != ResourceManager.MagicNumber)
119 throw new ArgumentException(String.Format ("Stream is not a valid .resources file, magic=0x{0:x}", manager_magic));
121 int manager_ver = reader.ReadInt32();
122 int manager_len = reader.ReadInt32();
124 /* We know how long the header is, even if
125 * the version number is too new
127 if(manager_ver > ResourceManager.HeaderVersionNumber) {
128 reader.BaseStream.Seek(manager_len, SeekOrigin.Current);
130 string reader_class=reader.ReadString();
131 if(!reader_class.StartsWith("System.Resources.ResourceReader")) {
132 throw new NotSupportedException("This .resources file requires reader class " + reader_class);
135 string set_class=reader.ReadString();
136 if(!set_class.StartsWith(typeof(ResourceSet).FullName) && !set_class.StartsWith("System.Resources.RuntimeResourceSet")) {
137 throw new NotSupportedException("This .resources file requires set class " + set_class);
141 /* Now read the ResourceReader header */
142 resource_ver = reader.ReadInt32();
149 throw new NotSupportedException("This .resources file requires unsupported set class version: " + resource_ver.ToString());
152 resourceCount = reader.ReadInt32();
153 typeCount = reader.ReadInt32();
155 types=new Type[typeCount];
157 for(int i=0; i<typeCount; i++) {
158 types[i]=Type.GetType(reader.ReadString(), true);
161 /* There are between 0 and 7 bytes of
162 * padding here, consisting of the
163 * letters PAD. The next item (Hash
164 * values for each resource name) need
165 * to be aligned on an 8-byte
169 int pad_align=(int)(reader.BaseStream.Position & 7);
173 pad_chars=8-pad_align;
176 for(int i=0; i<pad_chars; i++) {
177 byte pad_byte=reader.ReadByte();
178 if(pad_byte!="PAD"[i%3]) {
179 throw new ArgumentException("Malformed .resources file (padding values incorrect)");
183 /* Read in the hash values for each
184 * resource name. These can be used
185 * by ResourceSet (calling internal
186 * methods) to do a fast compare on
187 * resource names without doing
188 * expensive string compares (but we
189 * dont do that yet, so far we only
190 * implement the Enumerator interface)
192 hashes=new int[resourceCount];
193 for(int i=0; i<resourceCount; i++) {
194 hashes[i]=reader.ReadInt32();
197 /* Read in the virtual offsets for
200 positions=new long[resourceCount];
201 for(int i=0; i<resourceCount; i++) {
202 positions[i]=reader.ReadInt32();
205 dataSectionOffset = reader.ReadInt32();
206 nameSectionOffset = reader.BaseStream.Position;
207 } catch(EndOfStreamException e) {
208 throw new ArgumentException("Stream is not a valid .resources file! It was possibly truncated.", e);
212 /* Cut and pasted from BinaryReader, because it's
215 private int Read7BitEncodedInt() {
221 b = reader.ReadByte();
223 ret = ret | ((b & 0x7f) << shift);
225 } while ((b & 0x80) == 0x80);
230 private string ResourceName(int index)
234 long pos=positions[index]+nameSectionOffset;
235 reader.BaseStream.Seek(pos, SeekOrigin.Begin);
237 /* Read a 7-bit encoded byte length field */
238 int len=Read7BitEncodedInt();
239 byte[] str=new byte[len];
241 reader.Read(str, 0, len);
242 return Encoding.Unicode.GetString(str);
246 // TODO: Read complex types
247 object ReadValueVer2 (int type_index)
249 switch ((PredefinedResourceType)type_index)
251 case PredefinedResourceType.Null:
254 case PredefinedResourceType.String:
255 return reader.ReadString();
257 case PredefinedResourceType.Bool:
258 return reader.ReadBoolean ();
260 case PredefinedResourceType.Char:
261 return (char)reader.ReadUInt16();
263 case PredefinedResourceType.Byte:
264 return reader.ReadByte();
266 case PredefinedResourceType.SByte:
267 return reader.ReadSByte();
269 case PredefinedResourceType.Int16:
270 return reader.ReadInt16();
272 case PredefinedResourceType.UInt16:
273 return reader.ReadUInt16();
275 case PredefinedResourceType.Int32:
276 return reader.ReadInt32();
278 case PredefinedResourceType.UInt32:
279 return reader.ReadUInt32();
281 case PredefinedResourceType.Int64:
282 return reader.ReadInt64();
284 case PredefinedResourceType.UInt64:
285 return reader.ReadUInt64();
287 case PredefinedResourceType.Single:
288 return reader.ReadSingle();
290 case PredefinedResourceType.Double:
291 return reader.ReadDouble();
293 case PredefinedResourceType.Decimal:
294 return reader.ReadDecimal();
296 case PredefinedResourceType.DateTime:
297 return new DateTime(reader.ReadInt64());
299 case PredefinedResourceType.TimeSpan:
300 return new TimeSpan(reader.ReadInt64());
302 case PredefinedResourceType.ByteArray:
303 return reader.ReadBytes (reader.ReadInt32 ());
305 case PredefinedResourceType.Stream:
306 throw new NotImplementedException (PredefinedResourceType.Stream.ToString ());
309 type_index -= (int)PredefinedResourceType.FistCustom;
310 return ReadNonPredefinedValue (types[type_index]);
313 object ReadValueVer1 (Type type)
315 // The most common first
316 if (type==typeof(String))
317 return reader.ReadString();
318 if (type==typeof(Int32))
319 return reader.ReadInt32();
320 if (type==typeof(Byte))
321 return(reader.ReadByte());
322 if (type==typeof(Double))
323 return(reader.ReadDouble());
324 if (type==typeof(Int16))
325 return(reader.ReadInt16());
326 if (type==typeof(Int64))
327 return(reader.ReadInt64());
328 if (type==typeof(SByte))
329 return(reader.ReadSByte());
330 if (type==typeof(Single))
331 return(reader.ReadSingle());
332 if (type==typeof(TimeSpan))
333 return(new TimeSpan(reader.ReadInt64()));
334 if (type==typeof(UInt16))
335 return(reader.ReadUInt16());
336 if (type==typeof(UInt32))
337 return(reader.ReadUInt32());
338 if (type==typeof(UInt64))
339 return(reader.ReadUInt64());
340 if (type==typeof(Decimal))
341 return(reader.ReadDecimal());
342 if (type==typeof(DateTime))
343 return(new DateTime(reader.ReadInt64()));
345 return ReadNonPredefinedValue(type);
348 // TODO: Add security checks
349 object ReadNonPredefinedValue (Type exp_type)
351 object obj=formatter.Deserialize(reader.BaseStream);
352 if(obj.GetType() != exp_type) {
359 * BadImageFormatException,
366 throw new InvalidOperationException("Deserialized object is wrong type");
371 private object ResourceValue(int index)
375 long pos=positions[index]+nameSectionOffset;
376 reader.BaseStream.Seek(pos, SeekOrigin.Begin);
378 /* Read a 7-bit encoded byte length field */
379 long len=Read7BitEncodedInt();
380 /* ... and skip that data to the info
381 * we want, the offset into the data
384 reader.BaseStream.Seek(len, SeekOrigin.Current);
386 long data_offset=reader.ReadInt32();
387 reader.BaseStream.Seek(data_offset+dataSectionOffset, SeekOrigin.Begin);
388 int type_index=Read7BitEncodedInt();
391 if (resource_ver == 2)
392 return ReadValueVer2 (type_index);
394 if (type_index == -1)
397 return ReadValueVer1 (types[type_index]);
406 public IDictionaryEnumerator GetEnumerator () {
408 throw new InvalidOperationException("ResourceReader is closed.");
411 return new ResourceEnumerator (this);
415 IEnumerator IEnumerable.GetEnumerator ()
417 return ((IResourceReader) this).GetEnumerator();
420 void IDisposable.Dispose ()
425 private void Dispose (bool disposing)
439 internal class ResourceEnumerator : IDictionaryEnumerator
441 private ResourceReader reader;
442 private int index = -1;
443 private bool finished = false;
445 internal ResourceEnumerator(ResourceReader readerToEnumerate){
446 reader = readerToEnumerate;
449 public virtual DictionaryEntry Entry
452 if (reader.reader == null)
453 throw new InvalidOperationException("ResourceReader is closed.");
455 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
457 DictionaryEntry entry = new DictionaryEntry();
464 public virtual object Key
467 if (reader.reader == null)
468 throw new InvalidOperationException("ResourceReader is closed.");
470 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
471 return (reader.ResourceName(index));
475 public virtual object Value
478 if (reader.reader == null)
479 throw new InvalidOperationException("ResourceReader is closed.");
481 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
482 return(reader.ResourceValue(index));
486 public virtual object Current
489 /* Entry does the checking, no
490 * need to repeat it here
496 public virtual bool MoveNext ()
498 if (reader.reader == null)
499 throw new InvalidOperationException("ResourceReader is closed.");
504 if (++index < reader.resourceCount){
513 public void Reset () {
514 if (reader.reader == null)
515 throw new InvalidOperationException("ResourceReader is closed.");
519 } // internal class ResourceEnumerator
520 } // public sealed class ResourceReader
521 } // namespace System.Resources