/***************************************************************************
*   Copyright (C) 2005 by Jean-Michel Petit                               *
*   jm_petit@laposte.net                                                  *
*                                                                         *
*   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 of the License, 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; if not, write to the                         *
*   Free Software Foundation, Inc.,                                       *
*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.             *
***************************************************************************/
#include "k9dvdbackup.h"
#include "k9dvd.h"
#include "bswap.h"
#include "k9ifo2.h"
#include "k9vamps.h"
#include "ac.h"
#include "k9dvdtitleset.h"
#include <tdemessagebox.h>
#include <tdeapplication.h>
#include <tqapplication.h>
#include <tqfileinfo.h>

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <tdelocale.h>
#include <tqdir.h>
#include <kstandarddirs.h>
#include "k9backupdlg.h"
#include "dvdread.h"
#include "k9script.h"
#include "k9tools.h"

#define BUF_SECS	1024


#include "k9dvdbackup.moc"

int k9CadtList::compareItems(TQPtrCollection::Item item1,TQPtrCollection::Item item2) {
    cell_adr_t * it1=(cell_adr_t*)item1;
    cell_adr_t * it2=(cell_adr_t*)item2;
    return it1->start_sector - it2->start_sector;
};

int k9TitleSetList::compareItems ( TQPtrCollection::Item item1, TQPtrCollection::Item item2 ) {
    k9TitleSet * it1,*it2;
    it1=(k9TitleSet *) item1;
    it2=(k9TitleSet *)item2;
    return it1->VTS - it2->VTS;
}

/*void k9TitleSet::clearCells() {
    while (!cells.isEmpty()) delete cells.take();
    //qDeleteAll(cells);
    cells.clear();
}
  */    

k9TitleSet::k9TitleSet(int _VTS):TQObject(NULL,"") {
    startSector=0;
    lastSector=0;
    VTS=_VTS;
    cells.setAutoDelete(true);
    ifoTitle=NULL;
}

k9TitleSet::~k9TitleSet() {
    //ifoTitle->closeIFO();
    //delete ifoTitle;
}

k9Cell* k9TitleSet::addCell(int _vts,int _pgc, int _vob) {
    return cells.addCell(_vts,_pgc,_vob);

}

uint32_t k9TitleSet::getSize() {
    return (lastSector +1) ;
}


k9DVDBackup::k9DVDBackup(TQObject* _dvd,const char* name,const TQStringList& )
        : TQObject(NULL, name) {
    DVD = (k9DVD*)_dvd;
    m_dvdread=DVD->getdvd();
    currVTS=0;
    outputFile=NULL;
    currTS=NULL;
    errMsg="";
    error=false;
    backupDlg = new k9BackupDlg(tqApp->mainWidget(),"",true);
    //  cells.setAutoDelete(true);
    vamps=new k9vamps(this);
    m_withMenu=false;
    m_forcedFactor=false;
    m_preserve=true;
}


k9DVDBackup::~k9DVDBackup() {
    if (outputFile !=NULL) {
        outputFile->close();
        delete outputFile;
    }
    delete backupDlg;
    delete vamps;
}

void k9DVDBackup::setWithMenus(bool _value) {
    m_withMenu=_value;
}

bool k9DVDBackup::geterror() {
    return error;
}

void k9DVDBackup::seterror(const TQString &_msg) {
    error=true;
    errMsg=_msg;
}


TQString k9DVDBackup::getErrMsg() {
    return(errMsg);
}

void k9DVDBackup::prepareVTS(int _VTS) {
    if (error)
        return;
    ifo_handle_t *hifo;

    cellSize=0;
    if (currTS==NULL) {
        currVTS=0;
        m_vmgSize=copyMenu2(0);
        calcFactor();
        if (outputFile != NULL) {
            outputFile->close();
            delete outputFile;
            outputFile=NULL;
        }
    }
    if (backupDlg->getAbort()) {
        seterror(tr2i18n("DVD backup cancelled"));
        return;
    }
    //creation of output file
    if (currVTS != _VTS) {
        if (outputFile != NULL) {
            outputFile->close();
            delete outputFile;
            outputFile=NULL;
            updateIfo();
            updateVob(&currTS->cells);
        }

        if (backupDlg->getAbort()) {
            seterror(tr2i18n("DVD backup cancelled"));
            return;
        }

        uint32_t startSector=0;

        if (currTS != NULL) {
            startSector = currTS->startSector + currTS->getSize();
        } else {
            k9Ifo2 *kifo;
            kifo=m_dvdread->getIfo(0);
            hifo = kifo->getIFO();
            if (!hifo) {
                seterror( tr2i18n("unable to open VIDEO_TS.IFO"));
                return;
            }
            startSector= hifo->vmgi_mat->vmg_last_sector+1  ;
        }

        currTS = new k9TitleSet(_VTS);
        currVOB=0;
        currVTS=_VTS;

	calcFactor(); //menus are always shrinked
        currTS->menuSize=copyMenu2(_VTS);
        if (outputFile != NULL) {
            outputFile->close();
            delete outputFile;
        }

        TQString filename;
        filename.sprintf("/VTS_%02d_1.VOB",_VTS);
        filename=output+filename;
        outputFile=new TQFile(filename);
        if (! outputFile->open(IO_WriteOnly)) {
            seterror(tr2i18n("Unable to open file ") + filename);
            return;
        }

        currVOB=1;

        currTS->startSector=startSector;
        currTS->lastSector += currTS->menuSize ;
        titleSets.append(currTS);

        k9Ifo2 *kifo;
        kifo=m_dvdread->getIfo(_VTS);
        currTS->ifoTitle=kifo;

        m_position=0;
        m_copyMenu=false;
        calcFactor();
    }

    currVTS=_VTS;

}


void k9DVDBackup::copyCell(int _VTS,k9Cell * _cell,bool _empty) {
    if (error)
        return;

    prepareVTS(_VTS);
    if (error)
        return;

    if (_cell->getforceFactor())
	forceFactor( _cell->getFactor());

    
    mutex.lock();
    k9Cell *cell= currTS->addCell(_VTS,0,0);
    //JMP cell->startSector=m_position;
    cell->startSector=cell->cellList->getPosition();
    currCell=cell;
    //start cell copy
    mutex.unlock();
    if (!_empty) {
        if (!vamps->running()) {
            vamps->reset();
            vamps->setPreserve(true);
            vamps->setInputSize(argSize);
            TQValueList<int>::iterator it;
            for ( it = _cell->audio.begin(); it != _cell->audio.end(); ++it ) {
                if (m_preserve)
                    vamps->addAudio(*it);
                else
                    vamps->addAudio(*it,_cell->getTitleset()->getNewAudioStreamId(*it));
            }

            for ( it = _cell->subpicture.begin(); it != _cell->subpicture.end(); ++it ) {
                vamps->addSubpicture(*it);
            }

            vamps->setVapFactor(argFactor);
            vamps->start(TQThread::NormalPriority);
        }
        playCell(_VTS,_cell,_empty);
    } else
        copyEmptyPgc(_VTS,_cell);
    tqApp->processEvents();
}

