/ source / mact / src / animlib.cpp
animlib.cpp
  1  //-------------------------------------------------------------------------
  2  /*
  3  Copyright (C) 1996, 2003 - 3D Realms Entertainment
  4  
  5  This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
  6  
  7  Duke Nukem 3D is free software; you can redistribute it and/or
  8  modify it under the terms of the GNU General Public License
  9  as published by the Free Software Foundation; either version 2
 10  of the License, or (at your option) any later version.
 11  
 12  This program is distributed in the hope that it will be useful,
 13  but WITHOUT ANY WARRANTY; without even the implied warranty of
 14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 15  
 16  See the GNU General Public License for more details.
 17  
 18  You should have received a copy of the GNU General Public License
 19  along with this program; if not, write to the Free Software
 20  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 21  
 22  Original Source: 1996 - Todd Replogle
 23  Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
 24  Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au)
 25  */
 26  //-------------------------------------------------------------------------
 27  
 28  #include "compat.h"
 29  #include "animlib.h"
 30  
 31  //****************************************************************************
 32  //
 33  // LOCALS
 34  //
 35  //****************************************************************************
 36  
 37  static anim_t * anim = NULL;
 38  
 39  //****************************************************************************
 40  //
 41  //      findpage ()
 42  //              - return the large page number a given frame resides in
 43  //
 44  //****************************************************************************
 45  
 46  static inline uint16_t findpage(uint16_t framenumber)
 47  {
 48      // curlpnum is initialized to 0xffff, obviously
 49      size_t i = anim->curlpnum & ~0xffff;
 50      size_t const nLps = anim->lpheader->nLps;
 51      bool j = true;
 52  
 53      if (framenumber < anim->currentframe)
 54          i = 0, j = false;
 55  
 56      // this scans the last used page and higher first and then scans the
 57      // previously accessed pages afterwards if it doesn't find anything
 58      do
 59      {
 60          for (; i < nLps; ++i)
 61          {
 62              lp_descriptor & lp = anim->LpArray[i];
 63              if (lp.baseRecord <= framenumber && framenumber < lp.baseRecord + lp.nRecords)
 64                  return (uint16_t)i;
 65          }
 66  
 67          if (j && i == nLps)
 68          {
 69              // handle out of order pages... I don't think any Duke .ANM files
 70              // have them, but they're part of the file spec
 71              i = 0, j = false;
 72              continue;
 73          }
 74  
 75          break;
 76      }
 77      while (1);
 78  
 79      return (uint16_t)i;
 80  }
 81  
 82  
 83  //****************************************************************************
 84  //
 85  //      loadpage ()
 86  //      - seek out and set pointers to the large page specified
 87  //
 88  //****************************************************************************
 89  
 90  static inline void loadpage(uint16_t pagenumber, uint16_t **pagepointer)
 91  {
 92      if (anim->curlpnum == pagenumber)
 93          return;
 94  
 95      anim->curlpnum = pagenumber;
 96      anim->curlp = &anim->LpArray[pagenumber];
 97      *pagepointer = (uint16_t *)(anim->buffer + 0xb00 + (pagenumber*IMAGEBUFFERSIZE) +
 98                                  sizeof(lp_descriptor) + sizeof(uint16_t));
 99  }
