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
10 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.Collections;
33 using System.Resources;
36 using System.Runtime.Serialization;
37 using System.Runtime.Serialization.Formatters.Binary;
38 using System.Security.Permissions;
40 namespace System.Resources
42 public sealed class ResourceReader : IResourceReader, IEnumerable, IDisposable
46 internal int resourceCount = 0;
51 int dataSectionOffset;
52 long nameSectionOffset;
55 [SecurityPermission (SecurityAction.LinkDemand, SerializationFormatter = true)]
56 public ResourceReader (Stream stream)
59 throw new ArgumentNullException ("Value cannot be null.");
62 throw new ArgumentException ("Stream was not readable.");
64 reader = new BinaryReader(stream, Encoding.UTF8);
65 formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File|StreamingContextStates.Persistence));
70 public ResourceReader (string fileName)
73 throw new ArgumentNullException ("Path cannot be null.");
75 if (!System.IO.File.Exists (fileName))
76 throw new FileNotFoundException ("Could not find file " + Path.GetFullPath(fileName));
78 reader = new BinaryReader (new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read));
79 formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File|StreamingContextStates.Persistence));
84 /* Read the ResourceManager header and the
85 * ResourceReader header.
87 private void ReadHeaders()
90 int manager_magic = reader.ReadInt32();
92 if(manager_magic != ResourceManager.MagicNumber) {
93 throw new ArgumentException("Stream is not a valid .resources file!");
96 int manager_ver = reader.ReadInt32();
97 int manager_len = reader.ReadInt32();
99 /* We know how long the header is, even if
100 * the version number is too new
102 if(manager_ver > ResourceManager.HeaderVersionNumber) {
103 reader.BaseStream.Seek(manager_len, SeekOrigin.Current);
105 string reader_class=reader.ReadString();
106 if(!reader_class.StartsWith("System.Resources.ResourceReader")) {
107 throw new NotSupportedException("This .resources file requires reader class " + reader_class);
110 string set_class=reader.ReadString();
111 if(!set_class.StartsWith(typeof(ResourceSet).FullName) && !set_class.StartsWith("System.Resources.RuntimeResourceSet")) {
112 throw new NotSupportedException("This .resources file requires set class " + set_class);
116 /* Now read the ResourceReader header */
117 int reader_ver = reader.ReadInt32();
119 if(reader_ver != 1) {
120 throw new NotSupportedException("This .resources file requires unsupported set class version: " + reader_ver.ToString());
123 resourceCount = reader.ReadInt32();
124 typeCount = reader.ReadInt32();
126 types=new Type[typeCount];
127 for(int i=0; i<typeCount; i++) {
128 string type_name=reader.ReadString();
130 /* FIXME: Should we ask for
131 * type loading exceptions
134 types[i]=Type.GetType(type_name);
136 throw new ArgumentException("Could not load type {0}", type_name);
140 /* There are between 0 and 7 bytes of
141 * padding here, consisting of the
142 * letters PAD. The next item (Hash
143 * values for each resource name) need
144 * to be aligned on an 8-byte
148 int pad_align=(int)(reader.BaseStream.Position & 7);
152 pad_chars=8-pad_align;
155 for(int i=0; i<pad_chars; i++) {
156 byte pad_byte=reader.ReadByte();
157 if(pad_byte!="PAD"[i%3]) {
158 throw new ArgumentException("Malformed .resources file (padding values incorrect)");
162 /* Read in the hash values for each
163 * resource name. These can be used
164 * by ResourceSet (calling internal
165 * methods) to do a fast compare on
166 * resource names without doing
167 * expensive string compares (but we
168 * dont do that yet, so far we only
169 * implement the Enumerator interface)
171 hashes=new int[resourceCount];
172 for(int i=0; i<resourceCount; i++) {
173 hashes[i]=reader.ReadInt32();
176 /* Read in the virtual offsets for
179 positions=new long[resourceCount];
180 for(int i=0; i<resourceCount; i++) {
181 positions[i]=reader.ReadInt32();
184 dataSectionOffset = reader.ReadInt32();
185 nameSectionOffset = reader.BaseStream.Position;
186 } catch(EndOfStreamException e) {
187 throw new ArgumentException("Stream is not a valied .resources file! It was possibly truncated.", e);
191 /* Cut and pasted from BinaryReader, because it's
194 private int Read7BitEncodedInt() {
200 b = reader.ReadByte();
202 ret = ret | ((b & 0x7f) << shift);
204 } while ((b & 0x80) == 0x80);
209 private string ResourceName(int index)
213 long pos=positions[index]+nameSectionOffset;
214 reader.BaseStream.Seek(pos, SeekOrigin.Begin);
216 /* Read a 7-bit encoded byte length field */
217 int len=Read7BitEncodedInt();
218 byte[] str=new byte[len];
220 reader.Read(str, 0, len);
221 return Encoding.Unicode.GetString(str);
225 private object ResourceValue(int index)
229 long pos=positions[index]+nameSectionOffset;
230 reader.BaseStream.Seek(pos, SeekOrigin.Begin);
232 /* Read a 7-bit encoded byte length field */
233 long len=Read7BitEncodedInt();
234 /* ... and skip that data to the info
235 * we want, the offset into the data
238 reader.BaseStream.Seek(len, SeekOrigin.Current);
240 long data_offset=reader.ReadInt32();
241 reader.BaseStream.Seek(data_offset+dataSectionOffset, SeekOrigin.Begin);
242 int type_index=Read7BitEncodedInt();
243 if (type_index == -1)
245 Type type=types[type_index];
247 if (type==typeof(Byte)) {
248 return(reader.ReadByte());
249 /* for some reason Char is serialized */
250 /*} else if (type==typeof(Char)) {
251 return(reader.ReadChar());*/
252 } else if (type==typeof(Decimal)) {
253 return(reader.ReadDecimal());
254 } else if (type==typeof(DateTime)) {
255 return(new DateTime(reader.ReadInt64()));
256 } else if (type==typeof(Double)) {
257 return(reader.ReadDouble());
258 } else if (type==typeof(Int16)) {
259 return(reader.ReadInt16());
260 } else if (type==typeof(Int32)) {
261 return(reader.ReadInt32());
262 } else if (type==typeof(Int64)) {
263 return(reader.ReadInt64());
264 } else if (type==typeof(SByte)) {
265 return(reader.ReadSByte());
266 } else if (type==typeof(Single)) {
267 return(reader.ReadSingle());
268 } else if (type==typeof(String)) {
269 return(reader.ReadString());
270 } else if (type==typeof(TimeSpan)) {
271 return(new TimeSpan(reader.ReadInt64()));
272 } else if (type==typeof(UInt16)) {
273 return(reader.ReadUInt16());
274 } else if (type==typeof(UInt32)) {
275 return(reader.ReadUInt32());
276 } else if (type==typeof(UInt64)) {
277 return(reader.ReadUInt64());
279 /* non-intrinsic types are
282 object obj=formatter.Deserialize(reader.BaseStream);
283 if(obj.GetType() != type) {
290 * BadImageFormatException,
297 throw new InvalidOperationException("Deserialized object is wrong type");
310 public IDictionaryEnumerator GetEnumerator () {
312 throw new InvalidOperationException("ResourceReader is closed.");
315 return new ResourceEnumerator (this);
319 IEnumerator IEnumerable.GetEnumerator ()
321 return ((IResourceReader) this).GetEnumerator();
324 void IDisposable.Dispose ()
329 private void Dispose (bool disposing)
343 internal class ResourceEnumerator : IDictionaryEnumerator
345 private ResourceReader reader;
346 private int index = -1;
347 private bool finished = false;
349 internal ResourceEnumerator(ResourceReader readerToEnumerate){
350 reader = readerToEnumerate;
353 public virtual DictionaryEntry Entry
356 if (reader.reader == null)
357 throw new InvalidOperationException("ResourceReader is closed.");
359 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
361 DictionaryEntry entry = new DictionaryEntry();
368 public virtual object Key
371 if (reader.reader == null)
372 throw new InvalidOperationException("ResourceReader is closed.");
374 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
375 return (reader.ResourceName(index));
379 public virtual object Value
382 if (reader.reader == null)
383 throw new InvalidOperationException("ResourceReader is closed.");
385 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
386 return(reader.ResourceValue(index));
390 public virtual object Current
393 /* Entry does the checking, no
394 * need to repeat it here
400 public virtual bool MoveNext ()
402 if (reader.reader == null)
403 throw new InvalidOperationException("ResourceReader is closed.");
408 if (++index < reader.resourceCount){
417 public void Reset () {
418 if (reader.reader == null)
419 throw new InvalidOperationException("ResourceReader is closed.");
423 } // internal class ResourceEnumerator
424 } // public sealed class ResourceReader
425 } // namespace System.Resources