/*==============================================================================

FICHIER     : [dvdtools.c]

DATE        : 2005/12/0001 22:04:33

CREATEUR    : [Linux!jef]

COMMENTAIRE :
		Released under GPL license, see gnu.org
================================================================================

==============================================================================*/
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <gnome.h>

#define CTEST(x)		/***/
#define CTEST2(x)		/***/

#include "dvdinfo.h"
#include "dvdtools.h"
#include "message.h"
#include "globals.h"
#include "uitools.h"
#include "ac.h"
#include "badsect.h"

static double FramesPerSec[4] = { -1.0, 25.00, -1.0, 29.97 };
static char * _AudioFormat[7] = { "ac3", "?", "mpeg1", "mpeg2", "lpcm", "sdds ", "dts" };

#define MAX_PISTE		100
static Piste_t	Pistes[MAX_PISTE];

#define BCD2INT( v )		(((v & 0xf0) >> 3) * 5 + (v & 0x0f))

/*------------------------------------------------------------------------------
	MILLISECONDE-
Linux!jef 2005/12/16 00:10:15
------------------------------------------------------------------------------*/

int MilliSeconde( dvd_time_t * dt )
{
	double fps = FramesPerSec[(dt->frame_u & 0xc0) >> 6];
	long ms;

	ms = (((dt->hour & 0xf0) >> 3) * 5 + (dt->hour & 0x0f)) * 3600000;
	ms += (((dt->minute & 0xf0) >> 3) * 5 + (dt->minute & 0x0f)) * 60000;
	ms += (((dt->second & 0xf0) >> 3) * 5 + (dt->second & 0x0f)) * 1000;

	if (fps > 0) ms += (((dt->frame_u & 0x30) >> 3) * 5 + (dt->frame_u & 0x0f)) * 1000.0 / fps;

	return( ms );
}

/*------------------------------------------------------------------------------
	ISNAVPACK-
Linux!jef 2006/01/05 21:47:00
------------------------------------------------------------------------------*/

int isNavPack( unsigned char *_ptr )
{
	uint32_t start_code;
	unsigned char * ptr = _ptr;

	if((ptr [0]!=0) || (ptr [1] !=0) || (ptr [2] != 0x01) || (ptr [3] != 0xba))
		return( 0 );

	if((ptr [4] & 0xc0) != 0x40 )	return( 0 );

    // ptr += 14;

	start_code  = (uint32_t) (ptr [14]) << 24;
	start_code |= (uint32_t) (ptr [15]) << 16;
	start_code |= (uint32_t) (ptr [16]) <<  8;
	start_code |= (uint32_t) (ptr [17]);

	if( start_code != 0x000001bb )	return( 0 );

    //  ptr += 24;

	start_code  = (uint32_t) (ptr [0x26]) << 24;
	start_code |= (uint32_t) (ptr [0x27]) << 16;
	start_code |= (uint32_t) (ptr [0x28]) <<  8;
	start_code |= (uint32_t) (ptr [0x29]);

	if( start_code != 0x000001bf )	return( 0 );

    //  ptr += 986;

	start_code  = (uint32_t) (ptr [0x400]) << 24;
	start_code |= (uint32_t) (ptr [0x401]) << 16;
	start_code |= (uint32_t) (ptr [0x402]) <<  8;
	start_code |= (uint32_t) (ptr [0x403]);

	if( start_code != 0x000001bf )	return( 0 );

	return( 1 );
}

/*------------------------------------------------------------------------------
	IDENTIFYSTREAM-
Linux!jef 2006/01/18 22:27:22
------------------------------------------------------------------------------*/

