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

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
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <alloca.h>


#include <locale.h>

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


#include "const.h"
#include "dvdformat.h"
#include "dvdinfo.h"
#include "globals.h"
#include "systools.h"
#include "dvdtools.h"
#include "ifo.h"
#include "bswap.h"
#include "vaporize.h"
#include "ac.h"
#include "dvdcell.h"
#include "dvdbackup.h"


#define MAX_VOB_SIZE		(1024*1024*1024)

typedef struct
{
	int etat;
	dvd_file_t * file_handle;
	uint32_t sector;
	uint32_t dsi_next_vobu;
	Cell_t * cell;
	char * targetDir;
	unsigned char * buf;
	int bufASize;
	unsigned char * bufPtr;
	int bufSize;
} VapContext_t;


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 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 int SimulF = 0;
static int NoMenuF = 0;
static long long OutNullSize;

static long VobSize[100];

/*@$#[dvdbackup.c] static proto. AutoProtoSigV1.1. date: 106/02/15 22:08:22 */
#include "proto.h"
#ifdef __cplusplus
extern "C" {
#endif
static int CopyMenu PROTO((int vts, char *targetDir));
static void AddVobus PROTO((Cell_t *c, char *buffer, int len, uint32_t position, int curVob));
static int CopyEmptyPgc PROTO((Cell_t *cell));
static int Output PROTO((unsigned char *buffer, int len, char *targetDir));
static void FindCellStart PROTO((Cell_t *c));
static Vobu_t *_FindVobu PROTO((VobuArray_t *va, uint32_t sector, int start, int end));
static Vobu_t *FindVobu PROTO((Cell_t *c, uint32_t sector));
static Vobu_t *RemapVobu PROTO((uint32_t *value));
static int UpdateMainIfo PROTO((char *targetDir, int fullBackup));
static int UpdateIfo PROTO((char *targetDir));
static void RemapOffset PROTO((uint32_t _sector, uint32_t *_offset, int _dir));
static int UpdateVob PROTO((int vts, int vobNum, long fSize, char *targetDir));
static void MakeRoom PROTO((VapContext_t *c, int size));
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 int UpdateVobs PROTO((char *targetDir));
static int OutputNull PROTO((void *parm, unsigned char *buffer, int size));
#ifdef __cplusplus
}
#endif
/*@$% end of AutoProtoSigV1.1 (Dont remove this line) [-I ../include]*/

/*------------------------------------------------------------------------------
	COPYMENU-
Linux!jef 2005/12/28 22:06:55
	Retourne la taille du menu en secteurs, -1 erreur
------------------------------------------------------------------------------*/

static int CopyMenu( int vts, char * targetDir )
{
	char vobFile[512];
	uint32_t size;
	FILE * fp = NULL;
	dvd_file_t * dvd_file;
	char buffer[DVD_VIDEO_LB_LEN];
	uint32_t sector;
	int ret = 0;

	if( NoMenuF )	return( 0 );

	if( !vts ) {
		sprintf( vobFile, "%s/VIDEO_TS/VIDEO_TS.VOB", targetDir );
		size = Ifo_zero->vmgi_mat->vmg_last_sector -1  - 2* Ifo_zero->vmgi_mat->vmgi_last_sector;
	}
	else {
		sprintf( vobFile, "%s/VIDEO_TS/VTS_%02d_0.VOB", targetDir, vts );
		size = Ifo[vts]->vtsi_mat->vtstt_vobs - Ifo[vts]->vtsi_mat->vtsi_last_sector -1;
	}
	size *= 2048;
	if( SimulF ) {
		TotalSize += size;
		return( size );
	}
	if( !size )	return( 0 );
	fp = fopen( vobFile, "w" );
	if( !fp )	return( -1 );
	dvd_file = DVDOpenFile( Dvd, vts, DVD_READ_MENU_VOBS);
	if( !dvd_file )	{
		ret = -1;
		goto out;
	}
	size /= DVD_VIDEO_LB_LEN;
//	DBG('b',fprintf(stderr,"%s: size: %u secteurs\n",__FUNCTION__, size););
	printf ("Copy menu vob%2d (%d sectors)\n", vts, size);
	for( sector = 0; sector < size; sector++ ) {
		int res;
		int size;

		res = DVDReadBlocks( dvd_file, sector, 1, buffer );
		if( res != 1 ) {
			ret = -1; goto out;
		}
		if( fwrite( buffer, 1, sizeof(buffer), fp ) != sizeof(buffer)) {
			ret = -1; goto out;
		}
		size = sizeof(buffer);
		TotalSize += size;
	}
	ret = size;
out:;
	if( dvd_file )	DVDCloseFile( dvd_file );
	fclose( fp );
	return( ret );
}
/*------------------------------------------------------------------------------
	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 ););
	}
}


/*------------------------------------------------------------------------------
	COPYEMPTYPGC-
Linux!jef 2005/12/29 20:22:21
------------------------------------------------------------------------------*/