void k9DVDBackup::copyEmptyPgc(int _vts,k9Cell *_cell) {
    if (error)
        return;

    ifo_handle_t *vts_handle=currTS->ifoTitle->getIFO();
    ;

    k9DVDFile *dvdfile;
    if ((dvdfile = m_dvdread->openTitle( _vts))== 0) {
        TQString stmp;
        stmp=i18n("Unable to open titleset %1").arg(_vts);
        seterror(stmp);
        return ;
    }
    backupDlg->setTotalSteps(vts_handle->vtsi_mat->vts_last_sector-vts_handle->vtsi_mat->vtstt_vobs  -1);
    TQString c;
    c=i18n("Extracting titleset %1").arg(_vts);
    backupDlg->setProgressLabel(c);
    backupDlg->show();


    uint32_t sector;
    int32_t len=0;
    uchar buffer[DVD_VIDEO_LB_LEN];
    sector = _cell->startSector;
    backupDlg->setProgress(sector);
    dsi_t	 dsi_pack;
    uint32_t	 nsectors;

    len=dvdfile->readBlocks (sector, 1, buffer);
    //JMP : D V C
    if (!k9Cell::isNavPack( buffer)  || len==-1)
    	setDummyNavPack( buffer,sector);

    k9Vobu * vobu=currCell->addVobu(sector);
    vobu->empty=true;
    currCell->addNewVobus((char*)buffer,DVD_VIDEO_LB_LEN,currCell->cellList->getPosition(),currVOB,outputFile->at());
    outputFile->writeBlock((char*)buffer,DVD_VIDEO_LB_LEN);

    /* parse contained DSI pack */
    navRead_DSI (&dsi_pack, buffer + DSI_START_BYTE);
    currCell->vob = dsi_pack.dsi_gi.vobu_vob_idn;
    
    //dummy video pack    
    setDummyPack( buffer);
    currCell->addNewVobus((char*)buffer,DVD_VIDEO_LB_LEN,currCell->cellList->getPosition()+1,currVOB,outputFile->at());
    outputFile->writeBlock((char*)buffer,DVD_VIDEO_LB_LEN);

    nsectors=1;
    len=1;

    //vobu->size +=nsectors;
    currCell->lastSector=currCell->startSector+ len;  
    currCell->cellList->setPosition(currCell->cellList->getPosition()+1+len);
    currTS->lastSector+=len+1;
    dvdfile->close();
    backupDlg->setProgressTotal(len+1);
    if (!m_forcedFactor) {
	m_cellCopyList->addInbytes( DVD_VIDEO_LB_LEN *2);
    	m_cellCopyList->addOutbytes( DVD_VIDEO_LB_LEN *2);
    } else {
	m_cellCopyList->addFrcinbytes(DVD_VIDEO_LB_LEN *2);
    	m_cellCopyList->addFrcoutbytes( DVD_VIDEO_LB_LEN *2);
    
    }
}


void k9DVDBackup::getOutput(uchar * buffer, uint32_t buflen) {
    if (error)
        return;
    mutex.lock();
    backupDlg->playMovie(buffer,buflen);
    mutex.unlock();

    if (!m_forcedFactor)
        m_cellCopyList->addOutbytes( buflen);
    else
    	m_cellCopyList->addFrcoutbytes( buflen);

    uchar *temp =buffer;
    TQString sName;
    if ((buflen %2048) !=0)
        tqDebug("getOutput, buffer : %u",buflen);
    uint end=0;

    for (uint itemp=0;itemp<buflen;itemp+=DVD_VIDEO_LB_LEN) {
        if (buflen-itemp <DVD_VIDEO_LB_LEN)
            break;


        mutex.lock();
        long fileSize=outputFile->size();
        end=itemp+DVD_VIDEO_LB_LEN;
        if (k9Cell::isNavPack(temp+itemp)) {

            k9Vobu * vobu = vobuQueue.dequeue();

            cellOut=vobu->parent;
            dsi_t dsiPack;
            navRead_DSI (&dsiPack, (uchar*)(temp+itemp) + DSI_START_BYTE);

            cellOut->vob = dsiPack.dsi_gi.vobu_vob_idn;
            if ((dsiPack.dsi_gi.vobu_ea * DVD_VIDEO_LB_LEN) + fileSize >= (1024*1024*1024)) {
                outputFile->close();
                delete outputFile;
                currVOB++;
                if (currVTS==0)
                    sName = "/VIDEO_TS.VOB";
                else
                    sName.sprintf("/VTS_%02d_%d.VOB",(int)currVTS,(int)currVOB);
                sName=output+sName;
                outputFile=new TQFile(sName);
                if ( !outputFile->open(IO_WriteOnly)) {
                    seterror(tr2i18n("Unable to open file ") + sName);
                    mutex.unlock();
                    return;
                }
            }
        }
        cellOut->addNewVobus((char*)(temp+itemp),DVD_VIDEO_LB_LEN,cellOut->cellList->getPosition() ,currVOB,outputFile->at());
        outputFile->writeBlock((char*)(temp+itemp),DVD_VIDEO_LB_LEN);

        backupDlg->setProgressTotal(1);
        cellOut->cellList->setPosition( cellOut->cellList->getPosition()+1);
        if (!m_copyMenu)
            currTS->lastSector++;
        mutex.unlock();
    }
}


/*!
\fn k9DVDBackup::setDevice(TQString _device)
*/
void k9DVDBackup::setDevice(TQString _device) {
    device=_device;
}


/*!
\fn k9DVDBackup::setOutput(TQString _output)
*/
void k9DVDBackup::setOutput(TQString _output) {
    output=TQDir::cleanDirPath(_output);
}