int IdentifyStream( unsigned char * buffer, int * packetType )
{
    *packetType = buffer[17];

    if( (*packetType >= 0xE0) && (*packetType <= 0xEF) ) {                 // video streams
        return stVideo;
    } else if( *packetType == 0xBB ) {                                       // system header
        return stOther;
    } else if( *packetType == 0xBE ) {                                       // padding
        return stOther;
    } else if( *packetType == 0xBF ) {                                       // nav pack
        return stOther;
    } else if( (*packetType >= 0xC0) && (*packetType <= 0xDF) ) {            // mpeg audio
        return stAudio;
    } else if( *packetType == 0xBD ) {                                       // private stream, check content
        *packetType = buffer[23+buffer[22]];
        if (( (*packetType >=0x80) && (*packetType <=0x8f)) || ((*packetType >=0xa0) && (*packetType <=0xa7)) || ((*packetType >=0xc0) && (*packetType <=0xdf)))
            return stAudio;
        if ( (*packetType >=0x20) && (*packetType <=0x3f))
            return stSubpicture;
        return stOther;
    }
	return stOther;
}

/*------------------------------------------------------------------------------
	GETSTREAMID-
Linux!jef 2006/01/18 22:49:32
------------------------------------------------------------------------------*/

int GetStreamID( int type )
{
	int abase = 0;

	if (type >= 0x80 && type <= 0x87) {
	// AC3 audio
		abase = 0x80;
	} else if (type >= 0x88 && type <= 0x8f) {
	// DTS audio
		abase = 0x88;
	} else if (type >= 0xa0 && type <= 0xbf) {
	// LPCM audio
		abase = 0xa0;
	} else if (type >= 0xc0 && type <= 0xdf) {
	// MPEG audio
		abase = 0xc0;
	} else if (type >= 0x20 && type <= 0x3f) {
	//subpicture;
		abase = 0x20;
	}
	return( type - abase );
}

/*------------------------------------------------------------------------------
	GETPGC-
Linux!jef 2006/02/16 21:37:01
------------------------------------------------------------------------------*/

pgc_t * GetPgc( int noPisteVideo )
{
	pgcit_t *vts_pgcit;
	vtsi_mat_t *vtsi_mat;
	vmgi_mat_t *vmgi_mat;
	video_attr_t *video_attr;
	int title_set_nr;
	int vts_ttn;
	pgc_t *pgc;

	vtsi_mat = Ifo[Ifo_zero->tt_srpt->title[noPisteVideo].title_set_nr]->vtsi_mat;
	vts_pgcit = Ifo[Ifo_zero->tt_srpt->title[noPisteVideo].title_set_nr]->vts_pgcit;
	video_attr = &vtsi_mat->vts_video_attr;
	vts_ttn = Ifo_zero->tt_srpt->title[noPisteVideo].vts_ttn;
	vmgi_mat = Ifo_zero->vmgi_mat;
	title_set_nr = Ifo_zero->tt_srpt->title[noPisteVideo].title_set_nr;
	pgc = vts_pgcit->pgci_srp[Ifo[title_set_nr]->vts_ptt_srpt->title[vts_ttn - 1].ptt[0].pgcn - 1].pgc;

	return( pgc );
}

/*------------------------------------------------------------------------------
	BUILDCHAPTERSTRING-
Linux!jef 2005/12/16 00:12:54
------------------------------------------------------------------------------*/

char * BuildChapitreString( int noPisteVideo )
{
	pgc_t * pgc = GetPgc( noPisteVideo );
	int cell;
	int i;
	int nbChapters = pgc->nr_of_programs;
	char * chapitreStr;
	long long milliSec = 0;

// fprintf(stderr,"Il y a %d chapitre\n", nbChapters );
	chapitreStr = malloc( (nbChapters * 15)+1);
	strcpy( chapitreStr, "00:00:00.000" );
	cell = 0;
	for( i = 0; i < pgc->nr_of_programs; i++ ) {
		int next = pgc->program_map[i + 1];

		if (i == pgc->nr_of_programs - 1)	next = pgc->nr_of_cells + 1;
		while( cell < next - 1 ) {
			milliSec += MilliSeconde( &pgc->cell_playback[cell].playback_time );
			cell++;
		}
		if (i < (pgc->nr_of_programs - 1)) {
			char szDummy[128];

			sprintf (szDummy,",%02lld:%02lld:%02lld.%03lld",
				milliSec / 3600000,
				(milliSec / 60000) % 60,
				(milliSec / 1000) % 60,
				milliSec % 1000);
			strcat( chapitreStr, szDummy);
		}
	}
// fprintf(stderr,"BuildChapitreString(%s)\n", chapitreStr );
	return( chapitreStr );
}