static int CopyEmptyPgc( Cell_t * cell )
{
	uint32_t sector;
	char buffer[DVD_VIDEO_LB_LEN];
	dvd_file_t * dvd_file;
	dsi_t dsi_pack;
	int nsectors = 0;
	Vobu_t * v;

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

	if( NoMenuF )	return( 0 );

	dvd_file = DVDOpenFile( Dvd, cell->vts, DVD_READ_TITLE_VOBS);
	if( !dvd_file )	return( -1 );
	sector = cell->startSector;

	if( DVDReadBlocks( dvd_file, sector, 1, buffer ) != 1 ) {
		return( -1 );
	}
	if( !SimulF ) {
		if( fwrite( buffer, 1, sizeof(buffer), OutFp ) != sizeof(buffer)) {
			return( -1 );
		}
	}
	OutSize += sizeof(buffer);
	TotalSize += sizeof(buffer);

	v = NewVobu( &CurCell->va, sector );
	v->empty = 1;
	AddVobus( CurCell, buffer, DVD_VIDEO_LB_LEN, Position, CurVOB );

	navRead_DSI( &dsi_pack, buffer + DSI_START_BYTE );

	if( dsi_pack.dsi_gi.vobu_ea != 0) {
		nsectors = 1;

		if( DVDReadBlocks( dvd_file, (sector + 1), 1, buffer) != 1 ) {
			return( -1 );
		}
		if( !SimulF ) {
			if( fwrite( buffer, 1, sizeof(buffer), OutFp ) != sizeof(buffer))	return( -1 );
		}
		OutSize += sizeof(buffer);
		TotalSize += sizeof(buffer);
	}
	CurCell->lastSector = CurCell->startSector + nsectors;
	CurTS->lastSector += nsectors + 1;
	Position += nsectors + 1;

	DVDCloseFile( dvd_file );
	return( 0 );
}

/*------------------------------------------------------------------------------
	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 ) ) {
			dsi_t dsiPack;

			navRead_DSI( &dsiPack, (unsigned char *)cBuff + DSI_START_BYTE);
			if((dsiPack.dsi_gi.vobu_ea * DVD_VIDEO_LB_LEN) + OutSize >= MAX_VOB_SIZE ) {
				char vobFile[512];

				fclose( OutFp );
				OutFp = NULL;
				VobSize[CurVOB] = OutSize;
				CurVOB++;
				sprintf( vobFile, "%s/VIDEO_TS/VTS_%02d_%d.VOB", targetDir, CurVTS, CurVOB );
				OutFp = fopen( vobFile, "w" );
				if( !OutFp ) {
					return( -1 );
				}
				OutSize = 0;
                	}
		}
		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;
		AddVobus( CurCell, 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; /* !! */
	}
}
/*------------------------------------------------------------------------------
	_FINDVOBU-
Linux!jef 2006/01/23 23:34:56
------------------------------------------------------------------------------*/

static Vobu_t * _FindVobu( VobuArray_t * va, uint32_t sector, int start, int end )
{
	while( start <= end ) {
		int m = (start + end ) / 2;
		Vobu_t * v = va->vobus + m;

		if( v->oldSector == sector )	return( v );
		if( v->oldSector > sector )
			end = m -1;
		else
			start = m + 1;
	}
	return( NULL );
}

/*------------------------------------------------------------------------------
	FINDVOBU-
Linux!jef 2006/01/19 19:17:34
 Vobu oldSector est toujours croissant
------------------------------------------------------------------------------*/

static Vobu_t * FindVobu( Cell_t * c, uint32_t sector )
{
	Vobu_t * v;

	if( !c->va.nbVobu )	return( NULL );

	v = &c->va.vobus[0];
	if( sector < v->oldSector )	return( NULL );

	v = &c->va.vobus[c->va.nbVobu-1];
	if( sector > v->oldSector )	return( NULL );

	return( _FindVobu( &c->va, sector, 0, c->va.nbVobu-1 ) );

}
/*------------------------------------------------------------------------------
	REMAPVOBU-
Linux!jef 2006/01/19 19:21:15
------------------------------------------------------------------------------*/

