home *** CD-ROM | disk | FTP | other *** search
/ Chip 2005 June / ccd0605.iso / LINUX / gopchop-1.1.7.tar.tar / gopchop-1.1.7.tar / gopchop-1.1.7 / src / gtkspu.c < prev    next >
C/C++ Source or Header  |  2004-02-24  |  34KB  |  1,097 lines

  1. /*
  2. #
  3. # Tool for displaying SPU graphics from DVD navigation streams
  4. #
  5. # $Id: gtkspu.c,v 1.19 2004/02/24 05:52:10 nemies Exp $
  6. #
  7. # Copyright (C) 2002-2003 Kees Cook
  8. # kees@outflux.net, http://outflux.net/
  9. # This program is free software; you can redistribute it and/or
  10. # modify it under the terms of the GNU General Public License
  11. # as published by the Free Software Foundation; either version 2
  12. # of the License, or (at your option) any later version.
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16. # GNU General Public License for more details.
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program; if not, write to the Free Software
  19. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  20. # http://www.gnu.org/copyleft/gpl.html
  21. #
  22. */
  23.  
  24. #include "GOPchop.h"
  25.  
  26. #include <stdio.h>
  27. #include <sys/types.h>
  28. #include <sys/stat.h>
  29. #include <unistd.h>
  30. #include <stdlib.h>
  31. #include <string.h>
  32. #include <getopt.h>
  33. #include <gtk/gtk.h>
  34. #include "file_buffer.h"
  35.  
  36. typedef struct
  37. {
  38.     gint red;
  39.     gint green;
  40.     gint blue;
  41. }
  42. quick_color;
  43.  
  44. quick_color clut[0xF];
  45.  
  46. /* since we don't have a valid color map, we're going to just
  47.  * display the 4 possible colors as red, green, blue, or yellow
  48.  */
  49. #define COLOR_BACK      0 /* Green */
  50. #define COLOR_PATTERN   1 /* Red */
  51. #define COLOR_EMP1      2 /* Blue */
  52. #define COLOR_EMP2      3 /* Yellow */
  53. #define COLOR_NOTHING   4
  54.  
  55. gint width = 0;
  56. gint height = 0;
  57. guchar *rgbbuf = NULL;
  58. guchar *backbuf = NULL;
  59. uint8_t *spu_pixelbuf = NULL;
  60.  
  61. int clut_index[4];
  62. int alpha[5] = { 0x0, 0x0, 0x0, 0x0, 0x0 };     // all transparent
  63. quick_color cmap[4];
  64.  
  65. static struct option long_options[] = {
  66.         // name, has_arg, flag, val
  67.         {"help",    0, 0, 'h'},
  68.         {"blind",   0, 0, 'b'},
  69.         {"width",   1, 0, 'x'},
  70.         {"height",  1, 0, 'y'},
  71.         {"skip",    1, 0, 's'},
  72.         {"debug",   0, 0, 'd'},
  73.         {"version", 0, 0, 'V'},
  74.         {NULL,      0, 0, 0},
  75. };
  76. static char *short_options = "h?bdVx:y:s:";
  77. int opt_debug = 0;
  78. int opt_version = 0;
  79. int opt_blind = 0;
  80. // This is used to size the SPU display area, in the case that it
  81. // shouldn't be auto-sized to the spu bounding box
  82. int opt_width = -1;
  83. int opt_height = -1;
  84. off_t opt_skip = 0;
  85.  
  86. void usage(char *argv[])
  87. {
  88.     printf("Usage: %s [OPTIONS] SPU_FILE [packet-number]\n",argv[0]);
  89.     printf("\n");
  90.     printf("-b,--blind     Blindly read the input file as a SPU stream\n");
  91.     printf("-x,--width=X   Force display area width to at least X\n");
  92.     printf("-y,--height=Y  Force display area height to at least Y\n");
  93.     printf("-s,--skip=B    Skip B bytes before processing SPU stream\n");
  94.     printf("-d,--debug     Display debugging information\n");
  95.     printf("-V,--version   Display version number\n");
  96.     printf("-h,-?,--help   Display this help\n");
  97.     printf("\n");
  98.     printf("To generate the SPU_FILE, you can extract the stream\n");
  99.     printf("from a DVD VOB file with 'mpegcat -w'.  They are usually\n");
  100.     printf("in the DVD audio streams, starting from sub-id's 0x20.\n");
  101.     printf("Try: 'mpegcat -iDaw dump.spu,0xBD,0x20 FILE.VOB'\n");
  102.  
  103.     exit(1);
  104. }
  105.  
  106. int handle_options(int argc, char *argv[])
  107. {
  108.     int c;
  109.     int option_index = 0;
  110.  
  111.     while (1) {
  112.         c = getopt_long(argc, argv, short_options, long_options, &option_index);
  113.         if (c == -1)
  114.             break;
  115.  
  116.         switch (c)
  117.         {
  118.             case '?':
  119.             case 'h':
  120.                 usage(argv);
  121.                 break;
  122.             case 'b':
  123.                 opt_blind = 1;
  124.                 break;
  125.             case 'd':
  126.                 opt_debug = 1;
  127.                 break;
  128.             case 'V':
  129.                 opt_version = 1;
  130.                 break;
  131.             case 'x':
  132.                 opt_width = atol(optarg);
  133.                 break;
  134.             case 'y':
  135.                 opt_height = atol(optarg);
  136.                 break;
  137.             case 's':
  138.                 opt_skip = atol(optarg);
  139.                 break;
  140.             default:
  141.                 printf("?? getopt return character code 0x%x ??\n", c);
  142.                 break;
  143.         }
  144.     }
  145.  
  146.     return optind;
  147. }
  148.  
  149. gboolean on_darea_expose(GtkWidget * widget,
  150.                          GdkEventExpose * event, gpointer user_data);
  151.  
  152. int yank_nibble(guchar * rledata, int *addr, int *halfbyte)
  153. {
  154.     int val = 0;
  155.  
  156.     if (!*halfbyte)
  157.     {
  158.         // take the top half
  159.         val = (rledata[*addr] & 0xF0) >> 4;
  160.  
  161.         *halfbyte = 1;
  162.     }
  163.     else
  164.     {
  165.         // take the bottom half
  166.         val = rledata[*addr] & 0x0F;
  167.  
  168.         *halfbyte = 0;
  169.         *addr += 1;
  170.     }
  171.     return val;
  172. }
  173.  
  174. void count_colors(uint8_t *spu, int height, int width) {
  175.     int color_count[4];
  176.     int x, y;
  177.  
  178.     /* clear count */
  179.     for (x=0;x<4;x++) { color_count[x]=0; }
  180.     for (y = 0; y < height; y++) {
  181.         for (x = 0; x < width; x++) {
  182.             if (*spu != COLOR_NOTHING) color_count[*spu++]++;
  183.         }
  184.     }
  185.  
  186.     printf("Pattern   (R): %d\n",color_count[COLOR_PATTERN]);
  187.     printf("Background(G): %d\n",color_count[COLOR_BACK]);
  188.     printf("Emphasis1 (B): %d\n",color_count[COLOR_EMP1]);
  189.     printf("Emphasis2 (Y): %d\n",color_count[COLOR_EMP2]);
  190.  
  191. }
  192.  
  193. #define GRAB_NIBBLE             yank_nibble(rledata,&addr,&halfbyte)
  194. // depends on global "height" and "width"
  195. // what I really need here is a struct for the SPU area, and just draw
  196. // it.  I should deal with screen width/height when merging instead.
  197. void draw_spu(uint8_t *drawbuf,
  198.               int row_start, int row_end, int row_step,
  199.               int colstart, int colend,
  200.               uint8_t * rledata, int addr)
  201. {
  202.     int row;
  203.     int col = colstart;
  204.  
  205.     int halfbyte = 0;
  206.     unsigned int bits;
  207.     unsigned int number;
  208.     uint8_t cindex;
  209.  
  210.     for (row = row_start; row <= row_end;)
  211.     {
  212.         bits = GRAB_NIBBLE;
  213.         if ((bits & 0xC) != 0x0)
  214.         {
  215.             // have 4-bit code
  216.             number = (bits & 0xC) >> 2;
  217.             cindex = (bits & 0x3);
  218.         }
  219.         else
  220.         {
  221.             bits <<= 4;
  222.             bits |= GRAB_NIBBLE;
  223.  
  224.             if ((bits & 0xF0) != 0)
  225.             {
  226.                 // have 8-bit code
  227.                 number = (bits & 0x3C) >> 2;
  228.                 cindex = (bits & 0x3);
  229.             }
  230.             else
  231.             {
  232.                 bits <<= 4;
  233.                 bits |= GRAB_NIBBLE;
  234.  
  235.                 if ((bits & 0xFC0) != 0)
  236.                 {
  237.                     // have 12-bit code
  238.                     number = (bits & 0xFC) >> 2;
  239.                     cindex = (bits & 0x3);
  240.                 }
  241.                 else
  242.                 {
  243.                     // have 16-bit code
  244.                     bits <<= 4;
  245.                     bits |= GRAB_NIBBLE;
  246.  
  247.                     number = (bits & 0x3FC) >> 2;
  248.                     cindex = (bits & 0x3);
  249.  
  250.                     if (number == 0)
  251.                         number = width; // entire row
  252.                 }
  253.             }
  254.         }
  255.  
  256.  
  257.         // write "number" many "cindex"s until end of row
  258.         //printf("row: %d col: %d cindex: %d\n", row,col,cindex);
  259.         for (; number > 0 && col <= colend; number--)
  260.         {
  261.             drawbuf[row * width + col++] = cindex;
  262.         }
  263.  
  264.         if (col > colend)
  265.         {
  266.             row += row_step;
  267.             col = colstart;
  268.  
  269.             // fill to end of byte on row end
  270.             if (halfbyte)
  271.             {
  272.                 halfbyte = 0;
  273.                 addr++;
  274.             }
  275.         }
  276.     }
  277.  
  278. }
  279.  
  280. #define SPU_CMD_END                     0xFF    // ends one SP_DCSQ
  281. #define SPU_FORCED_START_DISPLAY        0x00    // no arguments
  282. #define SPU_START_DISPLAY               0x01    // no arguments
  283. #define SPU_STOP_DISPLAY                0x02    // no arguments
  284.  
  285. // provides four indices into the CLUT for the current PGC to associate with
  286. // the four pixel values. One nibble per pixel value for a total of 2 bytes: 
  287. // emphasis2 emphasis1 pattern background
  288. #define SPU_SET_COLOR                   0x03
  289.  
  290. // directly provides the four contrast (alpha blend) values to associate with
  291. // the four pixel values. One nibble per pixel value for a total of 2 bytes.
  292. // 0x0 = transparent, 0xF = opaque
  293. // e2 e1   p b 
  294. #define SPU_SET_CONTRAST                0x04
  295.  
  296. // defines the display area, each pair (X and Y) of values is 3 bytes wide,
  297. // for a total of 6 bytes, and has the form:
  298. // sx sx   sx ex   ex ex   sy sy   sy ey   ey ey
  299. // sx = starting X coordinate
  300. // ex = ending X coordinate
  301. // sy = starting Y coordinate
  302. // ey = ending Y coordinate 
  303. #define SPU_DEFINE_AREA                 0x05
  304.  
  305. // defines the pixel data addresses. First a 2-byte offset to the top field
  306. // data, followed by a 2-byte offset to the bottom field data, for a total of
  307. // 4 bytes.
  308. #define SPU_DEFINE_PIXEL_ADDRESS        0x06
  309.  
  310. // allows for changing the COLor and CONtrast within one or more areas of the
  311. // display. This command contains a series of parameters, arranged in a
  312. // hierarchy.
  313. // Following the command byte is a 2-byte value for the total size of the
  314. // parameter area, including the size word.
  315. //
  316. // The parameter sequence begins with a LN_CTLI, which defines a vertically
  317. // bounded area of the display. The LN_CTLI may include from one to fifteen
  318. // PX_CTLI parameters, which define a starting horizontal position and new
  319. // color and contrast value to apply from that column on towards the right to
  320. // the next PX_CTLI or the right side of the display. 
  321. //
  322. // LN_CTLI, 4 bytes, special value of 0f ff ff ff signifies the end of the
  323. // parameter area (this termination code MUST be present as the last parameter)
  324. // 0 s   s s   n t   t t
  325. // sss = csln, the starting (top-most) line number for this area
  326. // n = number of PX_CTLI to follow
  327. // ttt = ctln, the terminating (bottom-most) line number for this area
  328. //
  329. // PX_CTLI, 6 bytes, defines a starting column and new color and contrast values
  330. // bytes 0 and 1 - starting column number
  331. // bytes 2 and 3 - new color values, as per SET_COLOR
  332. // bytes 4 and 5 - new contrast values, as per SET_CONTR 
  333. #define SPU_CHANGE_COLOR_CONTRAST       0x07
  334.  
  335. struct spu_cmds_t {
  336.     uint8_t cmd;
  337.     int     arg_bytes;
  338.     char *  name;
  339. };
  340.  
  341. // -1 arg_bytes means two-byte arg length
  342. struct spu_cmds_t spu_cmds[]={
  343.     { SPU_FORCED_START_DISPLAY,     0,  "Forced Start Display" },
  344.     { SPU_START_DISPLAY,            0,  "Start Display" },
  345.     { SPU_STOP_DISPLAY,             0,  "Stop Display" },
  346.     { SPU_SET_COLOR,                2,  "Set Color" },
  347.     { SPU_SET_CONTRAST,             2,  "Set Contrast" },
  348.     { SPU_DEFINE_AREA,              6,  "Define Area" },
  349.     { SPU_DEFINE_PIXEL_ADDRESS,     4,  "Define Pixel Address" },
  350.     { SPU_CHANGE_COLOR_CONTRAST,   -1,  "Change Color and Contrast" },
  351.     { SPU_CMD_END,                  0,  "Command End" },
  352.     { 0,                            0,  NULL },
  353. };
  354.  
  355. #define ADDR_OKAY               (location<spu_len)
  356. #define GRAB_BYTE               (buf[location])
  357. #define GRAB_WORD(buf,loc)      (((buf)[(loc)]<<8)|(buf)[(loc)+1])
  358. #define GRAB_HIGH_NIBBLE        ((buf[location]&0xF0)>>4)
  359. #define GRAB_LOW_NIBBLE         ((buf[location]&0x0F))
  360.  
  361. // ran past the end of the buffer
  362. #define SPU_ERR_OUT_OF_BOUNDS   -1
  363. // no valid command found
  364. #define SPU_ERR_NO_CMD          -2
  365. // null pointer argument
  366. #define SPU_ERR_NULL_ARG        -3
  367.  
  368. /* this expects to find a SPU command at the current location */
  369. // the returned spu_cmd must be free'd by the caller
  370. struct spu_cmds_t * spu_cmd_lookup(file_buf * fb,
  371.                                    struct spu_cmds_t * spu_cmd_copy)
  372. {
  373.     uint8_t             cmd_info[3];
  374.     unsigned int        cmd_len=0;
  375.     struct spu_cmds_t * spu_cmd;
  376.  
  377.     if (!fb) return NULL;
  378.  
  379.     if (buffer_look_ahead(fb, cmd_info, 1) != FILE_BUF_OKAY) {
  380.         if (opt_debug)
  381.             printf("\t\tFile ends before next command\n");
  382.         return NULL;
  383.     }
  384.  
  385.     for (spu_cmd=spu_cmds; spu_cmd->name; spu_cmd++) {
  386.         if (spu_cmd->cmd == cmd_info[0]) {
  387.             if (opt_debug)
  388.                 printf("\t\tChecking command 0x%02X (%s)\n",
  389.                        cmd_info[0], spu_cmd->name);
  390.             break;
  391.         }
  392.     }
  393.     if (!spu_cmd->name) {
  394.         if (opt_debug)
  395.             printf("\t\tNo such command 0x%02X\n",cmd_info[0]);
  396.         return NULL;
  397.     }
  398.     if (spu_cmd->arg_bytes!=0) {
  399.         // variable length arguments
  400.         if (spu_cmd->arg_bytes==-1) {
  401.             if (buffer_look_ahead(fb, cmd_info, 3) != FILE_BUF_OKAY) {
  402.                 if (opt_debug)
  403.                     printf("\t\tFile ends before variable command length\n");
  404.                 return NULL;
  405.             }
  406.             cmd_len=GRAB_WORD(cmd_info,1);
  407.         }
  408.         else {
  409.             cmd_len=spu_cmd->arg_bytes;
  410.         }
  411.     }
  412.     if (!spu_cmd_copy) {
  413.         if (!(spu_cmd_copy=(struct spu_cmds_t *)
  414.                             malloc(sizeof(*spu_cmd_copy)))) {
  415.             perror("malloc");
  416.             exit(1);
  417.         }
  418.     }
  419.     *spu_cmd_copy=*spu_cmd;
  420.     spu_cmd_copy->arg_bytes=cmd_len;
  421.  
  422.     if (opt_debug)
  423.         printf("\t\tCommand length 0x%04X valid\n",cmd_len);
  424.  
  425.     return spu_cmd_copy;
  426. }
  427.  
  428.  
  429. #define SPU_PACKET_VALID         0 
  430. #define SPU_PACKET_NOT_VALID    -1
  431. #define SPU_PACKET_EOF          -2
  432. #define SPU_PACKET_NULL_BUFFER  -3
  433. int spu_packet_valid(file_buf * fb, off_t file_loc, uint8_t ** buf) {
  434.     uint8_t words[4];
  435.     size_t  packet_len;
  436.     size_t  dscq_table;
  437.     size_t  dscq;
  438.     size_t  next_dscq;
  439.     size_t  prev_dscq;
  440.     size_t  loc; // location within the packet
  441.  
  442.     // sanity-check the data area between the spu header and the dscq table
  443.     int has_pixel_data=0;
  444.     int has_pixel_cmd=0;
  445.     int commands=0;
  446.  
  447.     if (!buf) return SPU_PACKET_NULL_BUFFER;
  448.  
  449.     if (opt_debug)
  450.         printf("%06"OFF_T_FORMAT" ...\n",file_loc);
  451.     if (buffer_look_ahead(fb, words, 4) != FILE_BUF_OKAY) {
  452.         if (opt_debug)
  453.             printf("\tFile ends within 4 bytes "
  454.                    "(no room for SPU packet header)\n");
  455.         return SPU_PACKET_EOF;
  456.     }
  457.     packet_len=GRAB_WORD(words,0);
  458.     dscq_table=GRAB_WORD(words,2);
  459.  
  460.     // packet_len must be at least 4+4+1 long (spu header, dscq,
  461.     // and a single "end" command
  462.     if (packet_len<9) {
  463.         if (opt_debug)
  464.             printf("\tPacket length < 9 bytes\n");
  465.         return SPU_PACKET_NOT_VALID;
  466.     }
  467.  
  468.     // is there a gap between the header and the dscq table?
  469.     if (dscq_table>4)
  470.         has_pixel_data=1;
  471.  
  472.     // if the table is beyond the packet len, it's not a SPU
  473.     if (dscq_table>=packet_len) {
  474.         if (opt_debug)
  475.             printf("\tdscq table can't be past end of packet\n");
  476.         return SPU_PACKET_NOT_VALID;
  477.     }
  478.  
  479.     if (!(*buf=(uint8_t*)realloc(*buf,packet_len))) {
  480.         perror("realloc");
  481.         exit(1);
  482.     }
  483.     // if we can't read in the remaining packet, it's not a SPU
  484.     if (buffer_look_ahead(fb, *buf, packet_len) != FILE_BUF_OKAY) {
  485.         if (opt_debug)
  486.             printf("\tFile ends before end of packet\n");
  487.         return SPU_PACKET_NOT_VALID;
  488.     }
  489.  
  490.     // FIXME: when decoding dscq starts, each should start right after the
  491.     // previous one
  492.     prev_dscq=0;
  493.     loc=dscq_table;
  494.     for (dscq=dscq_table;
  495.          dscq<packet_len && prev_dscq!=dscq;
  496.          dscq=next_dscq) {
  497.         struct spu_cmds_t spu_cmd;
  498.         struct spu_cmds_t * spu_cmd_valid;
  499.         unsigned int clocks;
  500.  
  501.         // notice when next == current
  502.         prev_dscq=dscq;
  503.  
  504.         // freak out if dscq!=loc
  505.         if (dscq != loc) {
  506.             if (opt_debug)
  507.                 printf("\tNext dscq is at 0x%04X instead of loc 0x%04X\n",
  508.                        dscq,loc);
  509.             return SPU_PACKET_NOT_VALID;
  510.         }
  511.  
  512.         clocks   =GRAB_WORD(*buf,dscq);
  513.         next_dscq=GRAB_WORD(*buf,dscq+2);
  514.  
  515.         if (opt_debug)
  516.             printf("\tDSCQ 0x%04X (Next 0x%04X)\n",dscq,next_dscq);
  517.  
  518.         // spu's don't go backwards
  519.         if (next_dscq<dscq) {
  520.             if (opt_debug)
  521.                 printf("\tNext dscq can't be before current dscq\n");
  522.             return SPU_PACKET_NOT_VALID;
  523.         }
  524.  
  525.         // move to cmd area
  526.         loc+=4;
  527.  
  528.         // I can leave the command loop when I either
  529.         // a) see an "END" command
  530.         // b) exactly hit the end-of-packet boundry?
  531.         // c) exactly hit the end-of-dscq boundry?
  532.         do {
  533.             // seek
  534.             if (buffer_seek(fb,file_loc+loc) != FILE_BUF_OKAY) {
  535.                 if (opt_debug)
  536.                     printf("\tFile ends before next command area\n");
  537.                 return SPU_PACKET_NOT_VALID;
  538.             }
  539.             // lookup & check
  540.             if (!(spu_cmd_valid=spu_cmd_lookup(fb,&spu_cmd))) {
  541.                 if (opt_debug)
  542.                     printf("\tNo valid SPU command not found\n");
  543.                 return SPU_PACKET_NOT_VALID;
  544.             }
  545.             // verify packet contains command
  546.             loc+=spu_cmd.arg_bytes+1; // total length of command
  547.             if (loc>packet_len) {
  548.                 if (opt_debug)
  549.                     printf("\tCommand can't be outside current packet\n");
  550.                 return SPU_PACKET_NOT_VALID;
  551.             }
  552.             // handle command?
  553.             commands++;
  554.             if (spu_cmd.cmd==SPU_DEFINE_PIXEL_ADDRESS) {
  555.                 has_pixel_cmd=1;
  556.             }
  557.             // leave on "last" cmd
  558.         } while (spu_cmd.cmd!=SPU_CMD_END);
  559.     }
  560.  
  561.     if (prev_dscq!=dscq) {
  562.         // end without correct end-of-dscq's
  563.         if (opt_debug)
  564.             printf("\tPacket finished without finishing last dscq\n");
  565.         return SPU_PACKET_NOT_VALID;
  566.     }
  567.  
  568.     // at least one command in a SPU packet
  569.     if (commands<1) {
  570.         if (opt_debug)
  571.             printf("\tSPU packet has no commands\n");
  572.         return SPU_PACKET_NOT_VALID;
  573.     }
  574.     // check pixel data space
  575.     if (has_pixel_data != has_pixel_cmd) {
  576.         if (opt_debug) {
  577.             if (has_pixel_data)
  578.                 printf("\tSPU has pixel area used but no cmd addressing it\n");
  579.             if (has_pixel_cmd)
  580.                 printf("\tSPU has pixel cmd but no area defined for use\n");
  581.         }
  582.         return SPU_PACKET_NOT_VALID;
  583.     }
  584.  
  585.     // It sure LOOKS like a SPU...
  586.     if (opt_debug)
  587.         printf("\tLooks good, with length %u\n",packet_len);
  588.     return SPU_PACKET_VALID;
  589. }
  590.  
  591. /* sub-picture unit header (spuh):
  592.  * offset  size      description
  593.  * 0x0:    0x2       total length of all packets in bytes
  594.  * 0x2:    0x2       offset to first sp_dcsqt
  595.  * 0x4:    variable  pixel data
  596.  *
  597.  * sub-picture display control sequence table (sp_dcsqt):
  598.  * offset  size      description
  599.  * 0x0:    0x2       90KHz/1024 clock units to wait until running
  600.  * 0x2:    0x2       next sp_dcsq (or self if end)
  601.  *
  602.  * One way to scan for a SPU packet would be to look for
  603.  * DWORD-aligned pairs of WORDs where WORD1 > WORD2
  604.  * and offset+WORD2 is a valid SPU command (0-7, 0xFF).
  605.  * Or rather, a series of valid commands.
  606.  *
  607.  * Additionally, you could check for 0xFF near the end?
  608.  *
  609.  */
  610. int seek_to_next_valid_spu(file_buf * fb) {
  611.     off_t        file_loc;
  612.     uint8_t *    buf=NULL;
  613.     uint8_t      skip_byte;
  614.     int          valid=0;
  615.     int          percent, prev_percent=-1;
  616.     double       calc;
  617.     off_t        file_size;
  618.  
  619.     if (!fb) return -1;
  620.  
  621.     file_loc = buffer_tell(fb);
  622.     file_size = buffer_file_size(fb);
  623.     do {
  624.         switch (spu_packet_valid(fb,file_loc,&buf)) {
  625.         case SPU_PACKET_VALID:
  626.                 // it's valid
  627.                 valid=1;
  628.                 break;
  629.         case SPU_PACKET_NOT_VALID:
  630.                 // it's not valid
  631.                 break;
  632.         default:
  633.                 // eof or other error
  634.                 goto eof;
  635.         }
  636.  
  637.         // reset file location
  638.         if (buffer_seek(fb,file_loc) != FILE_BUF_OKAY)
  639.             goto eof;
  640.  
  641.         if (valid) break;
  642.  
  643.         // advance the buffer to the next byte
  644.         if (buffer_get_byte(fb,&skip_byte) != FILE_BUF_OKAY)
  645.             goto eof;
  646.         file_loc = buffer_tell(fb);
  647.  
  648.         // update a little counter
  649.         calc=(double)file_loc;
  650.         calc/=(double)file_size;
  651.         calc*=(double)10000;
  652.         percent=(int)(calc);
  653.         if (percent!=prev_percent) {
  654.             printf("%"OFF_T_FORMAT"/%"OFF_T_FORMAT" (%0.2f%%)\r",
  655.                    file_loc, file_size, (double)percent/(double)100);
  656.             fflush(NULL);
  657.         }
  658.         prev_percent=percent;
  659.     } while (1);
  660.  
  661.     // good packet!
  662.     if (buf) free(buf);
  663.     return 0;
  664.  
  665. eof:
  666.     if (buf) free(buf);
  667.     return -1;
  668. }
  669.  
  670. unsigned int odd_pixel_rows;
  671. unsigned int even_pixel_rows;
  672. gint sx, sy, ex, ey, i;
  673.  
  674. // returns -1 if done, or offset of next sequence
  675. int spu_run(uint8_t * buf, off_t file_offset, int spu_seq_loc, size_t spu_len)
  676. {
  677.     int location = spu_seq_loc;
  678.     int my_location = spu_seq_loc;
  679.     unsigned int stall_time;
  680.     unsigned int next_sequence;
  681.     unsigned int size;
  682.     uint8_t cmd;
  683.  
  684.     // adjust our buffer & max_addr
  685.     if (!buf) return -1;
  686.  
  687.     printf("0x%05X: ", location);
  688.     stall_time = GRAB_WORD(buf,location);
  689.     location += 2;
  690.     next_sequence = GRAB_WORD(buf,location);
  691.     printf("SEQ: Stall time: %u, Next sequence: 0x%X\n",
  692.            stall_time, next_sequence);
  693.     location += 2;
  694.  
  695.     for (cmd = buf[location]; ADDR_OKAY; cmd = buf[location])
  696.     {
  697.         printf("0x%05X: ", location);
  698.         location++;
  699.  
  700.         switch (cmd)
  701.         {
  702.             case SPU_FORCED_START_DISPLAY:
  703.                 printf("Forced Start Display\n");
  704.                 break;
  705.             case SPU_START_DISPLAY:
  706.                 printf("Start Display\n");
  707.                 break;
  708.             case SPU_STOP_DISPLAY:
  709.                 printf("Stop Display\n");
  710.                 break;
  711.  
  712.             case SPU_SET_COLOR:
  713.                 clut_index[COLOR_EMP2] = GRAB_HIGH_NIBBLE;
  714.                 clut_index[COLOR_EMP1] = GRAB_LOW_NIBBLE;
  715.                 location++;
  716.                 clut_index[COLOR_PATTERN] = GRAB_HIGH_NIBBLE;
  717.                 clut_index[COLOR_BACK] = GRAB_LOW_NIBBLE;
  718.                 location++;
  719.                 printf
  720.                     ("Set Color (pat(R): 0x%X back(G): 0x%X emp1(B): 0x%X emp2(Y): 0x%X)\n",
  721.                      clut_index[COLOR_PATTERN], clut_index[COLOR_BACK],
  722.                      clut_index[COLOR_EMP1], clut_index[COLOR_EMP2]);
  723.  
  724.                 cmap[0] = clut[clut_index[0]];
  725.                 cmap[1] = clut[clut_index[1]];
  726.                 cmap[2] = clut[clut_index[2]];
  727.                 cmap[3] = clut[clut_index[3]];
  728.  
  729.                 /* over-ride the colors, since we don't have a color map */
  730.                 cmap[COLOR_PATTERN].red=0xFF;
  731.                 cmap[COLOR_PATTERN].green=0x00;
  732.                 cmap[COLOR_PATTERN].blue=0x00;
  733.  
  734.                 cmap[COLOR_BACK].red=0x00;
  735.                 cmap[COLOR_BACK].green=0xFF;
  736.                 cmap[COLOR_BACK].blue=0x00;
  737.  
  738.                 cmap[COLOR_EMP1].red=0x00;
  739.                 cmap[COLOR_EMP1].green=0x00;
  740.                 cmap[COLOR_EMP1].blue=0xFF;
  741.  
  742.                 cmap[COLOR_EMP2].red=0xFF;
  743.                 cmap[COLOR_EMP2].green=0xFF;
  744.                 cmap[COLOR_EMP2].blue=0x80;
  745.  
  746.                 break;
  747.  
  748.             case SPU_SET_CONTRAST:
  749.                 alpha[COLOR_EMP2] = GRAB_HIGH_NIBBLE;
  750.                 alpha[COLOR_EMP1] = GRAB_LOW_NIBBLE;
  751.                 location++;
  752.                 alpha[COLOR_PATTERN] = GRAB_HIGH_NIBBLE;
  753.                 alpha[COLOR_BACK] = GRAB_LOW_NIBBLE;
  754.                 location++;
  755.                 printf
  756.                     ("Set Alpha (pat(R): 0x%X back(G): 0x%X emp1(B): 0x%X emp2(Y): 0x%X)\n",
  757.                      alpha[COLOR_PATTERN], alpha[COLOR_BACK],
  758.                      alpha[COLOR_EMP1], alpha[COLOR_EMP2]);
  759.  
  760.                 // force visible bitmap if they're all transparent
  761.                 if (alpha[COLOR_PATTERN] == 0x0 &&
  762.                     alpha[COLOR_BACK]    == 0x0 &&
  763.                     alpha[COLOR_EMP1]    == 0x0 &&
  764.                     alpha[COLOR_EMP2]    == 0x0)
  765.                 {
  766.                     printf("All transparent, forcing full visibility...\n");
  767.                     alpha[COLOR_PATTERN] = 0xF;
  768.                     alpha[COLOR_BACK]    = 0xF;
  769.                     alpha[COLOR_EMP1]    = 0xF;
  770.                     alpha[COLOR_EMP2]    = 0xF;
  771.                 }
  772.                 break;
  773.  
  774.             case SPU_DEFINE_AREA:
  775.                 sx = GRAB_BYTE;
  776.                 location++;
  777.                 sx <<= 4;
  778.                 sx |= GRAB_HIGH_NIBBLE;
  779.                 ex = GRAB_LOW_NIBBLE;
  780.                 location++;
  781.                 ex <<= 8;
  782.                 ex |= GRAB_BYTE;
  783.                 location++;
  784.  
  785.                 sy = GRAB_BYTE;
  786.                 location++;
  787.                 sy <<= 4;
  788.                 sy |= GRAB_HIGH_NIBBLE;
  789.                 ey = GRAB_LOW_NIBBLE;
  790.                 location++;
  791.                 ey <<= 8;
  792.                 ey |= GRAB_BYTE;
  793.                 location++;
  794.  
  795.                 printf("Define Area (%d,%d)-(%d,%d) %dx%d\n",
  796.                        sx, sy, ex, ey, (ex-sx)+1, (ey-sy)+1);
  797.                 if (opt_width != -1 || opt_height != -1) {
  798.                     // this version draws from the screen's top left
  799.                     // plus any extra space created by the cmdline opts
  800.                     width  = MAX(opt_width,ex + 1);
  801.                     height = MAX(opt_height,ey + 1);
  802.                 }
  803.                 else {
  804.                     // this version draws only the defined SPU area
  805.                     width  = (ex - sx) + 1;
  806.                     height = (ey - sy) + 1;
  807.                 }
  808.  
  809.                 break;
  810.  
  811.             case SPU_DEFINE_PIXEL_ADDRESS:
  812.                 odd_pixel_rows = GRAB_WORD(buf,location);
  813.                 location += 2;
  814.                 even_pixel_rows = GRAB_WORD(buf,location);
  815.                 location += 2;
  816.                 printf("Define Pixels (odd: 0x%X even: 0x%X)\n",
  817.                        odd_pixel_rows, even_pixel_rows);
  818.                 // FIXME: validate pixel location
  819.                 break;
  820.  
  821.             case SPU_CHANGE_COLOR_CONTRAST:
  822.                 size = GRAB_WORD(buf,location);
  823.                 location += size;
  824.                 printf("Change Color & Contrast (%d)\n", size);
  825.                 break;
  826.  
  827.             case SPU_CMD_END:
  828.                 printf("End of Commands\n");
  829.  
  830.                 // handle drawing now that we got everything
  831.                 if (spu_pixelbuf)
  832.                     free(spu_pixelbuf);
  833.                 if (!(spu_pixelbuf =
  834.                       (uint8_t *) malloc(sizeof(*spu_pixelbuf) * width * height)))
  835.                 {
  836.                     perror("malloc");
  837.                     exit(1);
  838.                 }
  839.                 // clear the pixels
  840.                 memset(spu_pixelbuf, COLOR_NOTHING, width * height);
  841.  
  842.                 if (opt_width != -1 || opt_height != -1) {
  843.                     // this version draws from the screen's top left corner
  844.                     draw_spu(spu_pixelbuf, sy,     ey, 2, sx, ex,
  845.                              buf, odd_pixel_rows);
  846.                     draw_spu(spu_pixelbuf, sy + 1, ey, 2, sx, ex,
  847.                              buf, even_pixel_rows);
  848.                 }
  849.                 else {
  850.                     // this version draws only the defined SPU area
  851.                     draw_spu(spu_pixelbuf, 0, height-1, 2, 0, width-1,
  852.                              buf, odd_pixel_rows);
  853.                     draw_spu(spu_pixelbuf, 1, height-1, 2, 0, width-1,
  854.                              buf, even_pixel_rows);
  855.                 }
  856.  
  857.                 if (my_location == next_sequence)
  858.                     return -1;
  859.  
  860.                 return next_sequence;
  861.  
  862.             default:
  863.                 printf("Can't handle SPU command 0x%02X\n", cmd);
  864.                 return -1;
  865.         }
  866.     }
  867.     printf("SPU packet ran past end of buffer\n");
  868.     return -1;
  869. }
  870.  
  871.  
  872. void init_colors()
  873. {
  874.     int x;
  875.  
  876.     gdk_rgb_init();
  877.  
  878.     // make up a color map with shades
  879.     for (x = 0; x < 0xF; x++)
  880.     {
  881.         clut[x].red = (x+1)*0x11;
  882.         clut[x].green = (x+1)*0x11;
  883.         clut[x].blue = (x+1)*0x11;
  884.     }
  885. }
  886.  
  887. // if packet is -1, do an auto-find
  888. void prepare_packet(int packet, file_buf * fb) {
  889. }
  890.  
  891. int main(int argc, char *argv[])
  892. {
  893.     GtkWidget *window, *darea;
  894.     gint x, y;
  895.     guchar *pos;
  896.     guchar info[4];
  897.  
  898.     char *filename;
  899.     file_buf * fb=NULL;
  900.     int packet;
  901.     int packet_wanted=0;
  902.     off_t spu_packet_start;
  903.     uint8_t * buf=NULL;
  904.     int spu_seq_loc;
  905.     size_t spu_len;
  906.  
  907.  
  908.     gtk_init(&argc, &argv);
  909.     optind = handle_options(argc,argv);
  910.  
  911.     if (opt_version) {
  912.         printf("$Id: gtkspu.c,v 1.19 2004/02/24 05:52:10 nemies Exp $\n");
  913.         exit(0);
  914.     }
  915.  
  916.     if (argc - optind < 1)
  917.         usage(argv);
  918.  
  919.     // start up program initialization
  920.     init_colors();
  921.  
  922.     filename = argv[optind];
  923.     if (argc - optind > 1)
  924.         packet_wanted = atoi(argv[optind+1]);
  925.  
  926.     if (!(fb=buffer_start(fb,filename,DEFAULT_FILE_BUFFER_SIZE))) {
  927.         perror(filename);
  928.         exit(1);
  929.     }
  930.  
  931.     spu_packet_start = opt_skip;
  932.     if (buffer_seek(fb,spu_packet_start) != FILE_BUF_OKAY) {
  933.             perror("seek");
  934.             exit(1);
  935.     }
  936.     for (packet = 0; packet <= packet_wanted; packet++) {
  937.         if (!opt_blind) {
  938.             if (seek_to_next_valid_spu(fb)<0) {
  939.                 printf("No more SPU packets can be found\n");
  940.                 exit(1);
  941.             }
  942.         }
  943.         spu_packet_start=buffer_tell(fb);
  944.         if (buffer_look_ahead(fb, info, 4) == FILE_BUF_OKAY) {
  945.             // execute every sequence in this SPU packet
  946.             spu_len     = GRAB_WORD(info,0);
  947.             spu_seq_loc = GRAB_WORD(info,2);
  948.         }
  949.         else {
  950.             printf("No more packets in file\n");
  951.             exit(1);
  952.         }
  953.                        
  954.         if (packet == packet_wanted) {
  955.             if (!(buf=(uint8_t *)realloc(buf,spu_len))) {
  956.                 perror("malloc");
  957.                 exit(1);
  958.             }
  959.             if (buffer_look_ahead(fb,buf,spu_len) != FILE_BUF_OKAY) {
  960.                 printf("Tried to read SPU packet, but read off end of file\n");
  961.                 free(buf);
  962.                 exit(1);
  963.             }
  964.  
  965.             printf("Packet %d @ offset %"OFF_T_FORMAT" length %u\n",
  966.                    packet,spu_packet_start,spu_len);
  967.             while (spu_seq_loc != -1) {
  968.                 spu_seq_loc = spu_run(buf, spu_packet_start,
  969.                                       spu_seq_loc, spu_len);
  970.             }
  971.             break; // we processed the packet we wanted
  972.         }
  973.         else {
  974.             if (buffer_seek(fb,spu_packet_start+spu_len) != FILE_BUF_OKAY) {
  975.                 printf("Ran off the end of file\n");
  976.                 exit(2);
  977.             }
  978.         }
  979.     }
  980.     if (buf)
  981.         free(buf);
  982.  
  983.     if (width == 0 || height == 0)
  984.     {
  985.         printf("SPU didn't have enough info about window size\n");
  986.         exit(1);
  987.     }
  988.  
  989.     if (!(backbuf = (guchar *) malloc(sizeof(*backbuf) * 3 * width * height)))
  990.     {
  991.         perror("malloc");
  992.         exit(1);
  993.     }
  994.     memset(backbuf, 0xFF, 3 * width * height);
  995.  
  996.     if (!(rgbbuf = (guchar *) malloc(sizeof(*rgbbuf) * 3 * width * height)))
  997.     {
  998.         perror("malloc");
  999.         exit(1);
  1000.     }
  1001.     memset(rgbbuf, 0xFF, 3 * width * height);
  1002.  
  1003.     window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  1004.     darea = gtk_drawing_area_new();
  1005.     gtk_drawing_area_size(GTK_DRAWING_AREA(darea), width, height);
  1006.     gtk_container_add(GTK_CONTAINER(window), darea);
  1007.     gtk_signal_connect(GTK_OBJECT(darea), "expose-event",
  1008.                        GTK_SIGNAL_FUNC(on_darea_expose), NULL);
  1009.     gtk_widget_show_all(window);
  1010.  
  1011.     /* Set up the background RGB buffer. */
  1012.     pos = backbuf;
  1013.     for (y = 0; y < height; y++)
  1014.     {
  1015.         int on = 0;
  1016.         for (x = 0; x < width; x++)
  1017.         {
  1018.             // checker board a la Gimp
  1019.             on = ((x >> 4) & 1);
  1020.             if ((y >> 4 & 1)) on = !on;
  1021.             *pos++ = 0xFF - (on ? 0x80 : 0);      /* Red. */
  1022.             *pos++ = 0xFF - (on ? 0x80 : 0);      /* Green. */
  1023.             *pos++ = 0xFF - (on ? 0x80 : 0);      /* Blue. */
  1024.         }
  1025.     }
  1026.  
  1027.     /* count colors */
  1028.     count_colors(spu_pixelbuf,height,width);
  1029.  
  1030.     gtk_main();
  1031.     return 0;
  1032. }
  1033.  
  1034. // I should deal with the SPU being smaller than the screen area here,
  1035. // rather than drawing a giant spu in the draw_spu function
  1036. gboolean
  1037. on_darea_expose(GtkWidget * widget,
  1038.                 GdkEventExpose * event, gpointer user_data)
  1039. {
  1040.     uint8_t *spu;
  1041.     guchar *screen;
  1042.     guchar *back;
  1043.     gint x, y;
  1044.  
  1045.  
  1046.     // merge back with SPU onto rgbbuf
  1047.     spu = spu_pixelbuf;
  1048.     back = backbuf;
  1049.     screen = rgbbuf;
  1050.     for (y = 0; y < height; y++)
  1051.     {
  1052.         for (x = 0; x < width; x++)
  1053.         {
  1054.             if (*spu > COLOR_NOTHING)
  1055.             {
  1056.                 printf("spu buffer freaked out (%d @ %d,%d)\n", *spu, x, y);
  1057.                 exit(1);
  1058.             }
  1059.             /*
  1060.              * Optimized alpha blending
  1061.              *
  1062.              *   x*a + y*(1-a)       =
  1063.              *   x*a + y*(1) - y*a   =
  1064.              *   x*a - y*a + y       =
  1065.              *   (x-y)*a + y
  1066.              */
  1067.  
  1068.             // Red
  1069.             //*screen = alpha[*spu] * cmap[*spu].red + (0xF-alpha[*spu]) * *back;
  1070.             *screen = (cmap[*spu].red-*back)*alpha[*spu] + *back;
  1071.             screen++;
  1072.             back++;
  1073.             // Green
  1074.             //*screen = alpha[*spu] * cmap[*spu].green + (0xF-alpha[*spu]) * *back;
  1075.             *screen = (cmap[*spu].green-*back)*alpha[*spu] + *back;
  1076.             screen++;
  1077.             back++;
  1078.             // Blue
  1079.             //*screen = alpha[*spu] * cmap[*spu].blue + (0xF-alpha[*spu]) * *back;
  1080.             *screen = (cmap[*spu].blue-*back)*alpha[*spu] + *back;
  1081.             screen++;
  1082.             back++;
  1083.             spu++;
  1084.         }
  1085.     }
  1086.  
  1087.     gdk_draw_rgb_image(widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
  1088.                        0, 0, width, height,
  1089.                        GDK_RGB_DITHER_MAX, rgbbuf, width * 3);
  1090.     return 1;
  1091. }
  1092.  
  1093. /* vi:set ai ts=4 sw=4 expandtab: */
  1094.