/*------------------------------------------------------------------------------
	SAVEPALETTE-
Linux!jef 2005/12/15 21:59:05
------------------------------------------------------------------------------*/

void SavePalette( int noPisteVideo, char * pFile )
{
	FILE * fp;
	pgc_t * pgc = GetPgc( noPisteVideo );

	if( !pgc->palette )	return;

	fp = fopen( pFile, "w" );
	if( fp  ) {
		int i;

		for( i = 0; i < 16; i++ ) {
			fprintf( fp,"%06x\n", pgc->palette[i]);
		}
		fclose( fp );
	}
}

/*------------------------------------------------------------------------------
	AUDIOLANG-
Linux!jef 2005/12/16 22:07:24
------------------------------------------------------------------------------*/

char * AudioLang( int noPisteVideo, int noPisteAudio )
{
	static char lang_code[3];
	vtsi_mat_t *vtsi_mat;
	audio_attr_t *audio_attr;

	vtsi_mat = Ifo[Ifo_zero->tt_srpt->title[noPisteVideo].title_set_nr]->vtsi_mat;

	audio_attr = &vtsi_mat->vts_audio_attr[noPisteAudio];
	sprintf( lang_code, "%c%c", audio_attr->lang_code >> 8,audio_attr->lang_code & 0xff);
	return( lang_code );
}

/*------------------------------------------------------------------------------
	SUBLANG-
Linux!jef 2005/12/16 22:07:24
------------------------------------------------------------------------------*/

char * SubLang( int noPisteVideo, int noSubtitle )
{
	static char lang_code[3];
	vtsi_mat_t *vtsi_mat;
	subp_attr_t *subp_attr;

	vtsi_mat = Ifo[Ifo_zero->tt_srpt->title[noPisteVideo].title_set_nr]->vtsi_mat;

	subp_attr = &vtsi_mat->vts_subp_attr[noSubtitle];
	sprintf (lang_code, "%c%c", subp_attr->lang_code >> 8, subp_attr->lang_code & 0xff);
	if (!lang_code[0])	strcpy (lang_code, "xx");

	return( lang_code );
}


/*------------------------------------------------------------------------------
	AUDIOFORMAT-
Linux!jef 2005/12/16 22:07:24
------------------------------------------------------------------------------*/

char * AudioFormat( int noPisteVideo, int noPisteAudio )
{
	vtsi_mat_t *vtsi_mat;
	audio_attr_t *audio_attr;

	vtsi_mat = Ifo[Ifo_zero->tt_srpt->title[noPisteVideo].title_set_nr]->vtsi_mat;

	audio_attr = &vtsi_mat->vts_audio_attr[noPisteAudio];
//!!	if( (audio_attr->channels + 1) == 6 )	return( "dts" );
	return( _AudioFormat[audio_attr->audio_format] );
}
/***************************************************************************//**
 *	AUDIOSTREAMID-
 * @date	2009/06/03 22:19:15
 * @author	jef-Linux
 * @brief
 *	Pas d'explication
 * @details
 *	Pas de details
 * @param[in]	Pas de detail
 * @param[out]	Pas de detail
*******************************************************************************/
int AudioStreamId(  int noPisteVideo, int noPisteAudio )
{
	vtsi_mat_t *vtsi_mat;
	audio_attr_t *audio_attr;
	Piste_t * piste = GetPiste( noPisteVideo );
	pgc_t * pgc;
	int streamId;

	vtsi_mat = Ifo[Ifo_zero->tt_srpt->title[noPisteVideo].title_set_nr]->vtsi_mat;

	audio_attr = &vtsi_mat->vts_audio_attr[noPisteAudio];
/* StreamId is given in audio_control array of first PGC */
	pgc = piste->pgcs[0];
	streamId = pgc->audio_control[noPisteAudio];
	streamId = (streamId >> 8) & 0x07;

	switch( audio_attr->audio_format ) {
		case 0 : /* AC3 */
			return( 0x80 + streamId );
		case 2 : /* Mpeg-1 */
		case 3 : /* Mpeg-2 */
//			return( 0xC0 + streamId );
			return( 0x00 + streamId ); /* from man mencoder/-aid */
		case 4: /* LPCM */
			return( 0xA0 + streamId );
		case 6: /* DTS */
			return( 0x88 + streamId );
		default :
			return( 0x80 + streamId );
	}
}

