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

FICHIER     : [dvdbackup.c]

DATE        : 2006/01/0005 20:56:20

CREATEUR    : [Linux!jef]

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

==============================================================================*/
#define __USE_LARGEFILE64
#define _LARGEFILE64_SOURCE
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <alloca.h>

#include <gnome.h>

#ifdef __APPLE__
/* fopen is 64-bit aware on Mac OS X */
#define fopen64(A,B) fopen(A,B)
#endif

#define CTEST(x)		/***/

/*
0 : Normal operation
1 : Test file /tmp/xxx.vob
2 : Gen file /tmp/xxx.vob
*/

#define TEST_AUTHOR		0

#include "support.h"

#include "const.h"
#include "dvdformat.h"
#include "dvdinfo.h"
#include "message.h"
#include "gconfig.h"
#include "interface.h"
#include "globals.h"
#include "systools.h"
#include "avancement.h"
#include "uitools.h"
#include "dvdtools.h"
#include "dvdcopy.h"
#include "ifo.h"
#include "bswap.h"
#include "vaporize.h"
#include "ac.h"
#include "dvdcell.h"
#include "../dvdauthor/libauthor.h"
#include "vapcontext.h"
#include "mpeg2dec.h"

static char TitreDvd[128];

static CellArray_t CellArray;
static TitleArray_t TitleArray;

static TitleSet_t * CurTS = NULL;
static Cell_t * CurCell = NULL;

static int CurVTS;
static FILE * OutFp;
static long long OutSize;
static long long TotalSize;

static int CurVOB;
static uint32_t Position;

static unsigned char TempBuf[DVD_VIDEO_LB_LEN * 2];
static int TempSize = 0;

static double AuthorlPct = -1;
static int AuthorFixStage = 0;
static long long AuthorSize;

/*@$#[dvd2vob.c] static proto. AutoProtoSigV1.1. date: 2009/02/04 23:14:33 */
#include "proto.h"
#ifdef __cplusplus
extern "C" {
#endif
/* dvd2vob.c */
static void AddVobus PROTO((Cell_t *c, char *buffer, int len, uint32_t position, int curVob));
static int Output PROTO((unsigned char *buffer, int len, char *targetDir));
static void FindCellStart PROTO((Cell_t *c));
static void MakeRoom PROTO((VapContext_t *c, int size));
static uint32_t _FindNextVobu PROTO((VapContext_t *c));
static int Readin PROTO((void *parm, unsigned char *buffer, int size));
static int OutputFlux PROTO((void *parm, unsigned char *buffer, int size));
static int CopyCell PROTO((Cell_t *c, char *targetDir));
static void FreeAll PROTO((void));
static char *BuildAudioString PROTO((int noPisteVideo, AudioMap_t *audioMap));
static char *BuildFormatString PROTO((int noPisteVideo));
static char *BuildSubString PROTO((int noPisteVideo, SubMap_t *subMap));
static void AuthorProgress PROTO((char *string));
static int ExecuteDvdAuthor PROTO((int argc, char **argv, long long size));
static double ReCpuFactor PROTO((void));
#ifdef __cplusplus
}
#endif
/*@$% end of AutoProtoSigV1.1 (Dont remove this line) [-I ../include]*/

/*------------------------------------------------------------------------------
	ADDVOBUS-
Linux!jef 2006/01/16 22:58:40
------------------------------------------------------------------------------*/

static void AddVobus( Cell_t * c, char * buffer, int len, uint32_t position, int curVob )
{
	Vobu_t * v;
	int i;

	for( i= 0 ; i < len ; i += DVD_BLOCK_LEN ) {
		if( isNavPack((unsigned char *)buffer+i) ) {
			v = c->va.vobus + c->numVobu;
			v->newSector = i/DVD_BLOCK_LEN + position;
			c->numVobu++;
		}
		else {
			int st;
			int packetType,id;

			st =  IdentifyStream( (unsigned char *)buffer+i, &packetType);
			v = c->va.vobus + c->numVobu -1;
			switch (st) {
				case stAudio:
					id = GetStreamID(packetType);
					if( v->firstAudio[id] == -1 ) {
						v->firstAudio[id]= ((i/ DVD_BLOCK_LEN) + position) - v->newSector;
					}
					break;
				case stSubpicture:
					id = GetStreamID( packetType );
					if ((id >=0) && (id<32)) { /* here 20 */
						if( v->firstSubp[id] == -1 ) {
							v->firstSubp[id]= ((i / DVD_BLOCK_LEN) + position) - v->newSector;
// fprintf(stderr,"v->firstSubp[%d]=%d\n", id, v->firstSubp[id] );
						}
					}
					break;
				case stVideo:
					if( v->firstVideo == -1 ) {
						v->firstVideo =  ((i / DVD_BLOCK_LEN) + position) - v->newSector;
					}
//					c->nbVideoNew++;
					break;
			}
		}
		v->size = position - v->newSector;
		c->lastSector = position;
//!!		DBG('b',fprintf(stderr,"AddVobus: %d size: %u\n", v->idx, v->size ););
	}
}

/*------------------------------------------------------------------------------
	OUTPUT-
Linux!jef 2006/01/03 21:05:50
------------------------------------------------------------------------------*/