static Vobu_t * RemapVobu( uint32_t * value )
{
	Vobu_t * v;
	uint32_t sector, mask;
	int i;

	if( (*value & 0x80000000) == 0x80000000 ) {
		sector = *value & 0x7FFFFFFF;
		mask = 0x80000000;
	}
	else {
		sector = *value;
		mask = 0;
	}
	*value = 0;

	for( i = 0; i < CurTS->ca.nbCells; i++ ) {
		Cell_t * c =  CurTS->ca.cells + i;

		v = FindVobu( c, sector);
		if( v ) {
			*value = v->newSector | mask;
			return( v );
		}
	}
	return( NULL );
}
/*------------------------------------------------------------------------------
	UPDATEMAINIFO-
Linux!jef 2006/01/20 20:57:51
------------------------------------------------------------------------------*/

static int UpdateMainIfo( char * targetDir, int fullBackup )
{
	ifo_handle_t * ifo = CopyIfo( 0 );
	int i, iTS;
	int found = 0;

	for( i = 0 ; i < ifo->tt_srpt->nr_of_srpts; i++ ) {
		for( iTS = 0; (iTS < TitleArray.nbTitles) && (!found); iTS++ ) {
			TitleSet_t * ts = TitleArray.titles + iTS;
			if( ts->vts == ifo->tt_srpt->title[i].title_set_nr ) {
				ifo->tt_srpt->title[i].title_set_sector = ts->startSector;
				found = 1;
			}
		}
		found = 0;
	}
	return( SaveIfo( ifo, 0, targetDir  ) );
}


/*------------------------------------------------------------------------------
	UPDATEIFO-
Linux!jef 2006/01/19 19:29:36
------------------------------------------------------------------------------*/