100  
101  
102  //****************************************************************************
103  //
104  //      decodeframe ()
105  //      - I found this less obfuscated version of the .ANM "decompressor",
106  //        (c) 1998 "Jari Komppa aka Sol/Trauma".  This code is public domain
107  //        but has been mostly rewritten by me.
108  //
109  //      - As a side note, it looks like this format came about in 1989 and
110  //        never went anywhere after that, and appears to have been the format
111  //        used by Electronic Arts' DeluxePaint Animation, which never made it
112  //        past version 1.0.
113  //
114  //****************************************************************************
115  
116  static void decodeframe(uint8_t * srcP, uint8_t * dstP)
117  {
118      do
119      {
120          {
121              /* short op */
122              uint8_t count = *srcP++;
123  
124              if (!count) /* short RLE */
125              {
126                  uint8_t color = *(srcP+1);
127                  count = *(uint8_t *)srcP;
128                  srcP += sizeof(int16_t);
129                  Bmemset(dstP, color, count);
130                  dstP += count;
131                  continue;
132              }
133              else if ((count & 0x80) == 0) /* short copy */
134              {
135                  Bmemcpy(dstP, srcP, count);
136                  dstP += count;
137                  srcP += count;
138                  continue;
139              }
140              else if ((count &= ~0x80) > 0) /* short skip */
141              {
142                  dstP += count;
143                  continue;
144              }
145          }
146  
147          {
148              /* long op */
149              uint16_t count = B_LITTLE16(B_UNBUF16(srcP));
150              srcP += sizeof(int16_t);
151  
152              if (!count) /* stop sign */
153                  return;
154              else if ((count & 0x8000) == 0) /* long skip */
155              {
156                  dstP += count;
157                  continue;
158              }
159              else if ((count &= ~0x8000) & 0x4000) /* long RLE */
160              {
161                  uint8_t color = *srcP++;
162                  count &= ~0x4000;
163                  Bmemset(dstP, color, count);
164                  dstP += count;
165                  continue;
166              }
167  
168              /* long copy */
169              Bmemcpy(dstP, srcP, count);
170              dstP += count;
171              srcP += count;
172          }
173      }
174      while (1);
175  }
176  
177  
178  //****************************************************************************
179  //
180  //      renderframe ()
181  //      - draw the frame sepcified from the large page in the buffer pointed to
182  //
183  //****************************************************************************
184  
185  static void renderframe(uint16_t framenumber, uint16_t *pagepointer)
186  {
187      uint16_t offset = 0;
188      uint16_t frame = framenumber - anim->curlp->baseRecord;
189  
190      while (frame--) offset += B_LITTLE16(pagepointer[frame]);
191  
192      if (offset >= anim->curlp->nBytes)
193          return;
194  
195      uint8_t *ppointer = (uint8_t *)(pagepointer) + anim->curlp->nRecords*2 + offset + 4;
196  
197      if ((ppointer-4)[1])
198      {
199          uint16_t const temp = B_LITTLE16(((uint16_t *)(ppointer-4))[1]);
200          ppointer += temp + (temp & 1);
201      }
202  
203      decodeframe((uint8_t *)ppointer, (uint8_t *)anim->imagebuffer);
204  }
205  
206  
207  //****************************************************************************
208  //
209  //      drawframe ()
210  //      - high level frame draw routine
211  //
212  //****************************************************************************
213  
214  static inline void drawframe(uint16_t framenumber)
215  {
216      loadpage(findpage(framenumber), &anim->thepage);
217      renderframe(framenumber, anim->thepage);
218  }
219  
220  // <length> is the file size, for consistency checking.
221  int32_t ANIM_LoadAnim(uint8_t *buffer, int32_t length)
222  {
223      length -= sizeof(lpfileheader)+128+768;
224      if (length < 0)
225          return -1;
226  
227      anim = (anim_t *)Xrealloc(anim, sizeof(anim_t));
228  
229      anim->curlpnum = 0xffff;
230      anim->currentframe = -1;
231  
232      // this just modifies the data in-place instead of copying it elsewhere now
233      lpfileheader & lpheader = *(anim->lpheader = (lpfileheader *)(anim->buffer = buffer));
234  
235      lpheader.id              = B_LITTLE32(lpheader.id);
236      lpheader.maxLps          = B_LITTLE16(lpheader.maxLps);
237      lpheader.nLps            = B_LITTLE16(lpheader.nLps);
238      lpheader.nRecords        = B_LITTLE32(lpheader.nRecords);
239      lpheader.maxRecsPerLp    = B_LITTLE16(lpheader.maxRecsPerLp);
240      lpheader.lpfTableOffset  = B_LITTLE16(lpheader.lpfTableOffset);
241      lpheader.contentType     = B_LITTLE32(lpheader.contentType);
242      lpheader.width           = B_LITTLE16(lpheader.width);
243      lpheader.height          = B_LITTLE16(lpheader.height);
244      lpheader.nFrames         = B_LITTLE32(lpheader.nFrames);
245      lpheader.framesPerSecond = B_LITTLE16(lpheader.framesPerSecond);
246  
247      length -= lpheader.nLps * sizeof(lp_descriptor);
248      if (length < 0)
249          return -2;
250  
251      buffer += sizeof(lpfileheader)+128;
252  
253      // load the color palette
254      for (uint8_t * pal = anim->pal, * pal_end = pal+768; pal < pal_end; pal += 3, buffer += 4)
255      {
256          pal[2] = buffer[0];
257          pal[1] = buffer[1];
258          pal[0] = buffer[2];
259      }
260  
261      // set up large page descriptors
262      anim->LpArray = (lp_descriptor *)buffer;
263  
264      // theoretically we should be able to play files with more than 256 frames now
265      // assuming the utilities to create them can make them that way
266      for (lp_descriptor * lp = anim->LpArray, * lp_end = lp+lpheader.nLps; lp < lp_end; ++lp)
267      {
268          lp->baseRecord = B_LITTLE16(lp->baseRecord);
269          lp->nRecords   = B_LITTLE16(lp->nRecords);
270          lp->nBytes     = B_LITTLE16(lp->nBytes);
271      }
272  
273      return 0;
274  }
275  
276  
277  void ANIM_FreeAnim(void)
278  {
279      DO_FREE_AND_NULL(anim);
280  }
281  
282  
283  int32_t ANIM_NumFrames(void)
284  {
285      return anim->lpheader->nRecords;
286  }
287  
288  
289  uint8_t * ANIM_DrawFrame(int32_t framenumber)
290  {
291      uint32_t cnt = anim->currentframe;
292  
293      // handle first play and looping or rewinding
294      if (cnt > (uint32_t)framenumber)
295          cnt = 0;
296  
297      do drawframe(cnt++);
298      while (cnt < (uint32_t)framenumber);
299  
300      anim->currentframe = framenumber;
301      return anim->imagebuffer;
302  }
303  
304  
305  uint8_t * ANIM_GetPalette(void)
306  {
307      return anim->pal;
308  }