uint32_t k9DVDBackup::copyMenu2(int _vts) {
    if (error || !m_withMenu )
        return 0;
    k9Ifo2 *kifo;
    kifo=m_dvdread->getIfo( _vts);
    ifo_handle_t *hifo =kifo->getIFO();
    m_ifo=hifo;
    uint32_t msize=0;
    uint32_t menuLastSector;
    if (_vts==0)
        msize=hifo->vmgi_mat->vmg_last_sector -1  - 2* hifo->vmgi_mat->vmgi_last_sector;
    else
        msize=hifo->vtsi_mat->vtstt_vobs - hifo->vtsi_mat->vtsi_last_sector -1;

    if (msize==0) {
        //kifo.closeIFO();
        return 0;
    }
    menuLastSector=msize-1;


    m_position=0;
    m_copyMenu=true;
    TQString targetName;
    if (_vts == 0) {
        targetName="VIDEO_TS.VOB";
    } else {
        targetName.sprintf("VTS_%02i_0.VOB",_vts);
    }
    targetName=output+"/"+targetName;

    outputFile=new TQFile(targetName);
    if (! outputFile->open(IO_WriteOnly)) {
        seterror(tr2i18n("Unable to open file ") + targetName);
        return 0;
    }

    k9DVDFile *dvdfile;
    if ((dvdfile = m_dvdread->openMenu( _vts))== 0) {
        TQString stmp;
        stmp=i18n("Unable to open menu for titleset %1").arg(_vts);
        seterror (stmp);
        return 0;
    }

    k9CellList *lstCell;
    if (_vts==0)
        lstCell = &vmgCells;
    else
        lstCell = &currTS->menuCells;

    lstCell->setAutoDelete(true);


    c_adt_t *c_adt = hifo->menu_c_adt;
    // c_adt shouldn't be null. It would say that the menu doesn't contain video !?
    if (c_adt==NULL)
	return 0;

    uint32_t length = c_adt->last_byte + 1 - C_ADT_SIZE;
    cell_adr_t *ptr;
    ptr= c_adt->cell_adr_table;

    uint32_t sector, dsi_next_vobu = 0;
    uint32_t imax=length/sizeof(cell_adr_t);
    TQString c;
    c=i18n("Extracting menu for titleset %1").arg(_vts);
    backupDlg->setProgressLabel(c);

    backupDlg->show();
    backupDlg->setTotalSteps(ptr[imax-1].last_sector);

    k9CadtList cadr;
    
    uint32_t nbCells=0;
    for (uint32_t i=0;i<imax;i++) {
    if ((ptr+i)->last_sector > menuLastSector)
        (ptr+i)->last_sector=menuLastSector;

	if ((ptr+i)->start_sector <=  menuLastSector) {
        	cadr.append(ptr+i);
		nbCells++;
        }
	//else 
	    //tqDebug() << TQString("cell start sector (%1) exceed menu size (%2)").arg((ptr+i)->start_sector).arg(menuLastSector);
    }
    cadr.sort();
    vamps->reset();
    vamps->setPreserve(true);
    for (uint i=1;i<=8;i++)
        vamps->addAudio(i);

    for (uint i=1;i<=32;i++)
        vamps->addSubpicture(i);

    vamps->setVapFactor(argFactor);
    vamps->setInputSize(msize*2048);

    vamps->start(TQThread::NormalPriority);
    //	while(!vamps->running() && !vamps->finished());

    for(uint32_t i = 0; i < nbCells; i++) {
        currCell=lstCell->addCell(_vts,1,1);
        dsi_next_vobu=0;
        cell_adr_t * cellAdr=cadr.at(i);
        for (sector = cellAdr->start_sector;
                sector <= cellAdr->last_sector; sector += dsi_next_vobu & 0x7fffffff) {
            backupDlg->setProgress(sector);
            if (backupDlg->getAbort()) {
                seterror(tr2i18n("DVD backup cancelled"));
            }

            if (error) {
                vamps->abort();
                break;
            } else if (vamps->geterror()) {
                seterror( vamps->geterrMsg());
                break;
            }
            dsi_next_vobu= copyVobu(dvdfile,sector,NULL);
        }
    }
    vamps->setNoData();
    vamps->wait();
    uint32_t size=0;

    //JMP    size=m_position;
    size =lstCell->getPosition();

    dvdfile->close();
    outputFile->close();
    delete outputFile;
    outputFile=NULL;
    //kifo.closeIFO();

    updateVob(lstCell);

    m_copyMenu=false;
    return size;
}




void k9DVDBackup::playCell (int vts_num, k9Cell *_cell,bool _empty) {

    if (error)
        return;

    ifo_handle_t *vts_handle;
    k9DVDFile *dvdfile;
    uint32_t	sector, dsi_next_vobu = 0;
    /* open disc */
    if (m_dvdread->opened()) {
        /* load information for the given VTS */
        // vts_handle = ifoOpen (dvd_handle, vts_num);
        vts_handle=currTS->ifoTitle->getIFO();
        if (!vts_handle) {
            TQString stmp;
            stmp=i18n("Unable to open ifo file for titleset %1").arg(vts_num);
            seterror (stmp);
            //JMP            vamps->setNoData();
            return;
        }

        backupDlg->setTotalSteps( vts_handle->vtsi_mat->vts_last_sector-vts_handle->vtsi_mat->vtstt_vobs  -1);
        TQString c;
        c=i18n("Extracting titleset %1").arg(vts_num);
        backupDlg->setProgressLabel(c);
        backupDlg->show();
    } else {
        seterror(tr2i18n("Unable to open DVD"));
        //JMP        vamps->setNoData();
        return;
    }


    /* open VTS data */
    dvdfile = m_dvdread->openTitle (vts_num);
    if (! dvdfile) {
        TQString stmp;
        stmp=i18n("Unable to open vobs for titleset %1").arg(vts_num);
        seterror( stmp);
        //JMP        vamps->setNoData();
        return;
    }


    /* loop until out of the cell */
//TO REMOVE    currCell->oldStartSector=_cell->startSector;
    for (sector = _cell->startSector;
            sector <= _cell->lastSector; sector += dsi_next_vobu & 0x7fffffff) {

        backupDlg->setProgress(sector);
        if (backupDlg->getAbort()) {
            seterror(tr2i18n("DVD backup cancelled"));
        }

        if (error) {
            vamps->abort();
            break;
        } else if (vamps->geterror()) {
            seterror( vamps->geterrMsg());
            break;
        }
        dsi_next_vobu= copyVobu(dvdfile,sector,NULL,_empty);
    }
    dvdfile->close();
}

void k9DVDBackup::setDummyNavPack(uchar *buf,uint32_t _sector)
{
  int8_t *ptr = (int8_t*)buf;
  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 */
    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
  };

  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*)buf + 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));

  dsi_t dsiPack;
  pci_t pciPack;
  navRead_DSI (&dsiPack, buf + DSI_START_BYTE);
  k9Ifo2::navRead_PCI (&pciPack, buf+0x2d);
  dsiPack.dsi_gi.nv_pck_lbn=_sector;
  dsiPack.dsi_gi.vobu_ea = 1;

  navRead_DSI((dsi_t*)(buf + DSI_START_BYTE),(uchar*)&dsiPack);
  pciPack.pci_gi.nv_pck_lbn =dsiPack.dsi_gi.nv_pck_lbn;
  k9Ifo2::navRead_PCI((pci_t*)(buf+0x2d),(uchar*)&pciPack);
}


void k9DVDBackup::setDummyPack(uchar *_buffer) {
    int8_t *ptr = (int8_t*)_buffer;
    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
        };

    tc_memcpy (ptr, dummy_pack, sizeof (dummy_pack));
    ptr += sizeof (dummy_pack);
    memset (ptr, 0xff, DVD_VIDEO_LB_LEN - sizeof (dummy_pack));

}


uint32_t k9DVDBackup::findNextVobu(uint32_t _sector) {
    k9Ifo2 *ifo;
    ifo = m_dvdread->getIfo(currVTS);
    m_ifo=ifo->getIFO();
    vobu_admap_t * vobu_admap;
    if (m_copyMenu)
	vobu_admap = m_ifo->menu_vobu_admap;
    else
        vobu_admap = m_ifo->vts_vobu_admap;
    uint32_t length = vobu_admap->last_byte + 1 - VOBU_ADMAP_SIZE;
    for(uint32_t i = 0; i < length/sizeof(uint32_t); i++) {
        if (vobu_admap->vobu_start_sectors[i] >= _sector) {
	    uint32_t nextVobu=vobu_admap->vobu_start_sectors[i+1];
	    //ifo.closeIFO();
            return nextVobu;
        }
    }
    //ifo.closeIFO();
    return 0;

}