static int Output( buffer, len, targetDir )
unsigned char * buffer;
int len;
char * targetDir;
{
	unsigned char * cBuff;

	if( !TempSize && len == DVD_VIDEO_LB_LEN ) {
		TempSize = len;
		cBuff = buffer;
	}
	else {
//!! fprintf(stderr,"Output: len %d TempSize: %d\n", len, TempSize );
		tc_memcpy( TempBuf + TempSize, buffer, len );
		TempSize += len;
		cBuff = TempBuf;
	}
	if( TempSize >= DVD_VIDEO_LB_LEN ) {
		if( !isNavPack( cBuff ) ) {
			Mpeg2Demux( cBuff, DVD_VIDEO_LB_LEN );
		}
		if( fwrite( cBuff, 1, DVD_VIDEO_LB_LEN, OutFp ) != DVD_VIDEO_LB_LEN )	return( -1 );
		OutSize += DVD_VIDEO_LB_LEN;
		TotalSize += DVD_VIDEO_LB_LEN;
		SetTotalSize( TotalSize );
		AddVobus( CurCell, (char *)cBuff, DVD_VIDEO_LB_LEN, Position, CurVOB );
		CurTS->lastSector++;
		Position++;
		TempSize -= DVD_VIDEO_LB_LEN;
		if( TempSize > 0 )	tc_memcpy( cBuff, cBuff + DVD_VIDEO_LB_LEN, TempSize );
	}
	return( 0 );
}
/*------------------------------------------------------------------------------
	FINDCELLSTART-
Linux!jef 2006/01/16 21:43:42
------------------------------------------------------------------------------*/

static void FindCellStart( Cell_t * c )
{
	if( !c->idx ) {
		c->startSector = 0;
	}
	else {
		Cell_t * p = &c[-1];

		if( p->vts == c->vts )
			c->startSector = p->lastSector + 1;
		else
			c->startSector = 0; /* !! */
	}
}
/*------------------------------------------------------------------------------
	MAKEROOM-
Linux!jef 2006/01/24 22:46:53
------------------------------------------------------------------------------*/

static void MakeRoom( VapContext_t * c, int size )
{
//	DBG('b',fprintf(stderr,"%s: size: %d\n", __FUNCTION__, size ););

	if( c->bufASize < (c->bufSize + size) ) {
		if( c->bufPtr != c->buf ) {
			memmove( c->buf, c->bufPtr, c->bufSize );
			c->bufPtr = c->buf;
		}
		c->bufASize = c->bufSize + size;
		c->buf = realloc( c->buf, c->bufASize );
		c->bufPtr = c->buf;
	}
	if( !c->bufSize ) {
		c->bufPtr = c->buf;
		return;
	}
}

/*------------------------------------------------------------------------------
	_FINDNEXTVOBU-
Linux!jef 2007/08/16 01:37:18
------------------------------------------------------------------------------*/

static uint32_t _FindNextVobu( c )
VapContext_t * c;
{
	vobu_admap_t * vobu_admap;
	uint32_t length, i;

	vobu_admap = c->cell->ifo->vts_vobu_admap;
	length = vobu_admap->last_byte + 1 - VOBU_ADMAP_SIZE;

	for( i = 0; i < length/sizeof(uint32_t); i++) {
// DBG('b',fprintf(stderr,"%s: vobu at sector: %d\n", __FUNCTION__, vobu_admap->vobu_start_sectors[i] ););
/* Vobu adress map seems to be ordered */
		if( vobu_admap->vobu_start_sectors[i] > c->sector) {
			DBG('b',fprintf(stderr,"%s: returning next vobu at sector: %d\n", __FUNCTION__, vobu_admap->vobu_start_sectors[i] ););
			return( vobu_admap->vobu_start_sectors[i] );
		}
//		if( vobu_admap->vobu_start_sectors[i] == c->sector) {
//			return( vobu_admap->vobu_start_sectors[i+1] );
//		}
	}
	DBG('b',fprintf(stderr,"%s: nextvobu notfound for sector: %d\n", __FUNCTION__, c->sector ););
	return( SRI_END_OF_CELL );
}

/*------------------------------------------------------------------------------
	READIN-
Linux!jef 2006/01/24 22:28:46
------------------------------------------------------------------------------*/

