@@ -637,8 +637,12 @@ public enum PcxProperty
637637 Version ,
638638 }
639639
640- public sealed class PcxDirectory : Directory < IEntry >
640+ public sealed class PcxDirectory : IDirectory
641641 {
642+ public const int HeaderSizeBytes = 74 ;
643+
644+ private readonly byte [ ] _bytes ;
645+
642646 // NOTE this directory must be strict about the values it can receive for properties.
643647 // Version, for example, must be a byte. Other types are invalid and cannot be written.
644648 // This is different to TIFF, for example, where a value has an expected type, but may
@@ -652,15 +656,126 @@ public sealed class PcxDirectory : Directory<IEntry>
652656 // operations work directly on that byte array. Maybe all read/write operations provide the
653657 // directory instance. This would also help with Exif tags that need to read multiple values
654658 // as part of their description, but will make the API uglier I think.
659+
660+ private static readonly IndexedTable _pcxVersionTable = new IndexedTable ( new [ ]
661+ {
662+ "2.5 with fixed EGA palette information" ,
663+ null ,
664+ "2.8 with modifiable EGA palette information" ,
665+ "2.8 without palette information (default palette)" ,
666+ "PC Paintbrush for Windows" ,
667+ "3.0 or better"
668+ } ) ;
669+
670+ private static readonly IndexedTable _pcxColorPlanesTable = new IndexedTable ( new [ ] { "24-bit color" , "16 colors" } , baseIndex : 3 ) ;
671+ private static readonly IndexedTable _pcxPaletteTypeTable = new IndexedTable ( new [ ] { "Color or B&W" , "Grayscale" } , baseIndex : 1 ) ;
672+
673+ public PcxDirectory ( byte [ ] bytes )
674+ {
675+ if ( bytes == null )
676+ throw new ArgumentNullException ( nameof ( bytes ) ) ;
677+ if ( bytes . Length != HeaderSizeBytes )
678+ throw new ArgumentException ( $ "Must contain { HeaderSizeBytes } bytes", nameof ( bytes ) ) ;
679+ _bytes = bytes ;
680+ }
681+
682+ public byte Version
683+ {
684+ get => _bytes [ 1 ] ;
685+ set => _bytes [ 1 ] = value ;
686+ }
687+
688+ public byte BitsPerPixel
689+ {
690+ get => _bytes [ 3 ] ;
691+ set => _bytes [ 3 ] = value ;
692+ }
693+
694+ // TODO additional properties
695+
696+ IEnumerator IEnumerable . GetEnumerator ( ) => GetEnumerator ( ) ;
697+
698+ public IEnumerator < IEntry > GetEnumerator ( )
699+ {
700+ yield return new Entry < byte > ( "Version" , Version , index => _pcxVersionTable . LookUp ( index ) ) ;
701+ yield return new Entry < byte > ( "Bits Per Pixel" , BitsPerPixel ) ;
702+ yield return new Entry < ushort > ( "X Min" , XMin ) ;
703+ yield return new Entry < ushort > ( "Y Min" , YMin ) ;
704+ yield return new Entry < ushort > ( "X Max" , XMax ) ;
705+ yield return new Entry < ushort > ( "Y Max" , YMax ) ;
706+ yield return new Entry < ushort > ( "Horizontal DPI" , HorizontalDpi ) ;
707+ yield return new Entry < ushort > ( "Vertical DPI" , VerticalDpi ) ;
708+ yield return new Entry < byte [ ] > ( "Palette" , Palette ) ;
709+ yield return new Entry < byte > ( "Color Planes" , ColorPlanes , index => _pcxColorPlanesTable . LookUp ( index ) ) ;
710+ yield return new Entry < ushort > ( "Bytes Per Line" , BytesPerLine ) ;
711+ yield return new Entry < ushort > ( "Palette Type" , PalleteType , index => _pcxPaletteTypeTable . LookUp ( index ) ) ;
712+ }
713+
714+ public int Count => 14 ;
715+
716+ public string Name => "PCX" ;
717+
718+ public IEnumerable < IDirectory > SubDirectories => Enumerable . Empty < IDirectory > ( ) ;
719+ }
720+
721+ internal sealed class IndexedTable
722+ {
723+ public IndexedTable ( string ? [ ] strings , int baseIndex = 0 )
724+ {
725+ }
726+
727+ public string ? LookUp ( int index )
728+ {
729+ asdf
730+ }
731+ }
732+
733+ public class Entry < T > : IEntry
734+ {
735+ private readonly Func < T , string > _descriptor ;
736+
737+ public Entry ( string name , T value , Func < T , string > ? descriptor = null )
738+ {
739+ _descriptor = descriptor ;
740+ Name = name ;
741+ Value = value ;
742+ }
743+
744+ private T Value { get ; }
745+ object IEntry . Value => Value ;
746+ public string Name { get ; }
747+ public string Description => _descriptor == null ? Value . ToString ( ) : _descriptor ( Value ) ;
655748 }
656749
657750 public sealed class PcxReader
658751 {
659752 public PcxDirectory Extract ( SequentialReader reader )
660753 {
661- reader = reader . WithByteOrder ( isMotorolaByteOrder : false ) ;
754+ byte [ ] bytes ;
755+ try
756+ {
757+ bytes = reader . GetBytes ( PcxDirectory . HeaderSizeBytes ) ;
758+ }
759+ catch ( Exception ex )
760+ {
761+ return WithError ( "Exception reading PCX metadata: " + ex . Message ) ;
762+ }
763+
764+ if ( bytes [ 0 ] != 0x0A )
765+ return WithError ( "Invalid PCX identifier byte" ) ;
766+ if ( bytes [ 2 ] != 0x01 )
767+ return WithError ( "Invalid PCX encoding byte" ) ;
768+
769+ // reader = reader.WithByteOrder(isMotorolaByteOrder: false);
770+
771+ return new PcxDirectory ( bytes ) ;
662772
663- var directory = new PcxDirectory ( ) ;
773+ static PcxDirectory WithError ( string errorMessage )
774+ {
775+ var directory = new PcxDirectory ( ) ;
776+ directory . AddError ( errorMessage ) ;
777+ return directory ;
778+ }
664779
665780 try
666781 {
0 commit comments