Subversion Repositories Games.Carmageddon

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /**
  2.         libsmacker - A C library for decoding .smk Smacker Video files
  3.         Copyright (C) 2012-2017 Greg Kennedy
  4.  
  5.         See smacker.h for more information.
  6.  
  7.         smacker.c
  8.                 Main implementation file of libsmacker.
  9.                 Open, close, query, render, advance and seek an smk
  10. */
  11.  
  12. #include "smacker.h"
  13.  
  14. /* safe malloc and free */
  15. #include "smk_malloc.h"
  16.  
  17. /* data structures */
  18. #include "smk_bitstream.h"
  19. #include "smk_hufftree.h"
  20.  
  21. /* GLOBALS */
  22. /* tree processing order */
  23. #define SMK_TREE_MMAP 0
  24. #define SMK_TREE_MCLR 1
  25. #define SMK_TREE_FULL 2
  26. #define SMK_TREE_TYPE 3
  27.  
  28. /* SMACKER DATA STRUCTURES */
  29. struct smk_t {
  30.     /* meta-info */
  31.     /* file mode: see flags, smacker.h */
  32.     unsigned char mode;
  33.  
  34.     /* microsec per frame - stored as a double to handle scaling
  35.                 (large positive millisec / frame values may overflow a ul) */
  36.     double usf;
  37.  
  38.     /* total frames */
  39.     unsigned long f;
  40.     /* does file have a ring frame? (in other words, does file loop?) */
  41.     unsigned char ring_frame;
  42.  
  43.     /* Index of current frame */
  44.     unsigned long cur_frame;
  45.  
  46.     /* SOURCE union.
  47.                 Where the data is going to be read from (or be stored),
  48.                 depending on the file mode. */
  49.     union {
  50.         struct
  51.         {
  52.             /* on-disk mode */
  53.             FILE* fp;
  54.             unsigned long* chunk_offset;
  55.         } file;
  56.  
  57.         /* in-memory mode: unprocessed chunks */
  58.         unsigned char** chunk_data;
  59.     } source;
  60.  
  61.     /* shared array of "chunk sizes"*/
  62.     unsigned long* chunk_size;
  63.  
  64.     /* Holds per-frame flags (i.e. 'keyframe') */
  65.     unsigned char* keyframe;
  66.     /* Holds per-frame type mask (e.g. 'audio track 3, 2, and palette swap') */
  67.     unsigned char* frame_type;
  68.  
  69.     /* video and audio structures */
  70.     /* Video data type: enable/disable decode switch,
  71.                 video info and flags,
  72.                 pointer to last-decoded-palette */
  73.     struct smk_video_t {
  74.         /* enable/disable decode switch */
  75.         unsigned char enable;
  76.  
  77.         /* video info */
  78.         unsigned long w;
  79.         unsigned long h;
  80.         /* Y scale mode (constants defined in smacker.h)
  81.                         0: unscaled
  82.                         1: doubled
  83.                         2: interlaced */
  84.         unsigned char y_scale_mode;
  85.  
  86.         /* version ('2' or '4') */
  87.         unsigned char v;
  88.  
  89.         /* Huffman trees */
  90.         /* unsigned long tree_size[4]; */
  91.         struct smk_huff16_t* tree[4];
  92.  
  93.         /* Palette data type: pointer to last-decoded-palette */
  94.         unsigned char palette[256][3];
  95.         /* Last-unpacked frame */
  96.         unsigned char* frame;
  97.     } video;
  98.  
  99.     /* audio structure */
  100.     struct smk_audio_t {
  101.         /* set if track exists in file */
  102.         unsigned char exists;
  103.  
  104.         /* enable/disable switch (per track) */
  105.         unsigned char enable;
  106.  
  107.         /* Info */
  108.         unsigned char channels;
  109.         unsigned char bitdepth;
  110.         unsigned long rate;
  111.         long max_buffer;
  112.  
  113.         /* compression type
  114.                         0: raw PCM
  115.                         1: SMK DPCM
  116.                         2: Bink (Perceptual), unsupported */
  117.         unsigned char compress;
  118.  
  119.         /* pointer to last-decoded-audio-buffer */
  120.         void* buffer;
  121.         unsigned long buffer_size;
  122.     } audio[7];
  123. };
  124.  
  125. union smk_read_t {
  126.     FILE* file;
  127.     unsigned char* ram;
  128. };
  129.  
  130. /* An fread wrapper: consumes N bytes, or returns -1
  131.         on failure (when size doesn't match expected) */
  132. static char smk_read_file(void* buf, const size_t size, FILE* fp) {
  133.     /* don't bother checking buf or fp, fread does it for us */
  134.     size_t bytesRead = fread(buf, 1, size, fp);
  135.     if (bytesRead != size) {
  136.         fprintf(stderr, "libsmacker::smk_read_file(buf,%lu,fp) - ERROR: Short read, %lu bytes returned\n\tReason: %s\n", (unsigned long)size, (unsigned long)bytesRead, strerror(errno));
  137.         return -1;
  138.     }
  139.     return 0;
  140. }
  141.  
  142. /* A memcpy wrapper: consumes N bytes, or returns -1
  143.         on failure (when size too low) */
  144. static char smk_read_memory(void* buf, const unsigned long size, unsigned char** p, unsigned long* p_size) {
  145.     if (size > *p_size) {
  146.         fprintf(stderr, "libsmacker::smk_read_memory(buf,%lu,p,%lu) - ERROR: Short read\n", (unsigned long)size, (unsigned long)*p_size);
  147.         return -1;
  148.     }
  149.     memcpy(buf, *p, size);
  150.     *p += size;
  151.     *p_size -= size;
  152.     return 0;
  153. }
  154.  
  155. /* Helper functions to do the reading, plus
  156.         byteswap from LE to host order */
  157. /* read n bytes from (source) into ret */
  158. #define smk_read(ret, n)                                                                                                                                       \
  159.     {                                                                                                                                                          \
  160.         if (m) {                                                                                                                                               \
  161.             r = (smk_read_file(ret, n, fp.file));                                                                                                              \
  162.         } else {                                                                                                                                               \
  163.             r = (smk_read_memory(ret, n, &fp.ram, &size));                                                                                                     \
  164.         }                                                                                                                                                      \
  165.         if (r < 0) {                                                                                                                                           \
  166.             fprintf(stderr, "libsmacker::smk_read(...) - Errors encountered on read, bailing out (file: %s, line: %lu)\n", __FILE__, (unsigned long)__LINE__); \
  167.             goto error;                                                                                                                                        \
  168.         }                                                                                                                                                      \
  169.     }
  170.  
  171. /* Calls smk_read, but returns a ul */
  172. #define smk_read_ul(p)                                                                                                              \
  173.     {                                                                                                                               \
  174.         smk_read(buf, 4);                                                                                                           \
  175.         p = ((unsigned long)buf[3] << 24) | ((unsigned long)buf[2] << 16) | ((unsigned long)buf[1] << 8) | ((unsigned long)buf[0]); \
  176.     }
  177.  
  178. /* PUBLIC FUNCTIONS */
  179. /* open an smk (from a generic Source) */
  180. static smk smk_open_generic(const unsigned char m, union smk_read_t fp, unsigned long size, const unsigned char process_mode) {
  181.     smk s = NULL;
  182.  
  183.     /* Temporary variables */
  184.     long temp_l;
  185.     unsigned long temp_u;
  186.  
  187.     /* r is used by macros above for return code */
  188.     char r;
  189.     unsigned char buf[4] = { '\0' };
  190.  
  191.     /* video hufftrees are stored as a large chunk (bitstream)
  192.                 these vars are used to load, then decode them */
  193.     unsigned char* hufftree_chunk = NULL;
  194.     unsigned long tree_size;
  195.     /* a bitstream struct */
  196.     struct smk_bit_t* bs = NULL;
  197.  
  198.     /* safe malloc the structure */
  199.     smk_malloc(s, sizeof(struct smk_t));
  200.  
  201.     /* Check for a valid signature */
  202.     smk_read(buf, 3);
  203.     if (buf[0] != 'S' || buf[1] != 'M' || buf[2] != 'K') {
  204.         fprintf(stderr, "libsmacker::smk_open_generic - ERROR: invalid SMKn signature (got: %s)\n", buf);
  205.         goto error;
  206.     }
  207.  
  208.     /* Read .smk file version */
  209.     smk_read(&s->video.v, 1);
  210.     if (s->video.v != '2' && s->video.v != '4') {
  211.         fprintf(stderr, "libsmacker::smk_open_generic - Warning: invalid SMK version %c (expected: 2 or 4)\n", s->video.v);
  212.         /* take a guess */
  213.         if (s->video.v < '4')
  214.             s->video.v = '2';
  215.         else
  216.             s->video.v = '4';
  217.         fprintf(stderr, "\tProcessing will continue as type %c\n", s->video.v);
  218.     }
  219.  
  220.     /* width, height, total num frames */
  221.     smk_read_ul(s->video.w);
  222.     smk_read_ul(s->video.h);
  223.  
  224.     smk_read_ul(s->f);
  225.  
  226.     /* frames per second calculation */
  227.     smk_read_ul(temp_u);
  228.     temp_l = (int)temp_u;
  229.     if (temp_l > 0) {
  230.         /* millisec per frame */
  231.         s->usf = temp_l * 1000;
  232.     } else if (temp_l < 0) {
  233.         /* 10 microsec per frame */
  234.         s->usf = temp_l * -10;
  235.     } else {
  236.         /* defaults to 10 usf (= 100000 microseconds) */
  237.         s->usf = 100000;
  238.     }
  239.  
  240.     /* Video flags follow.
  241.                 Ring frame is important to libsmacker.
  242.                 Y scale / Y interlace go in the Video flags.
  243.                 The user should scale appropriately. */
  244.     smk_read_ul(temp_u);
  245.     if (temp_u & 0x01) {
  246.         s->ring_frame = 1;
  247.     }
  248.     if (temp_u & 0x02) {
  249.         s->video.y_scale_mode = SMK_FLAG_Y_DOUBLE;
  250.     }
  251.     if (temp_u & 0x04) {
  252.         if (s->video.y_scale_mode == SMK_FLAG_Y_DOUBLE) {
  253.             fputs("libsmacker::smk_open_generic - Warning: SMK file specifies both Y-Double AND Y-Interlace.\n", stderr);
  254.         }
  255.         s->video.y_scale_mode = SMK_FLAG_Y_INTERLACE;
  256.     }
  257.  
  258.     /* Max buffer size for each audio track - used to pre-allocate buffers */
  259.     for (temp_l = 0; temp_l < 7; temp_l++) {
  260.         smk_read_ul(s->audio[temp_l].max_buffer);
  261.     }
  262.  
  263.     /* Read size of "hufftree chunk" - save for later. */
  264.     smk_read_ul(tree_size);
  265.  
  266.     /* "unpacked" sizes of each huff tree - we don't use
  267.                 but calling application might. */
  268.     for (temp_l = 0; temp_l < 4; temp_l++) {
  269.         /*              smk_read_ul(s->video.tree_size[temp_u]); */
  270.         smk_read_ul(temp_u);
  271.     }
  272.  
  273.     /* read audio rate data */
  274.     for (temp_l = 0; temp_l < 7; temp_l++) {
  275.         smk_read_ul(temp_u);
  276.         if (temp_u & 0x40000000) {
  277.             /* Audio track specifies "exists" flag, malloc structure and copy components. */
  278.             s->audio[temp_l].exists = 1;
  279.  
  280.             /* and for all audio tracks */
  281.             smk_malloc(s->audio[temp_l].buffer, s->audio[temp_l].max_buffer);
  282.  
  283.             if (temp_u & 0x80000000) {
  284.                 s->audio[temp_l].compress = 1;
  285.             }
  286.             s->audio[temp_l].bitdepth = ((temp_u & 0x20000000) ? 16 : 8);
  287.             s->audio[temp_l].channels = ((temp_u & 0x10000000) ? 2 : 1);
  288.             if (temp_u & 0x0c000000) {
  289.                 fprintf(stderr, "libsmacker::smk_open_generic - Warning: audio track %ld is compressed with Bink (perceptual) Audio Codec: this is currently unsupported by libsmacker\n", temp_l);
  290.                 s->audio[temp_l].compress = 2;
  291.             }
  292.             /* Bits 25 & 24 are unused. */
  293.             s->audio[temp_l].rate = (temp_u & 0x00FFFFFF);
  294.         }
  295.     }
  296.  
  297.     /* Skip over Dummy field */
  298.     smk_read_ul(temp_u);
  299.  
  300.     /* FrameSizes and Keyframe marker are stored together. */
  301.     smk_malloc(s->keyframe, (s->f + s->ring_frame));
  302.     smk_malloc(s->chunk_size, (s->f + s->ring_frame) * sizeof(unsigned long));
  303.  
  304.     for (temp_u = 0; temp_u < (s->f + s->ring_frame); temp_u++) {
  305.         smk_read_ul(s->chunk_size[temp_u]);
  306.  
  307.         /* Set Keyframe */
  308.         if (s->chunk_size[temp_u] & 0x01) {
  309.             s->keyframe[temp_u] = 1;
  310.         }
  311.         /* Bits 1 is used, but the purpose is unknown. */
  312.         s->chunk_size[temp_u] &= 0xFFFFFFFC;
  313.     }
  314.  
  315.     /* That was easy... Now read FrameTypes! */
  316.     smk_malloc(s->frame_type, (s->f + s->ring_frame));
  317.     for (temp_u = 0; temp_u < (s->f + s->ring_frame); temp_u++) {
  318.         smk_read(&s->frame_type[temp_u], 1);
  319.     }
  320.  
  321.     /* HuffmanTrees
  322.                 We know the sizes already: read and assemble into
  323.                 something actually parse-able at run-time */
  324.     smk_malloc(hufftree_chunk, tree_size);
  325.     smk_read(hufftree_chunk, tree_size);
  326.  
  327.     /* set up a Bitstream */
  328.     bs = smk_bs_init(hufftree_chunk, tree_size);
  329.     /* create some tables */
  330.     for (temp_u = 0; temp_u < 4; temp_u++) {
  331.         smk_huff16_build(bs, s->video.tree[temp_u]);
  332.     }
  333.  
  334.     /* clean up */
  335.     smk_free(bs);
  336.     smk_free(hufftree_chunk);
  337.  
  338.     /* Go ahead and malloc storage for the video frame */
  339.     smk_malloc(s->video.frame, s->video.w * s->video.h);
  340.  
  341.     /* final processing: depending on ProcessMode, handle what to do with rest of file data */
  342.     s->mode = process_mode;
  343.  
  344.     /* Handle the rest of the data.
  345.                 For MODE_MEMORY, read the chunks and store */
  346.     if (s->mode == SMK_MODE_MEMORY) {
  347.         smk_malloc(s->source.chunk_data, (s->f + s->ring_frame) * sizeof(unsigned char*));
  348.         for (temp_u = 0; temp_u < (s->f + s->ring_frame); temp_u++) {
  349.             smk_malloc(s->source.chunk_data[temp_u], s->chunk_size[temp_u]);
  350.             smk_read(s->source.chunk_data[temp_u], s->chunk_size[temp_u]);
  351.         }
  352.     } else {
  353.         /* MODE_STREAM: don't read anything now, just precompute offsets.
  354.                         use fseek to verify that the file is "complete" */
  355.         smk_malloc(s->source.file.chunk_offset, (s->f + s->ring_frame) * sizeof(unsigned long));
  356.         for (temp_u = 0; temp_u < (s->f + s->ring_frame); temp_u++) {
  357.             s->source.file.chunk_offset[temp_u] = ftell(fp.file);
  358.             if (fseek(fp.file, s->chunk_size[temp_u], SEEK_CUR)) {
  359.                 fprintf(stderr, "libsmacker::smk_open - ERROR: fseek to frame %lu not OK.\n", temp_u);
  360.                 perror("\tError reported was");
  361.                 goto error;
  362.             }
  363.         }
  364.     }
  365.  
  366.     return s;
  367.  
  368. error:
  369.     smk_free(bs);
  370.     smk_free(hufftree_chunk);
  371.     smk_close(s);
  372.     return NULL;
  373. }
  374.  
  375. /* open an smk (from a memory buffer) */
  376. smk smk_open_memory(const unsigned char* buffer, const unsigned long size) {
  377.     smk s = NULL;
  378.  
  379.     union smk_read_t fp;
  380.  
  381.     smk_assert(buffer);
  382.  
  383.     /* set up the read union for Memory mode */
  384.     fp.ram = (unsigned char*)buffer;
  385.  
  386.     if (!(s = smk_open_generic(0, fp, size, SMK_MODE_MEMORY))) {
  387.         fprintf(stderr, "libsmacker::smk_open_memory(buffer,%lu) - ERROR: Fatal error in smk_open_generic, returning NULL.\n", size);
  388.     }
  389.  
  390.     /* fall through, return s or null */
  391. error:
  392.     return s;
  393. }
  394.  
  395. /* open an smk (from a file) */
  396. smk smk_open_filepointer(FILE* file, const unsigned char mode) {
  397.     smk s = NULL;
  398.     union smk_read_t fp;
  399.  
  400.     smk_assert(file);
  401.  
  402.     /* Copy file ptr to internal union */
  403.     fp.file = file;
  404.  
  405.     if (!(s = smk_open_generic(1, fp, 0, mode))) {
  406.         fprintf(stderr, "libsmacker::smk_open_filepointer(file,%u) - ERROR: Fatal error in smk_open_generic, returning NULL.\n", mode);
  407.         fclose(fp.file);
  408.         goto error;
  409.     }
  410.  
  411.     if (mode == SMK_MODE_MEMORY) {
  412.         fclose(fp.file);
  413.     } else {
  414.         s->source.file.fp = fp.file;
  415.     }
  416.  
  417.     /* fall through, return s or null */
  418. error:
  419.     return s;
  420. }
  421.  
  422. /* open an smk (from a file) */
  423. smk smk_open_file(const char* filename, const unsigned char mode) {
  424.     FILE* fp;
  425.  
  426.     smk_assert(filename);
  427.  
  428.     if (!(fp = fopen(filename, "rb"))) {
  429.         // Jeff commented out error messages
  430.         // fprintf(stderr,"libsmacker::smk_open_file(%s,%u) - ERROR: could not open file (errno: %d)\n",filename,mode,errno);
  431.         // perror ("\tError reported was");
  432.         goto error;
  433.     }
  434.  
  435.     /* kick processing to smk_open_filepointer */
  436.     return smk_open_filepointer(fp, mode);
  437.  
  438.     /* fall through, return s or null */
  439. error:
  440.     return NULL;
  441. }
  442.  
  443. /* close out an smk file and clean up memory */
  444. void smk_close(smk s) {
  445.     unsigned long u;
  446.  
  447.     smk_assert(s);
  448.  
  449.     /* free video sub-components */
  450.     {
  451.         for (u = 0; u < 4; u++) {
  452.             if (s->video.tree[u])
  453.                 smk_huff16_free(s->video.tree[u]);
  454.         }
  455.         smk_free(s->video.frame);
  456.     }
  457.  
  458.     /* free audio sub-components */
  459.     for (u = 0; u < 7; u++) {
  460.         smk_free(s->audio[u].buffer);
  461.     }
  462.  
  463.     smk_free(s->keyframe);
  464.     smk_free(s->frame_type);
  465.  
  466.     if (s->mode == SMK_MODE_DISK) {
  467.         /* disk-mode */
  468.         if (s->source.file.fp) {
  469.             fclose(s->source.file.fp);
  470.         }
  471.         smk_free(s->source.file.chunk_offset);
  472.     } else {
  473.         /* mem-mode */
  474.         if (s->source.chunk_data != NULL) {
  475.             for (u = 0; u < (s->f + s->ring_frame); u++) {
  476.                 smk_free(s->source.chunk_data[u]);
  477.             }
  478.             smk_free(s->source.chunk_data);
  479.         }
  480.     }
  481.     smk_free(s->chunk_size);
  482.  
  483.     smk_free(s);
  484.  
  485. error:;
  486. }
  487.  
  488. /* tell some info about the file */
  489. char smk_info_all(const smk object, unsigned long* frame, unsigned long* frame_count, double* usf) {
  490.     /* sanity check */
  491.     smk_assert(object);
  492.     if (!frame && !frame_count && !usf) {
  493.         fputs("libsmacker::smk_info_all(object,frame,frame_count,usf) - ERROR: Request for info with all-NULL return references\n", stderr);
  494.         goto error;
  495.     }
  496.     if (frame)
  497.         *frame = (object->cur_frame % object->f);
  498.  
  499.     if (frame_count)
  500.         *frame_count = object->f;
  501.  
  502.     if (usf)
  503.         *usf = object->usf;
  504.  
  505.     return 0;
  506.  
  507. error:
  508.     return -1;
  509. }
  510.  
  511. char smk_info_video(const smk object, unsigned long* w, unsigned long* h, unsigned char* y_scale_mode) {
  512.     /* sanity check */
  513.     smk_assert(object);
  514.     if (!w && !h && !y_scale_mode) {
  515.         fputs("libsmacker::smk_info_all(object,w,h,y_scale_mode) - ERROR: Request for info with all-NULL return references\n", stderr);
  516.         return -1;
  517.     }
  518.  
  519.     if (w)
  520.         *w = object->video.w;
  521.  
  522.     if (h)
  523.         *h = object->video.h;
  524.  
  525.     if (y_scale_mode)
  526.         *y_scale_mode = object->video.y_scale_mode;
  527.  
  528.     return 0;
  529.  
  530. error:
  531.     return -1;
  532. }
  533.  
  534. char smk_info_audio(const smk object, unsigned char* track_mask, unsigned char channels[7], unsigned char bitdepth[7], unsigned long audio_rate[7]) {
  535.     unsigned char i;
  536.  
  537.     /* sanity check */
  538.     smk_assert(object);
  539.  
  540.     if (!track_mask && !channels && !bitdepth && !audio_rate) {
  541.         fputs("libsmacker::smk_info_audio(object,track_mask,channels,bitdepth,audio_rate) - ERROR: Request for info with all-NULL return references\n", stderr);
  542.         return -1;
  543.     }
  544.     if (track_mask) {
  545.         *track_mask = ((object->audio[0].exists) | ((object->audio[1].exists) << 1) | ((object->audio[2].exists) << 2) | ((object->audio[3].exists) << 3) | ((object->audio[4].exists) << 4) | ((object->audio[5].exists) << 5) | ((object->audio[6].exists) << 6));
  546.     }
  547.     if (channels) {
  548.         for (i = 0; i < 7; i++) {
  549.             channels[i] = object->audio[i].channels;
  550.         }
  551.     }
  552.     if (bitdepth) {
  553.         for (i = 0; i < 7; i++) {
  554.             bitdepth[i] = object->audio[i].bitdepth;
  555.         }
  556.     }
  557.     if (audio_rate) {
  558.         for (i = 0; i < 7; i++) {
  559.             audio_rate[i] = object->audio[i].rate;
  560.         }
  561.     }
  562.     return 0;
  563.  
  564. error:
  565.     return -1;
  566. }
  567.  
  568. /* Enable-disable switches */
  569. char smk_enable_all(smk object, const unsigned char mask) {
  570.     unsigned char i;
  571.  
  572.     /* sanity check */
  573.     smk_assert(object);
  574.  
  575.     /* set video-enable */
  576.     object->video.enable = (mask & 0x80);
  577.  
  578.     for (i = 0; i < 7; i++) {
  579.         if (object->audio[i].exists) {
  580.             object->audio[i].enable = (mask & (1 << i));
  581.         }
  582.     }
  583.  
  584.     return 0;
  585.  
  586. error:
  587.     return -1;
  588. }
  589.  
  590. char smk_enable_video(smk object, const unsigned char enable) {
  591.     /* sanity check */
  592.     smk_assert(object);
  593.  
  594.     object->video.enable = enable;
  595.     return 0;
  596.  
  597. error:
  598.     return -1;
  599. }
  600.  
  601. char smk_enable_audio(smk object, const unsigned char track, const unsigned char enable) {
  602.     /* sanity check */
  603.     smk_assert(object);
  604.  
  605.     object->audio[track].enable = enable;
  606.     return 0;
  607.  
  608. error:
  609.     return -1;
  610. }
  611.  
  612. const unsigned char* smk_get_palette(const smk object) {
  613.     smk_assert(object);
  614.  
  615.     return (unsigned char*)object->video.palette;
  616.  
  617. error:
  618.     return NULL;
  619. }
  620. const unsigned char* smk_get_video(const smk object) {
  621.     smk_assert(object);
  622.  
  623.     return object->video.frame;
  624.  
  625. error:
  626.     return NULL;
  627. }
  628. const unsigned char* smk_get_audio(const smk object, const unsigned char t) {
  629.     smk_assert(object);
  630.  
  631.     return object->audio[t].buffer;
  632.  
  633. error:
  634.     return NULL;
  635. }
  636. unsigned long smk_get_audio_size(const smk object, const unsigned char t) {
  637.     smk_assert(object);
  638.  
  639.     return object->audio[t].buffer_size;
  640.  
  641. error:
  642.     return 0;
  643. }
  644.  
  645. /* Decompresses a palette-frame. */
  646. static char smk_render_palette(struct smk_video_t* s, unsigned char* p, unsigned long size) {
  647.     /* Index into palette */
  648.     unsigned short i = 0;
  649.     /* Helper variables */
  650.     unsigned short count, src;
  651.  
  652.     static unsigned char oldPalette[256][3];
  653.  
  654.     /* Smacker palette map: smk colors are 6-bit, this table expands them to 8. */
  655.     const unsigned char palmap[64] = {
  656.         0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C,
  657.         0x20, 0x24, 0x28, 0x2C, 0x30, 0x34, 0x38, 0x3C,
  658.         0x41, 0x45, 0x49, 0x4D, 0x51, 0x55, 0x59, 0x5D,
  659.         0x61, 0x65, 0x69, 0x6D, 0x71, 0x75, 0x79, 0x7D,
  660.         0x82, 0x86, 0x8A, 0x8E, 0x92, 0x96, 0x9A, 0x9E,
  661.         0xA2, 0xA6, 0xAA, 0xAE, 0xB2, 0xB6, 0xBA, 0xBE,
  662.         0xC3, 0xC7, 0xCB, 0xCF, 0xD3, 0xD7, 0xDB, 0xDF,
  663.         0xE3, 0xE7, 0xEB, 0xEF, 0xF3, 0xF7, 0xFB, 0xFF
  664.     };
  665.  
  666.     /* sanity check */
  667.     smk_assert(s);
  668.     smk_assert(p);
  669.  
  670.     // Copy palette to old palette
  671.     memcpy(oldPalette, s->palette, 256 * 3);
  672.  
  673.     /* Loop until palette is complete, or we are out of bytes to process */
  674.     while ((i < 256) && (size > 0)) {
  675.         if ((*p) & 0x80) {
  676.             /* 0x80: Skip block
  677.                                 (preserve C+1 palette entries from previous palette) */
  678.             count = ((*p) & 0x7F) + 1;
  679.             p++;
  680.             size--;
  681.  
  682.             /* check for overflow condition */
  683.             if (i + count > 256) {
  684.                 fprintf(stderr, "libsmacker::palette_render(s,p,size) - ERROR: overflow, 0x80 attempt to skip %d entries from %d\n", count, i);
  685.                 goto error;
  686.             }
  687.  
  688.             /* finally: advance the index. */
  689.             i += count;
  690.         } else if ((*p) & 0x40) {
  691.             /* 0x40: Color-shift block
  692.                                 Copy (c + 1) color entries of the previous palette,
  693.                                 starting from entry (s),
  694.                                 to the next entries of the new palette. */
  695.             if (size < 2) {
  696.                 fputs("libsmacker::palette_render(s,p,size) - ERROR: 0x40 ran out of bytes for copy\n", stderr);
  697.                 goto error;
  698.             }
  699.  
  700.             /* pick "count" items to copy */
  701.             count = ((*p) & 0x3F) + 1;
  702.             p++;
  703.             size--;
  704.  
  705.             /* start offset of old palette */
  706.             src = *p;
  707.             p++;
  708.             size--;
  709.  
  710.             /* overflow: see if we write/read beyond 256colors, or overwrite own palette */
  711.             if (i + count > 256 || src + count > 256 || (src < i && src + count > i)) {
  712.                 fprintf(stderr, "libsmacker::palette_render(s,p,size) - ERROR: overflow, 0x40 attempt to copy %d entries from %d to %d\n", count, src, i);
  713.                 goto error;
  714.             }
  715.  
  716.             /* OK!  Copy the color-palette entries. */
  717.             memmove(&s->palette[i][0], &oldPalette[src][0], count * 3);
  718.  
  719.             i += count;
  720.         } else {
  721.             /* 0x00: Set Color block
  722.                                 Direct-set the next 3 bytes for palette index */
  723.             if (size < 3) {
  724.                 fprintf(stderr, "libsmacker::palette_render - ERROR: 0x3F ran out of bytes for copy, size=%lu\n", size);
  725.                 goto error;
  726.             }
  727.  
  728.             for (count = 0; count < 3; count++) {
  729.                 if (*p > 0x3F) {
  730.                     fprintf(stderr, "libsmacker::palette_render - ERROR: palette index exceeds 0x3F (entry [%u][%u])\n", i, count);
  731.                     goto error;
  732.                 }
  733.                 s->palette[i][count] = palmap[*p];
  734.                 p++;
  735.                 size--;
  736.             }
  737.             i++;
  738.         }
  739.     }
  740.  
  741.     if (i < 256) {
  742.         fprintf(stderr, "libsmacker::palette_render - ERROR: did not completely fill palette (idx=%u)\n", i);
  743.         goto error;
  744.     }
  745.  
  746.     return 0;
  747.  
  748. error:
  749.     /* Error, return -1
  750.                 The new palette probably has errors but is preferrable to a black screen */
  751.     return -1;
  752. }
  753.  
  754. static char smk_render_video(struct smk_video_t* s, unsigned char* p, unsigned int size) {
  755.     unsigned char* t = s->frame;
  756.     unsigned char s1, s2;
  757.     unsigned short temp;
  758.     unsigned long i, j, k, row, col, skip;
  759.  
  760.     /* used for video decoding */
  761.     struct smk_bit_t* bs = NULL;
  762.  
  763.     /* results from a tree lookup */
  764.     long unpack;
  765.  
  766.     /* unpack, broken into pieces */
  767.     unsigned char type;
  768.     unsigned char blocklen;
  769.     unsigned char typedata;
  770.     char bit;
  771.  
  772.     const unsigned short sizetable[64] = {
  773.         1, 2, 3, 4, 5, 6, 7, 8,
  774.         9, 10, 11, 12, 13, 14, 15, 16,
  775.         17, 18, 19, 20, 21, 22, 23, 24,
  776.         25, 26, 27, 28, 29, 30, 31, 32,
  777.         33, 34, 35, 36, 37, 38, 39, 40,
  778.         41, 42, 43, 44, 45, 46, 47, 48,
  779.         49, 50, 51, 52, 53, 54, 55, 56,
  780.         57, 58, 59, 128, 256, 512, 1024, 2048
  781.     };
  782.  
  783.     /* sanity check */
  784.     smk_assert(s);
  785.     smk_assert(p);
  786.  
  787.     row = 0;
  788.     col = 0;
  789.  
  790.     /* Set up a bitstream for video unpacking */
  791.     /* We could check the return code but it will only fail if p is null and we already verified that. */
  792.     bs = smk_bs_init(p, size);
  793.  
  794.     /* Reset the cache on all bigtrees */
  795.     smk_huff16_reset(s->tree[0]);
  796.     smk_huff16_reset(s->tree[1]);
  797.     smk_huff16_reset(s->tree[2]);
  798.     smk_huff16_reset(s->tree[3]);
  799.  
  800.     while (row < s->h) {
  801.         smk_huff16_lookup(bs, s->tree[SMK_TREE_TYPE], unpack);
  802.  
  803.         type = ((unpack & 0x0003));
  804.         blocklen = ((unpack & 0x00FC) >> 2);
  805.         typedata = ((unpack & 0xFF00) >> 8);
  806.  
  807.         /* support for v4 full-blocks */
  808.         if (type == 1 && s->v == '4') {
  809.             smk_bs_read_1(bs, bit);
  810.             if (bit) {
  811.                 type = 4;
  812.             } else {
  813.                 smk_bs_read_1(bs, bit);
  814.                 if (bit) {
  815.                     type = 5;
  816.                 }
  817.             }
  818.         }
  819.  
  820.         for (j = 0; (j < sizetable[blocklen]) && (row < s->h); j++) {
  821.             skip = (row * s->w) + col;
  822.             switch (type) {
  823.             case 0:
  824.                 smk_huff16_lookup(bs, s->tree[SMK_TREE_MCLR], unpack);
  825.                 s1 = (unpack & 0xFF00) >> 8;
  826.                 s2 = (unpack & 0x00FF);
  827.                 smk_huff16_lookup(bs, s->tree[SMK_TREE_MMAP], unpack);
  828.  
  829.                 temp = 0x01;
  830.                 for (k = 0; k < 4; k++) {
  831.                     for (i = 0; i < 4; i++) {
  832.                         if (unpack & temp) {
  833.                             t[skip + i] = s1;
  834.                         } else {
  835.                             t[skip + i] = s2;
  836.                         }
  837.                         temp = temp << 1;
  838.                     }
  839.                     skip += s->w;
  840.                 }
  841.                 break;
  842.  
  843.             case 1: /* FULL BLOCK */
  844.                 for (k = 0; k < 4; k++) {
  845.                     smk_huff16_lookup(bs, s->tree[SMK_TREE_FULL], unpack);
  846.                     t[skip + 3] = ((unpack & 0xFF00) >> 8);
  847.                     t[skip + 2] = (unpack & 0x00FF);
  848.                     smk_huff16_lookup(bs, s->tree[SMK_TREE_FULL], unpack);
  849.                     t[skip + 1] = ((unpack & 0xFF00) >> 8);
  850.                     t[skip] = (unpack & 0x00FF);
  851.                     skip += s->w;
  852.                 }
  853.                 break;
  854.             case 2: /* VOID BLOCK */
  855.                 /* break;
  856.                                         if (s->frame)
  857.                                         {
  858.                                                 memcpy(&t[skip], &s->frame[skip], 4);
  859.                                                 skip += s->w;
  860.                                                 memcpy(&t[skip], &s->frame[skip], 4);
  861.                                                 skip += s->w;
  862.                                                 memcpy(&t[skip], &s->frame[skip], 4);
  863.                                                 skip += s->w;
  864.                                                 memcpy(&t[skip], &s->frame[skip], 4);
  865.                                         } */
  866.                 break;
  867.             case 3: /* SOLID BLOCK */
  868.                 memset(&t[skip], typedata, 4);
  869.                 skip += s->w;
  870.                 memset(&t[skip], typedata, 4);
  871.                 skip += s->w;
  872.                 memset(&t[skip], typedata, 4);
  873.                 skip += s->w;
  874.                 memset(&t[skip], typedata, 4);
  875.                 break;
  876.             case 4: /* V4 DOUBLE BLOCK */
  877.                 for (k = 0; k < 2; k++) {
  878.                     smk_huff16_lookup(bs, s->tree[SMK_TREE_FULL], unpack);
  879.                     for (i = 0; i < 2; i++) {
  880.                         memset(&t[skip + 2], (unpack & 0xFF00) >> 8, 2);
  881.                         memset(&t[skip], (unpack & 0x00FF), 2);
  882.                         skip += s->w;
  883.                     }
  884.                 }
  885.                 break;
  886.             case 5: /* V4 HALF BLOCK */
  887.                 for (k = 0; k < 2; k++) {
  888.                     smk_huff16_lookup(bs, s->tree[SMK_TREE_FULL], unpack);
  889.                     t[skip + 3] = ((unpack & 0xFF00) >> 8);
  890.                     t[skip + 2] = (unpack & 0x00FF);
  891.                     t[skip + s->w + 3] = ((unpack & 0xFF00) >> 8);
  892.                     t[skip + s->w + 2] = (unpack & 0x00FF);
  893.                     smk_huff16_lookup(bs, s->tree[SMK_TREE_FULL], unpack);
  894.                     t[skip + 1] = ((unpack & 0xFF00) >> 8);
  895.                     t[skip] = (unpack & 0x00FF);
  896.                     t[skip + s->w + 1] = ((unpack & 0xFF00) >> 8);
  897.                     t[skip + s->w] = (unpack & 0x00FF);
  898.                     skip += (s->w << 1);
  899.                 }
  900.                 break;
  901.             }
  902.             col += 4;
  903.             if (col >= s->w) {
  904.                 col = 0;
  905.                 row += 4;
  906.             }
  907.         }
  908.     }
  909.  
  910.     smk_free(bs);
  911.  
  912.     return 0;
  913.  
  914. error:
  915.     smk_free(bs);
  916.     return -1;
  917. }
  918.  
  919. /* Decompress audio track i. */
  920. static char smk_render_audio(struct smk_audio_t* s, unsigned char* p, unsigned long size) {
  921.     unsigned int j, k;
  922.     unsigned char* t = s->buffer;
  923.     struct smk_bit_t* bs = NULL;
  924.  
  925.     char bit;
  926.     short unpack, unpack2;
  927.  
  928.     /* used for audio decoding */
  929.     struct smk_huff8_t* aud_tree[4] = { NULL, NULL, NULL, NULL };
  930.  
  931.     /* sanity check */
  932.     smk_assert(s);
  933.     smk_assert(p);
  934.  
  935.     if (!s->compress) {
  936.         /* Raw PCM data, update buffer size and malloc */
  937.         s->buffer_size = size;
  938.  
  939.         memcpy(t, p, size);
  940.     } else if (s->compress == 1) {
  941.         /* SMACKER DPCM compression */
  942.         /* need at least 4 bytes to process */
  943.         if (size < 4) {
  944.             fputs("libsmacker::smk_render_audio() - ERROR: need 4 bytes to get unpacked output buffer size.\n", stderr);
  945.             goto error;
  946.         }
  947.         /* chunk is compressed (huff-compressed dpcm), retrieve unpacked buffer size */
  948.         s->buffer_size = ((unsigned int)p[3] << 24) | ((unsigned int)p[2] << 16) | ((unsigned int)p[1] << 8) | ((unsigned int)p[0]);
  949.  
  950.         p += 4;
  951.         size -= 4;
  952.  
  953.         /* Compressed audio: must unpack here */
  954.         /*  Set up a bitstream */
  955.         bs = smk_bs_init(p, size);
  956.  
  957.         smk_bs_read_1(bs, bit);
  958.  
  959.         if (!bit) {
  960.             fputs("libsmacker::smk_render_audio - ERROR: initial get_bit returned 0\n", stderr);
  961.             goto error;
  962.         }
  963.  
  964.         smk_bs_read_1(bs, bit);
  965.         if (s->channels != (bit == 1 ? 2 : 1)) {
  966.             fputs("libsmacker::smk_render - ERROR: mono/stereo mismatch\n", stderr);
  967.         }
  968.         smk_bs_read_1(bs, bit);
  969.         if (s->bitdepth != (bit == 1 ? 16 : 8)) {
  970.             fputs("libsmacker::smk_render - ERROR: 8-/16-bit mismatch\n", stderr);
  971.         }
  972.  
  973.         /* build the trees */
  974.         smk_huff8_build(bs, aud_tree[0]);
  975.         j = 1;
  976.         k = 1;
  977.         if (s->bitdepth == 16) {
  978.             smk_huff8_build(bs, aud_tree[1]);
  979.             k = 2;
  980.         }
  981.         if (s->channels == 2) {
  982.             smk_huff8_build(bs, aud_tree[2]);
  983.             j = 2;
  984.             k = 2;
  985.             if (s->bitdepth == 16) {
  986.                 smk_huff8_build(bs, aud_tree[3]);
  987.                 k = 4;
  988.             }
  989.         }
  990.  
  991.         /* read initial sound level */
  992.         if (s->channels == 2) {
  993.             smk_bs_read_8(bs, unpack);
  994.             if (s->bitdepth == 16) {
  995.                 smk_bs_read_8(bs, ((short*)t)[1])((short*)t)[1] |= (unpack << 8);
  996.             } else {
  997.                 ((unsigned char*)t)[1] = (unsigned char)unpack;
  998.             }
  999.         }
  1000.         smk_bs_read_8(bs, unpack);
  1001.         if (s->bitdepth == 16) {
  1002.             smk_bs_read_8(bs, ((short*)t)[0])((short*)t)[0] |= (unpack << 8);
  1003.         } else {
  1004.             ((unsigned char*)t)[0] = (unsigned char)unpack;
  1005.         }
  1006.  
  1007.         /* All set: let's read some DATA! */
  1008.         while (k < s->buffer_size) {
  1009.             if (s->bitdepth == 8) {
  1010.                 smk_huff8_lookup(bs, aud_tree[0], unpack);
  1011.                 ((unsigned char*)t)[j] = (char)unpack + ((unsigned char*)t)[j - s->channels];
  1012.                 j++;
  1013.                 k++;
  1014.             } else {
  1015.                 smk_huff8_lookup(bs, aud_tree[0], unpack);
  1016.                 smk_huff8_lookup(bs, aud_tree[1], unpack2);
  1017.                 ((short*)t)[j] = (short)(unpack | (unpack2 << 8)) + ((short*)t)[j - s->channels];
  1018.                 j++;
  1019.                 k += 2;
  1020.             }
  1021.             if (s->channels == 2) {
  1022.                 if (s->bitdepth == 8) {
  1023.                     smk_huff8_lookup(bs, aud_tree[2], unpack);
  1024.                     ((unsigned char*)t)[j] = (char)unpack + ((unsigned char*)t)[j - 2];
  1025.                     j++;
  1026.                     k++;
  1027.                 } else {
  1028.                     smk_huff8_lookup(bs, aud_tree[2], unpack);
  1029.                     smk_huff8_lookup(bs, aud_tree[3], unpack2);
  1030.                     ((short*)t)[j] = (short)(unpack | (unpack2 << 8)) + ((short*)t)[j - 2];
  1031.                     j++;
  1032.                     k += 2;
  1033.                 }
  1034.             }
  1035.         }
  1036.  
  1037.         /* All done with the trees, free them. */
  1038.         for (j = 0; j < 4; j++) {
  1039.             if (aud_tree[j]) {
  1040.                 smk_huff8_free(aud_tree[j]);
  1041.             }
  1042.         }
  1043.  
  1044.         /* free bitstream */
  1045.         smk_free(bs);
  1046.     }
  1047.  
  1048.     return 0;
  1049.  
  1050. error:
  1051.     /* All done with the trees, free them. */
  1052.     for (j = 0; j < 4; j++) {
  1053.         if (aud_tree[j]) {
  1054.             smk_huff8_free(aud_tree[j]);
  1055.         }
  1056.     }
  1057.  
  1058.     smk_free(bs);
  1059.  
  1060.     return -1;
  1061. }
  1062.  
  1063. /* "Renders" (unpacks) the frame at cur_frame
  1064.         Preps all the image and audio pointers */
  1065. static char smk_render(smk s) {
  1066.     unsigned long i, size;
  1067.     unsigned char *buffer = NULL, *p, track;
  1068.  
  1069.     /* sanity check */
  1070.     smk_assert(s);
  1071.  
  1072.     /* Retrieve current chunk_size for this frame. */
  1073.     if (!(i = s->chunk_size[s->cur_frame])) {
  1074.         fprintf(stderr, "libsmacker::smk_render(s) - Warning: frame %lu: chunk_size is 0.\n", s->cur_frame);
  1075.         goto error;
  1076.     }
  1077.  
  1078.     if (s->mode == SMK_MODE_DISK) {
  1079.         /* Skip to frame in file */
  1080.         if (fseek(s->source.file.fp, s->source.file.chunk_offset[s->cur_frame], SEEK_SET)) {
  1081.             fprintf(stderr, "libsmacker::smk_render(s) - ERROR: fseek to frame %lu (offset %lu) failed.\n", s->cur_frame, s->source.file.chunk_offset[s->cur_frame]);
  1082.             perror("\tError reported was");
  1083.             goto error;
  1084.         }
  1085.  
  1086.         /* In disk-streaming mode: make way for our incoming chunk buffer */
  1087.         smk_malloc(buffer, i);
  1088.  
  1089.         /* Read into buffer */
  1090.         if (smk_read_file(buffer, s->chunk_size[s->cur_frame], s->source.file.fp) < 0) {
  1091.             fprintf(stderr, "libsmacker::smk_render(s) - ERROR: frame %lu (offset %lu): smk_read had errors.\n", s->cur_frame, s->source.file.chunk_offset[s->cur_frame]);
  1092.             goto error;
  1093.         }
  1094.     } else {
  1095.         /* Just point buffer at the right place */
  1096.         if (!s->source.chunk_data[s->cur_frame]) {
  1097.             fprintf(stderr, "libsmacker::smk_render(s) - ERROR: frame %lu: memory chunk is a NULL pointer.\n", s->cur_frame);
  1098.             goto error;
  1099.         }
  1100.         buffer = s->source.chunk_data[s->cur_frame];
  1101.     }
  1102.  
  1103.     p = buffer;
  1104.  
  1105.     /* Palette record first */
  1106.     if (s->frame_type[s->cur_frame] & 0x01) {
  1107.         /* need at least 1 byte to process */
  1108.         if (!i) {
  1109.             fprintf(stderr, "libsmacker::smk_render(s) - ERROR: frame %lu: insufficient data for a palette rec.\n", s->cur_frame);
  1110.             goto error;
  1111.         }
  1112.  
  1113.         /* Byte 1 in block, times 4, tells how many
  1114.                         subsequent bytes are present */
  1115.         size = 4 * (*p);
  1116.  
  1117.         /* If video rendering enabled, kick this off for decode. */
  1118.         if (s->video.enable) {
  1119.             smk_render_palette(&(s->video), p + 1, size - 1);
  1120.         }
  1121.         p += size;
  1122.         i -= size;
  1123.     }
  1124.  
  1125.     /* Unpack audio chunks */
  1126.     for (track = 0; track < 7; track++) {
  1127.         if (s->frame_type[s->cur_frame] & (0x02 << track)) {
  1128.             /* need at least 4 byte to process */
  1129.             if (i < 4) {
  1130.                 fprintf(stderr, "libsmacker::smk_render(s) - ERROR: frame %lu: insufficient data for audio[%u] rec.\n", s->cur_frame, track);
  1131.                 goto error;
  1132.             }
  1133.  
  1134.             /* First 4 bytes in block tell how many
  1135.                                 subsequent bytes are present */
  1136.             size = (((unsigned int)p[3] << 24) | ((unsigned int)p[2] << 16) | ((unsigned int)p[1] << 8) | ((unsigned int)p[0]));
  1137.  
  1138.             /* If audio rendering enabled, kick this off for decode. */
  1139.             if (s->audio[track].enable) {
  1140.                 smk_render_audio(&s->audio[track], p + 4, size - 4);
  1141.             }
  1142.             p += size;
  1143.             i -= size;
  1144.         } else {
  1145.             s->audio[track].buffer_size = 0;
  1146.         }
  1147.     }
  1148.  
  1149.     /* Unpack video chunk */
  1150.     if (s->video.enable) {
  1151.         smk_render_video(&(s->video), p, i);
  1152.     }
  1153.  
  1154.     if (s->mode == SMK_MODE_DISK) {
  1155.         /* Remember that buffer we allocated?  Trash it */
  1156.         smk_free(buffer);
  1157.     }
  1158.  
  1159.     return 0;
  1160.  
  1161. error:
  1162.     if (s->mode == SMK_MODE_DISK) {
  1163.         /* Remember that buffer we allocated?  Trash it */
  1164.         smk_free(buffer);
  1165.     }
  1166.  
  1167.     return -1;
  1168. }
  1169.  
  1170. /* rewind to first frame and unpack */
  1171. char smk_first(smk s) {
  1172.     smk_assert(s);
  1173.  
  1174.     s->cur_frame = 0;
  1175.     if (smk_render(s) < 0) {
  1176.         fprintf(stderr, "libsmacker::smk_first(s) - Warning: frame %lu: smk_render returned errors.\n", s->cur_frame);
  1177.         goto error;
  1178.     }
  1179.  
  1180.     if (s->f == 1)
  1181.         return SMK_LAST;
  1182.     return SMK_MORE;
  1183.  
  1184. error:
  1185.     return -1;
  1186. }
  1187.  
  1188. /* advance to next frame */
  1189. char smk_next(smk s) {
  1190.     smk_assert(s);
  1191.  
  1192.     if (s->cur_frame + 1 < (s->f + s->ring_frame)) {
  1193.         s->cur_frame++;
  1194.         if (smk_render(s) < 0) {
  1195.             fprintf(stderr, "libsmacker::smk_next(s) - Warning: frame %lu: smk_render returned errors.\n", s->cur_frame);
  1196.             goto error;
  1197.         }
  1198.         if (s->cur_frame + 1 == (s->f + s->ring_frame)) {
  1199.             return SMK_LAST;
  1200.         }
  1201.         return SMK_MORE;
  1202.     } else if (s->ring_frame) {
  1203.         s->cur_frame = 1;
  1204.         if (smk_render(s) < 0) {
  1205.             fprintf(stderr, "libsmacker::smk_next(s) - Warning: frame %lu: smk_render returned errors.\n", s->cur_frame);
  1206.             goto error;
  1207.         }
  1208.         if (s->cur_frame + 1 == (s->f + s->ring_frame)) {
  1209.             return SMK_LAST;
  1210.         }
  1211.         return SMK_MORE;
  1212.     }
  1213.     return SMK_DONE;
  1214.  
  1215. error:
  1216.     return -1;
  1217. }
  1218.  
  1219. /* seek to a keyframe in an smk */
  1220. char smk_seek_keyframe(smk s, unsigned long f) {
  1221.     smk_assert(s);
  1222.  
  1223.     /* rewind (or fast forward!) exactly to f */
  1224.     s->cur_frame = f;
  1225.  
  1226.     /* roll back to previous keyframe in stream, or 0 if no keyframes exist */
  1227.     while (s->cur_frame > 0 && !(s->keyframe[s->cur_frame])) {
  1228.         s->cur_frame--;
  1229.     }
  1230.  
  1231.     /* render the frame: we're ready */
  1232.     if (smk_render(s) < 0) {
  1233.         fprintf(stderr, "libsmacker::smk_seek_keyframe(s,%lu) - Warning: frame %lu: smk_render returned errors.\n", f, s->cur_frame);
  1234.         goto error;
  1235.     }
  1236.  
  1237.     return 0;
  1238.  
  1239. error:
  1240.     return -1;
  1241. }
  1242.