static int Readin( void * parm, unsigned char * buffer, int size )
{
	VapContext_t * c = (VapContext_t *) parm;
	int readed = 0;
	int len;
	dsi_t	dsi_pack;
	uint32_t nsectors;
	int badNavPack = 0;

//	DBG('b',fprintf(stderr,"%s: size: %d inBuf: %d\n", __FUNCTION__, size, c->bufSize ););

	if( c->bufSize ) {
		len = MyMin( size, c->bufSize );

		tc_memcpy( buffer, c->bufPtr, len );
		buffer += len;
		size -= len;
		c->bufPtr += len;
		c->bufSize -= len;
		readed += len;
	}
	if( !size ) {
//		DBG('b',fprintf(stderr,"%s: readed: %d\n", __FUNCTION__, readed ););
		return( readed );
	}

	if( c->dsi_next_vobu == SRI_END_OF_CELL ) {
		if( readed )	return( readed );

//		DBG('b',fprintf(stderr,"%s: EOF\n", __FUNCTION__ ););
		return( 0 ); /* EOF */
	}

/* read nav pack */
	MakeRoom( c, DVD_VIDEO_LB_LEN );
	len = MyDVDRead1Block( c->file_handle, c->sector, c->bufPtr );
	if( len == -1 )	return( -1 );
/* parse contained DSI pack */
	navRead_DSI( &dsi_pack, c->bufPtr + DSI_START_BYTE );
	if( len == 0 || dsi_pack.dsi_gi.nv_pck_lbn != c->sector ) {
		CreateDummyNavPack( c->bufPtr, c->sector );
		badNavPack = 1;
		c->dsi_next_vobu = _FindNextVobu( c );
	}

	len = DVD_VIDEO_LB_LEN;
	c->bufSize += len;

	Avancement( len );
	if( AvancementUi() < 0 )	return( -1 );
	if( isNavPack( c->bufPtr ) ) {
		Vobu_t * v;

		CurCell->oldLastSector = c->sector;
		v = NewVobu( &CurCell->va, c->sector );
	}

	if( badNavPack ) {
		MakeRoom( c, 1 * DVD_VIDEO_LB_LEN );
		CreateDummyPack( c->bufPtr + DVD_VIDEO_LB_LEN );
		nsectors = 1;
	}
	else {
		int i;
		unsigned char * cBuff;

		nsectors  = dsi_pack.dsi_gi.vobu_ea;
		if( !nsectors ) { /* Protected dvd ? */
			DBG('b',fprintf(stderr,"%s: no sectors in DSI !\n", __FUNCTION__););
			return( 0 ); /* EOF */
		}
		c->dsi_next_vobu = dsi_pack.vobu_sri.next_vobu;

		MakeRoom( c, nsectors*DVD_VIDEO_LB_LEN );
		cBuff = c->bufPtr + DVD_VIDEO_LB_LEN;
/* read VOBU */
		for( i = 0; i < nsectors; i++ ) {
			len = MyDVDRead1Block( c->file_handle, c->sector + 1 + i, cBuff );
// fprintf(stderr,"PACK: MyDVDRead1Block(%d)=%d\n", c->sector+1+i, len );
			if( len == -1 )	return( -1 );
			if( len == 0 ) {
				CreateDummyPack( cBuff );
				nsectors = 1;
				break;
			}
			cBuff += DVD_VIDEO_LB_LEN;
		}
	}
	len = nsectors * DVD_VIDEO_LB_LEN;
	c->bufSize += len;

	Avancement( len );
	if( AvancementUi() < 0 )	return( -1 );
	if( badNavPack ) {
		c->sector = c->dsi_next_vobu;
		c->dsi_next_vobu  = 0;
	}
	else
		c->sector += (c->dsi_next_vobu & 0x7fffffff);

	return( readed + Readin( parm, buffer, size ) );
}

/*------------------------------------------------------------------------------
	OUTPUTFLUX-
Linux!jef 2006/01/24 23:07:28
------------------------------------------------------------------------------*/

static int OutputFlux( void * parm, unsigned char * buffer, int size )
{
	VapContext_t * c = (VapContext_t *) parm;
	int writen = 0;

//	DBG('b',fprintf(stderr,"%s: size: %d\n", __FUNCTION__, size ););
	while( size > 0 ) {
		int len = MyMin( size, DVD_VIDEO_LB_LEN );

		if( Output( buffer, len, c->targetDir ) < 0 ) {
			return( -1 );
		}
		buffer += len;
		size -= len;
		writen += len;
	}
	return( writen );
}

/*------------------------------------------------------------------------------
	COPYCELL-
Linux!jef 2005/12/28 22:01:08
------------------------------------------------------------------------------*/

static int CopyCell( Cell_t * c, char * targetDir )
{
	uint32_t size;
	dvd_file_t * file_handle;
	Cell_t * cell;
	VapContext_t vc;
	long long lSize;
	int ret;

	if( !OutFp ) {
		char vobFile[512];
		int menuSize = 0;
		uint32_t startSector;

		sprintf( vobFile, "%s.vob", targetDir );
		OutFp = fopen64( vobFile, "w" );
		if( !OutFp )	return( -1 );
		OutSize = 0;
		CurVOB = 1;
		CurVTS = c->vts;

		if( CurTS ) {
			startSector = CurTS->startSector + CurTS->lastSector + 1; /* !! */
		}
		else {
			startSector = Ifo_zero->vmgi_mat->vmg_last_sector + 1;
		}
		CurTS = NewTitleSet( &TitleArray );
		CurTS->vts = CurVTS;
		CurTS->startSector = startSector;
		CurTS->lastSector += menuSize;
		CurTS->ifo = Ifo[CurVTS];
	}

	cell = NewCell( &CurTS->ca );
	cell->vts = CurVTS;
	cell->pgc = c->pgc;
	FindCellStart( cell );
	CurCell = cell;
	CurCell->oldStartSector = c->startSector;
	CurCell->oldLastSector = c->lastSector;
	Position = CurCell->startSector;

	if( !c->selected )	return( 0 );

	DBG('b',fprintf(stderr,"%s: startSector: %u lastSector: %u vts: %d\n", __FUNCTION__, c->startSector, c->lastSector, c->vts ););

	size = c->lastSector - c->startSector;
	DisplayAvancement( _("Copie de la cellule %d (%d secteurs)\n"), c->chapter + 1, size);
	InitAvancement( size );
	file_handle = DVDOpenFile( Dvd, c->vts, DVD_READ_TITLE_VOBS);
	if( !file_handle ) {
		return( -1 );
	}
	memset( &vc, 0, sizeof(vc));
/* Initialize Vaporize context */
	vc.file_handle = file_handle;
	vc.sector = c->startSector;
	vc.cell = c;
	vc.targetDir = targetDir;
//	lSize = (long long) size * (long long) DVD_VIDEO_LB_LEN;
/* Vaporize need total sector size */
	lSize = CountTotalSectors( &CellArray ) * (long long) DVD_VIDEO_LB_LEN;
	ret = Vaporize( lSize, &vc );

	if( vc.buf ) {
		free( vc.buf );
	}
	DVDCloseFile( file_handle );
	c->done = 1;
	return( ret );
}

