Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * OpenTyrian: A modern cross-platform port of Tyrian
  3.  * Copyright (C) 2007-2009  The OpenTyrian Development Team
  4.  *
  5.  * This program is free software; you can redistribute it and/or
  6.  * modify it under the terms of the GNU General Public License
  7.  * as published by the Free Software Foundation; either version 2
  8.  * of the License, or (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  18.  */
  19. #include "animlib.h"
  20.  
  21. #include "file.h"
  22. #include "keyboard.h"
  23. #include "network.h"
  24. #include "nortsong.h"
  25. #include "palette.h"
  26. #include "sizebuf.h"
  27. #include "video.h"
  28.  
  29. #include <assert.h>
  30.  
  31. /*** Structs ***/
  32. /* The actual header has a lot of fields that are basically useless to us since
  33.  * we both set our own framerate and the format itself only allows for
  34.  * 320x200x8.  Should a (nonexistent) ani be played that doesn't have the same
  35.  * assumed values we are going to use, TOO BAD.  It'll just be treated as
  36.  * corrupt in playback.
  37.  */
  38. #define PALETTE_OFFSET    0x100 // 128 + sizeof(header)
  39. #define PAGEHEADER_OFFSET 0x500 // PALETTE_OFFSET + sizeof(palette)
  40. #define ANIM_OFFSET   0x0B00    // PAGEHEADER_OFFSET + sizeof(largepageheader) * 256
  41. #define ANI_PAGE_SIZE 0x10000   // 65536.
  42. typedef struct anim_FileHeader_s
  43. {
  44.         unsigned int nlps;            /* Number of 'pages', max 256. */
  45.         unsigned int nRecords;        /* Number of 'records', max 65535 */
  46. } anim_FileHeader_t;
  47. typedef struct anim_LargePageHeader_s
  48. {
  49.         unsigned int baseRecord;      /* The first record's number */
  50.         unsigned int nRecords;        /* Number of records.  Supposedly there are bit flags but I saw no such code */
  51.         unsigned int nBytes;          /* Number of bytes used, excluding headers */
  52. } anim_LargePageHeader_t;
  53.  
  54.  
  55. /*** Globals ***/
  56. Uint8 CurrentPageBuffer[65536];
  57. anim_LargePageHeader_t PageHeader[256];
  58. unsigned int CurrentPageRecordSizes[256];
  59.  
  60. anim_LargePageHeader_t CurrentPageHeader;
  61. anim_FileHeader_t FileHeader;
  62.  
  63. unsigned int Curlpnum;
  64.  
  65. FILE * InFile;
  66.  
  67.  
  68. /*** Function decs ***/
  69. int JE_playRunSkipDump( Uint8 *, unsigned int );
  70. void JE_closeAnim( void );
  71. int JE_loadAnim( const char * );
  72. int JE_renderFrame( unsigned int );
  73. int JE_findPage ( unsigned int );
  74. int JE_drawFrame( unsigned int );
  75. int JE_loadPage( unsigned int );
  76.  
  77. /*** Implementation ***/
  78.  
  79.  
  80. /* Loads the given page into memory.
  81.  *
  82.  * Returns  0 on success or nonzero on failure (bad data)
  83.  */
  84. int JE_loadPage( unsigned int pagenumber )
  85. {
  86.         unsigned int i, pageSize;
  87.  
  88.  
  89.         if (Curlpnum == pagenumber) { return(0); } /* Already loaded */
  90.         Curlpnum = pagenumber;
  91.  
  92.         /* We need to seek to the page and load it into our buffer.
  93.          * Pages have a fixed size of 0x10000; any left over space is padded
  94.          * unless it's the end of the file.
  95.          *
  96.          * Pages repeat their headers for some reason.  They then have two bytes of
  97.          * padding folowed by a word for every record.  THEN the data starts.
  98.          */
  99.         fseek(InFile, ANIM_OFFSET + (pagenumber * ANI_PAGE_SIZE), SEEK_SET);
  100.         efread(&CurrentPageHeader.baseRecord, 2, 1, InFile);
  101.         efread(&CurrentPageHeader.nRecords,   2, 1, InFile);
  102.         efread(&CurrentPageHeader.nBytes,     2, 1, InFile);
  103.  
  104.         fseek(InFile, 2, SEEK_CUR);
  105.         for (i = 0; i < CurrentPageHeader.nRecords; i++)
  106.         {
  107.                 efread(&CurrentPageRecordSizes[i], 2, 1, InFile);
  108.         }
  109.  
  110.         /* What remains is the 'compressed' data */
  111.         efread(CurrentPageBuffer, 1, CurrentPageHeader.nBytes, InFile);
  112.  
  113.         /* Okay, we've succeeded in all our IO checks.  Now, make sure the
  114.          * headers aren't lying or damaged or something.
  115.          */
  116.         pageSize = 0;
  117.         for (i = 0; i < CurrentPageHeader.nRecords; i++)
  118.         {
  119.                 pageSize += CurrentPageRecordSizes[i];
  120.         }
  121.  
  122.         if(pageSize != CurrentPageHeader.nBytes) { return(-1); }
  123.  
  124.         /* So far, so good */
  125.         return(0);
  126. }
  127.  
  128. int JE_drawFrame( unsigned int framenumber )
  129. {
  130.         int ret;
  131.  
  132.  
  133.         ret = JE_loadPage(framenumber);
  134.         if (ret) { return(ret); }
  135.  
  136.         ret = JE_renderFrame (framenumber);
  137.         if (ret) { return(ret); }
  138.  
  139.         return(0);
  140. }
  141.  
  142. int JE_findPage( unsigned int framenumber )
  143. {
  144.         unsigned int i;
  145.  
  146.  
  147.         for (i = 0; i < FileHeader.nlps; i++)
  148.         {
  149.                 if (PageHeader[i].baseRecord <= framenumber
  150.                  && PageHeader[i].baseRecord + PageHeader[i].nRecords > framenumber)
  151.                 {
  152.                         return(i);
  153.                 }
  154.         }
  155.  
  156.         return(-1); /* Did not find */
  157. }
  158.  
  159. int JE_renderFrame( unsigned int framenumber )
  160. {
  161.         unsigned int i, offset, destframe;
  162.  
  163.  
  164.         destframe = framenumber - CurrentPageHeader.baseRecord;
  165.  
  166.         offset = 0;
  167.         for (i = 0; i < destframe; i++)
  168.         {
  169.                 offset += CurrentPageRecordSizes[i];
  170.         }
  171.  
  172.         return (JE_playRunSkipDump(CurrentPageBuffer + offset + 4, CurrentPageRecordSizes[destframe] - 4));
  173. }
  174.  
  175. void JE_playAnim( const char *animfile, JE_byte startingframe, JE_byte speed )
  176. {
  177.         unsigned int i;
  178.         int pageNum;
  179.  
  180.         if (JE_loadAnim(animfile) != 0)
  181.         {
  182.                 return; /* Failed to open or process file */
  183.         }
  184.  
  185.         /* Blank screen */
  186.         JE_clr256(VGAScreen);
  187.         JE_showVGA();
  188.  
  189.  
  190.         /* re FileHeader.nRecords-1: It's -1 in the pascal too.
  191.          * The final frame is a delta of the first, and we don't need that.
  192.          * We could also, if we ever ended up needing to loop anis, check
  193.          * the bools in the header to see if we should render the last
  194.          * frame.  But that's never going to be encessary :)
  195.          */
  196.     for (i = startingframe; i < FileHeader.nRecords-1; i++)
  197.     {
  198.         /* Handle boring crap */
  199.         setjasondelay(speed);
  200.  
  201.                 /* Load required frame.  The loading function is smart enough to not re-load an already loaded frame */
  202.                 pageNum = JE_findPage(i);
  203.                 if(pageNum == -1) { break; }
  204.                 if (JE_loadPage(pageNum) != 0) { break; }
  205.  
  206.                 /* render frame. */
  207.         if (JE_renderFrame(i) != 0) { break; }
  208.         JE_showVGA();
  209.  
  210.  
  211.                 /* Return early if user presses a key */
  212.                 service_SDL_events(true);
  213.                 if (newkey)
  214.                 {
  215.                         break;
  216.                 }
  217.  
  218.                 /* Wait until we need the next frame */
  219.                 NETWORK_KEEP_ALIVE();
  220.                 wait_delay();
  221.     }
  222.  
  223.         JE_closeAnim();
  224. }
  225.  
  226. /* loadAnim opens the file and loads data from it into the header structs.
  227.  * It should take care to clean up after itself should an error occur.
  228.  */
  229. int JE_loadAnim( const char *filename )
  230. {
  231.         unsigned int i, fileSize;
  232.         char temp[4];
  233.  
  234.  
  235.         Curlpnum = -1;
  236.         InFile = dir_fopen(data_dir(), filename, "rb");
  237.         if(InFile == NULL)
  238.         {
  239.                 return(-1);
  240.         }
  241.  
  242.         fileSize = ftell_eof(InFile);
  243.         if(fileSize < ANIM_OFFSET)
  244.         {
  245.                 /* We don't know the exact size our file should be yet,
  246.                  * but we do know it should be way more than this */
  247.                 fclose(InFile);
  248.                 return(-1);
  249.         }
  250.  
  251.         /* Read in the header.  The header is 256 bytes long or so,
  252.          * but that includes a lot of padding as well as several
  253.          * vars we really don't care about.  We shall check the ID and extract
  254.          * the handful of vars we care about.  Every value in the header that
  255.          * is constant will be ignored.
  256.          */
  257.  
  258.         efread(&temp, 1, 4, InFile); /* The ID, should equal "LPF " */
  259.         fseek(InFile, 2, SEEK_CUR); /* skip over this word */
  260.         efread(&FileHeader.nlps, 2, 1, InFile); /* Number of pages */
  261.         efread(&FileHeader.nRecords, 4, 1, InFile); /* Number of records */
  262.  
  263.         if (memcmp(temp, "LPF ", 4) != 0
  264.          || FileHeader.nlps == 0  || FileHeader.nRecords == 0
  265.          || FileHeader.nlps > 256 || FileHeader.nRecords > 65535)
  266.         {
  267.                 fclose(InFile);
  268.                 return(-1);
  269.         }
  270.  
  271.         /* Read in headers */
  272.         fseek(InFile, PAGEHEADER_OFFSET, SEEK_SET);
  273.         for (i = 0; i < FileHeader.nlps; i++)
  274.         {
  275.                 efread(&PageHeader[i].baseRecord, 2, 1, InFile);
  276.                 efread(&PageHeader[i].nRecords,   2, 1, InFile);
  277.                 efread(&PageHeader[i].nBytes,     2, 1, InFile);
  278.         }
  279.  
  280.  
  281.         /* Now we have enough information to calculate the 'expected' file size.
  282.          * Our calculation SHOULD be equal to fileSize, but we won't begrudge
  283.          * padding */
  284.         if (fileSize < (FileHeader.nlps-1) * ANI_PAGE_SIZE + ANIM_OFFSET
  285.           + PageHeader[FileHeader.nlps-1].nBytes
  286.           + PageHeader[FileHeader.nlps-1].nRecords * 2 + 8)
  287.         {
  288.                 fclose(InFile);
  289.                 return(-1);
  290.         }
  291.  
  292.  
  293.         /* Now read in the palette. */
  294.         fseek(InFile, PALETTE_OFFSET, SEEK_SET);
  295.         for (i = 0; i < 256; i++)
  296.         {
  297.                 efread(&colors[i].b,      1, 1, InFile);
  298.                 efread(&colors[i].g,      1, 1, InFile);
  299.                 efread(&colors[i].r,      1, 1, InFile);
  300.                 efread(&colors[i].unused, 1, 1, InFile);
  301.         }
  302.         set_palette(colors, 0, 255);
  303.  
  304.         /* Whew!  That was hard.  Let's go grab some beers! */
  305.         return(0);
  306. }
  307.  
  308. void JE_closeAnim( void )
  309. {
  310.         fclose(InFile);
  311. }
  312.  
  313. /* RunSkipDump decompresses the video.  There are three operations, run, skip,
  314.  * and dump.  They can be used in either byte or word variations, making six
  315.  * possible actions, and there's a seventh 'stop' action, which looks
  316.  * like 0x80 0x00 0x00.
  317.  *
  318.  * Run is a memset.
  319.  * Dump is a memcpy.
  320.  * Skip leaves the old data intact and simply increments the pointers.
  321.  *
  322.  * returns 0 on success or 1 if decompressing failed.  Failure to decompress
  323.  * indicates a broken or malicious file; playback should terminate.
  324.  */
  325. int JE_playRunSkipDump( Uint8 *incomingBuffer, unsigned int IncomingBufferLength )
  326. {
  327.         sizebuf_t Buffer_IN, Buffer_OUT;
  328.         sizebuf_t * pBuffer_IN = &Buffer_IN, * pBuffer_OUT = &Buffer_OUT;
  329.  
  330.         #define ANI_SHORT_RLE  0x00
  331.         #define ANI_SHORT_SKIP 0x80
  332.         #define ANI_LONG_OP    0x80
  333.         #define ANI_LONG_COPY_OR_RLE  0x8000
  334.         #define ANI_LONG_RLE   0x4000
  335.         #define ANI_STOP       0x0000
  336.  
  337.         SZ_Init(pBuffer_IN,  incomingBuffer,    IncomingBufferLength);
  338.         SZ_Init(pBuffer_OUT, VGAScreen->pixels, VGAScreen->h * VGAScreen->pitch);
  339.  
  340.  
  341.         /* 320x200 is the only supported format.
  342.          * Assert is here as a hint should our screen size ever changes.
  343.          * As for how to decompress to the wrong screen size... */
  344.         assert(VGAScreen->h * VGAScreen->pitch == 320 * 200);
  345.  
  346.  
  347.         while (1)
  348.         {
  349.                 /* Get one byte.  This byte may have flags that tell us more */
  350.                 unsigned int opcode = MSG_ReadByte(pBuffer_IN);
  351.  
  352.                 /* Before we continue, check the error states/
  353.                  * We should *probably* check these after every read and write, but
  354.                  * I've rigged it so that the buffers will never go out of bounds.
  355.                  * So we can afford to be lazy; if the buffer overflows below it will
  356.                  * silently fail its writes and we'll catch the failure on our next
  357.                  * run through the loop.  A failure means we should be
  358.                  * leaving ANYWAY.  The contents of our buffers doesn't matter.
  359.                  */
  360.                 if (SZ_Error(pBuffer_IN) || SZ_Error(pBuffer_OUT))
  361.                 {
  362.                         return(-1);
  363.                 }
  364.  
  365.                 /* Divide into 'short' and 'long' */
  366.                 if (opcode == ANI_LONG_OP) /* long ops */
  367.                 {
  368.                         opcode = MSG_ReadWord(pBuffer_IN);
  369.  
  370.                         if (opcode == ANI_STOP) /* We are done decompressing.  Leave */
  371.                         {
  372.                                 break;
  373.                         }
  374.                         else if (!(opcode & ANI_LONG_COPY_OR_RLE)) /* If it's not those two, it's a skip */
  375.                         {
  376.                                 unsigned int count = opcode;
  377.                                 SZ_Seek(pBuffer_OUT, count, SEEK_CUR);
  378.                         }
  379.                         else /* Now things get a bit more interesting... */
  380.                         {
  381.                                 opcode &= ~ANI_LONG_COPY_OR_RLE; /* Clear that flag */
  382.  
  383.                                 if (opcode & ANI_LONG_RLE) /* RLE */
  384.                                 {
  385.                                         unsigned int count = opcode & ~ANI_LONG_RLE; /* Clear flag */
  386.  
  387.                                         /* Extract another byte */
  388.                                         unsigned int value = MSG_ReadByte(pBuffer_IN);
  389.  
  390.                                         /* The actual run */
  391.                                         SZ_Memset(pBuffer_OUT, value, count);
  392.                                 }
  393.                                 else
  394.                                 { /* Long copy */
  395.                                         unsigned int count = opcode;
  396.  
  397.                                         /* Copy */
  398.                                         SZ_Memcpy2(pBuffer_OUT, pBuffer_IN, count);
  399.                                 }
  400.                         }
  401.                 } /* End of long ops */
  402.                 else /* short ops */
  403.                 {
  404.                         if (opcode & ANI_SHORT_SKIP) /* Short skip, move pointer only */
  405.                         {
  406.                                 unsigned int count = opcode & ~ANI_SHORT_SKIP; /* clear flag to get count */
  407.                                 SZ_Seek(pBuffer_OUT, count, SEEK_CUR);
  408.                         }
  409.                         else if (opcode == ANI_SHORT_RLE) /* Short RLE, memset the destination */
  410.                         {
  411.                                 /* Extract a few more bytes */
  412.                                 unsigned int count = MSG_ReadByte(pBuffer_IN);
  413.                                 unsigned int value = MSG_ReadByte(pBuffer_IN);
  414.  
  415.                                 /* Run */
  416.                                 SZ_Memset(pBuffer_OUT, value, count);
  417.                         }
  418.                         else /* Short copy, memcpy from src to dest. */
  419.                         {
  420.                                 unsigned int count = opcode;
  421.  
  422.                                 /* Dump */
  423.                                 SZ_Memcpy2(pBuffer_OUT, pBuffer_IN, count);
  424.                         }
  425.                 } /* End of short ops */
  426.         }
  427.  
  428.         /* And that's that */
  429.         return(0);
  430. }
  431.  
  432.