// mpv_read.cpp - MPEG2V12 getbits.c "_read()" are sent here!
//
// MPEG2AVI
// --------
//
// v0.16B34, multithreading, requires some change of settings to
//   the Visual C++ 6 project.  First, the default "libc.lib" must be
//   explicitly ignored.  (Setting in Linker, "Input" tab, add "libc.lib"
//   to the ignore list.
//   Must also manually add "libcmt.lib" to the linker list

//   As of v0.16B33, mpv_read()'s behavior has changed.  Please see
//   the revision long below.  The demux_queue is no longer used.
//
//   As of 0.16B24, more functions have been added, to support
//   the notion of "HIGH-LEVEL EMULATION" and "LOW-LEVEL EMULATION"
//
//   For now, the "high-level" emulation = mpeg2decoder I/O call emulation
//    (that is, all calls coming from the MPEG2Decoder are routed to the
//     high-level emulators.)
//   The "low-level" emulation sit between the operating-system calls
//    and the high-level emulation functions.  This allows the "filestream"
//    concept to work with or without the demuxer in place.
//
//  one "stream" or "streamlist" = "a list of files."
//  The streamlist is the list of files.  The stream is the prsentation of
//  the object, as a single file-handle (so the application sees the 
//  streamlist as a plain file.)
//
// v0.16B33 - 
//   MAJOR CHANGE ---- removed demux_queue from MPEG2AVI.  I've modified the
//   the getbits.c package to work with a variable buffer-size.  This 
//   new feature eliminates the need for demux_queue, whose only purpose
//   was to store the cDemuxer's variable length-output, and present the
//   the demuxed-data in fixed-size chunks (MPV_BSBUFSIZE)
//   Removing the demux_queue makes the program reduces MPEG2AVI's overhead.
//
// v0.16B3 - trap error conditions on g_demuxer.lba_seek_next_sysh()
//   get_stream_list() now checks if #files in streamlist.lst exceeds MAXFILES
// v0.16B24 - substantial modifications for multi-file streaming
//   currently, the multi-file implementation is limited to
//   "continuous stream" mode.  That means my_read() presents the
//   file-data in sequential order, one file after the next, just like
//   one big file.  The decoder/demuxer sees one continuous stream.
//   Hence, it is not possible to connect different bitstreams
//   together (yet.)
//
// v0.16B23 - s_read() renamed to mpv_read()
//   Made the UserOptVOBStartLBA option ('-@') more robust. Calls a
//   function which scans for an LBA with the supplied starting position.
//   If the starting-position isn't a valid SYSTEM_HEADER, the cDemuxer
//   continues searching for the next valid SYSTEM_HEADER.
//   
//   "mpv_read" = "MPEG Video decoder fread", to distinguish this from
//   the low-level fread emulator, my_read() 
//
//   Multi-file input, the C-library routine "_read()" is redirected
//    to an emulator-function in this file, my_read().
//    my_read() reads a list of bitstream-files (in seq order), and
//    only returns EOF at the end of the *LAST* bitstream-file.
//
//    From the perspective of the application, the bitstream-list
//    appears like one contiguous bitstream file.
//  
//   fixed bug with handler_nodemux() returning the wrong# of bytes
//    in buffer[] (used to always return l
// v0.16B2 - updated for the new cDemuxer object
//  Originally, BBDMUX was a global C-function demux_mpeg2().
//  Now, the demultiplexing-operation has been encapsulated into a C++
//  object called cDemuxer.  This facilitates better statistical reporting
//  and multi-file input.
//  It also allows cDemuxer to be used in other programs (like ac3dec_wave)
//  fixed stupid bug, forgot 'static' in front of BOOL first_iteration
//
//  relevant chagnes include ...
//    fill-handler calls the go-demux() function ...
//    go_demux() returns data in *buffer and #bytes in *pcbData
//    The fill-handler returns these parameters to the demux_buffer::refill_queue()
//    (go_demux() no longer calls push_data() )
//
//
// v0.13 - first release
// 
//  Performance of regular M2V reading (doesn't use BBDMUX) has
//  deteriorated as a result of using the demux_queue.
//
// The queue can be set to pull-data from either
//    (1) direct file I/O (default)
//  <or>
//    (2) BBDMUX demultiplexer ( Main_Bistream_Filename == "*.VOB" )
//
//  When functions in getbits.c require data, they call s_read(),
//   which extracts data from g_demuxbuffer (global object.)
//   g_demuxbuffer automatically refills itself by calling the
//   user-supplied fill-handler.  
//
//  The fill-handler is either (1) handler_demux or (2) handler_nodemux
//   (1) handler_demux (used for VOB reading) calls demux_mpeg2()
//	 (2) handler_nodemux calls _read(), like the original getbits.c
//

//
// LOW-LEVEL EMULATION FUNCTIONS
// -----------------------------
//  my_read(), my_open(), my_close(), my_lseeki64(), etc.
//
// And now with v0.16B24, the low-level emulation functions supports
// the concept of "streamlists" . A "streamlist" =  a list of files,
// treated as a single file-handle.  In other words, the low-level
// wrapper functions present the streamlist as a single continuous
// file (supporting _read(), for now... lseek() beyond first file isn't
// yet supported!)


// HIGH-LEVEL EMULATION FUNCTIONS
// -----------------------------
//  mpv_read(), mpv_open(), mpv_close(), mpv_lseek(), etc.
//

#define THR_STACK_SIZE 256

#include<windows.h>
#include<stdio.h>
#include<fcntl.h>
#include<stdlib.h>
//#include "demuxbuf.h"	// C++ object definition for class demux_buffer

