home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 302.lha / ILBMLib / ILBMLib.doc.pp / ILBMLib.doc
Text File  |  1980-12-03  |  74KB  |  1,408 lines

  1.                       THE IFF ILBM LIBRARY MANUAL
  2.                      ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
  3.                        by Jeff Glatt, dissidents
  4.                        6 Sycamore Drive East
  5.                        New Hartford, NY 13413
  6.  
  7.  
  8.    What's an ilbm.library?
  9.   ------------------------
  10.  
  11.    The IFF ilbm.library is a disk-based runtime library. This means that any
  12. application, upon being run, can open and call routines in this library just
  13. like a program might utilize routines in the Intuition or Graphics libraries. 
  14. The primary purpose of the ilbm.library is to provide functions to read and
  15. write ILBM picture files, but there is additional support to read non-ILBM
  16. IFF and ANIM forms. The functions are based upon the original Electronic Arts
  17. code (i.e. OpenRIFF, GetBODY, etc.) as well as a few Commodore enhancements
  18. (i.e. Scheppner's getBitMap, handleCAMG, etc.). There have been a few changes
  19. to the internal logic of some routines, but generally, they perform the same
  20. functions as the original code. A few more significant changes were made to
  21. certain routines in order to accomodate ANIM and non-ILBM files, and also to
  22. streamline the parsing of LISTS, CATS, and PROPS. Furthermore, some higher
  23. level routines have been added to make it extremely easy to load and write
  24. IFF pictures via just two library routines. Finally, the biggest change
  25. between the original code and this library is that the code has been rewrit-
  26. ten in the tightest possible 68000 assembly. The size of the library is < 7000
  27. bytes. A program that uses this library can be smaller and faster than if
  28. the program used the original EA code.¹ Because the library uses no global
  29. data and is therefore re-entrant, many applications can use the library
  30. simultaneously. Finally, access to the lowest level functions is provided.
  31. The IFF routines, OpenRIFF, GetChunkHdr, IFFReadBytes, IFFWriteBytes,
  32. etc. are all included. These routines are equivilent to the original EA code
  33. except that the lib code is smaller and faster. Because of this, it is pos-
  34. sible to use this library for any non-ILBM reader and writer with a result-
  35. ing improvement in the application's size and speed. In the future, if the
  36. Amiga becomes utilized in any non-graphics applications, additional high
  37. level routines may be added to handle non-ILBM forms (i.e. 8SVX, SMUS, etc).
  38. Suggestions/comments may be sent to the above address.
  39.    Although a library of this sort has long been promised from both CATS and
  40. certain 3rd party developers, as usual nothing has been delivered by those
  41. folks.
  42.    Electronic Arts is a company that deserves credit for helping make life
  43. easier for the Amiga programmer and end user. The company has promoted the
  44. Amiga since the computer's introduction more so than CBM (although very few
  45. organizations have promoted the Amiga less than its manufacturer). By esta-
  46. blishing IFF standards on behalf of CBM and releasing source code for reading
  47. and writing IFF files, Electronic Arts has helped make the Amiga the success
  48. that it is in the video/graphics market. Indeed, these efforts are probably
  49. the reason why CBM is still in the business of making computers. Unfortunately,
  50. the people who wrote the documents describing the IFF file formats have an
  51. appalling lack of respect for the English language. No effort was made to be
  52. concise and clear. Sentences seem to have been constructed by heaving several,
  53. long phrases together, and subsequently inserting a verb or two. Then, if the
  54. text didn't sound "technical" or "serious" enough, adjectives were invented,
  55. and brazenly thrust into any sentence of less than 40 words. This mish-mash
  56. of technical newspeak is typical of computer programmers who never learned
  57. to write in any language other than Pascal. In fact, if the trend toward
  58. inventing technical jargon continues, we may see the day when technical
  59. literature will have the words BEGIN and END surrounding each "standard
  60. written language module" (i.e. paragraph).  Many misguided programmers
  61. (and others engaged in scientific disciplines) are also taught that any use
  62. of the first person (i.e. I or we) is too conversational, and therefore
  63. unprofessional. Apparently, the function of technical literature is not to
  64. converse, but to bore. Because the original IFF documents exhibit all of
  65. these flaws, I will attempt to paraphrase those documents. I do not guarantee
  66. that I will have correctly ascertained the intentions of the lifeform that
  67. wrote those documents, so you should consult the original text.
  68.  
  69.   BEGIN
  70.   IFF stands for "InterChange Format Files". This is some of that technical
  71. jargon that I was alluding to earlier. Basically, an IFF file is a set of
  72. data that is in a form that many, unrelated programs can read. An IFF file
  73. should not have anything in it that was intended specifically for just one,
  74. particular program. If a program must save some "personal" data in an IFF
  75. file, it must be saved in a manner which allows another program to "skip
  76. over" this data. There are several different types of IFF files. ILBM files
  77. store picture data. SMUS files store musical scores. 8SVX store sampled
  78. sounds. Each of these files must start with an ID which indicates that it is
  79. indeed an IFF file, followed by an ID that indicates which type of file. So
  80. what is an ID? An ID is four, printable ascii characters. If you use the CLI
  81. Type command (opt h) to print out an IFF file to the CLI window, you will
  82. notice that every so often you will see 4 letters in a row. These 4 letters
  83. are an ID. Every IFF file must start with one of the following 3 IDs.
  84.  
  85.   'FORM'  'LIST'  'CAT '
  86.  
  87.  If the first 4 chars (bytes) in a file are not one of these, then it is not
  88. an IFF file. These IDs are referred to as group IDs in EA literature.
  89.    END
  90.  
  91.   After this group ID, there is a ULONG that indicates how many bytes are
  92. in the entire file. This count does not include the 4 byte group ID, nor this
  93. LONG. This LONG is useful if you wish to load the rest of the file into mem-
  94. ory to examine it. After this LONG, there is an ID that indicates which type
  95. of IFF file this is. As mentioned earlier, "ILBM", "SMUS", and "8SVX" are 3
  96. types of IFF files. There are many more, and programmers are always inventing
  97. new types for lack of better things to do. What you find after the type ID
  98. depends on which type it is (i.e. From here on, an ILBM will be different
  99. than an 8SVX). Here is the beginning of a typical ILBM file.
  100.  
  101.   'FORM'  <- OK. This really is an IFF file.
  102.   13000   <- There are 13000 more bytes after this ULONG
  103.   'ILBM'  <- It is an ILBM (picture) file
  104.  
  105.   One thing that all IFF files do have in common after the group ID, byte
  106. count, and type ID, is that data is organized into chunks. OK, more jargon.
  107. What's a chunk? A chunk consists of an ID, a ULONG that tells how many bytes
  108. of data are in the chunk, and then all those data bytes. For example, here
  109. is a CMAP chunk (which would be found in an ILBM file).
  110.  
  111.   'CMAP'      <- This is the 4 byte chunk ID
  112.   6           <- This tells how many data bytes are in the chunk (chunkSize)
  113.   0,0,0,1,1,4 <- Here are the 6 data bytes
  114.  
  115.   Notice that the chunk size doesn't include the 4 byte ID or the ULONG for
  116. the chunk Size.
  117.   So, all IFF files are made up of several chunks. There are a few other
  118. details to note. A chunk cannot have an odd number of data bytes (such as 3).
  119. If necessary, an extra zero byte must be written to make an even number of
  120. data bytes. The chunk Size doesn't include this extra byte. So for example,
  121. if you want to write 3 bytes in a CMAP chunk, it would look like this:
  122.  
  123.   'CMAP'
  124.   3        <- Note that chunk Size is 3
  125.   0,1,33,0 <- Note that there is an extra zero byte
  126.  
  127.   In the preceding example, the group ID was 'FORM'. There are other group
  128. IDs as well. A 'CAT ' is a collection of many different FORMs all stuck
  129. together consecutively in 1 IFF file. For example, if you had an animation
  130. with 6 sound effects, you would want to save the animation frames in an ANIM
  131. FORM, and you would want to save the sound effects in several 8SVX FORMs
  132. (one per sound effect). Note: a better way to store sound samples would be
  133. the SAMP format. See Fish Disc #203. You could save the animation and sound
  134. in 7 separate files. The ANIM file would start this way:
  135.  
  136.   FORM
  137.   120000  <- Whatever the size happens to be (this is expressed in 32 bits)
  138.   ANIM
  139.  
  140.   Each 8SVX file would start this way:
  141.  
  142.   FORM
  143.   8000 <- whatever size
  144.   8SVX
  145.  
  146.   If the user wanted to copy the data to another disc, he would have to copy
  147. 7 files. On the other hand, you could save all the data in one CAT file.
  148.  
  149.   CAT
  150.   4+120008+8008+2028+...  <- The total size of the ANIM and the 6 8SVX files
  151.   '    '           <- Type of CAT. 4 spaces for the type ID means "a grab bag"
  152.                       of IFF FORMs are going to be inside of this CAT
  153.   FORM
  154.   120000
  155.   ANIM
  156.   ...all the chunks in the ANIM file placed here (note: ANIMs have imbedded
  157.      ILBM FORMs)
  158.  
  159.   FORM
  160.   8000
  161.   8SVX
  162.   ...all the chunks in the first sound effect here
  163.  
  164.   FORM
  165.   2020
  166.   8SVX
  167.   ...all the chunks in the second sound effect here
  168.  
  169.   ...etc. for the other 4 sound effects
  170.  
  171.   To further complicate matters, there are LISTs. LISTs are a lot like CATs
  172. except that there is an additional group ID associated with LISTs. That ID
  173. is a PROP. LISTs can have imbedded PROPS just like an ILBM can have an im-
  174. bedded CMAP chunk. A PROP header looks very much like a FORM header in that
  175. you must follow it with a type ID. For example, here is an ILBM PROP with
  176. a CMAP in it.
  177.  
  178.   PROP  <- Here's a PROP
  179.   4+14  <- Here's how many bytes follow in the PROP
  180.   ILBM  <- It's an ILBM PROP
  181.   CMAP      <- Here's a CMAP chunk inside this ILBM PROP
  182.   6         <- There are 6 bytes following in this CMAP chunk
  183.   0,0,0,1,1,4 
  184.  
  185.     LISTs are meant to encompass similiar FORMs (i.e. several 8SVX
  186. files stuck together). Often, when you have similiar FORMs stuck together,
  187. some of the chunks in the individual FORMs are the same. For example,
  188. assume that we have 2 8SVX sound effects. 8SVX FORMs can have a NAME chunk
  189. which contains the ascii string that is the name of the sound effect. Also
  190. assume that both sounds are called "car crash". Wouldn't it be nice if we
  191. didn't have to have the same NAME chunk in each 8SVX FORM like so:
  192.  
  193.   CAT             <- We put the 2 files into 1 CAT
  194.   4+1004+504
  195.   8SVX            <- It's an CAT of several 8SVX FORMs
  196.  
  197.   FORM            <- here's the start of the first sound effect file
  198.   1000
  199.   8SVX 
  200.  
  201.   ...some chunks
  202.  
  203.   NAME            <- here's the name chunk for the 1st sound effect
  204.   9
  205.   'car crash',0
  206.  
  207.   ...more chunks
  208.  
  209.   FORM            <- here's the start of the second sound effect file
  210.   500
  211.   8SVX
  212.  
  213.   ...some chunks
  214.  
  215.   NAME            <- here's the name chunk for the 2nd sound effect. Look
  216.   9                  familiar?
  217.   'car crash',0
  218.  
  219.   ...more chunks
  220.  
  221.   With a LIST, we can have PROPs. A PROP is group ID that allows us to place
  222. chunks that pertain to all the FORMs in the LIST. So, we can rip out the
  223. NAME chunks inside both 8SVX FORMs and replace it with one NAME chunk inside
  224. of a PROP.
  225.  
  226.   LIST            <- Notice that we use a LIST instead of a CAT
  227.   4+30+990+490+...
  228.   8SVX
  229.  
  230.   PROP            <- Here's where we put chunks intended for ALL the
  231.   22                 subsequent FORMS; inside a PROP.
  232.   8SVX            <- type of PROP
  233.   NAME            <- here's the name chunk inside of the PROP
  234.   9
  235.   'car crash',0
  236.  
  237.  
  238.   FORM            <- here's the start of the first sound effect file
  239.   982             <- size is 18 bytes less because no NAME chunk here
  240.   8SVX 
  241.  
  242.   ...some chunks, but no NAME chunk
  243.  
  244.   FORM            <- here's the start of the second sound effect file
  245.   482
  246.   8SVX 
  247.  
  248.   ...some chunks, but no NAME for this guy either
  249.  
  250.   Notice that the PROP group ID is followed by a type ID (in this case 8SVX).
  251. This means that the PROP only affects any 8SVX FORMs. If you were to sneak
  252. in an SMUS FORM at the end, the NAME chunk would not apply to it. Also, if
  253. you included a NAME chunk in one of the 8SVX FORMs, it would override the
  254. PROP. For example, assume that you have a LIST containing 10 8SVX FORMs. All
  255. but 1 of them is named "CBM needs an advertising budget". You can store a
  256. NAME chunk in a PROP 8SVX for "CBM needs an advertising budget". Then, in
  257. the one 8SVX FORM whose name is not "CBM needs an advertising budget", you
  258. can include a NAME chunk to override the PROP.
  259.   It should be noted that you can take several LISTs and squash them toge-
  260. ther inside of a CAT or another LIST.² Personally, I have never seen a data
  261. file with this level of nesting, and doubt that it would be of much use. If
  262. you need this level of complexity, forget IFF and make your own proprietary
  263. data format, then give info to anyone who wants to access your data. No doubt,
  264. somewhere, there is a moron devising a plot to inflict a CAT of LISTs upon an
  265. unsuspecting public now that VirusX has defeated his more devious aspirations.
  266.  
  267. ««««««««««««««««««««««««««««««««««««««»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
  268.  
  269.          AND NOW FOR SOMETHING COMPLETELY DIFFERENT...
  270.  
  271.   At the highest levels, the type of IFF file that this library is designed
  272. to read and write is an ILBM. An ILBM must have a BMHD and a BODY chunk. It
  273. may also have CMAP, CAMG, CRNG, and other chunks.
  274.    When reading a file, the library can look for ILBMs inside of CATs and
  275. LISTs, sidestepping all of the other non-ILBM FORMs. It rummages around inside
  276. of such a file looking for the first ILBM that it can find. You can enable
  277. the library to inform you of other types of FORMs as it finds them (in case
  278. you want to use an 8SVX sound that was stored with an ILBM), or you can
  279. say, "Don't bother coming back to me until you've found a picture in that
  280. file and displayed it in a window, or looked through the whole file and found
  281. no picture."  When it finds an ILBM, it can "skip" over any chunks whose ID
  282. it doesn't understand. On the other hand, you can tell the library, "If you
  283. come across an ID that you don't understand, give it to me and I'll take care
  284. of that chunk". (A program may store "private" data which it does not want
  285. other programs to "choke" on by storing the data in a chunk with an undo-
  286. cumented ID. This way, another program will not "recognize" what the chunk
  287. is, and can use the chunkSize to determine how many subsequent data bytes to
  288. "skip" in order to get to the next chunk.)
  289.   The library takes care of many details such as writing extra zero bytes
  290. if you tell it to write an odd-sized chunk, and error checking.
  291.   These routines ASSUME that they're the only ones reading/writing to the
  292. file.
  293.   Most library routines return an IFFP code. This is a number from 0 to -11,
  294. or in some cases, a 4 byte ascii ID. 0 usually means success. The negative
  295. numbers mean errors. See the include files for details. Your calling routines
  296. should check all returned IFFP error codes. Don't press on after an error!
  297. These routines try to have no side effects if an error, except that partial
  298. I/O is sometimes unavoidable. All of these routines may return DOS_ERROR.
  299. In that case, ask DOS for the specific error code, if desired, via the DOS
  300. library's IOErr() routine. There is an ilbm.library function that will help
  301. in presenting error msgs to a user.
  302.   The library has functions that can isolate you from the drudgery of dealing
  303. with ILBM files completely. These functions are referred to as "high level"
  304. functions. Some functions give you more control over how the image is saved
  305. or loaded. These routines require you to deal with certain structures (to
  306. be described later). These functions, called "mid-level", are more complica-
  307. ted to use than the high level ones, but still isolate you from dealing with
  308. IFF chunks, etc. If neither of these levels is suitable for your needs, then
  309. you can use the "low level" routines. You must fully understand the IFF spec
  310. in order to use these routines, but you should be able to construct any con-
  311. ceivable IFF reader/writer with them.
  312.   The high level, writing (save) routines of the library are very straight-
  313. forward, and require no special structures or programming techniques.
  314.   The high level, reading (load) routines of the library use an ILBMFrame
  315. structure. See the INCLUDE files for a description of this structure. The
  316. C include file is "ILBM_lib.h". The assembly include is "IFF.i". Basic users
  317. should read "BasicUsers".
  318.  
  319.     An ILBMFrame is a structure for reading ILBM FORMS within an IFF file.
  320.  Using high level routines, the library does the actual parsing (loading
  321.  and processing) of the IFF file. It loads the image into an open window
  322.  based upon the info that it finds in CMAP, BMHD, CAMG, etc chunks. It looks
  323.  for ILBM FORMs inside of LISTS and CATS, and also handles any ILBM PROPS.
  324.  You MUST supply a master ILBMFrame for the library's load routines.
  325.  
  326.     At the high level are two routines called SaveWindowToIFF() and
  327.  LoadIFFToWindow(). These 2 routines are for writing and reading IFF ILBM
  328.  files respectively. In the case of SaveWindowToIFF(), you pass the address
  329.  of the window whose image you wish saved, and the name of the file. This
  330.  routine will save the window image as an IFF ILBM file. It manages all the
  331.  low level library routines for writing chunks so that you need not be bo-
  332.  thered with any of the details of ILBM files. In the case of an error, it
  333.  also deletes the aborted file. Likewise, LoadIFFToWindow() handles all of
  334.  the low level reading of an ILBM file. This routine is passed the name of
  335.  the file to read, and an ILBMFrame structure.
  336.    LoadIFFToWindow will take care of initializing all of the fields of the
  337.  master ILBMFrame except for the iScreen, iWindow, and iUserFlags fields.
  338.  You must set up these fields initially in order to tell the library if you
  339.  want a window opened for the ILBM image, a visible screen bar and mouse
  340.  pointer, and a few other options. If the iWindow field in the ILBMFrame
  341.  structure is not zero (i.e. it is the address of an opened window), then
  342.  the image will be scaled to fit inside that window. What this means is that
  343.  you can load a HIRES image into an opened LORES window, and vice versa. The
  344.  library will scale the picture to "fit" the window.
  345.    If the ILBMFrame's iWindow is NULL (0), then a screen and backdrop window
  346.  will be opened for the image. The screen attributes (i.e. HAM, INTERLACE,
  347.  etc.) will be determined by the ILBM file so that the picture will be dis-
  348.  played without scaling. When this routine returns (successfully), the
  349.  addresses of the window and screen will be in the ILBMFrame's iWindow and
  350.  iScreen fields. You may then ModifyIDCMP() or whatever at this point.
  351.  
  352. ************************ SaveWindowToIFF **************************
  353.  IFFP = SaveWindowToIFF(fileName, window)
  354.   d0                       d1       a0
  355.  Saves a window's image as an IFF ILBM file. Returns an IFFP code (0 if OK,
  356.  a negative, non-zero number for an error. Z-flag set accordingly.)
  357.  This procedure calls SaveILBM(), passing in an <x, y> location of <0, 0>,
  358.  and a NULL mask. It also assumes you want to write out all the bitplanes
  359.  in the BitMap. Also, it applies byte run compression to the BODY.
  360.  If an error in saving, it deletes the partial file. The fileName should be
  361.  a complete path as might be typed at the CLI (i.e. df0:extras/myName). This
  362.  is an ascii, NULL-terminated string. window is the address of the opened
  363.  window (as returned from Intuition's OpenWindow).
  364.  
  365.  
  366. ;************************* LoadIFFToWindow ********************************
  367. ;IFFP = LoadIFFToWindow( fileName, ILBMFrame )
  368. ; d0                        d1       a1
  369. ; Loads an ILBM file into the ILBMFrame's iWindow. If iWindow is NULL, opens
  370. ; a screen/backdrop window for the image. This routine calls LoadILBM().
  371.  
  372.  If neither one of these routines serves your purpose, then you will need to
  373.  use some lower level routines instead. The library's lowest level routines
  374.  are the same routines as the original EA code, IFFr.c and IFFw.c, except
  375.  that they are smaller and faster asm modules.
  376.    At this point, I need to warn you that the arguments passed to some
  377.  routines have been changed, as well as the order of the arguments. This
  378.  was done to make the code smaller and faster in assembly by using the
  379.  movem instruction.
  380.  
  381.  Here are some routines at the mid-level. They isolate you from dealing with
  382. low level structures and loading chunks, but give you the option of install-
  383. ing custom routines to handle FORMs, PROPs, or undocumented IDs inside of
  384. FORMs. See the program, ANIMInfo, for an example of using a custom handler
  385. for FORMs.
  386.  
  387. ******************************* SaveILBM() ******************************
  388.  Writes an entire BitMap as a FORM ILBM in an IFF file. Unlike SaveWindowTo-
  389.  IFF, this routine allows you to save an uncompressed image, or with a
  390.  different xy position than 0,0 (for saving a portion of the whole image),
  391.  or saving additional chunks.
  392.  This version works for any display mode (orig code by C. Scheppner).
  393.  Normal return result is IFF_OKAY.
  394.  
  395.  The utility program IFFCheck would print the following outline of the
  396.  resulting file:
  397.  
  398.    FORM ILBM
  399.      BMHD
  400.      CAMG
  401.      CMAP
  402.      ...other chunks that you save
  403.      BODY       (compressed) or (uncompressed)
  404.  
  405. IFFP=SaveILBM(ViewModes,Compress,fileHandle,Mask,colors,BitMap,xyPoint,handler)
  406.  d0              d0       d1         d2      d3    a0     a1     a2       a3
  407.  
  408.  ViewModes  - The viewmodes of the screen as an unsigned LONG
  409.  Compress   - Compression type. 0=None 1=cmpByteRun1
  410.  fileHandle - DOS handle of an opened file
  411.  Mask       - address of a mask plane, or NULL
  412.  colors     - address of the colorTable to save as a CMAP chunk
  413.  BitMap     - address of the Bitmap structure whose planes are to be saved
  414.               as an ILBM BODY
  415.  xyPoint    - the address of two UWORD values that describe the xy position
  416.               to be saved in the BMHD chunk
  417.  handler    - the address of a routine to be called before the BODY is
  418.               written out. This allows an application to save additional
  419.               chunks such as CRNG, etc. Passed a context structure, you must
  420.               open the Context (OpenRGroup) and use PutCk, PutCkKnown, or
  421.               calls to IFFWriteBytes (after PutCkHdr, and ending with
  422.               PutCkEnd) to write out the chunk.
  423.  
  424.  If an error, you must delete the partial file yourself.
  425.  
  426. ;******************************* SaveANIM() ******************************
  427. ; Writes an ANIM file. Writes the passed BitMap as the first frame (FORM
  428. ; ILBM). Assumes user wants to write out all planes of the BitMap. Calls
  429. ; application routine for subsequent frames. Normal return result is
  430. ; IFF_OKAY. This version works for any display mode.
  431. ;
  432. ; The utility program IFFCheck would print the following outline of the
  433. ; resulting file:
  434. ;
  435. ; LIST
  436. ;   PROP ILBM
  437. ;     BMHD
  438. ;     CAMG
  439. ;     CMAP
  440. ;     ANHD     ;if passed
  441. ;   FORM ANIM
  442. ;     FORM ILBM
  443. ;       BODY       (compressed) or (uncompressed)
  444. ;     ....application routine's saved chunks (i.e. additional Frames)
  445. ;
  446. ;IFFP = SaveANIM(ViewModes,Compress,fileHandle,Mask,colors,BitMap,xyPoint,
  447. ; d0                 d0        d1        d2      d3   a0     a1     a2
  448. ;
  449. ;       FrameHandler,ANHDaddress)
  450. ;            a3         a4
  451.  
  452.   This is similiar to SaveILBM except your FrameHandler is called repeatedly
  453.   (so you can write numerous frames) while return = IFF_OKAY. Return
  454.   a 1 to stop the Handler loop successfully. Returning an IFFP error aborts
  455.   the save. Note that the file written is a LIST with a PROP ILBM. This is
  456.   so that each frame need not contain identical data. If the passed ANHD
  457.   address is not NULL, the ANHD chunk will be written in the PROP. In this
  458.   way, a simple ANIM can be written where the first frame need only contain
  459.   a BODY chunk, and subsequent frames contain a DLTA. If all successful,
  460.   final return is IFF_OKAY. If an error, you must delete the partial file.
  461.  
  462.  
  463. *************************** LoadILBM() *****************************
  464.  IFFP = LoadILBM(fileHandle, vectors, masterILBMFrame)
  465.   d0                d1         a0          a1
  466.  
  467.  Can read an ILBM file. Unlike LoadIFFToWindow, you can arrange for your
  468.  own custom routines to handle FORMs, PROPs, and certain ILBM chunks.
  469.  This entry point is useful for ANIM readers where you don't want the lib to
  470.  actually load the first frame into some window's bitmap's planes. This is
  471.  done by replacing the default lib FORMhandler routine with your own handler.
  472.  This routine will return IFF_DONE if an image has been successfully load-
  473.  ed into a window by the lib's default 'FORM' handler. All other IFFP codes
  474.  indicate that an image wasn't loaded.
  475.  
  476.  EXTREMELY modified version of ReadPict.c
  477.   by Jerry Morrison, Steve Shaw, and Steve Hayes, Electronic Arts.
  478.  
  479.  Modified by C. Scheppner and Jeff Glatt
  480.  *  The default library 'FORM' handler routine can open a window/screen of
  481.     the correct size using information placed in the ILBMFrame. The BODY
  482.     chunk data is then decompressed as it is read into this window's BitMap
  483.     structure's bit planes. If the application already has a window open,
  484.     then the lib will scale the picture to fit inside the window's bitmap's
  485.     planes (i.e. if you are trying to stuff a HIRES picture into a LORES
  486.     window). The default FORMhandler also loads in CCRT or CRNG chunks
  487.     (converts CCRT to CRNG) and CAMG chunks (i.e. for HAM) placing data in
  488.     the ILBMFrame structure.
  489.  *  Handles LISTS, CATS, and ILBM PROPS automatically.
  490.  *  Can call your custom routines when it gets to a FORM or non-ILBM PROP if
  491.     you don't want the lib to decompress any image data into bit-planes. Some
  492.     ANIM players may want to decompress BODY and DLTA chunks during playback.
  493.  *  Can call your custom routine for ANHD, DLTA, or any other ILBM chunk
  494.     that the lib's default 'FORM' handler doesn't "understand". So, for each
  495.     frame of an ANIM file, the application can control where and how the
  496.     associated chunks are loaded.
  497.  
  498.  One of the parameters to this routine is a Vector structure. This is simply
  499. a structure that holds the addresses of whatever routines you would like the
  500. lib to execute while parsing an IFF file. If the vector address is 0, then
  501. the default lib routine is used. You must initialize the structure before
  502. calling LoadILBM().
  503.   One field holds the address of a routine to handle nonILBM 'PROP's or an
  504. ILBM PROP chunk that is "unknown" to the lib. The lib doesn't know about the
  505. following ILBM chunks: ANHD, DEST, GRAB, SPRT, and DLTA. It will skip these
  506. in an ILBM PROP if you don't install a PROPhandler. Also, it will skip nonILBM
  507. PROPs if you don't have a PROPhandler. If you don't care, set this field to
  508. NULL.
  509.   Another field is for the your FORMhandler routine address. If not NULL,
  510. this routine is called instead of the lib's default 'FORM' handler. You are
  511. expected to handle all parsing of FORMs as the library finds them.
  512.   Another field is for a CHUNKhandler. This is called by lib's default 'FORM'
  513. handler when it encounters "unknown" chunks inside an ILBM (see PROPhandler).
  514. If NULL, unknown chunks are skipped. If you don't use the lib's default
  515. 'FORM' handler, then this field is free to use.
  516.   The last field is for the NonILBMhandler. This is called by lib's default
  517. 'FORM' handler when it encounters a FORM other than ILBM (i.e. 8SVX, etc).
  518. If NULL, nonILBM FORMs are skipped over in search of ILBMs. If you don't use
  519. the lib's default 'FORM' handler, then this field is free to use.
  520.   By installing routines via the vectors structure, you can "take away"
  521. certain parsing duties from the lib, while it handles LISTS, CATS, or other
  522. messy details. For example, you could install just a CHUNKhandler and set
  523. the other fields to 0. The lib would parse all LISTS, PROPS, CATS, and FORMS.
  524. It would only call your routine if it encountered an ANHD, DEST, GRAB, SPRT,
  525. or DLTA. 
  526.   Later, I will detail the parameters passed to your handlers, and what
  527. value should be returned.
  528.  
  529.    The lib handles LISTs and PROPs, diving into a LIST, if present, to read
  530.  the first FORM ILBM. (E.g. a DeluxePrint library of images is a LIST of
  531.  FORMs ILBM.)
  532.  It also dives into non-ILBM FORMs, if present, looking for a nested FORM
  533.  ILBM. (E.g. a DeluxeVideo C.S. animated object file is a FORM ANBM 
  534.  containing a FORM ILBM for each image frame.) Also, an ANIM is considered
  535.  a non-ILBM form with imbedded ILBMs. This lib's default 'FORM' handler can
  536.  automatically load the first frame of an ANIM.
  537.        LoadILBM() zeros out the ILBMFrame's iFlags (but not iUserFlags),
  538.  iBMAP, iNumColors, and iCycleCnt fields. You must initialize the
  539.  iUserFlags field prior to calling this routine. Various bits of this field
  540.  affect certain options (see Include File for details). Other bits are set
  541.  by the lib to inform you of certain facts. (i.e. the ANIMB bit would be
  542.  set by the default 'FORM' handler if an ANIM FORM was encountered in the
  543.  file.)
  544.  
  545.     Here's how LoadILBM() works. The lib initializes the aforementioned
  546. fields of the ILBMFrame, and creates a special PROP list to take care of any
  547. PROPS in the file. Next, the opened file is parsed. If an ILBM PROP (inside
  548. of a LIST) is encountered, an ILBMPropFrame is allocated, linked into the
  549. PROP list, and data in the PROP is copied to this Frame.
  550.   An ILBMPropFrame is an ILBMFrame with a few extra fields in order to allow
  551. this structure to be linked into a list. There are lib routines to facilitate
  552. allocating and freeing these structures, as well as searching the PROP list
  553. for certain types of Frames.
  554.   The lib automatically handles all ILBM PROPS. For other PROPS, it calls
  555. your PROPhandler. If no PROPhandler, it "ignores" that non-ILBM PROP. The
  556. lib only handles the following chunks inside an ILBM PROP:
  557.  
  558.   BMHD  CMAP  CAMG  CRNG  CCRT
  559.  
  560.  For other types (i.e. ANHD, DEST, etc), it will call your PROPhandler. One
  561. of the parameters passed to your PROPhandler is the PROP ID. You can use this
  562. to determine if the lib is sending you a non-ILBM PROP, or an ILBM PROP with
  563. an unknown chunk. In the case of an unknown ILBM chunk, the PROP ID will be
  564. 'ILBM' and the ID of the chunk will be passed as well. Your PROPhandler
  565. should return an IFFP code. There are other parameters passed to your PROP
  566. handler also. One parameter is the address of the PROP list. You should add
  567. any allocated ILBMPropFrames to this list (using GetPROPStruct) so that the
  568. lib can free them for you when it is done loading the file.
  569.   When the lib encounters a FORM, it checks to see if you have installed a
  570. FORMhandler. If so, control is passed to your routine. Let's assume that
  571. this is the case. Your custom FORM handler is passed the address of an
  572. initialized Context structure (to be discussed later). Although no further
  573. initialization of this structure is required on your part, you may need it
  574. for calling certain lib functions. Save the address somewhere handy.
  575. You are also passed your master ILBMFrame so that you can save data to it
  576. from within your FORMhandler. You are passed the type ID of the FORM (i.e.
  577. is it an 'ILBM'?).  Also, you have the address of the PROP list. One of the
  578. first things you'll probably want to do is search it for any PropFrame with
  579. the same type ID as the FORM. The function SearchPROP does this. You may then
  580. copy that Frame's data into the Frame that is used to hold data within your
  581. FORMhandler. CopyILBMProp() can copy an ILBMPropFrame to your master ILBMFrame.
  582. Then, as you parse chunks in your FORMhandler, the new data will overwrite 
  583. the corresponding PROP data (as it should). You also are passed the Vectors
  584. structure address in case you need it further. Your FORMhandler is expected
  585. to parse the FORM (using a few low level routines) and eventually return an
  586. IFFP code. See ANIMInfo for an example of a custom FORM routine.
  587.   Of course, if you don't have a custom FORMhandler, then the default lib
  588. routine is used. As you may have guessed, this is the big beast of the lib.
  589. Here is the logic that this routine employs.
  590.  
  591. 1). It checks to see if the FORM is an ILBM.
  592.     A). If not, it checks to see if you have a NonILBMhandler installed, and
  593.         passes control as above. If no NonILBMhandler, it parses the FORM
  594.         for imbedded ILBMs, setting the ANIMB of iUserFlags if an ANIM.
  595.         It does nothing else with NonILBM FORMs.
  596.     B). If it is an ILBM FORM, it goes to step 2.
  597. 2). It checks the PROP list for the last ILBMPropFrame, and if one is found,
  598.     copies the data to the master ILBMFrame.
  599. 3). It parses the FORM's chunks.
  600.     A). A CAMG's ViewModes is placed in the ILBMFrame
  601.     B). A CMAP is loaded into the ILBMFrame, and iNumColors determined.
  602.     C). A BMHD is loaded into the ILBMFrame with BMHDFLAG of iFlags set.
  603.     D). CCRT and CRNG chunks are loaded (up to 8) into the ILBMFrame, and
  604.         iCycleCnt is determined.
  605.     E). A BODY chunk is the last chunk the lib cares about. When it finds
  606.         one of these, it loads/decompresses the image into the ILBMFrame's
  607.         iWindow. If iWindow is NULL, then a backdrop window/screen compatible
  608.         with the image is opened. The ILBMFrame's iWindow and iScreen fields
  609.         are set to these addresses. The lib returns IFF_DONE which knocks it
  610.         out of the load process, unless the ANIMB of iUserFlags is set. In
  611.         this case, the lib assumes that there are more frames to follow, and
  612.         continues scanning to the end of the ANIM.
  613.         Eventually, IFF_DONE is returned for a successful image load.
  614.     F). If the chunk wasn't one of the proceeding types, the lib calls your
  615.         CHUNKhandler. If CHUNKhandler is NULL, it ignores this chunk. Your
  616.         CHUNKhandler must return an IFFP code. If the ANIMB bit of iUserFlags
  617.         is set, and you wish to continue scanning the remaining ANIM frames,
  618.         then return IFF_DONE for any DLTA chunks, and IFF_OKAY for all others.
  619.         Remember that the lib calls your CHUNKhandler for ANHD and DLTA, so
  620.         for each DLTA you encounter, that is the end of another ANIM Frame
  621.         (imbedded ILBM FORM). If ANIMB is not set, then you are in a plain
  622.         ILBM file. Return IFF_OKAY unless you get an error from the low level
  623.         routines. Returning IFF_DONE (or other errors) will force the load
  624.         to terminate if an ILBM. If an ANIM, returning END_MARK (or other
  625.         errors) will do the same.
  626.  
  627.  
  628.  Here are the parameters passed to your custom vectors. Return an IFFP code.
  629.  
  630.  IFFP = PROPhandler(chunkID,PropID,Context,Vectors,Frame,PROPList)
  631.   d0                   d0     d2      a0     a2      a3     a4
  632.  
  633.  IFFP = FORMhandler(chunkID,Context,Vectors,Frame,PROPList)
  634.   d0                  d0       a0      a2    a3      a4
  635.  
  636.  NonILBMFormHandler and CHUNKhandler same args as FORMhandler.
  637.  
  638.  
  639. ««««««««««««««««««««««««««««««««««««»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
  640.  
  641.                           OH NO! IT'S PROPS!
  642.  
  643.    You are responsible for keeping track of any ILBMFrame (or other struct-
  644. ures) that you create for loading a file. In particular, you should allo-
  645. cate some sort of frame structure to hold data whenever a PROP is encountered.
  646. There are 4 routines for allocating, examining, and freeing frame structures
  647. from what I call a PROPList. A PROPList is a linked list structure used to
  648. manage Frames allocated to hold PROP data. When using the high or mid level
  649. routines, the lib creates a PROPList for you.
  650.  In order to link a Frame structure (i.e. an ILBMFrame) into this list, we
  651. need to "extend" the Frame with a few extra fields at the beginning of the
  652. structure. We add the following structure "on top" of the frame, and refer
  653. to the whole thing as a PropFrame.
  654.  
  655. ID   dc.b [the 4 byte type ID]  ;type of PropFrame
  656. size dc.w [size]                ;the size of the entire structure (with the
  657.                                 ;subsequent data part)
  658.  
  659. So an ILBMPropFrame would be as follows:
  660.  
  661. ID   dc.b  'ILBM'
  662. size dc.w  SizeOfILBMFrame+10
  663.  ;an ILBMFrame structure immediately follows. This is the data part.
  664.  
  665.   So an ILBMPropFrame has two extra fields prepended to it. It is 10 bytes
  666. larger than an ILBMFrame. Here is the routine used to allocate a PropFrame.
  667. Since the lib handles ILBM PROPs for you, it calls this routine when it
  668. needs an ILBMPropFrame. Note that the passed size would be 10+sizeofILBMFrame.
  669.  
  670. ************************* GetPROPStruct **************************
  671.  Frame = GetPROPStruct(size,typeID,PROPList)
  672.    d0                   d0    d1      a1
  673.  
  674.  Allocates a PropFrame of passed size, and stores its ID, links it at the
  675.  tail of the passed PROPList, and increments PROPList's entries. Returns
  676.  the address of the new Frame (data part), or 0 if error.
  677.  
  678.   Note that the address of the data part is returned (i.e. skips those first
  679. 2 "extra" fields) so that you can treat the returned pointer just like a
  680. regular frame. If you were allocating an ILBMPropFrame, the returned address
  681. would be that of an ILBMFrame.
  682.  
  683.    When writing a custom PROP routine, you must define the PropFrame
  684. for nonILBM PROPs (i.e. what does an SMUSPropFrame look like? You decide.)
  685. Then, allocate that Structure via GetPROPStruct() from within your PROP
  686. routine. The lib always allocates an ILBMPropFrame for you.
  687.  
  688.   Now, whenever you encounter a FORM, your FORMhandler should search the
  689. PROPList for any PropFrames with the same ID. The following routine does this
  690.  
  691. ************************ SearchPROP *************************
  692.  frame = SearchPROP(ID,PROPList)
  693.   d0                d0    a1
  694.  Searches the passed PROPList for the last PropFrame with the same ID as
  695.  passed. If it finds one of the same type, returns the address of the
  696.  data part (the frame part), or 0 if none of that type found in the list.
  697.  
  698.  If SearchPROP returns an address, you'll want to copy that Frame to
  699. the Frame you intend to use to parse the FORM. There is a routine for copying
  700. ILBMFrames. You'll need to write routines for other Frames that you devise.
  701.  
  702. ************************ CopyILBMProp ***************************
  703.  CopyILBMProp(FromFrame,ToFrame)
  704.                   d0        a1
  705.  Copies FromFrame to ToFrame. Both frames must be ILBM frames. It transfers
  706.  the iUserFlags field of FromFrame to ToFrame without altering any set bits
  707.  in ToFrame's iUserFlags.
  708.  
  709.  Finally, the lib frees all of the PropFrames in the PROPList for you. But
  710. in case you are maintaining a second PROPList which you need to free, here's
  711. a routine that you can use.
  712.  
  713. ************************* FreePROPList **************************
  714.  FreePROPList(PROPList)
  715.                 a1
  716.  Frees all the PropFrames in the passed PROPList.
  717.  
  718.   When using LoadILBM() or LoadIFF(), the lib allocates/initializes the
  719. PROPList. These routines also free up that PROPList and all its PropFrames
  720. upon termination of the load.
  721.  
  722.  
  723. «««««««««««««««««« Writing a Reader/Writer from scratch »»»»»»»»»»»»»»»»»»»
  724.  
  725.   If the high level functions LoadIFFToWindow() and SaveWindowToIFF(), or
  726. the mid-level functions SaveILBM(), LoadILBM(), and LoadIFF() aren't suita-
  727. ble, you may use the low level routines to create a Reader/Writer.
  728.    Unless otherwise stated, the normal (successful) return codes for the low
  729. level routines is IFF_OKAY. This means that assembly programmers can always
  730. bne to an error routine (z-flag set appropriately).
  731.  
  732.     The library's low level routines utilize a 36 byte structure called a
  733. "Context". The Context structure that the library uses is identical to the
  734. original EA structure. Check the INCLUDE files for a description.
  735.  
  736.    For the high and mid-level routines, the lib allocates a new Context (and
  737.  initializes it by OpenRIFF or OpenRGroup) for every group (FORM, CAT, LIST,
  738.  or PROP) encountered. This is done for you even if you have custom vectors
  739.  for FORMs and PROPs.
  740.     You will only need to deal with Context structures if you are using the
  741.  lowest level library routines. This structure is for reading and writing
  742.  groups and their chunks. It's just a linked node type of structure for
  743.  reading (nested) chunks. A Context structure must be created whenever a-
  744.  nother level is encountered. The parentContext field contains the address
  745.  of another Context structure if this is not the top level. For example, if
  746.  a LIST is encountered, a Context structure is created. For the first FORM
  747.  in the LIST, another context structure is created. The parentContext field
  748.  of the FORM's Context would contain the address of the LIST's Context
  749.  structure. The parentContext field of the LIST's Context structure would be
  750.  NULL (as long as it wasn't inside of that moron's CAT). You must read or
  751.  skip over all the chunks in the FORM before you try to examine what comes
  752.  next in the LIST. You can read a chunk via GetChunkHdr() and IFFReadBytes.
  753.  You can skip a chunk by calling GetChunkHdr() a second time for the next
  754.  chunk's ID, or SkipFwd().
  755.    The UserData field is set to the parent's Userdata field by certain routines.
  756.  In this way, you can pass the contents from level to sublevel.
  757.    If you use the low level routines properly, the lib will take care of
  758.  padding out chunks to even bytes, and keeping track of the total bytes read
  759.  in or written out.
  760.      The bound field of the Context merits special mention in
  761.  that it appears to have no useful purpose. If you place any value other
  762.  than UNKNOWN there, it serves as the MAX number of bytes that can be written.
  763.  So for example, if the bound = 10000, then you will be prevented from writing
  764.  out a total of more than 10000 bytes. For most IFF applications, it isn't
  765.  initially known how many bytes will eventually be written. All of the IFF
  766.  writers which I've seen always set this to UNKNOWN. Also, it doesn't seem
  767.  useful to have the code impose a barrier. AmigaDOS already returns an error
  768.  if you attempt to write beyond a disc's capacity. I can only assume that
  769.  this "feature" was included so that this code would work on other computers
  770.  with stranger DOS than the Amiga. This field is used in PutCkEnd to deter-
  771.  mine whether the lib needs to adjust UNKNOWN chunkSizes to the real value
  772.  after it writes out a chunk. If EA had used a bit flag for UNKNOWN rather
  773.  than a value to be compared against the bound field, then the code could be
  774.  smaller and faster. Then again, the original code was written in C, a lan-
  775.  guage invented by and for people who can't be bothered with trivial details
  776.  like setting bits.
  777.  
  778.              ******* Low Level Reader Routines ********
  779.  
  780.     For reading a chunk, the procedure is to allocate a Context structure,
  781.  initialize it with OpenRIFF or OpenRGroup, read the chunks with GetChunkHdr
  782.  (and its kin) and IFFReadBytes, and close the Context with CloseRGroup or
  783.  EndRGroup.
  784.  
  785.   IFFP = OpenRIFF(fileHandle, Context)
  786.    d0                 d1        a0
  787.  Given an open file, this initializes a Context spanning the whole file.
  788.  ASSUMES context was allocated by caller but not initialized.
  789.  ASSUMES caller doesn't deallocate the context before calling CloseRGroup.
  790.  Returns NOT_IFF IFFP code if the file is too small for even a chunk header.
  791.  
  792.  
  793.  FileSize = FileLength(fileHandle)
  794.     d0                    d1
  795.  Returns the length of the whole file or else a negative IFFP error code of
  796.  NO_FILE or DOS_ERROR.
  797.  SIDE EFFECT: Thanks to AmigaDOS, we have to change the file's position
  798.  to find its length. Assembly programmers: please note that this routine
  799.  does not restore a6 upon exist, so save it before you call this.
  800.  This routine is normally only used by OpenRIFF() but I included access to
  801.  it because you might find it useful for other non-IFF uses.
  802.  
  803.  
  804.  IFFP = OpenRGroup(parent, new)   passed 2 Context structures
  805.   d0                 a0     a1
  806.  Open the remainder of the current chunk as a group read context.
  807.  This will be called just after the group's subtype ID has been read
  808.  (automatically by GetChunkHdr for LIST, FORM, PROP, and CAT) so the
  809.  remainder is a sequence of chunks.
  810.  This sets new's UserData = parent's UserData.
  811.  ASSUMES new context allocated by caller but not initialized.
  812.  ASSUMES caller doesn't deallocate the context or access the parent context
  813.  before calling CloseRGroup on the new context.
  814.  BAD_IFF ERROR if context end is odd or extends past parent.
  815.  
  816.  
  817.  IFFP = CloseRGroup(context)
  818.   d0                  a0
  819.  Close a group read context, updating its parent context.
  820.  After calling this, the old context may be deallocated and the parent
  821.  context can be accessed again. It's okay to call this particular procedure
  822.  after an error has occurred reading the group.
  823.  
  824.  
  825.  ID = GetChunkHdr(context)
  826.  d0                  a0
  827.  Skip any remaining bytes of the previous chunk and any padding, then
  828.  read the next chunk header into context's chunkID and chunkSize fields.
  829.  If the chunkID is LIST, FORM, CAT, or PROP, this automatically reads the
  830.  subtype ID into context's subID field.
  831.  Caller should dispatch on groupID (and typeID) to an appropriate handler.
  832.  RETURNS the chunkID (the 4 ascii bytes of the new chunk header) if it found
  833.  another chunk. Otherwise, it will return one of the following errors:
  834.   1). END_MARK if there are no more chunks in this context
  835.   2). NOT_IFF if at the top level and it isn't a FORM, LIST, or CAT
  836.   3). BAD_IFF if a malformed chunk, the chunkSize is negative or too big for
  837.       containing context, ID isn't positive, or we hit end-of-file.
  838.  Note that if an error, bit #31 of d0 will be set and so you can either
  839.   1).  move.l d0,d1
  840.        bmi    to an error routine.
  841.   2).  btst.l #31,d0
  842.        bne    to an error routine.
  843.  The Z and N Flags are set appropriately for low level functions.
  844.  See also GetFChunkHdr, GetF1ChunkHdr, and GetPChunkHdr, below.
  845.  
  846.  
  847.  IFFP = IFFReadBytes(nBytes, context, buffer)
  848.   d0                   d0      a0       a1
  849.  Read the specified number of data bytes of current chunk. (Use OpenGroup,
  850.  etc. instead to read the contents of a group chunk.) You can call this
  851.  several times to read the data piecemeal.
  852.  CLIENT_ERROR if nBytes < 0. SHORT_CHUNK if nBytes > remaining bytes
  853.  which could be due to a application bug or a chunk that's shorter than it
  854.  ought to be (bad form). (If CLIENT_ERROR or SHORT_CHUNK, IFFReadBytes won't
  855.  read any bytes.)
  856.  
  857.  
  858.  IFFP = SkipFwd(bytes, context)
  859.   d0              d1      a0
  860.  Skip over bytes in a chunk. Won't go backwards.
  861.  Updates context's position but not context's bytesSoFar.
  862.  
  863.  
  864.  NOTE: The original EA code's SkipGroup() routine has been eliminated
  865.  since the library is set up to parse LISTS and PROPS.
  866.  
  867.  
  868.  IFFP = LoadIFF(file, vector, dataAddress)
  869.   d0             d1     a0     a1
  870.  Given an open file, allocates a group context and uses it to read the FORM,
  871.  LIST, or CAT and it's contents. The idea is to parse the file's contents,
  872.  and for each FORM, LIST, CAT, or PROP encountered, call the FORMhandler,
  873.  PROPhandler, Cat, or List procedure passing the GroupContext address.
  874.  If you want to handle FORMs, LISTs, and CATs nested within FORMs, the
  875.  FORMhandler must dispatch to FORMhandler, List, and Cat procedures (using
  876.  GetF1ChunkHdr). The dataAddress parameter is optional. It could be a Frame
  877.  of some sort. The lib will pass this to your FORMhandler.
  878.  Normal return is IFF_OKAY (if whole file scanned) or IFF_DONE (if a custom
  879.  routine said "done" first).
  880.  
  881.  
  882.  ID = GetFChunkHdr(context)
  883.  d0                  a0
  884.  Call GetFChunkHdr instead of GetChunkHdr to read each chunk inside a FORM.
  885.  It just calls GetChunkHdr and returns BAD_IFF if it gets a PROP chunk.
  886.  
  887.  
  888.  ID = GetF1ChunkHdr(context)
  889.  d0                   a0
  890.  GetF1ChunkHdr is like GetFChunkHdr, but it automatically dispatches to the
  891.  getFORM, List, or Cat procedure (and returns the result) if it encounters
  892.  a FORM, LIST, or CAT.
  893.  
  894.  
  895.  ID = GetPChunkHdr(context)
  896.  d0                  a0
  897.  Call GetPChunkHdr instead of GetChunkHdr to read each chunk inside a PROP.
  898.  It just calls GetChunkHdr and returns BAD_IFF if it gets a group chunk.
  899.  
  900.  
  901.  IFFP = GetCMAP(Context, colorMap, pNColorRegs)
  902.   d0              d0        a0          a1
  903.  Reads in a CMAP chunk and creates the colorMap at passed address.
  904.  pNColorRegs is passed in as a pointer to the number of ColorRegisters
  905.  caller has space to hold.  GetCMAP sets to the number actually read.
  906.  
  907.  
  908.  IFFP = GetBODY( bitmap, mask, context, BMHD )
  909.   d0               d0     d1    a0       a1
  910.  Reads the BODY into the passed bitmap's planes, decompressing if necessary.
  911.  Passed the addresses of a BitMap, mask plane (or NULL), the context
  912.  structure, and the loaded BitMap header chunk.
  913.  
  914.  
  915.  BOOL = UnPackRow(dstBytes0, srcBytes0, Source, Dest)
  916.   d0                 d1         d3        a2     a3
  917.    Converts data from "cmpByteRun1" run compression.
  918.     control bytes:
  919.      [0..127]   : followed by n+1 bytes of data.
  920.      [-1..-127] : followed by byte to be repeated (-n)+1 times.
  921.      -128       : NOOP.
  922. Unpacks one row, returning the source and destination addresses when it
  923. produces dstBytes bytes. This routine no longer uses POINTERS to POINTERS
  924. as in the original Elec Arts code. Also, args in a different order.
  925.  
  926.         ********* Low Level Writing (Save) Routines *********
  927.  
  928.  These routines will random access back to set a chunk size value when the
  929.  caller doesn't know it ahead of time (UNKNOWN size).
  930.  
  931.  The overall scheme is to open an output Group Context via OpenWIFF or
  932.  OpenWGroup, call either PutCk or {PutCkHdr {IFFWriteBytes}* PutCkEnd} for
  933.  each chunk, then use CloseWGroup to close the Group Context.
  934.  
  935.  To write a group (LIST, FORM, PROP, or CAT), call StartWGroup, write out
  936.  its chunks, then call EndWGroup. StartWGroup automatically writes the
  937.  group header and opens a nested context for writing the contents.
  938.  EndWGroup closes the nested context and completes the group chunk.
  939.  
  940.  IFFP = OpenWIFF(limit, fileHandle, new)   new is the address of a Context
  941.   d0               d0      d1       a0     structure
  942.  Given a file open for output, initialize a new, write context.
  943.  The "limit" arg imposes a fence or upper limit on the logical file
  944.  position for writing data in this context. Pass in UNKNOWN to be limited
  945.  only by disk capacity.
  946.  ASSUMES new context structure allocated by caller but not initialized.
  947.  ASSUMES caller doesn't deallocate the context before calling CloseWGroup.
  948.  The caller is only allowed to write out one FORM, LIST, or CAT in this top
  949.  level context (see StartWGroup and PutCkHdr).
  950.  CLIENT_ERROR if limit is odd.
  951.  
  952.  
  953.  IFFP = StartWGroup(groupType, groupSize, subtype, parent, new)
  954.   d0                   d0         d1        d2        a0    a1
  955.  parent and new are the addresses of Context structures, groupType and
  956.  subtype are IDs, groupsize is a LONG
  957.  Start writing a group (presumably LIST, FORM, PROP, or CAT), opening a
  958.  nested context. The groupSize includes all nested chunks + the subtype ID.
  959.  The subtype of a LIST or CAT is a hint at the contents' FORM type(s). Pass
  960.  in FILLER ("    ") if it's a mixture of different kinds.
  961.  This writes the chunk header via PutCkHdr, writes the subtype ID via
  962.  IFFWriteBytes, and calls OpenWGroup. The caller may then write the nested
  963.  chunks and finish by calling EndWGroup.
  964.  The OpenWGroup call sets new's ILBMFrame to parent's ILBMFrame.
  965.  ASSUME new context structure allocated by caller but not initialized.
  966.  ASSUME caller doesn't deallocate the context or access the parent context
  967.  before calling CloseWGroup.
  968.  ERROR conditions: See PutCkHdr, IFFWriteBytes, OpenWGroup.
  969.  
  970.  
  971.  IFFP = OpenWGroup(parent, new)  parent and new are GroupContext structures
  972.   d0                 a0    a1
  973.  Open the remainder of the current chunk as a group write context.
  974.  This is normally only called by StartWGroup.
  975.  Any fixed limit to this group chunk or a containing context will impose
  976.  a limit on the new context.
  977.  This will be called just after the group's subtype ID has been written
  978.  so the remaining contents will be a sequence of chunks.
  979.  This sets new's UserData = parent's UserData.
  980.  ASSUME new context structure allocated by caller but not initialized.
  981.  ASSUME caller doesn't deallocate the context or access the parent context
  982.  before calling CloseWGroup.
  983.  CLIENT_ERROR if context end is odd or PutCkHdr wasn't called first.
  984.  
  985.  
  986.  IFFP = EndWGroup(old)
  987.   d0               a0
  988.  End a group started by StartWGroup.
  989.  This just calls CloseWGroup and PutCkEnd.
  990.  ERROR conditions: See CloseWGroup and PutCkEnd.
  991.  
  992.  
  993.  IFFP = CloseWGroup(old)  old is the address of a Context structure
  994.   d0                a0
  995.  Close a write context and update its parent context.
  996.  This is normally only called by EndWGroup.
  997.  If this is a top level context (created by OpenWIFF) we'll set the file's
  998.  EOF (end of file) but won't close the file.
  999.  After calling this, the old context may be deallocated and its parent
  1000.  context can be accessed again.
  1001.  Amiga DOS Note: There's no call to set the EOF. We just position to the
  1002.  desired end and return. Caller must Close file at that position.
  1003.  CLIENT_ERROR if PutCkEnd wasn't called first.
  1004.  
  1005.  
  1006.  IFFP = PutCk(chunkID, chunkSize, context, data)
  1007.   d0             d0       d1        a0      a1
  1008.  Writes a whole chunk to a Context. This writes the chunk ID, chunkSize,
  1009.  data bytes, and (if needed) a pad byte. It also updates the Context to
  1010.  reflect how many bytes have been written to the file. Returns CLIENT_ERROR
  1011.  if chunkSize = UNKNOWN. This is because you must know how many bytes of
  1012.  data you wish to write in order to use this routine. (i.e. Use this routine
  1013.  instead of a PutCkHdr/IFFWriteBytes/PutCkEnd series of calls when you know
  1014.  exactly how many bytes will be in the chunk). See also PutCkHdr errors.
  1015.  
  1016.  
  1017.  IFFP = PutCkHdr(chunkID, chunkSize, context)
  1018.   d0                d0        d1        a0
  1019.  Writes just an 8 byte chunk header. The chunk header consists of the 4 byte
  1020.  ascii ID, and the chunkSize LONG. You should follow this will any number of
  1021.  calls to IFFWriteBytes in order to write out the chunk data. Finally, when
  1022.  all the chunk data is output, call PutCkEnd.
  1023.  If you don't yet know how big the chunk is, pass in chunkSize = UNKNOWN,
  1024.  then PutCkEnd will set the chunkSize for you later. This method is used
  1025.  when you really don't know how many data bytes will eventually get written
  1026.  out. (i.e. maybe you're compressing the data as it's being written out and
  1027.  you don't want to bother with knowing or keeping track of how many bytes
  1028.  have been written out. The lib does this for you as long as you specify
  1029.  chunkSize = UNKNOWN and you use only the IFFWriteBytes routine to write data
  1030.  to the file).
  1031.  Otherwise, IFFWriteBytes and PutCkEnd will ensure that the specified
  1032.  number of bytes get written.
  1033.  CLIENT_ERROR if the chunk would overflow the Context's bound, if
  1034.  PutCkHdr was previously called without a matching PutCkEnd, if chunkSize
  1035.  < 0 (except UNKNOWN), if you're trying to write something other
  1036.  than one FORM, LIST, or CAT in a top level (file level) context, or
  1037.  if chunkID <= 0 (these illegal ID values are used for error codes).
  1038.  
  1039.  
  1040.  IFFP = IFFWriteBytes(nBytes, context, data)
  1041.   d0                    d0       a0     a1
  1042.  Write nBytes number of data bytes for the current chunk and update the
  1043.  Context.
  1044.  Returns CLIENT_ERROR if any of the following conditions:
  1045.    1). Writing nBytes of data would overflow the Context's limit or
  1046.        current chunk's chunkSize. If you have specified these fields to be
  1047.        UNKNOWN, then this condition is not applicable.
  1048.    2). If PutCkHdr wasn't called first.
  1049.    3). nBytes < 0 (i.e. ridiculously large).
  1050.  
  1051.  
  1052.  IFFP = PutCkEnd(context)
  1053.   d0               a0
  1054.  Complete the current chunk, write a pad byte if needed, and update the
  1055.  Context.
  1056.  If current chunk's chunkSize = UNKNOWN, this goes back and sets the
  1057.  chunkSize in the file.
  1058.  CLIENT_ERROR if PutCkHdr wasn't called first, or if the application hasn't
  1059.  written 'chunkSize' number of bytes with IFFWriteBytes.
  1060.  
  1061.  
  1062.  IFFP = InitBMHdr(masking, compression, transparentColor,
  1063.   d0               d0          d1              d2
  1064.                 pageWidth, pageHeight, bmHdr0, bitmap)
  1065.                     d3         d4        a0      a1
  1066.  Initializes a BitMap Header (BMHD) chunk to the passed values, and sets the
  1067.  BMHD's aspect ratio.
  1068.  
  1069.  
  1070.  IFFP PutCMAP(depth, context, colorMap)
  1071.   d0            d0     a0        a1
  1072.  Writes out the passed colorMap (actually colorTable) as a CMAP chunk.
  1073.  
  1074.  
  1075.  IFFP = PutBODY(mask, context, bmHdr, bitmap)
  1076.   d0             d0     d1       a0     a1
  1077.  Writes the BODY chunk to disk in compressed or uncompressed form.
  1078.  
  1079.  
  1080.  bytes, newSource, newDest = PackRow(rowSize, pSource, pDest)
  1081.   d0       a0         a1                d0       a0      a1
  1082.  Given addresses of source and dest, packs one row, returning the new source
  1083.  and destination addresses in a0 and a1. RETURNs count of packed bytes in d0.
  1084.  Please note that this routine needs the actual addresses to the source and
  1085.  destination buffers unlike the original EA code which wanted PTRS to PTRS.
  1086.  That technique is unnecessary for assembly applications which can access
  1087.  multiple return values in several registers. I decided to put the ineffic-
  1088.  iency where it really belongs; in the C application. For C programmers, the
  1089.  new source and dest addresses can be found at sourceptr and destptr respec-
  1090.  tively. These are globals contained in the module ILBMInterface.asm which
  1091.  must be assembled and linked with your application. These globals can be
  1092.  accessed after a call to PackRow so that you'll have the addresses to pass
  1093.  on the next, subsequent call. Of course, you'll be able to access the
  1094.  returned number of packed bytes as a normal return. Ultimately what this
  1095.  means is that if you call PackRow from a C application, you can never make
  1096.  that application fully re-entrant (unless you put a FORBID/PERMIT around
  1097.  the call).
  1098.  
  1099. «««««««««««««««««««««««««««««««««»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
  1100.  
  1101.                           ANIM SUPPORT
  1102.  
  1103.   There are a few routines to assist in the construction of an ANIM player.
  1104. These routines can unpack BODY and DLTA chunks, modifying a BitMap's planes
  1105. in order to construct the next frame of an animation. Also there is a routine
  1106. to setup a BitMap structure initially based on a loaded BMHD chunk.
  1107.   The normal procedure for an anim player would be to replace the lib's
  1108. default 'FORM' handler with a custom handler. This handler should load the
  1109. data part (skip the 8 byte header) of the first frame's BMHD and BODY, and
  1110. the ANHD and DLTA for all subsequent frames. Now you can play back the anim
  1111. as follows:
  1112.  
  1113.  1). Allocate a raster large enough to hold the decompressed image. You can
  1114.      determine the size by the fields in the BMHD.
  1115.  
  1116.      height = BMHD's h or pageHeight, whichever is larger
  1117.      planedepth = numPlanes * height
  1118.      width = BMHD's w or pageWidth, whichever is larger
  1119.      RowBytes = (15+width)/8, rounded down to the nearest integer
  1120.      RasterSize = RowBytes * planeDepth
  1121.      get CHIP mem for the raster
  1122.  
  1123.  2). Setup the BitMap structure to be used for displaying the ANIM by calling
  1124.      the ilbm lib's SetupBitMap. Pass your allocated raster to SetupBitMap.
  1125.      You will need to set up the View, ViewPort, and colorMap yourself.
  1126.  
  1127.  3). Decompress the BODY into the BitMap's planes (i.e. your allocated
  1128.      raster). For double-buffered animation, you will need to setup an
  1129.      identical Bitmap/raster and copy the orig image into the new raster.
  1130.  
  1131.  4). Display the BitMap's planes (i.e. the first frame).
  1132.  
  1133.  5). Modify the BitMap's planes via DecompDLTA to show subsequent frames.
  1134.  
  1135. ;********************************************************************
  1136.        DecodeVKPlane(linebytes,ytable,in,out)
  1137.                         d0       d1   a0  a1
  1138.  By Jim Kent. Modified JG. Copyright 1987 Dancing Flame all rights reserved.
  1139.  Decompresses a DLTA chunk in vertical-byte-run-with-skips compression mode.
  1140.  
  1141.  where in is a bit-plane's worth of vertical-byte-run-with-skips data
  1142.  and out is a bit-plane that STILL has the image from last frame on it.
  1143.  Linebytes is the number of bytes-per-line (UWORD) in the out bitplane, and
  1144.  it should certainly be noted that the passed variable ytable must be
  1145.  initialized to point to a multiplication table of 0*linebytes, 1*linebytes
  1146.  ... n*linebytes  before this routine is called.
  1147.  Each entry in ytable is a UWORD.
  1148.  
  1149.  The format of "in":
  1150.    Each column of the bitplane is compressed separately.  A 320x200
  1151.    bitplane would have 40 columns of 200 bytes each.  The linebytes
  1152.    parameter is used to count through the columns, it is not in the
  1153.    "in" data, which is simply a concatenation of columns.
  1154.  
  1155.    Each columns is an op-count followed by a number of ops.
  1156.    If the op-count is zero, that's ok, it just means there's no change
  1157.    in this column from the last frame.
  1158.    The ops are of three classes, and followed by a varying amount of
  1159.    data depending on which class.
  1160.        1. Skip ops - this is a byte with the hi bit clear that says how many
  1161.           rows to move the "dest" pointer forward, ie to skip. It is non-
  1162.           zero
  1163.        2. Uniq ops - this is a byte with the hi bit set.  The hi bit is
  1164.           masked down and the remainder is a count of the number of bytes
  1165.           of data to copy literally.  It's of course followed by the
  1166.           data to copy.
  1167.        3. Same ops - this is a 0 byte followed by a count byte, followed
  1168.           by a byte value to repeat count times.
  1169.  
  1170. ;**********************************************************************
  1171.  MakeYTable(width,height,table)
  1172.               d0    d1    a0
  1173.  Makes a ytable for use with DecodeVKPlane. Table should be an memblock
  1174.  capable of holding 500 WORDs (1000 bytes).
  1175.  
  1176.  
  1177.  Here are the 3 "main" routines that you'll use in an ANIM player.
  1178. ***********************************************************************
  1179.  SetupBitmap(raster,bitmap,BMHD)
  1180.                d0     a0    a1
  1181.  
  1182.  Initializes passed bitmap's depth, rows, and BytesPerRow based on passed
  1183.  BMHD's x, y, and numPlanes, then stores addresses into bitmap's planes[]
  1184.  of each plane within the passed raster. (All the planes in the raster form
  1185.  1 contiguous CHIP mem block). Raster must be large enough for numPlanes
  1186.  * BMHD's (x+15)/8 * BMHD's y.
  1187.  After an ANIM is loaded, and you allocate a raster (via AllocRaster maybe)
  1188.  based on the BMHD's x, y, and numPlanes, you can use this routine to setup
  1189.  a BitMap structure in order to decompress the BODY with DecompBODY and
  1190.  start calling DecompDLTA.
  1191.  
  1192.  
  1193. ;**********************************************************************
  1194.  DecompBODY(BODY,BMHD,Bitmap)
  1195.              a0   a1    a2
  1196.  Decompresses a BODY chunk's data into the BitMap's planes based on the
  1197.  passed BMHD (BitMapHeader) chunk's w,y,numPlanes. This can be used to make
  1198.  the first frame of an ANIM.
  1199.  
  1200.  
  1201. ;**********************************************************************
  1202.  DecompDLTA(DLTA,Bitmap)
  1203.              a0    a2
  1204.  Decompresses a DLTA chunk's data into the BitMap's planes (which must still
  1205.  have the previous frame's image). This routine calls MakeYTable and
  1206.  DecodeVKPlane, and can be used to "make" the next frame of an animation
  1207.  being double-buffered.
  1208.  
  1209.  
  1210. «««««««««««««««««««««««««««««««««««»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
  1211.  
  1212.                  IMAGE MANIPULATION
  1213.  
  1214.   When you ask the lib to load an image into an already opened window, some-
  1215. times the image won't be the exact same size as the window. The lib scales
  1216. the image to fill the window. Here is a routine you can use for scaling a
  1217. section of one BitMap to fit into a section of another RastPort. It is
  1218. passed a standard, graphics structure called a Rectangle.
  1219.  
  1220. BOOL ScaleImage(dest_rectangle,source_rectangle,dest_rport,source_bitmap)
  1221.                      a0                  a1               a2            a4
  1222. Passed addresses of the source bitmap, source rectangle structure
  1223. (current dimensions), destination rastport (to fit source into),
  1224. and dest rectangle struct (desired dimensions).
  1225. Scales the rectangular chunk (as described by source_rectangle) of source
  1226. bitmap into the rectangular chunk (as described by dest_rectangle) of
  1227. destination rastport. It achieves this by remapping the color value of
  1228. each pixel, discarding or adding extra pixels per the scaling dimensions
  1229. using an array for the colors. This does not create a larger or smaller
  1230. palette of colors if the # of planes is different. It simply fits one
  1231. image of given width and height into a different size raster. Alters the
  1232. passed sorce BitMap's planes while transfering. It only does one line at a
  1233. time of the source so it is SLOW.
  1234. Returns a 1 if success, 0 if error (no mem for color array).
  1235.  
  1236. ««««««««««««««««««««««««««««««««»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
  1237.  
  1238.                       ERROR MESSAGES
  1239.  
  1240.  Finally, there is a routine to help display error messages to the user.
  1241.  This routine returns the address of a NULL-terminated string which des-
  1242.  cribes the IFFP error number. You can then display this string to the user.
  1243.  
  1244.  String = GetIFFPMsg(IFFP)
  1245.   d0                  d0
  1246.  
  1247.  
  1248. ««««««««««««««««««««««««««««««««««»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
  1249.  
  1250.                     MISC STUFF
  1251.  
  1252. Here's a routine to make the mouse pointer in the passed window "disappear".
  1253. Use Intuition's ClearPointer() or SetPointer() to change it again.
  1254.  
  1255.   BlankPointer(window)
  1256.                  a0
  1257.  
  1258.  
  1259. ««««««««««««««««««««««««««««««««««««««»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
  1260.  
  1261.      ADDITIONAL NOTES AND THINGS TO MAKE YOU QUEASY OR APPREHENSIVE
  1262.  
  1263.   When a picture is scaled to fit inside a window (as in loading a HIRES
  1264. image into a LORES window), the lib's scaling routine makes multiple calls
  1265. to graphics lib routines which appear to do temporary mem alloc/dealloc.
  1266. Unfortunately, the deallocation is not done in the same order as the alloca-
  1267. tion. The net result: severe memory fragmentation. It is likely that
  1268. you will not be able to scale a LORES picture into a HIRES screen twice in
  1269. a row. Unfortunately, the Amiga operating system is not smart enough to
  1270. re-coalese free blocks when it could. You have to reboot the computer. You
  1271. want smart memory coalesing, buy a MAC. Despite this, I decided that scaling
  1272. the picture yielded more pleasing results than cropping it.
  1273.   The scaling is meant to fit an image with a given width and height into
  1274. a screen with a different width and/or height. The lib does not currently
  1275. adjust for different depths (number of planes). For this reason, an image
  1276. with a depth of 5 will probably end up with "weird" colors when loaded into
  1277. a screen with a depth of 4. Perhaps in the future, a routine will be added
  1278. to interpolate color tables if there appears to be a need for such a feature.
  1279.   Note that the process of scaling a picture to fit a different size screen
  1280. is notably slow. Get used to it.
  1281.  
  1282.   An effort was made to test all of the lib functions in a variety of ways,
  1283. but I'm sure that there are things which have escaped me. Please send any
  1284. bug reports to the above address. I am not offering free consultation to
  1285. anyone, but if you have a particular problem or question, I will try to
  1286. assist. If a particular piece of code appears not to work with the library,
  1287. I would be interested in seeing the code. There will almost certainly be new,
  1288. improved lib versions in the future. These will be sent to Fred Fish.
  1289.   Obviously, more programming examples are needed for the custom handlers,
  1290. (especially in those ugly, high level languages) but since everything you
  1291. now have is free, it would be presumptious of you to expect more. If
  1292. you write any code utilizing this library which you would like to share with
  1293. others, send it directly to Fred Fish.
  1294.   The modules that you should have are as follows:
  1295.  
  1296.   ILBMLib.Doc       this text file
  1297.   ilbm.library      the library (to be copied to your boot disk's libs drawer)  
  1298.   IFF.i             assembly language include file
  1299.   ILBM_Lib.h         C Include file
  1300.   ShowPic.c          a C example of using the lib
  1301.   ILBMInterface.asm  the awful "poot" that is required for any C example
  1302.   BasicILBM          an AmigaBasic example
  1303.   ilbm.bmap          the bmap file for the Basic example
  1304.   ilbm_lib.fd        the Basic fd file
  1305.   BasicUsers         additional info for Basic programmers
  1306.   ShowPic.asm        an assembly example
  1307.   ANIMInfo.asm       an assembly example of installing a custom FORM handler
  1308.   ANIMInfo           an executable of the above
  1309.   ShowPic            a re-entrant ILBM viewer with color-cycling (operates
  1310.                      like CBM's "Display" program)
  1311.  
  1312. ««««««««««««««««««««««««««««««««««»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
  1313.                            PARTING SHOTS
  1314.  
  1315.   The ilbm.library is based upon much code placed in the public domain by
  1316. several individuals. The programmers who offer such favors are the ones
  1317. who are really responsible for the Amiga still being a viable product. Al-
  1318. though most of these people are not on Commodore's payroll, they have managed
  1319. to support the Amiga much more visibly and consistantly than Commodore's
  1320. overpaid, middle management.
  1321.   A local Amiga user's group started up an amiga-oriented BBS, and within
  1322. days, received a personal greeting from Jay Miner. Although Mr. Miner is no
  1323. longer directly involved with the marketing of the Amiga, I understand that
  1324. he keeps close ties to the Amiga community. Am I overly sensitive, or does
  1325. anyone else sense that Commodore management lacks the personal commitment
  1326. exhibited by Jay Miner? Aren't these the same type of people who were in
  1327. charge when Commodore tottered on the brink of collapse a few years back?
  1328. What has changed since then (besides the half a dozen presidents)?
  1329.   About 1 year ago, I sent in a disk full of assembly code to CATS. I
  1330. assumed that CATS would decide whether anything was of any use to the amiga
  1331. community, and distribute it via bulletin boards or Fred Fish. After seeing
  1332. nothing, I decided to send the code to Fred Fish. Within a month, all of the
  1333. code appeared on a Fish disk. How is it that a single individual with limited
  1334. resources can maintain more visible and efficient support than a corporation?
  1335. Is Fred Fish a super being from another galaxy, or is Commodore inefficient?
  1336. What other kind of information is being irretrievably sucked into CATS?
  1337.   And what can you say about Charlie Heath and his friends who rewrote more
  1338. of the Amiga's DOS than the 1.3 dev team? Is the problem with Commodore's
  1339. paltry R & D budget? How does that compare with management salaries?
  1340.   I'm sure that you've heard about Commodore's problems with the IRS. There
  1341. are also dozens more examples of sloppy, indifferent, incompetent management
  1342. which have either thwarted the success of the Amiga, or alienated people who
  1343. could help promote the Amiga. Consider that WordPerfect Corp. has chopped
  1344. Amiga development to a minimum, siting lack of support from CBM as one
  1345. reason.
  1346.   One of my favorite stories is the following:
  1347.   I am mostly interested in music software development, and follow that
  1348. market closely. For the past few years, musicians have been increasingly
  1349. utilizing computers for many tasks. Companies like Apple have noticed this
  1350. growing market, and specifically targeted promotion in this area. Commodore
  1351. has not. Given the Amiga's dismal showing in this market, worried amiga
  1352. developers finally talked Commodore into running advertisements in leading
  1353. music publications. The result was a hideously uninspired, two page ad that
  1354. was almost universely described as "unappealing" by musicians. I personally
  1355. have never met a musician who liked the ad. The ad ran for a very short time
  1356. fortunately. After some months and a new ad firm, Commodore produced
  1357. a much improved, though smaller ad. After a very brief run, "someone" at
  1358. Commodore decided that the bigger ad was better, and against the advice of
  1359. the advertising editors of a major music publication, decided to resurrect
  1360. it. Is this not an example of some dumb ass manager making a decision which
  1361. he is obviously unqualified to make? By the way, that music magazine is a
  1362. publication with a readership of about 100,000 people per month.
  1363.    The reason that I bring all of this up is that I believe that the amiga
  1364. community needs to stop being nice to Commodore simply because that company
  1365. determines the fate of the Amiga. Let's tell Commodore management what a
  1366. bunch of buffoons we think that they are. Maybe they'll work harder simply
  1367. for the sake of improving their "image" if not for the sake of future CBM
  1368. products. And to Commodore stockholders I say this: company stock has not
  1369. risen so much that it can't be flattened by new developments at IBM and
  1370. Apple, with a subsequent lack of activity at CBM. Ask your company about
  1371. their R&D budget, about how well they work with 3rd party vendors, about
  1372. when, how, and if they plan on promoting their products, about whether they
  1373. have any idea what they want to do with both current and future product
  1374. lines. If CBM doesn't want to market the Amiga, maybe they shouldn't.
  1375.  
  1376.  
  1377.   Once again, I want to mention the people who helped make this public
  1378. domain offering possible:
  1379.  
  1380.  Jerry Morrison, Steve Shaw, and Steve Hayes, Electronic Arts.
  1381.  C. Scheppner, CATS.
  1382.  Jim Kent, Dancing Flame.
  1383.  
  1384.   This is one of those few efforts in which I didn't steal "something" from
  1385. Bryce Nesbitt. Bryce is such a good programmer and documentator that it is
  1386. a fluke that he actually works for CBM.
  1387.  
  1388. ;=============================NOTES===============================
  1389.  
  1390.  ¹ The routines that read and write data to disc (IFFReadBytes and IFFWrite-
  1391.    bytes) use unbuffered I/O. An application that uses the buffered version
  1392.    of EA's code will probably be faster but certainly not as memory effic-
  1393.    ient. Wouldn't you rather wait an extra 15 seconds to load an ANIM file
  1394.    rather than not be able to view it at all because the loader is sucking
  1395.    up RAM? Besides, if you want to waste memory with buffers that remain idle
  1396.    except during disk I/O, why not use the CLI ADDBUFFERS command. This way,
  1397.    you can speed up the lib to comparable levels with the buffered IFF code,
  1398.    and also allow other disc I/O code to benefit. I realize that the concept
  1399.    of conserving RAM is not popular amoung non-assembly programmers, most
  1400.    of whom write applications which unnecessarily require 1 MEG to run well.
  1401.  
  1402.  ² In order to handle PROPs more efficiently and with less stack use than
  1403.    the original EA code, I made the following limitation.
  1404.        There should be no LIST imbedded within a LIST.
  1405.    In a file that violates this rule, PROP data from the outer LIST may
  1406.    affect chunks inside the inner LIST. Since I have never seen imbedded
  1407.    LISTs, I decided that such abominations don't deserve special treatment.
  1408.