/*
	2004.02.01
		first released source code for IOMP
*/
/*
 *
 * Copyright (C) James Courtier-Dutton James@superbug.demon.co.uk - July 2001
 *
 * spu.c - converts DVD subtitles to an XPM image
 *
 * Mostly based on hard work by:
 *
 * Copyright (C) 2000   Samuel Hocevar <sam@via.ecp.fr>
 *                       and Michel Lespinasse <walken@via.ecp.fr>
 *
 * Lots of rearranging by:
 *	Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
 *	Thomas Mirlacher <dent@cosy.sbg.ac.at>
 *		implemented reassembling
 *		cleaner implementation of SPU are saving
 *		overlaying (proof of concept for now)
 *		... and yes, it works now with oms
 *		added tranparency (provided by the SPU hdr)
 *		changed structures for easy porting to MGAs DVD mode
 * This file is part of xine
 * This file was originally part of the OMS program.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: spu.c,v 1.20 2004/02/18 11:25:19 rwlin Exp $
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <inttypes.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

#include "xine_internal.h"
#include "video_out/alphablend.h"
#include "xineutils.h"
#include "spu.h"
#include "buffer.h"
#include "xine-engine/bswap.h"
#ifdef HAVE_DVDNAV
#  include <dvdnav/nav_read.h>
#  include <dvdnav/nav_print.h>
#else
#  include "nav_read.h"
#  include "nav_print.h"
#endif


/*
#define LOG_DEBUG 1
#define LOG_BUTTON 1
#define LOG_NAV 1
*/

void spudec_reassembly (spudec_seq_t *seq, uint8_t *pkt_data, u_int pkt_len);
void spudec_process( spudec_decoder_t *this, int stream_id);
void spudec_decode_nav( spudec_decoder_t *this, buf_element_t *buf);
void spudec_copy_nav_to_overlay(pci_t* nav_pci, uint32_t* clut, int32_t button, int32_t mode,
                                vo_overlay_t * overlay, vo_overlay_t * base );
static void spudec_do_commands (spudec_state_t *state, spudec_seq_t* seq, vo_overlay_t *ovl);
static void spudec_draw_picture (spudec_state_t *state, spudec_seq_t* seq, vo_overlay_t *ovl);
static void spudec_discover_clut (spudec_state_t *state, vo_overlay_t *ovl);
#ifdef LOG_DEBUG
static void spudec_print_overlay( vo_overlay_t *overlay );
#endif

void spudec_decode_nav(spudec_decoder_t *this, buf_element_t *buf) {
  uint8_t                  *p;
  uint32_t                  packet_len;
  uint32_t                  stream_id;
  uint32_t                  header_len;
  pci_t                     pci;
  dsi_t                     dsi;
  video_overlay_manager_t  *ovl_manager = this->stream->video_out->get_overlay_manager (this->stream->video_out);

  p = buf->content;
  if (p[0] || p[1] || (p[2] != 1)) {
    printf("libspudec:spudec_decode_nav:nav demux error! %02x %02x %02x (should be 0x000001) \n",p[0],p[1],p[2]);
    return;
  }

  packet_len = p[4] << 8 | p[5];
  stream_id  = p[3];

  header_len = 6;
  p += header_len;

  if (stream_id == 0xbf) { /* Private stream 2 */
/*   int i;
 *   for(i=0;i<80;i++) {
 *     printf("%02x ",p[i]);
 *   }
 *   printf("\n p[0]=0x%02x\n",p[0]);
 */
    if(p[0] == 0x00) {
#ifdef LOG_NAV
      int btngr_ns = 0, btn_ns = 0;
      printf("libspudec:nav_PCI\n");
#endif
      navRead_PCI(&pci, p+1);
/*
      printf("libspudec:nav:hli_ss=%u, hli_s_ptm=%u, hli_e_ptm=%u, btn_sl_e_ptm=%u pts=%lli\n",
       pci.hli.hl_gi.hli_ss,
       pci.hli.hl_gi.hli_s_ptm,
       pci.hli.hl_gi.hli_e_ptm,
       pci.hli.hl_gi.btn_se_e_ptm,
       buf->pts);
      printf("libspudec:nav:btn_sn/ofn=%u, btn_ns=%u, fosl_btnn=%u, foac_btnn=%u\n",
       pci.hli.hl_gi.btn_ofn, pci.hli.hl_gi.btn_ns,
       pci.hli.hl_gi.fosl_btnn, pci.hli.hl_gi.foac_btnn);
      printf("btngr_ns      %d\n",  pci.hli.hl_gi.btngr_ns);
      printf("btngr%d_dsp_ty    0x%02x\n", 1, pci.hli.hl_gi.btngr1_dsp_ty);
      printf("btngr%d_dsp_ty    0x%02x\n", 2, pci.hli.hl_gi.btngr2_dsp_ty);
      printf("btngr%d_dsp_ty    0x%02x\n", 3, pci.hli.hl_gi.btngr3_dsp_ty);
      //navPrint_PCI(&pci);
*/
#ifdef LOG_NAV
      navPrint_PCI_GI(&pci.pci_gi);
      navPrint_NSML_AGLI(&pci.nsml_agli);
      //navPrint_HLI(&pci.hli);
      navPrint_HL_GI(&pci.hli.hl_gi, & btngr_ns, & btn_ns);
#endif
    }

    p += packet_len;

    /* We should now have a DSI packet. */
    /* We don't need anything from the DSI packet here. */
    if(p[6] == 0x01) {
      packet_len = p[4] << 8 | p[5];
      p += 6;
#ifdef LOG_NAV
      printf("NAV DSI packet\n");  
#endif
      navRead_DSI(&dsi, p+1);

//      self->vobu_start = self->dsi.dsi_gi.nv_pck_lbn;
//      self->vobu_length = self->dsi.dsi_gi.vobu_ea;
    }
  }
  pthread_mutex_lock(&this->nav_pci_lock);
  switch (pci.hli.hl_gi.hli_ss) {
    case 0:
      /* No Highlight information for this VOBU */
      if ( this->pci.hli.hl_gi.hli_ss == 1) {
        /* Hide menu spu between menus */
#ifdef LOG_BUTTON
        printf("libspudec:nav:SHOULD HIDE SPU here\n");
#endif
        if( this->menu_handle < 0 ) {
          this->menu_handle = ovl_manager->get_handle(ovl_manager,1);
        }
        if( this->menu_handle >= 0 ) {
          this->event.object.handle = this->menu_handle;
          this->event.event_type = OVERLAY_EVENT_HIDE;
	  /* hide menu right now */
	  this->event.vpts = 0;
          ovl_manager->add_event(ovl_manager, (void *)&this->event);
        } else {
          printf("libspudec: No video_overlay handles left for menu\n");
        }
      }
      xine_fast_memcpy(&this->pci, &pci, sizeof(pci_t));
      /* incoming SPUs will be plain subtitles */
      this->event.object.object_type = 0;
      if (this->button_filter) {
	/* we possibly had buttons before, so we update the UI info */
	xine_event_t   event;
	xine_ui_data_t data;
	
	event.type = XINE_EVENT_UI_NUM_BUTTONS;
	event.data = &data;
	event.data_length = sizeof(data);
	data.num_buttons = 0;
	
	xine_event_send(this->stream, &event);
      }
      this->button_filter=0;

      break;
    case 1: 
      /* All New Highlight information for this VOBU */
      xine_fast_memcpy(&this->pci, &pci, sizeof(pci_t));
      /* incoming SPUs will be menus */
      this->event.object.object_type = 1;
      if (!this->button_filter) {
	/* we possibly entered a menu, so we update the UI button info */
	xine_event_t   event;
	xine_ui_data_t data;
	
	event.type = XINE_EVENT_UI_NUM_BUTTONS;
	event.data = &data;
	event.data_length = sizeof(data);
	data.num_buttons = pci.hli.hl_gi.btn_ns;
	
	xine_event_send(this->stream, &event);
      }
      this->button_filter=1;
      /*******************************
       * We should do something about fosl_btnn, but
       * until we can send the info to dvdnav, ignore it.
       * if( pci.hli.hl_gi.fosl_btnn) {
       *   this->buttonN = pci.hli.hl_gi.fosl_btnn;
       * }
       *******************************/
      break;
    case 2:
      /* Use Highlight information from previous VOBU */
      this->pci.pci_gi.vobu_s_ptm = pci.pci_gi.vobu_s_ptm;
      this->pci.pci_gi.vobu_e_ptm = pci.pci_gi.vobu_e_ptm;
      this->pci.pci_gi.vobu_se_e_ptm = pci.pci_gi.vobu_se_e_ptm;
      break;
    case 3:
      /* Use Highlight information from previous VOBU except commands, which come from this VOBU */
      this->pci.pci_gi.vobu_s_ptm = pci.pci_gi.vobu_s_ptm;
      this->pci.pci_gi.vobu_e_ptm = pci.pci_gi.vobu_e_ptm;
      this->pci.pci_gi.vobu_se_e_ptm = pci.pci_gi.vobu_se_e_ptm;
      /* FIXME: Add command copying here */
      break;
   default:
      printf("libspudec: unknown pci.hli.hl_gi.hli_ss = %d\n", pci.hli.hl_gi.hli_ss );
      break;
  }
  pthread_mutex_unlock(&this->nav_pci_lock);
  return;
}