/*------------------------------------------------------------------------------
	MYDVDREAD1BLOCK-
Linux!jef 2006/07/06 22:04:04
	Special for dvd errors handling
------------------------------------------------------------------------------*/
int MyDVDRead1Block( dvd_file_t * dvdFile, int noSect, void * buffer )
{
	ssize_t nbRead;

	CTEST2(fprintf(stderr,"%s: Reading(%d) at %p\n", __FUNCTION__, noSect, buffer ););
#if MNG_BADSECTORS
	if( IsBadSector( noSect ) )	return( 0 );
#endif
	nbRead = DVDReadBlocks( dvdFile, noSect, 1, buffer );
	CTEST2(fprintf(stderr,"%s: Readed:=%ld\n", __FUNCTION__, nbRead ););
	if( nbRead != 1 ) {
		DBG('b',fprintf(stderr,"%s: bad sector: %d\n", __FUNCTION__, noSect ););
		NoteBadSector( noSect );
		if( IgnoreErrors == -1 ) {
			int res = MessageBoxYesNo( _("Erreur de lecture ! Ignorer les erreurs ?" ) );
			if( res == 1 )
				IgnoreErrors = 1;
			else
				IgnoreErrors = 0;
			GtkFlush();
		}
		if( IgnoreErrors )	return( 0 );
		return( -1 );
	}
	return( 1 );
}

/*------------------------------------------------------------------------------
	GETPISTE-
Linux!jef 2006/11/21 23:55:54
------------------------------------------------------------------------------*/

Piste_t * GetPiste( int noPisteVideo )
{
	return( Pistes + noPisteVideo );
}
/*------------------------------------------------------------------------------
	RAZPISTES-
Linux!jef 2006/11/22 00:01:51
------------------------------------------------------------------------------*/

void RazPistes()
{
	memset( &Pistes, 0, sizeof(Pistes));
}
/*------------------------------------------------------------------------------
	CREATEDUMMYPACK-
Linux!jef 2007/08/16 00:41:38
------------------------------------------------------------------------------*/

void CreateDummyPack( unsigned char * buffer )
{
	int8_t *ptr = (int8_t*)buffer;
#pragma pack(1)
 	uint8_t dummy_pack [] =
        {
            /* pack header: SCR=0, mux rate=10080000bps, stuffing length=0 */
            0, 0, 1, 0xba, 0x44, 0x00, 0x04, 0x00, 0x04, 0x01, 0x01, 0x89, 0xc3, 0xf8,
            /* PES header for dummy video packet */
            0, 0, 1, 0xe0, 0x07, 0xec, 0x81, 0x00, 0x00
        };
#pragma pack()
 	tc_memcpy( ptr, dummy_pack, sizeof (dummy_pack));
 	ptr += sizeof (dummy_pack);
	memset( ptr, 0xff, DVD_VIDEO_LB_LEN - sizeof (dummy_pack));
}

/*------------------------------------------------------------------------------
	CREATEDUMMYNAVPACK-
Linux!jef 2007/08/16 00:43:54
------------------------------------------------------------------------------*/