#include "mpv_read.h"  // C++ object for cEventbox definition
#include "useropt.h"
#include "streaml.h"	// C++ object defintion for class cStreamlist
#include "VobReader.h"

#define TIMEOUT_MS	INFINITE // lots of waiting time
#define RETRY_COUNT 2		// retry twice

//SVE 03.06.2001
// Messages für StatusDlg
#define KWM_STARTALL     WM_USER+1
#define KWM_STARTFILE    WM_USER+2
#define KWM_UPDATESTATUS WM_USER+3

// for some reason, process.h doesn't have these definitions...
// you can comment these out if the definitions cause errors
extern "C" unsigned long _beginthread( void( __cdecl *start_address )( void * ), 
	unsigned stack_size, void *arglist );
extern "C" void _endthread( void );

const char *outputDirectory;

//SVE 03.06.2001 Runstate wird extern definiert
#include "runstate.h"
extern "C" runstate st_RunState;
/*
extern "C" struct runstate {
	short	fileCount;
	char	curFile[256];
//SVE 03.06.2001 Typen geändert auf DWORD
	DWORD	fileSize;
	DWORD	filePos;
//SVE 03.06.2001 double	fileSize;
//SVE 03.06.2001 double  filePos;
//SVE 03.06.2001 time_t  startTime;
	long    encFrames;
//SVE 03.06.2001 Typ geändert auf DWORD
	DWORD  kbWritten;
//SVE 03.06.2001 double  kbWritten;
	long    keyFrames;
//SVE 03.06.2001 double  FPS;
//SVE 03.06.2001 double  lowestFPS;
//SVE 03.06.2001 double  highestFPS;
//SVE 03.06.2001 char    RemainTime[9];
//SVE 03.06.2001 char    ElapsedTime[9];

//SVE 03.06.2001
	HWND	hWndStatusDlg;
	DWORD	fileSizes[10];
} st_RunState;
*/
// variables double-buffered read-ahead cache system
#define READ_AHEAD_SIZE (256*1024)
//#define READ_AHEAD_SIZE (1024*1024)

static DWORD g_dwTIDstr = -1; // threadID of the StreamListThreadHandler()
static unsigned char g_lpbuf_ra[ 2 ][ READ_AHEAD_SIZE ];
static DWORD g_dwFIndex; // buffer forward index (data insertion point)
static DWORD g_dwBIndex; // buffer back index (data extraction point)
static DWORD g_cbFValid; // counter, #valid bytes in forward buffer
static DWORD g_cbBValid; // counter, #valid bytes in back buffer
static DWORD g_dwBPos;	// buffer back position (byte position of extractor )

static double g_dwFPos;	// buffer back position (byte position of extractor )

// v0.16B34, ThreadHandler() is a function that manages the cStreamlist object.
// the functions in this file, my_open(), my_read(), my_close(), etc.,
// communicate with cStreamlist through the ReadAheadThread()
static void __cdecl ReadAheadThread( void *lpArgs ); // thread handler

class cEventBox g_oEBreader = cEventBox( 7 );
class cEventBox *g_poEBreader = &g_oEBreader;
static BOOL read_ahead_reset( void ); // perform read-ahead manually

// class demux_buffer g_demuxbuffer;
// == The demuxtiplexer buffer.  It used to sit between MPEG2V12's getbits.c
// and the demultiplexor object.  As of v0.16B33, it has been retired.
void vobReaderMessage(const char* msg, int p)
{
	if (p<3)
		printf("%s\n", msg);
}

class VobReader g_demuxer(vobReaderMessage);
// The demultiplexor object (BBDMUX).  It sits between demultiplxeor-buffer
// and the File I/O system.

// streamlist processor
class cStreamlist *g_poStreamlist = NULL;


// global variables from mpeg2dec.c 
extern "C" int g_vobflag;
extern "C" DWORD g_dwSystemPackLBA;
extern "C" tUserOption UserOptVOBStartLBA;
extern "C" tUserOption UserOptVOBStreamID;
extern "C" tUserOption UserOptNoAudioDecoding;


// high-level emulation for MPEG2DECODER (mpeg2dec.c, getbits.c)
// these functions eventually call the low-level emulation functions my_xxx()
extern "C" int mpv_read( int handle, void *buffer, unsigned int count );
extern "C" int mpv_open( const char *filename, int oflag, const char* outDir);
extern "C" int mpv_close( int handle );
//extern "C" long mpv_lseek( int handle, long offset, int origin );


// low-level emulation of _read(),_open(), _close(), _lseek(), etc.
// since some of MPEG2AVI's C-code depends on these functions,
// these low-level functions must be C-callable
extern "C" int my_read( int handle, void *buffer, unsigned int count );
extern "C" int my_open( const char *filename, int oflag );
extern "C" int my_close( int handle );
extern "C" __int64 my_lseeki64( int handle, __int64 offset, int origin );

// text manipulator compare (from store.c)
extern "C" int rstrcmpi( const char a[], const char b[] );



//////////////////////////////////////////////////////////////
// The "mpv_...()" functions are called by the MPEG2decoder 
//  They act as high-level emulation for the corresponding file I/O calls.
//
// The goal of the emulation is to allow behind the scenes processing
// of the file.  For example, mpv_read() can call BBDMUX to demultiplex 
// the input file (VOB), before returning to the MPEG2decoder.
//
// as of v0.16B33, mpv_read()'s behavior deivates from _read()'s behavior.
//  _read() is supposed to return the EXACT #bytes requested, but
// mpv_read() does not.  If the demuxer is in use, mpv_read() will return
// anywhere from 0.5*count to 1.0*count #bytes.  mpv_read() returns 0 bytes
// when it reaches EOF.
//
////////////////////////////////////////////////