uint32_t k9DVDBackup::copyVobu(k9DVDFile  *_fileHandle,uint32_t _startSector,k9Vobu * _vobu,bool _empty) {
    dsi_t	dsi_pack;
    k9Vobu * currVobu;
    bool badNavPack=false;

    uint32_t	nsectors, nextVobu=0;
    int32_t len=0;
    uchar *buf;
    uint32_t sector=_startSector;
    /* read nav pack */
    buf=(uchar*) malloc(DVD_VIDEO_LB_LEN);
    len = _fileHandle->readBlocks ( sector, 1, buf);
    /* parse contained DSI pack */

    //test if nav pack is ok
    if (len !=-1) {
	navRead_DSI (&dsi_pack, buf + DSI_START_BYTE);
	if (dsi_pack.dsi_gi.nv_pck_lbn != sector) {
		len=-1;
	}
    }

    if (len==-1) {
       setDummyNavPack(buf,sector); 
       nextVobu=findNextVobu(sector);
       tqDebug ("VOBU : %u Read Error !!!!  ==>  %u",sector,nextVobu);
       badNavPack=true;
    }
    currVobu=_vobu;

    mutex.lock();
    if (k9Cell::isNavPack(buf)) {
//TO REMOVE        currCell->oldLastSector=sector;
        if (currVobu==NULL) {
            currVobu =currCell->addVobu(sector);
            vobuQueue.enqueue(currVobu);
        }
    }
    mutex.unlock();
    /* generate an MPEG2 program stream (including nav packs) */
    wrote=false;
    
    vamps->addData(buf,DVD_VIDEO_LB_LEN);
    
    
    
    if (!m_forcedFactor)
    	m_cellCopyList->addInbytes( DVD_VIDEO_LB_LEN);
    else
    	m_cellCopyList->addFrcinbytes(DVD_VIDEO_LB_LEN);
    uint32_t end = 0;

    if (badNavPack) {
       setDummyPack(buf);
       nsectors=1;
       if (nextVobu !=0) end=nextVobu-_startSector-1;

    } else {
        if (!_empty) 
		nsectors = dsi_pack.dsi_gi.vobu_ea;
	else
		nsectors = 1;
			
	//uint32_t dsi_next_vobu = dsi_pack.vobu_sri.next_vobu;
	
	buf=(uchar*) realloc(buf,nsectors*DVD_VIDEO_LB_LEN);
	
	end=dsi_pack.dsi_gi.vobu_ea;
	
	if (_empty)
	   setDummyPack(buf);
	/* read VOBU */
	else
		for (uint32_t i=0;i< nsectors;i++) {
			len = _fileHandle->readBlocks ( (sector + 1)+i, 1, buf +(i*DVD_VIDEO_LB_LEN));
			if (len==-1) {
			tqDebug ("VOBU : %u Read Error !!!!",sector);
			//setDummyPack(buf + (i*DVD_VIDEO_LB_LEN));
			setDummyPack(buf);
			nsectors=1;
			break;
			}
		}
    }

    /* write VOBU */
    for (uint32_t i=0;i<nsectors ;i++) {
       	vamps->addData(buf + (i*DVD_VIDEO_LB_LEN), DVD_VIDEO_LB_LEN);
    }
    free(buf);

    if (! m_forcedFactor)
    	m_cellCopyList->addInbytes( nsectors*DVD_VIDEO_LB_LEN);
    else
    	m_cellCopyList->addFrcinbytes( nsectors*DVD_VIDEO_LB_LEN);

    mutex.lock();
    tqApp->processEvents();
    mutex.unlock();

    return (end+1);
}



k9Vobu * k9DVDBackup::remapVobu(uint32_t *value) {
    k9Vobu *vobu=NULL;
    uint32_t sector,mask;
    if ( (*value & 0x80000000) == 0x80000000) {
        sector = *value & 0x7FFFFFFF;
        mask=0x80000000;
    } else {
        sector =*value;
        mask=0;
    }
    *value=0;
    k9CellList *lstCell;
    if(! m_copyMenu) {
        lstCell=&(currTS->cells);
    } else {
        if (currVTS==0)
            lstCell=&vmgCells;
        else
            lstCell=&(currTS->menuCells);
    }
    vobu=lstCell->findVobu(sector);
    if (vobu !=NULL) {
        *value = vobu->newSector | mask;
        return vobu;
    }

    *value=0;
    return vobu;

}


k9Vobu * k9DVDBackup::remapOffset(uint32_t _sector,uint32_t *_offset,int _dir) {
    k9Vobu *vobu1=NULL, *vobu2=NULL;
    uint32_t offset,sector;
    uint32_t maskOffset1=0,maskOffset2=0,maskSector=0;

    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;
        }

        k9CellList *lstCell;
        if(! m_copyMenu) {
            lstCell=&(currTS->cells);
        } else {
            if (currVTS==0)
                lstCell=&vmgCells;
            else
                lstCell=&(currTS->menuCells);
        }
        vobu1 = lstCell->findVobu(sector);
        vobu2 = lstCell->findVobu(sector+_dir*offset);



        if ((vobu1 !=NULL) && (vobu2!=NULL)) {
            *_offset = labs(vobu1->newSector - vobu2->newSector)  | maskOffset1 ;
            *_offset |= maskOffset2;
            return vobu2;
        }

        if (vobu1==NULL && vobu2==NULL)
            tqDebug ("remapOffset : sector not found");
    }
    return vobu2;
}


void k9DVDBackup::updateMainIfo() {

    if (error)
        return;

    k9Ifo2 ifo(m_dvdread);
    ifo.setOutput(output);
    ifo.setDevice(device);

    ifo.openIFO(0);
    ifo_handle_t *hifo =ifo.getIFO();

    hifo->vmgi_mat->vmg_last_sector= 1+ m_vmgSize+hifo->vmgi_mat->vmgi_last_sector*2;
    if (m_vmgSize >0) {
        if (hifo->vmgi_mat->vmgm_vobs != hifo->vmgi_mat->vmgi_last_sector +1)
            tqDebug ("error in ifo file : vmgm_vobs %u -> %u)",hifo->vmgi_mat->vmgm_vobs,hifo->vmgi_mat->vmgi_last_sector +1);
        hifo->vmgi_mat->vmgm_vobs=hifo->vmgi_mat->vmgi_last_sector +1;
    }

    currVTS=0;

    k9Vobu* vobu2=NULL;
    uint32_t newPos=0;
    //update first play PGC
    if (hifo->first_play_pgc !=NULL ) {
	m_copyMenu=true;
        pgc_t *pgc=hifo->first_play_pgc;
        if (! m_withMenu && pgc->command_tbl!=0 ) {
        	pgc->command_tbl->nr_of_pre=0;
        	pgc->command_tbl->nr_of_post=0;
        	pgc->command_tbl->nr_of_cell=0;
        }
        
        
        cell_playback_t *cell_playback =pgc->cell_playback;
        uint32_t nr= pgc->nr_of_cells;

        vobu2=NULL;
        cell_playback_t cell;
        newPos=0;
        for( uint32_t j = 0; j < nr; j++) {
            k9Vobu *vobu=remapVobu(&cell_playback[j].first_sector);
            vobu2=vobu;

            remapVobu(&cell_playback[j].first_ilvu_end_sector);
            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;// -1 ;
                cell_playback[newPos]=cell_playback[j];
                cell=cell_playback[newPos];
                newPos++;
            } else {
                cell_playback[newPos]=cell;
                newPos++;
            }
        }
        for (uint32_t j=newPos;j<nr;j++)
            cell_playback[j].last_sector=0;
        pgc->nr_of_cells=newPos;
	m_copyMenu=false;
    }



    updatePgci_ut(hifo);
    update4Menu(hifo);

    //mise ï¿½jour des startSectors
    k9TitleSet *TSp=NULL;
    titleSets.sort();
    for (uint iTS=0;iTS < titleSets.count();iTS++) {
        k9TitleSet *TS=titleSets.at(iTS);
        uint32_t startSector;
        if (TSp!=NULL)
            startSector = TSp->startSector + TSp->getSize();
        else
            startSector=hifo->vmgi_mat->vmg_last_sector+1  ;
        TS->startSector=startSector;
        TSp=TS;
    }

    hifo->vmgi_mat->vmg_category=0;
    bool found=false;
    for (uint32_t i=0 ; i< hifo->tt_srpt->nr_of_srpts;i++) {
        for (uint iTS=0;iTS<titleSets.count() &&(!found);iTS++) {
            k9TitleSet *TS=titleSets.at(iTS);
            if (TS->VTS == hifo->tt_srpt->title[i].title_set_nr ) {
                hifo->tt_srpt->title[i].title_set_sector = TS->startSector;
                found=true;
            }
        }
        found=false;
    }

    ifo.saveIFO();
    ifo.closeIFO();

}