static int UpdateIfo( char * targetDir )
{
	ifo_handle_t * ifo = CopyIfo( CurVTS );
	pgcit_t * pgcit = ifo->vts_pgcit;
	Vobu_t * vobu2 = NULL;
	uint32_t newPos = 0;
	c_adt_t * c_adt = ifo->vts_c_adt;
	uint32_t length = c_adt->last_byte + 1 - C_ADT_SIZE;
	cell_adr_t * ptr;
	vobu_admap_t * vobu_admap = ifo->vts_vobu_admap;
	vts_tmapt_t * vts_tmapt = ifo->vts_tmapt;
	int i,j;

//	DBG('b',fprintf(stderr,"%s: VTS %d\n", __FUNCTION__, CurVTS ););

//update total VTS size with IFO size
	CurTS->lastSector += 2 *(ifo->vtsi_mat->vtsi_last_sector ) +1;
	ifo->vtsi_mat->vts_last_sector =   CurTS->lastSector ;
//update first play PGC
	if( ifo->first_play_pgc ) {
		pgc_t * pgc = ifo->first_play_pgc;
		cell_playback_t * cell_playback = pgc->cell_playback;
		int nr = pgc->nr_of_cells;
		cell_playback_t cell;

		vobu2 = NULL;
		newPos = 0;
		for( j = 0; j < nr; j++) {
			Vobu_t * vobu = RemapVobu( &cell_playback[j].first_sector );
			vobu2 = vobu;

			if( vobu !=NULL ) {
				vobu = RemapVobu( &cell_playback[j].last_vobu_start_sector );
				if( vobu == NULL ) {
					cell_playback[j].last_vobu_start_sector = cell_playback[j].first_sector;
					vobu = vobu2;
					pgc->playback_time.hour = 0;
					pgc->playback_time.minute = 0;
					pgc->playback_time.second = 0;
					cell_playback[j].playback_time.hour = 0;
					cell_playback[j].playback_time.minute = 0;
					cell_playback[j].playback_time.second = 0;
				}
				cell_playback[j].last_sector = vobu->newSector+vobu->size;
				cell_playback[newPos] = cell_playback[j];
				cell = cell_playback[newPos];
				newPos++;
			}
			else {
				cell_playback[newPos] = cell;
				newPos++;
			}
		}
		for( j = newPos; j < nr; j++ )	cell_playback[j].last_sector = 0;
		pgc->nr_of_cells = newPos;
	}
	newPos = 0;
//update each PGC
	for( i = 0; i < pgcit->nr_of_pgci_srp; i++) {
		pgc_t * pgc = pgcit->pgci_srp[i].pgc;
		cell_playback_t * cell_playback = pgc->cell_playback;
		int nr = pgc->nr_of_cells;
		cell_playback_t cell;

		vobu2 = NULL;
		newPos = 0;
		for( j = 0; j < nr; j++) {
			Vobu_t * vobu = RemapVobu(&cell_playback[j].first_sector);
			vobu2 = vobu;

			if( vobu ) {
				vobu = RemapVobu(&cell_playback[j].last_vobu_start_sector);
				if( !vobu ) {
					cell_playback[j].last_vobu_start_sector=cell_playback[j].first_sector;
					vobu = vobu2;
					pgc->playback_time.hour=0;
					pgc->playback_time.minute=0;
					pgc->playback_time.second=0;

					cell_playback[j].playback_time.hour=0;
					cell_playback[j].playback_time.minute=0;
					cell_playback[j].playback_time.second=0;
				}
				cell_playback[j].last_sector = vobu->newSector+vobu->size;// -1 ;
				cell_playback[newPos]=cell_playback[j];
				cell=cell_playback[newPos];
				newPos++;
			}
			else {
				cell_playback[newPos]=cell;
				newPos++;
			}
		}
		for( j = newPos ; j < nr ; j++ )	cell_playback[j].last_sector=0;
		pgc->nr_of_cells = newPos;
	}

	ptr = c_adt->cell_adr_table;
	newPos = 0;
	for( i = 0; i < length/sizeof(cell_adr_t); i++) {
		uint32_t startSect = ptr[i].start_sector;

		ptr[i].start_sector = 0;
		ptr[i].last_sector = 0;
		for( j = 0; j < CurTS->ca.nbCells; j++ ) {
			Cell_t * cell = CurTS->ca.cells + j;

			if( cell->oldStartSector == startSect ) {
				ptr[newPos].start_sector = cell->startSector;
				ptr[newPos].last_sector = cell->lastSector;
				newPos++;
			}
		}
	}

	c_adt->last_byte=C_ADT_SIZE -1 +  ((newPos)*sizeof(cell_adr_t));
	c_adt->nr_of_vobs=newPos;


	length = vobu_admap->last_byte + 1 - VOBU_ADMAP_SIZE;
	newPos = 0;
	for( i = 0; i < length/sizeof(uint32_t); i++) {
		if( RemapVobu(&vobu_admap->vobu_start_sectors[i] ) != NULL ) {
			vobu_admap->vobu_start_sectors[newPos] = vobu_admap->vobu_start_sectors[i];
			newPos++;
		}
	}

	for( i = newPos; i < length/sizeof(uint32_t); i++ ) vobu_admap->vobu_start_sectors[i]=0;

	vobu_admap->last_byte = newPos * sizeof(uint32_t) -1 +VOBU_ADMAP_SIZE;
//update VTS_TMAP
	if( vts_tmapt ) {
		for( i = 0; i < vts_tmapt->nr_of_tmaps; i++) {
			map_ent_t * map_ent;
			if( vts_tmapt->tmap[i].nr_of_entries == 0) { // Early out if zero entries
				continue;
			}
			map_ent = vts_tmapt->tmap[i].map_ent;
			newPos = 0;
			for( j = 0; j < vts_tmapt->tmap[i].nr_of_entries; j++) {
                //bit 31 indicates whether  VOBU time codes are discontinous with previous
				uint32_t mask = map_ent[j] & 0x80000000 ;
				uint32_t value = map_ent[j] & 0x7FFFFFFF;

				if( RemapVobu(&value) !=NULL) {
					map_ent[j] = value | mask;
					map_ent[newPos] = map_ent[j];
					newPos++;
				}
				else {
					map_ent[j]=0;
				}
			}
			for( j = newPos; j < vts_tmapt->tmap[i].nr_of_entries;j++) map_ent[j]=0;
			vts_tmapt->tmap[i].nr_of_entries = newPos;
		}
	}
	return( SaveIfo( ifo, CurVTS, targetDir  ) );
}
/*------------------------------------------------------------------------------
	REMAPOFFSET-
Linux!jef 2006/01/23 21:03:48
------------------------------------------------------------------------------*/