// we must declare these functions as [extern "C"], because the calling
// object-code is located in *.c files (C-code, not C++ code!)
extern "C" int 
mpv_read( int handle, void *buffer, unsigned int count )
{
	long rc = 0; // return code
	int count1=0, count2=0;
	DWORD dwrc=0, retrycount=0; //dword return code
	BOOL brc=0;	// boolean return code


	// printf("\nmpv_read( bytes=%d ) :\n ", count );

	// g_cbBValid = # of valid bytes in back-buffer (g_lpbuf_ra[ g_dwBIndex ])

	// 1) we want to copy data from the read_ahead buffer g_lpbuf_ra[] to
	//    the application buffer ("*buffer")
	//    Operation requires 1 or 2 steps.
	//
	//    a) copy data from the back-buffer (current buffer)
	//   
	//    If we deplete the back-buffer but still need to copy more data,
	//    then we proceed to step b.
	//
	//    b) copy data from the forward-buffer.
	//       (Before we can copy, we have to make sure the PREVIOUS read_ahead()
	//       operation has finished.  we do that by waiting for READ_RDY event.)
	//
	//       Now, we request another 
		// buffer
		// count = count1 + count2
		// count1 = <bytes remaining in buffer> - <requested amount>
		// count2 = <requested amount> - count1

	count1 = min( ( g_cbBValid - g_dwBPos ), count );
	memcpy( buffer, &g_lpbuf_ra[ g_dwBIndex ][ g_dwBPos ], count1 );
	g_dwBPos += count1;

	// if <remaning data to be copied> != 0, AND we are not at end-of-file,
	// then we will request more data from read_ahead() thread
	//
	// otherwise, we skip the loop, and count2 = 0.
	//if ( (count1 < count) && (g_cbBValid == READ_AHEAD_SIZE) )

	if ( (count1 < count) )
	{
		// wait for PREVIOUS forward-buffer fill to return
		dwrc = WaitForSingleObject( g_poEBreader->m_events[ EVID_RDY_READ ],
			TIMEOUT_MS );
		while ( dwrc == WAIT_TIMEOUT && retrycount < RETRY_COUNT )
		{
			dwrc = WaitForSingleObject( g_poEBreader->m_events[ EVID_RDY_READ ],
				TIMEOUT_MS );
			++retrycount;
		}
		if ( dwrc == WAIT_TIMEOUT )
			fprintf( stderr, 
				"\nmpv_read() : waited for but never received  EVID_RDY_READ.");
		// previous read_ahead() is done, now reset the event we just detected
		brc = ResetEvent( g_poEBreader->m_events[ EVID_RDY_READ ] );

		// capture the new variables that read_ahead() just set
		g_dwBIndex = g_dwFIndex;
		g_cbBValid = g_cbFValid;
		// now advance the forward-buffer index, for next time
		g_dwFIndex = (g_dwFIndex==0); // toggle between 0, 1

		// request another forward buffer fill
		brc = SetEvent( g_poEBreader->m_events[ EVID_CMD_READ ] );
		if ( brc == 0 )
			fprintf( stderr, "\nmpv_read() : SetEvent( EVID_CMD_READ ) failed!" );

		// append forward buffer data to "*buffer"
		count2 = count - count1;
		// manually check to make sure count2 does not exceed the #valid bytes
		// in the forward-buffer
		count2 = min( count2, g_cbBValid );

		memcpy( ((char *)buffer) + count1, g_lpbuf_ra[ g_dwBIndex ], count2 );
		g_dwBPos = count2; // set to offset in the new buffer
	}

	
//SVE 20.05.2001	st_RunState.filePos = g_dwFPos;

	return (count1 + count2); // total # bytes copied to buffer[]

/*	if ( !g_vobflag )
	{
		rc = my_read( handle, buffer, count );
	}
	else
	{	// g_vobflag == TRUE

		// v0.16B34, go_demux() stuffs the buffer[] as full as possible
		// with a variable size payload : from "1" to "count" bytes. 
		// go_demux() returns 0 when it reaches end of file.
		rc = g_demuxer.go_demux( (unsigned char *)buffer, count );

		// get LBA of last good system header in VOB stream
	//	g_dwSystemPackLBA = g_demuxer.get_system_llba();

	} // end	if ( !g_vobflag )

	return rc; // return #bytes read (0 = end of file)
*/
}


