
!**************************************************************************
!*                                                                        *
!*    IFFUTILS: Routines for handling run-length encoded IFF-plus files   *
!*                                                                        *
!*                   JHB:  Version 1.7   25 Feb 1987                      *
!*                                                                        *
!**************************************************************************

! param change in IFF WRITE IMAGE: iffh -> %name

%include "inc:util.imp"

%constinteger %c
    iff not magic=16_81,
    iff no file = 16_82,
    iff wrong length = 16_83,
    iff header too short = 16_84,
    iff last one = 16_84

%constinteger iff read=0, iff write=1,
    iff op stream = 1
    
%conststring (31) %array iff error(iff not magic:iff last one)= %c
   "File has wrong magic number",
   "File does not exist",
   "File length inconsistent",
   "Header too short"

%recordformat context fm( %c
%integer filstart, filptr, readwrite, imaddr, %byte colour,screen,file,d1,
%integer xoff, yoff)
%constinteger context size = 28, app size=32

%recordformat iffhdr fm(%integer  hlen, datatype, ht, wid, 
signed, fov ht, fov wid, stereo, baseline, vergence, gaze, id, processed, fstop,
%integer focus, appno, images, subs, xoff, yoff, aspect, mapaddr, mapwid, maplen, 
%record (context fm) %name context, %record (*) %name app,
%string (255) title,
%string (8) date, time)

%externalintegerfn iff open file %alias "IFF_OPEN_FILE" %c
(%string (255) filename,%record (iffhdr fm) %name iffh, %integer readwrite)
   %integer filelen, magic, ht, wid, hlen, filptr, ostream
   %string (255) s1, s2
   %record (context fm) %name context

   %routine rshort(%integername i)
      i=byteinteger(filptr)<<8+byteinteger(filptr+1); filptr=filptr+2
      i=i&16_FFFF
   %end

   iffh=0
   iffh_context == record(heapget(context size))
   context == iffh_context
   context=0
   context_readwrite = readwrite
   %if readwrite = iff read %start

      %result = iff no file %if filename = ""
      filename = filename.".iff" %if exists(filename.".iff")
      %result = iff no file %unless exists(filename)
      connectfile(filename,0,context_filstart,filelen)

      !Basic consistency checks
      filptr = context_filstart;    rshort(hlen); rshort(ht); rshort(wid)
      %result = iff wrong length %if filelen < hlen*2

      filptr = context_filstart+46; rshort(magic)
      %if magic = 16_8516 %start
         context_filptr = context_filstart
         %result=0
      %else
         heapput(context_filstart); context_filstart=0
         %result = iff not magic
      %finish

   %else ;!Output
      filename = filename.".iff" %unless filename -> s1.(".").s2
      ostream = outstream
      openoutput(iff op stream, filename); selectoutput(ostream)
      context_filptr = iff op stream
      %result=0
   %finish
%end

%externalroutine iff close file %alias "IFF_CLOSE_FILE" %c
(%record (iffhdr fm) iffh)
   %integer ostream
   %if iffh_context_readwrite = iff read %start
      %if iffh_context_filstart # 0 %then heapput(iffh_context_filstart)
   %else
      ostream=outstream
      selectoutput(iffh_context_filptr); close output
      selectoutput(ostream)
   %finish
   dispose(iffh_context)
%end

