2 // System.Resources.ResourceReader.cs
5 // Duncan Mak <duncan@ximian.com>
6 // Nick Drochak <ndrochak@gol.com>
7 // Dick Porter <dick@ximian.com>
9 // (C) 2001, 2002 Ximian Inc, http://www.ximian.com
12 using System.Collections;
13 using System.Resources;
16 using System.Runtime.Serialization;
17 using System.Runtime.Serialization.Formatters.Binary;
19 namespace System.Resources
21 public sealed class ResourceReader : IResourceReader, IEnumerable, IDisposable
25 internal int resourceCount = 0;
30 int dataSectionOffset;
31 long nameSectionOffset;
34 public ResourceReader (Stream stream)
37 throw new ArgumentNullException ("Value cannot be null.");
40 throw new ArgumentException ("Stream was not readable.");
42 reader = new BinaryReader(stream, Encoding.UTF8);
43 formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File|StreamingContextStates.Persistence));
48 public ResourceReader (string fileName)
51 throw new ArgumentNullException ("Path cannot be null.");
53 if (!System.IO.File.Exists (fileName))
54 throw new FileNotFoundException ("Could not find file " + Path.GetFullPath(fileName));
56 reader = new BinaryReader (new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read));
57 formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File|StreamingContextStates.Persistence));
62 /* Read the ResourceManager header and the
63 * ResourceReader header.
65 private void ReadHeaders()
68 int manager_magic = reader.ReadInt32();
70 if(manager_magic != ResourceManager.MagicNumber) {
71 throw new ArgumentException("Stream is not a valid .resources file!");
74 int manager_ver = reader.ReadInt32();
75 int manager_len = reader.ReadInt32();
77 /* We know how long the header is, even if
78 * the version number is too new
80 if(manager_ver > ResourceManager.HeaderVersionNumber) {
81 reader.BaseStream.Seek(manager_len, SeekOrigin.Current);
83 string reader_class=reader.ReadString();
84 if(!reader_class.StartsWith("System.Resources.ResourceReader")) {
85 throw new NotSupportedException("This .resources file requires reader class " + reader_class);
88 string set_class=reader.ReadString();
89 if(!set_class.StartsWith(typeof(ResourceSet).FullName) && !set_class.StartsWith("System.Resources.RuntimeResourceSet")) {
90 throw new NotSupportedException("This .resources file requires set class " + set_class);
94 /* Now read the ResourceReader header */
95 int reader_ver = reader.ReadInt32();
98 throw new NotSupportedException("This .resources file requires unsupported set class version: " + reader_ver.ToString());
101 resourceCount = reader.ReadInt32();
102 typeCount = reader.ReadInt32();
104 types=new Type[typeCount];
105 for(int i=0; i<typeCount; i++) {
106 string type_name=reader.ReadString();
108 /* FIXME: Should we ask for
109 * type loading exceptions
112 types[i]=Type.GetType(type_name);
114 throw new ArgumentException("Could not load type {0}", type_name);
118 /* There are between 0 and 7 bytes of
119 * padding here, consisting of the
120 * letters PAD. The next item (Hash
121 * values for each resource name) need
122 * to be aligned on an 8-byte
126 int pad_align=(int)(reader.BaseStream.Position & 7);
130 pad_chars=8-pad_align;
133 for(int i=0; i<pad_chars; i++) {
134 byte pad_byte=reader.ReadByte();
135 if(pad_byte!="PAD"[i%3]) {
136 throw new ArgumentException("Malformed .resources file (padding values incorrect)");
140 /* Read in the hash values for each
141 * resource name. These can be used
142 * by ResourceSet (calling internal
143 * methods) to do a fast compare on
144 * resource names without doing
145 * expensive string compares (but we
146 * dont do that yet, so far we only
147 * implement the Enumerator interface)
149 hashes=new int[resourceCount];
150 for(int i=0; i<resourceCount; i++) {
151 hashes[i]=reader.ReadInt32();
154 /* Read in the virtual offsets for
157 positions=new long[resourceCount];
158 for(int i=0; i<resourceCount; i++) {
159 positions[i]=reader.ReadInt32();
162 dataSectionOffset = reader.ReadInt32();
163 nameSectionOffset = reader.BaseStream.Position;
164 } catch(EndOfStreamException e) {
165 throw new ArgumentException("Stream is not a valied .resources file! It was possibly truncated.", e);
169 /* Cut and pasted from BinaryReader, because it's
172 private int Read7BitEncodedInt() {
178 b = reader.ReadByte();
180 ret = ret | ((b & 0x7f) << shift);
182 } while ((b & 0x80) == 0x80);
187 private string ResourceName(int index)
191 long pos=positions[index]+nameSectionOffset;
192 reader.BaseStream.Seek(pos, SeekOrigin.Begin);
194 /* Read a 7-bit encoded byte length field */
195 int len=Read7BitEncodedInt();
196 byte[] str=new byte[len];
198 reader.Read(str, 0, len);
199 return Encoding.Unicode.GetString(str);
203 private object ResourceValue(int index)
207 long pos=positions[index]+nameSectionOffset;
208 reader.BaseStream.Seek(pos, SeekOrigin.Begin);
210 /* Read a 7-bit encoded byte length field */
211 long len=Read7BitEncodedInt();
212 /* ... and skip that data to the info
213 * we want, the offset into the data
216 reader.BaseStream.Seek(len, SeekOrigin.Current);
218 long data_offset=reader.ReadInt32();
219 reader.BaseStream.Seek(data_offset+dataSectionOffset, SeekOrigin.Begin);
220 int type_index=Read7BitEncodedInt();
221 Type type=types[type_index];
223 if (type==typeof(Byte)) {
224 return(reader.ReadByte());
225 /* for some reason Char is serialized */
226 /*} else if (type==typeof(Char)) {
227 return(reader.ReadChar());*/
228 } else if (type==typeof(Decimal)) {
229 return(reader.ReadDecimal());
230 } else if (type==typeof(DateTime)) {
231 return(new DateTime(reader.ReadInt64()));
232 } else if (type==typeof(Double)) {
233 return(reader.ReadDouble());
234 } else if (type==typeof(Int16)) {
235 return(reader.ReadInt16());
236 } else if (type==typeof(Int32)) {
237 return(reader.ReadInt32());
238 } else if (type==typeof(Int64)) {
239 return(reader.ReadInt64());
240 } else if (type==typeof(SByte)) {
241 return(reader.ReadSByte());
242 } else if (type==typeof(Single)) {
243 return(reader.ReadSingle());
244 } else if (type==typeof(String)) {
245 return(reader.ReadString());
246 } else if (type==typeof(TimeSpan)) {
247 return(new TimeSpan(reader.ReadInt64()));
248 } else if (type==typeof(UInt16)) {
249 return(reader.ReadUInt16());
250 } else if (type==typeof(UInt32)) {
251 return(reader.ReadUInt32());
252 } else if (type==typeof(UInt64)) {
253 return(reader.ReadUInt64());
255 /* non-intrinsic types are
258 object obj=formatter.Deserialize(reader.BaseStream);
259 if(obj.GetType() != type) {
266 * BadImageFormatException,
273 throw new InvalidOperationException("Deserialized object is wrong type");
286 public IDictionaryEnumerator GetEnumerator () {
288 throw new InvalidOperationException("ResourceReader is closed.");
291 return new ResourceEnumerator (this);
295 IEnumerator IEnumerable.GetEnumerator ()
297 return ((IResourceReader) this).GetEnumerator();
300 void IDisposable.Dispose ()
305 private void Dispose (bool disposing)
319 internal class ResourceEnumerator : IDictionaryEnumerator
321 private ResourceReader reader;
322 private int index = -1;
323 private bool finished = false;
325 internal ResourceEnumerator(ResourceReader readerToEnumerate){
326 reader = readerToEnumerate;
329 public virtual DictionaryEntry Entry
332 if (reader.reader == null)
333 throw new InvalidOperationException("ResourceReader is closed.");
335 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
337 DictionaryEntry entry = new DictionaryEntry();
344 public virtual object Key
347 if (reader.reader == null)
348 throw new InvalidOperationException("ResourceReader is closed.");
350 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
351 return (reader.ResourceName(index));
355 public virtual object Value
358 if (reader.reader == null)
359 throw new InvalidOperationException("ResourceReader is closed.");
361 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
362 return(reader.ResourceValue(index));
366 public virtual object Current
369 /* Entry does the checking, no
370 * need to repeat it here
376 public virtual bool MoveNext ()
378 if (reader.reader == null)
379 throw new InvalidOperationException("ResourceReader is closed.");
384 if (++index < reader.resourceCount){
393 public void Reset () {
394 if (reader.reader == null)
395 throw new InvalidOperationException("ResourceReader is closed.");
399 } // internal class ResourceEnumerator
400 } // public sealed class ResourceReader
401 } // namespace System.Resources