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("Stream is not a valid .resources file!");
122 int manager_ver = reader.ReadInt32();
123 int manager_len = reader.ReadInt32();
125 /* We know how long the header is, even if
126 * the version number is too new
128 if(manager_ver > ResourceManager.HeaderVersionNumber) {
129 reader.BaseStream.Seek(manager_len, SeekOrigin.Current);
131 string reader_class=reader.ReadString();
132 if(!reader_class.StartsWith("System.Resources.ResourceReader")) {
133 throw new NotSupportedException("This .resources file requires reader class " + reader_class);
136 string set_class=reader.ReadString();
137 if(!set_class.StartsWith(typeof(ResourceSet).FullName) && !set_class.StartsWith("System.Resources.RuntimeResourceSet")) {
138 throw new NotSupportedException("This .resources file requires set class " + set_class);
142 /* Now read the ResourceReader header */
143 resource_ver = reader.ReadInt32();
150 throw new NotSupportedException("This .resources file requires unsupported set class version: " + resource_ver.ToString());
153 resourceCount = reader.ReadInt32();
154 typeCount = reader.ReadInt32();
156 types=new Type[typeCount];
158 for(int i=0; i<typeCount; i++) {
159 types[i]=Type.GetType(reader.ReadString(), true);
162 /* There are between 0 and 7 bytes of
163 * padding here, consisting of the
164 * letters PAD. The next item (Hash
165 * values for each resource name) need
166 * to be aligned on an 8-byte
170 int pad_align=(int)(reader.BaseStream.Position & 7);
174 pad_chars=8-pad_align;
177 for(int i=0; i<pad_chars; i++) {
178 byte pad_byte=reader.ReadByte();
179 if(pad_byte!="PAD"[i%3]) {
180 throw new ArgumentException("Malformed .resources file (padding values incorrect)");
184 /* Read in the hash values for each
185 * resource name. These can be used
186 * by ResourceSet (calling internal
187 * methods) to do a fast compare on
188 * resource names without doing
189 * expensive string compares (but we
190 * dont do that yet, so far we only
191 * implement the Enumerator interface)
193 hashes=new int[resourceCount];
194 for(int i=0; i<resourceCount; i++) {
195 hashes[i]=reader.ReadInt32();
198 /* Read in the virtual offsets for
201 positions=new long[resourceCount];
202 for(int i=0; i<resourceCount; i++) {
203 positions[i]=reader.ReadInt32();
206 dataSectionOffset = reader.ReadInt32();
207 nameSectionOffset = reader.BaseStream.Position;
208 } catch(EndOfStreamException e) {
209 throw new ArgumentException("Stream is not a valied .resources file! It was possibly truncated.", e);
213 /* Cut and pasted from BinaryReader, because it's
216 private int Read7BitEncodedInt() {
222 b = reader.ReadByte();
224 ret = ret | ((b & 0x7f) << shift);
226 } while ((b & 0x80) == 0x80);
231 private string ResourceName(int index)
235 long pos=positions[index]+nameSectionOffset;
236 reader.BaseStream.Seek(pos, SeekOrigin.Begin);
238 /* Read a 7-bit encoded byte length field */
239 int len=Read7BitEncodedInt();
240 byte[] str=new byte[len];
242 reader.Read(str, 0, len);
243 return Encoding.Unicode.GetString(str);
247 // TODO: Read complex types
248 object ReadValueVer2 (int type_index)
250 switch ((PredefinedResourceType)type_index)
252 case PredefinedResourceType.Null:
255 case PredefinedResourceType.String:
256 return reader.ReadString();
258 case PredefinedResourceType.Bool:
259 return reader.ReadBoolean ();
261 case PredefinedResourceType.Char:
262 return (char)reader.ReadUInt16();
264 case PredefinedResourceType.Byte:
265 return reader.ReadByte();
267 case PredefinedResourceType.SByte:
268 return reader.ReadSByte();
270 case PredefinedResourceType.Int16:
271 return reader.ReadInt16();
273 case PredefinedResourceType.UInt16:
274 return reader.ReadUInt16();
276 case PredefinedResourceType.Int32:
277 return reader.ReadInt32();
279 case PredefinedResourceType.UInt32:
280 return reader.ReadUInt32();
282 case PredefinedResourceType.Int64:
283 return reader.ReadInt64();
285 case PredefinedResourceType.UInt64:
286 return reader.ReadUInt64();
288 case PredefinedResourceType.Single:
289 return reader.ReadSingle();
291 case PredefinedResourceType.Double:
292 return reader.ReadDouble();
294 case PredefinedResourceType.Decimal:
295 return reader.ReadDecimal();
297 case PredefinedResourceType.DateTime:
298 return new DateTime(reader.ReadInt64());
300 case PredefinedResourceType.TimeSpan:
301 return new TimeSpan(reader.ReadInt64());
303 case PredefinedResourceType.ByteArray:
304 throw new NotImplementedException (PredefinedResourceType.ByteArray.ToString ());
306 case PredefinedResourceType.Stream:
307 throw new NotImplementedException (PredefinedResourceType.Stream.ToString ());
310 type_index -= (int)PredefinedResourceType.FistCustom;
311 return ReadNonPredefinedValue (types[type_index]);
314 object ReadValueVer1 (Type type)
316 // The most common first
317 if (type==typeof(String))
318 return reader.ReadString();
319 if (type==typeof(Int32))
320 return reader.ReadInt32();
321 if (type==typeof(Byte))
322 return(reader.ReadByte());
323 if (type==typeof(Double))
324 return(reader.ReadDouble());
325 if (type==typeof(Int16))
326 return(reader.ReadInt16());
327 if (type==typeof(Int64))
328 return(reader.ReadInt64());
329 if (type==typeof(SByte))
330 return(reader.ReadSByte());
331 if (type==typeof(Single))
332 return(reader.ReadSingle());
333 if (type==typeof(TimeSpan))
334 return(new TimeSpan(reader.ReadInt64()));
335 if (type==typeof(UInt16))
336 return(reader.ReadUInt16());
337 if (type==typeof(UInt32))
338 return(reader.ReadUInt32());
339 if (type==typeof(UInt64))
340 return(reader.ReadUInt64());
341 if (type==typeof(Decimal))
342 return(reader.ReadDecimal());
343 if (type==typeof(DateTime))
344 return(new DateTime(reader.ReadInt64()));
346 return ReadNonPredefinedValue(type);
349 // TODO: Add security checks
350 object ReadNonPredefinedValue (Type exp_type)
352 object obj=formatter.Deserialize(reader.BaseStream);
353 if(obj.GetType() != exp_type) {
360 * BadImageFormatException,
367 throw new InvalidOperationException("Deserialized object is wrong type");
372 private object ResourceValue(int index)
376 long pos=positions[index]+nameSectionOffset;
377 reader.BaseStream.Seek(pos, SeekOrigin.Begin);
379 /* Read a 7-bit encoded byte length field */
380 long len=Read7BitEncodedInt();
381 /* ... and skip that data to the info
382 * we want, the offset into the data
385 reader.BaseStream.Seek(len, SeekOrigin.Current);
387 long data_offset=reader.ReadInt32();
388 reader.BaseStream.Seek(data_offset+dataSectionOffset, SeekOrigin.Begin);
389 int type_index=Read7BitEncodedInt();
392 if (resource_ver == 2)
393 return ReadValueVer2 (type_index);
395 if (type_index == -1)
398 return ReadValueVer1 (types[type_index]);
407 public IDictionaryEnumerator GetEnumerator () {
409 throw new InvalidOperationException("ResourceReader is closed.");
412 return new ResourceEnumerator (this);
416 IEnumerator IEnumerable.GetEnumerator ()
418 return ((IResourceReader) this).GetEnumerator();
421 void IDisposable.Dispose ()
426 private void Dispose (bool disposing)
440 internal class ResourceEnumerator : IDictionaryEnumerator
442 private ResourceReader reader;
443 private int index = -1;
444 private bool finished = false;
446 internal ResourceEnumerator(ResourceReader readerToEnumerate){
447 reader = readerToEnumerate;
450 public virtual DictionaryEntry Entry
453 if (reader.reader == null)
454 throw new InvalidOperationException("ResourceReader is closed.");
456 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
458 DictionaryEntry entry = new DictionaryEntry();
465 public virtual object Key
468 if (reader.reader == null)
469 throw new InvalidOperationException("ResourceReader is closed.");
471 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
472 return (reader.ResourceName(index));
476 public virtual object Value
479 if (reader.reader == null)
480 throw new InvalidOperationException("ResourceReader is closed.");
482 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
483 return(reader.ResourceValue(index));
487 public virtual object Current
490 /* Entry does the checking, no
491 * need to repeat it here
497 public virtual bool MoveNext ()
499 if (reader.reader == null)
500 throw new InvalidOperationException("ResourceReader is closed.");
505 if (++index < reader.resourceCount){
514 public void Reset () {
515 if (reader.reader == null)
516 throw new InvalidOperationException("ResourceReader is closed.");
520 } // internal class ResourceEnumerator
521 } // public sealed class ResourceReader
522 } // namespace System.Resources