void spudec_reassembly (spudec_seq_t *seq, uint8_t *pkt_data, u_int pkt_len)
{
#ifdef LOG_DEBUG
  printf ("libspudec: seq->complete = %d\n", seq->complete);
  printf("libspudec:1: seq->ra_offs = %d, seq->seq_len = %d, seq->buf_len = %d, seq->buf=%p\n",
             seq->ra_offs,
             seq->seq_len,
             seq->buf_len,
             seq->buf);
#endif
  if (seq->complete) {
    seq->seq_len = (((uint32_t)pkt_data[0])<<8) | pkt_data[1];
    seq->cmd_offs = (((uint32_t)pkt_data[2])<<8) | pkt_data[3];
    if (seq->cmd_offs >= seq->seq_len) { 
      printf("libspudec:faulty stream\n");
      seq->broken = 1;
    }
    if (seq->buf_len < seq->seq_len) {
      seq->buf_len = seq->seq_len;
#ifdef LOG_DEBUG
      printf ("spu: MALLOC1: seq->buf %p, len=%d\n", seq->buf,seq->buf_len);
#endif
      if (seq->buf) {
        free(seq->buf);
        seq->buf = NULL;
      }
      seq->buf = malloc(seq->buf_len);
#ifdef LOG_DEBUG
      printf ("spu: MALLOC2: seq->buf %p, len=%d\n", seq->buf,seq->buf_len);
#endif

    }
    seq->ra_offs = 0;

#ifdef LOG_DEBUG
    printf ("spu: buf_len: %d\n", seq->buf_len);
    printf ("spu: cmd_off: %d\n", seq->cmd_offs);
#endif
  }

#ifdef LOG_DEBUG
  printf("libspudec:2: seq->ra_offs = %d, seq->seq_len = %d, seq->buf_len = %d, seq->buf=%p\n",
             seq->ra_offs,
             seq->seq_len,
             seq->buf_len,
             seq->buf);
#endif
  if (seq->ra_offs < seq->seq_len) {
    if (seq->ra_offs + pkt_len > seq->seq_len)
      pkt_len = seq->seq_len - seq->ra_offs;
    memcpy (seq->buf + seq->ra_offs, pkt_data, pkt_len);
    seq->ra_offs += pkt_len;
  } else {
    printf("libspudec:faulty stream\n");
    seq->broken = 1;
  } 

  if (seq->ra_offs == seq->seq_len) {
    seq->finished = 0;
    seq->complete = 1;
    return; /* sequence ready */
  }
  seq->complete = 0;
  return;
}

extern dvdspu_color_block_t	*global_color_blocks; /* in xine-engine */
extern dvdspu_color_block_t *global_travel_cb;
extern int osd_is_showing;
extern int spu_current_enable;
static dvdspu_color_block_t	*TAIL = NULL;

/* added by rwlin@avamax.com */
static void
check_and_free_color_blocks()
{
    dvdspu_color_block_t    *cb;
    dvdspu_vert_block_t		*vb,*vbtmp;

	cb = global_color_blocks;
	while(cb!=NULL) {
		if(cb->signature!=0x879A) {
			printf("cb->signature = %d\n", cb->signature);
			printf("CLEAR !!!!\n");
			global_color_blocks=NULL;
			TAIL = NULL;
			global_travel_cb = NULL;
			break;
		}

		if(!cb->displayed)
			break;

		/*
		if(cb->next==NULL) {
			cb->displayed=0;
			printf("should not happend, cb->next==NULL but displayed\n");
			break;
		}*/

		global_color_blocks=cb->next;
		//printf("reset global_color_blocks=%p\n", global_color_blocks);

		/* deallocate it */
		for(vb=cb->vert_blocks;vb;vb=vbtmp) {
			vbtmp=vb->next;
			if(vb->hori_blocks)
				free(vb->hori_blocks);
			free(vb);
		}
		cb->signature=0;	/* in case we may reuse them after deallocating it */
		if (global_travel_cb==cb) {
			global_travel_cb = NULL;
			printf("CLEAR2 !!\n");
		}

		if (TAIL==cb)
			TAIL = NULL; /* clear TAIL */

		free(cb);
		cb = global_color_blocks;
	}
	//pthread_mutex_unlock(&ovl->update_lock);
}