extern "C" int 
mpv_open( const char *filename, int oflag, const char* outDir)
{
	outputDirectory = outDir;

	int fhandle = -1, rc =0;
	DWORD dwrc; // dword return code
	DWORD countRetry = 0;
	BOOL brc;	// boolean return code

	//printf ("\nmpv_open() : ");
	if ( g_vobflag )
	{
		// set MPEG-2 primary stream 0xE0, no substream
		//if ( CHECKOPT( UserOptVOBStreamID ) )
		//	g_demuxer.set_target_id( GETOPT( UserOptVOBStreamID ),
		//		STREAM_NOT_SET );
		//else
		//	g_demuxer.set_target_id( 0xE0, STREAM_NOT_SET );  // default
		// seting the filename automatically opens the bitstream
		VobReader::initDecode();

//SVE 03.06.2001
		g_demuxer.m_pRunState=&st_RunState;

		g_demuxer.IgnoreAudio = GETOPT(UserOptNoAudioDecoding);

		g_demuxer.SetReadBuffer( 65536 * 2 );

		if (g_demuxer.addFile( (char *) filename ))
			fhandle = 1;
		else
			fhandle = -1;

//SVE 03.06.2001	st_RunState.fileSize = g_demuxer.m_FileSize;
		//fhandle = g_demuxer.get_fhandle(); // get file handle

		// v0.16B2 start @ LBA 
		if ( CHECKOPT( UserOptVOBStartLBA ) )
		{
			int nbLbaToPass = GETOPT(UserOptVOBStartLBA);
			for (int i=0; i < nbLbaToPass; ++i)
			{
				VobReader::FrameType ft;
				long stream;
				unsigned long size;
				g_demuxer.getInterpretedFrame(ft, stream, size);
			}
			//dwrc = g_demuxer.lba_seek_next_sysh( GETOPT( UserOptVOBStartLBA ) );
			//if ( dwrc != DMXERR_OK )
			//{
			//	fprintf( stderr, "\nmpv_open() : could not seek start LBA %d ",
			//		GETOPT( UserOptVOBStartLBA ) );
			//	return -1;	// no file handle
			//}
		}

	} // end if ( g_vobflag )
	else
	{	// no VOB demuxing
		fhandle = my_open( filename, oflag );
//SVE 20.05.2001
		st_RunState.fileCount=1;
		st_RunState.fileSizes[0]=g_poStreamlist->qwStreamLength();
		st_RunState.fileSize=st_RunState.fileSizes[0];
	}

	// if we make it this far, we're ready to spawn the StreamListThreadHander
	if ( g_dwTIDstr == -1 )
	{
		g_dwTIDstr = _beginthread( ReadAheadThread, THR_STACK_SIZE, 
			(void *) g_poEBreader );
		if ( g_dwTIDstr == -1 ) // error
		{
			fprintf( stderr, "\nmpv_open() : thread creation failed!" );
			return -1;
		}
//SVE 20.05.2001		else
//SVE 20.05.2001		{
//SVE 20.05.2001			fprintf( stderr, "\nmpv_open() : thread creation succeeded!" );
//			SetThreadPriority(ReadAheadThread, THREAD_PRIORITY_IDLE);
//SVE 20.05.2001		}

		// if we make it here, the thread has been successfully created
		// now MPEG2AVI should wait for the thread to initialize itself
		do
		{
			dwrc = WaitForSingleObject(  
				g_poEBreader->m_events[ EVID_INIT_OK ], TIMEOUT_MS );
			++countRetry;
		} 
		while ( dwrc == WAIT_TIMEOUT && countRetry < RETRY_COUNT );
		// make sure the loop succeeded
		if ( dwrc != WAIT_OBJECT_0 ) 
		{	// nope, we never received event EVID_INIT_OK
			fprintf( stderr, "\nmpv_open() : ...waited for but did not receive EVID_INIT_OK.");
//			brc = PostThreadMessage(  g_dwTIDstr, WM_QUIT, 0, 0 );
			return -1;
		}
		ResetEvent( g_poEBreader->m_events[ EVID_INIT_OK ] );

	} // end if ( g_dwTIDstr == -1 )
	else
		printf( "\nmpv_open() : thread already exists." );

	if ( fhandle != -1 ) // was the file was successfully opened?
	{	// yes, initialize the read_ahead system
		brc = read_ahead_reset();
	}

	return fhandle; // return file handle, -1 == error
}


extern "C" int 
mpv_close( int handle )
{
	DWORD dwrc; // DWORD return code
	DWORD countRetry = 0;

	// issue the EXIT command -> thread
	//brc = PostThreadMessage(  g_dwTIDstr, WM_QUIT, 0, 0 );
	dwrc = SetEvent( g_poEBreader->m_events[ EVID_CMD_EXIT ] );
	do
	{
		dwrc = WaitForSingleObject( g_poEBreader->m_events[ EVID_EXIT_OK ],
			TIMEOUT_MS );
		++countRetry;
	}
	while ( dwrc == WAIT_TIMEOUT && countRetry < RETRY_COUNT );

	// make sure the loop succeeded
	if ( dwrc != WAIT_OBJECT_0 )
		fprintf( stderr, "\nmy_close() : ...waited for but did not receive EVID_EXIT_OK.");

//	dwrc=ResetEvent( g_poEBreader->m_events[ EVID_EXIT_OK ] );
	g_dwTIDstr = -1;	// reset threadID to "not in use"

	g_poEBreader->ResetAll();	// reset all events in eventbox

	return my_close( handle );
}


/*
extern "C" long 
mpv_lseek( int handle, long offset, int origin )
{
	//return _lseek( l_handle, offset, origin );
	return my_lseeki64( handle, (__int64) offset, origin );
}*/

////////////////////////////////////////////////////////////////////////////
//
// low-level I/O functions
//
//
////////////////////////////////////////////////////////////////////////////
//
// v0.16B34, multithreaded...a read-ahead thread fills the forward buffer
//
////////////////////////////////////////////////////////////////////////////


extern "C" __int64 
my_lseeki64( int handle, __int64 offset, int origin )
{
	// wait for PREVIOUS operation to complete
	__int64 lirc; // return code
	BOOL brc;	// boolean return code
	DWORD retryCount = 0;
	DWORD dwrc;
/*
	// 1) wait for previous thread operation to complete.  
	//   otherwise the thread might be using the stramlist at the
	//   same time
	dwrc = WaitForSingleObject( g_poEBreader->m_events[ EVID_RDY_CMD ],
		TIMEOUT_MS );
	while ( dwrc == WAIT_TIMEOUT && retryCount < RETRY_COUNT )
	{
		dwrc = WaitForSingleObject( g_poEBreader->m_events[ EVID_RDY_CMD],
			TIMEOUT_MS );
		++retryCount;
	}
	ResetEvent( g_poEBreader->m_events[ EVID_RDY_CMD ] );
*/	
	lirc = g_poStreamlist->my_lseeki64( handle, offset, origin );

//	brc = read_ahead_reset();

	return lirc;
}

