home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d5xx / d546 / 2view.lha / 2View / Source / 2View.c next >
C/C++ Source or Header  |  1991-09-19  |  26KB  |  938 lines

  1.  
  2. /***********************************************************************\
  3. *                 2View V1.11                *
  4. *     A simple, fast ILBM viewer, for use under AmigaOS V2.x.    *
  5. *      Written and ©1991 by Dave Schreiber.  All Rights Reserved.    *
  6. *                                    *
  7. * Usage:                                *
  8. *  2View FILE/A/M,FROM/K,SECS=SECONDS/K/N,TICKS/K/N,LOOP/S,PRINT    *
  9. *                                    *
  10. *  Where the following arguments are defined as follows:        *
  11. *   FILE - The name of one (or more) IFF ILBM files                     *
  12. *   FROM - A file containing a list of filenames.  Used instead of FILE *
  13. *   SECS - Number of seconds to display a file                *
  14. *   TICKS - Number of ticks (1/50ths of a second)                       *
  15. *   LOOP - When finished showing the last pictures, start over        *
  16. *   PRINT - Print each picture as it is shown                *
  17. *                                    *
  18. *  To compile (with SAS/C V5.10a):                                      *
  19. *     lc -v 2View ARexx                         *
  20. *     blink with 2View.lnk                        *
  21. *                                    *
  22. *  Version history:                            *
  23. *     1.11 - Improved error reporting (with this version, if the user   *
  24. *         run 2View from Workbench and there's an error, a requester *
  25. *         is put up.  Previously, the user was not notified at all    *
  26. *         of the error).                        *
  27. *         Released 9/11/91                        *
  28. *                                    *
  29. *     1.10 - Added support for Workbench, ARexx, scrollable bitmaps,    *
  30. *         and printing.  Also, the user can now use CTRL-C to advance*
  31. *         to the next frame, and CTRL-D to abort a playlist.     *
  32. *         Released 9/3/91                        *
  33. *                                    *
  34. *     1.00 - Original version.    Released 7/24/91            *
  35. *                                    *
  36. \************************************************************************/
  37.  
  38.  
  39. unsigned long availBytes,curPos,bufSize;
  40.  
  41. /*Include files*/
  42.  
  43. #include <exec/types.h>
  44. #include <libraries/iffparse.h>
  45. #include <dos/dos.h>
  46. #include <intuition/screens.h>
  47. #include <intuition/intuition.h>
  48. #include <dos/rdargs.h>
  49. #include <exec/memory.h>
  50. #include <devices/printer.h>
  51. #include <devices/prtgfx.h>
  52. #include <workbench/startup.h>
  53.  
  54. /*Prototypes*/
  55. #include <clib/exec_protos.h>
  56. #include <clib/intuition_protos.h>
  57. #include <clib/graphics_protos.h>
  58. #include <clib/iffparse_protos.h>
  59. #include <clib/dos_protos.h>
  60. #include <clib/alib_protos.h>
  61.  
  62. /*Pragmas*/
  63. #include <pragmas/exec_pragmas.h>
  64. #include <pragmas/intuition_pragmas.h>
  65. #include <pragmas/graphics_pragmas.h>
  66. #include <pragmas/iffparse_pragmas.h>
  67. #include <pragmas/dos_pragmas.h>
  68.  
  69. /*Other include files*/
  70. #include "iff.h"
  71. #include "2View.h"
  72. #include "arexx.h"
  73.  
  74. /*Libraries we'll need*/
  75. struct Library *IFFParseBase=NULL;
  76. struct Library *IntuitionBase=NULL;
  77. struct Library *GfxBase=NULL;
  78.  
  79. /*Provided by the compiler*/
  80. extern struct Library *SysBase;
  81. extern struct Library *DOSBase;
  82.  
  83. /*Generic screen and window definitions.  They will be used to define*/
  84. /*the screen and window that the various pictures will be shown on*/
  85. struct NewScreen newScreen=
  86. {
  87.    0,0,0,0,0,1,0,NULL,CUSTOMSCREEN|SCREENBEHIND|AUTOSCROLL,NULL,NULL,NULL,
  88.    NULL
  89. };
  90.  
  91. struct NewWindow newWindow =
  92. {
  93.    0,0,0,0,0,1,MENUDOWN|SELECTDOWN|ACTIVEWINDOW|VANILLAKEY,
  94.       RMBTRAP|BORDERLESS|NOCAREREFRESH|ACTIVATE,NULL,NULL,NULL,NULL,NULL,
  95.       0,0,640,400,CUSTOMSCREEN
  96. };
  97.  
  98. struct Screen *screen=NULL;
  99. struct Window *window=NULL;
  100.  
  101. /*A true here indicates the current ILBM file is compressed*/
  102. BYTE Compression;
  103.  
  104. /*An error message that used in various places*/
  105. char *errorMsg="An error occured while reading";
  106.  
  107. /*The version string.  Do a 'version 2View' to display it*/
  108. char *version="$VER: QView V1.11 (11.9.91)";
  109.  
  110. /*Just so that the © message is part of the actual program*/
  111. char *copyRightMsg="Copyright 1991 by Dave Schreiber.  All Rights Reserved.";
  112.  
  113. BYTE ExitFlag=FALSE;    /*'Exit now' flag*/
  114. UWORD ticks=0;          /*Delay requested by user.*/
  115.  
  116. /*The previous screen and window*/
  117. struct Window *prevWindow=NULL;
  118. struct Screen *prevScreen=NULL;
  119.  
  120. /*Data for a blank pointer*/
  121. UWORD chip fakePointerData[]={0,0,0,0,0};
  122.  
  123. BPTR StdErr=NULL;          /*'Standard error' for AmigaDOS IO functions*/
  124. struct IFFHandle *iff=NULL;   /*IFF handle*/
  125. BPTR pL=NULL;              /*Playlist file pointer*/
  126. BOOL masking,print,toFront,printPics;
  127. extern struct WBStartup *WBenchMsg;
  128.  
  129. char *playListFilename=NULL;
  130.  
  131. /*Variables that have to be global so that ARexx.c can access them*/
  132. ButtonTypes rexxAbort=none;
  133. extern void dispRexxPort(void);
  134. extern void dnRexxPort();
  135. long arexxSigBit;
  136. UWORD ticksRemaining=0;
  137. BOOL loop=FALSE;
  138.  
  139. char *picFilename;
  140. char buf[512];         /* A place to dump mask information */
  141.  
  142. void _main();
  143.  
  144. struct TagItem TagList[]=
  145. {
  146.       /* This defines what part of the displayed picture is shown.  It's */
  147.       /* necessary to have a line like this in here in order to get     */
  148.       /* 2.0 autoscrolling to work.                     */
  149.    {SA_Overscan,OSCAN_VIDEO},
  150.    {TAG_DONE,NULL}
  151. };
  152.  
  153. char *about1="2View";
  154. char *about2="Please";
  155.  
  156.  
  157. void _main()
  158. {
  159.    UWORD c;
  160.    LONG args[7];
  161.    char **filenames;
  162.    char curFilename[140];
  163.    BYTE playList; /*True if a playlist is being used, false otherwise*/
  164.  
  165.       /*Initialize the argument buffers to NULL*/
  166.    for(c=0;c<7;c++)
  167.       args[c]=NULL;
  168.  
  169.       /*Open the libraries*/
  170.    IFFParseBase=(struct Library *)OpenLibrary("iffparse.library",0L);
  171.    if(IFFParseBase==NULL)
  172.    {
  173.       cleanup();
  174.       exit(50);
  175.    }
  176.  
  177.    IntuitionBase=(struct Library *)OpenLibrary("intuition.library",0L);
  178.    if(IntuitionBase==NULL)
  179.    {
  180.       cleanup();
  181.       exit(75);
  182.    }
  183.  
  184.    GfxBase=(struct Library *)OpenLibrary("graphics.library",0L);
  185.    if(GfxBase==NULL)
  186.    {
  187.       cleanup();
  188.       exit(85);
  189.    }
  190.  
  191.       /*Get the arguments*/
  192.    if(WBenchMsg==NULL)
  193.    {
  194.       ParseArgs(args);
  195.  
  196.      /*If a playlist filename was provided, store it for later use*/
  197.       if(args[4]!=NULL)
  198.       {
  199.      playListFilename=(char *)args[4];
  200.      playList=TRUE;
  201.       }
  202.       else  /*Otherwise, read the filenames from the command line*/
  203.      playList=FALSE;
  204.  
  205.      /*If a time was provided (in ticks), use it*/
  206.       if(args[1]!=NULL)
  207.      ticks=*(ULONG *)args[1]*50;
  208.  
  209.      /*If a time was provided (in seconds), use it (overrides ticks)*/
  210.       if(args[2]!=NULL && *(ULONG *)args[2]!=0)
  211.      ticks=*(ULONG *)args[2];
  212.  
  213.      /*If neither a picture filename, nor a playlist filename, was*/
  214.      /*specified, print an error and exit.*/
  215.       if(args[0]==NULL && !playList)
  216.       {
  217.      printError("Please enter one or more filenames","");
  218.      cleanup();
  219.      exit(45);
  220.       }
  221.  
  222.      /*Determine if we should print the pictures we display or not*/
  223.       printPics=(args[5]!=NULL);
  224.  
  225.      /*Get the pointer to the list of filename*/
  226.       filenames=(char **)args[0];
  227.  
  228.      /*Will we loop back to the beginning once we finish displaying all*/
  229.      /*the pictures?*/
  230.       loop=(args[3]!=NULL);
  231.    }
  232.    else
  233.       if(WBenchMsg->sm_NumArgs==1)
  234.       {
  235.      EasyRequest(NULL,&erError1Line,NULL,
  236.              "2View V1.11 (September 11, 1991)",
  237.              "Written by Dave Schreiber");
  238.      cleanup();
  239.      exit(0);
  240.       }
  241.  
  242.  
  243.  
  244.       /* Initialize the ARexx port */
  245.    arexxSigBit=initRexxPort();
  246.    if(arexxSigBit==0)
  247.    {
  248.       cleanup();
  249.       exit(47);
  250.    }
  251.  
  252.       /*Allocate the IFF structure*/
  253.    iff=AllocIFF();
  254.  
  255.       /*If the allocation failed, abort*/
  256.    if(iff==NULL)
  257.    {
  258.       printError("Couldn't allocate necessary resources","");
  259.       cleanup();
  260.       exit(100);
  261.    }
  262.  
  263.       /*Run until we run out of filenames, or the user aborts*/
  264.    while(!ExitFlag)
  265.    {
  266.       picFilename=curFilename;     /*Get a pointer to the filename buffer*/
  267.  
  268.      /*Check to see if we're running from Workbench.  If so, and the*/
  269.      /*user provided names of pictures to display (by clicking on their*/
  270.      /*icons), display those pictures*/
  271.       if(WBenchMsg!=NULL && WBenchMsg->sm_NumArgs>1)
  272.       {
  273.      CurrentDir(WBenchMsg->sm_ArgList[1].wa_Lock);
  274.      picFilename=WBenchMsg->sm_ArgList[1].wa_Name;
  275.       }
  276.       else if(playList) /*If a playlist is being used*/
  277.       {
  278.      pL=Open(playListFilename,MODE_OLDFILE);   /*Open the playlist*/
  279.  
  280.      if(pL==NULL)   /*If we couldn't open the playlist, abort*/
  281.      {
  282.         printError("Can't open playlist","");
  283.         cleanup();
  284.         exit(199);
  285.      }
  286.  
  287.      do    /*Loop until we run out of playlist, or get a valid name*/
  288.      {
  289.         if(FGets(pL,picFilename,140)==NULL) /*If end-of-file*/
  290.            picFilename=NULL;       /*Set as NULL as a flag*/
  291.      }
  292.      while(picFilename!=NULL && picFilename[0]==0x0A);
  293.  
  294.      if(picFilename!=NULL)        /*If not NULL, it's a valid filename*/
  295.         picFilename[strlen(picFilename)-1]=NULL; /*Remove the linefeed*/
  296.       }
  297.       else  /*Otherwise, if a playlist isn't being used, get the current*/
  298.      picFilename=filenames[0];     /*filename*/
  299.  
  300.  
  301.      /*Loop while the user hasn't requested an abort, and while*/
  302.      /*there are still files to display*/
  303.       for(c=0;!ExitFlag && picFilename!=NULL;c++)
  304.       {
  305.      if((iff->iff_Stream=Open(picFilename,MODE_OLDFILE))==NULL)
  306.      {     /*If the ILBM file can't be opened...*/
  307.  
  308.            /*Print an error...*/
  309.         printError("Can't open:  ", picFilename);
  310.  
  311.         cleanup();
  312.         exit(200);
  313.      }
  314.  
  315.      InitIFFasDOS(iff);      /*The IFF file will be read from disk*/
  316.  
  317.      OpenIFF(iff,IFFF_READ); /*Make iffparse.library aware of the*/
  318.                  /*ILBM file*/
  319.  
  320.      /*Read in the file and display*/
  321.      ReadAndDisplay(picFilename,iff);
  322.  
  323.      CloseIFF(iff);          /*Release iffparse's hold on the file*/
  324.  
  325.      Close(iff->iff_Stream); /*Close the file*/
  326.  
  327.         /*Get the next filename, either from Workbench,*/
  328.      if(WBenchMsg!=NULL)
  329.      {
  330.         if(WBenchMsg->sm_NumArgs > c+2)
  331.         {
  332.            CurrentDir(WBenchMsg->sm_ArgList[c+2].wa_Lock);
  333.            picFilename=WBenchMsg->sm_ArgList[c+2].wa_Name;
  334.         }
  335.         else
  336.            picFilename=NULL;
  337.      }
  338.      else if(playList)   /*The playlist,*/
  339.      {
  340.         do
  341.         {
  342.            if(FGets(pL,picFilename,140)==NULL)
  343.           picFilename=NULL;
  344.         }
  345.         while(picFilename!=NULL && picFilename[0]==0x0A);
  346.  
  347.         if(picFilename!=NULL)
  348.            picFilename[strlen(picFilename)-1]=NULL;
  349.      }
  350.      else  /*or the command line*/
  351.         picFilename=filenames[c+1];
  352.       }
  353.  
  354.      /*We're finished with this run of pictures*/
  355.       if(playList)         /*Close playlist, if open*/
  356.      Close(pL);
  357.       pL=NULL;
  358.  
  359.       if(!loop && !printPics) /*If the loop flag wasn't given, exit*/
  360.      ExitFlag=TRUE;
  361.    }
  362.  
  363.       /*Time to exit, so close stuff*/
  364.  
  365.    cleanup();
  366.    exit(0); /*And exit*/
  367. }
  368.  
  369. LONG ilbmprops[] = { ID_ILBM,ID_CMAP,ID_ILBM,ID_BMHD,ID_ILBM,ID_CAMG };
  370.  
  371. /*Read in an ILBM file and display it*/
  372. void ReadAndDisplay(char *filename,struct IFFHandle *iff)
  373. {
  374.    int error;
  375.    UBYTE *bodyBuffer;    /*Pointer to buffer holding 'BODY' chunk info*/
  376.    ULONG ViewModes;    /*Holds the viewmodes flags*/
  377.    UWORD c;
  378.    ButtonTypes button;
  379.  
  380.       /*Structures required for IFF parsing*/
  381.    struct StoredProperty *bmhd,*cmap,*camg;
  382.    struct ContextNode *bodyContext;
  383.  
  384.       /*IntuiMessage...*/
  385.    struct IntuiMessage *mesg;
  386.  
  387.       /*Indentify chunks that should be stored during parse*/
  388.       /*(in this case, CMAP, BMHD, and CAMG)*/
  389.    error=PropChunks(iff,ilbmprops,3);
  390.  
  391.       /*If there was an error, print a message and return*/
  392.    if(error!=0)
  393.    {
  394.       printError(errorMsg,filename);
  395.       ExitFlag=TRUE;
  396.       return;
  397.    }
  398.  
  399.       /*Tell iffparse to stop at a 'BODY' chunk*/
  400.    error=StopChunk(iff,ID_ILBM,ID_BODY);
  401.  
  402.       /*Error handling yet again*/
  403.    if(error!=0 && error!=-1)
  404.    {
  405.       printError(errorMsg,filename);
  406.       ExitFlag=TRUE;
  407.       return;
  408.    }
  409.  
  410.       /*Do the actual parsing*/
  411.    error=ParseIFF(iff,IFFPARSE_SCAN);
  412.  
  413.       /*Check for errors yet again*/
  414.    if(error!=0 && error !=-1)
  415.    {
  416.       printError(errorMsg,filename);
  417.       ExitFlag=TRUE;
  418.       return;
  419.    }
  420.  
  421.       /*Get the BMHD, CMAP, and CAMG chunks that were read from the file*/
  422.    bmhd = FindProp(iff,ID_ILBM,ID_BMHD);
  423.    cmap = FindProp(iff,ID_ILBM,ID_CMAP);
  424.    camg = FindProp(iff,ID_ILBM,ID_CAMG);
  425.  
  426.       /*Get the descriptor for the BODY chunk*/
  427.    bodyContext=CurrentChunk(iff);
  428.  
  429.       /*If there wasn't a BMHD, CMAP, or BODY chunk, abort*/
  430.    if(!bmhd | !cmap | !bodyContext)
  431.    {
  432.       printError(filename,"is corrupted or is not in Amiga ILBM format");
  433.       ExitFlag=TRUE;
  434.       return;
  435.    }
  436.  
  437.       /*Prepare to determine screen modes*/
  438.    newScreen.ViewModes=NULL;
  439.  
  440.       /*If there was a CAMG chunk, use it to get the viewmodes*/
  441.    if(camg!=NULL)
  442.    {
  443.       ViewModes=( ((CAMG *)(camg->sp_Data))->viewmodes );
  444.  
  445.       if(ViewModes & HAM)
  446.      newScreen.ViewModes|=HAM;
  447.  
  448.       if(ViewModes & EXTRA_HALFBRITE)
  449.      newScreen.ViewModes|=EXTRA_HALFBRITE;
  450.  
  451.       if(ViewModes & LACE)
  452.      newScreen.ViewModes|=LACE;
  453.  
  454.       if(ViewModes & HIRES)
  455.      newScreen.ViewModes|=HIRES;
  456.    }
  457.  
  458.       /*Interpret the BMHD chunk*/
  459.    getBMHD((struct BitMapHeader *)bmhd->sp_Data);
  460.  
  461.       /*Open a screen, defined by the BMHD and CAMG chunks*/
  462.  
  463.    screen=OpenScreenTagList(&newScreen,TagList);
  464.  
  465.       /*If the screen couldn't be opened, abort*/
  466.    if(screen==NULL)
  467.    {
  468.       printError("Cannot open screen!","");
  469.       ExitFlag=TRUE;
  470.       return;
  471.    }
  472.  
  473.       /*Set the window dimensions from the screen dimensions*/
  474.    newWindow.Screen=screen;
  475.    newWindow.Width=newScreen.Width;
  476.    newWindow.Height=newScreen.Height;
  477.  
  478.       /*Open the window*/
  479.    window=OpenWindow(&newWindow);
  480.  
  481.       /*Abort if the window couldn't be opened*/
  482.    if(window==NULL)
  483.    {
  484.       printError("Cannot open window!","");
  485.  
  486.       ExitFlag=TRUE;
  487.       return;
  488.    }
  489.  
  490.       /*Allocate enough memory to hold the BODY data*/
  491.       /*We want to find out what the biggest block of memory is.  If*/
  492.       /*we have enough memory to hold the entire body chunk, we'll load*/
  493.       /*the whole thing into memory and decompress from there.    If not,*/
  494.       /*we'll load as much as we can and refill the buffer when it*/
  495.       /*empties*/
  496.       /*We do a Forbid() to keep anyone from allocating memory between*/
  497.       /*the AvailMem() and AllocMem() calls.  This way, we're guaranteed*/
  498.       /*that a memory block of the size given by Availmem() can be */
  499.       /*allocated.  For this reason, we don't need to check the result*/
  500.       /*of the AllocMem();  we're guaranteed that it worked*/
  501.    Forbid();
  502.    bufSize=AvailMem(MEMF_LARGEST);
  503.    bufSize=MIN(bufSize,bodyContext->cn_Size+1);
  504.    if(bufSize==0)    /*It'll never happen, but just in case...*/
  505.    {
  506.       Permit();
  507.       ExitFlag=TRUE;
  508.       return;
  509.    }
  510.  
  511.    bodyBuffer=AllocMem(bufSize,0L);
  512.    Permit();
  513.  
  514.    availBytes = bufSize;
  515.    curPos = bufSize;
  516.  
  517.       /*Blank out the pointer*/
  518.    SetPointer(window,fakePointerData,1,16,0,0);
  519.  
  520.       /*Set the screen colors to those provided in the CMAP chunk*/
  521.    setScreenColors(screen,cmap->sp_Data,newScreen.Depth);
  522.  
  523.       /*Put the BODY chunk information into the bitmap*/
  524.    ReadBodyIntoBitmap(&(screen->BitMap),bodyBuffer,bufSize);
  525.  
  526.       /*Activate the window, and flush any IDCMP message*/
  527.    ActivateWindow(window);
  528.    while((mesg=(struct IntuiMessage *)GetMsg(window->UserPort))!=NULL)
  529.       ReplyMsg((struct Message *)mesg);
  530.  
  531.       /*Bring the screen to the front*/
  532.    ScreenToFront(screen);
  533.  
  534.  
  535.       /*If the user used the 'print' flag on the command line, print*/
  536.       /*the picture*/
  537.    if(printPics)
  538.       dumpRastPort(&(screen->RastPort),&(screen->ViewPort));
  539.  
  540.    print=TRUE;
  541.  
  542.       /*Close the previous window and screen*/
  543.    if(prevWindow!=NULL)
  544.       CloseWindow(prevWindow);
  545.    if(prevScreen!=NULL)
  546.       CloseScreen(prevScreen);
  547.  
  548.       /*Free the buffer that holds the BODY chunk information*/
  549.    FreeMem(bodyBuffer,bufSize);
  550.  
  551.       /*Store the current window & screen structures, so they can be*/
  552.       /*closed later*/
  553.    prevWindow=window;
  554.    prevScreen=screen;
  555.  
  556.    screen=NULL;
  557.    window=NULL;
  558.  
  559.    rexxAbort=none;
  560.    if(ticks==0) /*If ticks==0, this means that no delay was specified*/
  561.    {          /*by the user.  So just wait for him to click a button*/
  562.  
  563.       while((button=checkButton())==none && rexxAbort==none)
  564.       {
  565.      Wait((1<<prevWindow->UserPort->mp_SigBit) | arexxSigBit);
  566.      dispRexxPort();
  567.       }
  568.  
  569.       if(button==menu || rexxAbort==menu)
  570.      ExitFlag=TRUE;
  571.    }
  572.    else     /*Otherwise, wait for the specified amount of time*/
  573.    {
  574.       for(c=0;c<ticks;c+=25)
  575.       {
  576.      ticksRemaining=ticks-c;
  577.      Delay((ticksRemaining < 25) ? ticksRemaining : 25);
  578.  
  579.      dispRexxPort();         /*Check ARexx port*/
  580.  
  581.      button=checkButton();   /*After each 25 ticks, check to see if*/
  582.      if(button==menu || rexxAbort==menu)    /*the user wants to abort*/
  583.      {
  584.         ExitFlag=TRUE;
  585.         return;
  586.      }
  587.      if(button==select || rexxAbort==select)  /*Or advance prematurely*/
  588.         return;
  589.       }
  590.    }
  591. }
  592.  
  593. /*Convert the CMAP information into a color map, then set the colors*/
  594. /*of the screen according to that colormap*/
  595. void setScreenColors(struct Screen *scr, UBYTE *colorMap, UBYTE depth)
  596. {
  597.    int i,numColors;
  598.  
  599.    numColors=1<<depth;    /*Get the number of colors (generally 2^depth)*/
  600.  
  601.    if(newScreen.ViewModes & HAM)    /*There are, of course, 2 exceptions*/
  602.       numColors=16;
  603.    if(newScreen.ViewModes & EXTRA_HALFBRITE)
  604.       numColors=32;
  605.  
  606.       /*For each color, convert it from CMAP to Amiga form and*/
  607.       /*install it*/
  608.    for(i=0;i<numColors;i++)
  609.       SetRGB4(&(scr->ViewPort),i,colorMap[i*3]>>4,colorMap[i*3+1]>>4,
  610.            colorMap[i*3+2]>>4);
  611.  
  612.    return;
  613. }
  614.  
  615. /*Transform the information in the BODY chunk into a displayable picture,*/
  616. /*decompressing if necessary*/
  617. void ReadBodyIntoBitmap(struct BitMap *bm,UBYTE *buffer,ULONG bufferSize)
  618. {
  619.    register int d;
  620.    register int repl;
  621.    register UBYTE toRepl;
  622.    BYTE *src,*dest;
  623.    register int depth,rows;
  624.    register int bytesInRow;
  625.    register int reqDepth;
  626.  
  627.    src=buffer;
  628.    bytesInRow=((newScreen.Width+15)/8) & (~1);
  629.  
  630.       /*If there's a mask plane, we have an extra bitplane to read in*/
  631.    reqDepth=(masking) ? newScreen.Depth+1 : newScreen.Depth;
  632.  
  633.       /*Loop once for each row*/
  634.    for(rows=0;rows<newScreen.Height;rows++)
  635.      /*Loop once for each in plane in each row*/
  636.       for(depth=0;depth<reqDepth;depth++)
  637.       {
  638.         /*If we've read all the valid bitplane information and are*/
  639.         /*now about to get to the mask, point the destination*/
  640.         /*buffer pointer at an unused buffer (a bit bucket)*/
  641.      if(masking && depth==reqDepth-1)
  642.         dest=buf;
  643.      else
  644.         dest=&(bm->Planes[depth][bytesInRow*rows]);
  645.  
  646.      if(Compression==1)   /*If the file is compressed, decompress*/
  647.      {
  648.            /*Loop for each byte in the destination row*/
  649.         for(d=0;d<bytesInRow;)
  650.         {
  651.            /*If the compression buffer is full, read more data from*/
  652.            /*the disk*/
  653.            if(curPos >= availBytes)
  654.            {
  655.           ReadChunkBytes(iff,src,bufSize);
  656.           curPos=0;
  657.            }
  658.  
  659.           /*Note: n = the value in the current byte.  [n]+1 = the*/
  660.           /*value in the next byte*/
  661.  
  662.           /*If 128>n>=0, copy the next n+1 bytes literally*/
  663.  
  664.            if(src[curPos]>=0 && src[curPos]<128)
  665.            {
  666.           repl=src[curPos++]+1;
  667.  
  668.           /*If there aren't n+1 bytes in the buffer, transfer what */
  669.           /*is there to the bitmap, then refill the buffer*/
  670.           if(curPos+repl > bufSize)
  671.           {
  672.              repl-=(bufSize-curPos);
  673.  
  674.              CopyMem(&src[curPos],&dest[d],bufSize-curPos);
  675.              d+=(bufSize-curPos);
  676.              ReadChunkBytes(iff,src,bufSize);
  677.              curPos=0;
  678.           }
  679.           CopyMem(&src[curPos],&dest[d],repl);
  680.           curPos+=repl;
  681.           d+=repl;
  682.            }
  683.            else if(src[curPos] < 0)   /*If n < 0, the next -n+1 bytes*/
  684.            {              /*are to be set equal to [n]+1*/
  685.           repl=-src[curPos++]+1+d;
  686.           if(curPos >= availBytes)
  687.           {
  688.              ReadChunkBytes(iff,src,bufSize);
  689.              curPos=0;
  690.           }
  691.           toRepl=src[curPos++];
  692.           for(;d<repl;d++)
  693.              dest[d]=toRepl;
  694.            }
  695.            else          /*Otherwise (n==128), just skip that byte*/
  696.           curPos++;
  697.         }
  698.      }
  699.      else  /*Otherwise, the source buffer isn't compressed, so just*/
  700.      {     /*copy the bytes*/
  701.  
  702.         repl=bytesInRow;
  703.         d=0;
  704.  
  705.         /*Again, if the buffer doesn't contain all the data we need,*/
  706.         /*transfer what's there and then refill the buffer*/
  707.         if(curPos+repl > bufSize)
  708.         {
  709.            repl-=(bufSize-curPos);
  710.            CopyMem(&src[curPos],&dest[d],bufSize-curPos);
  711.            d+=(bufSize-curPos);
  712.            ReadChunkBytes(iff,src,bufSize);
  713.            curPos=0;
  714.         }
  715.         CopyMem(&src[curPos],&dest[d],repl);
  716.  
  717.         curPos+=repl;
  718.      }
  719.       }
  720.    return;
  721. }
  722.  
  723. /*Make a newScreen structure using the BMHD chunk*/
  724. void getBMHD(struct BitMapHeader *bmhd)
  725. {
  726.    if(bmhd->w > 400 && bmhd->w <=704)  /*Define the screen as hires*/
  727.       newScreen.ViewModes|=HIRES;      /*If wider than 400 pixels*/
  728.  
  729.    if(bmhd->h > 300 && bmhd->h <=512)  /*Define the screen as interlaced*/
  730.       newScreen.ViewModes|=LACE;       /*if the height is > 300 pixels*/
  731.  
  732.    newScreen.Width=bmhd->w;           /*Store the rest of the values*/
  733.    newScreen.Height=bmhd->h;
  734.  
  735.    newScreen.LeftEdge=bmhd->x;
  736.    newScreen.TopEdge=bmhd->y;
  737.    newScreen.Depth=bmhd->nplanes;
  738.  
  739.    masking=(bmhd->Masking == 1);
  740.  
  741.    Compression=bmhd->Compression;   /*Compression flag.  Store for*/
  742.                     /*later use*/
  743.    return;
  744. }
  745.  
  746. /*Data structures for ReadArgs()*/
  747. struct RDArgs ra=
  748. {
  749.    {NULL,0,0},
  750.    NULL,
  751.    buf,
  752.    512,
  753.    "FILE/A/M,SECS=SECONDS/K/N,TICKS/K/N,LOOP/S,FROM/K,PRINT/S"
  754. };
  755.  
  756. /*Parse the argument list, using ReadArgs()*/
  757. void ParseArgs(ULONG *args)
  758. {
  759.    ReadArgs("FILE/A/M,SECS=SECONDS/K/N,TICKS/K/N,LOOP/S,FROM/K,PRINT/S",
  760.         args,&ra);
  761.    return;
  762. }
  763.  
  764. /*Check to see which mouse buttons have been pressed*/
  765. ButtonTypes checkButton(void)
  766. {
  767.    struct IntuiMessage *mesg;
  768.    ButtonTypes Button=none;
  769.    static justActivated=FALSE;
  770.  
  771.       /*This function disregards a select (left) mouse button click*/
  772.       /*if the window's just been activated.  This is so that a user*/
  773.       /*can click on another window, then make this one active again,*/
  774.       /*without advancing to the next picture*/
  775.  
  776.      /*While there are messages to be read...*/
  777.    while((mesg=(struct IntuiMessage *)GetMsg(prevWindow->UserPort))!=NULL)
  778.    {
  779.      /*Interpret them*/
  780.       switch(mesg->Class)
  781.       {
  782.      case ACTIVEWINDOW:   /*Set the appropriate flag if the window*/
  783.         justActivated=TRUE;  /*was just activated*/
  784.         break;
  785.      case VANILLAKEY:
  786.         switch(mesg->Code)
  787.         {
  788.            case 16:       /*CTRL-P - Print (if this picture hasn't been*/
  789.           if(print)   /*printed;  this is designed in case the user*/
  790.           {          /*holds down CTRL-P: we don't want 10-20     */
  791.                   /*print requests to get queued up        */
  792.              dumpRastPort(&(prevScreen->RastPort),
  793.                   &(prevScreen->ViewPort));
  794.              print=FALSE;
  795.           }
  796.           break;
  797.            case 4:          /*CTRL-D - Abort everything*/
  798.           Button=menu;
  799.           break;
  800.            case 3:
  801.           Button=select; /*CTRL-C - Advance to next picture*/
  802.           break;
  803.         }
  804.         break;
  805.      case MOUSEBUTTONS:   /*Interpret a button click*/
  806.         if(mesg->Code==SELECTDOWN) /*If the left button was pushed,*/
  807.            if(justActivated)       /*and not so as to activate the*/
  808.            {               /*window, advance to the next*/
  809.           justActivated=FALSE;         /*screen*/
  810.           break;
  811.            }
  812.            else
  813.           Button=select;
  814.         else if(mesg->Code == MENUDOWN)  /*If the right button was*/
  815.            Button=menu;             /*pushed, we'll want to*/
  816.         break;                 /*abort*/
  817.       }
  818.       ReplyMsg((struct Message *)mesg);      /*Reply to the message*/
  819.    }
  820.    return(Button);                           /*Return the results*/
  821. }
  822.  
  823. /*This prints an error to the console, if we were run from the CLI*/
  824. /*This is done instead of using Output() so as to get around any redirection*/
  825. /*that may be in place (just like the standard C stderr)*/
  826. /*If we can't open a StdErr or 2View was run from Workbench, a requester */
  827. /*is put up*/
  828. void printError(char *error1,char *error2)
  829. {
  830.    if(StdErr==NULL)
  831.       StdErr=Open("CONSOLE:",MODE_OLDFILE);
  832.  
  833.    /* If we can't open StdErr, or Output()==NULL (meaning we're running */
  834.    /* Workbench), put up a requester */
  835.    if(StdErr==NULL || Output()==NULL)
  836.    {
  837.       if(error2==NULL || error2[0]==NULL)
  838.      EasyRequest(NULL,&erError1Line,NULL,error1,"Exiting...");
  839.       else
  840.      EasyRequest(NULL,&erError2Line,NULL,error1,error2,"Exiting...");
  841.    }
  842.    else
  843.    {
  844.       FPuts(StdErr,error1);
  845.       FPuts(StdErr,error2);
  846.       FPuts(StdErr,"\nExiting\n");
  847.    }
  848.    return;
  849. }
  850.  
  851. /*Free allocated resources in anticipation of quitting*/
  852. void cleanup()
  853. {
  854.       /*Close the ARexx port*/
  855.    dnRexxPort();
  856.  
  857.       /*Close the standard-error file if opened*/
  858.    if(StdErr!=NULL)
  859.       Close(StdErr);
  860.  
  861.       /*Close a previous screen and window, if open*/
  862.    if(prevWindow!=NULL)
  863.       CloseWindow(prevWindow);
  864.    if(prevScreen!=NULL)
  865.       CloseScreen(prevScreen);
  866.  
  867.       /*Close a current screen and window, if open*/
  868.    if(window!=NULL)
  869.       CloseWindow(window);
  870.    if(screen!=NULL)
  871.       CloseScreen(screen);
  872.  
  873.    if(iff!=NULL)
  874.       FreeIFF(iff);
  875.  
  876.    if(pL!=NULL)
  877.       Close(pL);
  878.  
  879.    if(IFFParseBase!=NULL)
  880.       CloseLibrary(IFFParseBase);
  881.  
  882.    if(IntuitionBase!=NULL)
  883.       CloseLibrary(IntuitionBase);
  884.  
  885.    if(GfxBase!=NULL)
  886.       CloseLibrary(GfxBase);
  887. }
  888.  
  889. /*Print the specified RastPort (whose ViewPort is pointed to by vp*/
  890. BOOL dumpRastPort(struct RastPort *rp,struct ViewPort *vp)
  891. {
  892.    struct IODRPReq *printerMsg;
  893.    struct MsgPort *printerPort;
  894.    static BOOL ableToPrint=TRUE;
  895.  
  896.    if(ableToPrint)
  897.    {
  898.       ableToPrint=FALSE;
  899.       printerPort=CreatePort("2View.print.port",0);
  900.       if(printerPort!=NULL)
  901.       {
  902.      printerMsg=(struct IORequest *)CreateExtIO(printerPort,
  903.                        (long)sizeof(struct IODRPReq));
  904.  
  905.      if(printerMsg != NULL)
  906.      {
  907.         /*Open the printer device*/
  908.         if(OpenDevice("printer.device",0,printerMsg,0)==0)
  909.         {
  910.            /*Set up the IODRPReq structure*/
  911.            printerMsg->io_Command=PRD_DUMPRPORT;
  912.            printerMsg->io_RastPort=rp;
  913.            printerMsg->io_ColorMap=vp->ColorMap;
  914.            printerMsg->io_Modes=vp->Modes;
  915.            printerMsg->io_SrcX=0;
  916.            printerMsg->io_SrcY=0;
  917.            printerMsg->io_SrcWidth=vp->DWidth;
  918.            printerMsg->io_SrcHeight=vp->DHeight;
  919.            printerMsg->io_Special=SPECIAL_ASPECT|SPECIAL_FULLROWS|
  920.                       SPECIAL_FULLCOLS;
  921.  
  922.            /*Do it*/
  923.            if(DoIO(printerMsg)==0)
  924.           ableToPrint=TRUE;
  925.  
  926.            CloseDevice(printerMsg);
  927.         }
  928.         DeleteExtIO(printerMsg);
  929.      }
  930.      DeletePort(printerPort);
  931.       }
  932.       return(ableToPrint);
  933.    }
  934. }
  935.  
  936. /*End of 2View.c*/
  937.  
  938.