void CreateDummyNavPack( unsigned char * buffer, unsigned int sector )
{
	int8_t *ptr = (int8_t*)buffer;
	dsi_t dsiPack;
	pci_t pciPack;
#pragma pack(1)
	static uint8_t nav_pack1 [] =
	{
	    /* pack header: SCR=0, mux rate=10080000bps, stuffing length=0 */
	    0, 0, 1, 0xba, 0x44, 0x00, 0x04, 0x00, 0x04, 0x01, 0x01, 0x89, 0xc3, 0xf8,
	    /* system header length=0x12=18*/
	    0, 0, 1, 0xbb, 0x00, 0x12,
	    /* contents of system header filled in at run time (18 bytes) */
	    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	    /* PES header for first private stream 2 packet */
	    0, 0, 1, 0xbf, 0x03, 0xd4
	  };
	static uint8_t nav_pack2 [] =
	{
	    /* PES header for second private stream 2 packet */
	    0, 0, 1, 0xbf, 0x03, 0xfa
	};
#pragma pack()
	tc_memcpy( ptr, nav_pack1, sizeof (nav_pack1));
	ptr += sizeof (nav_pack1);
	memset( ptr, 0, DVD_VIDEO_LB_LEN/2 - sizeof (nav_pack1));
	ptr = (int8_t*)buffer + DVD_VIDEO_LB_LEN/2;
	tc_memcpy( ptr, nav_pack2, sizeof (nav_pack2));
	ptr += sizeof (nav_pack2);
	memset( ptr, 0, DVD_VIDEO_LB_LEN/2 - sizeof (nav_pack2));

	navRead_DSI( &dsiPack, buffer + DSI_START_BYTE);
	navRead_PCI( &pciPack, buffer+0x2d);
	dsiPack.dsi_gi.nv_pck_lbn= sector;
	dsiPack.dsi_gi.vobu_ea = 1;

	navRead_DSI((dsi_t*)(buffer + DSI_START_BYTE),(unsigned char*)&dsiPack);
	pciPack.pci_gi.nv_pck_lbn = dsiPack.dsi_gi.nv_pck_lbn;
	navRead_PCI((pci_t*)(buffer+0x2d),(unsigned char*)&pciPack);
}
/*------------------------------------------------------------------------------
	VERIFNAVPACK-
Linux!jef 2007/08/22 22:35:53
------------------------------------------------------------------------------*/

int VerifNavPack( unsigned char * p )
{
	int32_t        bMpeg1 = 0;
	uint32_t       nHeaderLen;
	uint32_t       nPacketLen;
	uint32_t       nStreamID;

	if (p[3] == 0xBA) { /* program stream pack header */
		int32_t nStuffingBytes;

		bMpeg1 = (p[4] & 0x40) == 0;

		if (bMpeg1) {
			p += 12;
		} else { /* mpeg2 */
			nStuffingBytes = p[0xD] & 0x07;
			p += 14 + nStuffingBytes;
		}
	}

	if (p[3] == 0xbb) { /* program stream system header */
		nHeaderLen = (p[4] << 8) | p[5];
		p += 6 + nHeaderLen;
	}

/* we should now have a PES packet here */
	if (p[0] || p[1] || (p[2] != 1)) {
		fprintf(stderr, "%s: demux error! %02x %02x %02x (should be 0x000001) \n",__FUNCTION__,p[0],p[1],p[2]);
		return( 0 );
	}

	nPacketLen = p[4] << 8 | p[5];
	nStreamID  = p[3];

	nHeaderLen = 6;
	p += nHeaderLen;

	if (nStreamID == 0xbf) { /* Private stream 2 */
//		if(p[0] == 0x00)	navRead_PCI(nav_pci, p+1);
		p += nPacketLen;
	/* We should now have a DSI packet. */
		if(p[6] == 0x01) {
			nPacketLen = p[4] << 8 | p[5];
			p += 6;
//			navRead_DSI(nav_dsi, p+1);
		}
		return( 1 );
	}
	fprintf(stderr,"%s: bad streamid 0x%x\n", __FUNCTION__, nStreamID );
	return( 0 );
}