/*------------------------------------------------------------------------------
	FREEALL-
Linux!jef 2006/01/16 22:10:25
------------------------------------------------------------------------------*/

static void FreeAll()
{
	int i;

	CurTS = NULL;
	CurCell = NULL;

	if( TitleArray.nbTitles ) {
		for( i = 0; i < TitleArray.nbTitles; i++ ) {
			FreeCellArray( &TitleArray.titles[i].ca );
		}
		free( TitleArray.titles );
		TitleArray.titles = NULL;
		TitleArray.nbTitles = 0;
	}
	FreeCellArray( &CellArray );
}
/*------------------------------------------------------------------------------
	BUILDAUDIOSTRING-
Linux!jef 2006/02/16 21:55:22
------------------------------------------------------------------------------*/

static char * BuildAudioString( int noPisteVideo, AudioMap_t * audioMap )
{
	char audioString[1024];
	int i;

	*audioString = 0;
	for( i = 0; i < MAX_AUDIO_TRACK; i++ ) {
		if( audioMap->audioTracks[i] ) {
			char * lang;

			if( *audioString )	strcat( audioString,"," );
			strcat( audioString, AudioFormat( noPisteVideo, i ));
			strcat( audioString, "+" );
			lang = AudioLang( noPisteVideo, i );
			if( !*lang )
				strcat( audioString, "nolang" );
			else
				strcat( audioString, AudioLang( noPisteVideo, i ) );
		}
	}
	return( strdup( audioString ) );
}

/*------------------------------------------------------------------------------
	BUILDFORMATSTRING-
Linux!jef 2007/01/29 22:39:05
------------------------------------------------------------------------------*/

static char * BuildFormatString( int noPisteVideo )
{
	char *video_format[2] = {"ntsc", "pal"};
	char *aspect_ratio[4] = {"4:3", "16:9", "4:3", "16:9"};
	char *video_height[4] = {"480", "576", "576", "576"};
	char *video_width[4]  = {"720", "704", "352", "352"};
	char result[1024];

	ifo_handle_t * ifo = Ifo[Ifo_zero->tt_srpt->title[noPisteVideo].title_set_nr];
	vtsi_mat_t *vtsi_mat = ifo->vtsi_mat;
	video_attr_t *video_attr;

	video_attr = &vtsi_mat->vts_video_attr;

	if( *aspect_ratio[video_attr->display_aspect_ratio] == '4' )
		sprintf( result, "%s+%s+%sx%s",
			video_format[video_attr->video_format],
			aspect_ratio[video_attr->display_aspect_ratio],
			video_width[video_attr->picture_size],
			video_height[video_attr->video_format] );
	else
		sprintf( result, "%s+%s+nopanscan+%sx%s",
			video_format[video_attr->video_format],
			aspect_ratio[video_attr->display_aspect_ratio],
			video_width[video_attr->picture_size],
			video_height[video_attr->video_format] );

	DBG('c',fprintf(stderr,"%s: (%s)\n", __FUNCTION__, result ););
	return( strdup( result ) );
}


/*------------------------------------------------------------------------------
	BUILDSUBSTRING-
Linux!jef 2006/02/16 21:55:22
------------------------------------------------------------------------------*/

static char * BuildSubString( int noPisteVideo, SubMap_t * subMap )
{
	char subString[1024];
	int i;

	*subString = 0;
	for( i = 0; i < MAX_SUB_TRACK; i++ ) {
		if( subMap->subTracks[i] ) {
			if( *subString )	strcat( subString,"," );
			strcat( subString, SubLang( noPisteVideo, i ) );
		}
	}
	return( strdup( subString ) );
}

#if 0
/*------------------------------------------------------------------------------
	_READLINE-
Linux!jef 2006/02/16 22:58:23
------------------------------------------------------------------------------*/

static int _readline( FILE * fp, char * line, int size )
{
	int readed = 0;

	size--;
	while( size ) {
		int c = fgetc( fp );

		if( c == EOF )	break;
		if( c == '\r' )	c = '\n';
		*line++ = (char)c;
		readed++;
		size--;
		if( c == '\n' )	break;
	}
	*line = 0;
	return( readed );
}
/*------------------------------------------------------------------------------
	EXECUTEDVDAUTHOR-
Linux!jef 2005/12/20 22:17:17
------------------------------------------------------------------------------*/

