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