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 }