%externalintegerfn iff read header %alias "IFF_READ_HEADER" %c
(%record (iffhdr fm) %name iffh)
   %integer i, hlen, magic, junk, mapaddr, filstart, filptr
   %record (context fm) %name context
   %shortarrayname map(0:*)

   %routine rsymbol(%integername i)
      i=byteinteger(filptr)&255; filptr=filptr+1
   %end

   %routine rshort(%integername i)
      i=byteinteger(filptr)<<8+byteinteger(filptr+1); filptr=filptr+2
      i=i&16_FFFF
   %end

   %routine rstring(%string (*) %name s, %integer maxlen)
      %integer c
      s=""
      %cycle
         c=byteinteger(filptr); filptr=filptr+1
         %exit %if c=0
         s=s.tostring(c)
      %repeatuntil length(s)=maxlen
   %end

   context == iffh_context
   map == array(iffh_mapaddr)
   filstart=context_filstart; filptr=filstart
                            !Fld Byt Description
   rshort(iffh_hlen);       !1    0  Header length in 16-bit words
   rshort(iffh_datatype);   !2    2  Image type
   rshort(iffh_ht);         !3    4  Image height (pixels)
   rshort(iffh_wid);        !4    6  Image width  (pixels)
   rshort(iffh_signed);     !5    8  Signed/Unsigned data
   rshort(iffh_fov ht);     !6   10  field of view height (mrad)
   rshort(iffh_fov wid);    !7   12  field of view width (mrad)
   rshort(iffh_stereo);     !8   14  stereo pair/mono image
   rshort(iffh_baseline);   !9   16  optical centres separation (mm)
   rshort(iffh_vergence);   !10  18  vergence angle (mrad)
   rshort(iffh_gaze);       !11  20  gaze angle (mrad)
   rshort(iffh_id);         !12  22  i.d. of camera or synthesizer
   rshort(iffh_processed);  !13  24  true if image not directly from
                            !        source - i.e. processed since.
   rstring(iffh_date,8);    !14  26  date of generation (dd/mm/yy)
   rstring(iffh_time,8);    !15  34  time of generation (hh:mm:ss)
   rshort(iffh_fstop);      !16  42  lens stop (f no. x 100)
   rshort(iffh_focus);      !17  44  lens focal length (mm)
   rshort(magic);           !18  46  magic number
   rstring(iffh_title,255); !19  48  image title

   hlen = iffh_hlen * 2 ;!Bytes

   %if hlen>512 %start
      filptr=filstart+496

      rshort(iffh_appno);   !J8
      rshort(iffh_images);  !J7
      rshort(iffh_subs);    !J6
      rshort(iffh_xoff);    !J5
      rshort(iffh_yoff);    !J4
      rshort(iffh_aspect);  !J3
      rshort(iffh_mapwid);  !J2
      rshort(iffh_maplen);  !J1

      %if iffh_maplen#0 %and iffh_mapaddr#0 %start
         %for i=0,1,iffh_maplen-1 %cycle
            rshort(junk); map(i)<-junk
         %repeat
      %finish

   %else
      iffh_maplen=0
   %finish

   context_filptr=context_filstart+hlen
   %result=0
%end

%externalintegerfn iff write header %alias "IFF_WRITE_HEADER" %c
(%record (iffhdr fm) %name iffh)

   %integer hptr,hlen,i,ostream
   %record (context fm) %name context

   %routine psymbol(%integer c)
     printsymbol(c); hptr=hptr+1
   %end

   %routine pstring(%string (255) s)
      %integer i
      %if length(s)#0 %start
         %for i=1,1,length(s) %cycle
            psymbol(charno(s, i))
         %repeat
      %finish
   %end

   %routine pshort(%integer i)
      psymbol(i>>8); psymbol(i&255)
   %end
   
   %integerfn min hlen(%record (iffhdr fm) %name h)
      !Return a short header if no JHB fields are used.
      %result = 512 %if h_mapaddr#0
      %result = 256 %unless h_app == nil
      %result = 256 %if h_appno#0 %or h_images#0 %or h_subs#0 %or %c
      h_xoff#0 %or h_yoff#0 %or h_aspect#0
      %result = 128
   %end

   !Set defaults.
   !Default date and time are now.
   !Default header length is minimum >= 128 words
   !Note these values will be returned to IFFHDR.

   iffh_date = date %if iffh_date = ""
   %if iffh_time = "" %start
      iffh_time=time; iffh_time=iffh_time.":00" %if length(iffh_time)=5
   %finish
   %if iffh_mapaddr=0 %then iffh_maplen=0 %and iffh_mapwid=0
   %if iffh_hlen=0 %start ;!He's left it to us
      iffh_hlen = min hlen(iffh)
   %else ;!He wants header a specific length
      %result = iff header too short %if iffh_hlen < min hlen(iffh)
   %finish

   ostream = outstream; selectoutput(iff op stream)
   hptr=0
   pshort(iffh_hlen);                  ! 1: Header length (16-bit words)
   pshort(iffh_datatype);              ! 2: Image type
   pshort(iffh_ht);                    ! 3: Height/Pixels
   pshort(iffh_wid);                   ! 4: Width/Pixels
   pshort(iffh_signed);                ! 5: Unsigned
   pshort(iffh_fov ht);                ! 6: field of view ht = 0
   pshort(iffh_fov wid);               ! 7: field of view wid =0
   pshort(iffh_stereo);                ! 8: mono
   pshort(iffh_baseline);              ! 9: baseline sep=0
   pshort(iffh_vergence);              !10: vergence=0
   pshort(iffh_gaze);                  !11: gaze=0
   pshort(iffh_id);                    !12: source id=0
   pshort(iffh_processed);             !13: unprocessed
   pstring(iffh_date);                 !14: date
   pstring(iffh_time);                 !15: time
   pshort(iffh_fstop);                 !16: f/stop
   pshort(iffh_focus);                 !17: focal length/mm
   pshort(16_8516);                    !18: magic no.
   pstring(iffh_title); psymbol(0);    !19: title

   %if iffh_hlen>=256 %start
      %for i=1,1,496-hptr %cycle; psymbol(0); %repeat
      pshort(iffh_appno);   !J8
      pshort(iffh_images);  !J7
      pshort(iffh_subs);    !J6
      pshort(iffh_xoff);    !J5
      pshort(iffh_yoff);    !J4
      pshort(iffh_aspect);  !J3
      pshort(iffh_mapwid);  !J2
      pshort(iffh_maplen);  !J1
      %if iffh_maplen#0 %start
         %for i=0,1,iffh_maplen-1 %cycle
            pshort(shortinteger(iffh_mapaddr+i<<1))
         %repeat
      %finish
   %finish
   %if hptr#iffh_hlen*2 %start
      %for i=1,1,iffh_hlen*2-hptr %cycle; psymbol(0); %repeat ;!Padding
   %finish
   selectoutput(ostream)
   %result=0