static void RemapOffset( uint32_t _sector, uint32_t * _offset, int _dir )
{
	Vobu_t * vobu1 = NULL;
	Vobu_t * vobu2 = NULL;
	uint32_t offset,sector;
	uint32_t maskOffset1=0,maskOffset2=0,maskSector=0;
	uint32_t i;

	if ((*_offset!= 0xbfffffff) && (*_offset!=0x3fffffff) && (*_offset!=0x7fffffff)) {
		if ( (*_offset & 0x80000000) == 0x80000000)	maskOffset1= 0x80000000;
		if ( (*_offset & 0x40000000) == 0x40000000)	maskOffset2= 0x40000000;
		offset = *_offset & 0x3FFFFFFF;

		if ( (_sector & 0x80000000) == 0x80000000) {
			sector = _sector & 0x7FFFFFFF;
			maskSector=0x80000000;
		}
		else {
			sector =_sector;
			maskSector=0;
		}

		for( i = 0;i < CurTS->ca.nbCells; i++ ) {
			Cell_t * cell = CurTS->ca.cells + i;

			vobu1 = FindVobu( cell, sector );
			vobu2 = FindVobu( cell, sector+_dir*offset );
			if( vobu1 && vobu2 ) {
				*_offset = abs(vobu1->newSector - vobu2->newSector)  | maskOffset1 ;
				*_offset |= maskOffset2;
				return;
			}
		}
//		if( !vobu1 )	DBG('b',fprintf(stderr,"%s: vobu1 sector not found %u\n", __FUNCTION__, sector ););
//		if( !vobu2 )	DBG('b',fprintf(stderr,"%s: vobu2 sector not found %u\n", __FUNCTION__, sector+_dir*offset ););
	}
}

/*------------------------------------------------------------------------------
	UPDATEVOB-
Linux!jef 2006/01/23 20:44:46
------------------------------------------------------------------------------*/