extern "C" int
my_read( int handle, void *buffer, unsigned int count )
{
//PX3	DWORD retrycount = 0;
//PX3	DWORD count1=0, count2=0; // temp vars for splitting count into two parts
//PX3	DWORD dwrc;
//PX3	BOOL brc;
	int retval;
/*
	// g_cbBValid = # of valid bytes in back-buffer (g_lpbuf_ra[ g_dwBIndex ])

	// 1) we want to copy data from the read_ahead buffer g_lpbuf_ra[] to
	//    the application buffer ("*buffer")
	//    Operation requires 1 or 2 steps.
	//
	//    a) copy data from the back-buffer (current buffer)
	//   
	//    If we deplete the back-buffer but still need to copy more data,
	//    then we proceed to step b.
	//
	//    b) copy data from the forward-buffer.
	//       (Before we can copy, we have to make sure the PREVIOUS read_ahead()
	//       operation has finished.  we do that by waiting for READ_RDY event.)
	//
	//       Now, we request another 
		// buffer
		// count = count1 + count2
		// count1 = <bytes remaining in buffer> - <requested amount>
		// count2 = <requested amount> - count1

	count1 = min( ( g_cbBValid - g_dwBPos ), count );
	memcpy( buffer, &g_lpbuf_ra[ g_dwBIndex ][ g_dwBPos ], count1 );
	g_dwBPos += count1;

	// if <remaning data to be copied> != 0, AND we are not at end-of-file,
	// then we will request more data from read_ahead() thread
	//
	// otherwise, we skip the loop, and count2 = 0.
	if ( (count1 < count) && (g_cbBValid == READ_AHEAD_SIZE) )
	{
		// wait for PREVIOUS forward-buffer fill to return
		dwrc = WaitForSingleObject( g_poEBreader->m_events[ EVID_RDY_READ ],
			TIMEOUT_MS );
		while ( dwrc == WAIT_TIMEOUT && retrycount < RETRY_COUNT )
		{
			dwrc = WaitForSingleObject( g_poEBreader->m_events[ EVID_RDY_READ ],
				TIMEOUT_MS );
			++retrycount;
		}
		if ( dwrc == WAIT_TIMEOUT )
			fprintf( stderr, 
				"\nmy_read() : waited for but never received  EVID_RDY_READ.");

		// previous read_ahead() is done, now reset the event we just detected
		brc = ResetEvent( g_poEBreader->m_events[ EVID_RDY_READ ] );

		// capture the new variables that read_ahead() just set
		g_dwBIndex = g_dwFIndex;
		g_cbBValid = g_cbFValid;
		// now advance the forward-buffer index, for next time
		g_dwFIndex = (g_dwFIndex==0); // toggle between 0, 1

		// request another forward buffer fill
		brc = SetEvent( g_poEBreader->m_events[ EVID_CMD_READ ] );
		if ( brc == 0 )
			fprintf( stderr, "\nmy_read() : SetEvent( EVID_CMD_READ ) failed!" );

		// append forward buffer data to "*buffer"
		count2 = count - count1;
		// manually check to make sure count2 does not exceed the #valid bytes
		// in the forward-buffer
		count2 = min( count2, g_cbBValid );

		memcpy( ((char *)buffer) + count1, g_lpbuf_ra[ g_dwBIndex ], count2 );
		g_dwBPos = count2; // set to offset in the new buffer
	}

	return (count1 + count2); // total # bytes copied to buffer[]
*/

	retval = g_poStreamlist->my_read( handle, buffer, count );
	g_dwFPos = g_poStreamlist->my_telli64( handle );
//SVE 20.05.2001
	st_RunState.filePos = g_poStreamlist->my_telli64( handle );
	return retval;


}

extern "C" int 
my_open( const char *filename, int oflag )
{
//	printf("\nmy_open( %s ) : ", filename );
	int rc;
	BOOL brc;	// boolean return code
	DWORD dwrc;	// dword return code
	DWORD countRetry = 0; // # of times retried
	if ( g_poStreamlist != NULL )
	{
		fprintf( stderr, "\nmpv_open() : g_poStreamlist already exists!" );
		return -1;
	}
	else
		g_poStreamlist = new (cStreamlist);

	if ( g_poStreamlist == NULL )
	{
		fprintf( stderr, "\nmpv_open() : couldn't create cStreamlist object!" );
		return -1; // error code
	}

/*
	// if we make it this far, we're ready to spawn the StreamListThreadHander
	if ( g_dwTIDstr == -1 )
	{
		g_dwTIDstr = _beginthread( ReadAheadThread, THR_STACK_SIZE, 
			(void *) g_poEBreader );
		if ( g_dwTIDstr == -1 ) // error
		{
			fprintf( stderr, "\nmy_open() : thread creation failed!" );
			delete g_poStreamlist;
			g_poStreamlist = NULL;
			return -1;
		}
		else
			fprintf( stderr, "\nmy_open() : thread creation succeeded!" );

		// if we make it here, the thread has been successfully created
		// now MPEG2AVI should wait for the thread to initialize itself
		do
		{
			dwrc = WaitForSingleObject(  
				g_poEBreader->m_events[ EVID_INIT_OK ], TIMEOUT_MS );
			++countRetry;
		} 
		while ( dwrc == WAIT_TIMEOUT && countRetry < RETRY_COUNT );
		// make sure the loop succeeded
		if ( dwrc != WAIT_OBJECT_0 ) 
		{	// nope, we never received event EVID_INIT_OK
			fprintf( stderr, "\nmy_open() : ...waited for but did not receive EVID_INIT_OK.");
//			brc = PostThreadMessage(  g_dwTIDstr, WM_QUIT, 0, 0 );
			delete g_poStreamlist;
			g_poStreamlist = NULL;
			return -1;
		}
		ResetEvent( g_poEBreader->m_events[ EVID_INIT_OK ] );

	} // end if ( g_dwTIDstr == -1 )
	else
		printf( "\nmy_open() : thread already exists." );
*/
	rc = g_poStreamlist->my_open( filename, oflag );

/*
	if ( rc != -1 ) // was the file was successfully opened?
	{	// yes, initialize the read_ahead system
		brc = read_ahead_reset();
	}
*/
	return rc;	// return the code from my_open()

}


