//=========== READING ============ #region Reading /** <summary> Returns an object info structure loaded from the specified stream. </summary> */ public static ObjectDataInfo FromStream(Stream stream, bool readName) { ObjectDataInfo objInfo = new ObjectDataInfo(); try { BinaryReader reader = new BinaryReader(stream, Encoding.Unicode); // Read the object data header objInfo.Flags = reader.ReadUInt32(); objInfo.FileName = ""; for (int i = 0; i < 8; i++) { char c = (char)reader.ReadByte(); if (c != ' ') { objInfo.FileName += c; } } objInfo.CheckSum = reader.ReadUInt32(); objInfo.Name = ""; if (readName) { uint headerSize = 0x0; switch (objInfo.Type) { case ObjectTypes.Attraction: headerSize = Attraction.HeaderSize; break; case ObjectTypes.SmallScenery: headerSize = SmallScenery.HeaderSize; break; case ObjectTypes.LargeScenery: headerSize = LargeScenery.HeaderSize; break; case ObjectTypes.Wall: headerSize = Wall.HeaderSize; break; case ObjectTypes.PathBanner: headerSize = PathBanner.HeaderSize; break; case ObjectTypes.Path: headerSize = Pathing.HeaderSize; break; case ObjectTypes.PathAddition: headerSize = PathAddition.HeaderSize; break; case ObjectTypes.SceneryGroup: headerSize = SceneryGroup.HeaderSize; break; case ObjectTypes.ParkEntrance: headerSize = ParkEntrance.HeaderSize; break; case ObjectTypes.Water: headerSize = Water.HeaderSize; break; case ObjectTypes.ScenarioText: headerSize = ScenarioText.HeaderSize; break; default: objInfo.Flags = 0xFFFFFFFF; break; } // Decode enough of the chunk to get the name if (headerSize != 0x0) { ReadChunkInfo(reader, objInfo.Type, headerSize, ref objInfo); } } reader.Close(); } catch (System.Exception) { objInfo.Flags = 0xFFFFFFFF; } return(objInfo); }
/** <summary> Reads and decodes the chunk. </summary> */ internal static void ReadChunkInfo(BinaryReader reader, ObjectTypes type, uint headerSize, ref ObjectDataInfo objInfo) { ChunkEncoding encoding = (ChunkEncoding)reader.ReadByte(); uint chunkSize = reader.ReadUInt32(); uint chunkPosition = 0; uint position = 0; long startPos = reader.BaseStream.Position; MemoryStream stream = new MemoryStream(); BinaryWriter writer = new BinaryWriter(stream); switch (encoding) { case ChunkEncoding.None: //Console.WriteLine("None"); writer.Write(reader.ReadBytes((int)headerSize + 200)); position += headerSize + 200; break; case ChunkEncoding.RLE: case ChunkEncoding.RLEString: //Console.WriteLine((encoding == EncodingType.RLE ? "RLE" : "RLEString")); //http://tid.rctspace.com/RLE.html // While the end of the uncompressed chunk has not been reached while (chunkPosition < (long)chunkSize && position < headerSize + 200) { // Read the next byte byte b = reader.ReadByte(); // If the MSB is 0, copy the next (b + 1) bytes if ((b & 0x80) == 0) { uint length = (uint)(b + 1); chunkPosition += length + 1; position += length; //Console.WriteLine("Copy: " + b + " " + length + " Position: " + chunkPosition); writer.Write(reader.ReadBytes((int)length)); } // Else the MSB is 1, repeat the following byte (-b + 1) times else { byte copyByte = reader.ReadByte(); uint length = (uint)((byte)(-(sbyte)b) + 1); chunkPosition += 2; position += length; //Console.WriteLine("Repeat: " + b + " " + length + " " + copyByte + " Position: " + chunkPosition); for (var i = 0; i < length; i++) { writer.Write(copyByte); } } } // Decompress strings if (encoding == ChunkEncoding.RLEString) { Console.WriteLine("RLE String Encoding not supported!"); } break; case ChunkEncoding.Rotate: int shift = 1; while (chunkPosition < (long)chunkSize) { byte b = reader.ReadByte(); b = (byte)((b >> shift) | (b << (8 - shift))); writer.Write(b); chunkPosition++; position++; shift = (shift + 2) % 8; } break; } writer.Close(); BinaryReader reader2 = new BinaryReader(new MemoryStream(stream.GetBuffer())); switch (type) { case ObjectTypes.Attraction: objInfo.Header = new AttractionHeader(); break; case ObjectTypes.SmallScenery: objInfo.Header = new SmallSceneryHeader(); break; case ObjectTypes.LargeScenery: objInfo.Header = new LargeSceneryHeader(); break; case ObjectTypes.Wall: objInfo.Header = new WallHeader(); break; case ObjectTypes.PathBanner: objInfo.Header = new PathBannerHeader(); break; case ObjectTypes.Path: objInfo.Header = new PathingHeader(); break; case ObjectTypes.PathAddition: objInfo.Header = new PathAdditionHeader(); break; case ObjectTypes.SceneryGroup: objInfo.Header = new SceneryGroupHeader(); break; case ObjectTypes.ParkEntrance: objInfo.Header = new ParkEntranceHeader(); break; case ObjectTypes.Water: objInfo.Header = new WaterHeader(); break; case ObjectTypes.ScenarioText: objInfo.Header = new ScenarioTextHeader(); break; } if (objInfo.Header != null) { objInfo.Header.Read(reader2); objInfo.Subtype = objInfo.Header.ObjectSubtype; } else { reader2.ReadBytes((int)headerSize); } byte bs = reader2.ReadByte(); objInfo.Name = ""; // If the lanuage index is 0xFF, end the string table while (bs != 0xFF && bs < 2 && objInfo.Name.Replace(" ", "").Length == 0) { // Read the null-terminated string objInfo.Name = ""; char c = (char)reader2.ReadByte(); while (c != 0x00) { objInfo.Name += c; c = (char)reader2.ReadByte(); } bs = reader2.ReadByte(); } reader2.Close(); stream.Close(); }