ContentReader.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. #include "stdafx.h"
  2. #include "ContentReader.h"
  3. ContentReader::ContentReader(FILE* file, TypeReaderManager* typeReaderManager)
  4. : BinaryReader(file),
  5. typeReaderManager(typeReaderManager)
  6. {
  7. }
  8. // Parses the entire contents of an XNB file.
  9. void ContentReader::ReadXnb()
  10. {
  11. // Read the XNB header.
  12. uint32_t endPosition = ReadHeader();
  13. ReadTypeManifest();
  14. uint32_t sharedResourceCount = Read7BitEncodedInt();
  15. // Read the primary asset data.
  16. Log.WriteLine("Asset:");
  17. ReadObject();
  18. // Read any shared resource instances.
  19. for (uint32_t i = 0 ; i < sharedResourceCount; i++)
  20. {
  21. Log.WriteLine("Shared resource %d:", i);
  22. ReadObject();
  23. }
  24. // Make sure we read the amount of data that the file header said we should.
  25. if (FilePosition() != endPosition)
  26. {
  27. throw exception("End position does not match XNB header: unexpected amount of data was read.");
  28. }
  29. }
  30. // Reads the XNB file header (version number, size, etc.).
  31. uint32_t ContentReader::ReadHeader()
  32. {
  33. uint32_t startPosition = FilePosition();
  34. // Magic number.
  35. uint8_t magic1 = ReadByte();
  36. uint8_t magic2 = ReadByte();
  37. uint8_t magic3 = ReadByte();
  38. if (magic1 != 'X' ||
  39. magic2 != 'N' ||
  40. magic3 != 'B')
  41. {
  42. throw exception("Not an XNB file.");
  43. }
  44. // Target platform.
  45. uint8_t targetPlatform = ReadByte();
  46. switch (targetPlatform)
  47. {
  48. case 'w': Log.WriteLine("Target platform: Windows"); break;
  49. case 'm': Log.WriteLine("Target platform: Windows Phone"); break;
  50. case 'x': Log.WriteLine("Target platform: Xbox 360"); break;
  51. default: Log.WriteLine("Unknown target platform %d", targetPlatform); break;
  52. }
  53. // Format version.
  54. uint8_t formatVersion = ReadByte();
  55. if (formatVersion != 5)
  56. {
  57. Log.WriteLine("Warning: not an XNA Game Studio version 4.0 XNB file. Parsing may fail unexpectedly.");
  58. }
  59. // Flags.
  60. uint8_t flags = ReadByte();
  61. if (flags & 1)
  62. {
  63. Log.WriteLine("Graphics profile: HiDef");
  64. }
  65. else
  66. {
  67. Log.WriteLine("Graphics profile: Reach");
  68. }
  69. bool isCompressed = (flags & 0x80) != 0;
  70. // File size.
  71. uint32_t sizeOnDisk = ReadUInt32();
  72. if (startPosition + sizeOnDisk > FileSize())
  73. {
  74. throw exception("XNB file has been truncated.");
  75. }
  76. if (isCompressed)
  77. {
  78. uint32_t decompressedSize = ReadUInt32();
  79. uint32_t compressedSize = startPosition + sizeOnDisk - FilePosition();
  80. Log.WriteLine("%d bytes of asset data are compressed into %d", decompressedSize, compressedSize);
  81. throw exception("Don't support reading the contents of compressed XNB files.");
  82. }
  83. return startPosition + sizeOnDisk;
  84. }
  85. // Reads the manifest of what types are contained in this XNB file.
  86. void ContentReader::ReadTypeManifest()
  87. {
  88. Log.WriteLine("Type readers:");
  89. Log.Indent();
  90. // How many type readers does this .xnb use?
  91. uint32_t typeReaderCount = Read7BitEncodedInt();
  92. typeReaders.clear();
  93. for (uint32_t i = 0; i < typeReaderCount; i++)
  94. {
  95. // Read the type reader metadata.
  96. wstring readerName = ReadString();
  97. int32_t readerVersion = ReadInt32();
  98. Log.WriteLine("%S (version %d)", readerName.c_str(), readerVersion);
  99. // Look up and store this type reader implementation class.
  100. TypeReader* reader = typeReaderManager->GetByReaderName(readerName);
  101. typeReaders.push_back(reader);
  102. }
  103. // Initialize the readers in a separate pass after they are all registered, in case there are
  104. // circular dependencies between them (eg. an array of classes which themselves contain arrays).
  105. for each (TypeReader* reader in typeReaders)
  106. {
  107. reader->Initialize(typeReaderManager);
  108. }
  109. Log.Unindent();
  110. }
  111. // Reads a single polymorphic object from the current location.
  112. void ContentReader::ReadObject()
  113. {
  114. Log.Indent();
  115. // What type of object is this?
  116. TypeReader* typeReader = ReadTypeId();
  117. if (typeReader)
  118. {
  119. Log.WriteLine("Type: %S", typeReader->TargetType().c_str());
  120. // Call into the appropriate TypeReader to parse the object data.
  121. typeReader->Read(this);
  122. }
  123. else
  124. {
  125. Log.WriteLine("null");
  126. }
  127. Log.Unindent();
  128. }
  129. // Reads either a raw value or polymorphic object, depending on whether the specified typeReader represents a value type.
  130. void ContentReader::ReadValueOrObject(TypeReader* typeReader)
  131. {
  132. if (typeReader->IsValueType())
  133. {
  134. // Read a value type.
  135. Log.Indent();
  136. typeReader->Read(this);
  137. Log.Unindent();
  138. }
  139. else
  140. {
  141. // Read a reference type.
  142. ReadObject();
  143. }
  144. }
  145. // Reads the typeId from the start of a polymorphic object, and looks up the appropriate TypeReader implementation.
  146. TypeReader* ContentReader::ReadTypeId()
  147. {
  148. uint32_t typeId = Read7BitEncodedInt();
  149. if (typeId > 0)
  150. {
  151. // Look up the reader for this type of object.
  152. typeId--;
  153. if (typeId >= typeReaders.size())
  154. {
  155. throw exception("Invalid XNB file: typeId is out of range.");
  156. }
  157. return typeReaders[typeId];
  158. }
  159. else
  160. {
  161. // A zero typeId indicates a null object.
  162. return nullptr;
  163. }
  164. }
  165. // Reads a typeId, and validates that it is the expected type.
  166. void ContentReader::ValidateTypeId(wstring const& expectedType)
  167. {
  168. TypeReader* reader = ReadTypeId();
  169. if (!reader || reader->TargetType() != expectedType)
  170. {
  171. throw exception("Invalid XNB file: got an unexpected typeId.");
  172. }
  173. }
  174. // Reads a shared resource ID, which indexes into the table of shared object instances that come after the primary asset.
  175. void ContentReader::ReadSharedResource()
  176. {
  177. uint32_t resourceId = Read7BitEncodedInt();
  178. if (resourceId)
  179. {
  180. Log.WriteLine("shared resource #%u", resourceId - 1);
  181. }
  182. else
  183. {
  184. Log.WriteLine("null");
  185. }
  186. }