static int ExecuteDvdAuthor( char * sysCmd, long long size )
{
	FILE * fp;
	char line[1024];
	int res = 0;
	double lPct = -1;
	int fixStage = 0;

	InitAvancement(0);
/* re-direct stderr */
	strcat( sysCmd, " 2>&1" );
	DBG('c',fprintf(stderr,"%s: executing (%s) size: %lld\n", __FUNCTION__, sysCmd, size ););
	fp = my_popen( sysCmd, "r" );
	if( !fp )	 return( -1 );
	setbuf( fp, NULL );
	while( 1 )
	{
		char * p;

		if( !_readline( fp, line, sizeof(line)))	break;
		if( !*line )	continue;
//!!fprintf(stderr,"line(%s)\n", line );
		p = strchr( line, '%' );
		if( p ) {
			double pct;

			if( !fixStage ) {
				fixStage = 1;
				DisplayAvancement( _("Fixation de la structure du DVD.\n") );
				InitAvancement(0);
			}
			p = strrchr( line, ' ' );
			pct = atoi( p + 1 );
			pct /= 100;
			if( AvancementUi2( pct ) < 0 ) { res = -1; break; }
			continue;
		}
		p = strrchr( line, 'U' ); /* find VOBU */
		if( !p )	continue;
		p = strstr( p, "at " );
		if( p ) {
			long long doneSize;
			double pct;

			p += 3;
			doneSize = atoi( p );
			doneSize *= 1024 * 1024;
			pct = (double)doneSize / (double)size;
// fprintf(stderr,"%lld / %lld = %f lPct: %f\n", doneSize, size, pct, lPct );
			if( pct > lPct ) {
				if( AvancementUi2( pct ) < 0 ) { res = -1; break; }
				lPct = pct;
			}
		}
	}
	my_pclose( fp );
	return( res );
}
#else
/*------------------------------------------------------------------------------
	AUTHORPROGRESS-
Linux!jef 2006/02/24 22:27:02
------------------------------------------------------------------------------*/
static void AuthorProgress( char * string )
{
	char * p;

	DBG('b',fprintf(stderr,"%s\n", string ););

	p = strchr( string, '%' );
	if( p ) {
		double pct;

		if( !AuthorFixStage ) {
			AuthorFixStage = 1;
			DisplayAvancement( _("Fixation de la structure du DVD.\n") );
			InitAvancement(0);
		}
		p = strrchr( string, ' ' );
		pct = atoi( p + 1 );
		pct /= 100;
		if( AvancementUi2( pct ) < 0 ) { ExitAuthor(); }
		return;
	}
	p = strrchr( string, 'U' ); /* find VOBU */
	if( !p )	return;
	p = strstr( p, "at " );
	if( p ) {
		long long doneSize;
		double pct;

		p += 3;
		doneSize = atoi( p );
		doneSize *= 1024 * 1024;
		pct = (double)doneSize / (double)AuthorSize;
// fprintf(stderr,"%lld / %lld = %f lPct: %f\n", doneSize, AuthorSize, pct, lPct );
		if( pct > AuthorlPct ) {
			if( AvancementUi2( pct ) < 0 ) { ExitAuthor(); }
			AuthorlPct = pct;
		}
	}
//!!	DisplayAvancement( "%s\n", string );
}

/*------------------------------------------------------------------------------
	EXECUTEDVDAUTHOR-
Linux!jef 2005/12/20 22:17:17
------------------------------------------------------------------------------*/

static int ExecuteDvdAuthor( int argc, char ** argv, long long size )
{
	int res;

	InitAvancement(0);
	SetStatusFunction( AuthorProgress );
	AuthorFixStage = 0;
	AuthorlPct = -1;
	AuthorSize = size;
	res = DvdAuthorMain( argc, argv );
	if( res )	res = -1;
	return( res );
}
#endif
/*------------------------------------------------------------------------------
	RECPUFACTOR-
Linux!jef 2006/02/20 22:30:41
------------------------------------------------------------------------------*/

