public bool UpdateVariables(IList <FileFragment> list, ref int index, ref List <int> changed) { //Starting saftey thingy if (index == 0) { (list[index] as GIFFileFragment).SectionType = SectionTypes.Header; return(false); } List <SectionTypes> SectionTypeQueue = new List <SectionTypes>(); bool exists = new PackedGlobalColorTableInfo(BinaryFileInterpreter.ReadFileAs <byte>(list[3].Path)).Exists; if (index <= (exists ? 6 : 5)) { SectionTypeQueue.AddRange(new SectionTypes[] { SectionTypes.Header, SectionTypes.Width, SectionTypes.Height, SectionTypes.GlobalColorTablePacked, SectionTypes.ColorIndex, SectionTypes.PixelAspectRatio }); if (exists) { SectionTypeQueue.Add(SectionTypes.ColorTable); } index = 0; } else { index = FindSectionOfType(list, index, SectionTypes.Sentinal, SectionTypes.Trailer); switch ((list[index] as GIFFileFragment).SectionType) { case (SectionTypes.Trailer): index++; goto case (SectionTypes.Sentinal); case (SectionTypes.Sentinal): switch (BinaryFileInterpreter.ReadFileAs <byte>(list[index].Path)) { case ((byte)','): SectionTypeQueue.AddRange(new SectionTypes[] { SectionTypes.Sentinal, SectionTypes.XOffset, SectionTypes.YOffset, SectionTypes.Width, SectionTypes.Height, SectionTypes.LocalColorTablePacked }); bool lctexists = new PackedLocalColorTableInfo(BinaryFileInterpreter.ReadFileAs <byte>(list[index + 5].Path)).Exists; if (lctexists) { SectionTypeQueue.Add(SectionTypes.ColorTable); } SectionTypeQueue.Add(SectionTypes.Unknown); //Unencoded length? //Need to +8 if the local color table exists SectionTypeQueue.AddRange(LabelDataSections(list, index + (lctexists ? 8 : 7), true)); break; case ((byte)'!'): SectionTypeQueue.AddRange(new SectionTypes[] { SectionTypes.Sentinal, SectionTypes.ExtensionType }); switch (BinaryFileInterpreter.ReadFileAs <byte>(list[index + 1].Path)) { //Plain Text case (0x01): SectionTypeQueue.AddRange(new SectionTypes[] { SectionTypes.Length, SectionTypes.XOffset, SectionTypes.YOffset, SectionTypes.Width, SectionTypes.Height, SectionTypes.Width, SectionTypes.Height, SectionTypes.ColorIndex, SectionTypes.ColorIndex }); SectionTypeQueue.AddRange(LabelDataSections(list, index + 11, false)); break; //Graphics Control case (0xF9): SectionTypeQueue.AddRange(new SectionTypes[] { SectionTypes.Length, SectionTypes.GraphicsControlPacked, SectionTypes.DelayTime, SectionTypes.ColorIndex }); break; //Comment case (0xFE): SectionTypeQueue.AddRange(LabelDataSections(list, index + 2, false)); break; //Application case (0xFF): SectionTypeQueue.AddRange(new SectionTypes[] { SectionTypes.Length, SectionTypes.ApplicationIdentifier, SectionTypes.ApplicationAuthCode }); SectionTypeQueue.AddRange(LabelDataSections(list, index + 5, false)); break; //Unknown default: SectionTypeQueue.Add(SectionTypes.Unknown); break; } SectionTypeQueue.Add(SectionTypes.Trailer); break; case ((byte)';'): SectionTypeQueue.Add(SectionTypes.EndOfFile); EndOfFileIndex = index; //TODO check this again break; default: SectionTypeQueue.Add(SectionTypes.Unknown); break; } break; } } bool didWork = false; for (int i = 0; i < SectionTypeQueue.Count; i++) //TODO is this confusing having i++ all the way up here? { if ((list[index] as GIFFileFragment).SectionType != SectionTypeQueue[i]) { (list[index] as GIFFileFragment).SectionType = SectionTypeQueue[i]; changed.Add(index); didWork = true; } index++; } return(didWork); }
//I now have a newfound hatred for .gif after writing this code public void ParseTo(string filename, ref List <FileFragmentReference> output) { using (BinaryReader br = new BinaryReader(new FileStream(filename, FileMode.Open, FileAccess.Read))) { //Header string fileHeader = br.ReadString(6); output.Add(new GIFFileFragment(0, 6, new string[] { "Header", "Header" }, headerTypes.Contains(fileHeader) ? Validity.Valid : Validity.HardInvalid, $"File header = {fileHeader}", SectionTypes.Header)); //Logical Screen Descriptor short LSDwidth = br.ReadInt16(); output.Add(new GIFFileFragment(6, 2, new string[] { "Header", "Logical Screen Descriptor", "Width" }, (LSDwidth >= 0) ? Validity.Valid : Validity.HardInvalid, $"Width = {LSDwidth}", SectionTypes.Width)); short LSDheight = br.ReadInt16(); output.Add(new GIFFileFragment(8, 2, new string[] { "Header", "Logical Screen Descriptor", "Height" }, (LSDheight >= 0) ? Validity.Valid : Validity.HardInvalid, $"Height = {LSDheight}", SectionTypes.Height)); PackedGlobalColorTableInfo globalColorTableInfo = new PackedGlobalColorTableInfo(br.ReadByte()); //TODO validate all of these output.Add(new GIFFileFragment(10, 1, new string[] { "Header", "Packed Byte" }, globalColorTableInfo.ToString(), SectionTypes.GlobalColorTablePacked)); output.Add(new GIFFileFragment(11, 1, new string[] { "Header", "Background Color Index" }, Validity.Unknown, SectionTypes.ColorIndex)); output.Add(new GIFFileFragment(12, 1, new string[] { "Header", "Pixel Aspect Ratio" }, Validity.Unknown, SectionTypes.PixelAspectRatio)); br.BaseStream.Seek(2, SeekOrigin.Current); int GCTLength = 3 * (1 << (globalColorTableInfo.BitsPerEntry + 1)); if (globalColorTableInfo.Exists) { //TODO split more? output.Add(new GIFFileFragment(13, (ulong)GCTLength, new string[] { "Header", "Global Color Table" }, SectionTypes.ColorTable)); br.BaseStream.Seek(GCTLength, SeekOrigin.Current); } //Loop through sections //TODO seperate section counts for extensions and images for (int section = 1; br.BaseStream.Position < br.BaseStream.Length; section++) { //Switch off of section header switch (br.ReadByte()) { #region Image case ((byte)','): output.Add(new GIFFileFragment(br.BaseStream.Position - 1, 1, new string[] { $"Image {section}", "Sentinal" }, Validity.Valid, SectionTypes.Sentinal)); //I would really hope this is valid if we've made it to this case... output.AddRange(ReadLeftTopWidthHeight(br, $"Image {section}", LSDwidth, LSDheight)); PackedLocalColorTableInfo localColorTableInfo = new PackedLocalColorTableInfo(br.ReadByte()); //TODO Validate? output.Add(new GIFFileFragment(br.BaseStream.Position - 1, 1, new string[] { $"Image {section}", "Packed Byte" }, localColorTableInfo.ToString(), SectionTypes.LocalColorTablePacked)); if (localColorTableInfo.Exists) { int LCTLength = 3 * (1 << (localColorTableInfo.BitsPerEntry + 1)); //TODO split more? output.Add(new GIFFileFragment(br.BaseStream.Position, LCTLength, new string[] { $"Image {section}", "Local Color Table" }, SectionTypes.ColorTable)); br.BaseStream.Seek(LCTLength, SeekOrigin.Current); } //TODO what even is this???????? output.Add(new GIFFileFragment(br.BaseStream.Position, 1, new string[] { $"Image {section}", "Unencoded Length" }, br.ReadByte().ToString(), SectionTypes.Unknown /*TODO uncompressed length?*/)); output.AddRange(ReadDataSections(br, $"Image {section}", "Image Data", false, true, Validity.OutOfScope)); break; #endregion #region Extension case ((byte)'!'): output.Add(new GIFFileFragment(br.BaseStream.Position - 1, 1, new string[] { $"Extension {section}", "Sentinal" }, Validity.Valid, SectionTypes.Sentinal)); byte type = br.ReadByte(); //TODO Validate this by making a list of all types output.Add(new GIFFileFragment(br.BaseStream.Position - 1, 1, new string[] { $"Extension {section}", "Type" }, Validity.Unchecked, SectionTypes.ExtensionType)); switch (type) { #region Plain Text case (0x01): byte plainTextSize = br.ReadByte(); //Should always be 0x0Ch output.Add(new GIFFileFragment(br.BaseStream.Position - 1, 1, new string[] { $"Extension {section}", "Plain Text Block Length" }, plainTextSize == 0x0C ? Validity.Valid : Validity.HardInvalid, SectionTypes.Length)); //tbh, this value not being 0x0C would be a hard invalid for filephoenix rn output.AddRange(ReadLeftTopWidthHeight(br, $"Extension {section}", LSDwidth, LSDheight)); //TODO validate one byte output.Add(new GIFFileFragment(br.BaseStream.Position, 1, new string[] { $"Extension {section}", "Character Cell Width" }, Validity.Valid, br.ReadByte().ToString(), SectionTypes.Width)); output.Add(new GIFFileFragment(br.BaseStream.Position, 1, new string[] { $"Extension {section}", "Character Cell Height" }, Validity.Valid, br.ReadByte().ToString(), SectionTypes.Height)); //TODO this is flawed since it needs to fall back to the Local Color table if !GCTExists //Also, if both the GCT and LCT aren't there? Oh boy... int maxIndex = GCTLength / 3; byte TextColorIndex = br.ReadByte(); output.Add(new GIFFileFragment(br.BaseStream.Position, 1, new string[] { $"Extension {section}", "Text Color Index" }, TextColorIndex < maxIndex ? Validity.Valid : Validity.HardInvalid, TextColorIndex.ToString(), SectionTypes.ColorIndex)); byte BackColorIndex = br.ReadByte(); output.Add(new GIFFileFragment(br.BaseStream.Position, 1, new string[] { $"Extension {section}", "Text Background Color Index" }, BackColorIndex < maxIndex ? Validity.Valid : Validity.HardInvalid, BackColorIndex.ToString(), SectionTypes.ColorIndex)); output.AddRange(ReadDataSections(br, $"Extension {section}", "Plain Text", true, false, Validity.Valid)); break; #endregion #region Graphics Control case (0xF9): byte graphicsControlSize = br.ReadByte(); //Should always be 0x04 output.Add(new GIFFileFragment(br.BaseStream.Position - 1, 1, new string[] { $"Extension {section}", "Graphics Control Block Length" }, graphicsControlSize == 0x04 ? Validity.Valid : Validity.HardInvalid, SectionTypes.Length)); PackedGraphicsControlInfo graphicsControlInfo = new PackedGraphicsControlInfo(br.ReadByte()); output.Add(new GIFFileFragment(br.BaseStream.Position - 1, 1, new string[] { $"Extension {section}", "Packed Byte" }, graphicsControlInfo.ToString(), SectionTypes.GraphicsControlPacked)); //In Centiseconds (hundreths of seconds) short DelayTime = br.ReadInt16(); output.Add(new GIFFileFragment(br.BaseStream.Position - 2, 2, new string[] { $"Extension {section}", "Delay Time" }, 0 <= DelayTime ? Validity.Valid : Validity.HardInvalid, //TODO check if this is hard or soft SectionTypes.DelayTime)); output.Add(new GIFFileFragment(br.BaseStream.Position, 1, new string[] { $"Extension {section}", "Transparent Color Index" }, br.ReadByte() < GCTLength / 3 ? Validity.Valid : Validity.HardInvalid, //TODO < or <=? SectionTypes.ColorIndex)); break; #endregion #region Comment case (0xFE): output.AddRange(ReadDataSections(br, $"Extension {section}", "Comment", true, false, Validity.Valid)); break; #endregion #region Application case (0xFF): //TODO kind of unrealistic to parse all possible application chunks... right? byte applicationSize = br.ReadByte(); //Should always be 0x0B output.Add(new GIFFileFragment(br.BaseStream.Position - 1, 1, new string[] { $"Extension {section}", "Application Extension Length" }, applicationSize == 0x0B ? Validity.Valid : Validity.HardInvalid, SectionTypes.Length)); output.Add(new GIFFileFragment(br.BaseStream.Position, 8, new string[] { $"Extension {section}", "Identifier" }, Validity.Valid, br.ReadString(8), SectionTypes.ApplicationIdentifier)); output.Add(new GIFFileFragment(br.BaseStream.Position, 3, new string[] { $"Extension {section}", "Authentication Code" }, Validity.Valid, string.Join(", ", br.ReadBytes(3)), //This could be human readable, or not, so byte[] jsut to be sure SectionTypes.ApplicationAuthCode)); output.AddRange(ReadDataSections(br, $"Extension {section}", "Application", false, false, Validity.Valid)); break; #endregion #region Unknown default: output.Add(FindNextSection(br, true, new string[] { $"Extension {section}", "Unknown Extension Data" }, Validity.Unknown)); break; #endregion } output.Add(new GIFFileFragment(br.BaseStream.Position, 1, new string[] { $"Extension {section}", "Extension Block Trailer" }, br.ReadByte() == 0 ? Validity.Valid : Validity.SoftInvalid, SectionTypes.Trailer)); break; #endregion #region End of file case ((byte)';'): output.Add(new GIFFileFragment(br.BaseStream.Position - 1, 1, new string[] { $"End Of File Marker {section}" }, (br.BaseStream.Position != br.BaseStream.Length || EndOfFileIndex != null) ? Validity.SoftInvalid : Validity.Valid , SectionTypes.EndOfFile)); EndOfFileIndex = output.Count - 1; break; #endregion #region Unknown default: br.BaseStream.Position--; output.Add(FindNextSection(br, false, new string[] { $"Unknown Section {section}" }, Validity.HardInvalid)); break; #endregion } } } }