static int IS_SHOW;
static int IS_HIDE;
static int IS_WIPE = 0; /* kevin */
static int FORCE_CLEAR_FLAG = 0;
extern int previous_timestamp;
extern int osd_frame_counter;
extern int forced_display;
extern int enable_alpha;
extern int enable_color;

void
force_free_color_blocks()
{
    dvdspu_color_block_t    *cb,*tmp;
    dvdspu_vert_block_t		*vb,*vbtmp;

	previous_timestamp = 0; /* clear the time stamp, for video_out/alphablend.c */
	for(cb=global_color_blocks;cb && cb->signature==0x879A;cb=tmp) {
		tmp=cb->next;

		/* deallocate it */
		for(vb=cb->vert_blocks;vb;vb=vbtmp) {
			vbtmp=vb->next;
			if(vb->hori_blocks)
				free(vb->hori_blocks);
			free(vb);
		}
		cb->signature=0;	/* in case we may reuse them after deallocating it */
		free(cb);
	}
	global_color_blocks = NULL;
	TAIL = NULL;
	global_travel_cb = NULL;
	FORCE_CLEAR_FLAG = 1;
	osd_frame_counter = -1; /* reset to default */
	osd_is_showing = 1; /* reset osd showing flag */
  	forced_display = 1; /* global variable in xine-engine/osd.c */
	enable_alpha = 0;
	enable_color = 0;
	spu_current_enable = 1;
//	printf("CLEAR ALL CB global_color_blocks=NULL\n");
}


#define OSD_CMD_COLORBLOCK	0
#define OSD_CMD_SHOW		1
#define OSD_CMD_HIDE		2
#define OSD_CMD_ALPHA		3
#define OSD_CMD_COLOR		4

extern int				default_active_button;
static spudec_decoder_t *active_spu_decoder=NULL;
static long				foac_end_time=0;

static void*
foac_btnn_thread(void *arg)
{
 	xine_event_t 			event;
 	int				buttonN;
 	struct timeval 			tt;

	if(active_spu_decoder==NULL)
		return NULL;

 	while(default_active_button>=0) {
 		gettimeofday(&tt,NULL);
 		if(tt.tv_sec>=(foac_end_time-1) && tt.tv_usec>800000)
 			break;
 	
 		xine_usec_sleep(50000);
 	}
	foac_end_time=0;

	if(default_active_button<0)
		return NULL;

	buttonN = default_active_button;
	event.stream = active_spu_decoder->stream;
	event.data = &buttonN;
	event.data_length = sizeof(buttonN);

	if(buttonN<63) {
    	event.type = XINE_EVENT_INPUT_BUTTON_FORCE;
    	xine_event_send(event.stream, &event);
 	}
    event.type = XINE_EVENT_INPUT_SELECT;
    xine_event_send(event.stream, &event);
    default_active_button=-1;

    return NULL;
}