static double ReCpuFactor(void)
{
	double dFactor;
	long long videoSize;
	long long audioSize;
	long long subpSize;
	long long navSize;
	long long remainVideoSize;
	long long remainAudioSize;
	long long remainSubpSize;
	long long remainNavSize;
	long long remainSize;
	long long tSize;
	int vVideoSize;
	int vAudioSize;
	int vSubpSize;
	int vNavSize;
	int totalSectors = 0;
	int doneSectors = 0;
	int remainSectors;
	int i;
	double pct;

/* Compute total sectors size */
	for( i = 0; i < CellArray.nbCells; i++ ) {
		Cell_t * c = CellArray.cells + i;

		if( c->selected )	totalSectors += SECTOR_SZ(c);
		if( c->done )		doneSectors += SECTOR_SZ(c);
	}
/* On attend d'avoir traiter un minimum de choses avant d'aligner le facteur */
	pct = (double)doneSectors / (double)totalSectors;
	if( pct < DYNQUAL_PCT )	return( 0 );

	VaporizeSizes( &vVideoSize, &vAudioSize, &vSubpSize, &vNavSize );
	tSize = (long long)vVideoSize * DVD_BLOCK_LEN;
	tSize += (long long)vAudioSize * DVD_BLOCK_LEN;
	tSize += (long long)vSubpSize * DVD_BLOCK_LEN;
	tSize += (long long)vNavSize * DVD_BLOCK_LEN;


	DBG( 'b',fprintf(stderr,"0) VAPORIZE VIDEO SIZE: %s\n", LLSize2Giga((long long)vVideoSize * DVD_BLOCK_LEN )););
	DBG( 'b',fprintf(stderr,"0) VAPORIZE AUDIO SIZE: %s\n", LLSize2Giga((long long)vAudioSize * DVD_BLOCK_LEN )););
	DBG( 'b',fprintf(stderr,"0) VAPORIZE SUBP  SIZE: %s\n", LLSize2Giga((long long)vSubpSize * DVD_BLOCK_LEN )););
	DBG( 'b',fprintf(stderr,"0) VAPORIZE NAV   SIZE: %s\n", LLSize2Giga((long long)vNavSize * DVD_BLOCK_LEN )););


	DBG( 'b',fprintf(stderr,"THEORICAL TOTAL SIZE: %s\n", LLSize2Giga(tSize)););
	DBG( 'b',fprintf(stderr,"PRATICAL TOTAL SIZE : %s\n", LLSize2Giga(TotalSize)););

	VaporizePacks( &vVideoSize, NULL );

	DBG( 'b',fprintf(stderr,"VAPORIZE VIDEO SIZE: %s\n", LLSize2Giga((long long)vVideoSize * DVD_BLOCK_LEN )););
	DBG( 'b',fprintf(stderr,"VAPORIZE AUDIO SIZE: %s\n", LLSize2Giga((long long)vAudioSize * DVD_BLOCK_LEN )););
	DBG( 'b',fprintf(stderr,"VAPORIZE SUBP  SIZE: %s\n", LLSize2Giga((long long)vSubpSize * DVD_BLOCK_LEN )););
	DBG( 'b',fprintf(stderr,"VAPORIZE NAV   SIZE: %s\n", LLSize2Giga((long long)vNavSize * DVD_BLOCK_LEN )););

	videoSize = ( (long long)vVideoSize * (long long)DVD_BLOCK_LEN * (long long)totalSectors ) / (long long)doneSectors;
	DBG( 'b',fprintf(stderr,"2) ESTIMATED TOTAL VIDEOSIZE: %s\n", LLSize2Giga(videoSize)););
	audioSize = ( (long long)vAudioSize * (long long)DVD_BLOCK_LEN * (long long)totalSectors ) / (long long)doneSectors;
	DBG( 'b',fprintf(stderr,"2) ESTIMATED TOTAL AUDIOSIZE: %s\n", LLSize2Giga(audioSize)););
	subpSize = ( (long long)vSubpSize * (long long)DVD_BLOCK_LEN * (long long)totalSectors ) / (long long)doneSectors;
	DBG( 'b',fprintf(stderr,"2) ESTIMATED TOTAL SUBSIZE: %s\n", LLSize2Giga(subpSize)););
	navSize = ( (long long)vNavSize * (long long)DVD_BLOCK_LEN * (long long)totalSectors ) / (long long)doneSectors;
	DBG( 'b',fprintf(stderr,"2) ESTIMATED TOTAL NAVSIZE: %s\n", LLSize2Giga(navSize)););

/* On calcul ce qu'il reste a faire */
	remainSectors = totalSectors - doneSectors;
	remainVideoSize = videoSize - ((long long)vVideoSize * (long long)DVD_BLOCK_LEN);
	remainAudioSize = audioSize - ((long long)vAudioSize * (long long)DVD_BLOCK_LEN);
	remainSubpSize = subpSize - ((long long)vSubpSize * (long long)DVD_BLOCK_LEN);
	remainNavSize = navSize - ((long long)vNavSize * (long long)DVD_BLOCK_LEN);
	remainSize = GetWantedBytes() - TotalSize;

	DBG( 'b',fprintf(stderr,"3) REMAIN VIDEOSIZE: %s\n", LLSize2Giga(remainVideoSize)););
	DBG( 'b',fprintf(stderr,"3) REMAIN AUDIOSIZE: %s\n", LLSize2Giga(remainAudioSize)););
	DBG( 'b',fprintf(stderr,"3) REMAIN   SUBSIZE: %s\n", LLSize2Giga(remainSubpSize)););
	DBG( 'b',fprintf(stderr,"3) REMAIN   NAVSIZE: %s\n", LLSize2Giga(remainNavSize)););
	DBG( 'b',fprintf(stderr,"3) REMAIN   DVDSIZE: %s\n", LLSize2Giga(remainSize)););

/* Les packets de navigations sont de la video non compressible ! */

	dFactor = (double)( remainVideoSize ) / (double)( remainSize - remainAudioSize - remainSubpSize - remainNavSize );
	DBG( 'b',fprintf(stderr,"dFactor0: %f\n", dFactor ););

	if (dFactor < 1)	dFactor = 1;

	pct = (1 - (dFactor - 1)) * 100;
	DBG( 'b',fprintf(stderr,"New Quality: %f\n", pct ););
	return( dFactor );
}


/*------------------------------------------------------------------------------
	COPYDVD-
Linux!jef 2005/12/12 22:36:50
------------------------------------------------------------------------------*/