void k9DVDBackup::updatePgci_ut(ifo_handle_t *_hifo) {
    pgci_ut_t *pgci_ut=_hifo->pgci_ut;
    k9Vobu* vobu2=NULL;
    uint newPos=0;
    //update pgci_ut

    if (pgci_ut !=0 && m_withMenu) {
        m_copyMenu=true;
        for (uint i=0; i <pgci_ut->nr_of_lus;i++) {
            pgci_lu_t *pgci_lu= &pgci_ut->lu[i];
            pgcit_t * pgcit= pgci_lu->pgcit;
            for (uint i2=0 ; i2 <pgcit->nr_of_pgci_srp;i2++) {
                pgci_srp_t * pgci_srp=&pgcit->pgci_srp[i2];
                pgc_t *pgc=pgci_srp->pgc;
                
                
                if (! m_withMenu && pgc->command_tbl!=0) {
        		pgc->command_tbl->nr_of_pre=0;
        		pgc->command_tbl->nr_of_post=0;
        		pgc->command_tbl->nr_of_cell=0;
        	}	

                cell_playback_t *cell_playback =pgc->cell_playback;
                uint32_t nr= pgc->nr_of_cells;

                vobu2=NULL;
                cell_playback_t cell;
                newPos=0;
                for( uint32_t j = 0; j < nr; j++) {
                    k9Vobu *vobu=remapVobu(&cell_playback[j].first_sector);
                    vobu2=vobu;

                    if (cell_playback[j].first_ilvu_end_sector !=0) {
                        uint32_t tmp=cell_playback[j].first_ilvu_end_sector+1;
                        remapVobu(&tmp);
                        if (tmp!=0)
                            cell_playback[j].first_ilvu_end_sector=tmp-1;
                    }
                    if (vobu !=NULL) {
                        vobu=remapVobu(&cell_playback[j].last_vobu_start_sector);
			if (vobu !=NULL) 
				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 (uint32_t j=newPos;j<nr;j++)
                    cell_playback[j].last_sector=0;
                pgc->nr_of_cells=newPos;
            }
        }
        m_copyMenu=false;
    }
    
    if (!m_withMenu   ) {
	k9Script *script=new k9Script(_hifo,DVD);
	script->updatePGCIUT();
	script->updateFPPGC();
	delete script;
	if (_hifo->vmgi_mat) {
		_hifo->vmgi_mat->vmgm_c_adt=0;
		_hifo->vmgi_mat->vmgm_vobu_admap=0;
	} else {	
		_hifo->vtsi_mat->vtsm_c_adt=0;
		_hifo->vtsi_mat->vtsm_vobu_admap=0;
	}    
    }


}

void k9DVDBackup::update4Menu(ifo_handle_t *_hifo) {
    if (!m_withMenu)
    	return;
    // Mise ï¿½jour vtsm_c_adt pour le menu
    m_copyMenu=true;   //indispensable pour remapvobu
    c_adt_t *c_adt = _hifo->menu_c_adt;
    uint32_t length;
    if (c_adt!=NULL) {
        length = c_adt->last_byte + 1 - C_ADT_SIZE;
        cell_adr_t *ptr= c_adt->cell_adr_table;
        for(uint32_t i = 0; i < length/sizeof(cell_adr_t); i++) {
            uint32_t startSect=ptr[i].start_sector;
            // last sector of a vobu = start sector of next vobu -1
            uint32_t lastSect= ptr[i].last_sector +1;
            k9Vobu *vobu=remapVobu(&startSect);
            if (vobu == NULL)
                tqDebug ("Error : could not find startSector");
            else  {
                if (remapVobu(&lastSect)==NULL)
                    lastSect=vobu->parent->lastSector;
                else
                    lastSect--;
                ptr[i].start_sector = startSect;
                ptr[i].last_sector = lastSect;
            }

        }
    }

    vobu_admap_t * vobu_admap = _hifo->menu_vobu_admap;
    if (vobu_admap != NULL) {
        length = vobu_admap->last_byte + 1 - VOBU_ADMAP_SIZE;
        uint newPos=0;
        for(uint32_t 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 (uint32_t 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;
    }
    m_copyMenu=false;

}

void k9DVDBackup::updateIfo() {

    if (error)
        return;

    k9Ifo2 ifo(m_dvdread);
    ifo.setOutput(output);
    ifo.setDevice(device);

    ifo.openIFO(currVTS);

    ifo_handle_t *hifo =ifo.getIFO();

    pgcit_t * pgcit = hifo->vts_pgcit;

    //update total VTS size with IFO size 
   currTS->lastSector += 2 *(hifo->vtsi_mat->vtsi_last_sector ) +1;

    hifo->vtsi_mat->vts_last_sector =   currTS->lastSector ;

    hifo->vtsi_mat->vtstt_vobs = hifo->vtsi_mat->vtsi_last_sector + 1 + currTS->menuSize;
    //JMP
    if (currTS->menuSize >0) {
        if (hifo->vtsi_mat->vtsm_vobs != hifo->vtsi_mat->vtsi_last_sector +1)
            tqDebug ("error in ifo file %u : vtsm_vobs %u -> %u",currTS->VTS,hifo->vtsi_mat->vtsm_vobs,hifo->vtsi_mat->vtsi_last_sector +1);
        hifo->vtsi_mat->vtsm_vobs= hifo->vtsi_mat->vtsi_last_sector +1 ;

    }else
       hifo->vtsi_mat->vtsm_vobs=0;

    updatePgci_ut(hifo);

    k9Vobu* vobu2=NULL;
    uint32_t newPos=0;
    //update first play PGC
    if (hifo->first_play_pgc !=NULL ) {
        pgc_t *pgc=hifo->first_play_pgc;
        if (! m_withMenu && pgc->command_tbl!=0) {
        	pgc->command_tbl->nr_of_pre=0;
        	pgc->command_tbl->nr_of_post=0;
        	pgc->command_tbl->nr_of_cell=0;
        }
        cell_playback_t *cell_playback =pgc->cell_playback;
        uint32_t nr= pgc->nr_of_cells;

        vobu2=NULL;
        cell_playback_t cell;
        newPos=0;
        for( uint j = 0; j < nr; j++) {
            k9Vobu *vobu=remapVobu(&cell_playback[j].first_sector);
            vobu2=vobu;

            remapVobu(&cell_playback[j].first_ilvu_end_sector);
            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;// -1 ;
                cell_playback[newPos]=cell_playback[j];
                cell=cell_playback[newPos];
                newPos++;
            } else {
                cell_playback[newPos]=cell;
                newPos++;
            }
        }
        for (uint32_t j=newPos;j<nr;j++)
            cell_playback[j].last_sector=0;
        pgc->nr_of_cells=newPos;
    }

    newPos=0;


    audio_attr_t vts_audio_attr[8];

    //update each PGC
    bool bUpdateAudioAttr=true;

    if (!m_preserve) {
        memcpy (vts_audio_attr, hifo->vtsi_mat->vts_audio_attr,sizeof(audio_attr_t)*8);
        memset(hifo->vtsi_mat->vts_audio_attr,0,sizeof(audio_attr_t)*8);
    }

    for(uint32_t i = 0; i < pgcit->nr_of_pgci_srp; i++) {
        pgc_t *pgc=pgcit->pgci_srp[i].pgc;
        int numTitle=pgcit->pgci_srp[i].entry_id & 0x7F;
	
	if (! m_withMenu) {
		k9Script *script=new k9Script(hifo,DVD);        
		script->updatePGC(pgc,currTS->VTS,numTitle);
		delete script;
	}
	 
        //shift audio streams;       
        if (!m_preserve) {
            int idx=0;
            uint16_t audio_control [8];
            memcpy(audio_control,pgc->audio_control,sizeof(uint16_t) *8);
            memset(pgc->audio_control,0,sizeof(uint16_t) *8);
            for (int istr=0; istr< 8; istr++) {
                uint16_t ctrl=audio_control[istr] & 0xF8FF;
                int streamId = 1+ ((audio_control[istr]>>8) & 0x7) ;
                int newStreamId = DVD->gettitleset(currTS->VTS-1)->getNewAudioStreamId(streamId);
                if (newStreamId !=0) {
                    pgc->audio_control[idx]=ctrl | ((newStreamId-1)<<8);
                    if (bUpdateAudioAttr) {
                        hifo->vtsi_mat->vts_audio_attr[idx]=vts_audio_attr[istr];
                    }
                    idx++;
                }
            }
            if (bUpdateAudioAttr)
                hifo->vtsi_mat->nr_of_vts_audio_streams=idx;
            bUpdateAudioAttr=false;
        } 
 
        cell_playback_t *cell_playback =pgc->cell_playback;
        uint32_t nr= pgc->nr_of_cells;

        vobu2=NULL;
        cell_playback_t cell;
        newPos=0;
        for( uint32_t j = 0; j < nr; j++) {
            k9Vobu *vobu=remapVobu(&cell_playback[j].first_sector);
            vobu2=vobu;

            if (cell_playback[j].first_ilvu_end_sector !=0) {
                uint32_t tmp=cell_playback[j].first_ilvu_end_sector+1;
                remapVobu(&tmp);
                if (tmp!=0)
                    cell_playback[j].first_ilvu_end_sector=tmp-1;
            }
            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;// -1 ;
                cell_playback[newPos]=cell_playback[j];
                cell=cell_playback[newPos];
                newPos++;
            } else {
                cell_playback[newPos]=cell;
                newPos++;
            }
        }
        for (uint32_t j=newPos;j<nr;j++)
            cell_playback[j].last_sector=0;
        pgc->nr_of_cells=newPos;
    }


    c_adt_t *c_adt = hifo->vts_c_adt;
    uint32_t length = c_adt->last_byte + 1 - C_ADT_SIZE;
    cell_adr_t *ptr= c_adt->cell_adr_table;
    newPos=0;
    for(uint32_t i = 0; i < length/sizeof(cell_adr_t); i++) {
        uint32_t startSect=ptr[i].start_sector;
        // last sector of a vobu = start sector of next vobu -1
        uint32_t lastSect= ptr[i].last_sector +1;
        k9Vobu *vobu=remapVobu(&startSect);
        if (vobu == NULL)
            tqDebug ("Error : could not find startSector");
        else {
            if (remapVobu(&lastSect)==NULL)
                lastSect= vobu->parent->lastSector;
            else
                lastSect--;
            ptr[i].start_sector = startSect;
            ptr[i].last_sector = lastSect;
        }
    }


    vobu_admap_t * vobu_admap = hifo->vts_vobu_admap;
    length = vobu_admap->last_byte + 1 - VOBU_ADMAP_SIZE;
    newPos=0;
    for(uint32_t 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 (uint32_t 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
    vts_tmapt_t *vts_tmapt=hifo->vts_tmapt;
    if (vts_tmapt) {
        for(uint32_t i = 0; i < vts_tmapt->nr_of_tmaps; i++) {
            if(vts_tmapt->tmap[i].nr_of_entries == 0) { // Early out if zero entries
                continue;
            }
            map_ent_t * map_ent=vts_tmapt->tmap[i].map_ent;
            newPos=0;
            for(uint32_t 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 (int j = newPos; j < vts_tmapt->tmap[i].nr_of_entries;j++)
                map_ent[j]=0;
            vts_tmapt->tmap[i].nr_of_entries=newPos;
        }
    }

    update4Menu(hifo);

    ifo.saveIFO();
}

void k9DVDBackup::updateVob(k9CellList *cellLst) {
    int nbVobuUpdated=0;

    uchar buffer[DVD_VIDEO_LB_LEN];
    TQFile *file=NULL;
    TQString dbg;
    int pVobNum=-1;
    //for (uint iCell=0;iCell<currTS->cells.count();iCell++) {
    //    k9Cell *cell=currTS->cells.at(iCell);
    for (uint iCell=0;iCell< cellLst->count();iCell++) {
        k9Cell *cell=cellLst->at(iCell);
        for (uint ivobu=0; ivobu<cell->vobus.count();ivobu++) {
            tqApp->processEvents();
            k9Vobu * vobu = cell->vobus.at(ivobu);
            int VobNum=vobu->vobNum;
            if (error)
                return;
            if (pVobNum !=VobNum) {
                if (file !=NULL){
                    file->close();
                    delete file;
                }
                    //fclose(file);
                TQString sName;
                if (currVTS==0)
                    sName="VIDEO_TS.VOB";
                else
                    sName.sprintf("VTS_%02d_%d.VOB",(int)currVTS,(int)VobNum);
                dbg=i18n("Updating vob %1").arg(sName);
                sName=output+"/"+sName;
                TQFileInfo finfo(sName);
                long fileSize=finfo.size();

                backupDlg->setTotalSteps(fileSize);
                backupDlg->setProgressLabel(dbg);
		file =new TQFile(sName);
		file->open( IO_ReadWrite);
                //file=fopen(sName,"r+b");
                pVobNum=VobNum;
            }
            if( file !=NULL) {
                uint32_t sector=0;
                long pos=vobu->vobPos;
                backupDlg->setProgress(pos);
                if (backupDlg->getAbort()) {
                    seterror(tr2i18n("DVD backup canceled"));
                    break;
                }

                bool emptyPgc=false;
                /*		long currpos=ftell(file);
                		long offset=pos-currpos;
                		fseek(file,offset,SEEK_CUR);
                */
                //fseek(file,pos,SEEK_SET);
                file->at(pos);
                //fread(buffer,DVD_VIDEO_LB_LEN,1,file);
                file->readBlock((char*)buffer,DVD_VIDEO_LB_LEN);
                if (k9Cell::isNavPack((uchar*)buffer)) {
                    dsi_t dsiPack;
                    pci_t pciPack;
                    nbVobuUpdated++;
                    navRead_DSI (&dsiPack, buffer + DSI_START_BYTE);
                    k9Ifo2::navRead_PCI (&pciPack, buffer+0x2d);
                    sector=dsiPack.dsi_gi.nv_pck_lbn;  //JMP : pour debug
                    //vobu=remapVobu(&dsiPack.dsi_gi.nv_pck_lbn );
                    sector=vobu->oldSector;
                    dsiPack.dsi_gi.nv_pck_lbn=vobu->newSector;
                    if (vobu != NULL) {
                        dsiPack.dsi_gi.vobu_ea = vobu->size;
                        emptyPgc=vobu->empty;
                    } else {
                        dbg.sprintf("remapVobu failed for %d",dsiPack.dsi_gi.nv_pck_lbn);
                    }

                    if (!emptyPgc) {
                        remapOffset(sector, &dsiPack.vobu_sri.next_video,1 );
                        for (int 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 (int 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 (int i =0 ;i <8 ;i++) {
                            //if (((dsiPack.synci.a_synca[i] & 0x8000) != 0x8000 ) && (dsiPack.synci.a_synca[i] !=0x3FFF) && (dsiPack.synci.a_synca[i] !=0x0)) {
                            if ( (dsiPack.synci.a_synca[i] !=0x3FFF) && (dsiPack.synci.a_synca[i] !=0x0)) {
                                if (vobu->firstAudio[i] !=-1) {
                                    dsiPack.synci.a_synca[i]=vobu->firstAudio [i];
                                } else {
                                    //JMP dsiPack.synci.a_synca[i] =0;
                                    dsiPack.synci.a_synca[i] =0x3FFF;
                                }
                            }
                        }
                        //1st subpicture packet
                        for (int 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) && (dsiPack.synci.sp_synca[i] != 0x0)) {
                              if ((dsiPack.synci.sp_synca[i] != 0x3FFFFFFF) && (dsiPack.synci.sp_synca[i] != 0x0)) {
                                if (vobu->firstSubp[i] !=-1) {
                                    dsiPack.synci.sp_synca[i]=vobu->firstSubp [i];
                                } else {
                                    //JMP dsiPack.synci.sp_synca[i] =0;
                                    dsiPack.synci.sp_synca[i] =0x3FFFFFFF;
                                }
                            }
                        }
                        //ILVU

                        for (int i=0;i<9;i++) {
                            if (dsiPack.sml_agli.data[i].address !=0 && dsiPack.sml_agli.data[i].address !=0x7FFFFFFF) {
                                uint32_t tmpAdr=dsiPack.sml_agli.data[i].address;
                                uint32_t tmpSize=(dsiPack.sml_agli.data[i].address & 0x7FFFFFFF)  + dsiPack.sml_agli.data[i].size;
                                int dir;
                                if ((tmpAdr & 0x80000000) ==0x80000000)
                                    dir=-1;
                                else
                                    dir=1;
                                remapOffset(sector,&tmpAdr,dir);
                                remapOffset(sector,&tmpSize,1);
                                dsiPack.sml_agli.data[i].address=tmpAdr;
                                dsiPack.sml_agli.data[i].size=tmpSize-(tmpAdr &0x7FFFFFFF);
                            }
                        }


                        if (dsiPack.sml_pbi.ilvu_ea !=0) {
                            uint32_t tmp=dsiPack.sml_pbi.ilvu_ea+1;
                            remapOffset(sector,&tmp,1);
                            if (tmp!=0)
                                tmp--;
                            dsiPack.sml_pbi.ilvu_ea=tmp;
                        }
                        if (dsiPack.sml_pbi.ilvu_sa !=0) {
                            k9Vobu *vobu2=remapOffset(sector,&dsiPack.sml_pbi.ilvu_sa,1);
                            if (vobu2!= NULL) {
                                TQFile *file2;
                                if ( vobu2->vobNum != VobNum) {
                                    TQString sName;
                                    sName.sprintf("/VTS_%02d_%d.VOB",(int)currVTS,(int)vobu2->vobNum);
                                    sName=output+sName;
                                    file2=new TQFile(sName);
                                    file2->open(IO_ReadWrite);
                                    //file2=fopen(sName,"rb");
                                } else
                                    file2=file;
                                //fseek(file2,vobu2->vobPos,SEEK_SET);
                                file2->at(vobu2->vobPos);
                                uchar *tmpbuff=(uchar*)malloc(2048);
                                //fread(tmpbuff,DVD_VIDEO_LB_LEN,1,file2);
                                file2->readBlock( (char*)tmpbuff,DVD_VIDEO_LB_LEN);
                                dsi_t dsiNext;
                                navRead_DSI (&dsiNext, tmpbuff + DSI_START_BYTE);
                                uint32_t sectmp= dsiNext.sml_pbi.ilvu_ea+1;
                                remapOffset(dsiNext.dsi_gi.nv_pck_lbn,&sectmp,1);
                                dsiPack.sml_pbi.size=sectmp;
                                free (tmpbuff);
                                if (vobu2->vobNum!=VobNum) {
                                    file2->close();
                                    delete file2;
                                }
                                   
                            }
                        }
                        // end block reference frames
                        dsiPack.dsi_gi.vobu_1stref_ea = vobu->firstRef;
                        dsiPack.dsi_gi.vobu_2ndref_ea=vobu->secondRef;
                        dsiPack.dsi_gi.vobu_3rdref_ea=vobu->thirdRef;


                        // update pci pack
                        for (int i=0; i<9;i++) {
                            if ((pciPack.nsml_agli.nsml_agl_dsta[i] & 0x80000000) != 0x80000000)
                                remapOffset(sector,&pciPack.nsml_agli.nsml_agl_dsta[i],1);
                        }

                    } else {
                        dsiPack.vobu_sri.next_video= 0xbfffffff;
                        for (int i =0;i<19;i++)
                            dsiPack.vobu_sri.fwda[i] = 0x3fffffff;
                        dsiPack.vobu_sri.next_vobu=0x3fffffff;
                        dsiPack.vobu_sri.prev_vobu=0x3fffffff;
                        for (int i =0;i<19;i++)
                            dsiPack.vobu_sri.bwda[i] = 0x3fffffff;
                        dsiPack.vobu_sri.prev_video=0xbfffffff;
                        for (int i =0 ;i <8 ;i++)
                            dsiPack.synci.a_synca[i]=0x3fff;
                        for (int i =0 ;i <32 ;i++)
                            dsiPack.synci.sp_synca[i] =0x3FFFFFFF;
                        // end block reference frames
                        dsiPack.dsi_gi.vobu_1stref_ea = 0;
                        dsiPack.dsi_gi.vobu_2ndref_ea=0;
                        dsiPack.dsi_gi.vobu_3rdref_ea=0;
                        //JMP for tests
                        pciPack.pci_gi.vobu_s_ptm=0;
                        pciPack.pci_gi.vobu_e_ptm=0;
                        pciPack.pci_gi.vobu_se_e_ptm=0;
                        pciPack.pci_gi.e_eltm.hour = pciPack.pci_gi.e_eltm.minute =pciPack.pci_gi.e_eltm.second=0;
                        dsiPack.dsi_gi.c_eltm.hour=dsiPack.dsi_gi.c_eltm.minute=dsiPack.dsi_gi.c_eltm.second=0;
                        
                        for (int i=0;i<9;i++) {
                            dsiPack.sml_agli.data[i].address=0x7FFFFFFF;
                            dsiPack.sml_agli.data[i].size=0;
                        }
                        dsiPack.sml_pbi.ilvu_ea=0;
                        dsiPack.sml_pbi.ilvu_sa=0;
                        dsiPack.sml_pbi.size=0;
                        
                        dsiPack.dsi_gi.vobu_1stref_ea = 0;
                        dsiPack.dsi_gi.vobu_2ndref_ea=0;
                        dsiPack.dsi_gi.vobu_3rdref_ea=0;

                    }
                    // mise en place des donnees modifiï¿½s dans le buffer de sortie
                    navRead_DSI((dsi_t*)(buffer + DSI_START_BYTE),(uchar*)&dsiPack);
                    pciPack.pci_gi.nv_pck_lbn =dsiPack.dsi_gi.nv_pck_lbn;
                    k9Ifo2::navRead_PCI((pci_t*)(buffer+0x2d),(uchar*)&pciPack);
                    //mise ï¿½jour du fichier
                    //fseek(file,pos,SEEK_SET);
                    file->at(pos);
                    //fwrite(buffer,DVD_VIDEO_LB_LEN,1,file);
                    file->writeBlock((const char*)buffer,DVD_VIDEO_LB_LEN);
                }

            } else {
                tqDebug ("erreur positionning");
            }
        }
    }
    if (file!=NULL) {
    	file->close();
    	delete file;
    }
}


uint k9DVDBackup::getLastCell(k9CellCopyList *_cellCopyList, uint _index) {

    k9Cell *orig=(k9Cell*)_cellCopyList->at(_index);
    uint result=_index;

    uint64_t dsize=1+orig->lastSector-orig->startSector;

    uchar audioOrig[8];
    uchar subpOrig[32];
    memset(audioOrig,0,sizeof(uchar)*8);
    memset(subpOrig,0,sizeof(uchar)*32);

    ;
    TQValueList<int>::iterator it;
    for ( it = orig->audio.begin(); it != orig->audio.end(); ++it )
        audioOrig[*it -1]=1;

    for ( it = orig->subpicture.begin(); it != orig->subpicture.end(); ++it )
        subpOrig[*it -1]=1;

   float factor=-1;

    for (uint iCell=_index+1;(iCell<_cellCopyList->count()) ;iCell++) {
        k9Cell *cell=(k9Cell*)_cellCopyList->at(iCell);
        if ((cell->vts== orig->vts) && ( cell->selected)) {
	    //if the cell factor changed, it's a new group of cells
	    if (factor==-1)
		factor=cell->getFactor();
	    else {
		if (cell->getFactor()!=factor)
		   break;
	    }

            uchar audio[8];
            uchar subp[32];
            memset(audio,0,sizeof(uchar)*8);
            memset(subp,0,sizeof(uchar)*32);
            ;
            TQValueList<int>::iterator it;
            for ( it = cell->audio.begin(); it != cell->audio.end(); ++it )
                audio[*it -1]=1;

            for ( it = cell->subpicture.begin(); it != cell->subpicture.end(); ++it )
                subp[*it -1]=1;

            if ( (memcmp(audioOrig,audio,sizeof(uchar)*8) ==0) && (memcmp(subpOrig,subp,sizeof(uchar)*32) ==0))  {
                result=iCell;
                dsize+=1+cell->lastSector-cell->startSector;
            } else
                break;
        } else
            break;
    }

    dsize*=DVD_BLOCK_LEN;
    argSize=dsize;
    return result;
}

void k9DVDBackup::calcFactor() {
    double factor=m_cellCopyList->getfactor(m_withMenu,false);
    TQString sFactor;
    sFactor.sprintf("%.2f",factor);
    backupDlg->setFactor(sFactor);
    argFactor =  factor;
    m_forcedFactor=false;
}

void k9DVDBackup::forceFactor(double _factor) {
    double factor=_factor;
    double minFactor=m_cellCopyList->getMinFactor( m_withMenu);
    if (factor<minFactor)
    	factor=minFactor;
    TQString sFactor;
    sFactor.sprintf("%.2f",factor);
    backupDlg->setFactor(sFactor);
    argFactor =  factor;
    m_forcedFactor=true;
    tqDebug("force factor : %f   min:%f",factor,minFactor);
}


void k9DVDBackup::execute() {
    TQString sOutput=output;

    output=TQDir::cleanDirPath(output +"/dvd");

    TQDir root("/");
    root.mkdir(output);
    k9Tools::clearOutput(output);

    TQDir dir(output);
    dir.mkdir("VIDEO_TS");
    dir.mkdir("AUDIO_TS");

    output=TQDir::cleanDirPath(output +"/VIDEO_TS");

    m_dvdread->openDevice(device);
    if (!m_dvdread->opened()) {
        seterror(tr2i18n("Unable to open DVD"));
        return;
    }

    k9CellCopyList *cellCopyList =new k9CellCopyList(m_dvdread,DVD);
    m_cellCopyList=cellCopyList;

    double totalSize=cellCopyList->gettotalSize();
    if (m_withMenu)
       totalSize+=DVD->getmenuSize() *2048 ;
    
    totalSize/=(1024*1024);
    totalSize = (totalSize >k9DVDSize::getMaxSize()) ? k9DVDSize::getMaxSize():totalSize;

    backupDlg->setTotalMax((uint32_t)totalSize);

    int lastCell;
    calcFactor();

    //VTSList is sorted by size, so it is easier to ajust the compression factor
    for(uint iTS=0;iTS<cellCopyList->VTSList.count() &&(!error);iTS++) {
        k9CellCopyVTS *VTS=cellCopyList->VTSList.at(iTS);
        //loop on each cell from the titleset
        lastCell=-1;
        for (uint iCell=0;(iCell<cellCopyList->count()) ;iCell++) {
            k9Cell *cell=(k9Cell*)cellCopyList->at(iCell);
            if (cell->vts==(int) VTS->getnum() && (!cell->copied)) {
                //		currCopyCell=cell;
                if (lastCell < (int)iCell) {
                    lastCell=getLastCell( cellCopyList,iCell);
                    //adjusting factor of compression
		    if ( cell->getforceFactor())
			forceFactor( cell->getFactor());
		    else
                    	calcFactor();
                }
                copyCell(cell->vts,cell,! cell->selected);
                if (!error) {
                    cell->copied=true;
                }
                //                }
                if (lastCell==(int)iCell) {
                    vamps->setNoData();
                    vamps->wait();
                }
                if (error)
                    break;
            }
        }
    	vamps->setNoData();
    	vamps->wait();
    }
    delete cellCopyList;

    if (!error) {
        updateIfo();
        updateVob(&currTS->cells);
        updateMainIfo();
    }
    output=sOutput;
    backupDlg->hide();

    if (error)
        KMessageBox::error(0,errMsg,"DVD Backup");
    //m_dvdread->close();
}