void spudec_process (spudec_decoder_t *this, int stream_id) {
  int FIRST_SHOW_HIDE = 1;
  int SPU_COUNTER = 0;
  int FIRST_SHOW_HIDE_COUNTER = 0;

  spudec_seq_t    *cur_seq;
  video_overlay_manager_t *ovl_manager = this->stream->video_out->get_overlay_manager (this->stream->video_out);
  int pending = 1;
  cur_seq = &this->spudec_stream_state[stream_id].ra_seq;

#ifdef LOG_DEBUG
  printf ("spu: Found SPU from stream %d pts=%lli vpts=%lli\n",stream_id,
          this->spudec_stream_state[stream_id].pts,
          this->spudec_stream_state[stream_id].vpts);
#endif
  this->state.cmd_ptr = cur_seq->buf + cur_seq->cmd_offs;
  this->state.modified = 1; /* Only draw picture if = 1 on first event of SPU */
  this->state.visible = OVERLAY_EVENT_SHOW;
  this->state.forced_display = 0; /* 0 - No value, 1 - Forced Display. */
  this->state.delay = 0;
  cur_seq->finished=0;

  //printf("DISABLE FORCED DISPLAY\n");
  forced_display = 0; /* global variable in xine-engine/osd.c */

//  printf("spudec_process called\n");

  do {
	check_and_free_color_blocks();

    if (!(cur_seq->finished) ) {

      /* spu_channel is now set based on whether we are in the menu or not. */
      /* Bit 7 is set if only forced display SPUs should be shown */
      if ( (this->stream->spu_channel & 0x1f) != stream_id  ) {
#ifdef LOG_DEBUG
        printf ("spu: Dropping SPU channel %d. Not selected stream_id\n", stream_id);
#endif
        return;
      }
      /* parse SPU command sequence, this will update forced_display, so it must come
       * before the check for it */
      spudec_do_commands(&this->state, cur_seq, &this->overlay);
      /* FIXME: Check for Forced-display or subtitle stream
       *        For subtitles, open event.
       *        For menus, store it for later.
       */
      if (cur_seq->broken) {
        printf("libspudec: dropping broken SPU\n");
	cur_seq->broken = 0;
	return;
      }
      if ( (this->state.forced_display == 0) && (this->stream->spu_channel & 0x80) ) {

#ifdef LOG_DEBUG
        printf ("spu: Dropping SPU channel %d. Only allow forced display SPUs\n", stream_id);
#endif
        //return;
		spu_current_enable = 0;
      } else {
		spu_current_enable = 1;
	  }

#ifdef LOG_DEBUG
      /* spudec_print_overlay( &this->overlay ); */
      printf ("spu: forced display:%s\n", this->state.forced_display ? "Yes" : "No" );
#endif
      pthread_mutex_lock(&this->nav_pci_lock);
      if (this->pci.hli.hl_gi.hli_s_ptm == this->spudec_stream_state[stream_id].pts) {
        if(this->pci.hli.hl_gi.foac_btnn>0 && foac_end_time<=0) {
        	pthread_t	*th;
        	struct timeval 	tt;
        
			default_active_button=this->pci.hli.hl_gi.foac_btnn;
            active_spu_decoder=this;
            		gettimeofday(&tt,NULL);
			foac_end_time=tt.tv_sec+(this->pci.hli.hl_gi.btn_se_e_ptm-this->pci.hli.hl_gi.hli_s_ptm)/90000;
			//printf("foac_button=%d after %d seconds\n",this->pci.hli.hl_gi.foac_btnn,(this->pci.hli.hl_gi.btn_se_e_ptm-this->pci.hli.hl_gi.hli_s_ptm)/90000);

			pthread_create(&th,NULL,foac_btnn_thread,NULL);
        }

        if (this->state.visible == OVERLAY_EVENT_HIDE) {
          /* menus are hidden via nav packet decoding, not here */
	  /* FIXME: James is not sure about this solution and may want to look this over.
	   *        I'm commiting it, because I haven't found a disc it breaks, but it fixes
	   *        some instead.   Michael Roitzsch */
          pthread_mutex_unlock(&this->nav_pci_lock);
          continue;
        }
        if ( this->pci.hli.hl_gi.fosl_btnn > 0) {
          int buttonN;
	  xine_event_t event;

          this->buttonN = this->pci.hli.hl_gi.fosl_btnn ;
          event.type = XINE_EVENT_INPUT_BUTTON_FORCE;
	  event.stream = this->stream;
	  event.data = &buttonN;
	  event.data_length = sizeof(buttonN);
          buttonN  = this->buttonN;
          xine_event_send(this->stream, &event);
        }
#ifdef LOG_BUTTON
        fprintf(stderr, "libspudec:Full Overlay\n");
#endif
        spudec_copy_nav_to_overlay(&this->pci, this->state.clut, this->buttonN, 0, &this->overlay,
                                   &this->overlay);
      } else {
      /* Subtitle and not a menu button */
        int i;
        for (i = 0;i < 4; i++) {
          this->overlay.clip_color[i] = this->overlay.color[i];
          this->overlay.clip_trans[i] = this->overlay.trans[i];
        }
      }

      pthread_mutex_unlock(&this->nav_pci_lock);

      if ((this->state.modified) ) {
        spudec_draw_picture(&this->state, cur_seq, &this->overlay);
      }

      if (this->state.need_clut) {
        spudec_discover_clut(&this->state, &this->overlay);
      }

      /* Subtitle */
      if( this->menu_handle < 0 ) {
        this->menu_handle = ovl_manager->get_handle(ovl_manager,1);
      }

      if( this->menu_handle < 0 ) {
        printf("libspudec: No video_overlay handles left for menu\n");
        return;
      }
      this->event.object.handle = this->menu_handle;
      this->event.object.pts = this->spudec_stream_state[stream_id].pts;

      xine_fast_memcpy(this->event.object.overlay,
             &this->overlay,
             sizeof(vo_overlay_t));

#if 0
	  /* by kevin , copy an rle */
	  if ((this->overlay.data_size>0)&&(IS_SHOW_HIDE==0)&&this->event.object.overlay->rle!=NULL) {
		this->overlay.rle = malloc(this->overlay.data_size);
		xine_fast_memcpy(this->overlay.rle, 
			this->event.object.overlay->rle, this->overlay.data_size);
	  } else {
      	this->overlay.rle = NULL;
	  }
#else
	  this->overlay.rle = NULL; 
#endif
	  //printf("CLEAR RLE this->overlay=%p\n", &this->overlay);

      /* For force display menus */
      //if ( !(this->state.visible) ) {
      //  this->state.visible = OVERLAY_EVENT_SHOW;
      //}

      this->event.event_type = this->state.visible;
      /*
      printf("spu event %d handle: %d vpts: %lli\n", this->event.event_type,
         this->event.object.handle, this->event.vpts );
      */

      this->event.vpts = this->spudec_stream_state[stream_id].vpts+(this->state.delay*1000);

      /* Keep all the events in the correct order. */
      /* This corrects for errors during estimation around discontinuity */
	  //printf("VPTS NORMAL !!! this->event.vpts=%lld, this->last_event_vpts=%lld\n", 
	if (FORCE_CLEAR_FLAG==1) {
		this->last_event_vpts = 0; // kevin
		FORCE_CLEAR_FLAG = 0;
	}
	//				  this->event.vpts, this->last_event_vpts);
      if( this->event.vpts < this->last_event_vpts ) {
	//	printf("VPTS ERROR !!! this->event.vpts=%lld, this->last_event_vpts=%lld\n",
	//		this->event.vpts, this->last_event_vpts);
		//this->last_event_vpts = this->event.vpts; /* kevin for test disc */
        this->event.vpts = this->last_event_vpts + 1;
      }
      this->last_event_vpts = this->event.vpts;

#ifdef LOG_BUTTON
      fprintf(stderr, "libspudec: add_event type=%d : current time=%lld, spu vpts=%lli\n",
        this->event.event_type,
        this->stream->xine->clock->get_current_time(this->stream->metronom),
        this->event.vpts);
#endif

	  if (SPU_COUNTER==0) {
 		add_osd_command(OSD_CMD_SHOW, this->state.delay,  0); /* must be this->state.delay */
	  }

	  SPU_COUNTER++;
	  if (IS_SHOW!=0||IS_HIDE!=0) {
#if 0
		if (SPU_COUNTER<0) {
			this->state.modified = 1;
      		ovl_manager->add_event(ovl_manager, (void *)&this->event);
		} else
#endif
		if (FIRST_SHOW_HIDE) {
			this->state.modified = 1; // kevin
      		ovl_manager->add_event(ovl_manager, (void *)&this->event);
			FIRST_SHOW_HIDE = 0;
			FIRST_SHOW_HIDE_COUNTER = SPU_COUNTER;
		} else {
			if (IS_SHOW) {
				//printf("receive show !! delay=%d\n", this->state.delay);
	  			add_osd_command(OSD_CMD_SHOW, this->state.delay, 0);
			} else {
				//printf("receive hide !! delay=%d\n", this->state.delay);
				add_osd_command(OSD_CMD_HIDE, this->state.delay, 0);
			}
		}
	  } else {
		if (!IS_WIPE)
      		ovl_manager->add_event(ovl_manager, (void *)&this->event);
	  }
    } else {
      pending = 0;
    }
  } while (pending);
}

