PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/WorldView/Utilities/BackwardsScanner.cs

#
C# | 505 lines | 372 code | 92 blank | 41 comment | 111 complexity | a48598aeadc39d36566154aa42909737 MD5 | raw file
  1. using System;
  2. using System.IO;
  3. using System.Drawing;
  4. using MoreTerra.Structures;
  5. namespace MoreTerra.Utilities
  6. {
  7. class BackwardsScanner
  8. {
  9. private FileStream stream;
  10. private BackwardsBinaryReader backReader;
  11. private Int32 MaxX, MaxY;
  12. private WorldHeader header;
  13. #region Constructors
  14. public BackwardsScanner(FileStream str, WorldHeader head)
  15. {
  16. stream = str;
  17. MaxX = head.MaxTiles.X;
  18. MaxY = head.MaxTiles.Y;
  19. header = head;
  20. backReader = new BackwardsBinaryReader(stream);
  21. }
  22. #endregion
  23. public Int64 SeekToChestsBackwards()
  24. {
  25. Footer useFooter;
  26. NPC useNPC;
  27. Sign useSign = new Sign();
  28. Item useItem = new Item();
  29. Chest useChest;
  30. Int32 signCount;
  31. Int32 chestCount;
  32. Int32 i, j;
  33. Int32 countByte;
  34. long beforeReads;
  35. stream.Seek(0, SeekOrigin.End);
  36. useFooter = tryReadFooterBackwards();
  37. // We'll fail if we don't have a good footer object.
  38. if (useFooter.Active == false)
  39. return 0;
  40. if (header.ReleaseNumber >= 0x24)
  41. {
  42. String NPCName;
  43. if (header.ReleaseNumber >= 68)
  44. {
  45. NPCName = backReader.ReadBackwardsString(false);
  46. if (NPCName == null)
  47. return 0;
  48. header.PiratesName = NPCName;
  49. NPCName = backReader.ReadBackwardsString(false);
  50. if (NPCName == null)
  51. return 0;
  52. header.WitchDoctorsName = NPCName;
  53. NPCName = backReader.ReadBackwardsString(false);
  54. if (NPCName == null)
  55. return 0;
  56. header.PaintersName = NPCName;
  57. NPCName = backReader.ReadBackwardsString(false);
  58. if (NPCName == null)
  59. return 0;
  60. header.CyborgsName = NPCName;
  61. NPCName = backReader.ReadBackwardsString(false);
  62. if (NPCName == null)
  63. return 0;
  64. header.PartyGirlsName = NPCName;
  65. NPCName = backReader.ReadBackwardsString(false);
  66. if (NPCName == null)
  67. return 0;
  68. header.DyeTradersName = NPCName;
  69. NPCName = backReader.ReadBackwardsString(false);
  70. if (NPCName == null)
  71. return 0;
  72. header.SteamPunkersName = NPCName;
  73. NPCName = backReader.ReadBackwardsString(false);
  74. if (NPCName == null)
  75. return 0;
  76. header.TrufflesName = NPCName;
  77. }
  78. NPCName = backReader.ReadBackwardsString(false);
  79. if (NPCName == null)
  80. return 0;
  81. header.MechanicsName = NPCName;
  82. NPCName = backReader.ReadBackwardsString(false);
  83. if (NPCName == null)
  84. return 0;
  85. header.WizardsName = NPCName;
  86. NPCName = backReader.ReadBackwardsString(false);
  87. if (NPCName == null)
  88. return 0;
  89. header.TinkerersName = NPCName;
  90. NPCName = backReader.ReadBackwardsString(false);
  91. if (NPCName == null)
  92. return 0;
  93. header.DemolitionistsName = NPCName;
  94. NPCName = backReader.ReadBackwardsString(false);
  95. if (NPCName == null)
  96. return 0;
  97. header.ClothiersName = NPCName;
  98. NPCName = backReader.ReadBackwardsString(false);
  99. if (NPCName == null)
  100. return 0;
  101. header.GuidesName = NPCName;
  102. NPCName = backReader.ReadBackwardsString(false);
  103. if (NPCName == null)
  104. return 0;
  105. header.DryadsName = NPCName;
  106. NPCName = backReader.ReadBackwardsString(false);
  107. if (NPCName == null)
  108. return 0;
  109. header.ArmsDealersName = NPCName;
  110. NPCName = backReader.ReadBackwardsString(false);
  111. if (NPCName == null)
  112. return 0;
  113. header.NursesName = NPCName;
  114. NPCName = backReader.ReadBackwardsString(false);
  115. if (NPCName == null)
  116. return 0;
  117. header.MerchantsName = NPCName;
  118. }
  119. // The NPC section always ends with 00 for "No more NPCs".
  120. if (backReader.ReadBackwardsByte() != 00)
  121. return 0;
  122. do
  123. {
  124. useNPC = tryReadNPCBackwards();
  125. } while (useNPC.Active == true);
  126. // So now we need to find a way to read backwards through all the zeros and find our way to the
  127. // last sign.
  128. // So at first it looked like most zeros we could have was 9 and still get a
  129. // good sign but Terraria only allows you to go to within 22 of the edges.
  130. // This means it's actually three max.
  131. signCount = countBackwardZeros(1003);
  132. // Every sign item ends with at least two zeros. If we have less then
  133. // reading backwards failed.
  134. if (signCount < 2)
  135. return 0;
  136. if (signCount == 2)
  137. {
  138. stream.Seek(2, SeekOrigin.Current);
  139. signCount = 0;
  140. }
  141. else
  142. {
  143. stream.Seek(3, SeekOrigin.Current);
  144. signCount -= 3;
  145. }
  146. // Simple loop. We set it to the earliest we could have a good sign then
  147. // keep reading in as many signs as we can. If we don't get a good sign see
  148. // if we have a zero at the very start. If so we can call it an empty sign
  149. // and shift over and try again.
  150. for (i = signCount; i < 1000; i++)
  151. {
  152. useSign = tryReadSignBackwards();
  153. if (useSign.Active == true)
  154. continue;
  155. j = backReader.ReadBackwardsByte();
  156. if (j != 0)
  157. return 0;
  158. }
  159. // Time to read the chests.
  160. // So just like signs the longest 0 string we can get is a pure empty chest with
  161. // Y coord less then 256. This is 23.
  162. chestCount = countBackwardZeros(1023);
  163. if (chestCount < 23)
  164. {
  165. stream.Seek(chestCount, SeekOrigin.Current);
  166. chestCount = 0;
  167. }
  168. else
  169. {
  170. stream.Seek(23, SeekOrigin.Current);
  171. chestCount -= (23);
  172. }
  173. int numChestItems;
  174. if (header.ReleaseNumber < 68)
  175. numChestItems = 20;
  176. else
  177. numChestItems = 40;
  178. for (i = chestCount; i < 1000; i++)
  179. {
  180. beforeReads = stream.Position;
  181. for (j = 0; j < numChestItems; j++)
  182. {
  183. countByte = backReader.PeekBackwardsByte();
  184. if (countByte != 00)
  185. {
  186. useItem = tryReadChestItemBackwards();
  187. if (useItem.Count == 0)
  188. return 0;
  189. }
  190. else
  191. {
  192. backReader.ReadBackwardsByte();
  193. }
  194. }
  195. useChest = tryReadChestHeaderBackwards();
  196. if (useChest.Active == false)
  197. {
  198. stream.Seek(beforeReads, SeekOrigin.Begin);
  199. countByte = backReader.ReadBackwardsByte();
  200. if (countByte != 00)
  201. return 0;
  202. }
  203. }
  204. return stream.Position;
  205. }
  206. private Footer tryReadFooterBackwards()
  207. {
  208. Footer newFooter = new Footer();
  209. newFooter.Id = backReader.ReadBackwardsInt32();
  210. newFooter.Name = backReader.ReadBackwardsString();
  211. newFooter.Active = backReader.ReadBackwardsBoolean();
  212. return newFooter;
  213. }
  214. /// <summary>
  215. /// Tries to make the next bytes backward in the stream fit into an Chest object.
  216. /// If it fails sets the position back to where it was.
  217. /// This does nothing at all for the items in the chest, only the Chest itself.
  218. /// </summary>
  219. /// <returns>An Chest object. Active will be true on any valid Chest.</returns>
  220. private Chest tryReadChestHeaderBackwards()
  221. {
  222. Int32 x, y;
  223. Int32 strictBool;
  224. Chest returnChest = new Chest();
  225. long oldPosition = stream.Position;
  226. Boolean validChest = false;
  227. #if (DEBUG == false)
  228. try
  229. {
  230. #endif
  231. y = backReader.ReadBackwardsInt32();
  232. x = backReader.ReadBackwardsInt32();
  233. returnChest.Coordinates = new Point(x, y);
  234. strictBool = backReader.ReadBackwardsByte();
  235. if (strictBool == 1)
  236. returnChest.Active = true;
  237. if (returnChest.Active == true && x > 0&& y > 0 && x < MaxX && y < MaxY)
  238. validChest = true;
  239. #if (DEBUG == false)
  240. }
  241. catch (EndOfStreamException e)
  242. {
  243. e.GetType();
  244. }
  245. #endif
  246. if (validChest == false)
  247. {
  248. stream.Seek(oldPosition, SeekOrigin.Begin);
  249. returnChest.Active = false;
  250. }
  251. return returnChest;
  252. }
  253. /// <summary>
  254. /// Tries to make the next bytes backward in the stream fit into an Item object.
  255. /// If it fails sets the position back to where it was.
  256. /// </summary>
  257. /// <returns>An Item object. Count will be non-zero on any valid item.</returns>
  258. private Item tryReadChestItemBackwards()
  259. {
  260. Item returnItem = new Item();
  261. long oldPosition = stream.Position;
  262. Boolean validItem = false;
  263. #if (DEBUG == false)
  264. try
  265. {
  266. #endif
  267. if (header.ReleaseNumber >= 0x24)
  268. {
  269. returnItem.Prefix = backReader.ReadBackwardsByte();
  270. returnItem.Id = backReader.ReadBackwardsInt32();
  271. returnItem.Name = Global.Instance.Info.GetItemName(returnItem.Id);
  272. }
  273. else
  274. {
  275. returnItem.Name = backReader.ReadBackwardsString();
  276. }
  277. if (!String.IsNullOrEmpty(returnItem.Name))
  278. {
  279. if (header.ReleaseNumber >= 68)
  280. returnItem.Count = backReader.ReadBackwardsInt16();
  281. else
  282. returnItem.Count = backReader.ReadBackwardsByte();
  283. if (returnItem.Count != 0)
  284. validItem = true;
  285. }
  286. #if (DEBUG == false)
  287. }
  288. catch (EndOfStreamException e)
  289. {
  290. e.GetType();
  291. }
  292. #endif
  293. if (validItem == false)
  294. {
  295. stream.Seek(oldPosition, SeekOrigin.Begin);
  296. returnItem.Count = 0;
  297. }
  298. return returnItem;
  299. }
  300. /// <summary>
  301. /// Tries to make the next bytes backward in the stream fit into an Sign object.
  302. /// If it fails sets the position back to where it was.
  303. /// </summary>
  304. /// <returns>An Sign object. activeSign will be true for a valid Sign.</returns>
  305. private Sign tryReadSignBackwards()
  306. {
  307. Int32 x, y;
  308. Int32 strictbool;
  309. Sign returnSign = new Sign();
  310. long oldPosition = stream.Position;
  311. Boolean validSign = false;
  312. #if (DEBUG == false)
  313. try
  314. {
  315. #endif
  316. y = backReader.ReadBackwardsInt32();
  317. x = backReader.ReadBackwardsInt32();
  318. returnSign.Position = new Point(x, y);
  319. // We're going to try to read in the string. In a Sign the string should
  320. // always have a 0x01 before it to show it was an active Sign.
  321. returnSign.Text = backReader.ReadBackwardsString(true, 1500, 1);
  322. //returnSign.Text = backReader.ReadBackwardsString(true, 1500);
  323. if (returnSign.Text != null)
  324. {
  325. strictbool = backReader.ReadBackwardsByte();
  326. if (strictbool == 1)
  327. returnSign.Active = true;
  328. if (returnSign.Active == true && y != 0 && x != 0)
  329. validSign = true;
  330. }
  331. #if (DEBUG == false)
  332. }
  333. catch (EndOfStreamException e)
  334. {
  335. e.GetType();
  336. }
  337. #endif
  338. if (validSign == false)
  339. {
  340. stream.Seek(oldPosition, SeekOrigin.Begin);
  341. returnSign.Active = false;
  342. }
  343. return returnSign;
  344. }
  345. /// <summary>
  346. /// Tries to make the next bytes backward in the stream fit into an NPC object.
  347. /// If it fails sets the position back to where it was.
  348. /// </summary>
  349. /// <returns>An NPC object. activeNpc will be true for a valid Sign.</returns>
  350. private NPC tryReadNPCBackwards()
  351. {
  352. Int32 x, y;
  353. NPC returnNpc = new NPC();
  354. long oldPosition = stream.Position;
  355. Boolean validNPC = false;
  356. #if (DEBUG == false)
  357. try
  358. {
  359. #endif
  360. y = backReader.ReadBackwardsInt32();
  361. x = backReader.ReadBackwardsInt32();
  362. returnNpc.HomeTile = new Point(x, y);
  363. returnNpc.Homeless = backReader.ReadBackwardsBoolean();
  364. returnNpc.Position = new PointSingle(backReader.ReadBackwardsSingle(),
  365. backReader.ReadBackwardsSingle());
  366. returnNpc.Name = backReader.ReadBackwardsString();
  367. if (!String.IsNullOrEmpty(returnNpc.Name))
  368. {
  369. returnNpc.Active = backReader.ReadBackwardsBoolean();
  370. if (returnNpc.Active == true)
  371. validNPC = true;
  372. }
  373. #if (DEBUG == false)
  374. }
  375. catch (EndOfStreamException e)
  376. {
  377. e.GetType();
  378. }
  379. #endif
  380. if (validNPC == false)
  381. {
  382. stream.Seek(oldPosition, SeekOrigin.Begin);
  383. returnNpc.Active = false;
  384. }
  385. return returnNpc;
  386. }
  387. private Int32 countBackwardZeros(Int32 MaxCount = 0)
  388. {
  389. long oldPosition = stream.Position;
  390. Int32 upperBound = (int)oldPosition;
  391. Int32 count;
  392. Byte readByte;
  393. if (MaxCount > 0)
  394. {
  395. if (oldPosition > MaxCount)
  396. upperBound = MaxCount;
  397. }
  398. for (count = 0; count < upperBound; count++)
  399. {
  400. readByte = backReader.ReadBackwardsByte();
  401. if (readByte != 00)
  402. {
  403. // We need to shove it back forward a step now.
  404. backReader.ReadByte();
  405. break;
  406. }
  407. }
  408. if (count > upperBound)
  409. count = upperBound;
  410. return count;
  411. }
  412. }
  413. }