[matroska] r959 - trunk/DvdMenuXtractor

[email protected]
Newsgroups gmane.comp.multimedia.matroska.cvs
Message-ID <[email protected]>
Author: robux4
Date: 2004-12-11 14:08:50 +0300 (Sat, 11 Dec 2004)
New Revision: 959

Modified:
   trunk/DvdMenuXtractor/DvdMenuXtractor.cpp
   trunk/DvdMenuXtractor/VobParser.cpp
   trunk/DvdMenuXtractor/VobParser.h
Log:
added support for extracting PCI packets containing buttons
preliminary support for general timecode files for each output

Modified: trunk/DvdMenuXtractor/DvdMenuXtractor.cpp
===================================================================
--- trunk/DvdMenuXtractor/DvdMenuXtractor.cpp	2004-11-30 15:45:16 UTC (rev 958)
+++ trunk/DvdMenuXtractor/DvdMenuXtractor.cpp	2004-12-11 11:08:50 UTC (rev 959)
@@ -5,7 +5,7 @@
 #endif
 
 #include "MyWizard.h"
-#include <time.h>
+#include <time.h>
 
 #ifdef __BORLANDC__
 #pragma hdrstop
@@ -21,10 +21,10 @@
 #include <inttypes.h>
 #include "IFOFile.h"
 #include "VOBParser.h"
-#include "MPEG2Decoder.h"
-#include "A52Decoder.h"
-#include "SUBDecoder.h"
-#include "base64.h"
+#include "MPEG2Decoder.h"
+#include "A52Decoder.h"
+#include "SUBDecoder.h"
+#include "base64.h"
 #include "iso/iso_lang.h"
 
 // TODO : settings
@@ -62,14 +62,15 @@
 // GLOBAL
 // ----------------------------------------------------------------------------
 
-IFOFile* IfoFile = NULL;
+IFOFile* IfoFile = NULL;
 wxLogWindow* wxLogWin;
 wxString m2vDirectory;
 wxString audioDirectory;
 wxString subDirectory;
+wxString btnDirectory;
 wxString bmpDirectory;
-//wxString wavDirectory;
-//wxString maskDirectory;
+//wxString wavDirectory;
+//wxString maskDirectory;
 const wxString APPLICATION_NAME = "DVDMenu-X-tractor";
 
 // ----------------------------------------------------------------------------
@@ -97,7 +98,7 @@
         this->m_pageTitle = "Data menu extraction";
         this->m_pageDesc = "Source files selection";
 
-		srand( (unsigned)time( NULL ) );
+		srand( (unsigned)time( NULL ) );
 
         wxFileConfig *pConfig = (wxFileConfig *)wxConfigBase::Get();
         wxFlexGridSizer *fgs = new wxFlexGridSizer(4,3,0,0);
@@ -105,10 +106,10 @@
 
         pConfig->SetPath("/Input");
         // Input directory
-        m_lbInDir = new wxStaticText(this,-1,"Input directory :",
+        m_lbInDir = new wxStaticText(this,-1,"Input directory :",
 			wxDefaultPosition,wxDefaultSize, DEFAULT_COMMON_STYLE);
-        m_edInDir = new wxTextCtrl(this,-1,
-			pConfig->Read("InDirectory",""), wxDefaultPosition,
+        m_edInDir = new wxTextCtrl(this,-1,
+			pConfig->Read("InDirectory",""), wxDefaultPosition,
 			wxDefaultSize, DEFAULT_COMMON_STYLE);
         m_bttBrowseInDir = new wxButton(this, wxID_P1_BROWSE_IN_DIR, "...",
             wxDefaultPosition, wxSize(24,24),DEFAULT_COMMON_STYLE);
@@ -117,10 +118,10 @@
         fgs->Add(m_bttBrowseInDir,0,wxALL,5);
 
         // Output directory
-        m_lbOutDir = new wxStaticText(this,-1,"Output directory :",
+        m_lbOutDir = new wxStaticText(this,-1,"Output directory :",
 			wxDefaultPosition,wxDefaultSize, DEFAULT_COMMON_STYLE);
-        m_edOutDir = new wxTextCtrl(this,-1,
-			pConfig->Read("OutputDirectory",""), wxDefaultPosition,
+        m_edOutDir = new wxTextCtrl(this,-1,
+			pConfig->Read("OutputDirectory",""), wxDefaultPosition,
 			wxDefaultSize, DEFAULT_COMMON_STYLE);
         m_bttBrowseOutDir = new wxButton(this, wxID_P1_BROWSE_OUT_DIR, "...",
             wxDefaultPosition, wxSize(24,24),DEFAULT_COMMON_STYLE);
@@ -141,14 +142,14 @@
     {
         SaveSettings();
     }
-
+
     void OnBrowseOutputDirectory(wxCommandEvent& WXUNUSED(event))
     {
-        wxString defaultDir = m_edOutDir->GetValue();
-		if (defaultDir.IsEmpty())
-		{
-			defaultDir = m_edInDir->GetValue();
-		}
+        wxString defaultDir = m_edOutDir->GetValue();
+		if (defaultDir.IsEmpty())
+		{
+			defaultDir = m_edInDir->GetValue();
+		}
         wxDirDialog dialog(this, "Select the output directory :", defaultDir,
             wxDD_NEW_DIR_BUTTON);
         if (dialog.ShowModal() == wxID_OK)
@@ -167,7 +168,7 @@
 
     void OnBrowseInputDirectory(wxCommandEvent& WXUNUSED(event))
     {
-        wxString defaultDir = m_edInDir->GetValue();
+        wxString defaultDir = m_edInDir->GetValue();
         wxDirDialog dialog(this, "Select the DVD input directory :", defaultDir,
             wxDD_NEW_DIR_BUTTON);
         if (dialog.ShowModal() == wxID_OK)
@@ -233,12 +234,12 @@
         if(IfoFile != NULL)
         {
             delete IfoFile;
-        }
+        }
 
         try
-        {
+        {
 			wxFileName _FileName(m_edInDir->GetValue());
-            IfoFile = new IFOFile(_FileName.GetPath(wxPATH_GET_VOLUME));
+            IfoFile = new IFOFile(_FileName.GetPath(wxPATH_GET_VOLUME));
         }
         catch(IFOException&)
         {
@@ -252,7 +253,7 @@
     {
         wxFileConfig *pConfig = (wxFileConfig *)wxConfigBase::Get();
         pConfig->SetPath("/Input");
-
+
         pConfig->Write("InDirectory",    m_edInDir->GetValue());	
         pConfig->Write("OutputDirectory",m_edOutDir->GetValue());	
     }
@@ -510,36 +511,36 @@
 		this->SetAutoLayout(TRUE);
 		this->SetSizer(fgs);
 		fgs->Fit(this);
-		fgs->SetSizeHints(this);
-		
+		fgs->SetSizeHints(this);
+		
 		m_OperationInProgress = FALSE;
-    }
-
-	~wxMyWizardPage4()
-	{}
-
-
-	VobParser * BuildVobMap(int16_t title, bool language)
-	{
-		wxFileConfig *pConfig = (wxFileConfig *)wxConfigBase::Get();
-
-		m_gaDemuxProgress->SetValue(0);
-
-		VobParser * _VobParser = NULL;
-		try {
+    }
+
+	~wxMyWizardPage4()
+	{}
+
+
+	VobParser * BuildVobMap(int16_t title, bool language)
+	{
+		wxFileConfig *pConfig = (wxFileConfig *)wxConfigBase::Get();
+
+		m_gaDemuxProgress->SetValue(0);
+
+		VobParser * _VobParser = NULL;
+		try {
 			_VobParser = new VobParser(pConfig->Read("InDirectory","").c_str(), title, language);
 		} catch (...)
 		{
 			m_txtDemuxLog->AppendText(wxString::Format("no VOB files found in %s for Title %02d\n",pConfig->Read("InDirectory","").c_str(),title));
 		}
-		return _VobParser;
+		return _VobParser;
 	}
 
-	wxString CellName(int16_t title, int16_t vobid, int16_t cellid, bool language) const
-	{
-		return wxString::Format("cell_%sT%02d_V%03d_C%02d", language?"L_":"", title, vobid, cellid);
-	}
-
+	wxString CellName(int16_t title, int16_t vobid, int16_t cellid, bool language) const
+	{
+		return wxString::Format("cell_%sT%02d_V%03d_C%02d", language?"L_":"", title, vobid, cellid);
+	}
+
     void SplitAndDemux(VobParser & aVobParser, int16_t title, bool language)
     {
         wxString newFilename;
@@ -549,8 +550,8 @@
 		if (!CellsList)
 			return;
 
-		aVobParser.Reset();
-		
+		aVobParser.Reset();
+		
         try
         {
             m_gaDemuxProgress->SetRange(CellsList->GetCount());
@@ -571,8 +572,8 @@
                     if(IfoFile->IsACandidateCell(aVobParser.GetVobID(),aVobParser.GetCellID()))
                     {
 						demuxer.Reset();
-                        newFilename = CellName(title, aVobParser.GetVobID(), aVobParser.GetCellID(), language);
-
+                        newFilename = CellName(title, aVobParser.GetVobID(), aVobParser.GetCellID(), language);
+
                         m_txtDemuxLog->AppendText("Processing " + newFilename + "\n");
 
 						Demuxer * _muxer = new VideoDemuxWriter(m2vDirectory+newFilename);
@@ -625,10 +626,10 @@
 								default:
 									myfprintf(stderr, "Unknown audio format %d", _attr->audio_format);
 									break;
-								}
-							}
-						}
-
+								}
+							}
+						}
+
 						if (_subTracks.GetCount())
 						{
 							for (_stream=0; _stream<_subTracks.GetCount();_stream++)
@@ -644,14 +645,15 @@
 										_langName = wxString::Format("_%d_un", _IDs.Item(_IDidx));
 									else
 										_langName = wxString::Format("_%d_%c%c", _IDs.Item(_IDidx), _attr->lang_code >> 8, _attr->lang_code & 0xFF);
-//									if(aVobParser.m_pci.btn_ns > 0)
-									{
-										_muxer = new SubDemuxWriter(subDirectory+newFilename+_langName, 0x20 + _IDs.Item(_IDidx), _width, _height, _palette, _attr->lang_code, _attr->lang_extension == 9);
-										if (!demuxer.AddDemuxer(0x20 + _IDs.Item(_IDidx), _muxer))
-											delete _muxer;
-									}
+									_muxer = new SubDemuxWriter(subDirectory+newFilename+_langName, 0x20 + _IDs.Item(_IDidx), _width, _height, _palette, _attr->lang_code, _attr->lang_extension == 9);
+									if (!demuxer.AddDemuxer(0x20 + _IDs.Item(_IDidx), _muxer))
+										delete _muxer;
 								}
 							}
+							// create a possible button demuxer too
+							_muxer = new BtnDemuxWriter(btnDirectory+newFilename);
+							if (!demuxer.AddDemuxer(0x00, _muxer))
+								delete _muxer;
 						}
                     }
                 }