#define CMD_SPU_FORCE_DISPLAY	0x00
#define CMD_SPU_SHOW		0x01
#define CMD_SPU_HIDE		0x02
#define CMD_SPU_SET_PALETTE	0x03
#define CMD_SPU_SET_ALPHA	0x04
#define CMD_SPU_SET_SIZE	0x05
#define CMD_SPU_SET_PXD_OFFSET	0x06
#define CMD_SPU_WIPE		0x07  /* Not currently implemented */
#define CMD_SPU_EOF		0xff

dvdspu_color_block_t *
duplicate_cb(dvdspu_color_block_t *in_cb)
{
	dvdspu_color_block_t *cb;
    dvdspu_vert_block_t		*vb, *new_vb, *tail_vb;

	cb = (dvdspu_color_block_t *)malloc(sizeof(dvdspu_color_block_t));
	memcpy(cb, in_cb, sizeof(dvdspu_color_block_t));

	if (in_cb->vert_blocks!=NULL) {
		tail_vb = NULL;
		vb = in_cb->vert_blocks;
		while (vb) {
			new_vb = (dvdspu_vert_block_t *)malloc(sizeof(dvdspu_vert_block_t));
			memcpy(new_vb, vb, sizeof(dvdspu_vert_block_t));

			if (vb->hori_blocks!=NULL) {
				new_vb->hori_blocks = (dvdspu_hori_block_t *)
						malloc(sizeof(dvdspu_hori_block_t)*vb->num_hori_blocks);
				memcpy(new_vb->hori_blocks, vb->hori_blocks, sizeof(dvdspu_hori_block_t)*
								vb->num_hori_blocks);
			}

			if (tail_vb==NULL) {
				cb->vert_blocks = new_vb;
			} else {
				tail_vb->next = new_vb;
			}
			tail_vb = new_vb;
			vb = vb->next;
		}
		if (tail_vb!=NULL)
			tail_vb->next = NULL;
	}

	return cb;
}

static void 
add_osd_command(int osd_cmd, int delay, uint32_t value) /* 0 -> show */
{
    dvdspu_color_block_t    *cb, *dcb;
	cb=(dvdspu_color_block_t *)malloc(sizeof(dvdspu_color_block_t));
	cb->time_stamp = delay;
	cb->displayed=0;
	cb->signature=0x879A;
	cb->next=NULL;
	cb->osd_cmd = osd_cmd; /* show */
	cb->vert_blocks=NULL;
	cb->alpha_color = value;

	if (TAIL==NULL) {
		//printf("reset global_color_blocks=%p\n", cb);
		global_color_blocks = cb;
		TAIL = cb;
	} else {
		if (TAIL->vert_blocks!=NULL) {
			dcb = duplicate_cb(TAIL);
			dcb->time_stamp = delay;
			cb->next = dcb;
			TAIL->next = cb;
			TAIL = dcb;
		} else {
			TAIL->next = cb;
			TAIL = cb;
		}
	}
}


uint32_t 
pack_trans(uint8_t *trans)
{
	uint32_t v0, v1, v2, v3;
	v0 = trans[0];
	v1 = trans[1];
	v2 = trans[2];
	v3 = trans[3];
	return ((v0<<24)|(v1<<16)|(v2<<8)|(v3));
}