static int UpdateVob( int vts, int vobNum, long fSize, char * targetDir )
{
	struct {
		uint32_t nv_pck_lbn;      /**< sector address of pci */
	}
	ATTRIBUTE_PACKED pci;
	unsigned char buffer[DVD_VIDEO_LB_LEN];
	char vobFile[512];
	FILE * fp;
	uint32_t sector = 0;
	long pos = 0;
	int ret = 0;

//	DBG('b',fprintf(stderr,"%s: vts: %d vobNum: %d\n", __FUNCTION__, vts, vobNum ););

	printf ("Update VOB %d-%d\n", vts, vobNum );

	sprintf( vobFile, "%s/VIDEO_TS/VTS_%02d_%d.VOB", targetDir, vts, vobNum );
	fp = fopen( vobFile, "r+b" );
	if( !fp )	return( -1 );

	while( pos < fSize ) {
		int emptyPgc = 0;
		int i;

		if( fread( buffer,DVD_VIDEO_LB_LEN,1,fp) != 1 )	break;

		if( isNavPack(buffer)) {
			dsi_t dsiPack;
			Vobu_t * vobu;
			uint32_t dsi_next_vobu;

			navRead_DSI (&dsiPack, buffer + DSI_START_BYTE);
			memcpy(&pci,buffer+0x2d,sizeof(pci));
			B2N_32(pci.nv_pck_lbn);

			sector = dsiPack.dsi_gi.nv_pck_lbn;
			vobu = RemapVobu( &dsiPack.dsi_gi.nv_pck_lbn );
			if( vobu ) {
				dsiPack.dsi_gi.vobu_ea = vobu->size;
				emptyPgc = vobu->empty;
			}
			else {
				fprintf( stderr, "remapVobu failed for %d\n",dsiPack.dsi_gi.nv_pck_lbn);
			}

			if( !emptyPgc ) {
				RemapOffset(sector, &dsiPack.vobu_sri.next_video,1 );
				for( i =0; i < 19 ; i++ ) {
					RemapOffset(sector,&dsiPack.vobu_sri.fwda[i],1);
				}
				RemapOffset(sector,&dsiPack.vobu_sri.next_vobu,1);
				RemapOffset(sector,&dsiPack.vobu_sri.prev_vobu,-1);
				for( i = 0 ; i < 19 ; i++ ) {
					RemapOffset(sector,&dsiPack.vobu_sri.bwda[i],-1);
				}
				RemapOffset(sector,&dsiPack.vobu_sri.prev_video,-1);
//1st audio packet
				for( i = 0 ; i < 8 ; i++) {
					if (((dsiPack.synci.a_synca[i] & 0x8000) != 0x8000 ) && (dsiPack.synci.a_synca[i] !=0x3FFF)) {
						if (vobu->firstAudio[i] !=-1) {
							dsiPack.synci.a_synca[i] = vobu->firstAudio[i];
						}
						else {
							dsiPack.synci.a_synca[i] =0;
						}
					}
				}
//1st subpicture packet
				for( i = 0 ; i < 32 ; i++ ) {
					if (((dsiPack.synci.sp_synca[i] & 0x80000000) != 0x80000000) && (dsiPack.synci.sp_synca[i] != 0x3FFFFFFF) && (dsiPack.synci.sp_synca[i] != 0x7FFFFFFF)) {
						if (vobu->firstSubp[i] != -1) {
							dsiPack.synci.sp_synca[i] = vobu->firstSubp[i];
//!! fprintf(stderr,"dsiPack.synci.sp_synca[%d]=%d\n", i, dsiPack.synci.sp_synca[i] );
						}
						else {
							dsiPack.synci.sp_synca[i] = 0;
						}
					}
				}
			}
			else {
				dsiPack.vobu_sri.next_video= 0xbfffffff;
				for( i = 0; i < 19 ; i++ )	dsiPack.vobu_sri.fwda[i] = 0x3fffffff;
				dsiPack.vobu_sri.next_vobu=0x3fffffff;
				dsiPack.vobu_sri.prev_vobu=0x3fffffff;
				for( i = 0; i < 19 ; i++ )	dsiPack.vobu_sri.bwda[i] = 0x3fffffff;
				dsiPack.vobu_sri.prev_video=0xbfffffff;
				for( i = 0 ; i < 8 ; i++ )	dsiPack.synci.a_synca[i]=0;
				for( i = 0 ; i < 32 ; i++ ) dsiPack.synci.sp_synca[i] =0;
			}
// mise en place des donnees modifi�s dans le buffer de sortie
			navRead_DSI((dsi_t*)(buffer + DSI_START_BYTE),(unsigned char *)&dsiPack);
			pci.nv_pck_lbn =dsiPack.dsi_gi.nv_pck_lbn;
			B2N_32(pci.nv_pck_lbn);
			memcpy(buffer+0x2d,&pci,sizeof(pci));
//mise �jour du fichier
			fseek(fp, pos, SEEK_SET );
			if( fwrite(buffer,DVD_VIDEO_LB_LEN,1,fp ) != 1 ) {
				ret = -1;
				goto out;
			}
//pointing on next VOBU
			dsi_next_vobu = dsiPack.vobu_sri.next_vobu;
			if( dsi_next_vobu != SRI_END_OF_CELL ) {
				uint32_t sect = dsi_next_vobu & 0x7fffffff;
				long newPos = pos + sect * 2048;
// fprintf(stderr,"At %ld Seeking at: %ld nextVobu: %p\n", pos, pos + sect * 2048, dsi_next_vobu );
				if( newPos > fSize || newPos <= pos )	break;
				fseek( fp,pos+sect*2048,SEEK_SET);
			}
		}
		pos = ftell( fp );
	}
out:;
	fclose( fp );
	return( ret );
}
/*------------------------------------------------------------------------------
	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;
	}
}

/*------------------------------------------------------------------------------
	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;

//	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 = DVDReadBlocks( c->file_handle, c->sector, 1, c->bufPtr );
	if( len != 1 )	return( -1 );
	len = DVD_VIDEO_LB_LEN;
	c->bufSize += len;

	if( isNavPack( c->bufPtr ) ) {
		Vobu_t * v;

		CurCell->oldLastSector = c->sector;
		v = NewVobu( &CurCell->va, c->sector );
	}
/* parse contained DSI pack */
	navRead_DSI( &dsi_pack, c->bufPtr + DSI_START_BYTE );
	nsectors      = dsi_pack.dsi_gi.vobu_ea;
	c->dsi_next_vobu = dsi_pack.vobu_sri.next_vobu;

	MakeRoom( c, nsectors*DVD_VIDEO_LB_LEN );