extern "C" int 
my_close( int handle )
{
//	printf("\nmy_close( %d ) : ", handle );
	//return _close( l_handle );
	int i;
	DWORD dwrc; // dword return code
	DWORD countRetry = 0;

	if ( g_poStreamlist == NULL )
	{
		fprintf( stderr, "\nmy_close() : g_poStreamlist doesn't exist!" );
		return -1;
	}
/*
	// issue the EXIT command -> thread
	//brc = PostThreadMessage(  g_dwTIDstr, WM_QUIT, 0, 0 );
	dwrc = SetEvent( g_poEBreader->m_events[ EVID_CMD_EXIT ] );
	do
	{
		dwrc = WaitForSingleObject( g_poEBreader->m_events[ EVID_EXIT_OK ],
			TIMEOUT_MS );
		++countRetry;
	}
	while ( dwrc == WAIT_TIMEOUT && countRetry < RETRY_COUNT );

	// make sure the loop succeeded
	if ( dwrc != WAIT_OBJECT_0 )
		fprintf( stderr, "\nmy_close() : ...waited for but did not receive EVID_EXIT_OK.");

	dwrc=ResetEvent( g_poEBreader->m_events[ EVID_EXIT_OK ] );
	g_dwTIDstr = -1;	// reset threadID to "not in use"
*/
	i = g_poStreamlist->my_close( handle );
	delete g_poStreamlist;
	g_poStreamlist = NULL;

//	g_poEBreader->ResetAll();	// reset all events in eventbox

	return i;
}


////////////////////////////////////////////////////////////////
//
//  read_ahead system  (v0.16B34)
//
////////////////////////////////////////////////////////////////
//
//  The read_ahead system is implemented with the following functions
//
//  ReadAheadThread() - my_read() spawns this function as a thread.  It remains
//   active until my_close() is called.  During this time, the
//   ReadAheadThread fulfills read_ahead_requests (EVID_CMD_READ)
//   
//  read_ahead_reset() - my_open() and my_lseeki64() call this
//   function to "prepare" the read_ahead system.  The preparation
//   puts the read_ahead buffer g_lpbuf_ra[][] in a known state.
//
//  Theory of operation
//
//  MPEG2AVI (main thread)    |               (2nd thread )
//                            |                     
//                            |                     
//   my_open()            cEventBox         ReadAheadThread()
//   my_read()    <-->     object    <-->               
//   my_lseeki64()      (g_oEBreader) 
//   my_close()               |                  
//                            |                    
//  
//
//   The eventbox contains variables called "events."  The events are used
//   as "synchronization objects."  When one thread wishes to communicate
//   with a different thread, it sets a predefined EVENT inside the event
//   box.  The set-event triggers the other thread, which has been listening.
//
//   When the called thread is done processing, it sets an "acknowledgement"
//   or ready "event", to let the caller know processing is complete.
//
//   Overall this is a very, very primitive method of synchronizing two
//   threads (since the application has the burden of enforcing the
//   request/acknowledge protocol), but it is good enough for the simple 
//   read_ahead buffer.
//
//   The read-ahead mechanism
//
//   Rather than implementing a full-blown circular buffer, which would 
//   require more sophisticated thread-synchronization, MPEG2AVI uses a
//   basic "double-buffer."  One buffer is used for application reading,
//   while the 2nd buffer is refilled.  When application depletes the 
//   first buffer, the ReadAhead system exchanges the two buffers.  Now
//   the application reads from the 2nd buffer (which has just been
//   filled), and the ReadAhead function starts refilling the empty 
//   buffer.
//
//      MPEG2AVI (consumer)    g_lpbuf_ra[]       read-ahead (producer)
//                            +-----------+
//          my_read() <------ | Buffer #0 |
//                            +-----------+
//                            | Buffer #1 |  <---------  ThreadReadAhead()
//                            +-----------+
//    
//    The synchronization between MPEG2AVI & ThreadReadAhead() is carried
//    out entirely within my_read() and ThreadReadAhead().
//    The request/acknowledge protocol ensures that the functions do not
//    overwrite each other's variables.  
//
//    When MPEG2AVI opens a new file, the read_ahead buffer must be manually
//    loaded to enforce synchronization.  Both my_open() and my_lseeki64()
//    call read_ahead_reset(), to flush the read_ahead buffer and reinitialize
//    it.
//
//    In this arrangement, cStreamlist is called by both ThreadReadAhead()
//    and my_read()/my_open()/my_close().  In other words, cStreamlist is
//    used concurrently by two separate threads.  The request/ack protocol
//    discussed earlier effectively serializes thread access to cStreamlist,
//    preventing both threads from calling cStreamlist at the exact same time.
//
//    Example of operation
//
//     1) my_open()
//
//        a) calls cStreamlist->my_open() [ opens disk file ]
//           spawns thread "ReadAheadThread()"
//           wait for ReadAheadThread() to return "EVID_INIT_OK"
//
//        b) calls read_ahead_reset(), which does the following:
//           1) loads buffer#0, waits for operation to complete
//           2) requests a refill on buffer#1, returns immediately
//
//        c) returns to caller
//
//     2) my_read()
//
//        a) reads data from buffer#0 (no delay)
//         ....
//        b) buffer#0 is depleted.
//        c) waits for event EVID_RDY_READ (signal that buffer#1 is ready)
//         ....
//        d) Swaps buffer#0 and buffer#1
//        e) submits a refill request for buffer#0 (the empty buffer)
//        f) reads data from buffer#1 ( no delay)
//         ....
//        g) buffer#1 is depleted.
//        h) waits for event EVID_RDY_READ (signal that buffer#0 is ready)
//         ....
//        etc.
//      
//
//     3) my_close()
//        a) waits for event EVID_RDY_CMD (signal that thread is idle)
//        b) closes the file [ cStreamlist->my_close() ]
//        c) sends shutdown event to thread ( EVID_CMD_EXIT )
//        d) waits for acknowledgement EVID_EXIT_OK
//        e) returns to caller
//
////////////////////////////////////////////////////////////////v