static void spudec_do_commands(spudec_state_t *state, spudec_seq_t* seq, vo_overlay_t *ovl)
{
  uint8_t *buf = state->cmd_ptr;
  uint8_t *next_seq;
  int32_t param_length;
  uint32_t value;
  dvdspu_color_block_t    *cb;

#ifdef LOG_DEBUG
  printf ("spu: SPU DO COMMANDS\n");
#endif

  state->delay = (buf[0] << 8) + buf[1];
#ifdef LOG_DEBUG
  printf ("spu: \tdelay=%d\n",state->delay);
#endif
  next_seq = seq->buf + (buf[2] << 8) + buf[3];
  buf += 4;
#ifdef LOG_DEBUG
  printf ("spu: \tnext_seq=%d\n",next_seq - seq->buf);
#endif

/* if next equals current, this is the last one
 */
  if (state->cmd_ptr >= next_seq)
    next_seq = seq->buf + seq->seq_len; /* allow to run until end */

  state->cmd_ptr = next_seq;

  IS_SHOW = 0;
  IS_HIDE = 0;
  IS_WIPE = 0;
  while (buf < next_seq && *buf != CMD_SPU_EOF) {
    switch (*buf) {
    case CMD_SPU_SHOW:		/* show subpicture */
		//printf("CMD_SPU_SHOW <-------------------------\n");
#ifdef LOG_DEBUG
      printf ("spu: \tshow subpicture\n");
#endif
      state->visible = OVERLAY_EVENT_SHOW;
      buf++;

		IS_SHOW = 1;
		//printf("SPU_IS_SHOW delay=%d\n", state->delay);
      break;

    case CMD_SPU_HIDE:		/* hide subpicture */
		//printf("CMD_SPU_HIDE <-------------------------\n");
#ifdef LOG_DEBUG
      printf ("spu: \thide subpicture\n");
#endif
      state->visible = OVERLAY_EVENT_HIDE;
      buf++;
		IS_HIDE = 1;
		//printf("SPU_IS_HIDE delay=%d\n", state->delay);
      break;

    case CMD_SPU_SET_PALETTE: {	/* CLUT */
      spudec_clut_t *clut = (spudec_clut_t *) (buf+1);

      state->cur_colors[3] = clut->entry0;
      state->cur_colors[2] = clut->entry1;
      state->cur_colors[1] = clut->entry2;
      state->cur_colors[0] = clut->entry3;

	/* This is a bit out of context for now */
      ovl->color[3] = state->clut[clut->entry0];
      ovl->color[2] = state->clut[clut->entry1];
      ovl->color[1] = state->clut[clut->entry2];
      ovl->color[0] = state->clut[clut->entry3];

#if 0
	  printf("CMD_SPU_SET_COLOR (%d,%d,%d,%d)\n",
			ovl->color[0],
			ovl->color[1],
			ovl->color[2],
			ovl->color[3]);
#endif

	  //value = pack_trans(ovl->color);
	  //add_osd_command(OSD_CMD_COLOR, state->delay, value);

#ifdef LOG_DEBUG
      printf ("spu: \tclut [%x %x %x %x]\n",
	      ovl->color[0], ovl->color[1], ovl->color[2], ovl->color[3]);
      printf ("spu: \tclut base [%x %x %x %x]\n",
	      clut->entry0, clut->entry1, clut->entry2, clut->entry3);
#endif
      state->modified = 1;
      buf += 3;
      break;
    }	
    case CMD_SPU_SET_ALPHA:	{	/* transparency palette */
      spudec_clut_t *trans = (spudec_clut_t *) (buf+1);
/* This should go into state for now */
	  if (state->delay==0) {
        ovl->trans[3] = trans->entry0;
        ovl->trans[2] = trans->entry1;
        ovl->trans[1] = trans->entry2;
        ovl->trans[0] = trans->entry3;
		enable_alpha = 0;
#if 0
        printf("CMD_SPU_SET_ALPHA delay=[%d](%d,%d,%d,%d)\n", state->delay,
					  ovl->trans[0], 
					  ovl->trans[1], 
					  ovl->trans[2], 
					  ovl->trans[3]);
#endif
	  } else {
		static uint8_t my_trans[4];
		my_trans[3] = trans->entry0;
		my_trans[2] = trans->entry1;
		my_trans[1] = trans->entry2;
		my_trans[0] = trans->entry3;
	    value = pack_trans(my_trans);
	    add_osd_command(OSD_CMD_ALPHA, state->delay, value);
#if 0
        printf("CMD_SPU_SET_ALPHA delay=[%d](%d,%d,%d,%d)\n", state->delay,
					  my_trans[0], 
					  my_trans[1], 
					  my_trans[2], 
					  my_trans[3]);
#endif
 	  }

#ifdef LOG_DEBUG
      printf ("spu: \ttrans [%d %d %d %d]\n",
	       ovl->trans[0], ovl->trans[1], ovl->trans[2], ovl->trans[3]);
#endif
      //state->modified = 1; /* mark by kevin */
      buf += 3;
      break;
    }

    case CMD_SPU_SET_SIZE:		/* image coordinates */
/*    state->o_left  = (buf[1] << 4) | (buf[2] >> 4);
      state->o_right = (((buf[2] & 0x0f) << 8) | buf[3]);

      state->o_top    = (buf[4]  << 4) | (buf[5] >> 4);
      state->o_bottom = (((buf[5] & 0x0f) << 8) | buf[6]);
 */
      ovl->x      = (buf[1] << 4) | (buf[2] >> 4);
      ovl->y      = (buf[4]  << 4) | (buf[5] >> 4);
      ovl->width  = (((buf[2] & 0x0f) << 8) | buf[3]) - ovl->x + 1; 
      ovl->height = (((buf[5] & 0x0f) << 8) | buf[6]) - ovl->y + 1;
      ovl->clip_top    = -1;
      ovl->clip_bottom = ovl->height - 1;
      ovl->clip_left   = 0;
      ovl->clip_right  = ovl->width - 1;

#ifdef LOG_DEBUG
      printf ("spu: \tx = %d y = %d width = %d height = %d\n",
	      ovl->x, ovl->y, ovl->width, ovl->height );
#endif
      state->modified = 1;
      buf += 7;
      break;

    case CMD_SPU_SET_PXD_OFFSET:	/* image top[0] field / image bottom[1] field*/
      state->field_offs[0] = (((u_int)buf[1]) << 8) | buf[2];
      state->field_offs[1] = (((u_int)buf[3]) << 8) | buf[4];

#ifdef LOG_DEBUG
      printf ("spu: \toffset[0] = %d offset[1] = %d\n",
	       state->field_offs[0], state->field_offs[1]);
#endif

      if ((state->field_offs[0] >= seq->seq_len) ||
          (state->field_offs[1] >= seq->seq_len)) {
        printf("libspudec:faulty stream\n");
        seq->broken = 1;
      }
      state->modified = 1;
      buf += 5;
      break;

    case CMD_SPU_WIPE:
#ifdef LOG_DEBUG
      printf ("libspudec: \tSPU_WIPE not implemented yet\n");
#endif

  	IS_WIPE = 1;
      param_length = (buf[1] << 8) | (buf[2]);
      {
      	/* added by rwlin@avamax.com */
      	//dvdspu_color_block_t    *cb,*cbprev;
      	dvdspu_vert_block_t		*vb,*vbtail;
      	spudec_clut_t			*clut,*trans;
      	int						i,n;
      	unsigned char			*sp;

		/* append each verticle block */
		for(vbtail=NULL,sp=buf+3;*(int *)sp!=0xFFFFFF0F;vbtail=vb) {
			vb=(dvdspu_vert_block_t *)malloc(sizeof(dvdspu_vert_block_t));
			if(vb==NULL)
				break;
			vb->next=NULL;
			vb->y=(sp[0] & 0x3) << 8 | sp[1];
			vb->height=(sp[2] & 0x3) << 8 | sp[3];
			vb->height-=vb->y;
			vb->num_hori_blocks=n=sp[2] >> 4;
			vb->hori_blocks=(dvdspu_hori_block_t *)malloc(sizeof(dvdspu_hori_block_t)*n);

			sp+=4;
			for(i=0;i<n;i++,sp+=6) {
				vb->hori_blocks[i].x=(sp[0] & 0x3) << 8 | sp[1];
				clut=(spudec_clut_t *)(sp+2);
				trans=(spudec_clut_t *)(sp+4);
		      	vb->hori_blocks[i].color[3] = state->clut[clut->entry0];
      			vb->hori_blocks[i].color[2] = state->clut[clut->entry1];
      			vb->hori_blocks[i].color[1] = state->clut[clut->entry2];
      			vb->hori_blocks[i].color[0] = state->clut[clut->entry3];
      			vb->hori_blocks[i].trans[3] = trans->entry0;
      			vb->hori_blocks[i].trans[2] = trans->entry1;
      			vb->hori_blocks[i].trans[1] = trans->entry2;
      			vb->hori_blocks[i].trans[0] = trans->entry3;
			}


			if(vbtail==NULL) {
				cb=(dvdspu_color_block_t *)malloc(sizeof(dvdspu_color_block_t));
				cb->time_stamp=state->delay;
				cb->displayed=0;
				cb->signature=0x879A;
				cb->next=NULL;
				cb->osd_cmd = OSD_CMD_COLORBLOCK; /* wiping show */
				cb->vert_blocks=vb;

				if (TAIL==NULL) {
					//printf("reset global_color_blocks=%p\n", cb);
					global_color_blocks = cb;
				} else {
					TAIL->next = cb;
				}
				TAIL = cb;
				//printf("1 color block added cb->time_stamp=%d\n", state->delay);
			} else
				vbtail->next=vb;
		}

		//pthread_mutex_unlock(&ovl->update_lock);

#if 0
		if(ovl->rle) {
			printf("\trle= ");
			for(i=0;i<ovl->num_rle;i++) {
				printf("(%d,%d)",ovl->rle[i].color,ovl->rle[i].len);
			}
			printf("\n");
		} else
			printf("\tthere's no rles in ovl %x\n",ovl);
#endif
	  }
	  //state->visible = OVERLAY_EVENT_SHOW;
	  //state->modified = 1;  // mark by kevin
      buf += 1 + param_length;
      break;

    case CMD_SPU_FORCE_DISPLAY:
#ifdef LOG_DEBUG
      printf ("libspudec: \tForce Display/Menu\n");
#endif
	  //printf("SPU FORCED DISPLAY delay=%d\n", state->delay);
      state->forced_display = 1;
  	  forced_display = 1; /* global variable in xine-engine/osd.c */
      buf++;
      break;

    default:
      printf("libspudec: unknown seqence command (%02x)\n", buf[0]);
      /* FIXME: SPU should be dropped, and buffers resynced */
      buf = next_seq;
      seq->broken = 1;
      break;
    }
  }
  if (next_seq >= seq->buf + seq->seq_len)
    seq->finished = 1;       /* last sub-sequence */
  
  state->cmd_ptr = next_seq;


}
/* FIXME: Get rid of all these static values */
static uint8_t *bit_ptr[2];
static int field;		// which field we are currently decoding
static int put_x, put_y;