/***************************************************************************//**
 *	DUMPPGC-
 * @date	2010/05/04 22:43:14
 * @author	jef-Linux
 * @brief
 *	Pas d'explication
 * @details
 *	Pas de details
 * @param[in]	Pas de detail
 * @param[out]	Pas de detail
*******************************************************************************/
void DumpPgc( pgc_t * pgc )
{
	int i;
	int sec;

	fprintf( stderr, "**************** PGC %p DUMP ********************\n", pgc );
	fprintf( stderr, "nr_of_programs=%d\n", pgc->nr_of_programs );
	fprintf( stderr, "nr_of_cells=%d\n", pgc->nr_of_cells );
	sec = BCD2INT( pgc->playback_time.hour ) * 3600;
	sec += BCD2INT( pgc->playback_time.minute ) * 60;
	sec += BCD2INT( pgc->playback_time.second );
	fprintf( stderr, "dvd_time_t playback_time=%d\n", sec );
	for( i = 0; i < 8; i++ )	fprintf(stderr,"audio_control[%d]=%x\n", i, pgc->audio_control[i] );
	for( i = 0; i < 32; i++ )	fprintf(stderr,"subp_control[%d]=%x\n", i, pgc->subp_control[i] );
	fprintf( stderr, "next_pgc_n=%d\n", pgc->next_pgc_nr );
	fprintf( stderr, "prev_pgc_nr=%d\n",pgc->prev_pgc_nr);
	fprintf( stderr, "goup_pgc_nr=%d\n",pgc->goup_pgc_nr);
	fprintf( stderr, "still_time=%d\n",pgc->still_time);
	fprintf( stderr, "pg_playback_mode=%d\n", pgc->pg_playback_mode );
	fprintf( stderr, "command_tbl_offset=%d\n", pgc->command_tbl_offset);
	fprintf( stderr, "program_map_offset=%d\n", pgc->program_map_offset);
	fprintf( stderr, "cell_playback_offset=%d\n", pgc->cell_playback_offset);
	fprintf( stderr, "cell_position_offset=%d\n", pgc->cell_position_offset);
//  pgc_command_tbl_t *command_tbl;
//  pgc_program_map_t  *program_map;
	for( i = 0; i < pgc->nr_of_cells; i++ ) {
		cell_playback_t * c = &pgc->cell_playback[i];

		fprintf(stderr,"cell(%d): block_mode=%d block_type=%d first_sector=%d last_sector=%d\n", i, c->block_mode, c->block_type, c->first_sector, c->last_sector );
	}
	for( i = 0; i < pgc->nr_of_cells; i++ ) {
		cell_position_t * c = &pgc->cell_position[i];

		fprintf(stderr,"cell(%d): vob_id_nr=%d cell_nr=%d\n", i, c->vob_id_nr, c->cell_nr );
	}
	fprintf( stderr, "*************************************************\n" );
}

/***************************************************************************//**
 *	CHECKPGC-
 * @date	2010/05/04 23:11:16
 * @author	jef-Linux
 * @brief
 *	Try to detect bad pgc with duplicated cells
 * @details
 *	Pas de details
 * @param[in]	Pas de detail
 * @param[out]	Pas de detail
*******************************************************************************/
int CheckPgc( pgc_t * pgc )
{
	int i,j;

	for( i = 0; i < pgc->nr_of_cells; i++ ) {
		cell_position_t * c1 = &pgc->cell_position[i];

		for( j = i+1; j < pgc->nr_of_cells; j++ ) {
			cell_position_t * c2 = &pgc->cell_position[j];

			if( c1->vob_id_nr == c2->vob_id_nr && c1->cell_nr == c2->cell_nr )	return( -1 ); /* Duplicated cell ! */

		}
	}
	return( 0 );
}
