[Back to GRAPHICS SWAG index]  [Back to Main SWAG index]  [Original]

{
Here a program to display files from deLux Paint II (*.LBM files)
drawings. It uses a PD BGI driver for 320x200x256 color mode. Other
drivers can also be used. Otherwise look in the SWAG lib for routines to
enter this graphics mode and set a pixel in a certain color.

Code isn't optimal and can be made much faster but works. It doesn't
display some brushes because of some undocumented sections in the LBM
file. If the defines are made active the file is decoded and dumped
textual to the screen. After the program a part of a small PD text file
I found and used as base for this program. It contained some bugs but
one evening works does a lot.

Btw. Does anybody know how to distinguish deLux Paint II and deLux Paint
II Enhanced files?
}

{---------------------------------------------------------}
{ Written by : Ir. G.W. van der Vegt                      }
{ Purpose    : a Delux Paint II LBM file displayer/decoder}
{              displays 16 and 256 color bitmaps but no   }
{              brushes and Delux Paint IIe files yet      }
{                                                         }
{              Displays at the moment only                }
{              320x200 or smaller pictures.               }
{                                                         }
{              Uses a PD SVGA driver to access MCGA mode. }
{              Substitute your own if you havn't got it.  }
{                                                         }
{ File format info by Bob Montgomery 9-21-90, although    }
{ it wasn't very accurate (he forgot Motorola swaps       }
{ lo & hi byes of words) and didn't cover the             }
{ DPPV, CRNG & GRAB sections.                             }
{                                                         }
{ Use the verbose (and rle) defines to get a dump of the  }
{ lbm file.                                               }
{---------------------------------------------------------}

PROGRAM lbm(INPUT,OUTPUT);

USES
  crt,
  dos,
  graph;
  routines;

{ DEFINE verbose}
{ DEFINE rle    }
{$I SVGA256.INC}

TYPE
  rgb  = (r,g,b);
  dp2l = ARRAY[0..3] OF BYTE; {read left to right}
  dp2m = ARRAY[1..4] OF CHAR;
  dp2t = ARRAY[rgb] OF BYTE;
  dp2h = RECORD
           msg1         : dp2m; { "FORM" }
           flen         : dp2l;                 { File length - 8 }
           msg2,                { "ILBM" }
           msg3         : dp2m; { "BMHD" }
           hlen         : dp2l;                 { Length of header }
           width,
           Length,
           xoff,
           yoff         : WORD;
           planes,
           masking,
           compression,
           pad          : BYTE;
           tansparent   : INTEGER;
           x_aspect,
           y_aspect     : BYTE;
           screenwidth,
           screenheight : WORD;
         END;

CONST
  max  = 1023;

VAR
   f   : FILE;
   dp2 : dp2h;
   msg : dp2m;
   len : dp2l;
   col : dp2t;

   i,
   j,
   k,
   y   : INTEGER;
   c   : BYTE;
   bl,
   h,
   l   : LONGINT;
   w   : WORD;
   grmode,
   grdriver : INTEGER;
   lin      : ARRAY[0..max] OF BYTE;

CONST
  form  : dp2m = ('F','O','R','M');
  ilbm  : dp2m = ('I','L','B','M');
  bmhd  : dp2m = ('B','M','H','D');
  cmap  : dp2m = ('C','M','A','P');
  body  : dp2m = ('B','O','D','Y');

{$F+}
FUNCTION Detectvga256 : INTEGER;

BEGIN
  Detectvga256 := svga320x200x256;
END;
{$F-}
FUNCTION Len2long(a : dp2l) : LONGINT;

BEGIN
  Len2long:=(((a[0]*256+a[1])*256+a[2])*256+a[3]);
END;

FUNCTION Msg2str(a : dp2m) : STRING;

BEGIN
  Msg2str:=a[1]+a[2]+a[3]+a[4];
END;

FUNCTION Readnext : BYTE;

VAR
 w : WORD;
 c : BYTE;

BEGIN
  Blockread(f,c,1,w);
  IF (w<>1)
    THEN
      BEGIN
        Closegraph;
        Writeln('Unexpected EOF encountered');
        Halt(3);
      END
    ELSE Readnext:=c;
END;

CONST
  cnt : BYTE    = 0;
  rle : BOOLEAN = false;
  dat : BYTE    = 0;
  vir : LONGINT = 0;
  rel : LONGINT = 0;

FUNCTION Getnext : BYTE;

VAR
  c : BYTE;
  w : WORD;

BEGIN
(*
get a code BYTE from the data stream.
IF the msb is 1, the 'count' is (1 - code), max = 127. get the next
   BYTE from the data stream, AND REPEAT it 'count' times.
IF the msb is 0, the 'count' is (1 + code), max = 128. get the next
   'count' bytes from the data stream.
*)
  IF (dp2.compression=0)
    THEN Getnext:=Readnext
    ELSE
      IF (cnt=0)
        THEN
          BEGIN
            c:=Readnext;
            rle:=(c>127);
            IF rle
              THEN
                BEGIN
                  cnt    :=SHORTINT(1-c);
                  dat    :=Readnext;
                  Getnext:=dat;
{$IFDEF rle}
  Delay(500);
  Writeln;
  Write('RLE : ',byte2hex(c),' = ',cnt:3,'x',byte2hex(dat));
{$ENDIF}
                END
              ELSE
                BEGIN
                  cnt    :=1+c;
                  dat    :=Readnext;
                  Getnext:=dat;
{$IFDEF rle}
  Delay(500);
  Writeln;
  Write('UNC : ',byte2hex(c),' : ',byte2hex(dat));
{$ENDIF}
                END;
          END
        ELSE
          BEGIN
            IF NOT(rle)
              THEN dat:=Readnext;
            Getnext:=dat;
{$IFDEF rle}
  IF NOT(rle) THEN Write(' ',byte2hex(dat));
{$ENDIF}
          END;

  Dec(cnt);
  rel:=Filepos(f)-h;
  Inc(vir);
END;

BEGIN
  Assign(f,Paramstr(1)+'.lbm');
  Reset(f,1);

  Blockread(f,dp2,Sizeof(dp2));

  WITH dp2 DO
    BEGIN
{$IFDEF verbose}
      FOR i:=1 TO Sizeof(msg1) DO Write(msg1[i]); Writeln;
      FOR i:=1 TO Sizeof(msg2) DO Write(msg2[i]); Writeln;
{$ENDIF}
      IF (msg1<>form) OR (msg2<>ilbm) OR (msg3<>bmhd)
        THEN
          BEGIN
            Writeln('No DeLux Paint LBM file.');
            Halt(1);
          END;

{$IFNDEF verbose}
      grdriver:=Installuserdriver('SVGA256',@detectvga256);
      grmode  :=svga320x200x256;
      grdriver:=detect;
      Initgraph(grdriver,grmode,'');
{$ENDIF}

    {----Low & high words/bytes are swapped (Motorola 680x0 convention)}
{$IFDEF verbose}
      Writeln('filelength : ',Len2long(flen));
      Writeln('headlength : ',Len2long(hlen));
{$ENDIF}

    {----Low & high bytes are swapped (Motorola 680x0 convention)}
      width       :=Swap(width);
      Length      :=Swap(Length);
      xoff        :=Swap(xoff);
      yoff        :=Swap(yoff);
      screenwidth :=Swap(screenwidth);
      screenheight:=Swap(screenheight);

{$IFDEF verbose}
      Writeln('W .L  : ',width      ,'x',Length);
      Writeln('Xo.Yo : ',xoff       ,'x',yoff  );
      Writeln('Xa.Ya : ',x_aspect   ,'x',y_aspect);
      Writeln('W. H  : ',screenwidth,'x',screenheight);
      Writeln('Planes: ',planes);
      Writeln('Pad   : ',pad);
{$ENDIF}

      Blockread(f,msg,Sizeof(msg));
      Blockread(f,len,Sizeof(len));

{$IFDEF verbose}
      Writeln(Msg2str(msg));
      Delay(1000);
{$ENDIF}

      IF (msg=cmap)
        THEN
          BEGIN
            l:=Len2long(len);
{$IFDEF verbose}
            Writeln('CMAPlen : ',l);
{$ENDIF}
            FOR i:=1 TO l DIV 3 DO
              BEGIN
                Blockread(f,col,Sizeof(col));
{$IFDEF verbose}
                Delay(100);
                Writeln(i-1:4,col[r]:4,col[g]:4,col[b]:4);
{$ELSE}
                Setrgbpalette(i-1,col[r] DIV 4,col[g] DIV 4,col[b] DIV 4);
{$ENDIF}
              END;
            Blockread(f,msg,Sizeof(msg));
          END;

{----dump unkown sections dppv
     the 4 bytes Length is mostly 104 bytes
}
{----dump unkown sections grab
     the 4 bytes Length is 4, section found IN a brush only,
}
{----dump 4 unkown sections crng :
     seems each TO consist OF two entries WITH :
              00 00 0a aa,00 00 01 0e
              00 00 0a aa,00 00 00 00
              00 00 0a aa,00 00 00 00
              00 00 0a aa,00 00 00 00
     brushes contain different values.
}
      WHILE (msg<>body) DO
        BEGIN
          Blockread(f,len,Sizeof(len));
          l:=Len2long(len);
          Writeln(Msg2str(msg)+' : ',l);
          FOR h:=1 TO l DO
            BEGIN
              Blockread(f,c,1);
              Write(' ',byte2hex(c));
            END;
          Blockread(f,msg,Sizeof(msg));
          Writeln;
        END;

      IF (msg=body)
        THEN
          BEGIN
{$IFDEF verbose}
            Writeln(Msg2str(msg));
{$ENDIF}
            Blockread(f,len,Sizeof(len));
            l:=Len2long(len);
            h :=Filepos(f);
{$IFDEF verbose}
            Writeln('BODYlen : ',l);
{$ENDIF}
            IF compression=0
              THEN bl:=l DIV Length DIV planes
              ELSE bl:=width DIV 8;
{$IFDEF verbose}
            Writeln('Bytew   : ',bl);
{$ENDIF}
            FOR y:=1 TO Length DO
              BEGIN
                FOR i:=0 TO max DO lin[i]:=0;
{$R-}
                FOR j:=0 TO planes-1 DO
                  FOR i:=0 TO bl-1 DO
                    BEGIN
                      c:=Getnext;
                      FOR k:=0 TO 7 DO
                        IF (c AND (128 SHR k))>0
                          THEN lin[(i*8)+k]:=lin[(i*8)+k] OR 1 SHL j;
                    END;
{$R+}
{$IFNDEF verbose}
                FOR i:=1 TO width DO
                  Putpixel(i,y,lin[i])
{$ENDIF}
              END;

          END;

{$IFNDEF verbose}
      WHILE NOT Keypressed DO;
      Closegraph;
{$ELSE}
      Writeln('image  ',LONGINT(width)*Length*planes DIV 8);
      Writeln('bodys  ',h);
      Writeln('files  ',Filesize(f));
      Writeln('filep  ',Filepos(f));
      Writeln('heads  ',Sizeof(dp2h));
      Writeln('virtu  ',vir);
{$ENDIF}
      Close(f);
    END;

END.

(*
deluxe paint ii lbm & iff files

the deluxe paint lbm (AND iff) FILE header (40 bytes) has the following
content:
     struct dp2
     {   CHAR msg1[4];               "form"
         BYTE a3, a2, a1, a0;        FILE Length - 8  (Read left TO right)
         CHAR msg2[8];               "ilbmbmhd"
         BYTE b3, b2, b1, b0;        Length OF header (Read left TO right)
         Int  width, Length, xoff, yoff;
         BYTE planes, masking, compression, pad;
         Int  tansparent;
         BYTE x_aspect, y_aspect;
         Int  screenwidth, screenheight;
     } ;
   there may be a color map following a STRING "cmap" IN the FILE. after cmap
        is the Length OF the color map (4 bytes, Read left TO right). the color
        map is BYTE triples (r, g, b) FOR each colors. the number OF colors is
        1 shifted left by planes (1 << planes).
   the actual picture data follows a STRING "body" AND Length OF the picture
        data (4 bytes Read left TO right). the picture data is organized on a
        color plane basis FOR dp2, AND on a pixel basis FOR dp2e (enhanced).
        thus, FOR dp2:
            there are (width / 8) bytes per row.
            the data stream FOR each row consists OF all the bytes FOR plane 0,
                followed by all the bytes FOR plane 1, etc.
        AND FOR dp2e:
            there are (width) bytes/row, where each BYTE is a pixel color.
   IF the data is uncomperessed (compression flag = 0), the data stream bytes
        are fed TO the OUTPUT unmodified. IF it is compressed, it is run Length
        encoded as follows:
            get a code BYTE from the data stream.
            IF the msb is 1, the 'count' is (1 - code), max = 127. get the next
                BYTE from the data stream, AND REPEAT it 'count' times.
            IF the msb is 0, the 'count' is (1 + code), max = 128. get the next
                'count' bytes from the data stream.
*)

[Back to GRAPHICS SWAG index]  [Back to Main SWAG index]  [Original]