static u_int get_bits (u_int bits)
{
  static u_int data;
  static u_int bits_left;
  u_int ret = 0;

  if (!bits) {	/* for realignment to next byte */
    bits_left = 0;
  }

  while (bits) {
    if (bits > bits_left) {
      ret |= data << (bits - bits_left);
      bits -= bits_left;

      data = *bit_ptr[field]++;
      bits_left = 8;
    } else {
      bits_left -= bits;
      ret |= data >> (bits_left);
      data &= (1 << bits_left) - 1;
      bits = 0;
    }
  }

  return ret;
}

static int spudec_next_line (vo_overlay_t *spu)
{
  get_bits (0); // byte align rle data
	
  put_x = 0;
  put_y++;
  field ^= 1; // Toggle fields
	
  if (put_y >= spu->height) {
#ifdef LOG_DEBUG
    printf ("spu: put_y >= spu->height\n");
#endif
    return -1;
  }
  return 0;
}

static void spudec_draw_picture (spudec_state_t *state, spudec_seq_t* seq, vo_overlay_t *ovl)
{
  rle_elem_t *rle;
  field = 0;
  bit_ptr[0] = seq->buf + state->field_offs[0];
  bit_ptr[1] = seq->buf + state->field_offs[1];
  put_x = put_y = 0;
  get_bits (0);	/* Reset/init bit code */

/*  ovl->x      = state->o_left;
 *  ovl->y      = state->o_top;
 *  ovl->width  = state->o_right - state->o_left + 1;
 *  ovl->height = state->o_bottom - state->o_top + 1;

 *  ovl->clip_top    = 0;
 *  ovl->clip_bottom = ovl->height - 1;
 *  ovl->clip_left   = 0;
 *  ovl->clip_right  = ovl->width - 1;
 */


  ovl->data_size = seq->cmd_offs * 2 * sizeof(rle_elem_t);

  if (ovl->rle) {
    //printf ("libspudec: spudec_draw_picture: ovl->rle is not empty!!!! It should be!!! You should never see this message.\n");
    free(ovl->rle);
    ovl->rle=NULL;
  }
  ovl->rle = malloc(ovl->data_size);

  state->modified = 0; /* mark as already processed */
  rle = ovl->rle;
#ifdef LOG_DEBUG
  printf ("libspudec: Draw RLE=%p,ovl=%x\n",rle,ovl);
#endif

  while (bit_ptr[1] < seq->buf + seq->cmd_offs) {
    u_int len;
    u_int vlc;

    vlc = get_bits (4);
    if (vlc < 0x0004) {
      vlc = (vlc << 4) | get_bits (4);
      if (vlc < 0x0010) {
		vlc = (vlc << 4) | get_bits (4);
		if (vlc < 0x0040) {
		  vlc = (vlc << 4) | get_bits (4);
		}
      }
    }

    len   = vlc >> 2;

    /* if len == 0 -> end sequence - fill to end of line */
    if (len == 0)
      len = ovl->width - put_x;

	/* debug code  if(vlc>0) vlc++;  */

    rle->len = len;
    rle->color = vlc & 0x03;
    rle++;
    put_x += len;

    if (put_x >= ovl->width) {
      if (spudec_next_line (ovl) < 0)
        break;
    }
  }

  ovl->num_rle = rle - ovl->rle;
  ovl->rgb_clut = 0;
#ifdef LOG_DEBUG
  printf ("spu: Num RLE=%d\n",ovl->num_rle);
  printf ("spu: Date size=%d\n",ovl->data_size);
  printf ("spu: sizeof RLE=%d\n",sizeof(rle_elem_t));
#endif

  //printf("ovl->rle=%p\n", ovl->rle);
}

/* Heuristic to discover the colors used by the subtitles
   and assign a "readable" pallete to them.
   Currently looks for sequence of border-fg-border or
   border1-border2-fg-border2-border1.
   MINFOUND is the number of ocurrences threshold.
*/
#define MINFOUND 20
static void spudec_discover_clut(spudec_state_t *state, vo_overlay_t *ovl)
{
  int bg,c;
  int seqcolor[10];
  int n,i;
  rle_elem_t *rle;

  int found[2][16];

  static clut_t text_clut[] = {
  CLUT_Y_CR_CB_INIT(0x80, 0x90, 0x80),
  CLUT_Y_CR_CB_INIT(0x00, 0x90, 0x00),
  CLUT_Y_CR_CB_INIT(0xff, 0x90, 0x00)
  };

  memset(found,0,sizeof(found));
  rle = ovl->rle;

  /* this seems to be a problem somewhere else,
     why rle is null? */
  if( !rle )
    return;

  /* suppose the first and last pixels are bg */
  if( rle[0].color != rle[ovl->num_rle-1].color )
    return;

  bg = rle[0].color;

  i = 0;
  for( n = 0; n < ovl->num_rle; n++ )
  {
    c = rle[n].color;

    if( c == bg )
    {
      if( i == 3 && seqcolor[1] == seqcolor[3] )
      {
        found[0][seqcolor[2]]++;
        if( found[0][seqcolor[2]] > MINFOUND )
        {
           memcpy(&state->clut[state->cur_colors[seqcolor[1]]], &text_clut[1],
             sizeof(clut_t));
           memcpy(&state->clut[state->cur_colors[seqcolor[2]]], &text_clut[2],
             sizeof(clut_t));
           ovl->color[seqcolor[1]] = state->clut[state->cur_colors[seqcolor[1]]];
           ovl->color[seqcolor[2]] = state->clut[state->cur_colors[seqcolor[2]]];
           state->need_clut = 0;
           break;
        }
      }
      if( i == 5 && seqcolor[1] == seqcolor[5]
             && seqcolor[2] == seqcolor[4] )
      {
        found[1][seqcolor[3]]++;
        if( found[1][seqcolor[3]] > MINFOUND )
        {
           memcpy(&state->clut[state->cur_colors[seqcolor[1]]], &text_clut[0],
             sizeof(clut_t));
           memcpy(&state->clut[state->cur_colors[seqcolor[2]]], &text_clut[1],
             sizeof(clut_t));
           memcpy(&state->clut[state->cur_colors[seqcolor[3]]], &text_clut[2],
             sizeof(clut_t));
           ovl->color[seqcolor[1]] = state->clut[state->cur_colors[seqcolor[1]]];
           ovl->color[seqcolor[2]] = state->clut[state->cur_colors[seqcolor[2]]];
           ovl->color[seqcolor[3]] = state->clut[state->cur_colors[seqcolor[3]]];
           state->need_clut = 0;
           break;
        }
      }
      i = 0;
      seqcolor[i] = c;
    }
    else if ( i < 6 )
    {
      i++;
      seqcolor[i] = c;
    }
  }
}