/* read VOBU */
	len = DVDReadBlocks( c->file_handle, (c->sector + 1), nsectors, c->bufPtr + DVD_VIDEO_LB_LEN );
	if( len != nsectors )	return( -1 );
	len = nsectors * DVD_VIDEO_LB_LEN;
	c->bufSize += len;

	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( c->vts != CurVTS ) {
		char vobFile[512];
		int menuSize;
		uint32_t startSector;

		if( OutFp ) {
			fclose( OutFp );
			OutFp = NULL;
			UpdateIfo( targetDir );
			VobSize[CurVOB] = OutSize;
			if( UpdateVobs( targetDir ) < 0 )	return( -1 );
		}
		menuSize = CopyMenu( c->vts, targetDir );
		if( menuSize < 0 )	return( -1 );
		if( !SimulF ) {
			sprintf( vobFile, "%s/VIDEO_TS/VTS_%02d_1.VOB", targetDir, c->vts );
			OutFp = fopen( 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( CopyEmptyPgc( c ) );

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

	size = c->lastSector - c->startSector;
	if( SimulF )
		printf ("Analyse cell %d (%d sectors)\n", c->chapter, size);
	else
		printf ("Copy cell %d (%d sectors)\n", c->chapter, 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;
	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 );
}

/*------------------------------------------------------------------------------
	UPDATEVOBS-
Linux!jef 2006/02/01 22:38:09
------------------------------------------------------------------------------*/

static int UpdateVobs( char * targetDir )
{
	int i;

	for( i = 1; i <= CurVOB; i++ ) {
		if( UpdateVob( CurVTS, i, VobSize[i], targetDir ) < 0 )	return( -1 );
	}
	return( 0 );
}

/*------------------------------------------------------------------------------
	RECPUFACTOR-
Linux!jef 2006/02/20 22:30:41
------------------------------------------------------------------------------*/

static double ReCpuFactor( )
{
	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 < 0.10 )	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: %lld\n", (long long)vVideoSize * DVD_BLOCK_LEN ););
//	DBG( 'b',fprintf(stderr,"0) VAPORIZE AUDIO SIZE: %lld\n", (long long)vAudioSize * DVD_BLOCK_LEN ););
//	DBG( 'b',fprintf(stderr,"0) VAPORIZE SUBP  SIZE: %lld\n", (long long)vSubpSize * DVD_BLOCK_LEN ););
//	DBG( 'b',fprintf(stderr,"0) VAPORIZE NAV   SIZE: %lld\n", (long long)vNavSize * DVD_BLOCK_LEN ););


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

	VaporizePacks( &vVideoSize, NULL );

//	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 ););

	videoSize = ( (long long)vVideoSize * (long long)DVD_BLOCK_LEN * (long long)totalSectors ) / (long long)doneSectors;
//	DBG( 'b',fprintf(stderr,"2) ESTIMATED TOTAL VIDEOSIZE: %lld\n", videoSize ););
	audioSize = ( (long long)vAudioSize * (long long)DVD_BLOCK_LEN * (long long)totalSectors ) / (long long)doneSectors;
//	DBG( 'b',fprintf(stderr,"2) ESTIMATED TOTAL AUDIOSIZE: %lld\n", audioSize ););
	subpSize = ( (long long)vSubpSize * (long long)DVD_BLOCK_LEN * (long long)totalSectors ) / (long long)doneSectors;
//	DBG( 'b',fprintf(stderr,"2) ESTIMATED TOTAL SUBSIZE: %lld\n", subpSize ););
	navSize = ( (long long)vNavSize * (long long)DVD_BLOCK_LEN * (long long)totalSectors ) / (long long)doneSectors;
//	DBG( 'b',fprintf(stderr,"2) ESTIMATED TOTAL NAVSIZE: %lld\n", 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 = DVD5_WANTED_BYTES_LL - TotalSize;

//	DBG( 'b',fprintf(stderr,"3) REMAIN VIDEOSIZE: %lld\n", remainVideoSize ););
//	DBG( 'b',fprintf(stderr,"3) REMAIN AUDIOSIZE: %lld\n", remainAudioSize ););
//	DBG( 'b',fprintf(stderr,"3) REMAIN   SUBSIZE: %lld\n", remainSubpSize ););
//	DBG( 'b',fprintf(stderr,"3) REMAIN   NAVSIZE: %lld\n", remainNavSize ););
//	DBG( 'b',fprintf(stderr,"3) REMAIN   DVDSIZE: %lld\n", 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 );
}

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

int FullBackupDvd( int noPisteVideo, AudioMap_t * audioMap, SubMap_t * subMap, double
                   factor, char* szDVDTitel,
                   char * szDevice, char * tmpDir, char * isoDir,
                   int dynQual, int fullBackup
                 )
{
	char targetDir[512];
	char sysCmd[10240]; /*KK:*/
	int i;
	long long fsize;
	char injectionFile[100];
	char * text;
	int remove = 1;
	int res;
	int ret = 0;
	int nbSteps;

        printf ("Audio: %ld\n", audioMap->audioTracks[0]);
        printf ("Subp: %ld\n", subMap->subTracks[0]);
        printf ("Titleset-Nr: %ld\n", noPisteVideo);
        printf ("Faktor: %f\n", factor);
        printf ("Title: %s\n", szDVDTitel);
        printf ("Device: %s\n", szDevice);
        printf ("Tmp-Dir: %s\n", tmpDir);
        printf ("IsoDir: %s\n", isoDir);
        printf ("Quality Dynamic: %ld\n", dynQual);
        printf ("Fullbackup: %ld\n", fullBackup);

//	factor += 0.1;

//	DBG('b',fprintf(stderr,"%s: factor: %f\n", __FUNCTION__, factor ););

/* Verify we have enought free space to run */
	fsize = FsFree( tmpDir ) - DVD5_WANTED_BYTES_LL;

/* Reprise du titre du DVD */
	strcpy( TitreDvd, szDVDTitel );

        int rc = DvdOpen (szDevice);
	FreeCellArray( &CellArray );
	BuildCells( noPisteVideo, &CellArray );

	CurVTS = 0;
	CurVOB = 0;
	OutFp = NULL;
	OutSize = 0;
	TotalSize = 0;
	SimulF = NoMenuF = 0;

	nbSteps = CountSelectedCells( &CellArray ) + 1; /* +1 pour final Vob update */
/*	Ajout des copy de menu */
	nbSteps += Ifo_zero->vmgi_mat->vmg_nr_of_title_sets +  1; /* VTS0 */

	printf ("Copy %d cells.\n", CountSelectedCells( &CellArray ) );
/*	Create empty filesystem tree */
	printf ("Create empty dvd-directory.\n");

	sprintf( targetDir, "%s/%s", tmpDir, TitreDvd );
	sprintf( sysCmd,"mkdir -p \"%s/VIDEO_TS\"", targetDir );
	system( sysCmd );
	sprintf( sysCmd,"mkdir -p \"%s/AUDIO_TS\"", targetDir );
	system( sysCmd );

	sprintf( injectionFile, "%s/vapinj.%d", tmpDir, getpid());

/* Setup vaporizer */
	if( VaporizeInit( Readin, OutputFlux ) < 0 ) {
		ret = -1; goto out;
	}
/* Setup tracks & factor */
	for( i = 0; i < MAX_AUDIO_TRACK; i++ ) {
		if( audioMap->audioTracks[i] ) {
			int id = GetAudioId( noPisteVideo, audioMap->audioTracks[i]-1 );
			printf("VaporizeAudio(%d)\n", id );
			VaporizeAudio( id );
		}
	}
	for( i = 0; i < MAX_SUB_TRACK; i++ ) {
		if( subMap->subTracks[i] ) {
			int id = GetSubId( noPisteVideo, subMap->subTracks[i]-1 );
			printf("VaporizeSubp(%d)\n", id );
			VaporizeSubp( id );
		}
	}

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

/* Copy Root Menu */
	if( CopyMenu( 0, targetDir ) < 0 ) {
		ret = -1; goto out;
	}
/* 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 ) {
			if( dynQual ) {
				factor = ReCpuFactor( factor );
				if( factor ) {
					double pct;

					VaporizeFactor( factor, 0 );
					pct = (1 - (factor - 1)) * 100;
					printf ("Calculate Factor %.2f%%.\n", pct );
				}
			}
		}

//		DBG('b',fprintf(stderr,"CopyCell: (%u,%u)=>(%u,%u)\n", c->startSector, c->lastSector,CurCell->startSector, CurCell->lastSector ););
//		DumpVobus( CurCell );
	}
out:;
	VaporizeTerm();
	if( OutFp ) { fclose( OutFp ); OutFp = NULL; }

	if( !ret ) {
		VobSize[CurVOB] = OutSize;
		UpdateIfo( targetDir );
		UpdateMainIfo( targetDir, fullBackup );
//		if( UpdateVob( CurVTS, CurVOB, OutSize, targetDir ) < 0 )	return( -1 );
		if( UpdateVobs( targetDir ) < 0 )	return( -1 );
	}

	FreeAll();
        DvdClose();
	return( ret );
}

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

static int OutputNull( void * parm, unsigned char * buffer, int size )
{
	OutNullSize += size;
	return( size );
}
