ResourceReader.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. //
  2. // System.Resources.ResourceReader.cs
  3. //
  4. // Authors:
  5. // Duncan Mak <[email protected]>
  6. // Nick Drochak <[email protected]>
  7. // Dick Porter <[email protected]>
  8. // Marek Safar <[email protected]>
  9. //
  10. // (C) 2001, 2002 Ximian Inc, http://www.ximian.com
  11. // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
  12. //
  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:
  20. //
  21. // The above copyright notice and this permission notice shall be
  22. // included in all copies or substantial portions of the Software.
  23. //
  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.
  31. //
  32. using System.Collections;
  33. using System.Resources;
  34. using System.IO;
  35. using System.Text;
  36. using System.Runtime.Serialization;
  37. using System.Runtime.Serialization.Formatters.Binary;
  38. using System.Security.Permissions;
  39. namespace System.Resources
  40. {
  41. internal enum PredefinedResourceType
  42. {
  43. Null = 0,
  44. String = 1,
  45. Bool = 2,
  46. Char = 3,
  47. Byte = 4,
  48. SByte = 5,
  49. Int16 = 6,
  50. UInt16 = 7,
  51. Int32 = 8,
  52. UInt32 = 9,
  53. Int64 = 10,
  54. UInt64 = 11,
  55. Single = 12,
  56. Double = 13,
  57. Decimal = 14,
  58. DateTime = 15,
  59. TimeSpan = 16,
  60. ByteArray = 32,
  61. Stream = 33,
  62. FistCustom = 64
  63. }
  64. public sealed class ResourceReader : IResourceReader, IEnumerable, IDisposable
  65. {
  66. BinaryReader reader;
  67. IFormatter formatter;
  68. internal int resourceCount = 0;
  69. int typeCount = 0;
  70. Type[] types;
  71. int[] hashes;
  72. long[] positions;
  73. int dataSectionOffset;
  74. long nameSectionOffset;
  75. int resource_ver;
  76. // Constructors
  77. [SecurityPermission (SecurityAction.LinkDemand, SerializationFormatter = true)]
  78. public ResourceReader (Stream stream)
  79. {
  80. if (stream == null)
  81. throw new ArgumentNullException ("Value cannot be null.");
  82. if (!stream.CanRead)
  83. throw new ArgumentException ("Stream was not readable.");
  84. reader = new BinaryReader(stream, Encoding.UTF8);
  85. formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File|StreamingContextStates.Persistence));
  86. ReadHeaders();
  87. }
  88. public ResourceReader (string fileName)
  89. {
  90. if (fileName == null)
  91. throw new ArgumentNullException ("Path cannot be null.");
  92. if (!System.IO.File.Exists (fileName))
  93. throw new FileNotFoundException ("Could not find file " + Path.GetFullPath(fileName));
  94. reader = new BinaryReader (new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read));
  95. formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File|StreamingContextStates.Persistence));
  96. ReadHeaders();
  97. }
  98. /* Read the ResourceManager header and the
  99. * ResourceReader header.
  100. */
  101. private void ReadHeaders()
  102. {
  103. try {
  104. int manager_magic = reader.ReadInt32();
  105. if(manager_magic != ResourceManager.MagicNumber) {
  106. throw new ArgumentException("Stream is not a valid .resources file!");
  107. }
  108. int manager_ver = reader.ReadInt32();
  109. int manager_len = reader.ReadInt32();
  110. /* We know how long the header is, even if
  111. * the version number is too new
  112. */
  113. if(manager_ver > ResourceManager.HeaderVersionNumber) {
  114. reader.BaseStream.Seek(manager_len, SeekOrigin.Current);
  115. } else {
  116. string reader_class=reader.ReadString();
  117. if(!reader_class.StartsWith("System.Resources.ResourceReader")) {
  118. throw new NotSupportedException("This .resources file requires reader class " + reader_class);
  119. }
  120. string set_class=reader.ReadString();
  121. if(!set_class.StartsWith(typeof(ResourceSet).FullName) && !set_class.StartsWith("System.Resources.RuntimeResourceSet")) {
  122. throw new NotSupportedException("This .resources file requires set class " + set_class);
  123. }
  124. }
  125. /* Now read the ResourceReader header */
  126. resource_ver = reader.ReadInt32();
  127. if(resource_ver != 1
  128. #if NET_2_0
  129. && resource_ver != 2
  130. #endif
  131. ) {
  132. throw new NotSupportedException("This .resources file requires unsupported set class version: " + resource_ver.ToString());
  133. }
  134. resourceCount = reader.ReadInt32();
  135. typeCount = reader.ReadInt32();
  136. types=new Type[typeCount];
  137. for(int i=0; i<typeCount; i++) {
  138. types[i]=Type.GetType(reader.ReadString(), true);
  139. }
  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
  145. * boundary.
  146. */
  147. int pad_align=(int)(reader.BaseStream.Position & 7);
  148. int pad_chars=0;
  149. if(pad_align!=0) {
  150. pad_chars=8-pad_align;
  151. }
  152. for(int i=0; i<pad_chars; i++) {
  153. byte pad_byte=reader.ReadByte();
  154. if(pad_byte!="PAD"[i%3]) {
  155. throw new ArgumentException("Malformed .resources file (padding values incorrect)");
  156. }
  157. }
  158. /* Read in the hash values for each
  159. * resource name. These can be used
  160. * by ResourceSet (calling internal
  161. * methods) to do a fast compare on
  162. * resource names without doing
  163. * expensive string compares (but we
  164. * dont do that yet, so far we only
  165. * implement the Enumerator interface)
  166. */
  167. hashes=new int[resourceCount];
  168. for(int i=0; i<resourceCount; i++) {
  169. hashes[i]=reader.ReadInt32();
  170. }
  171. /* Read in the virtual offsets for
  172. * each resource name
  173. */
  174. positions=new long[resourceCount];
  175. for(int i=0; i<resourceCount; i++) {
  176. positions[i]=reader.ReadInt32();
  177. }
  178. dataSectionOffset = reader.ReadInt32();
  179. nameSectionOffset = reader.BaseStream.Position;
  180. } catch(EndOfStreamException e) {
  181. throw new ArgumentException("Stream is not a valied .resources file! It was possibly truncated.", e);
  182. }
  183. }
  184. /* Cut and pasted from BinaryReader, because it's
  185. * 'protected' there
  186. */
  187. private int Read7BitEncodedInt() {
  188. int ret = 0;
  189. int shift = 0;
  190. byte b;
  191. do {
  192. b = reader.ReadByte();
  193. ret = ret | ((b & 0x7f) << shift);
  194. shift += 7;
  195. } while ((b & 0x80) == 0x80);
  196. return ret;
  197. }
  198. private string ResourceName(int index)
  199. {
  200. lock(this)
  201. {
  202. long pos=positions[index]+nameSectionOffset;
  203. reader.BaseStream.Seek(pos, SeekOrigin.Begin);
  204. /* Read a 7-bit encoded byte length field */
  205. int len=Read7BitEncodedInt();
  206. byte[] str=new byte[len];
  207. reader.Read(str, 0, len);
  208. return Encoding.Unicode.GetString(str);
  209. }
  210. }
  211. // TODO: Read complex types
  212. object ReadValueVer2 (int type_index)
  213. {
  214. switch ((PredefinedResourceType)type_index)
  215. {
  216. case PredefinedResourceType.Null:
  217. return null;
  218. case PredefinedResourceType.String:
  219. return reader.ReadString();
  220. case PredefinedResourceType.Bool:
  221. return reader.ReadBoolean ();
  222. case PredefinedResourceType.Char:
  223. return (char)reader.ReadUInt16();
  224. case PredefinedResourceType.Byte:
  225. return reader.ReadByte();
  226. case PredefinedResourceType.SByte:
  227. return reader.ReadSByte();
  228. case PredefinedResourceType.Int16:
  229. return reader.ReadInt16();
  230. case PredefinedResourceType.UInt16:
  231. return reader.ReadUInt16();
  232. case PredefinedResourceType.Int32:
  233. return reader.ReadInt32();
  234. case PredefinedResourceType.UInt32:
  235. return reader.ReadUInt32();
  236. case PredefinedResourceType.Int64:
  237. return reader.ReadInt64();
  238. case PredefinedResourceType.UInt64:
  239. return reader.ReadUInt64();
  240. case PredefinedResourceType.Single:
  241. return reader.ReadSingle();
  242. case PredefinedResourceType.Double:
  243. return reader.ReadDouble();
  244. case PredefinedResourceType.Decimal:
  245. return reader.ReadDecimal();
  246. case PredefinedResourceType.DateTime:
  247. return new DateTime(reader.ReadInt64());
  248. case PredefinedResourceType.TimeSpan:
  249. return new TimeSpan(reader.ReadInt64());
  250. case PredefinedResourceType.ByteArray:
  251. throw new NotImplementedException (PredefinedResourceType.ByteArray.ToString ());
  252. case PredefinedResourceType.Stream:
  253. throw new NotImplementedException (PredefinedResourceType.Stream.ToString ());
  254. }
  255. type_index -= (int)PredefinedResourceType.FistCustom;
  256. return ReadNonPredefinedValue (types[type_index]);
  257. }
  258. object ReadValueVer1 (Type type)
  259. {
  260. // The most common first
  261. if (type==typeof(String))
  262. return reader.ReadString();
  263. if (type==typeof(Int32))
  264. return reader.ReadInt32();
  265. if (type==typeof(Byte))
  266. return(reader.ReadByte());
  267. if (type==typeof(Double))
  268. return(reader.ReadDouble());
  269. if (type==typeof(Int16))
  270. return(reader.ReadInt16());
  271. if (type==typeof(Int64))
  272. return(reader.ReadInt64());
  273. if (type==typeof(SByte))
  274. return(reader.ReadSByte());
  275. if (type==typeof(Single))
  276. return(reader.ReadSingle());
  277. if (type==typeof(TimeSpan))
  278. return(new TimeSpan(reader.ReadInt64()));
  279. if (type==typeof(UInt16))
  280. return(reader.ReadUInt16());
  281. if (type==typeof(UInt32))
  282. return(reader.ReadUInt32());
  283. if (type==typeof(UInt64))
  284. return(reader.ReadUInt64());
  285. if (type==typeof(Decimal))
  286. return(reader.ReadDecimal());
  287. if (type==typeof(DateTime))
  288. return(new DateTime(reader.ReadInt64()));
  289. return ReadNonPredefinedValue(type);
  290. }
  291. // TODO: Add security checks
  292. object ReadNonPredefinedValue (Type exp_type)
  293. {
  294. object obj=formatter.Deserialize(reader.BaseStream);
  295. if(obj.GetType() != exp_type) {
  296. /* We got a bogus
  297. * object. This
  298. * exception is the
  299. * best match I could
  300. * find. (.net seems
  301. * to throw
  302. * BadImageFormatException,
  303. * which the docs
  304. * state is used when
  305. * file or dll images
  306. * cant be loaded by
  307. * the runtime.)
  308. */
  309. throw new InvalidOperationException("Deserialized object is wrong type");
  310. }
  311. return obj;
  312. }
  313. private object ResourceValue(int index)
  314. {
  315. lock(this)
  316. {
  317. long pos=positions[index]+nameSectionOffset;
  318. reader.BaseStream.Seek(pos, SeekOrigin.Begin);
  319. /* Read a 7-bit encoded byte length field */
  320. long len=Read7BitEncodedInt();
  321. /* ... and skip that data to the info
  322. * we want, the offset into the data
  323. * section
  324. */
  325. reader.BaseStream.Seek(len, SeekOrigin.Current);
  326. long data_offset=reader.ReadInt32();
  327. reader.BaseStream.Seek(data_offset+dataSectionOffset, SeekOrigin.Begin);
  328. int type_index=Read7BitEncodedInt();
  329. #if NET_2_0
  330. if (resource_ver == 2)
  331. return ReadValueVer2 (type_index);
  332. #endif
  333. if (type_index == -1)
  334. return null;
  335. return ReadValueVer1 (types[type_index]);
  336. }
  337. }
  338. public void Close ()
  339. {
  340. Dispose(true);
  341. }
  342. public IDictionaryEnumerator GetEnumerator () {
  343. if (reader == null){
  344. throw new InvalidOperationException("ResourceReader is closed.");
  345. }
  346. else {
  347. return new ResourceEnumerator (this);
  348. }
  349. }
  350. IEnumerator IEnumerable.GetEnumerator ()
  351. {
  352. return ((IResourceReader) this).GetEnumerator();
  353. }
  354. void IDisposable.Dispose ()
  355. {
  356. Dispose(true);
  357. }
  358. private void Dispose (bool disposing)
  359. {
  360. if(disposing) {
  361. if(reader!=null) {
  362. reader.Close();
  363. }
  364. }
  365. reader=null;
  366. hashes=null;
  367. positions=null;
  368. types=null;
  369. }
  370. internal class ResourceEnumerator : IDictionaryEnumerator
  371. {
  372. private ResourceReader reader;
  373. private int index = -1;
  374. private bool finished = false;
  375. internal ResourceEnumerator(ResourceReader readerToEnumerate){
  376. reader = readerToEnumerate;
  377. }
  378. public virtual DictionaryEntry Entry
  379. {
  380. get {
  381. if (reader.reader == null)
  382. throw new InvalidOperationException("ResourceReader is closed.");
  383. if (index < 0)
  384. throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
  385. DictionaryEntry entry = new DictionaryEntry();
  386. entry.Key = Key;
  387. entry.Value = Value;
  388. return entry;
  389. }
  390. }
  391. public virtual object Key
  392. {
  393. get {
  394. if (reader.reader == null)
  395. throw new InvalidOperationException("ResourceReader is closed.");
  396. if (index < 0)
  397. throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
  398. return (reader.ResourceName(index));
  399. }
  400. }
  401. public virtual object Value
  402. {
  403. get {
  404. if (reader.reader == null)
  405. throw new InvalidOperationException("ResourceReader is closed.");
  406. if (index < 0)
  407. throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
  408. return(reader.ResourceValue(index));
  409. }
  410. }
  411. public virtual object Current
  412. {
  413. get {
  414. /* Entry does the checking, no
  415. * need to repeat it here
  416. */
  417. return Entry;
  418. }
  419. }
  420. public virtual bool MoveNext ()
  421. {
  422. if (reader.reader == null)
  423. throw new InvalidOperationException("ResourceReader is closed.");
  424. if (finished) {
  425. return false;
  426. }
  427. if (++index < reader.resourceCount){
  428. return true;
  429. }
  430. else {
  431. finished=true;
  432. return false;
  433. }
  434. }
  435. public void Reset () {
  436. if (reader.reader == null)
  437. throw new InvalidOperationException("ResourceReader is closed.");
  438. index = -1;
  439. finished = false;
  440. }
  441. } // internal class ResourceEnumerator
  442. } // public sealed class ResourceReader
  443. } // namespace System.Resources