// the read ahead thread.
// this thread is a simple 1-buffer read-ahead mechanism for MPEG2AVI's
// file I/O system.  The handler directly calls cStreamlist's my_read() 
//  Hence, the read-ahead mechanism sits between MPEG2AVI's low-level I/O
// file routines and the actual implementation (cStreamlist)
// 
// Required parameters : lpArgs = pointer to the EventBox object
//
//  The eventbox object contains various user-defined Windows events.
//  These allow MPEG2AVI and the read-thread to synchronize properly

void  __cdecl
ReadAheadThread( void *lpArgs )
{
	DWORD dwrc;	// dword return code
	MSG m_msg; // local message
	BOOL brc;	// boolean return code
	BOOL fQuit = FALSE; // central loop control flag
	DWORD countRetry = 0;
	BOOL fMode_RealTime = FALSE;

	// fRealTime : FALSE - thread will wait indefinitely for commands
	// fRealTime : TRUE  - thread will "time out" after xx seconds

	class cEventBox *m_poEB = (cEventBox *)lpArgs;; // pointer to event box

	if ( lpArgs == NULL )
	{
		printf("\n::ThreadHandler() : lpArgs == NULL! Aborting" );
		// notify MPEG2AVI that the thread has shutdown properly.
		brc = SetEvent( m_poEB->m_events[ EVID_EXIT_OK ] ); 
		if ( brc == 0 )
			printf("\n::ReadAheadThread() : SetEvent( EVID_EXIT_OK ) failed...");
		_endthread();
	}

	// 1) call PeekMessage() to force Windows to set up a messagehandler for
	//    this thread.  At this point in time, we do not expect any messages,
	//    so we'll discard any we receive
	// peek for all messages, for current thread only.
	// must use PM_NOREMOVE, otherwise
	brc = PeekMessage( &m_msg, NULL, 0, 0, PM_NOREMOVE );

	brc = SetEvent( m_poEB->m_events[ EVID_INIT_OK ] );
	if ( brc == 0 )
		printf("\n::ReadAheadThread() : SetEvent( EVID_INIT_OK ) failed...");
//	else
//		printf("... init sequence complete.");


	// for some reason, the MessageHandler doesn't work....

	do
	{
		// set EVID_RDY_CMD, to signal that thread-handler is ready
		// to accept next command
		SetEvent( m_poEB->m_events[ EVID_RDY_CMD ] );
		if ( fMode_RealTime )
		{	// wait for specified time-interval before time-out
			dwrc = WaitForMultipleObjects( 2, m_poEB->m_events, FALSE,
				(TIMEOUT_MS<<2) );	
		}
		else
		{	// wait indefinitely for next command
			dwrc = WaitForMultipleObjects( 2, m_poEB->m_events, FALSE,
				INFINITE );	
		}
		// reset event, to prevent new commands from being issued
		ResetEvent( m_poEB->m_events[ EVID_RDY_CMD ] ); 

		if ( dwrc == WAIT_TIMEOUT )
		{
			++countRetry;
			printf("::ReadAheadThread() : waiting...%d\n", countRetry );
			if ( countRetry == RETRY_COUNT )
			{
				fQuit = TRUE;
				printf("::ReadAheadThread() : I'm tired of waiting, goodbye.\n" );
			}
		}
		else
			countRetry = 0;	// reset counter

	// now process the eventBox.
	// since it's possible for MORE THAN 1 event to be set, we need to
	// check each signal individually.

//		printf ( "\n::ThreadHandler got '%d'", dwrc - WAIT_OBJECT_0 );


		dwrc = WaitForSingleObject( m_poEB->m_events[ EVID_CMD_EXIT ], 0 );
//		printf( "\n::ThreadHandler checking '%d', %d", EVID_CMD_EXIT,
//			dwrc );
		if ( dwrc == WAIT_OBJECT_0 )
		{
			ResetEvent( m_poEB->m_events[ EVID_CMD_EXIT ] );
			fQuit = TRUE;
		}

		dwrc = WaitForSingleObject( m_poEB->m_events[ EVID_CMD_READ ], 0 );
//		printf( "\n::ThreadHandler checking '%d', %d", EVID_CMD_READ,
//			dwrc );
		if ( dwrc == WAIT_OBJECT_0 )
		{
			ResetEvent( m_poEB->m_events[ EVID_CMD_READ ] );

			// perform the readahead operation, ARBITRARY_VALUE is 
			// the file handle returned by streamlist.cpp
			if ( g_vobflag )
			{
				g_cbFValid = g_demuxer.getM2VBytes(g_lpbuf_ra[ g_dwFIndex ], READ_AHEAD_SIZE, outputDirectory);
				//g_cbFValid = g_demuxer.go_demux(g_lpbuf_ra[ g_dwFIndex ],
				//	READ_AHEAD_SIZE );
				g_dwSystemPackLBA = g_demuxer.getLastLBA();
				// get LBA of last good system header in VOB stream
				//g_dwSystemPackLBA = g_demuxer.get_system_llba();
//SVE 03.06.2001 g_dwFPos = g_demuxer.m_FilePos+g_demuxer.m_FilePosRead;
//SVE 03.06.2001 st_RunState.filePos = g_demuxer.m_FilePos+g_demuxer.m_FilePosRead;
			}
			else
			{
				g_cbFValid = g_poStreamlist->my_read( ARBITRARY_VALUE, 
					g_lpbuf_ra[ g_dwFIndex ], READ_AHEAD_SIZE );
//SVE 20.05.2001 Filepointer
				st_RunState.filePos+=g_cbFValid;
			}

			SetEvent( m_poEB->m_events[ EVID_RDY_READ ] );
		}

		dwrc = WaitForSingleObject( m_poEB->m_events[ EVID_CMD_REALT ], 0 );
		if ( dwrc == WAIT_OBJECT_0 )
		{
			ResetEvent( m_poEB->m_events[ EVID_CMD_REALT ] );
			printf("\n::ThreadHandler() : 'EVID_CMD_REALT' switching to realtime op...");
			fMode_RealTime = TRUE;
		}

		dwrc = WaitForSingleObject( m_poEB->m_events[ EVID_CMD_PAUSE ], 0 );
		if ( dwrc == WAIT_OBJECT_0 )
		{
			ResetEvent( m_poEB->m_events[ EVID_CMD_PAUSE ] );
			printf("\n::ThreadHandler() : 'EVID_CMD_PAUSE' switching to wait-mode op...");
			fMode_RealTime = FALSE;
		}

	} 
	while ( fQuit == FALSE );

//SVE 20.05.2001	printf("::ReadAheadThread() : exiting...\n" );
	// notify MPEG2AVI that the thread has shutdown properly.
	brc = SetEvent( m_poEB->m_events[ EVID_EXIT_OK ] ); 
	if ( brc == 0 )
		printf("\n::ReadAheadThread() : SetEvent( EVID_EXIT_OK) failed...");

	_endthread();
}