#ifdef LOG_DEBUG
static void spudec_print_overlay( vo_overlay_t *ovl ) {
  printf ("spu: OVERLAY to show\n");
  printf ("spu: \tx = %d y = %d width = %d height = %d\n",
	  ovl->x, ovl->y, ovl->width, ovl->height );
  printf ("spu: \tclut [%x %x %x %x]\n",
	  ovl->color[0], ovl->color[1], ovl->color[2], ovl->color[3]);
  printf ("spu: \ttrans [%d %d %d %d]\n",
	  ovl->trans[0], ovl->trans[1], ovl->trans[2], ovl->trans[3]);
  printf ("spu: \tclip top=%d bottom=%d left=%d right=%d\n",
	  ovl->clip_top, ovl->clip_bottom, ovl->clip_left, ovl->clip_right);
  printf ("spu: \tclip_clut [%x %x %x %x]\n",
	  ovl->clip_color[0], ovl->clip_color[1], ovl->clip_color[2], ovl->clip_color[3]);
  printf ("spu: \tclip_trans [%d %d %d %d]\n",
	  ovl->clip_trans[0], ovl->clip_trans[1], ovl->clip_trans[2], ovl->clip_trans[3]);
  return;
}
#endif

void spudec_copy_nav_to_overlay(pci_t* nav_pci, uint32_t* clut, int32_t button, int32_t mode,
                                vo_overlay_t * overlay, vo_overlay_t * base ) {
  btni_t *button_ptr = NULL;
  unsigned int btns_per_group;
  int i;

  /* FIXME: Need to communicate with dvdnav vm to get/set 
    "self->vm->state.HL_BTNN_REG" info. 
    now done via button events from dvdnav.
   *
   * if ( this->pci.hli.hl_gi.fosl_btnn > 0) {
   *   button = this->pci.hli.hl_gi.fosl_btnn ;
   * }
   */
  if((button <= 0) || (button > nav_pci->hli.hl_gi.btn_ns))
    return;
  
  btns_per_group = 36 / nav_pci->hli.hl_gi.btngr_ns;

  /* choose button group: we can always use a normal 4:3 or widescreen button group
   * as long as xine blends the overlay before scaling the image to its aspect */
  if (!button_ptr && nav_pci->hli.hl_gi.btngr_ns >= 1 && !(nav_pci->hli.hl_gi.btngr1_dsp_ty & 6))
    button_ptr = &nav_pci->hli.btnit[0 * btns_per_group + button - 1];
  if (!button_ptr && nav_pci->hli.hl_gi.btngr_ns >= 2 && !(nav_pci->hli.hl_gi.btngr2_dsp_ty & 6))
    button_ptr = &nav_pci->hli.btnit[1 * btns_per_group + button - 1];
  if (!button_ptr && nav_pci->hli.hl_gi.btngr_ns >= 3 && !(nav_pci->hli.hl_gi.btngr3_dsp_ty & 6))
    button_ptr = &nav_pci->hli.btnit[2 * btns_per_group + button - 1];
  if (!button_ptr) {
    printf("libspudec: No suitable menu button group found, using group 1.\n");
    button_ptr = &nav_pci->hli.btnit[button - 1];
  }
  
  /* button areas in the nav packet are in screen coordinates,
   * overlay clipping areas are in overlay coordinates;
   * therefore we must subtract the display coordinates of the underlying overlay */
  overlay->clip_left   = (button_ptr->x_start > base->x) ? (button_ptr->x_start - base->x) : 0;
  overlay->clip_top    = (button_ptr->y_start > base->y) ? (button_ptr->y_start - base->y) : 0;
  overlay->clip_right  = (button_ptr->x_end   > base->x) ? (button_ptr->x_end   - base->x) : 0;
  overlay->clip_bottom = (button_ptr->y_end   > base->y) ? (button_ptr->y_end   - base->y) : 0;
  if(button_ptr->btn_coln != 0) {
#ifdef LOG_BUTTON
    fprintf(stderr, "libspudec: normal button clut\n");
#endif
    for (i = 0;i < 4; i++) {
      overlay->clip_color[i] = clut[0xf & (nav_pci->hli.btn_colit.btn_coli[button_ptr->btn_coln-1][mode] >> (16 + 4*i))];
      overlay->clip_trans[i] = 0xf & (nav_pci->hli.btn_colit.btn_coli[button_ptr->btn_coln-1][mode] >> (4*i));
    }
  } else {
#ifdef LOG_BUTTON
    fprintf(stderr, "libspudec: abnormal button clut\n");
#endif
    for (i = 0;i < 4; i++) {
#ifdef LOG_BUTTON
      printf("libspudec:btn_coln = 0, clip_color = color\n");
#endif
      overlay->clip_color[i] = overlay->color[i];
      overlay->clip_trans[i] = overlay->trans[i];
    }
  }

  /* spudec_print_overlay( overlay ); */
#ifdef LOG_BUTTON
  printf("libspudec:xine_decoder.c:NAV to SPU pts match!\n");
#endif
}