%end

%routine quick move(%integer bytes, %bytename from, to)
   !Clever dicks who think *dbra is better here should remember it has
   !a 16 bit argument.   (I didn't).  Note it won't move overlapping areas.
f loop:
   *move.b (a0)+, (a1)+
   *Subq.l #1, d0
   *bne    f loop
%end

%externalroutine iff show header %alias "IFF_SHOW_HEADER" %c
(%record (iffhdr fm) iffh, %integer detail)
   %integer i,j
   printstring(iffh_date." ".iffh_time.": ")
   printstring(iffh_title); newline
   write(iffh_stereo+1, -1)
   PrintString (" Image"); %if iffh_stereo#0 %then printsymbol('s')
   printstring(": ");Write(iffh_wid,0)
   PrintString (" pixels wide by ");Write(iffh_ht,0)
   PrintString (" pixels high.");Newline
   write(iffh_hlen, -1); printstring("-word header.  ")
   %if iffh_mapaddr=0 %or iffh_maplen=0 %start
      printstring("No colour map")
   %else
      printstring("Colour map: "); write(iffh_maplen,-1)
      printstring(" entries of "); write(iffh_mapwid,-1)
      printstring(" bits"); newline

      %if detail&1#0 %start
        %for i=0, 1, iffh_maplen-1 %cycle
           j=shortinteger(iffh_mapaddr+i<<1)
           phex2(j>>10); printsymbol(':'); phex2(j>>5&31); printsymbol(':')
           phex2(j&31)
           %if i&7=7 %then newline %else space
        %repeat
      %finish
   %finish
   newline
   %if iffh_aspect#0 %start
      printstring("Aspect ratio: "); write(iffh_aspect>>8,-1)
      printstring(":"); write(iffh_aspect&255,-1); newline
   %finish
%end

%externalintegerfn iff read image %alias "IFF_READ_IMAGE" %c
(%record (iffhdr fm) %name iffh, %integer address)
%label eof,cycle,incycle
%record (context fm) %name context
%integer i, j, rlleft, rlno, k, x, no, filptr

%integerfn get byte
   !Get the next character
   !Uses globals RLNO, RLLEFT, FILPTR

!!%bytefn next
!!   filptr=filptr+1 %if byteinteger(filptr)=10
!!   filptr=filptr+1
!!   %result=byteinteger(filptr-1)
!!%end

!!   %integer rl,no,k,filmark
!!   %if rlleft#0 %then rlleft=rlleft-1 %and %result=rlno
!!   no=next
!!   filmark=filptr
!!   %if no = 0 %start
!!      !next char. is 2nd byte of encoded char. or 1st byte K of run length
!!      no=next
!!      %if no=1 %start
!!         no=10
!!         filmark=filptr
!!         k=next
!!      %elseif no=0
!!         filmark=filptr
!!         k=next
!!      %elseif no=3
!!         %result=0
!!      %else
!!         %signal 15,1 ;!We're in a twist
!!      %finish
!!   %else
!!      k=next
!!   %finish
!!   rl=next
!!   %if k=0 %and rl>=4 %start
!!      %if rl>127 %then rl=(rl&127)<<8+next
!!      rlleft=rl-1; rlno=no; %result=rlno
!!   %else
!!      filptr=filmark
!!      %result=no
!!   %finish

%label nonl1,nonl2,nonl3,nonl6,nonl7
%label noneleft,notone,notzero,notthree,nonzero,nolen,onelen

   *MOVE.L  rlleft,D0    ;!%if rlleft#0 %then rlleft=rlleft-1 %and %result=rlno
   *BEQ     noneleft 
   *SUBQ.L  #1,rlleft
   *MOVE.L  rlno,D0
   *rts

noneleft:
   *clr.l d0
   *movea.l filptr,a0   
!!!   *movea.l (a0),a0

   *move.b (a0)+,d0
   *cmp.b #10,d0     ;!Get 1st character, ignoring linefeeds.
   *bne nonl1
   *move.b (a0)+,d0
nonl1:

   *MOVEa.L  a0,a1    ;!mark place immediately after it

   *MOVE.L  d0,d0        ;!If it's not zero, go see if a count follows
   *BNE     nonzero 

   !If it is zero, get next character
   *move.b (a0)+,d0
   *cmp.b #10,d0
   *bne nonl2
   *move.b (a0)+,d0
nonl2:

   *CMP.b   #1,d0       ;!Is it 00,01  (encoded LF)?
   *BNE     notone 

   *MOVE.b   #10,d0      ;!Yes.  Set to 10, mark place and look for count
   *MOVEa.L  a0,a1    ;!filmark=filptr
   *BRA     nonzero

notone:
   *MOVE.b  D0,D0        ;!Is it 00,00 (encoded 00)?
   *BNE     notzero 

   *MOVEa.L  a0,a1    ;!Yes. mark place and look for count
   *BRA     nonzero

notzero:
   *CMP.b   #3,D0       ;!Is it 00,03 (eof) ?
   *BNE     notthree 
   *MOVEQ   #-1,D0       ;!Yes. Result = -1.
                         !  *movea.l filptr,a1;  *move.l  a0,(a1)   instead of..
   *move.l a0,filptr
   *rts

notthree:
   *MOVE.L   #-2,D0      ;!Funny
   *rts

nonzero:
   *move.b (a0)+,d1
   *cmp.b #10,d1     ;!Get next byte.  Might be 0, leading to count
   *bne nonl3
   *move.b (a0)+,d1
nonl3:

   *MOVE.b  d1,D1        ;!Not 0 so not count.  Backpedal pointer & return
   *bne     nolen

   *clr.l  d2
   *move.b (a0)+,d2
   *cmp.b #10,d2     ;!Get one after that. If true count, will be >=4
   *bne nonl6
   *move.b (a0)+,d2
nonl6:

   *CMP.l   #4,D2       ;! >=4?
   *BLT     nolen        ;!No. Not a length

   *CMP.l   #127,D2
   *BLE     onelen       ;! <=127 = one-byte count.
   *AND.l   #127,d2     ;! > 127 = two-byte count.  And with 127, ..
   *LSL.L   #8,D2        ;!..shift up one byte

   *move.b (a0)+,d2
   *cmp.b #10,d2     ;! and read the bottom byte
   *bne nonl7
   *move.b (a0)+,d2
nonl7:

onelen:
   *SUBQ.L  #1,D2        ;!rlleft=rl-1; rlno=no; %result=rlno
   *MOVE.L  D2,rlleft
   *MOVE.b  d0,rlno
                          !  *movea.l filptr,a1;   *move.l a0,(a1)
   *move.l a0,filptr     ;!update filptr   
   *RTS        
nolen:
                          !  *movea.l filptr,a0;   *move.l a1,(a0)
   *move.l a1,filptr
   *rts
%end

context == iffh_context
filptr = context_filptr
context_imaddr = address ;!Used by IFFEXT utilities
rlleft=0; rlno=0
x=0
%if iffh_datatype&16_c0 # 0 %start
!!   %cycle
!!   
!!      !Read in the next run
!!   
!!      no = get byte; %exit %if no<0
!!   
!!      !write the run to the array
!!   
!!      !rlleft = run length - 1.  Note bottom bound of cycle is 0 not 1.
!!      %for k=rlleft,-1,0 %cycle
!!          byteinteger(address+x)=no; x=x+1
!!      %repeat
!!      rlleft=0
!!   %repeat {until filptr>=filend}
   
       *MOVEA.L address,a2

cycle:
       *BSR     getbyte    ;!no in d0
       *MOVE.L  D0,d0
       *BLT     eof
       *MOVE.L  rlleft,d2  ;!count <= 15 bits

incycle:
       *MOVE.B  d0,(a2)+    ;!a(x)=no
       *dbra    d2,incycle

       *CLR.L   rlleft       ;!rlleft=0
       *bra     cycle        ;!"trusting" loop - relies on EOF in file.
   !%repeatuntil filptr>=filend
eof:
       *move.l  d0,no        ;!Leave end condition where we can get at it
       no=0 %if no=-1

%else
   quick move(iffh_wid*iffh_ht, byteinteger(filptr), byteinteger(address))
   filptr = filptr + iffh_wid*iffh_ht
   no=0
%finish

context_filptr = filptr
%result = no
%end

%externalintegerfn iff write image %alias "IFF_WRITE_IMAGE" %c
(%record (iffhdr fm) %name iffh, %integer imaddr)
   %integer i, ix, rl, lastc, c, ct=0, ostream

   %routine pchar(%integer char, count)
      %integer i
   
      %routine pch(%integer char)
         %if char=0 %start
            printsymbol(0); ct=ct+1
         %finishelseif char=10 %start
            printsymbol(0); char=1; ct=ct+1
         %finish
         printsymbol(char); ct=ct+1
      %end

      %routine prl(%integer count)
         %integer i
         pch(char)
         %if count=1 %start
            !No action
         %finishelseif count<4 %start
            %for i=1,1,count-1 %cycle; pch(char); %repeat
         %finishelseif count<=127 %start
            %if count=10 %then pch(char) %and count=count-1
            printsymbol(0); printsymbol(count); ct=ct+2
         %finishelsestart
            %if count&255=10 %then pch(char) %and count=count-1
            printsymbol(0); printsymbol((count>>8)!16_80); printsymbol(count); ct=ct+3
         %finish
      %end
   
      %while count > 16_7FFF %cycle
         prl(16_7FFF); count=count - 16_7FFF
      %repeat
      prl(count) %if count # 0
         
      %if ct>=256 %then newline %and ct=0
   %end

   ostream = outstream; selectoutput(iff op stream)
   ix = imaddr
   %if iffh_datatype & 16_c0 = 0 %start
      %for i=0, 1 ,iffh_ht*iffh_wid-1 %cycle
         printsymbol(byteinteger(ix)); ix=ix+1
      %repeat
   %else
      lastc=byteinteger(ix); ix=ix+1; ct=0; rl=1
      %for i=0,1,iffh_ht*iffh_wid-1 %cycle
         c = byteinteger(ix); ix=ix+1
         %if c=lastc %then rl=rl+1 %elsestart
            pchar(lastc,rl)
            rl=1; lastc=c
         %finish
      %repeat

      %if rl>0 %then pchar(c,rl)
      printsymbol(0); printsymbol(3)
      newline
   %finish
   selectoutput(ostream)
   %result=0
%end

%externalintegerfn iff readin %alias "IFF_READIN" (%string (255) file,
%record (iffhdr fm) %name iffh, %integername ad)
  {Read in an IFF file.  If AD is 0 the routine will obtain space for the image}
  {on the heap otherwise it will store it at AD.}
   %integer rc, i, a, m

   rc = iff open file(file, iffh, iff read)
   %if rc=0 %start
      iffh_mapaddr = heapget(1024) ;!Somewhere to put the map
      rc = iff read header(iffh)   ;!read in the header
      %if rc=0 %start
         %if iffh_maplen=0 %then heapput(iffh_mapaddr) %and iffh_mapaddr=0
         %if ad=0 %start
            m = (iffh_images+1)*(iffh_stereo+1)*iffh_ht*iffh_wid
            a=heapget(m); m=a; ad=a
         %else
            a=ad; m=0
         %finish
         %for i=0, 1, iffh_images %cycle
            rc = iff read image(iffh, a)  ;!then read the actual images
            %if rc#0 %start
               !If read image failed, put back all heap space we got and bomb out
               heapput(iffh_mapaddr) %if iffh_mapaddr#0
               heapput(m) %if m#0
               %exit
            %finish
            a=a+iffh_wid*iffh_ht
         %repeat
         iff close file(iffh)
      %else
         !Put back space for map
         heapput(iffh_mapaddr)
      %finish
   %finish
   %result=rc
%end

%externalintegerfn iff writeout %alias "IFF_WRITEOUT" (%string (255) file,
%record (iffhdr fm) %name iffh, %integer ad)
   %record (iffhdr fm) h
   %record (context fm) %name c
   %integer i, rc, a
   h = iffh;   a=ad
   rc = iff open file(file, iffh, iff write)
   %if rc=0 %start
      c == iffh_context; iffh=h; iffh_context==c
      rc = iff write header(iffh)
      %if rc=0 %start
         %for i=1, 1, (iffh_images+1)*(iffh_stereo+1) %cycle
            rc = iff write image(iffh, ad)  ;!write the image..
            ad=ad+iffh_wid*iffh_ht
            %exit %if rc#0
         %repeat
      %finish
      iff close file(iffh) ;!and close down
   %finish
   %result=rc
%end

%endoffile
!Run-length encoding (where applied) is...
!<char> {<repeat>}   repeated indefinitely
!<char> = 0  encodes as 0, 0
!<char> = 10 encodes as 0, 1
!e.o.f.      encoded as 0, 3
!<repeat>    encoded as 0, <count>, 4<=<count><=127
!            or         0 <count>>8> ! 128, <count>&255

!Reason for twiddles is to make it insensitive to inserted newlines.
!Header is uncompressed and is 1024 bytes long.  Format is IFF format published
!by Andrew Blake but extended by 512 bytes to include colour map.

! -------------------------------------------------------------------------

! IFF.INC is a suite of routines for handling IFF files painlessly.
! The intended sequence of events is you do..
! 
! IFF OPEN FILE
! 
! IFF SHOW HEADER     if you want to
! IFF READ HEADER     or  IFF WRITE HEADER
! IFF READ IMAGE      or  IFF WRITE IMAGE
! 
! IFF CLOSE FILE
! 
! %externalintegerfn IFF OPEN FILE(%string (255) filename, %c
! %record (iffhdr fm) %name iffh, %integer readwrite)
! -------------------------------------------------------------------------
! 
! FILENAME is the name of the IFF file to be read or written.
! 
! READWRITE = IFF READ
! 
! The file is opened for input.
! Note that FILENAME is a root name.  The routine will look for FILENAME.IFF
! and will only read from FILENAME if FILENAME.IFF does not exist.
! IFFH is a record of type IFFHDR FM (q.v.).   It will be ZEROED and a pointer
! placed in IFFH_CONTEXT.  Do NOT alter this. E.G. don't do an "IFFH = 0" after
! calling this.
! 
! The function will return 0 if successful, IFF NO FILE if it can't find the file
! or IFF NOT MAGIC if the appropriate header bytes do not correspond to the IFF
! 'magic number'.
! 
! READWRITE = IFF WRITE
! 
! Stream 1 is opened for output to FILENAME.  Make sure stream 1 is not in use
! elsewhere.
! 
! %externalroutine IFF CLOSE FILE(%record (iffhdr fm) iffh)
! -------------------------------------------------------------------------
! 
! This routine should be called to close access to the file when you are finished
! with it.  Failure to do so will result in heap space being lost (READ mode)
! or stream 1 being left open (WRITE mode).
! 
! %externalintegerfn IFF READ HEADER(%record (iffhdr fm) %name iffh)
! -------------------------------------------------------------------------
! 
! The header of a previously opened IFF file will be read and the appropriate
! fields filled in.  If you supply a non-zero IFFH_MAPADDR it will be taken as
! the base address to which the embedded colour map (if present) will be written.
! If MAPADDR is left zero, any map will be ignored.
! 
! %externalintegerfn IFF WRITE HEADER(%record (iffhdr fm) %name iffh)
! -------------------------------------------------------------------------
! 
! This writes the parameters supplied in IFFHDR as an IFF header to a previously
! IFF OPENed IFF file.
! 
! DATE and TIME default to now.
! The header length defaults to the minimum >= 128 words.
! Note these values will be written back to IFFHDR.
! If IFFH_MAPADDR is non-zero it will be taken to be a colour map of length
! IFFH_MAPLEN and will be written.
! 
! %externalroutine IFF SHOW HEADER(%record (iffhdr fm) iffh, %integer detail)
! -------------------------------------------------------------------------
! 
! This displays the values of some of the more useful IFFHDR fields.
! 
! %externalintegerfn IFF READ IMAGE(%record (iffhdr fm) %name iffh, %c
! %integer address)
! -------------------------------------------------------------------------
! 
! This reads an image from a previously IFF OPENED file which has had its
! header read.  The image will be dumped to an area of store starting at
! ADDRESS and will be expanded if IFFHDR_TYPE indicates it has been run-length
! encoded.