int CopyDvd( GtkWidget * forme, int noPisteVideo, AudioMap_t * audioMap, SubMap_t * subMap, double factor )
{
	GtkWidget * item;
	char * tmpDir = ConfigGetString( KEY_TEMP, "/tmp" );
	char * isoDir = ConfigGetString( KEY_ISODIR, GetEnv( "HOME", "/tmp" ) );
	int lastCellF = ConfigGetInt( KEY_GENERIQUE, 0 );
	char targetDir[512];
	char sysCmd[10240]; /*KK:*/
	char vobFile[1024];
	char pFile[512];
	char * chapitreStr;
	int i;
	long long fsize;
	char injectionFile[100];
	const gchar * text;
	int remove = 1;
	int res;
	int ret = 0;
	int nbSteps;
	int dynQual = 0;
	int burnF = ConfigGetInt( KEY_GRAVER, 0 );
	int burnTool = BURNTOOL_NONE;
	char isoName[1024];
	int isoF = ConfigGetInt( KEY_MKISO, 0 );
	int buildImageF = 0;
	int removeImageF = 0;

	DBG('b',fprintf(stderr,"%s: factor: %f\n", __FUNCTION__, factor ););
#if TEST_AUTHOR == 0
/* Verify we have enought free space to run */
	fsize = FsFree( tmpDir ) - GetWorkBytes();
	if( fsize < 0 ) {
		PlayError();
		MessageBoxError( _("Pas assez de place sur %s\nIl manque %s octets !"), tmpDir, LLSize2Giga( -fsize ) );
		return( -1 );
	}
/* Verify we have enought free space to run */
	if( ConfigGetInt( KEY_MKISO, 0 ) ) {
		fsize = FsFree( isoDir ) - GetWantedBytes();
		if( fsize < 0 ) {
			DBG('b',fprintf(stderr,"FsFree(%lld) GetWantedBytes(%lld)\n", FsFree( isoDir ), GetWantedBytes() ););
			PlayError();
			MessageBoxError( _("Pas assez de place sur %s\nIl manque %s octets !"), isoDir, LLSize2Giga( -fsize ) );
			return( -1 );
		}
	}
#endif
/* Verify burn tool presence */
	if( burnF ) {
		burnTool = BurnTool();
		if( burnTool == BURNTOOL_NONE ) {
			PlayError();
			MessageBoxError( _("Aucun utilitaire de gravure install‚ !") );
			return( -1 );
		}
	}
/* Reprise du titre du DVD */
	item = lookup_widget(GTK_WIDGET(forme), "titre" );
	text = gtk_entry_get_text( GTK_ENTRY(item) );
	strcpy( TitreDvd, text );

	FreeCellArray( &CellArray );
	BuildCells( noPisteVideo, &CellArray, lastCellF, 0 );

	CurVTS = 0;
	CurVOB = 0;
	OutFp = NULL;
	OutSize = 0;
	TotalSize = 0;
	CreateAvancement( forme );

	nbSteps = CountSelectedCells( &CellArray ) + 1; /* +1 pour final dvdauthor */
	if( ConfigGetInt( KEY_MKISO, 0 ) )	nbSteps++;
	SetTotalSteps( nbSteps );

	DisplayAvancement( _("Copie de %d cellules.\n"), CountSelectedCells( &CellArray ) );
/*	Create empty filesystem tree */
	DisplayAvancement( _("Effacement des anciens fichiers.\n"));

	sprintf( targetDir, "%s/%s", tmpDir, TitreDvd );
	switch( DirExist( targetDir ) ) {
		case 1 :
			res = MessageBoxYesNo( _("Le repertoire %s existe. L'effacer ?"), targetDir );
			if( res != 1 ) {
				ret = -1; goto out;
			}
			sprintf( sysCmd,"rm -rf \"%s/VIDEO_TS\"", targetDir );
			res = system( sysCmd );
			sprintf( sysCmd,"rm -rf \"%s/AUDIO_TS\"", targetDir );
			res = system( sysCmd );
			break;
		case -1 :
			MessageBoxError( _("%s n'est pas un repertoire ! Veuillez l'effacer."), targetDir );
			ret = -1; goto out;
			break;
	}
	sprintf( sysCmd,"mkdir -p \"%s/VIDEO_TS\"", targetDir );
	res = system( sysCmd );
	sprintf( sysCmd,"mkdir -p \"%s/AUDIO_TS\"", targetDir );
	res = system( sysCmd );

	sprintf( injectionFile, "%s/vapinj.%d", tmpDir, getpid());
	sprintf( vobFile, "%s/%s.vob", tmpDir, TitreDvd );
	sprintf( pFile,"%s/palette.txt", tmpDir );

	SavePalette( noPisteVideo, pFile );

#if TEST_AUTHOR == 1
	ret = 0; goto author;
#endif
/* Setup vaporizer */
	if( VaporizeInit( Readin, OutputFlux, 1 ) < 0 ) {
		ret = -1; goto out;
	}
/* Setup tracks & factor */
	for( i = 0; i < MAX_AUDIO_TRACK; i++ ) {
		if( audioMap->audioTracks[i] ) {
			int id = GetAudioId( noPisteVideo, i );

			DBG('b',fprintf(stderr,"%s: VaporizeAudio(%d)\n", __FUNCTION__, id ););
			VaporizeAudio( id );
		}
	}
	for( i = 0; i < MAX_SUB_TRACK; i++ ) {
		if( subMap->subTracks[i] ) {
			int id = GetSubId( noPisteVideo, i );

			DBG('b',fprintf(stderr,"%s: VaporizeSubp(%d)\n", __FUNCTION__, id ););
			VaporizeSubp( id );
		}
	}

	if( ConfigGetInt( KEY_QUALDYN, 0 ) ) {
		DisplayAvancement( _("Ajustement automatique de la qualitÃ©.\n"));
		dynQual = 1;
	}

	Mpeg2Init();

	VaporizeFactor( factor, dynQual );
	VaporizeInjection( injectionFile );

/* Copy cells */
	for( i = 0; i < CellArray.nbCells; i++ ) {
		Cell_t * c = CellArray.cells + i;

		if( CopyCell( c, targetDir ) < 0 ) {
			ret = -1;
			goto out;
		}
		if( c->selected ) {
			NewStep();
			if( dynQual ) {
				double nFactor = ReCpuFactor();
				if( nFactor ) {
					double pct;

					VaporizeFactor( nFactor, 0 );
					if( MyAbs( factor - nFactor ) >= 0.01 ) {
						pct = (1 - (nFactor - 1)) * 100;
						DisplayAvancement( _("Ajustement de qualitÃ© Ã  %.2f%%.\n"), pct );
					}
					factor = nFactor;
				}
			}
		}
	}

	{
		int vVideoSize;
		int vAudioSize;
		int vSubpSize;
		int vNavSize;

		VaporizeSizes( &vVideoSize, &vAudioSize, &vSubpSize, &vNavSize );

		DBG( 'b',fprintf(stderr,"VAPORIZE VIDEO SIZE: %lld\n", (long long)vVideoSize * DVD_BLOCK_LEN ););
		DBG( 'b',fprintf(stderr,"VAPORIZE AUDIO SIZE: %lld\n", (long long)vAudioSize * DVD_BLOCK_LEN ););
		DBG( 'b',fprintf(stderr,"VAPORIZE SUBP  SIZE: %lld\n", (long long)vSubpSize * DVD_BLOCK_LEN ););
		DBG( 'b',fprintf(stderr,"VAPORIZE NAV   SIZE: %lld\n", (long long)vNavSize * DVD_BLOCK_LEN ););
	}

	if( TotalSize > GetMaxBytes() ) {
		int res;

		res = MessageBoxYesNo( _("Fichier VOB trop gros (%s) !\nDÃ©sirez vous continuer ?"), LLSize2Giga(TotalSize));
		if( res != 1 ) {
			ret = -1; goto out;
		}
	}

out:;

	VaporizeTerm();

	if( OutFp ) { fclose( OutFp ); OutFp = NULL; }

	Mpeg2End();

#if TEST_AUTHOR == 2
	goto out2;
#endif

#if TEST_AUTHOR == 1
author:;
#endif
	if( !ret ) { /* DvdAuthor */
		char * args[20];
		char * tmp;
		int argc = 0;

		DisplayAvancement( _("CrÃ©ation de la structure du DVD.\n") );
		chapitreStr = BuildChapitreString( noPisteVideo );
		args[argc++] = strdup( "dvdauthor" );
		args[argc++] = strdup( "-t" );
		tmp = BuildAudioString( noPisteVideo, audioMap );
		if( tmp ) {
			if( *tmp ) {
				args[argc++] = strdup( "-a" );
				args[argc++] = tmp;
			}
			else
				free( tmp );
		}
		args[argc++] = strdup( "-c" );
		args[argc++] = chapitreStr;
		args[argc++] = strdup( "-v" );
		args[argc++] = BuildFormatString( noPisteVideo ); // strdup( "pal+16:9+nopanscan+720x576" );
		args[argc++] = strdup( "-p" );
		args[argc++] = strdup( pFile );
		tmp = BuildSubString( noPisteVideo, subMap );
		if( tmp ) {
			if( *tmp ) {
				args[argc++] = strdup( "-s" );
				args[argc++] = tmp;
			}
			else
				free( tmp );
		}
		args[argc++] = strdup( "-o" );
		args[argc++] = strdup( targetDir);
		args[argc++] = strdup( "-f" );
		args[argc++] = strdup( vobFile );
		args[argc] = NULL;

		for( i = 0; i < argc; i++ ) {
			DBG('b',fprintf(stderr,"%s\n", args[i] ););
		}

		TotalSize = FileSize64( vobFile );
		res = ExecuteDvdAuthor( argc, args, TotalSize );
		for( i = 0; i < argc; i++ )	free( args[i] );

		if( !res ) { /* Generate TOC */
			char * args[20];
			int argc = 0;

			DisplayAvancement( _("Ajout de la table des matiÃ¨res.\n") );
			args[argc++] = strdup( "dvdauthor" );
			args[argc++] = strdup( "-T" );
			args[argc++] = strdup( "-o" );
			args[argc++] = strdup( targetDir );
			SetStatusFunction( NULL );
			res = DvdAuthorMain( argc, args );
			if( res )	ret = -1;
			for( i = 0; i < argc; i++ )	free( args[i] );
			NewStep();
		}
		else
			ret = -1;
	}
#if TEST_AUTHOR == 0
	unlink( pFile );
	unlink( vobFile );
	if( !ret ) {
/* OK Ask if the user want to see the movie */
		{
			int msgRet = MessagePhase2( targetDir );

			switch( msgRet ) {
				case PHASE2_ARRIERE :
					res = -1; goto out;
				case PHASE2_AVANCER :
					break;
			}
		}
/* OK iso image an burn stage */
		if( isoF )	buildImageF = 1;
		if( burnF && burnTool == BURNTOOL_BRASERO ) { /* Have to make a temporary image file */
			buildImageF = 1;
			if( !isoF )	removeImageF = 1;
		}
		if( buildImageF ) {
			sprintf( isoName, "%s/%s.iso", isoDir, TitreDvd );
			switch( FileExist( isoName ) ) {
				case 1 :
					res = MessageBoxYesNo( _("Le fichier %s existe. Le re-creer ?"), isoName );
					if( res != 1 )	break;
				case 0 :
					DisplayAvancement( _("Construction de l'image ISO.\n") );
					sprintf( sysCmd, "%s -dvd-video -V \"%s\"  -o \"%s\" \"%s\"",
						GetMkIsoTool(),
						TitreDvd,
						isoName,
						targetDir );
					ExecuteMkIsoFs( sysCmd );
					NewStep();
					break;
				case -1 :
					MessageBoxError( _("%s n'est pas un fichier ! Veuillez l'effacer."), isoName );
					ret = -1; goto out;
					break;
			}
		}
/* Burn DVD if needed */
		if( burnF ) {
			DisplayAvancement( _("Gravage sur DVD.\n") );
			DvdClose();
			if( burnTool == BURNTOOL_BRASERO )
				res = BurnIsoFile( burnTool, Device, isoName );
			else
				res = BurnVideoDir( burnTool, Device, targetDir );
			DvdOpen( Device );
		}
		if( removeImageF )	unlink( isoName );
	}
/* Nettoyage: keep VIDEO directory only if we do not build image, nor burn dvd */
	if( !ret && !isoF && !burnF )	remove = 0;

	if( remove ) {
		DisplayAvancement( _("Effacement des fichiers.\n"));
		sprintf( sysCmd, "rm -rf \"%s\"", targetDir );
		res = system( sysCmd );
	}
#endif

#if TEST_AUTHOR == 2
out2:;
#endif
	DestroyAvancement();
	FreeAll();
	if( !ret )
		PlaySuccess();
	else
		PlayError();
	return( ret );
}