@@ -687,7 +689,7 @@
 		{			
 			cle = node->GetData();
 			if(cle->isStill)
-			{				
+			{				
 				FilenameBase = CellName(title, cle->vobid, cle->cellid, language);
 				m_txtDemuxLog->AppendText("Converting " + FilenameBase + ".m2v to "+FilenameBase+".bmp ...\n");
 				BMPWriter->SetFilename(bmpDirectory+FilenameBase+".bmp");
@@ -703,146 +705,146 @@
 
 #if 0
     void ConvertAudioToWAV(int16_t title, bool language)
-    {	
-		wxString FilenameBase;
-		CellListElem* cle;
-		CellsListType::Node *node;
-		const CellsListType *CellsList = IfoFile->GetCellsList(title, language);
-		int i;			
-		wxFileConfig *pConfig = (wxFileConfig *)wxConfigBase::Get();
-
-		if (!CellsList)
-			return;
-
-		m_gaDemuxProgress->SetValue(0);
-		m_gaDemuxProgress->SetRange(CellsList->GetCount());
-				
-		WavfileA52DecWriter* WAVWriter = new WavfileA52DecWriter();
-		A52Decoder* a52decoder = new A52Decoder(WAVWriter);
-
-		for (i = 0, node = CellsList->GetFirst(); node != NULL; i++, node = node->GetNext() )
-		{
-			// TODO : check if audio exist
-			cle = node->GetData();
-			if(!cle->isStill)
-			{
-				FilenameBase = CellName(title, cle->vobid, cle->cellid, language);
-				m_txtDemuxLog->AppendText("Converting " + FilenameBase + ".ac3 to "+FilenameBase+".wav ...\n");
-				WAVWriter->SetFilename(wavDirectory+FilenameBase+".wav");
-				a52decoder->ProcessFile(audioDirectory+FilenameBase+".ac3");
-				wxYield();
-			}
-			m_gaDemuxProgress->SetValue(i);
-		}
-
-		delete WAVWriter;
+    {	
+		wxString FilenameBase;
+		CellListElem* cle;
+		CellsListType::Node *node;
+		const CellsListType *CellsList = IfoFile->GetCellsList(title, language);
+		int i;			
+		wxFileConfig *pConfig = (wxFileConfig *)wxConfigBase::Get();
+
+		if (!CellsList)
+			return;
+
+		m_gaDemuxProgress->SetValue(0);
+		m_gaDemuxProgress->SetRange(CellsList->GetCount());
+				
+		WavfileA52DecWriter* WAVWriter = new WavfileA52DecWriter();
+		A52Decoder* a52decoder = new A52Decoder(WAVWriter);
+
+		for (i = 0, node = CellsList->GetFirst(); node != NULL; i++, node = node->GetNext() )
+		{
+			// TODO : check if audio exist
+			cle = node->GetData();
+			if(!cle->isStill)
+			{
+				FilenameBase = CellName(title, cle->vobid, cle->cellid, language);
+				m_txtDemuxLog->AppendText("Converting " + FilenameBase + ".ac3 to "+FilenameBase+".wav ...\n");
+				WAVWriter->SetFilename(wavDirectory+FilenameBase+".wav");
+				a52decoder->ProcessFile(audioDirectory+FilenameBase+".ac3");
+				wxYield();
+			}
+			m_gaDemuxProgress->SetValue(i);
+		}
+
+		delete WAVWriter;
 		delete a52decoder;
     }
 
     void ConvertButtonMaskToBMP(int16_t title, bool language)
     {
-		wxString FilenameBase;
-		CellListElem* cle;
-		CellsListType::Node *node;
-		const CellsListType *CellsList = IfoFile->GetCellsList(title, language);
-		int i;			
-		wxFileConfig *pConfig = (wxFileConfig *)wxConfigBase::Get();
-
-		if (!CellsList)
-			return;
-		
-		m_gaDemuxProgress->SetValue(0);
-		m_gaDemuxProgress->SetRange(CellsList->GetCount());
-		
-		SUBDecOutput* BMPWriter = NULL;
-		SUBDecoder* subdecoder = new SUBDecoder(NULL);
-		
-		for (i = 0, node = CellsList->GetFirst(); node != NULL; i++, node = node->GetNext() )
-		{
-			cle = node->GetData();
-			// TODO : Select only cell with button
-			FilenameBase = CellName(title, cle->vobid, cle->cellid, language);
-			m_txtDemuxLog->AppendText("Converting " + FilenameBase + ".sub to "+FilenameBase+".bmp ...\n");
-			//WAVWriter->SetFilename(wavDirectory+FilenameBase+".wav");
-			
-			subdecoder->ProcessFile(subDirectory+FilenameBase+".sub", maskDirectory+FilenameBase);
-			wxYield();
-			m_gaDemuxProgress->SetValue(i);
-		}
-		
-		//delete BMPWriter;
+		wxString FilenameBase;
+		CellListElem* cle;
+		CellsListType::Node *node;
+		const CellsListType *CellsList = IfoFile->GetCellsList(title, language);
+		int i;			
+		wxFileConfig *pConfig = (wxFileConfig *)wxConfigBase::Get();
+
+		if (!CellsList)
+			return;
+		
+		m_gaDemuxProgress->SetValue(0);
+		m_gaDemuxProgress->SetRange(CellsList->GetCount());
+		
+		SUBDecOutput* BMPWriter = NULL;
+		SUBDecoder* subdecoder = new SUBDecoder(NULL);
+		
+		for (i = 0, node = CellsList->GetFirst(); node != NULL; i++, node = node->GetNext() )
+		{
+			cle = node->GetData();
+			// TODO : Select only cell with button
+			FilenameBase = CellName(title, cle->vobid, cle->cellid, language);
+			m_txtDemuxLog->AppendText("Converting " + FilenameBase + ".sub to "+FilenameBase+".bmp ...\n");
+			//WAVWriter->SetFilename(wavDirectory+FilenameBase+".wav");
+			
+			subdecoder->ProcessFile(subDirectory+FilenameBase+".sub", maskDirectory+FilenameBase);
+			wxYield();
+			m_gaDemuxProgress->SetValue(i);
+		}
+		
+		//delete BMPWriter;
 		delete subdecoder;
-    }
+    }
 #endif
-
-	static wxString IndentString(int lvl)
-	{
-		wxString result = "";
-
-		for(int i=0; i < lvl; i++)
-			result += "\t";
-
-		return result;
-	}
-
-	inline wxString CreateUID() const
-	{
-		return wxString::Format("%u", uint32_t(rand() | (rand() << 16)) );
-	}
-	
-	void AddCommands(int lvl, wxTextFile & XmlFile, const pgc_command_tbl_t * command_tbl)
-	{
-		if (command_tbl)
-		{
-			unsigned char *_buf = (unsigned char*) malloc(1+command_tbl->nr_of_pre * 8);
-			
-			// Pre commands
-			if (command_tbl->nr_of_pre)
-			{
+
+	static wxString IndentString(int lvl)
+	{
+		wxString result = "";
+
+		for(int i=0; i < lvl; i++)
+			result += "\t";
+
+		return result;
+	}
+
+	inline wxString CreateUID() const
+	{
+		return wxString::Format("%u", uint32_t(rand() | (rand() << 16)) );
+	}
+	
+	void AddCommands(int lvl, wxTextFile & XmlFile, const pgc_command_tbl_t * command_tbl)
+	{
+		if (command_tbl)
+		{
+			unsigned char *_buf = (unsigned char*) malloc(1+command_tbl->nr_of_pre * 8);
+			
+			// Pre commands
+			if (command_tbl->nr_of_pre)
+			{
 				XmlFile.AddLine(IndentString(lvl)+"<!-- Pre commands -->");
-				XmlFile.AddLine(IndentString(lvl)+"<ChapterProcess>");
-				lvl++;
-				XmlFile.AddLine(IndentString(lvl)+"<ChapterProcessTime>1</ChapterProcessTime>");
-				_buf[0] = command_tbl->nr_of_pre;
-				memcpy(&_buf[1], command_tbl->pre_cmds, command_tbl->nr_of_pre * 8);
-				XmlFile.AddLine(IndentString(lvl)+"<ChapterProcessCommand>"+encodebase64(_buf,1+command_tbl->nr_of_pre * 8)+"</ChapterProcessCommand>");
-				lvl--;
-				XmlFile.AddLine(IndentString(lvl)+"</ChapterProcess>");
-			}
-
-			// Cell commands
-			if (command_tbl->nr_of_cell)
-			{
+				XmlFile.AddLine(IndentString(lvl)+"<ChapterProcess>");
+				lvl++;
+				XmlFile.AddLine(IndentString(lvl)+"<ChapterProcessTime>1</ChapterProcessTime>");
+				_buf[0] = command_tbl->nr_of_pre;
+				memcpy(&_buf[1], command_tbl->pre_cmds, command_tbl->nr_of_pre * 8);
+				XmlFile.AddLine(IndentString(lvl)+"<ChapterProcessCommand>"+encodebase64(_buf,1+command_tbl->nr_of_pre * 8)+"</ChapterProcessCommand>");
+				lvl--;
+				XmlFile.AddLine(IndentString(lvl)+"</ChapterProcess>");
+			}
+
+			// Cell commands
+			if (command_tbl->nr_of_cell)
+			{
 				XmlFile.AddLine(IndentString(lvl)+"<!-- Cell commands -->");
-				XmlFile.AddLine(IndentString(lvl)+"<ChapterProcess>");
-				lvl++;
-				XmlFile.AddLine(IndentString(lvl)+"<ChapterProcessTime>0</ChapterProcessTime>");
-				_buf = (unsigned char*) realloc(_buf, 1+command_tbl->nr_of_cell * 8);
-				_buf[0] = command_tbl->nr_of_cell;
-				memcpy(&_buf[1], command_tbl->cell_cmds, command_tbl->nr_of_cell * 8);
-				XmlFile.AddLine(IndentString(lvl)+"<ChapterProcessCommand>"+encodebase64(_buf,1+command_tbl->nr_of_cell * 8)+"</ChapterProcessCommand>");
-				lvl--;
-				XmlFile.AddLine(IndentString(lvl)+"</ChapterProcess>");
-			}
-
-			// Post commands
-			if (command_tbl->nr_of_post)
-			{
+				XmlFile.AddLine(IndentString(lvl)+"<ChapterProcess>");
+				lvl++;
+				XmlFile.AddLine(IndentString(lvl)+"<ChapterProcessTime>0</ChapterProcessTime>");
+				_buf = (unsigned char*) realloc(_buf, 1+command_tbl->nr_of_cell * 8);
+				_buf[0] = command_tbl->nr_of_cell;
+				memcpy(&_buf[1], command_tbl->cell_cmds, command_tbl->nr_of_cell * 8);
+				XmlFile.AddLine(IndentString(lvl)+"<ChapterProcessCommand>"+encodebase64(_buf,1+command_tbl->nr_of_cell * 8)+"</ChapterProcessCommand>");
+				lvl--;
+				XmlFile.AddLine(IndentString(lvl)+"</ChapterProcess>");
+			}
+
+			// Post commands
+			if (command_tbl->nr_of_post)
+			{
 				XmlFile.AddLine(IndentString(lvl)+"<!-- Post commands -->");
-				XmlFile.AddLine(IndentString(lvl)+"<ChapterProcess>");
-				lvl++;
-				XmlFile.AddLine(IndentString(lvl)+"<ChapterProcessTime>2</ChapterProcessTime>");
-				_buf = (unsigned char*) realloc(_buf, 1+command_tbl->nr_of_post * 8);
-				_buf[0] = command_tbl->nr_of_post;
-				memcpy(&_buf[1], command_tbl->post_cmds, command_tbl->nr_of_post * 8);
-				XmlFile.AddLine(IndentString(lvl)+"<ChapterProcessCommand>"+encodebase64(_buf,1+command_tbl->nr_of_post * 8)+"</ChapterProcessCommand>");
-				lvl--;
-				XmlFile.AddLine(IndentString(lvl)+"</ChapterProcess>");
-			}
-			free(_buf);
-		}
-	}
-
+				XmlFile.AddLine(IndentString(lvl)+"<ChapterProcess>");
+				lvl++;
+				XmlFile.AddLine(IndentString(lvl)+"<ChapterProcessTime>2</ChapterProcessTime>");
+				_buf = (unsigned char*) realloc(_buf, 1+command_tbl->nr_of_post * 8);
+				_buf[0] = command_tbl->nr_of_post;
+				memcpy(&_buf[1], command_tbl->post_cmds, command_tbl->nr_of_post * 8);
+				XmlFile.AddLine(IndentString(lvl)+"<ChapterProcessCommand>"+encodebase64(_buf,1+command_tbl->nr_of_post * 8)+"</ChapterProcessCommand>");
+				lvl--;
+				XmlFile.AddLine(IndentString(lvl)+"</ChapterProcess>");
+			}
+			free(_buf);
+		}
+	}
+
 	static wxString GetPGCType(unsigned char entry_id)
 	{
 		wxString result;
@@ -901,14 +903,14 @@
 			next_program_entry_cell_num = pgc->program_map[program_number+1];
 		for (int cell_num = entry_cell_num; cell_num<next_program_entry_cell_num; cell_num++)
 		{
-			// Output cells
+			// Output cells
 			const cell_position_t & position = pgc->cell_position[cell_num-1];
 			const cell_playback_t & cell = pgc->cell_playback[cell_num-1];
-
-			if (cell.block_mode != 0 && cell.block_mode != 3)
-				continue;
-
-			if (cell.block_mode) // multi-angle
+
+			if (cell.block_mode != 0 && cell.block_mode != 3)
+				continue;
+
+			if (cell.block_mode) // multi-angle
 				XmlFile.AddLine(IndentString(lvl)+wxString::Format("<!-- Program %sCell CN#%d (%d angles) -->",(cell_num==entry_cell_num)?"Entry ":"", cell_num, cell_num - entry_cell_num + 1));
 			else
 				XmlFile.AddLine(IndentString(lvl)+wxString::Format("<!-- Program %sCell CN#%d -->",(cell_num==entry_cell_num)?"Entry ":"", cell_num));
@@ -923,8 +925,8 @@
 				_PrivateCN[4] = cell_num - entry_cell_num + 1; // Number of angles
 				XmlFile.AddLine(IndentString(lvl)+"<ChapterProcessedPrivate>"+encodebase64(_PrivateCN,5)+"</ChapterProcessedPrivate>");
 
-				//_TODO_: handle the vobID/cellID to determine the real timecode of the cells !
-				//          -> no, done when extracting the content streams, using the same timecode base
+				//_TODO_: handle the vobID/cellID to determine the real timecode of the cells !
+				//          -> no, done when extracting the content streams, using the same timecode base
 
 				end_time = st_time + DvdTimecodeToNano(cell.playback_time);
 				DisplayTime(lvl, XmlFile, st_time, end_time);
@@ -948,12 +950,12 @@
 
 		XmlFile.AddLine(IndentString(lvl)+wxString::Format("<!-- PGC PGC_%d type %s -->",pgc_num+1,GetPGCType(pgc_type).c_str()));
 		XmlFile.AddLine(IndentString(lvl)+"<ChapterAtom>");
-		lvl++;
+		lvl++;
 		wxString _myUID = CreateUID();
 		XmlFile.AddLine(IndentString(lvl)+"<ChapterUID>"+_myUID+"</ChapterUID>");
 		_PrivatePGC[1] = (pgc_num+1) >> 8;
 		_PrivatePGC[2] = (pgc_num+1) & 0xFF;
-		_PrivatePGC[3] = pgc_type;
+		_PrivatePGC[3] = pgc_type;
 		_PrivatePGC[4] = ((uint8_t*) &pgc->prohibited_ops)[0];
 		_PrivatePGC[5] = ((uint8_t*) &pgc->prohibited_ops)[1];
 		_PrivatePGC[6] = ((uint8_t*) &pgc->prohibited_ops)[2];
@@ -968,18 +970,18 @@
 		
 		// PGC commands
 		AddCommands(lvl, XmlFile, pgc->command_tbl);
-
+
 		// Handle the Programs/Cells in the PGC
 		for (int i=0; i< pgc->nr_of_programs; i++)
 		{
-			// handle the PTT/Chapter
-			if (ptts != NULL)
-			{
-				// find if the PGCN/PGN is in the list
-				for (int ptt_num=0; ptt_num<ptts->nr_of_ptts; ptt_num++)
-				{
-					if (ptts->ptt[ptt_num].pgcn == pgc_num+1 && ptts->ptt[ptt_num].pgn == i+1)
-					{
+			// handle the PTT/Chapter
+			if (ptts != NULL)
+			{
+				// find if the PGCN/PGN is in the list
+				for (int ptt_num=0; ptt_num<ptts->nr_of_ptts; ptt_num++)
+				{
+					if (ptts->ptt[ptt_num].pgcn == pgc_num+1 && ptts->ptt[ptt_num].pgn == i+1)
+					{
 						XmlFile.AddLine(IndentString(lvl)+wxString::Format("<!-- Chapter PTT_%d -->", ptt_num+1));
 						XmlFile.AddLine(IndentString(lvl)+"<ChapterAtom>");
 						lvl++;
@@ -994,15 +996,15 @@
 								XmlFile.AddLine(IndentString(lvl)+wxString::Format("<ChapterString>Your Name Here For Chapter #%d</ChapterString>", ptt_num+1));
 								lvl--;
 							XmlFile.AddLine(IndentString(lvl)+"</ChapterDisplay>");
-				
+				
 						// PTT start times (no end time, it's only a reference used to seek)
 						XmlFile.AddLine(IndentString(lvl)+"<ChapterTimeStart>"+TimeFormat(st_time)+"</ChapterTimeStart>");
-						lvl--;
-						XmlFile.AddLine(IndentString(lvl)+"</ChapterAtom>");
-						break;
-					}
+						lvl--;
+						XmlFile.AddLine(IndentString(lvl)+"</ChapterAtom>");
+						break;
+					}
 				}
-			}
+			}
 
 			XmlFile.AddLine(IndentString(lvl)+"<!-- Program PGN#"+wxString::Format("%d",i+1)+" in this PGC -->");
 			st_time = OutputProgram(lvl, XmlFile, pgc, i, st_time);
@@ -1044,9 +1046,9 @@
 		
 		return nano_duration;
 	}
-	
-	static wxString TimeFormat(uint64_t a_time)
-	{
+	
+	static wxString TimeFormat(uint64_t a_time)
+	{
 		int hour, min, sec;
 		uint32_t nano;
 		
@@ -1055,10 +1057,10 @@
 		sec = a_time % 60;
 		a_time /= 60;
 		min = a_time % 60;
-		hour = a_time / 60;
+		hour = a_time / 60;
 
 		return wxString::Format("%02d:%02d:%02d.%09d", hour, min, sec, nano);
-	}
+	}
 	
 	/// times in nanoseconds
 	static void DisplayTime(int lvl, wxTextFile & XmlFile, uint64_t start_time, uint64_t end_time)
@@ -1110,63 +1112,63 @@
 		return timecode_endB;
 	}
 
-	void GenerateXMLScript()
-	{
-		if (!IfoFile)
-		{
-			myfprintf(stderr, "No IFO file loaded !");
-			return;
-		}
-
-		static unsigned char _PrivateFP[]  = {0x30, 0x00, 0x00, 0x00};
+	void GenerateXMLScript()
+	{
+		if (!IfoFile)
+		{
+			myfprintf(stderr, "No IFO file loaded !");
+			return;
+		}
+
+		static unsigned char _PrivateFP[]  = {0x30, 0x00, 0x00, 0x00};
 		static unsigned char _PrivateVM[]  = {0x30, 0xC0, 0x00, 0x00};
 		static unsigned char _PrivateVTS[] = {0x30, 0x80, 0x00, 0x00};
 		static unsigned char _PrivateTT[]  = {0x28, 0x00, 0x00};
-
-		wxString line, editionUID = CreateUID();
-		wxFileConfig *pConfig = (wxFileConfig *)wxConfigBase::Get();
-        // Get sub directory
-        wxString Filename = pConfig->Read("OutputDirectory","") + "menuVMG.xml";
-		wxTextFile XmlFile(Filename);
-		XmlFile.Create();
-		XmlFile.AddLine("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>");
-		XmlFile.AddLine("<!DOCTYPE Chapters SYSTEM \"matroskachapters.dtd\">");
-		XmlFile.AddLine("");
-
-		int lvl = 0;
+
+		wxString line, editionUID = CreateUID();
+		wxFileConfig *pConfig = (wxFileConfig *)wxConfigBase::Get();
+        // Get sub directory
+        wxString Filename = pConfig->Read("OutputDirectory","") + "menuVMG.xml";
+		wxTextFile XmlFile(Filename);
+		XmlFile.Create();
+		XmlFile.AddLine("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>");
+		XmlFile.AddLine("<!DOCTYPE Chapters SYSTEM \"matroskachapters.dtd\">");
+		XmlFile.AddLine("");
+
+		int lvl = 0;
 		uint64_t timecode_startA = 0, timecode_endA = 0;
-
-		XmlFile.AddLine("<Chapters>");
-		lvl++;
-		XmlFile.AddLine(IndentString(lvl)+"<EditionEntry>");
-			lvl++;
-			XmlFile.AddLine(IndentString(lvl)+"<EditionUID>"+editionUID+"</EditionUID>");
-			XmlFile.AddLine(IndentString(lvl)+"<EditionProcessed>1</EditionProcessed>"); // DVD style menu
-
-			// Write the first play PGC
-			if (IfoFile->FirstPlayPGC())
-			{
-				XmlFile.AddLine(IndentString(lvl)+"<!-- First Play PGC -->");
-				XmlFile.AddLine(IndentString(lvl)+"<ChapterAtom>");
-					lvl++;
-					wxString _myUID = CreateUID();
-					XmlFile.AddLine(IndentString(lvl)+"<ChapterUID>"+_myUID+"</ChapterUID>");
-					// the First Play PGC has no cell and no time
-					DisplayTime(lvl, XmlFile, timecode_startA, timecode_endA);
-					// display the UID for the lazy rippers
-					XmlFile.AddLine(IndentString(lvl)+"<ChapterDisplay>");
-						lvl++;
-						XmlFile.AddLine(IndentString(lvl)+"<ChapterString>First Play PGC "+_myUID+"</ChapterString>");
-						lvl--;
-					XmlFile.AddLine(IndentString(lvl)+"</ChapterDisplay>");
-					
-					XmlFile.AddLine(IndentString(lvl)+"<ChapterProcessedPrivate>"+encodebase64(_PrivateFP,4)+"</ChapterProcessedPrivate>");
-					// PGC commands
-					AddCommands(lvl, XmlFile, IfoFile->FirstPlayPGC()->command_tbl);
-					lvl--;
-				XmlFile.AddLine(IndentString(lvl)+"</ChapterAtom>");
-			}
-			
+
+		XmlFile.AddLine("<Chapters>");
+		lvl++;
+		XmlFile.AddLine(IndentString(lvl)+"<EditionEntry>");
+			lvl++;
+			XmlFile.AddLine(IndentString(lvl)+"<EditionUID>"+editionUID+"</EditionUID>");
+			XmlFile.AddLine(IndentString(lvl)+"<EditionProcessed>1</EditionProcessed>"); // DVD style menu
+
+			// Write the first play PGC
+			if (IfoFile->FirstPlayPGC())
+			{
+				XmlFile.AddLine(IndentString(lvl)+"<!-- First Play PGC -->");
+				XmlFile.AddLine(IndentString(lvl)+"<ChapterAtom>");
+					lvl++;
+					wxString _myUID = CreateUID();
+					XmlFile.AddLine(IndentString(lvl)+"<ChapterUID>"+_myUID+"</ChapterUID>");
+					// the First Play PGC has no cell and no time
+					DisplayTime(lvl, XmlFile, timecode_startA, timecode_endA);
+					// display the UID for the lazy rippers
+					XmlFile.AddLine(IndentString(lvl)+"<ChapterDisplay>");
+						lvl++;
+						XmlFile.AddLine(IndentString(lvl)+"<ChapterString>First Play PGC "+_myUID+"</ChapterString>");
+						lvl--;
+					XmlFile.AddLine(IndentString(lvl)+"</ChapterDisplay>");
+					
+					XmlFile.AddLine(IndentString(lvl)+"<ChapterProcessedPrivate>"+encodebase64(_PrivateFP,4)+"</ChapterProcessedPrivate>");
+					// PGC commands
+					AddCommands(lvl, XmlFile, IfoFile->FirstPlayPGC()->command_tbl);
+					lvl--;
+				XmlFile.AddLine(IndentString(lvl)+"</ChapterAtom>");
+			}
+			
 			timecode_startA = timecode_endA;
 			
 			// Language Units
@@ -1185,31 +1187,31 @@
 					lvl--;
 				XmlFile.AddLine(IndentString(lvl)+"</ChapterAtom>");
 			}
-			lvl--;
-		XmlFile.AddLine(IndentString(lvl)+"</EditionEntry>");
-		lvl--;
-		XmlFile.AddLine("</Chapters>");
-		XmlFile.Write();
+			lvl--;
+		XmlFile.AddLine(IndentString(lvl)+"</EditionEntry>");
+		lvl--;
+		XmlFile.AddLine("</Chapters>");
+		XmlFile.Write();
 
         // Write the Video Title Set
         for (int title=1; title <= IfoFile->NumberOfTitles(); title++)
         {
 			timecode_startA = timecode_endA;
 			
-            wxString Filename = pConfig->Read("OutputDirectory","") + wxString::Format("menuVTS%02d.xml", title);
-            wxTextFile XmlFile(Filename);
-            XmlFile.Create();
-            XmlFile.AddLine("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>");
-            XmlFile.AddLine("<!DOCTYPE Chapters SYSTEM \"matroskachapters.dtd\">");
-            XmlFile.AddLine("");
-    
-            XmlFile.AddLine("<Chapters>");
-            lvl++;
-            XmlFile.AddLine(IndentString(lvl)+"<EditionEntry>");
-                lvl++;
-                XmlFile.AddLine(IndentString(lvl)+"<EditionUID>"+editionUID+"</EditionUID>");
-                XmlFile.AddLine(IndentString(lvl)+"<EditionProcessed>1</EditionProcessed>"); // DVD style menu
-    
+            wxString Filename = pConfig->Read("OutputDirectory","") + wxString::Format("menuVTS%02d.xml", title);
+            wxTextFile XmlFile(Filename);
+            XmlFile.Create();
+            XmlFile.AddLine("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>");
+            XmlFile.AddLine("<!DOCTYPE Chapters SYSTEM \"matroskachapters.dtd\">");
+            XmlFile.AddLine("");
+    
+            XmlFile.AddLine("<Chapters>");
+            lvl++;
+            XmlFile.AddLine(IndentString(lvl)+"<EditionEntry>");
+                lvl++;
+                XmlFile.AddLine(IndentString(lvl)+"<EditionUID>"+editionUID+"</EditionUID>");
+                XmlFile.AddLine(IndentString(lvl)+"<EditionProcessed>1</EditionProcessed>"); // DVD style menu
+    
 				XmlFile.AddLine(IndentString(lvl)+"<!-- Video Title Set -->");
 				XmlFile.AddLine(IndentString(lvl)+"<ChapterAtom>");
 					lvl++;
@@ -1225,7 +1227,7 @@
 						timecode_endA = HandleLanguageUnit(lvl, XmlFile, timecode_startA, *IfoFile, title);
 					}
 
-					// VTS content
+					// VTS content
 					if (IfoFile->VtsTitles(title) && IfoFile->VtsPGCs(title))
 					{
 						uint64_t timecode_startB = timecode_endA, timecode_endB = timecode_endA;
@@ -1233,7 +1235,7 @@
 						{
 							XmlFile.AddLine(IndentString(lvl)+wxString::Format("<!-- Title TTU_%d -->",i+1));
 							XmlFile.AddLine(IndentString(lvl)+"<ChapterAtom>");
-								lvl++;
+								lvl++;
 								wxString _myUIDa = CreateUID();
 								XmlFile.AddLine(IndentString(lvl)+"<ChapterUID>"+_myUIDa+"</ChapterUID>");
 
@@ -1241,7 +1243,7 @@
 								_PrivateTT[2] = (i+1) & 0xFF;
 								XmlFile.AddLine(IndentString(lvl)+"<ChapterProcessedPrivate>"+encodebase64(_PrivateTT,3)+"</ChapterProcessedPrivate>");
 							
-								// add the PGC that match this PTT
+								// add the PGC that match this PTT
 								uint64_t timecode_startC = timecode_startB, timecode_endC = timecode_startB;
 								for (int k=0; k<IfoFile->VtsPGCs(title)->nr_of_pgci_srp; k++)
 								{
@@ -1260,16 +1262,16 @@
 						}
 						timecode_endA = timecode_startB;
 					}
-					
+					
 					DisplayTime(lvl, XmlFile, timecode_startA, timecode_endA);
-					lvl--;
+					lvl--;
 				XmlFile.AddLine(IndentString(lvl)+"</ChapterAtom>");
-
-                lvl--;
-            XmlFile.AddLine(IndentString(lvl)+"</EditionEntry>");
-            lvl--;
-            XmlFile.AddLine("</Chapters>");
-            XmlFile.Write();
+
+                lvl--;
+            XmlFile.AddLine(IndentString(lvl)+"</EditionEntry>");
+            lvl--;
+            XmlFile.AddLine("</Chapters>");
+            XmlFile.Write();
         }
 	}
 
@@ -1286,29 +1288,32 @@
         // Create sub directory
         subDirectory = pConfig->Read("OutputDirectory","")+ "sub" + wxFILE_SEP_PATH;
         wxMkdir(subDirectory);
+        // Create sub directory
+        btnDirectory = pConfig->Read("OutputDirectory","")+ "buttons" + wxFILE_SEP_PATH;
+        wxMkdir(btnDirectory);
         // Create bmp directory
         bmpDirectory = pConfig->Read("OutputDirectory","")+ "bmp" + wxFILE_SEP_PATH;
-        wxMkdir(bmpDirectory);
-		// Create wav directory
-//        wavDirectory = pConfig->Read("OutputDirectory","")+ "wav" + wxFILE_SEP_PATH;
-//        wxMkdir(wavDirectory);
-		// Create mask directory
-//		maskDirectory = pConfig->Read("OutputDirectory","") + "mask" + wxFILE_SEP_PATH;
-//        wxMkdir(maskDirectory);
+        wxMkdir(bmpDirectory);
+		// Create wav directory
+//        wavDirectory = pConfig->Read("OutputDirectory","")+ "wav" + wxFILE_SEP_PATH;
+//        wxMkdir(wavDirectory);
+		// Create mask directory
+//		maskDirectory = pConfig->Read("OutputDirectory","") + "mask" + wxFILE_SEP_PATH;
+//        wxMkdir(maskDirectory);
 	}
 
    virtual void AfterShow(void)
-   {
-		const int NbsStep = 3;
-		wxString StepString, StepStringFormat = "Step %d/%d : %s...";
+   {
+		const int NbsStep = 3;
+		wxString StepString, StepStringFormat = "Step %d/%d : %s...";
 		
-		m_OperationInProgress = TRUE;
-
-		m_txtDemuxLog->AppendText("Creating output directories\n");
-
-		CreatDirectories();
+		m_OperationInProgress = TRUE;
 
-		bool language = true;
+		m_txtDemuxLog->AppendText("Creating output directories\n");
+
+		CreatDirectories();
+
+		bool language = true;
 		for (int16_t title = 0; title <= IfoFile->NumberOfTitles(); title++)
 		{
 			do {
@@ -1317,40 +1322,40 @@
 					language = true;
 					break;
 				}
-
-				m_txtDemuxLog->AppendText(wxString::Format("         Treating Title %d %sVOB file(s)\n", title, language?"LU ":""));
-
-				int Step = 1;
-
-				StepString = StepString.Format(StepStringFormat, Step++, NbsStep, "Building vob map");
-				m_lbStep->SetLabel(StepString);
-				m_txtDemuxLog->AppendText(StepString+"\n");
+
+				m_txtDemuxLog->AppendText(wxString::Format("         Treating Title %d %sVOB file(s)\n", title, language?"LU ":""));
+
+				int Step = 1;
+
+				StepString = StepString.Format(StepStringFormat, Step++, NbsStep, "Building vob map");
+				m_lbStep->SetLabel(StepString);
+				m_txtDemuxLog->AppendText(StepString+"\n");
 				m_lbStep->Update();
 				VobParser *aVobParser = BuildVobMap(title, language);
 
 				if (aVobParser != NULL)
-				{
-					StepString = StepString.Format(StepStringFormat, Step++, NbsStep, "Splitting and demuxing");
-					m_lbStep->SetLabel(StepString);
+				{
+					StepString = StepString.Format(StepStringFormat, Step++, NbsStep, "Splitting and demuxing");
+					m_lbStep->SetLabel(StepString);
 					m_txtDemuxLog->AppendText(StepString+"\n");		
 					m_lbStep->Update();
-					SplitAndDemux(*aVobParser, title, language);
-					
-					StepString = StepString.Format(StepStringFormat, Step++, NbsStep, "Converting still to bitmap");
-					m_lbStep->SetLabel(StepString);
-					m_txtDemuxLog->AppendText(StepString+"\n");		
+					SplitAndDemux(*aVobParser, title, language);
+					
+					StepString = StepString.Format(StepStringFormat, Step++, NbsStep, "Converting still to bitmap");
+					m_lbStep->SetLabel(StepString);
+					m_txtDemuxLog->AppendText(StepString+"\n");		
 					m_lbStep->Update();
-					ConvertStillToBMP(title, language);
-/*
-					StepString = StepString.Format(StepStringFormat, Step++, NbsStep, "Converting audio to wav");
-					m_lbStep->SetLabel(StepString);
-					m_txtDemuxLog->AppendText(StepString+"\n");		
+					ConvertStillToBMP(title, language);
+/*
+					StepString = StepString.Format(StepStringFormat, Step++, NbsStep, "Converting audio to wav");
+					m_lbStep->SetLabel(StepString);
+					m_txtDemuxLog->AppendText(StepString+"\n");		
 					m_lbStep->Update();
-					ConvertAudioToWAV(title, language);
-* /
-					StepString = StepString.Format(StepStringFormat, Step++, NbsStep, "Converting button mask to bitmap");
-					m_lbStep->SetLabel(StepString);
-					m_txtDemuxLog->AppendText(StepString+"\n");		
+					ConvertAudioToWAV(title, language);
+* /
+					StepString = StepString.Format(StepStringFormat, Step++, NbsStep, "Converting button mask to bitmap");
+					m_lbStep->SetLabel(StepString);
+					m_txtDemuxLog->AppendText(StepString+"\n");		
 					m_lbStep->Update();
 					ConvertButtonMaskToBMP(title, language);
 */
@@ -1359,9 +1364,9 @@
 				language = !language;
 			} while (!language);
 		}
-
-		GenerateXMLScript();
 
+		GenerateXMLScript();
+
 		m_OperationInProgress = FALSE;
 	}
 
@@ -1382,7 +1387,7 @@
     wxStaticText* m_lbStep;
     wxGauge* m_gaDemuxProgress;
     wxTextCtrl* m_txtDemuxLog;
-    bool m_OperationInProgress;
+    bool m_OperationInProgress;
 };
 
 // ----------------------------------------------------------------------------
@@ -1433,12 +1438,12 @@
 
 bool DvdMenuXtractorApp::OnInit()
 {
-    // Create config object
-	wxFileName configFilename;
-	configFilename.AssignCwd();
-	configFilename.SetFullName(GetAppName()+".cfg");
-    wxFileConfig *pConfig = new wxFileConfig("DvdMenuXtractor","ToffSoftware",
-		configFilename.GetFullPath(),"",wxCONFIG_USE_LOCAL_FILE);
+    // Create config object
+	wxFileName configFilename;
+	configFilename.AssignCwd();
+	configFilename.SetFullName(GetAppName()+".cfg");
+    wxFileConfig *pConfig = new wxFileConfig("DvdMenuXtractor","ToffSoftware",
+		configFilename.GetFullPath(),"",wxCONFIG_USE_LOCAL_FILE);
     wxConfigBase::Set(pConfig);
 
     wizard = new DvdMenuXtractorWizard();
@@ -1497,8 +1502,8 @@
     {
         delete IfoFile;
     }
-    wxFileConfig *pConfig = (wxFileConfig *)wxConfigBase::Get();
-	delete pConfig;
+    wxFileConfig *pConfig = (wxFileConfig *)wxConfigBase::Get();
+	delete pConfig;
 	pConfig = NULL;
 
     return 0;

Modified: trunk/DvdMenuXtractor/VobParser.cpp
===================================================================
--- trunk/DvdMenuXtractor/VobParser.cpp	2004-11-30 15:45:16 UTC (rev 958)
+++ trunk/DvdMenuXtractor/VobParser.cpp	2004-12-11 11:08:50 UTC (rev 959)
@@ -433,6 +433,8 @@
 		DebugLog("PCI {\n");
 		inc_lvl();
 		ParsePCI();
+		if (m_pci.btn_ns != 0) // there are some buttons
+			m_demuxer.ProcessStream(SUBSTREAM_PCI, &m_buff[0x2C], length, (int32_t)pktinfo.pts/90, wxString::Format("DTS %d PTS %d - %d %d\n", (int32_t)m_startdts/90, (int32_t)m_startpts/90, (int32_t)pktinfo.dts/90, (int32_t)pktinfo.pts/90));
 		dec_lvl();
 		DebugLog("}\n");
 		break;
@@ -659,7 +661,7 @@
 	ParsePESHeaderDataContentFlag();
 	ParsePESHeaderData();
 
-	m_demuxer.ProcessStream(0xE0, &m_buff[m_index],
+	m_demuxer.ProcessStream(VIDEO_STREAM, &m_buff[m_index],
 		length - 3 - pes_header_data_content.PES_header_data_len, (int32_t)pktinfo.pts/90, wxString::Format("DTS %d PTS %d - %d %d\n", (int32_t)m_startdts/90, (int32_t)m_startpts/90, (int32_t)pktinfo.dts/90, (int32_t)pktinfo.pts/90));
 }
 
@@ -870,5 +872,131 @@
 	fprintf(m_TimecodeFile, "timestamp: %s%02d:%02d:%02d:%03d, filepos: %09x\n", sign?"-":"", delay,minute,second,millisecond,filepos);
 }
 
+void BtnDemuxWriter::WriteTimecodeInfo(int32_t delay, uint64_t filepos, wxString & debug)
+{
+	if (!m_TimecodeFile)
+	{
+		wxString m_filename = m_Filename;
+		m_filename += "_btn.tmc";
+		m_TimecodeFile = fopen(m_filename.c_str(),"w");
+		fwrite("# timecode format v3\n", 1, 21, m_TimecodeFile);
+		fprintf(m_TimecodeFile, "assume %lf\n", 0.0);
+	}
+	bool sign = delay < 0;
+	delay = sign?-delay:delay;
+	int millisecond = delay % 1000;
+	delay /= 1000;
+	int second = delay % 60;
+	delay /= 60;
+	int minute = delay % 60;
+	delay /= 60;
+	fprintf(m_TimecodeFile, "timestamp: %s%02d:%02d:%02d:%03d, filepos: %09x\n", sign?"-":"", delay,minute,second,millisecond,filepos);
+}
+
+void DTSDemuxWriter::WriteTimecodeInfo(int32_t delay, uint64_t filepos, wxString & debug)
+{
+	if (!m_TimecodeFile)
+	{
+		wxString m_filename = m_Filename;
+		m_filename += "_dts.tmc";
+		m_TimecodeFile = fopen(m_filename.c_str(),"w");
+		fwrite("# timecode format v3\n", 1, 21, m_TimecodeFile);
+		fprintf(m_TimecodeFile, "assume %lf\n", 0.0);
+	}
+	bool sign = delay < 0;
+	delay = sign?-delay:delay;
+	int millisecond = delay % 1000;
+	delay /= 1000;
+	int second = delay % 60;
+	delay /= 60;
+	int minute = delay % 60;
+	delay /= 60;
+	fprintf(m_TimecodeFile, "timestamp: %s%02d:%02d:%02d:%03d, filepos: %09x\n", sign?"-":"", delay,minute,second,millisecond,filepos);
+}
+
+void LPCMDemuxWriter::WriteTimecodeInfo(int32_t delay, uint64_t filepos, wxString & debug)
+{
+	if (!m_TimecodeFile)
+	{
+		wxString m_filename = m_Filename;
+		m_filename += "_lpmc.tmc";
+		m_TimecodeFile = fopen(m_filename.c_str(),"w");
+		fwrite("# timecode format v3\n", 1, 21, m_TimecodeFile);
+		fprintf(m_TimecodeFile, "assume %lf\n", 0.0);
+	}
+	bool sign = delay < 0;
+	delay = sign?-delay:delay;
+	int millisecond = delay % 1000;
+	delay /= 1000;
+	int second = delay % 60;
+	delay /= 60;
+	int minute = delay % 60;
+	delay /= 60;
+	fprintf(m_TimecodeFile, "timestamp: %s%02d:%02d:%02d:%03d, filepos: %09x\n", sign?"-":"", delay,minute,second,millisecond,filepos);
+}
+
+void MPADemuxWriter::WriteTimecodeInfo(int32_t delay, uint64_t filepos, wxString & debug)
+{
+	if (!m_TimecodeFile)
+	{
+		wxString m_filename = m_Filename;
+		m_filename += "_mpa.tmc";
+		m_TimecodeFile = fopen(m_filename.c_str(),"w");
+		fwrite("# timecode format v3\n", 1, 21, m_TimecodeFile);
+		fprintf(m_TimecodeFile, "assume %lf\n", 0.0);
+	}
+	bool sign = delay < 0;
+	delay = sign?-delay:delay;
+	int millisecond = delay % 1000;
+	delay /= 1000;
+	int second = delay % 60;
+	delay /= 60;
+	int minute = delay % 60;
+	delay /= 60;
+	fprintf(m_TimecodeFile, "timestamp: %s%02d:%02d:%02d:%03d, filepos: %09x\n", sign?"-":"", delay,minute,second,millisecond,filepos);
+}
+
+void AC3DemuxWriter::WriteTimecodeInfo(int32_t delay, uint64_t filepos, wxString & debug)
+{
+	if (!m_TimecodeFile)
+	{
+		wxString m_filename = m_Filename;
+		m_filename += "_ac3.tmc";
+		m_TimecodeFile = fopen(m_filename.c_str(),"w");
+		fwrite("# timecode format v3\n", 1, 21, m_TimecodeFile);
+		fprintf(m_TimecodeFile, "assume %lf\n", 0.0);
+	}
+	bool sign = delay < 0;
+	delay = sign?-delay:delay;
+	int millisecond = delay % 1000;
+	delay /= 1000;
+	int second = delay % 60;
+	delay /= 60;
+	int minute = delay % 60;
+	delay /= 60;
+	fprintf(m_TimecodeFile, "timestamp: %s%02d:%02d:%02d:%03d, filepos: %09x\n", sign?"-":"", delay,minute,second,millisecond,filepos);
+}
+
+void VideoDemuxWriter::WriteTimecodeInfo(int32_t delay, uint64_t filepos, wxString & debug)
+{
+	if (!m_TimecodeFile)
+	{
+		wxString m_filename = m_Filename;
+		m_filename += "_vid.tmc";
+		m_TimecodeFile = fopen(m_filename.c_str(),"w");
+		fwrite("# timecode format v3\n", 1, 21, m_TimecodeFile);
+		fprintf(m_TimecodeFile, "assume %lf\n", 0.0);
+	}
+	bool sign = delay < 0;
+	delay = sign?-delay:delay;
+	int millisecond = delay % 1000;
+	delay /= 1000;
+	int second = delay % 60;
+	delay /= 60;
+	int minute = delay % 60;
+	delay /= 60;
+	fprintf(m_TimecodeFile, "timestamp: %s%02d:%02d:%02d:%03d, filepos: %09x\n", sign?"-":"", delay,minute,second,millisecond,filepos);
+}
+
 // ============================================================================
 

Modified: trunk/DvdMenuXtractor/VobParser.h
===================================================================
--- trunk/DvdMenuXtractor/VobParser.h	2004-11-30 15:45:16 UTC (rev 958)
+++ trunk/DvdMenuXtractor/VobParser.h	2004-12-11 11:08:50 UTC (rev 959)
@@ -224,6 +224,8 @@
 public:
 	VideoDemuxWriter(const char* filenamePrefix) : Writer(filenamePrefix,"m2v") {}
 	void ProcessStream(uint8_t* buff, uint32_t size, int32_t delay, wxString & debug) { Write(buff, size, delay, debug); }
+protected:
+	void WriteTimecodeInfo(int32_t delay, uint64_t filepos, wxString & debug);
 };
 
 // ----------------------------------------------------------------------------
@@ -239,6 +241,8 @@
 	}
 private:
 	uint8_t m_streamID;
+protected:
+	void WriteTimecodeInfo(int32_t delay, uint64_t filepos, wxString & debug);
 };
 
 // ----------------------------------------------------------------------------
@@ -254,6 +258,8 @@
 	}
 private:
 	uint8_t m_streamID;
+protected:
+	void WriteTimecodeInfo(int32_t delay, uint64_t filepos, wxString & debug);
 };
 
 // ----------------------------------------------------------------------------
@@ -269,6 +275,8 @@
 	}
 private:
 	uint8_t m_streamID;
+protected:
+	void WriteTimecodeInfo(int32_t delay, uint64_t filepos, wxString & debug);
 };
 
 // ----------------------------------------------------------------------------
@@ -284,6 +292,8 @@
 	}
 private:
 	uint8_t m_streamID;
+protected:
+	void WriteTimecodeInfo(int32_t delay, uint64_t filepos, wxString & debug);
 };
 
 // ----------------------------------------------------------------------------
@@ -315,6 +325,22 @@
 
 // ----------------------------------------------------------------------------
 
+class BtnDemuxWriter : public Demuxer, public Writer
+{
+public:
+	BtnDemuxWriter(const char* filenamePrefix)
+		:Writer(filenamePrefix, "btn")
+	{}
+	void ProcessStream(uint8_t* buff, uint32_t size, int32_t delay, wxString & debug)
+	{
+		Write(buff,size, delay, debug);
+	}
+protected:
+	void WriteTimecodeInfo(int32_t delay, uint64_t filepos, wxString & debug);
+};
+
+// ----------------------------------------------------------------------------
+
 class CompositeDemuxWriter
 {
 public:
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.