// this function is called by my_seek() and my_open(), immediately after a
// new file is opened, or the file's pointer has been changed (seek.)
// read_ahead_resst() loads the first read_ahead buffer, then issues 
// the command to load the 2nd buffer.  Then returns.  This sequence
// places the read_ahead system in the state expected by the 
// multithreaded my_read() command.
//
//    (my_read() never calls read_ahead_reset().)
// returns TRUE if success, FALSE if failure
static BOOL
read_ahead_reset( void )
{
	DWORD dwrc;
	DWORD countRetry = 0;
	BOOL brc;

	// wait for the read-ahead thread , before issuing a new command
	do
	{
		dwrc = WaitForSingleObject( g_poEBreader->m_events[ EVID_RDY_CMD ],
			TIMEOUT_MS );
		++countRetry;
	}
	while ( dwrc == WAIT_TIMEOUT && countRetry < RETRY_COUNT );

	// check for event confirmation, if loop timed-out, signal failure
	if ( dwrc == WAIT_TIMEOUT )
	{
		fprintf( stderr, "\nread_ahead_reset() : waited for but did not receive EVID_RDY_CMD.");
		return FALSE;	// failure
	}

	// reset the read-ahead system, pointers _> buffer#0
	g_dwFIndex = 0; // buffer forward index (data insertion point)
	g_dwBIndex = 0; // buffer back index (data extraction point)
	g_dwBPos = 0;
	g_cbBValid = 0;
	g_cbFValid = 0;	// (byte counter), #valid bytes in the forward buffer

	// 1) issue read command to fill buffer #0
	brc = ResetEvent( g_poEBreader->m_events[ EVID_RDY_CMD ] );
	brc = ResetEvent( g_poEBreader->m_events[ EVID_RDY_READ ] );
	brc = SetEvent( g_poEBreader->m_events[ EVID_CMD_READ ] );
	// 2) wait for the 1st read-command to complete
	do
	{
		dwrc = WaitForSingleObject( g_poEBreader->m_events[ EVID_RDY_READ ],
			TIMEOUT_MS );
		++countRetry;
	}
	while ( dwrc == WAIT_TIMEOUT && countRetry < RETRY_COUNT );
	brc = ResetEvent( g_poEBreader->m_events[ EVID_RDY_READ ] );

	//    set back-buffer index to the buffer we just filled
	g_dwBIndex = g_dwFIndex;
	g_cbBValid = g_cbFValid;
	g_dwFIndex = (g_dwFIndex==0); // toggle to the next buffer

	// 3) now issue a 2nd fill-request, then return
	brc = SetEvent( g_poEBreader->m_events[ EVID_CMD_READ ] );

	return TRUE; // success
}




/*	// main processing loop in ThreadReadAhead()
	// this do/while() loop executes repeatedly, until MPEG2AVI or Windows
	// sends a WM_QUIT message.  Note that the thread can be manually "killed"
	// by Windows.
	do
	{
		// thread suspends execution until a new message arrives.
		dwrc = GetMessage( &m_msg, NULL, 0, 0); 

		EVID_CMD_EXIT
		if ( dwrc <= 0 )	// WM_QUIT received?
			fQuit = TRUE;	// yes, time to quit
		if ( m_msg.message == WM_QUIT )
			fQuit = TRUE;

		printf("\n::ThreadHandler() : got message!" );

	} while ( fQuit == FALSE );

	if ( dwrc < 0 )
		printf("\n::ThreadHandler() : GetMessage() returned error!");
*/